原文来自SecIN社区—作者:tower
说明
在ctf中共享库的问题一直是一个非常非常非常恶心的问题,这里将会介绍动态链接库的命名以及如何修改一个程序依赖的动态链接库
命名
0x7f387805e000 0x7f3878091000 r-xp 33000 0 /lib/x86_64-linux-gnu/libseccomp.so.2.4.3
- lib表示库文件
- so表示动态链接
- 2.4.3中2表示主本号,4表示次版本,3表示发布版本
主版本是重大升级,不会向上兼容(如python2和3)
次版本是增量升级,添加一些新的接口
发布版本是进行一些错误修正,性能改进等
SO-NAME命名与软链接
用SO-NAME机制来记录共享库的依赖关系
每个库都有自己的SO-NAME知名库名字和主版本号如libc.so.2
SO-NAME相同的两个次版本号不同的库,次版本号大的兼容小的
在linux系统中,系统会为每个共享库在它所在的目录创建一个跟SO-NAME相同并且指向它的软连接如系统中存在共享库/lib/libfoo.so.2.6.1
那么linux共享库管理程序会为它创建一个指向它的软链接/lib/libfoo.so.2
也有一些不标准的命名,如libc.so.6 还有ld-2.6.1.so被命名为ld-linux.so
SO-NAME的好处
- 每个最新的库覆盖掉旧的,保持在主版本号内最新
- 可执行文件pwn如果依赖于libc.2.23.so会在.dynamic段DT_NEED字段写入libc.so.6写入SO-NAME可以避免库更新之后不能运行
编译时链接
在运行时需要链接一个程序可以加 -lxxx
,如-lpthread
编译器会根据当前环境在系统中的相关路径查找最新版本的库一般用-L
指定搜索路径
对于链接方式分为动态和静态链接在ld使用-static
选项时会搜索静态链接库(如libc.a.2.23)默认是动态
共享库的路径
- /lib存放系统最关键的共享库,一般是/bin,/sbin目录下程序需要的库还有系统启动时需要的库
- /usr/lib开发时可能用到的库
- /usr/local/lib跟操作系统本身不十分相关的库,一些第三方软件安装的库
共享库查找过程
如果DT_NEED存放绝对路径就从这个路径找
如果没有会在/lib,/usr/lib由 /etc/ld.so.conf配置文件指定的目录中查找共享库。
为了加快遍历速度,有一个叫ldconfig的程序各个共享库创建删除或更新相应的SO-NAME,把这些SO-NAME集中起来放到/etc/ld.so.cache
所以正常更新或替换共享库需要运行一次ldconfig
共享库创建和安装
编译
-share
选项表示输出结果是共享库类型
-Wl
选项可以传递给连接器选项 -Wl,-soname,my_soname
可以指定SO-NAME(如果不指定这个库就没有soname,用ldconfig也没用)
安装
包含两步1.创建SO-NAME软连接。 2.告诉编译器和程序如何查找共享库
- 把共享库复制到/lib或者/usr/lib等然后运行ldconfig
ldconfig -n shared_library_directory
在编译程序时提供-L
和-l
LD_LIBRARY_PATH也可以用来指定
共享库替换
修改环境变量替换
LD_LIBRARY_PATH临时改变某个应用程序的共享库查找路径而不会影响系统中其他程序
类似于/lib/ld-linux.so.2 -binary-path /home/user /bin/ls
。
LD_PRELOAD优先级更高,无论程序是否依赖动态库,被指定的动态库都会被加载
系统将会从下面搜索库
- LD_PRELOAD
- LD_LIBRARY_PATH指定的路径
- /etc/ld.so.cache指定的路径
- 默认共享目录,/usr/lib /lib等
修改二进制文件更改libc版本
有一个项目可以下载很多版本的libc
https://github.com/matrix1001/glibc-all-in-one
➜ git clone https://github.com/matrix1001/glibc-all-in-one.git
➜ glibc-all-in-one ./update_list
➜ glibc-all-in-one cat list 可以看到获取到的库名字
➜ glibc-all-in-one ./download 2.23-0ubuntu10_i386 后面的库名字是上一条命令看到的任意一个
然后需要创建目录,拷贝文件到对应目录
➜ glibc-all-in-one sduo mkdir -p /glibc/2.27/64/lib/
➜ glibc-all-in-one sudo cp ./libs/2.27-3ubuntu1.2_amd64/* /glibc/2.27/64/lib
为了完成上面的操作写了一个简单的脚本,完成更新列表,下载库,拷贝的过程
import os
def download(LibcNameList):
for item in LibcNameList:
os.system("./download {}".format(item))
def mkDir(name):
for item in name:
os.system("sudo mkdir -p /glibc/{}/64/lib/".format(item))
os.system("sudo mkdir -p /glibc/{}/32/lib/".format(item))
def getName(LibcNameList):
name=[]
for item in LibcNameList:
if item.split("-")[0] in name or item.split("-")[0]=="" :
continue
else:
name.append(item.split("-")[0])
print("name:",name)
return name
os.system("./update_list")
f=open("./list","r")
content=f.read()
print(content)
LibcNameList=content.split("\n")
name=getName(LibcNameList)
mkDir(name)
download(LibcNameList)
for LibcName in LibcNameList:
for item in name:
if (item in LibcName) and ("amd64" in LibcName):
os.system("sudo cp ./libs/{}/* /glibc/{}/64/lib/".format(LibcName,item))
if (item in LibcName) and ("i386" in LibcName):
os.system("sudo cp ./libs/{}/* /glibc/{}/32/lib/".format(LibcName,item))
修改二进制文件clibc
clibc
#!/bin/bash
FILE_NAME=$1
LIBC_VERSION=$2
WORKDIR=$(pwd)
LIBC_DIR=/glibc
LIBC_DIR=$(find $LIBC_DIR -name "$LIBC_VERSION*")
if [ "$LIBC_DIR" = "" ];then
echo "Not support version or your $LIBC_DIR don't have libc"
exit
fi
EBIT=$(file $FILE_NAME |awk '{print$3}'|cut -c 1-2)
if [ $EBIT -eq "32" ];then
libc_dir=$LIBC_DIR/32/lib
elif [ $EBIT -eq "64" ];then
libc_dir=$LIBC_DIR/64/lib
else
echo "It's not a elf file"
exit
fi
if [ "$3" ]
then
patchelf --set-interpreter $libc_dir/ld-$LIBC_VERSION.so --set-rpath $WORKDIR/ $1
else
patchelf --set-interpreter $libc_dir/ld-$LIBC_VERSION.so --set-rpath $libc_dir/ $1
fi
echo "success!!!"
clibc filename 2.23
测试结果
上图可以看到用clibc修改之后(左图)和Ubuntu16自带的libc2.23加载空间没有什么区别
注:图中看到的ld-2.23.so是链接器
项目地址
https://github.com/tower111/pwn-change-libc
来源:freebuf.com 2020-12-16 11:12:04 by: SecIN技术社区
请登录后发表评论
注册