17 glibc dynamic-linking ld fortify-source
在我尝试将"Steam for Linux"用于Debian时,我遇到了一个问题.libcef(Chromium Embedded Framework)可以正常使用GLIBC_2.13(Debian测试中的eglibc可以提供),但需要一个讨厌的额外函数GLIBC_2.15(eglibc无法提供):
$ readelf -s libcef.so | grep -E "@GLIBC_2\.1[4567]"
1037: 00000000 0 FUNC GLOBAL DEFAULT UND __fdelt_chk@GLIBC_2.15 (49)
2733: 00000000 0 FUNC GLOBAL DEFAULT UND __fdelt_chk@@GLIBC_2.15
Run Code Online (Sandbox Code Playgroud)
我的攻击计划是LD_PRELOAD一个提供这些功能的垫片库.这似乎不起作用.我真的想避免安装GLIBC_2.17(因为它是在Debian实验中;甚至Debian sid仍然有GLIBC_2.13).
这就是我尝试过的.
#include <sys/select.h>
# define strong_alias(name, aliasname) _strong_alias(name, aliasname)
# define _strong_alias(name, aliasname) \
extern __typeof (name) aliasname __attribute__ ((alias (#name)));
unsigned long int
__fdelt_chk (unsigned long int d)
{
if (d >= FD_SETSIZE)
__chk_fail ();
return d / __NFDBITS;
}
strong_alias (__fdelt_chk, __fdelt_warn)
Run Code Online (Sandbox Code Playgroud)
我的Versions脚本如下所示:
GLIBC_2.15 {
__fdelt_chk; __fdelt_warn;
};
Run Code Online (Sandbox Code Playgroud)
然后我按如下方式构建库:
$ gcc -m32 -c -fPIC fdelt_chk.c -o fdelt_chk.o
$ gcc -m32 -shared -nostartfiles -Wl,-s -Wl,--version-script Versions -o fdelt_chk.so fdelt_chk.o
Run Code Online (Sandbox Code Playgroud)
但是,如果我然后运行Steam(带有一堆额外的东西让它首先工作),加载器仍然拒绝找到符号:
% LD_LIBRARY_PATH="/home/tinctorius/.local/share/Steam/ubuntu12_32" LD_PRELOAD=./fdelt_chk.so:./steamui.so ./steam
./steam: /lib/i386-linux-gnu/i686/cmov/libc.so.6: version `GLIBC_2.15' not found (required by /home/tinctorius/.local/share/Steam/ubuntu12_32/libcef.so)
Run Code Online (Sandbox Code Playgroud)
但是,版本符号也是由.so我刚刚构建的:
% readelf -s fdelt_chk.so
Symbol table '.dynsym' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND __chk_fail@GLIBC_2.3.4 (3)
2: 0000146c 0 NOTYPE GLOBAL DEFAULT ABS _edata
3: 0000146c 0 NOTYPE GLOBAL DEFAULT ABS _end
4: 00000310 44 FUNC GLOBAL DEFAULT 11 __fdelt_warn@@GLIBC_2.15
5: 00000310 44 FUNC GLOBAL DEFAULT 11 __fdelt_chk@@GLIBC_2.15
6: 00000000 0 OBJECT GLOBAL DEFAULT ABS GLIBC_2.15
7: 0000146c 0 NOTYPE GLOBAL DEFAULT ABS __bss_start
Run Code Online (Sandbox Code Playgroud)
在这一点上,我不知道我能做什么来欺骗加载器(谁?)选择我的符号.我是否正朝着正确的方向前进?
我遇到了同样的问题,但没有使用Steam.我试图运行想要2.15的fdelt_chk,而我的系统了2.14.我找到了像我们这样的简单案例的解决方案,我们可以轻松地为缺少的功能提供我们自己的实现.
我从你试图实现功能的解决方案开始LD_PRELOAD.使用LD_DEBUG=all(正如osgx所建议的)显示链接器仍然在寻找2.15,所以只有正确的符号是不够的,并且在某处有一些其他的版本控制机制.我注意到了这两个objdump -p并且readelf -V都显示了引用2.15,所以我查找了有关ELF的文档并找到了有关版本要求的信息.
所以我的新目标是将引用转换2.15为对其他内容的引用.我可以覆盖2.15与引用某些较低版本的结构相关的结构,这似乎是合理的2.1.最后,经过一些试验和错误,我发现只是编辑正确的Elfxx_Vernaux(es?).gnu.version_r就足够了,但是我想是不好的警告.
该.gnu.version_r部分是16字节Elfxx_Verneeds和16字节Elfxx_Vernauxes的列表.每个Elfxx_Verneed条目后跟相关的Elfxx_Vernauxes.据我所知,vn_file实际上Elfxx_Vernaux有多少相关联的es,即使文档说number of associated verneed array entries.不过,这可能只是对我的误解.
因此,要开始编辑,让我们看一下来自的一些信息readelf -V.我剪掉了我们不关心的部分.
$ readelf -V mybinary
<snip stuff before .gnu.version_r>
Version needs section '.gnu.version_r' contains 5 entries:
Addr: 0x00000000000021ac Offset: 0x0021ac Link: 4 (.dynstr)
<snip libraries that don't refer to GLIBC_2.15>
0x00c0: Version: 1 File: libc.so.6 Cnt: 10
0x00d0: Name: GLIBC_2.3 Flags: none Version: 19
0x00e0: Name: GLIBC_2.7 Flags: none Version: 16
0x00f0: Name: GLIBC_2.2 Flags: none Version: 15
0x0100: Name: GLIBC_2.2.4 Flags: none Version: 14
0x0110: Name: GLIBC_2.1.3 Flags: none Version: 13
0x0120: Name: GLIBC_2.15 Flags: none Version: 12
0x0130: Name: GLIBC_2.4 Flags: none Version: 10
0x0140: Name: GLIBC_2.1 Flags: none Version: 9
0x0150: Name: GLIBC_2.3.4 Flags: none Version: 4
0x0160: Name: GLIBC_2.0 Flags: none Version: 2
Run Code Online (Sandbox Code Playgroud)
从这里我们看到该部分开始于0x21ac.列出的每个文件Elfxx_Verneed后面都会有一个Elfxx_Vernaux子条目(如GLIBC_2.3).我假设输出中信息的顺序将始终与文件中的顺序匹配,因为readelf只是转储结构.这是我的整个 .gnu.version_r部分.
000021A0 01 00 02 00
000021B0 A3 0C 00 00 10 00 00 00 30 00 00 00 11 69 69 0D
000021C0 00 00 11 00 32 0D 00 00 10 00 00 00 10 69 69 0D
000021D0 00 00 0B 00 3C 0D 00 00 00 00 00 00 01 00 02 00
000021E0 BE 0C 00 00 10 00 00 00 30 00 00 00 13 69 69 0D
000021F0 00 00 08 00 46 0D 00 00 10 00 00 00 10 69 69 0D
00002200 00 00 07 00 3C 0D 00 00 00 00 00 00 01 00 02 00
00002210 99 0C 00 00 10 00 00 00 30 00 00 00 11 69 69 0D
00002220 00 00 06 00 32 0D 00 00 10 00 00 00 10 69 69 0D
00002230 00 00 05 00 3C 0D 00 00 00 00 00 00 01 00 02 00
00002240 AE 0C 00 00 10 00 00 00 30 00 00 00 11 69 69 0D
00002250 00 00 12 00 32 0D 00 00 10 00 00 00 10 69 69 0D
00002260 00 00 03 00 3C 0D 00 00 00 00 00 00 01 00 0A 00
00002270 FF 0C 00 00 10 00 00 00 00 00 00 00 13 69 69 0D
00002280 00 00 13 00 46 0D 00 00 10 00 00 00 17 69 69 0D
00002290 00 00 10 00 50 0D 00 00 10 00 00 00 12 69 69 0D
000022A0 00 00 0F 00 5A 0D 00 00 10 00 00 00 74 1A 69 09
000022B0 00 00 0E 00 64 0D 00 00 10 00 00 00 73 1F 69 09
000022C0 00 00 0D 00 70 0D 00 00 10 00 00 00 95 91 96 06
000022D0 00 00 0C 00 7C 0D 00 00 10 00 00 00 14 69 69 0D
000022E0 00 00 0A 00 87 0D 00 00 10 00 00 00 11 69 69 0D
000022F0 00 00 09 00 32 0D 00 00 10 00 00 00 74 19 69 09
00002300 00 00 04 00 91 0D 00 00 10 00 00 00 10 69 69 0D
00002310 00 00 02 00 3C 0D 00 00 00 00 00 00
Run Code Online (Sandbox Code Playgroud)
简要地谈谈这里的结构,它从一个开始Elfxx_Verneed.根据文档,我们可以看到将有2个Elfxx_Vernauxes,一个偏移16个字节,下一个Elfxx_Verneed是偏移48个字节.这些偏移是从当前结构的开始.从技术上讲,相关的Elfxx_Vernauxes可能在当前之后不会相邻,Elfxx_Verneed但实际上在我搜索过的所有文件中都是如此.
从这里我们可以通过几种不同的方式找到我们想要的文件(libc.so.6).交叉引用字符串(我不会进入),找到Elfxx_Verneed计数为0A 00(10,匹配readelf上面的输出),或找到最后一个,Elfxx_Verneed因为它是最后一个readelf输出.在任何情况下,我的文件的正确的是0x226C.它的第一次Elfxx_Vernaux开始于0x227C.
我们希望找到Elfxx_Vernaux版本为0C 00(12,再次匹配readelf上面的输出).我们看到Elfxx_Vernaux匹配是在0x22CC,整个结构是95 91 96 06 00 00 0C 00 7C 0D 00 00 10 00 00 00.我们将覆盖前12个字节,以便单独留下偏移量.毕竟,我们只修改数据,而不是在结构周围移动.
要选择要覆盖的数据,我们只需从Elfxx_Vernaux我们可以满足的glibc版本中复制它.我在数据库中选择了一个2.1,0x22EC在我的文件中11 69 69 0D 00 00 09 00 32 0D 00 00 10 00 00 00.所以从这里取出前12个字节并覆盖上面的前12个字节,这就是十六进制编辑.
当然,您可能有多个引用要处理.您的程序可能有多个二进制文件要编辑.
此时,我们的程序仍然无法运行.但不要被告知类似的东西GLIBC_2.15 not found应该抱怨失踪__fdelt_chk.现在我们LD_PRELOAD在问题中描述了shim和ing,除了不对我们的实现进行版本控制2.15,我们使用我们在十六进制编辑时选择的版本.此时程序应该运行.
这种方法取决于能否提供任何缺失的实现.我们__fdelt_chk非常简单,但我不怀疑在某些情况下提供实现可能比仅升级系统的libc更困难.
对于它的价值,__ fdelt_chk函数与glibc 2.15中添加的FORTIFY_SOURCE功能相关.它支持编译时和运行时检查缓冲区溢出.
如果您能够使用添加的以下CFLAGS重新编译,它将构建向后兼容的二进制文件而无需额外检查:
-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0
Run Code Online (Sandbox Code Playgroud)