libtalloc: A GDB plugin for analysing the talloc heap

tl;dr

This post is about a GDB plugin I wrote while researching the Samba exploitation earlier in 2015. There is a python script available. See the README for usage examples. Note that the plugin was thrown together while hacking on bugs.

Introduction

The Samba project developed a custom heap dubbed the “trivial allocator” aka talloc. A great overview of how the allocator works is available in the official tutorial. This is an interesting hierarchical heap algorithm that is now used by a variety of other software projects as well. When exploiting vulnerabilities in Samba that involve the heap in some way, there aren’t currently great tools to use to analyse the heap. Although using GDB with debug symbols or patching in debug calls in various locations can be sufficient, I wanted to have something a bit more powerful and easy to automate. After exploiting an interesting Samba bug earlier in the year I was investigating some other manifestations of the bug that didn’t appear to be exploitable, but required me to understand a bit more about the heap layouts to know for sure. So to solve my problems I wrote the libtalloc GDB plugin, which is available publicly on our git repository – here. I will note that I only hack on python occasionally as and when I need it, and this is my first python-based GDB plugin, so the code quality reflects as much.

I also wanted to have something that could work even if debug symbols weren’t available, and that would automatically tell me what versions of the allocator were being used. And finally, it was a fun excuse to learn how to write a GDB Plugin in python, as I’d only written heap analysis tools using the GDB scripting language before, which often left something to be desired.

The tool could be useful for you if you’re just hacking on something that uses talloc, exploiting an interesting Samba bug of your own, or maybe analyzing someone else’s Samba exploit.

Source code

The source for the libtalloc plugin is available on the NCC Group git repository. I don’t currently plan to actively develop the script beyond the current functionality unless I end up looking into some more Samba bugs in the future. That said, if you run into problems with it you can feel free to let me know and I will do my best to fix it.

Functional overview

The libtalloc plugin is modeled after other public heap analysis plugins like libheap and unmask_jemalloc. I will show some basic examples show some basic usage of the tool on a 32-bit Samba instance, but it also has been tested to some degree on 64-bit as well. For more exhaustive usage examples I recommend reading the included README file.

So far I’ve tested the tool on versions 2.0.7, 2.0.8, 2.1.0, and 2.1.1 on x86 and x86_64, though not always exhaustively on each version.

To see a full list of libtalloc commands you can issue the tchelp command:

    (gdb) tchelp
    [libtalloc] talloc commands for gdb
    [libtalloc] tcchunk -v -x <addr>  : show chunk contents (-v for verbose, -x for data dump)
    [libtalloc] tcvalidate -a <addr>  : validate chunk (-a for whole heap)
    [libtalloc] tcsearch <addr>       : search heap for hex value or address
    [libtalloc] tcwalk <func>         : walk whole heap calling func on every chunk
    [libtalloc] tcreport <addr>       : give talloc_report_full() info on memory context
    [libtalloc] tcdump -s <addr>      : dump chunks linked to memory context (-s for sorted by addr)
    [libtalloc] tcparents <addr>      : show all parents of chunk
    [libtalloc] tcchildren <addr>     : show all children of chunk
    [libtalloc] tcinfo                : show information known about heap
    [libtalloc] tcprobe               : try to collect information about talloc version
    [libtalloc] tchelp                : this help message

The tcprobe command can be used to dynamically determine the version of talloc in use. This command has to be run in order to effectively analyze the heap contents because the underlying structure sizes and contents change fairly significantly across versions. An example of using tcprobe:

    (gdb) tcprobe
    Version: 2.1.1
    File: /usr/lib/libtalloc.so.2.1.1

The simplest example command to show is the tcchunk which will give you information about the chunk header, as well as optionally the chunks contents, with varying granularity. For instance:

    (gdb) tcchunk 0x80a13c88
    0x80a13c88 sz:0x00000020, flags:..p., name:struct netr_ServerPasswordSet

The following is a legend for chunks within the summary output:

    p - Member of a pool (POOLMEM flag)
    P - Chunk is a pool (POOL flag)
    F - Chunk is free (FREE flag)
    L - Chunk is looped (LOOP flag)

You can also request more verbose header output:

    (gdb) tcchunk -v 0x80a13c88
    struct talloc_chunk @ 0x80a13c88 {
    next         = 0x0
    prev         = 0x80a140c8
    parent       = 0x0
    child        = 0x80a14088
    refs         = 0x0
    destructor   = 0x0
    name         = 0x807d9f2f (struct netr_ServerPasswordSet)
    size         = 0x20
    flags        = 0xe8150c78 (POOLMEM)
    limit        = 0x0
    pool         = 0x80a13248

You can also use it to do things like validate the entire heap, analyse chunk pools, replicated built-in talloc debug output, list parent and child hierarchies, or the entire heap, search the heap, or write updated values to chunks to test modifications that you might find useful during exploitation, etc.

You might ask, where did I find this address to supply to tcchunk? So let’s just assume you’re analyzing Samba and you break somewhere while inside. You can do a backtrace and identify function arguments that might be talloc chunks. To be 100 per cent certain you’d want to look at the code or just try to see if the plugin recognizes it as a chunk. Consider the following backtrace:

(gdb) bt
#0  0xb6d89424 in __kernel_vsyscall ()
#1  0xb6a0664e in poll () from /lib/i386-linux-gnu/libc.so.6
#2  0xb71c8df0 in sys_poll (fds=0xb94b5f88, num_fds=3, timeout=29423) at ../lib/util/select.c:104
#3  0xb7200967 in s3_event_loop_once (ev=0xb94a52b0, location=0xb768b4d7 "smbd/server.c:844") at lib/events.c:341
#4  0xb7201758 in _tevent_loop_once (ev=0xb94a52b0, location=0xb768b4d7 "smbd/server.c:844") at ../lib/tevent/tevent.c:494
#5  0xb6e537e4 in smbd_parent_loop (parent=<optimized out>) at smbd/server.c:844
#6  main (argc=<error reading variable: Cannot access memory at address 0x4>, argv=<error reading variable: Cannot access memory at address 0x8>)  at smbd/server.c:1326

Let’s assume that the ev variable passed to s3_event_loop_once and _tevent_loop_once is a talloc chunk. We’ll pass the address we see above directly to tchunk:

 (gdb) tcchunk 0xb94a52b0
WARNING: 0xb94a52b0 not a talloc_chunk. Assuming ptr to chunk data
0xb94a5280 sz:0x0000003c, flags:...., name:struct tevent_context

You see a warning that the address directly isn’t a chunk, but the script tries to be helpful and also check at the address minus the chunk size to see if you passed it a pointer to chunk data instead. Above we see that that is the case, and the ev parameter is a struct tevent_context chunk of size 0x3c that.

This leads me to an important point. The talloc heap will have a parent node, called the null_context, which represents the root of the hierarchy. In order for certain functionality to work, such as heap searching, this root node must be known. When you run a command like tcchunk as shown above, behind the scenes the script will check if the root node is known yet, and if not will try to walk back up the tree of chunks to find it. You can verify if it’s found using tcinfo:

(gdb) tcinfo
[libtalloc] null_context: 0xb94a5028
[libtalloc] Version: 2.0.7
[libtalloc] File: /usr/lib/i386-linux-gnu/libtalloc.so.2.0.7

So a couple other neat things you can do. The tcvalidate command will analyse a chunk and try to tell if you anything is wrong with it:

(gdb) tcvalidate 0xb94a5280
Chunk header is valid

You could also run this validation across the entire heap using the tcwalk command and passing it the underlying python function that backs tcvalidate:

(gdb) tcwalk validate_chunk

As in the case above with calling validate_chunk, I’ve tried to add a lot of functionality like that so you can call methods to modify chunks directly, like set_destructor(), set_size(), flag setting functions, etc. There is a bunch more functionality and examples in the README, so I won’t repeat it all here.

Conclusion

Hopefully this helps you out if you’re looking into Samba for some reason or if you want to write your own GDB script and simply need some ideas. I appreciate any feedback, patches, bug reports, etc. As always, you can ping me over email at: aaron<dot>adams<at>nccgroup<dot>trust or on twitter @fidgetingbits.

Published date:  11 August 2015

Written by:  Aaron Adams