x86架构下pwn题目libc版本概述
Time: 2021-05-11 Tags: binaryLink: x86架构下pwn题目libc版本概述
鉴于最近关于 libc 的讨论又开始兴起,在此稍作总结。
关于什么是 libc :GNU C库
libc版本
如何查看
一种方法是直接运行 libc。
$ /lib/x86_64-linux-gnu/libc-2.27.so
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.4) stable release version 2.27.
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 7.5.0.
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/glibc/+bugs>.
另一种是 strings 加 grep
$ strings /lib/x86_64-linux-gnu/libc-2.27.so | grep GNU
GNU C Library (Ubuntu GLIBC 2.27-3ubuntu1.4) stable release version 2.27.
Compiled by GNU CC version 7.5.0.
大版本
在一般情况下,主流的 ubuntu 运行的 libc 大版本如下:(截至2021-05-13)
ubuntu版本 | libc版本 |
---|---|
ubuntu 16.04 LTS | 2.23-0ubuntu |
ubuntu 18.04 LTS | 2.27-3ubuntu |
ubuntu 20.04 LTS | 2.31-0ubuntu |
小版本
pwn 题目的搭建一般使用 docker。大多数出题人会在 Dockerfile 里面这么写:
RUN apt-get update && apt-get -y dist-upgrade
此外还可能安装一些 xinetd
之类的东西。这些操作都有可能将 libc 升级到目前大版本的最高小版本:
例如,截至 2021-05-13 版本如下:
ubuntu16.04 : 2.23-0ubuntu11.2
ubuntu18.04: 2.27-3ubuntu1.4
ubuntu20.04: 2.31-0ubuntu9.2
因此,常用版本一般是这三个。
我们可以在以下几个网站获取二进制文件:
https://pkgs.org/download/libc6
也可以获取源码:
https://ftp.gnu.org/gnu/glibc/
下面对它们的特点进行概述:
2.23-0ubuntu11.2
因为 ubuntu 16.04 已经于 2021-04-30 停止支持,此 libc 成为 ubuntu16 libc-2.23.so 的最后一个版本,也是 ubuntu系统 heap 中没有 tcache 的最后一个版本。可利用的漏洞较后两个版本来说更多一些。
2.27-3ubuntu1.4
加入了 tcache。由于 tcache double free 的严重性,2.27-3ubuntu1.3 中加入了 key 机制。本版本保留了 2.27-3ubuntu1.3 加入的 key 机制。
所以想体验原生 tcache double free 的同学可以找到 1.2(版本名称 ubuntu 后面的数字,下同) 以及之前的 libc 进行体验。
这里给出一种不怎么好但是简单的解决方案:
sudo apt install libc6=2.27-3ubuntu1.2
sudo apt install libc-dev-bin=2.27-3ubuntu1.2
sudo apt install libc6-dev=2.27-3ubuntu1.2
sudo apt install libc6-dbg=2.27-3ubuntu1.2
小tips:buuoj 的 ubuntu18 靶机中使用的 libc 版本是 2.27-3ubuntu1
另外,2.27 大版本下可能用到的其他小版本,有2.27-3ubuntu1
、2.27-3ubuntu1.2
、2.27-3ubuntu1.3
。
2.31-0ubuntu9.2
在 tcache_entry 中 加入了 key。对 unsorted bin 检查巨多。极大地限制了堆块的利用。另外还能在 gcc 编译选项里面加入 cet 或者 🐏️ switch。很蛋疼。
cet(ida 7.5以下版本 PLT 解析失败)解决指路:M4tsuri/ida_fxxk_cet
另外,2.31 大版本下可能用到的其他小版本,有2.31-0ubuntu9
、2.31-0ubuntu9.1
。
所以,需要准备的 libc 并不多,准备个 8 个左右就够应付比赛了。
libc加载
当一个动态链接的程序运行起来之后,libc 就会通过链接器 (ld) 加载到内存中,此时用户编写的二进制程序便能够调用其中的函数。在本地调试 pwn 题目时,默认加载的 libc 是本地的 libc。32 位 libc 目录为: /lib/i386-linux-gnu/libc.so.6
或者 /lib32/libc.so.6
,64 位 libc 目录为 /lib/x86_64-linux-gnu/libc.so.6
。而 libc.so.6
是一个指向 ./libc-xxx.so
的一个软链接。
lrwxrwxrwx 1 root root 12 Dec 8 00:38 libc.so.6 -> libc-2.27.so
所以
即使本地 exp 中写了
libc = ELF("./libc-xxx.so")
也是默认使用本机的 libc。验证如下(使用 libc = ELF("./libc-xxx.so")
写脚本,之后进行 gdb attach
):
pwndbg> vmmap
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
...
0x562ccc0dc000 0x562ccc0fd000 rw-p 21000 0 [heap]
0x7f6ee40a3000 0x7f6ee428a000 r-xp 1e7000 0 /lib/x86_64-linux-gnu/libc-2.27.so
0x7f6ee428a000 0x7f6ee448a000 ---p 200000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7f6ee448a000 0x7f6ee448e000 r--p 4000 1e7000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7f6ee448e000 0x7f6ee4490000 rw-p 2000 1eb000 /lib/x86_64-linux-gnu/libc-2.27.so
0x7f6ee4490000 0x7f6ee4494000 rw-p 4000 0
0x7f6ee4494000 0x7f6ee44bd000 r-xp 29000 0 /lib/x86_64-linux-gnu/ld-2.27.so
0x7f6ee46a0000 0x7f6ee46a2000 rw-p 2000 0
0x7f6ee46bd000 0x7f6ee46be000 r--p 1000 29000 /lib/x86_64-linux-gnu/ld-2.27.so
0x7f6ee46be000 0x7f6ee46bf000 rw-p 1000 2a000 /lib/x86_64-linux-gnu/ld-2.27.so
...
原因一是 ELF 只是获取了目标 libc 的信息,并未进行加载。再者如下:
$ ldd pwn
linux-vdso.so.1 (0x00007ffe7cf8a000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f74a57b5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f74a5daa000)
libc 和 ld 的路径已经默认写死在程序中了。
除非使用 ld 来运行程序
p = process(['./ld-xxx.so','./pwn'],env={"LD_PRELOAD":"./libc-xxx.so"})
但是笔者和 某dai 亲测 ld-2.23.so
并不好使。原因未知,推测是 2.23 版本有问题。
或者使用 patchelf
来将写死的 libc 换成目标 libc,这里网上教程很多,不再赘述。
(未解决)如何判断远程 libc
如果不给libc,全靠猜。
猜可以根据
- 泄露一个 libc 地址,然后查一下
- double free 一下看报错
- chunk 的个数限制
- 题目的难度和白给程度
- seccomp 设了多少
- 搁这挨个试