An integer underflow in GPSd (GPS daemon) in the parsing of Network Transport of RTCM via IP (NTRIP) packets. When parsing the HTTP response that contains the table of records for getting GPS data, they parse line-by-line until a carriage return (\r\n) or null byte is encountered. The getline()
routine will do some buffer tracking and decrement inbuflen
on each character of the buffer as it parses. The problem is, getline()
will be called continuously until the carriage return or null byte is provided without accounting for how much data is left in the buffer. While the loop in getline()
will break out when inbuflen == 0
, getline()
will be called again, decrement inbuflen
under zero, and underflow to a large value. Later, the left over bytes will be copied via memmove()
with the larger value and overflow.
This is actually a follow-up in a sense to the WebP 0day post we covered on Episode 218. So I won’t rehash how the vulnerability works, instead these two posts dive more onto the exploitation side of things.
First up, is the core primitive they gained from this Huffman table creation bug, as we suspected originally this would be a somewhat limited out-of-bounds write. First is where the write happens. This isn’t a linear overflow where it just keeps writting into adjacent memory, instead you’re able to trick the table writer into trying to write to an index that is out-of-bounds. They were able to gain some more granular control over the index by using having multiple 9-bit long prefix codes in the table. Each one of these codes could push the out-of-bounds write index up by 2, allowing the write to happen at an aligned address in multiples of 8 bytes. So that is a pretty nice base to have, not as nice as writing to any relative address, but being aligned its likely what can be written will line-up with fields in the object they can overflow into.
As for the actual value being written, it would be writing a HuffmanCode value which has two fields a uint8_t
which represented the number of bits for the HuffmanCode. As the overflow would happen later in the table this would be fixed (I believe) to 15
. one byte of padding that was not controlled, and a uint16_t
representing the symbol value. The Symbol value had limited range depending on the type of table being written, in this case the range was 0 to 39. Definitely some restrictions there to be concerned about, not enough to write any pointers for the common technique of creating an arbitrary read/write primitive.
OOB Write to Use-After-Free
The author goes into their search for a target object to corrupt and some challenges there, ultimately discovering the CSSVariableData
structure. A variaible sized object that would contain the string representation of a CSS Variable. This object began with a ref_count_
field, then a length_
and flags
fields before the variaible string data. The author was able to take a fairly classic target and corrupt the reference count to have the object be freed early, putting them into a use-after-free situation.
Use-after-free to Relative Read
With use-after-free, the user side of the equation is that CSSVariableData
structure which contains a length_
and a string that will be read. Which is the target of this stage. They get the freed CSSVariableData
object reclaimed by an AudioArray
which contains entirely attacker controlled data. Allowing them to corrupt the length_
value, so when the CSSVariableData
value is read in JavaScript it would read adjacent memory.
After this point the exploit goes into taking a double-free and attacking the PartitionAlloc metadata in a style reminiscent of a FastBin attack and gets into a bit more Chrome specific work. Check out the actual post for those details (Ctrl+F
Cross-Bucket Allocation in the Part2 post).