有关特定 glibc 版本的漏洞经常出现在 pwn 题里,之前是丢在自己个人笔记里面需要的时候再拿出来看,但是现在用的越来越多了,所以就写一篇博客好好讲讲该如何替换软件的 glibc 版本。
glibc & ld
glibc 全称叫 GNU C Library,是 GNU 项目实现的 C 语言标准库,其可以说是 Linux 内核系统调用的封装器,提供了大量的 API。glibc 在不断迭代更新,直到现在,glibc 最新版已经到了 2.42。很多漏洞都需要在特定的 glibc 版本上才能复现。
使用 ldd 可以输出指定程序需要的共享库,对于比较简单的 c 程序而言输出大致如下:
|
|
撇去关于内核的 linux-vdso 不看,就剩下 libc 和 ld 两个动态链接库了。可以说,在程序启动运行过程,glibc 和 ld 密切相关,ld.so 是 glibc 的一部分,其通常需要特定版本的 glibc,ld 负责加载正确版本的 glibc 和其他的共享库。
匹配正确 glibc 版本
通常,pwn 题目的附件只提供了 glibc.so.6,而不会提供对应的 ld,如果直接替换 glibc 程序就启动不了。首先需要去寻找相应 glibc 版本的 ld,一般使用 matrix1001/glibc-all-in-one 项目来下载。
首先使用 strings 搜索 glibc 有关版本号的字符段:
通常会有 GLIBC 的字段,一般如果是 ubuntu 发行版还会带有 Ubuntu,总的来说是相似的。
|
|
除此之外还需要获得程序的 CPU 位数,使用 file 或者 checksec 就能得到。
上图可知该 glibc 是 32 位的 ubuntu glibc 2.23,然后到 glibc-all-in-one 去下载对应 ld。
glibc-all-in-one 分为 update_list 、download 等小脚本。运行 update_list 后就会更新 list 和 old_list 下的 glibc 版本列表。
比如在 list 下通过 vim 的搜索功能找到对应版本: 2.23-0ubuntu11.3_i386
然后运行
|
|
就会开始从清华大学开源镜像站下载对应版本 glibc 相关文件,链接库放在 libs 文件夹内。
文件大致有这些:
ld 选择 ld-linux.so.2 复制到和攻击目标相同文件夹下就好了。
替换 ld
|
|
使用 patchelf 的 --set-interpreter 参数替换链接器。
–set-interpreter 用于更改传递给可执行文件的动态加载器。
替换后目标程序是无法运行的:
|
|
提示缺少相应版本的 libc.so.6,需要注意的是这里的 libc.so.6 名字要对。
替换 glibc
|
|
使用 –replace-needed 将已声明对动态库的依赖项替换为另一个动态库:
|
|
替换完成之后程序可以正常运行,再输入 ldd 查看可以发现这两个动态链接库都已经被替换为同目录下的相应文件:
|
|
不过我也不知道为什么 linux-vdso 变成了 linux-gate?
其他
patchelf 的用法还有很多,以上只是冰山一角。
其的 man pages 的 NAME 描写得很简洁:
patchelf - Modify ELF files
修改 ELF 文件,就说明了它的用处可不止是替换,而是修改。
它还可以修改程序运行时连接器查找动态库的搜索路径(rpath),输出一堆有关链接的信息。
显然,链接是一门很深的学问,而我才触碰了这一冰山一角。在查询有关 ld 的文档时,也发现了好多不太相关但有意思的博客,放在这里:
- Yizhou Shan Dynamic Linking¶
- Linux x86 Program Start Up or - How the heck do we get to main()? by Patrick Horgan