This vulnerability was analyzed during Episode 178 on 10 January 2023
An out-of-bounds read/write in FreeBSD’s bhyve hypervisor. The vulnerability here is in the E82545 gigabit ethernet controller’s emulator, specifically e82545_transmit()
. As the name suggests, it’s responsible for transmitting packets, and will iterate a ring buffer containing packet descriptors and write out iovecs. The two types of descriptors that are important are (d)ata descriptors that contain payload data, and (c)ontext descriptors which describe header length, payload length, and checksum offsets. If TCP segment offloading is enabled, the packet header length from the context descriptor gets used.
e82545_transmit()
has to validate that the checksum offset fields (ck_off
) don’t go beyond the bounds of the header. In the case of TCP packets, it’s validated. In non-TCP packets (such as UDP), the checksum offset isn’t validated and OOB R/W is possible. OOB read occurs when the pseudo-header checksum is saved, and an OOB write happens when it goes to carry the checksum over. Effectively, this gives them a constrained 2 byte write primitive at an offset of 0-255 (as of the offset is only 8-bits wide).
Exploitation was a bit tricky as the header is too far away from the return address or saved base pointer on the stack. However, the pointer to the header itself (hdr
) can be corrupted, and is used in two iteration loops later on to construct packet data and update IP headers. While they demonstrate the first loop can be used to leak stack contents, it’s unnecessary as FreeBSD 13’s bhyve doesn’t have ASLR by default. The second loop can be used to get a more useful relative write of a DWORD to smash the return address and ROP. As the function that calls e82545_transmit()
doesn’t return, they couldn’t target the saved frame pointer to easily stack pivot, so they used their write primitive multiple times to build a small ROP chain that pivots the stack to their controlled hdr
which will continue the chain to call system()
.