Show Notes

196 - An OpenBSD overflow and TPM bugs

Yet another case of bad syncronization or just performing operations in the wrong order. IIn this case ene_remove called when removing the device, will remove its internal allocations and everything before it actually unregisters the device. Meaning that there is a window of time where the pointers are free but the device is still accessible and so can lead to a use after free. It was also noticed that they did not clean up timers at all, so setting a timer then removing the device can lead to the timer triggering during the cleanup process, again leading to a use-after-free.

A type-confusion happens in during the initialization of TUN/TAP sockets that leads to the UID being fixed to 0. The root cause of this bug is in the incorrect assumption made by sock_init_data() regarding the struct socket input. It assumes the input socket will be contained within a struct socket_alloc, however for TUN and TAP devices it will be embeded in a struct tap_queue or struct tun_file when it gets called. Eventually, when container_of is called, with socket_alloc as the parent structure, it’ll calculate an incorrect container/parent pointer.

A straightforward integer underflow issue in OpenBSD TCP/IP socket’s sockopt handling. While ip_dooptions() and the IPOPT_SSRR option handler will check the user-provided optlen isn’t too large, it won’t check if it’s too small. When the IPOPT_SSRR handler code invokes save_rte(), it’ll calculate the isr->isr_nhops using the (optlen - IPOPT_OFFSET - 1) / sizeof(in_addr). A value such as optlen=2 will cause this to underflow and set isr_nhops to 0xFFFFFFFF.

This becomes critical when this field is used in a later function (ip_srcroute()) to iterate the source records, where it’ll go out-of-bounds on the isr_routes field.

Two vulnerabilities in the TPM 2.0 reference implementation’s CryptParameterDecryption(). The Trusted Platform Module (TPM) is used for key storage, key generation, and attestation via storing and taking “measurements” (integrity checks) in the boot process. As it’s doing sensitive actions, some commands like to have their parameters encrypted before going across the bus to the TPM. To handle this, the TPM facilitates “session-based encryption”, where you can establish a session and pass the session data in the command, and the TPM will have the session info to decrypt the parameters. As part of that though, the user provides a cipherSize and some other cipher-related data that follows it.

The first problem is that the system does not validate that the incoming command buffer is actually large enough to contain a parameterArea. The system will attempt to read a 15-bit value from the start of this area, In the case where there is no parameterArea in the buffer, this read will be 2bytes outside of the bounds of the command buffer. The impact of this 2-byte out-of-bounds read depends on the TPM user. On some targets, such as Hyper-V, the command buffer is a fixed size, and the unused bytes are zeroed out before being processed by the TPM, so the read will always be zero. If a platform does not zero out the command buffer, then it is possible that the read will get a stale value at that position, potentially leading to a more significant corruption as it tried to decrypt the stale data in-place.

The other problem is an out-of-bounds write due to a logic bug when checking the cipherSize. They try to ensure the cipherSize < sizeof(buffer), but since the size is encoded into the first two bytes of the buffer, they advance the buffer pointer by two bytes to get to the cipher data. However, they don’t take that into account when checking the cipherSize, and thus it’s possible to get a 2 byte OOB write. Again, how useful this is depends on the target. On VMware for example, a much larger buffer (0x10000) than the max TPM command size is used and as such, this bug is probably not useful there. Hyper-V on the other hand will allocate a static sized buffer which is exactly the max TPM command size (0x1000).