Lucene search

K
hackeroneM00nbsdH1:1350653
HistorySep 24, 2021 - 6:00 p.m.

PlayStation: Remote kernel heap overflow

2021-09-2418:00:21
m00nbsd
hackerone.com
11

Summary

The PlayStation has a kernel PPPoE driver, that originates from NetBSD. This driver has a kernel heap overflow vulnerability, that an attacker can remotely trigger over the LAN, with the ability to control both the contents that are overflown and their sizes.

Technical Details

PPPoE Protocol

In short, the PlayStation (PS) will:

  1. Send a PADI packet.
  2. Expect to receive a PADO packet.
  3. Send a PADR packet.
  4. Expect to receive a PADS packet.

The Vulnerability

I determined that the PS’ PPPoE driver originates from NetBSD. In that PPPoE driver, there is a vulnerability in the way PADR packets are allocated:

static int
pppoe_send_padr(struct pppoe_softc *sc)
{
	[...]

	/* Compute packet length. */
	len = sizeof(struct pppoetag);
	if (sc->sc_service_name != NULL) {
		l1 = strlen(sc->sc_service_name);
		len += l1;
	}
	if (sc->sc_ac_cookie_len > 0) {
		len += sizeof(struct pppoetag) + sc->sc_ac_cookie_len;
	}
	if (sc->sc_relay_sid_len > 0) {
		len += sizeof(struct pppoetag) + sc->sc_relay_sid_len;
	}
	len += sizeof(struct pppoetag) + sizeof(sc->sc_id);
	if (sc->sc_sppp.pp_if.if_mtu > PPPOE_MAXMTU) {
		len += sizeof(struct pppoetag) + 2;
	}

	/* Allocate packet. */
	m0 = pppoe_get_mbuf(len + PPPOE_HEADERLEN);
	if (m0 == NULL)
		return ENOBUFS;

	/* Fill in packet. */
	[...]
}

static struct mbuf *
pppoe_get_mbuf(size_t len)
{
	struct mbuf *m;

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m == NULL)
		return NULL;
	if (len + sizeof(struct ether_header) > MHLEN) {
		MCLGET(m, M_DONTWAIT);
		if ((m->m_flags & M_EXT) == 0) {
			m_free(m);
			return NULL;
		}
	}
	m->m_data += sizeof(struct ether_header);
	m->m_len = len;
	m->m_pkthdr.len = len;
	m_reset_rcvif(m);

	return m;
}

The flow is:

  • pppoe_send_padr():
    • It wants to send a PADR packet.
    • It computes the packet length, and calls pppoe_get_mbuf().
  • pppoe_get_mbuf():
    • If the length is larger than MHLEN, it allocates an mbuf cluster, of size MCLBYTES=2048.
    • It returns that mbuf cluster.
  • pppoe_send_padr():
    • It fills in the mbuf cluster.

The vulnerability here is that the packet length could actually be bigger than MCLBYTES, in which case the filling of the packet will overflow the mbuf cluster.

Constraints

To have a length that is larger than MCLBYTES, the sc_ac_cookie_len and sc_relay_sid_len values need to be large enough.

Both of these values are actually extracted from PADO packets that the PS previously received: they are the lengths of the ACCOOKIE and RELAYSID tags that were embedded in the PADO packets. The attacker can control these lengths.

There is a constraint on the MTU: given that the PS’ maximum MTU is 1500, the attacker cannot directly send just one PADO packet with sizes larger than MCLBYTES. To work around that constraint, the attacker just has to send two PADO packets, one with a big ACCOOKIE tag, and another with a big RELAYSID tag. After the second packet, the PS will send a PADR packet combining both big tags, which will overflow the mbuf cluster with the contents of the second tag.

Attack Scenario

  1. The PS sends a PADI.
  2. The attacker sends a PADO, with a ACCOOKIE tag whose size is 1400 bytes.
  3. The PS sends a PADR. This one is fine, there is no overflow here.
  4. The PS waits for a PADS packet.
  5. The PS times out, and resends a PADI.
  6. The attacker sends a PADO, with a RELAYSID tag whose size is 1400 bytes.
  7. The PS Sends a PADR. The overflow occurs here: the PS tries to embed the two tags (1400x2=2800 bytes) into a 2048-byte mbuf cluster.

Setup / PoC / Discussion

  • Enable PPPoE on the PS:
    • Settings -> Network -> Set Up Internet Connection -> Use a LAN Cable -> Custom
    • IP Address Settings: PPPoE
    • Enter whatever in the two User/Password fields, click Next
    • DNS Settings: Automatic
    • MTU Settings: set 1500
    • Proxy Server: Do Not Use
  • Connect a Linux laptop to the PS with an Ethernet cable.
  • On the Linux laptop:
    • cc -o poc poc.c -Wall
    • sudo ifconfig eth0 mtu 8000
    • sudo ./poc eth0
  • On the PS: click Test Internet Connection. This will initiate the PPPoE connection.

To see what happens:

  • Open WireShark on the Linux laptop, and look at the packets that are being exchanged with the PS. You can see that the PS sends 2844-byte PADR packets.
  • Actual exploitation/introspection will require a debugger, which I do not have. :'(
  • ███████

Impact

Possible RCE. I did my tests only on a friend’s PS4, but I suspect that the PS5 is affected as well.