Show Notes

130 - Chrome Heap OOB Access and TLStorm

Out of bounds read in Chrome’s PDFium Engine in the RequestThumbnail() method. The page_index parameter is used to index into a vector of pages to call that page’s RequestThumbnail() callback, however the page_index isn’t validated in production builds. There is a DCHECK(PageIndexInBounds(page_index)) call, but this is a debug check which is not compiled in production. This leads to what’s likely an easy code execution route, as this page pointer in the vector directly invokes a callback. By accessing the vector out of bounds and accessing an attacker-controlled pointer as a page, code execution should be trivial via hijacking the callback.

Two logic bugs that cause memory corruption in the handling of TLS packets due to unhandled error / return values when using the nanoSSL library, and a higher level design flaw with the firmware update system.

Uninitialized use yielding authentication bypass in TLS handshake First bug is in the TLS handshake process. Under normal circumstances, a client is supposed to send a ClientHello message to the server, which then responds with a ServerHello message. This server response contains the session ID as well as the cipher suite to be used. If the session ID matches the current TLS state’s session ID, the session is “resumed” and the master secret will be copied from the cache. If the session ID is new, the client will validate the cipher suite provided by the server and perform key exchange.

The problem comes if the client sees a cipher suite from the server that it doesn’t support. In this case, the handler returns an error and the TLS connection is supposed to be terminated. Because APC ignores this return value, the connection stays open, and upon subsequent ServerHello messages, it will reuse the previous session information and “resume” the uncompleted session. This results in the master secret in the cache being used uninitialized as zeroes, which allows an attacker to establish a successful session as if they were a trusted server.

Buffer overflow in TLS packet reassembly Second bug is in the TLS packet reassembly process. When TLS records are sent over TCP from peer to peer, they’re sent as a header (which includes the content type and payload length) followed by the payload. The library has to keep track of the “reassembly state” as it receives records. When expecting the payload, the incoming message buffer is reallocated if the length exceeds the previous buffer size. However, if the payload length >= 2389, an error value is returned and the buffer is not reallocated.

Again, this return value is ignored higher up in the call stack, this time by the coap_received_sock_cb() function. If an attacker simply sends a packet with a large length field, the buffer will never get reallocated and it will copy in packet data until that length is reached, without regard to the size of the incoming message buffer.

No code signing for firmware updates While crypto is used for firmware updates, only symmetric encryption is used. No proper code signing is used to authenticate the firmware update data. An attacker can just pull the symmetric key off one UPS and use it to create malicious firmware updates for other devices to backdoor a target device.

We have previously covered this bug, its an out-of-bounds access due to a broken assumption in every dup command having an associated immediate. When that assumption is broken by manually crafting netfilter rules nft_fwd_dup_netdev_offload function will perform an out of bounds access as it increments too far.

Where the post covers new ground is in the exploit strategy.

entry = &flow->rule->action.entries[ctx->num_actions++];
entry->id = id;
entry->dev = dev;

In the vulnerable code above the entries array may be access out of bound, performing two writes the the id and dev fields. The dev field was the interesting one as that it writing a pointer to the net_device structure. This provided two basic primitives depending on the rules being added (attacker controlled)

  1. Write at 24 bytes into the next block in kmalloc32 or 192 cache
  2. Write at 8 bytes into the next block in kmalloc128.

Unfortunately for the authors writes at these offsets didn’t seem too useful and he couldn’t find a good target for the write. However by performing the attack multiple times, he could write another 80bytes head, in kmalloc128, this goes from an offset of 8 to 88 to 40 (168 mod 128). That 40 byte offset he realized after reading Alexander Popov’s Four Bytes of Power exploit aligns with security pointer in a struct msg_msg

Overwriting the security field leads to an arbitrary free primitive when the msg_msg is received (attacker controlled) so if exploited in this scenario it would free the net_device. Leading to a more complete attack chain:

  1. Spray System V message queue messages (struct msg_msg) of a size to fit in kmalloc128
  2. Free some of the messages by recieving them
  3. Add a malicious netlink rule, trying to reuse one of these freed msg_msg structs
  4. Perform the OOB access three times, overwriting the among other things the security pointer of one of these messages
  5. Scan the messages for ones that have had their data corrupted to find the corrupted messages.
  6. Free the message with a corrupted security pointer, causing a free of the net_device
  7. Spray more messages this time of a size to fit in kmalloc4096, hopefully taking over the freed net_device
  8. Above control of the net_device to overwrite some of the netsec_ops function pointers for control flow hijacking