
The linux kernel socket sub-system is a surprisingly broad attack surface. It’s a group of syscalls that all multiplex very heavily, so it’s an excellent target for finding under-audited code paths when looking for kernel privilege escalation bugs.
One way to get a feel for the surface is to enumerate the set of instantiable socket types. I was personally bemused by the large number of obscure and mostly irrelevant sockets that are present by default in a standard distribution kernel. Here’s the data from my test rig:
family | type | protocol | description |
— | — | — | |
1 | 1 | 0 | AF_UNIX STREAM |
1 | 1 | 1 | AF_UNIX STREAM |
1 | 2 | 0 | AF_UNIX DGRAM |
1 | 2 | 1 | AF_UNIX DGRAM |
1 | 3 | 0 | AF_UNIX RAW |
1 | 3 | 1 | AF_UNIX RAW |
1 | 5 | 0 | AF_UNIX SEQPACKET |
1 | 5 | 1 | AF_UNIX SEQPACKET |
2 | 1 | 0 | AF_INET STREAM IP |
2 | 1 | 6 | AF_INET STREAM TCP |
2 | 1 | 132 | AF_INET STREAM SCTP |
2 | 2 | 0 | AF_INET DGRAM IP |
2 | 2 | 17 | AF_INET DGRAM UDP |
2 | 2 | 136 | AF_INET DGRAM UDPLITE |
2 | 5 | 0 | AF_INET SEQPACKET IP |
2 | 5 | 132 | AF_INET SEQPACKET SCTP |
2 | 6 | 0 | AF_INET DCCP IP |
2 | 6 | 33 | AF_INET DCCP DCCP |
3 | 2 | 0-255 | AF_AX25 DGRAM |
3 | 3 | 0-255 | AF_AX25 RAW |
3 | 5 | 0-255 | AF_AX25 SEQPACKET |
4 | 2 | 0-255 | AF_IPX DGRAM |
5 | 2 | 0-255 | AF_APPLETALK DGRAM |
5 | 3 | 0-255 | AF_APPLETALK RAW |
6 | 5 | 0 | AF_NETROM SEQPACKET |
8 | 0,2-10 | 0-255 | AF_ATMPVC |
9 | 5 | 0 | AF_X25 SEQPACKET |
12 | 1 | 0 | AF_INET6 STREAM IP |
10 | 1 | 6 | AF_INET6 STREAM TCP |
10 | 1 | 132 | AF_INET6 STREAM SCTP |
11 | 2 | 0 | AF_IPV6 DGRAM IP |
10 | 2 | 17 | AF_IPV6 DGRAM UDP |
10 | 1 | 136 | AF_IPV6 STREAM UDPLITE |
10 | 5 | 0 | AF_IPV6 SEQPACKET IP |
10 | 5 | 132 | AF_IPV6 STREAM SCTP |
10 | 6 | 0 | AF_IPV6 DCCP IP |
10 | 6 | 33 | AF_IPV6 DCCP DCCP |
11 | 5 | 0 | AF_ROSE SEQPACKET |
12 | 1 | 0-255 | AF_DECnet STREAM |
12 | 5 | 2 | AF_DECnet SEQPACKET NSP |
16 | 2 | 0-31 | AF_NETLINK DGRAM |
16 | 3 | 0-31 | AF_NETLINK RAW |
19 | 2 | 0-255 | AF_ECONET DGRAM |
20 | 0,2-10 | 0-255 | AF_ATMSVC |
21 | 5 | 0 | AF_RDS SEQPACKET |
23 | 1 | 0-255 | AF_IRDA STREAM |
23 | 2 | 0 | AF_IRDA DGRAM UNITDATA |
23 | 2 | 1 | AF_IRDA DGRAM ULTRA |
23 | 5 | 0-255 | AF_IRDA SEQPACKET |
24 | 0-10 | 0 | AF_PPPOX |
29 | 2 | 2 | AF_CAN STREAM BCM |
29 | 3 | 1 | AF_CAN RAW RAW |
31 | 1 | 3 | AF_BLUETOOTH STREAM RFCOMM |
31 | 2 | 0 | AF_BLUETOOTH DGRAM L2CAP |
31 | 3 | 1 | AF_BLUETOOTH RAW HCI |
31 | 3 | 3 | AF_BLUETOOTH RAW RFCOMM |
31 | 3 | 4 | AF_BLUETOOTH RAW BNEP |
31 | 3 | 5 | AF_BLUETOOTH RAW CMTP |
31 | 3 | 6 | AF_BLUETOOTH RAW HIDP |
31 | 5 | 0 | AF_BLUETOOTH SEQPACKET L2CAP |
31 | 5 | 2 | AF_BLUETOOTH SEQPACKET SCO |
33 | 2 | 2 | AF_RXRPC DGRAM |
36 | 2 | 0-255 | AF_IEEE802154 DGRAM |
36 | 3 | 0-255 | AF_IEEE802154 RAW |
Armed with this list, it’s a piece of cake to select a few of the lesser known socket families and choose an operation that is likely to be misused. I love the smell of privesc in the morning.
In my case, I first chose setsockopt. By its very nature it has to handle a lot of untrusted user-space input. After a couple of hours of auditing we get a few near misses, a nice memory leak and a non-exploitable unterminated string bug (I mention it since I haven’t seen one of these for a long time), but nothing much worth sniffing at.
Another good option is ioctl (or maybe even accept), but my next bet was on sendmsg. I already knew that this call inherits some interesting idiosyncracies from the socket layer, so that was enough to get me hopeful. And sure enough, one of the first protocols I looked at suffered from a very classical heap overflow flaw. All in all, it was about 4 hours from writing some enumeration code to a kernel oops.