In this post we will go over the GHOST PoC under a debugger. This will visually show how the buffer overflow condition is met.
Let me explain some of the above code.
First we define a CANARY, “in_the_coal_mine”. This is our overwrite target.
Next we define a struct, which consists of two chunks. These chunks represent a buffer that will be overflowed by the glibc gethostbyname_r function, and the data that comes after it (the CANARY).
The first chunk is named “buffer” and its size is 1024 bytes, which is the size that will be passed to gethostbyname_r via the “buflen” argument.
Following the “buffer” chunk is the CANARY chunk. By overflowing the “buffer” chunk into the CANARY chunk, we can confirm the PoC exploit works.
Now that we have defined our struct representing the buffer for gethostname_r, the name char array is being initialized with 999 bytes of ASCII ‘0’ / HEX 0x30.
name The name of the Internet host whose entry you want to find. result A pointer to a struct hostent where the function can store the host entry. buffer A pointer to a buffer that the function can use during the operation to store host database entries; buffer should be large enough to hold all of the data associated with the host entry. A 2K buffer is usually more than enough; a 256-byte buffer is safe in most cases. buflen The length of the area pointed to by buffer. h_errnop A pointer to a location where the function can store an herrno value if an error occurs.
Now, let’s see the binary under GDB debugger.
Lines 24-28, are where memset initializes name char array with 999 bytes of ‘0’. Line 31, name char array is being null terminated. Lines 36-41, is where the arguments for gethostbyname_r are being pushed to the stack, right to left. After the gethostbyname_r function, on lines 44-49 is where strcmp confirms if the CANARY has been modified or not.
Let’s set a breakpoint before the gethostbyname function and inspect the temp stuct.
As you can see, the content of temp is of two buffers named “buffer” and “canary”. Content of Buffer is the name of the buffer “buffer” which is 6 bytes + 1017 bytes of ‘0’ characters + null terminating byte = 1024 bytes. CANARY starts at memory location 0x804a440 or 1024 bytes within the temp struct, and it’s content is as initialized “in_the_coal_mine”.
Now let’s move 1 instruction down, past the gethostbyname_r function and inspect the temp struct again.
What this shows us is that now, the temp.buffer is 1028 bytes. 4 bytes from temp.buffer chunk has overflowed into the temp.canary chunk. If we inspect the 0x804a440 address, which use to be the start of the CANARY chunk, we should see the overflowed bytes followed by terminating null char.
As noted by Qualys in their report, the vulnerable code in the function is in nss/digits_dots.c. The patch mainly consists of adding 4 bytes to the size_needed object.
This adds the 4 missed bytes that cause the overflow from the first chunk to the next chunk. Now if we add this to the calculation of the “size_t len” from the above PoC code, instead of 999 bytes the name char array buffer will be 995 and the overflow will not work.