POSTED BY: argp / 22.08.2017

shadow v2 public release

About four months ago (April 2017), Vasilis Tsaousoglou and myself presented our work on exploiting Android's libc allocator at the 2017 INFILTRATE conference (Miami, Florida). Since version 5.0, Android has adopted the jemalloc allocator as its default libc malloc(3) implementation. For our talk we extended our previously released jemalloc heap exploration and exploitation tool called 'shadow' to support Android (both ARM32 and ARM64), and demonstrated its use on understanding the impact of libc heap corruption vulnerabilities. We also presented new jemalloc/Android-specific exploitation techniques for double free and arbitrary free vulnerabilities.

This research constitutes a continuation of our work on the exploitation of the jemalloc memory allocator, a brief history of which is presented below:

  • Our 2012 Phrack paper on exploiting the standalone version of jemalloc. Focused mainly on metadata corruption attacks, and included an exploit against jemalloc/FreeBSD-libc in the context of the VLC media player.
  • Our 2012 Black Hat USA talk on jemalloc metadata corruption attacks in the context of the Mozilla Firefox browser.
  • Our 2015 INFILTRATE talk on jemalloc/Firefox application-specific exploitation methodologies.
  • Our 2017 INFILTRATE talk on Android's use of jemalloc, that this blog post refers to.

We are now pleased to announce that version 2 of our shadow tool is available as open source software on the official CENSUS GitHub page. Apart from the tool's source code, the repository also includes work-in-progress documentation on setting up an Android userland debugging environment for utilizing shadow, a quick overview of Android's jemalloc structures using shadow, and some notes on how double, unaligned and arbitrary free() bugs behave on Android's jemalloc.

Let's see a brief walkthrough of shadow using an emulated double free bug in jemalloc/Android.

Initially, we view the thread cache ("tcache") bin stack for bin index 2:


    (gdb) jetcache -b 2
    index    lg_fill_div    ncached    low_water    ncached_max
    ---------------------------------------------------------------
    2        1              3          0x2          8
    
    stack
    ----------------
    0x7160571180
    0x715efa55a0
    0x715efa55c0

Bin index 2 corresponds to sizes 0x11-0x20:


    (gdb) jebininfo
    index    region_size    run_size    no_regions
    --------------------------------------------------
    0        0x8            0x1000      512
    1        0x10           0x1000      256
    2        0x20           0x1000      128
    3        0x30           0x3000      256
    ...

We now emulate the bug condition by calling free() with the same address twice using gdb:


    (gdb) jeinfo 0x715efa5580
    parent    address         size
    --------------------------------------
    arena     0x717a402200    -
    chunk     0x715ee00000    0x200000
    run       0x715efa5000    0x1000
    region    0x715efa5580    0x20

    (gdb) p free(0x715efa5580)
    $4 = 32
    (gdb) p free(0x715efa5580)
    $5 = 32

We now parse the heap again and view the tcache bin stack:


    (gdb) jeparse
    [shadow] parsing structures from memory...
    [shadow] structures parsed

    (gdb) jetcache -b 2
    index    lg_fill_div    ncached    low_water    ncached_max
    ---------------------------------------------------------------
    2        1              5          0x2          8

    stack
    ----------------
    0x715efa5580
    0x715efa5580
    0x7160571180
    0x715efa55a0
    0x715efa55c0

The freed address was pushed in the tcache bin stack twice. This means that the next two malloc() requests will return the same address, opening a lot of attack avenues, like turning the double free bug into a use-after-free, or a corruption of an object with attacker-controlled data.

For more details on this attack methodology and others, see our INFILTRATE 2017 talk's slides, and shadow's source code.