pwn ๐ป
/usr/lib/debug
(ๅฏ่ฝๆๅพไบ)set exec-wrapper env "LD_PRELOAD=./libc-2.29.so"
(for 2.29)
set environment LD_PRELOAD=./libc.so
ๅฐฑๅฏไปฅ๏ผๆๆๅพไธ่กset debug-file-directory /usr/lib/debug/lib/x86_64-linux-gnu
ref: https://stackoverflow.com/questions/10000335/how-to-use-debug-version-of-libc
libc package (dbg prefix - debug symbols)
https://mirror.tuna.tsinghua.edu.cn/ubuntu/pool/main/g/glibc/
In gdb๏ผ
dir /usr/src/glibc/glibc-2.31/exit.c
make debug symbol glibc
strace ./binary
)checksec
)\xcd\x80
) ๆ้บผ็gcc 4.8 ไปฅไธ, 64 bit ไธๆ่จฑๅค้็จ gadget
__libc_csu_init
ไธๆๅไบๆ
๏ผๅ ็บไปๅชๆฏ่ฒ ่ฒฌ call init entry ่ๅทฒ๏ผ่ init entry ไนไธๆๅนนๅpop rdx ; ret
ๆพไธๅฐ__libc_csu_init
_libc_csu_init
)_start
)pop_rsp_r13_r14_r15_ret
ไพๆ stack ้ทๅฐ got ไธ๏ผ้ๆจฃๅฐฑ่ฝๆ got value ๆพๅฐ register ๅ
งmov cs:__bass_start, 1
ๆๆ add ebx, esi ; ret ็ gadgetcall [r12+8*rbx]
ๅฏไปฅ็จ
_dl_runtime_resolve
ๅจ relro ๅ
จ้ๆ็กๆณ็จ_IO_file_write
ๅฏไปฅ leak libc (ๅคงๆฆ == write)gets
๏ผไฝฟ็ไธ็ _IO_file_write
ๅฏไปฅ่ขซ call ๅฐ๏ผไธฆ่่ๆงๅถ rdi, rsi, rdx _IO_new_file_write(stdin, got, 8)
*(ptr+0x70)
็บ 1 ๅฐฑๅฅฝ (stdout)๏ผ้ๆจฃๅฐฑ่ฝ leakhttps://github.com/lattera/glibc/blob/master/libio/fileops.c#L1180
__write (f->_fileno, data, to_do)
offset of _fileno
== 0x70
puts("/bin/sh")
== system("/bin/sh")
GOT ็ๅไธๅ entry ๅๅฅๆฏ๏ผ
_dl_runtime_resolve
ๆดๅ runtime resolve ็ function prototype๏ผ_dl_runtime_resolve(*link_map, rel_offset)
libc_start_main + 243, 242, 235 ...
)
%n, %hn ้ ่จญ็บๅฐไนๅๅฐๅบไพ character ๆธ้๏ผๅฏซๅฐๅฐๆ argument ptr ๆๅฐ็ไฝ็ฝฎ๏ผ่ๅฆๆๆฒๆๆๅฎ k$๏ผๅฏไปฅๅจไธๆฌก fmt ไนๅ งๆ็ ง้ ๅบไฟฎๆนๅคๆฌก
printf("%*");
๏ผ*
ไปฃ่กจๅๅฐๆๅฝๅผๅๆธ็ๅผ
็จไพ่จ็ฎ็ธฝๅ ฑๅฏซๅคๅฐๆฌก็ function
FILE struct
fopen()
็ข็FILE struct
ๆ็บๅฎฃๅ _IO_FILE_plus
_IO_FILE
_flags
"w+", "r" โฆ_IO_read_ptr
~ _IO_buf_end
_fileno
็ฑ system()
open ็ข็_IO_FILE_plus
_IO_FILE
ไธญ็บ _chain
fopen()
_IO_list_all
ไธ้ๅงๆไธฒ่ sterr, stdout and stdin_flag
, chain
and vtable
็ญโฆchain
ๅจๆๅๅๆฌ็ chain
)
_IO_list_all
ๆๅฐ๏ผ็ทๆฅ่ๆฏๅ
ถไป fd ่ stderr, stdout, stdin (้กไผผ LIFO ?)sys_open()
the filefread()
vtable->_IO_file_xsgetn
ๅคๆทvtable->_IO_file_doallocate
allocatevtable->_IO_file_underflow
่ฎไธๅคงๆฎตๅฐ stream buffer ไธ
sys_read()
่ฎๆชfwrite()
fclose()
flush()
and release()
stream bufferfopen()
ๆ่ชๅ malloc ไธๅ็ฉบ้ๅฐ heap ไธ_IO_new_file_init_internal
ๆ้ฒ่กstructure ๅๅงๅ
sys_open()
ๅณ้็ไธๅคง็ญ่ณๆ_IO_link_in
ๅฐๆญค structure insert ่ณ _IO_list_all
็้ ญ
_IO_list_all
็บ file_chain (linked list), ๆๅฐๆๆ FILE structure ไธฒ่ตทไพsys_open()
็็ตๆ็บ fd, assign ่ณ _fileno
fread()
ๆ, ๆๅ
vtable->_IO_file_xsgetn
็ file stream ๆฏๅฆ็บ NULL, ่ฅๆฏๅๅฉ็จ vtable->doallocate
allocate ไธๅก memory ็ตฆไปvtable->_IO_file_underflow
ๅผๅซ sys_read()
, ่ฎ _fileno
ๅฐๆ็ๆชๆก็ 1 page ๅฐ stream buffer ไธfread()
็ๅๆธ็ตฆไบๅฐๆๅคงๅฐ็ responsefclose()
ๆๆๅ
ๆๆญค FILE structure ๅพ _IO_list_all
ๆ้ค, ๆธ
็ฉบ (flush and release) file stream ไปฅๅ้้ๆชๆก, ๆๅพ free()
ๆๆญค FILE structure__GI__IO_file_XXX
๏ผๅฐฑๆฏๅฉ็จ vtable ไพๅผๅซ็ function_IO_buf_base
็บ buffer ้ๅง๏ผ้ๅธธๆฒ็นๅฅ่จญๅฎๅฐฑๆๅจ heap ไธ_IO_XXX_ptr or base or end
็ญ็ญๆฏๅจ _IO_new_file_underflow
๏ผไธ้ๅง้คไบ _IO_buf_end
๏ผๅ
ถไปๅ
จ้จ้ฝๆ็ญๆผ _IO_buf_base
read_end
ๆๅ้้ syscall read ่ฎๅฐ็็ตๅฐพread_ptr
ๆ == read_end
ไปฃ่กจ่ฎๅฎไบ_IO_new_file_underflow
)๏ผๅ่ฎไธๆฌก๏ผๆ่ฎๆๅ๏ผไฝๆฏๅชๆ EOF (0xffffffff)๏ผ่ๆญคๆๅฐฑๆ returnfread
fwrite
_IO_buf_base
่ผธๅ
ฅ่ผธๅบ buffer base_IO_buf_end
่ผธๅ
ฅ่ผธๅบ buffer end
_IO_buf
ๅจ _IO_doallocbuf
ๆ่ขซ init_IO_write_base
่ผธๅบ buffer base_IO_write_ptr
่ผธๅบๅทฒไฝฟ็จๅฐ็ไฝ็ฝฎ_IO_write_end
่ผธๅบ buffer end_IO_OVERFLOW
่ฝ fflush ๆๆ็_IO_new_file_overflow
ๅ
ง๏ผๆๅๅงๅๆฒๆ write ptr ็ fp
_IO_setb
ๆฏ็จไพ set buffer
_IO_write_base
== _IO_write_ptr
== _IO_buf_base
ใ_IO_write_end
== _IO_buf_end
_IO_OVERFLOW
flush buffer_IO_cleanup
-> _IO_flush_all_lockp
ๆๆๅฏซๅ_IO_new_file_overflow
๏ผ read ๆฏ call _IO_new_file_underflow
_IO_fread
็ bytes_read = _IO_sgetn (fp, (char *) buf, bytes_requested);
_IO_sgetn
็ return _IO_XSGETN (fp, data, n);
libio/genops.c
_IO_file_xsgetn
ๆ call __underflow()
๏ผๅฆไธ
__underflow
็ return _IO_UNDERFLOW (fp);
_IO_new_file_underflow
็ if (was_writing && _IO_switch_to_get_mode (fp))
Flush unwritten characters.
_IO_switch_to_get_mode
_IO_read_base
่จญ็บ _IO_buf_base
_IO_new_file_underflow
๏ผไธ้ข็จๅผ็ขผๆๆไปฅไธ ptr ้ฝ่จญ็บ buf_base
๏ผไนๅพๅจ call _IO_SYSREAD
_IO_file_read
ไฝ็บ read ็ wrapper๏ผๅๆฌ่ฆๅพ file ไธญ่ฎ 100 bytes (buffer ็้ไฟ) ๅฏซๅฐ buffer๏ผไฝ็พๅจ่ฎๆๅพ stdin ่ฎ (fileno = 0)๏ผๅฏซๅฐๆๅๆๅฎ็ buffer ไฝ็ฝฎ
_IO_fwrite
็ written = _IO_sputn (fp, (const char *) buf, request);
_IO_new_file_xsputn
็ if (_IO_OVERFLOW (f, EOF) == EOF)
_IO_new_file_overflow
็ if (ch == EOF)
f->_IO_write_ptr - f->_IO_write_base
้้็บ่ฆๅ
ๅฏซๅ fd ็ size๏ผ่ๅๅ ็บๆนๅ fd fileno ็บ 1๏ผๆไปฅๆๆๆญค range ๅฐๅฐ 1 (stdout)_IO_new_do_write
็ new_do_write
new_do_write
็ count = _IO_SYSWRITE (fp, data, to_do);
_IO_new_file_write
็ : write (f->_fileno, data, to_do));
ๆ้่ฆ็ๆฏๆญค if condition๏ผๆๅ
ๅคๆทๆๆฒๆ่ณๆ่ฆ flush๏ผไนๅพๅๅไธๆฌก new_do_write()
๏ผ่ฎ็ๆญฃ่ฆ่ฎ็ๆฑ่ฅฟ
https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/fileops.c#L1327
ๆญฃๅธธๆ
ๆณๆ่ท _IO_default_xsputn
๏ผๆ่ผธๅบๅฏซๅฐ buffer ๅ
ง
ไฝฟ็จๅฐ stdout ็ function (puts, fwriteโฆ) ๅฏไปฅ่ฎๆฑ่ฅฟๅฐ buffer (ไปปๆๅฏซ)๏ผไน่ฝๅฐ buffer ไธญ็ๆฑ่ฅฟๅฐๅบไพ (ไปปๆ่ฎ)
ไปปๆ่ฎ็ๆง้ ๅฆไธ๏ผๆงๅถ write / read ไปฅๅ fileno
ไปปๆๅฏซ็ example payload ๅฆไธ๏ผๅฉ็จ _IO_new_file_xsputn
ไธญๅคๆท (f->_IO_write_end > f->_IO_write_ptr)
๏ผ่ฅๆ็ซๅไปฃ่กจ่ผธๅบ buffer (write) ้่ฝๅญๆฑ่ฅฟ๏ผๆไปฅๅ
ๆ่ณๆๅฏซๅฐ่ฃก้ข๏ผไฝๆฏ่ฆๆงๅถๅฅฝ read_end
่ฆ็ญๆผ write_base
๏ผ้ๆจฃ buffer ๆไธๆ้็
่็บไป้บผ่ฆ 0x8000 (_IO_USER_LOCK
)๏ผๅ ็บๅฆๆๆฒๆ _IO_acquire_lock_clear_flags2
๏ผๅ process ๆ้ทๅ
ฅ for loop
https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/libioP.h#L872
https://elixir.bootlin.com/glibc/glibc-2.23/source/libio/libio.h#L448
_IO_new_fclose
call _IO_un_link ((struct _IO_FILE_plus *) fp);
_IO_un_link
_IO_list_all = (struct _IO_FILE_plus *) _IO_list_all->file._chain;
ๅฐฑๆฏๅจ unlink FILE_IO_new_fclose
๏ผไธฆไธ call _IO_file_close_it
้้ไป
_IO_new_fclose
้ๆ call _IO_new_file_close_it
_IO_NO_WRITES
(ไปฃ่กจๆๅฏซ) ็่ฉฑ๏ผๅฐฑๆๅ _IO_do_flush
๏ผๆ่ฆ write ็ๅผๅฏซ? _IO_SYSCLOSE (fp) : 0);
ไพ close fd_IO_setX
ไพๆธ
็ฉบ buffer_IO_un_link
_IO_FINISH
_IO_do_flush (fp);
๏ผไธฆไธ้้ไป_IO_setb (FILE *f, char *b, char *eb, int a)
๏ผset buffer_IO_setg(fp, eb, g, eg)
๏ผset read_XX (g ๆ่ฉฒๆฏๆ gets)
_IO_setp(__fp, __p, __ep)
๏ผset write_XX (p ๆ่ฉฒๆฏๆ print)
ๅๆ็บ
sebvbuf(stdout, 0, 2, 0)
ๆณ่พฆๆณๆ๏ผ
stdout->_flags == 0xfbad1800
0xfbad0000
(file magic)
_IO_NO_WRITES
== 0xfbad0000_IO_CURRENTLY_PUTTING
(0x800) == 0xfbad0800
_IO_IS_APPENDING
(0x1000) == 0xfbad1800
_IO_write_base
&(stdout->_flags)
ไน้ก็ libc ไฝ็ฝฎ (้่ฆๆฏๅๆฌๅฐ๏ผๅ ็บๅๆฌ็ _IO_write_base
== _IO_write_ptr
== _IO_write_end
ไนๅพๅจ puts
ๆๅฐฑๆๅพ &(stdout->_flags)
้ๅงๅด๏ผๅดๅฐ _IO_write_end
ๅๆ็บ
sebvbuf(stdin, 0, 2, 0)
็จ unsorted bin attack ่ๆ _IO_buf_end
๏ผๆญคๆๅๅฏซๅฐฑ่ฝๅพ _IO_buf_ptr
ๅฏซๅฐ _IO_buf_end
๏ผไนๅฐฑๆฏ unsorted bin
(in main_arena)๏ผ้็จไธญๅฐฑๆ __malloc_hook
ๅฏไปฅ่
https://ray-cp.github.io/archivers/IO_FILE_vtable_hajack_and_fsop
https://ray-cp.github.io/archivers/IO_FILE_vtable_check_and_bypass
้้ไฟฎๆน vtable ptr๏ผๆๅๅฏไปฅๆงๅถ็ๅฐๆน๏ผ็ถๅพๅ ็บ vtable ptr ๆๅฐ็ๅฐๆนไนๆฏไธๅ funcion ptr๏ผๆไปฅๆๅๅฏไปฅ้้ไฟฎๆนๅฐๆไฝฟ็จๅฐ็ function ptr ไพ้ฒ่กๆปๆใ (glibc 2.24 ๅ)
glibc 2.24 ๅพๅขๅ ไบไปฅไธ check๏ผไธป่ฆๆฏ check vtable ptr ๆฏไธๆฏๅจ glibc vtable range ไธญ
__start___libc_IO_vtables
็บ vtable range ็้ๅง__stop___libc_IO_vtables
็บ vtable range ็็ตๆhttps://elixir.bootlin.com/glibc/glibc-2.23/source/libio/genops.c#L58
glibc ไธญๆๅ function ๅซ _IO_flush_all_lockp
๏ผไธป่ฆ่ฒ ่ฒฌ flush ๆๆ FILE๏ผๅจ็จๅผ็ตๆไนๅๆ่ขซ call ๅฐ
GLIBC 2.27 the
abort()
function no longer calls_IO_flush_all_lockp()
ๆปๆๆนๆณ็บ๏ผๅฝ้ ไธๅ fake FILE๏ผไธฆๅฐ _IO_list_all
ๆๅฐๆๅ็ fake FILE๏ผๆๅพ็น้ไปฅไธๆชขๆฅ๏ผไฝฟ็จ _IO_OVERFLOW
ไพ control flow
glibc 2.23
่ house of orange ็บไพๅญ๏ผๅฐ_IO_list_all
่ๆ unsorted bin ๅพ๏ผsmallbin 0x60 ็ไฝ็ฝฎๅๅฅฝๆๆฏ_chain
๏ผ่้้ๅจๆญค chunk ๆง้ ๅฅฝ data ่ vtable๏ผๅจไฝฟ็จ vtable_IO_OVERFLOW
ๆๆๆ fp ็ถไฝ็ฌฌไธๅๅๆธๅณๅ ฅ๏ผๅฆๆๆๅๆ_IO_OVERFLOW
็ไฝ็ฝฎๅฏซๆ system๏ผfake chunk fp ไธ้ๅงๅฏซๆ/bin/sh\x00
๏ผ้ๆจฃๅจ call_IO_OVERFLOW
ๆๅฐฑ็ญๆผ่ทsystem(fp)
๏ผgetshell
glibc 2.24 ๅพ
ๅฉ็จ_IO_str_jumps
ๆ_IO_wstr_jumps
๏ผ่ๅ ฉ่ ๅชๅทฎๅจไธๅๆฏ่็ wchar (ๅฏฌๅญๅ )
่ฆๆ็ๅฐๆนๅจ _IO_str_finish
็ดๆฅ call ((_IO_strfile *) fp)->_s._free_buffer
๏ผไธฆๆ fp->_IO_buf_base
็ถไฝๅๆธ
ๅฆๆๆๅ่ฝ่ฎ vtable == _IO_str_jumps-8
๏ผ้ๆจฃๅฐๆๅฐ็ __overflow
offset ๅฐฑๆๆฏ _IO_str_finish
_IO_acquire_lock(fp)
, ๅ ็บ FILE ๅ
งๆ *_lock
, ้ ้ฒ multihread ๆ RC ็ๆ
ๆณ
_IO_FILE_plus.vtable
, ่ฎไปๆๅ system
/bin/sh
_chain
and _IO_list_all
_IO_flush_all_lockp
_IO_OVERFLOW
_IO_list_all
, ๅๆไนๆง้ ๅบ 0x60 ๅคงๅฐ็ chunk ้ฒๅ
ฅ small binmalloc()
ๆ, ไธ็ฎก unsorted bin ๆฏๅฆๆๅๅฅฝๅคงๅฐ็ chunk, ไป้ฝๆ unsorted bin ็ chunk ๅ unlink
_IO_FILE_plus._flags
< 0free()
็ๆ
ๆณไธๆ chunk ๆพๅ
ฅ small bin
malloc()
ๆ, ๆๆๅฐๆญค chunk, ็ผ็พ size ไธๅฐๅพๆๆพๅฐ small bin (0x60)
abort()
-> _IO_flush_all_lockp()
_IO_list_all
- 0x10
_IO_list_all
ๅ ็บ unlink unsorted bin ็ๆฉๅถๆๆๅ unsorted_bin[0] (in main_arena)_IO_list_all
), main_arena ็ smallbin[4]
(chunk size ็บ 0x60) ๆๆๅๆๅ็ chunk, ไธฆๆๆๅ็ chunk ็ถไฝ FILE structure ็
_IO_list_all->_chain
ๅๅฅฝ็บ smallbin[4] (0x60)_IO_write_ptr
> _IO_write_base
_IO_flush_all_lockp()
ๅฝฑ้ฟ
_flags
ๆนๆ '/bin/sh;'system()
system("/bin/sh")
_IO_vtable
section ไนๅ
ง
_dl_open_hook
ไน่ฝ็น, ไฝ่ฝๅฏซๅฐ้, ไนไธๅฟ
่ฆๅฏซๅฐๆญค hook_fileno
to fd of stdout_flag
& ~_IO_NO_WRITES
_flag
|= _O_CURRENTLY_PUTTING
write_base
& write_ptr
to mem you want to read_IO_read_end
== _IO_write_base
_fileno
to fd of stdin_flag
& ~_IO_NO_READS
read_base
== read_ptr
buf_base
& buf_ptr
to mem you want to readbuf_end
- buf_base
> size of fread__malloc_hook
/ __free_hook_
/ __realloc_hook_
_flags
_IO_write_base
ptr_IO_write_base
(่ชคไปฅ็บๆฑ่ฅฟ้ๆฒ่ผธๅบๅฎ)_IO_buf_end
scanf("%d", &var)
read(0, buf_base, sizeof(stdin buffer))
__malloc_hook
_IO_strfile_
== struct{ _IO_streambuf
; _IO_str_fields
}
_IO_str_finish
, _IO_str_jumps
, _IO_wstr_finish
็ญ็ญscanf("%d", &var)
ๅบๅฑคๆ call read(0, buf_base, sizeof(stdin buffer))
_IO_write_base
~ _IO_write_ptr
ๅ
งๅฎน็ๆฑ่ฅฟๆฏ่ฆ่ขซๅฏซๅพ๏ผๅฏซๅปๅช? _fileno
_IO_buf_base
~ _IO_buf_end
ๅ
งๅฎน็ๆฑ่ฅฟๆฏ่ฆ่ขซ่ฎ็๏ผๅพๅช่ฎ? _fileno
(1)ๅฏไปฅๆงๅถ stdout ๆๆฏ stdin ็ญ็ญ fp๏ผๅฏไปฅๅ่ฉฆๆนๅ chain๏ผๆนๆ heap address ไน้กๅฏไปฅๆงๅถ็ๅฐๆน๏ผไธฆๅฝ้ fake _IO_file_plus
็บๅฏไปฅๅจ runtime ไฝฟ็จไธฆ้ๆพ็ memory space, ๆฏ็ฑ low address -> large address
void *sbrk(intptr_t increment)
:
start_brk
็บ heap ่ตทๅง, brk
็บ heap ็ตๆbrk
void brk(void *addr)
:
brk
addressvoid *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
:
mmap(NULL, (size_t)(4*1024+1), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
PS. getchar()
, scanf()
็ญ็ญๆ้ input ็ function ็ๆไฝฟ็จๅฐ heap
The GNU C Library ("glibc", the usual C runtime on Ubuntu) allocates memory for internal use the first time stdout is used for anything
malloc()
:
132KB
็ heap segment(rw), ไธฆๆจ็คบ็บไฝฟ็จ้
, ๆๅ็จฑไน main arenafree()
ๆฏ้ๆพ็ตฆ glibcmalloc()
return ๅไพ็ ptr ๆๅ chunk, ็บ glibc ๅฏฆไฝ memory management ็ data struct (header + data)free()
:
ๅผๅซๆต็จๅฆไธ
malloc()
โโ> __lib_malloc()
โโ> _int_malloc()
free()
โโ> _int_free()
thread arena ๅฏไปฅๆๅคๅ heap, ไธๆฏๅ heap ้ฝๆ่ชๅทฑ็ header, ็จฑไฝ heap_info, ็จไพๆ่ฟฐ heap ็ info
mmap()
or brk()
allocate memory (arena) ็ตฆ heap, ่ๆญคๆ่ฉฒ thread ๆๅคๅ sub-heap (ๅ arena + ๆฐ arena), ็ตๆ heap
mmap()
ๅบ็ heap, ไธ heap2 ็ heap_info->prev
ๆๅฐ heap1 ็ heap_info->ar_ptr
, ่ heap1 ็ heap_info->ar_ptr
ๆๅฐ malloc_state
, ๆญคไฝๆณๆนไพฟๅพ็บ็ฎก็malloc_state ๅฒๅญ arena ็ info, ๅ ๅซ bin, top chunk, last remainder chunk ็ญ็ญ่ณ่จ (arena_header)
mmap()
create็ธๅ index ไธ๏ผๆๅๆไปฅไธ็น้ป๏ผ
mmap
ๆฟๅฐfree()
็ chunk ็บ bin ไธญ็็ฌฌไธๅ freed chunk, ๅ fd == bk == main_arena->bin
, ๅ ็บ main_arena->bin
linked list ็้ ญๅฐพ็บ main_arena->bin
่ชๅทฑ็ chunkmalloc()
ๆๆๆ่ขซๅกซๅ
ฅ
consolidate
, ๆๆธ
็ฉบ unsorted bin, ๅฐๅฐๆๅคงๅฐ็ chunk ๅ้
ๅฐ small & large binglibc >= 2.26 ๅพๅขๅ ็ๆฉๅถ (ubuntu 17.10), ็ฎ็่ฆๆๅ performance
malloc()
ๆฒๆๆชขๆฅ sizemalloc()
0x100 ไปฅไธ็ chunk 7 ๆฌก, ไธฆไธ free 7 ๆฌก่ฎ cache ๅกๆปฟ, ๆๅพๅฐฑๅฏไปฅ้ฒๅ
ฅ unsorted bin ไธฆ leak ๅบ libc addrmalloc()
ๆ่ขซๅๅบ
ๆ heap ็้้ป
ๅ จๅ็บ use-after-free, free ๅฎ ptr ๅพไธฆๆฒๆ
ptr = NULL;
, ๅฐ่ดๅ ptr ไปๆๅฐ heap address, ๅ็จฑ dangling pointer
information leak
็ฑๆผๆขไปถๅคๆท้ฏ่ชค, ๅฐ่ดๅจๅฏซๅ ฅๆ้กๅคๅฏซๅ ฅ 1 byte, ๅฏ่ฝๆฏไปปๆๅญๅ , ๅฏ่ฝๆฏ null, ๅ็จฑ one byte overflow
่่้็ byte, ๅจ heap ๆฎต่ฅๆฏprev_in_use
orprev_size
, ๅๅฏ่ฝๆๅบ็พๆผๆด
strlen(buf)
ๅคๆท้ทๅบฆไธฆ็กๅคๆท \x00
, ่ strcpy(a, buf)
ๆๆๆ \x00
ไนไธ่ตท copy, ๅฐ่ด่ๅฐไธไธๅ bytefor loop
ๅคไธๆฌก, ๅฏซๅฐไธ่ฉฒๅฏซๅ
ฅ็ๅฐๆนไธป่ฆ็บ่ prev_inuse bit
fastbin ๅจๆชขๆฅ double free ๆ, ๅชๆชขๆฅ linked list ็ฌฌไธๅๆฏๅฆ็บ่ฆ free ็ ptr, ๅฏไปฅ็จ
free(A); free(B); free(A);
bypass
malloc()
ๅๅพ่ฉฒไฝๅ็ ptr, ้้่ฎๅฏซๅ exploit
__malloc_hook
- 0x23 (0x13 for padding)malloc_consolidate()
, ๅฐ fastbin ๅฏไปฅ merge ็ merge ๅพๅๆพๅ
ฅ unsorted bin, ไธ่ฝ merge ็็ดๆฅๆพๅ
ฅ, ๆไปฅๅฏไปฅ bypass double freeไฝๆฏ็ฌฌไธๆฌก malloc()
ๆฟๅบ็ fake_chunk ๅฟ
้ ็ฌฆๅ fastbin check, chunk_size ้็ธ็ฌฆ
key (chunk+0x18) == tcache
้ๅๆปๆๆๆณๅฟ ้ ่ฆ่ฝๅจ stack ็ไธๆพ ROP chain ็ heap address๏ผ็ถๅพ่ฝๅฏซๅฐ realloc_hook ่ท malloc_hook
ๅ ็บ malloc ่ท realloc ๅจๅท่กๅ้ฝๆ push + sub rsp๏ผๆไปฅๅพๆๅฏ่ฝๆๆ heap address ๅ ๅจ function frame ไนไธญ๏ผ่ๆญคๆๅ่จญ่ท malloc๏ผ่ๆญคๆๅๅฅฝ rsp ๆพ่ heap address๏ผๆๅๅฏไปฅๆนๅฏซ malloc_hook ๆ realloc+6๏ผrealloc_hook ๆ pop rsp ; ret๏ผ้ๆจฃ malloc ๅจ call malloc_hook ๆ๏ผๆ call realloc+6๏ผ+6 ่ท + 0 ็ธๅทฎๆๅฐ push ไธๅ register๏ผ่ realloc ๅจ call realloc_hook ๆ๏ผๆๆ rsp ไธ็ value pop ๅๅป๏ผไฝๆฏๅ ็บไธ้ๅงๅฐ push ไธๅ๏ผๆไปฅๆๅพๅจ jmp ๅฐ pop rsp ; ret ๆ๏ผrsp ๅๅฅฝๆๆพ heap address๏ผๆๅพ้้ pop rsp ; ret ๅฐฑ่ฝ่ท ROP
่ๆญค็จฎๆปๆๆนๆณไน่ฝ็จๅจ one_gadget๏ผๅฏไปฅๆงๅถ่ฎ rsp ็ฌฆๅ one_gadget ็ๆขไปถ
__free_hook
__malloc_hook
printf("%10000c")
)__realloc_hook
printf()
) => buffer ไธๅค ๅคง => malloc()
=> malloc_hook__libc_start_main
ๅบๅฑคmalloc()
malloc - 0x10
calloc
skip ๆๆ่ tcache ็ธ้็ๆฑ่ฅฟ__free_hook
__malloc_hook
__realloc_hook
__free_hook
็บไพ, ่ฅ chunk (ptr) ๆพ็ๆฑ่ฅฟๆฏ '/bin/sh', ไธ __free_hook
ๅ
งๆพ system addr, ๅจ call free(ptr)
ๆๅ็่ก็บๅฐฑๆฏ system(ptr)
, ไนๅฐฑๆฏ system("/bim/sh")
malloc_hook
ไธ้ข (+ 0x10)๏ผๅ ๆญคๅฆๆ malloc_hook ๅฏซ one_gadget ้ฝๆฒ็จ็่ฉฑ๏ผๅฏไปฅๅ่ฉฆๅฏซ top_chunk
free_hook
ไธ้ข็ๅฐๆน๏ผไนๅพๅฐ top chunk ๅฏซๆๆญค chunkfree_hook
๏ผๅฏซ free_hookmp_.mmap_threshold
(0x20000) ๆ๏ผๆไฝฟ็จ mmap
้ไธๅๆฐ็ memory space ็ตฆ user๏ผ่้ๅไฝ็ฝฎ่ libc ็ offset ๆฏๅบๅฎ็๏ผ่ๆญคๅฏไปฅ leak libctop chunk
๏ผ่ฎ libc ๅจๅคๆทไธไบๆขไปถๅพ๏ผๆ top chunk ไธๅฐ unsorted bin ไธญ(old_top == initial_top (av) && old_size == 0)
๏ผไธ็ขบๅฎ้ๆฎตๆไธๆ้๏ผinitial_top ไผผไนๆฒๆณ็น๏ผ(unsigned long) (old_size) >= MINSIZE
๏ผold_size ๆฏๆ top chunk ็ size๏ผ่ณๅฐ่ฆ >= 0x20prev_inuse (old_top)
๏ผprev_inuse bit ่ฆ่จญ(unsigned long) old_end & (pagesize - 1)) == 0)
๏ผtop chunk ๅ ๅฎ size ๅพ็ address ้่ฆ่ page ๅฐ้ฝๅพ unsorted bin ๅ chunk ๅบไพๆ๏ผๅฉไธ็ chunk (last remainder) fd bk ๆๆๅ main_arena + 0x58๏ผ่ๆฟๅฐ็ chunk ้ฝๆๆฎ็ๅๆฌ chunk size ๅฐๆๅฐ็ large/small bin ็ address (in libc)
http://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Code-Gen-Options.html#Code-Gen-Options
https://stackoverflow.com/questions/2463150/what-is-the-fpie-option-for-position-independent-executables-in-gcc-and-ld
็ถ resulting file call shared object ็ function ๆ, ๆๆๅป so ๅ ง้จๆพ function ๅฏฆ้ไฝ็ฝฎไธฆๅฏซๅ ฅ .got.plt (global offset table), ไปฅๆธๅฐ initial ๅคง้ๅปถ้ฒ(ไธ้ๅงๅฐฑ่ฆๅ จๆพ) ไปฅๅ็ฏ็ๆ้(ๆไบๆ นๆฌไธๆ่ขซ call ๅฐ)
jmp
่ณ func@plt, ่ func@plt ็ฌฌไธ่กๅณๆฏ jmp
่ณ func@got ๅ
งๅฏซ็ๅผ, ่ๆญคๆ็บ func@plt+6. ่ๅพ func@plt+6 ๆๅท่ก push
ไปฅๅ jmp
, ่ฉฒ push
็ๅผ็บrel_offset(.rel.plt ็ index), jmp
ๅฐ plt[0]0x8048380
, ไนๅฐฑๆฏ plt[0] ๆๅ push
ไธฆ jmp
, ๆญคๆ push
็ๅผ็บ *link_map, jmp
ๅฐ lib ไธญ็ <dl_runtime_resolve> (.got.plt ็ ็ฌฌไธ้
, 0x8040a000(.got.plt) + 8) function ็ไฝ็ฝฎ้ฒ่ก binding._dl_runtime_resolve(*link_map, rel_offset)
_dl_runtime_resolve
ๆๆ นๆไปฅไธ pseudo code lookup function, ๆๅ่ฆๅ็ไบๆ
ๅฐฑๆฏ:
trace
_dl_runtime_resolve->_dl_fixup->_dl_lookup_symbol_x->do_lookup_x->check_match
_dl_runtime_resolve
:_dl_fixup
_dl_fix_up
:link_map
ไปฅๅ reloc_arg
็ถๅๆธ, ๅ่
็บๅฎๅผ .plt[1], ๅพ่
็บ function ๅจ .rel.plt ็ offset_dl_lookup_symbol_x
elf_machine_fixup_plt
ๅฐ got ๅฏซๅ
ฅ real function address_dl_lookup_symbol_x
:
do_lookup_x
ๆพๅฐ symboldo_lookup_x
:
check_match
:
strcmp
ๆฏๅฐ symbol name, ๅๆฏๅฐ versionref: https://quentinmeffre.fr/pwn/2017/01/26/ret_to_stack.html
่ไพไพ่ชช
__stack_prot
_dl_make_stack_executable
_dl_make_stack_executable(void* address)
__libc_stack_end
address ็ถไฝๅๆธ__mprotect
, ไฝฟๅ
ถๆ นๆ __stack_prot
mprotect(const void *start, size_t len, int prot);
https://code.woboq.org/userspace/glibc/csu/elf-init.c.html#__libc_csu_init
__libc_csu_init
ๆฏ __libc_start_main
ๆๆไฝฟ็จๅฐไพๅๅงๅ็ function๏ผไฝๆฏๅจๆไบๆขไปถไธ๏ผไป่ฝๅค ๅๅฐ "ๆงๅถ register ๅผ" + "call function" + ๅจ stack ็ไธๆฑ่ฅฟ๏ผไธฆไธๆฒๆๅฏไฝ็จ
่ๆๅ้่ฆ็้จๅๅๆๅ ฉๅก๏ผ
(r12)(rdi, rsi, rdx)
ๅจ่จญ rbx = 0
and rbp = 1
็ๆ
ๆณไธ๏ผcmp rbx, rbp
ๆ็ดๆฅ้้
้ๆ็็ๅพๅผท (X
้ๆฏ่ผ่ฆ่ฌ็ๆฏไธๅๆฆๅฟต
push ๆๆๆๅผๅพ regsiter ๅฏซๅฐ stack ไธ๏ผpop ๆๆๆๅผๅพ stack ๆฟๅฐ regsiter ๅ ง๏ผ้ไธๅฎๅคงๅฎถ้ฝ็ฅ้๏ผไฝๆฏ้้ๆไธๅ้้ต๏ผpush ๆๆๅผๆดๆ๏ผไฝๆฏ pop ๆฟไบๅผไธๆๆธ ้ถ
ๅ ๆญคๅท่กๅฎ libc function ๅพ๏ผๅพๅฎนๆๅจ stack ๆฎ็ libc address๏ผไฝๆฏ่ฆๅฐๅฟ rsp
่ rbp
็ๅผ๏ผๅ ็บ libc function ๅฏ่ฝ้ฝๆไฝฟ็จๅฐ๏ผๅฆๆ้ขๅคช่ฟๆๆฏๆๆจฃ๏ผๅฏ่ฝๆๆๅผ่ขซ่ๆ็็ๆ
ฎ๏ผๅ ๆญค่ฆๅค่งๅฏใๅค try
gets
ๅฉ็จไธๆทๅจ gets + stack pivoting๏ผไฝฟ็ไธ็ _IO_file_write
address ๅฏไปฅๆฎ็ๅจๆๅๆงๅถๅฐ็ๅฐๆน (bss ็ญ็ญ)๏ผไธฆ่่ __libc_csu_init
๏ผๆงๅถ rdi, rsi, rdx, r12๏ผๅท่ก _IO_new_file_write(stdin, got, 8)
https://github.com/lattera/glibc/blob/master/libio/fileops.c#L1180
็ๅพๅบไพๆญค function ๅช้่ฆ็จๅฐ fileno๏ผๅ ๆญค stdin [:0x70] ๅฏไปฅไปปๆๅผ๏ผๅช่ฆ *(fake_stdin+0x70)
็บ 1 ๅฐฑๅฅฝ
https://binpwn.com/papers/control-flow-enforcement-technology-preview.pdf
https://www.linuxplumbersconf.org/event/2/contributions/147/attachments/72/83/CET-LPC-2018.pdf
https://software.intel.com/content/www/us/en/develop/articles/emulating-applications-with-intel-sde-and-control-flow-enforcement-technology.html
CET
./sde -cet -- application
jmp rax
endbr mark valid jump target addresses of indirect calls and jumps in the program
gcc -fcf-protection ...
๏ผไฝไธ็ฅ้ๆๆจฃๆ็ฎๆๆ (?__unwind {
็ prefixsde -debug -- yourapp
__libc_stack_end
ๅฏไปฅ leak ๅบ stack ็ไฝ็ฝฎscanf("%u")
ๆๅฏไปฅ่ผธๅ
ฅ +
, -
็นๆฎ็ฌฆ่ไพ pass ้ๆฌก็่ฎๅ
+ -
๏ผpass ้ๆฌกsearch -8 <canary>
check_inuse_chunk
macro ๅชๆๅจ MALLOC_DEBUG == 1 ๆๆๅไบ๏ผ่ๅนณๅธธๅจๆชขๆฅ inuse ็้ฝๆๆฏ inuse
macrohttps://www.youtube.com/watch?v=1eF7fMdVoOA&ab_channel=jwang
https://www.youtube.com/watch?v=emt1yf2Fg9g&ab_channel=BlackHat
https://saelo.github.io/presentations/blackhat_us_18_attacking_client_side_jit_compilers.pdf
https://medium.com/walkout/ๆทบๆทก-js-engine-ๆฉๅถ-77391b4dd3db
add(a: smi, b: smi)
if (check int fail) {jmp bailout}
exit()
vs atexit()
๏ผ
int on_exit(void (*function)(int , void *), void *arg);
int atexit(void (*function)(void));
__libc_start_main -> main function -> __run_exit_handler -> dl-fini
ๅ
ถไธญๅจ __libc_start_main
ไธญ๏ผๆไฝฟ็จ atexit()
่จปๅไธไบ exit function
่ __cxa_atexit()
ๅ่ท atexit()
ๅทฎๅจๅช่ฃกๅข๏ผ
__cxa_atexit()
is not limited to 32 functions.__cxa_atexit()
will call the destructor of the static of a dynamic library when this dynamic library is unloaded before the program exits.control flow
exit()
ๆๅผๅซ __run_exit_handler()
__run_exit_handler()
(struct exit_function_list *) 0x7f1ff52229a0 <initial>
PTR_DEMANGLE
: ror 0x11 ; xor qword ptr fs:[0x30]
_dl_fini
๏ผไธป่ฆๅ็ไบๆ
ๆฏ "call the destructors for all still loaded objects"_DYNAMIC
๏ผl_ld
ๆๆๅ _DYNAMIC
section๏ผไธฆไธๆๅผๅซ l_ld->l_info[DT_FINI_ARRAY]
๏ผๆๅพ _DYNAMIC
ๆพๅฐ fini_array ็ไฝ็ฝฎ
#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */
(Elf64_Dyn *) 0x403e70
(ELF_base + fini)[0]
ไพๅผๅซ fini function
__do_global_dtors_aux
__do_global_ctors_aux
and __do_global_dtors_aux
for calling the constructors and destructors of these static objects__do_global_dtors_aux
๏ผๆๅผๅซๅฐ __cxa_finalize()
ไพๅท่ก็จ atexit()
่จปๅ็ function (glibc ไธ้ขๆฏ้ๆจฃๅฏซๆฒ้ฏ๏ผไฝๆฏๅฏฆ้ไธๅฅฝๅๆฏ _run_exit_handler
)execvpe
ๆพRUN_HOOK (__libc_atexit, ());
ๆๅผๅซ __elf_set___libc_atexit_element__IO_cleanup__
็ _IO_cleanup
__elf_set___libc_atexit_element__IO_cleanup__
ๅฏๅฏซ๏ผๆไปฅไน่ฝๅฐ one_gadget ๅฏซๅจ้vector => [abi:cxx11]
็ฌฌไธๅ element => vector member (data ptr, data_size)๏ผไธ vector ไธ้ๅง็ size ็บ 0x80 (4 elements)
ๅ่ๆ็ซ ๏ผhttps://medium.com/@ktecv2000/่่glibc-2-32-mallocๆฐๅข็ไฟ่ญทๆฉๅถ-safe-linking-9fb763466773
้ๅฐ tcache ่ fastbin ็ next pointer ๅ XOR ็ไฟ่ญท
ๆไปฅๅจ heap ไธๅๆปๆๆ๏ผๆ่ฉฒ่ฆๅ leak heap address๏ผๆ่ฝ้้ tcache ่ fastbin ไธญ address ็ๆชขๆฅ