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, …)
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 =newstd::ifstream("in.txt");...delete in;...char*other_buffer =newchar[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
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)functionsource_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 =newWebKitMediaSource();ms.addEventListener('webkitsourceopen', source_opened);vid.src=window.URL.createObjectURL(ms);
// implements JavaScript buffer.timestampOffset = 42void 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 VTableif(!m_player->sourceSetTimestampOffset(id, offset))...}bool MediaPlayer::sourceSetTimestampOffset(...){// m_private deleted when MediaPlayer deleted// this *is* a VTable-based callreturnm_private->sourceSetTimestampOffset(id, offset);}
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