Pwn题目本地调试加载libc版本
Time: 2020-03-10 Tags: binaryLink: Pwn题目本地调试加载libc版本
接上文
上文,我有幸和Blue-Whale的前辈们参加XCTF高校战“疫”网络安全分享赛。赛后回顾学习,用AiDai的exp本地调easyheap的时候出现了一点小插曲,让我陷入了深深的沉思。
原因在于题目使用的libc为2.23,而我的ubuntu18.04使用的libc为2.27。因此申请fastbin的时候变成了tcache,导致fd的指向不同。俺还得把padding去掉才能打得通。我想不能还原比赛环境有点遗憾,于是就搞了搞libc加载的配置,记录踩坑。
Method 1
原理
ELF文件在生成之后会把动态链接器和libc写死到ELF文件中。我们只要把ld改掉就可以将ELF文件链接到其他的libc,进而加载不同的libc。
坑1——gcc和g++切换
一会儿我们要进行编译glibc,而降版本的glibc最好用降版本的gcc编译,所以要事先配置好gcc和g++。
在ubuntu18.04上加上16的镜像源。sudo vim /etc/apt/source.list
。配置ubuntu16.04的时候也得放这个源,不然就会被直接更新到18.04.
deb http://mirrors.aliyun.com/ubuntu/ xenial main
deb http://mirrors.aliyun.com/ubuntu/ xenial universe
然后sudo apt update
。接下来安装gcc-4和gcc-5
sudo apt-get install gcc-4.8
sudo apt-get install gcc-4.8-multilib
sudo apt-get install g++-4.8
sudo apt-get install g++-4.8-multilib
sudo apt-get install gcc-5
sudo apt-get install gcc-5-multilib
sudo apt-get install g++-5
sudo apt-get install g++-5-multilib
然后加到update-alternatives中:
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.8 40 --slave /usr/bin/g++ g++ /usr/bin/g++-4.8
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 50 --slave /usr/bin/g++ g++ /usr/bin/g++-5
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 70 --slave /usr/bin/g++ g++ /usr/bin/g++-7
用这个命令切换gcc版本。
sudo update-alternatives --config gcc
坑2——下载编译个版本glibc
glibc-all-in-one里面有各个版本的glibc,可以用一下。
git clone https://github.com/matrix1001/glibc-all-in-one
cd glibc-all-in-one
chmod 777 build download extract
sudo ./build 2.29 amd64 #编译glibc
sudo ./build 2.27 amd64
sudo ./build 2.23 amd64
后来发现2.23不能正常编译,俺也不知道啥原因。换了gcc也不行,只好手动编译。如果可以编译就直接忽略。
tar -zxvf glibc-2.23.tar.gz
cd glibc-2.23
mkdir build
cd build
sudo ../configure --profix=/glibc/2.23
sudo make
sudo make install
2.27和2.29的ld和libc在/glibc/2.2?/amd64中,2.23在/glibc/2.23/lib/中。
指定libc
两种方法。一种方法是用patchelf改变elf的ld链接器和libc加载。另一种是跑了个脚本,其实原理都差不多。
先把libc和ld链接器cp到pwn题目录,用绝对路径不嫌麻烦也行。
patchelf:
patchelf --set-interpreter ./ld.so.2 ./elfname
patchelf --set-rpath ./libc-2.2?.so ./elfname
脚本:
#coding:utf-8
from pwn import *
import os
def change_ld(binary, ld):
if not os.access(ld, os.R_OK):
log.failure("Invalid path {} to ld".format(ld))
return None
if not os.access(binary, os.R_OK):
log.failure("Invalid path {} to binary".format(binary))
return None
binary = ELF(binary)
path = './{}_{}'.format(os.path.basename(binary.path), ld.split('.')[-2])
if os.access(path, os.F_OK):
os.remove(path)
print("remove exist file.....")
return ELF(path)
for segment in binary.segments:
if segment.header['p_type'] == 'PT_INTERP':
size = segment.header['p_memsz']
addr = segment.header['p_paddr']
data = segment.data()
if size <= len(ld):
log.failure("Failed to change PT_INTERP from {} to {}".
format(data, ld))
return None
binary.write(addr, ld.ljust(size, '\x00'))
break
binary.save(path)
os.chmod(path, 0b111000000) #rwx------
success("PT_INTERP has changed from {} to {}. Using temp file {}".format(data, ld, path))
return ELF(path)
elf = change_ld('./easyheapx', 'ld-2.23.so')
gdb调试pwn题
以前我都是gdb.attach(io)之后没加pause,结果脚本直接跑完了gdb显示程序未运行。所以长记性了,调试=下断点+观察,先gdbattach再加一个pause,调试完了在原窗口回车继续跑脚本。
Method 2
经过很多测试之后,发现在 libc2.27 环境下通过 env={"LD_PRELOAD":"./libc-2.23.so"}
加载 libc2.23 会失败,因此需要一个方案进行解决。
此内容已经移动至 ubuntu配置记录——pwn环境搭建的个人最优解