修改 elf 文件方式导入依赖高版本 libc 组件技术调研

背景

内部项目需要升级 php、python 等组件以修复漏洞,需要从标准发行版中导入这些组件版本,且尽可能不重新进行编译,需要进行技术调研。

二进制修改技术

openssl、openssh 这些开源组件一般都是动态链接依赖的其它库,可以通过修改 elf 文件的方式指定这些新导入的项目使用高版本中的依赖(依赖同时也要打包)。

对于动态链接的程序来说,主要涉及如下两个方面的修改:

  1. 动态库链接器的修改
  2. 动态库检索路径的修改

二进制程序使用动态库链接器

编译后此路径已经被确定,可以通过 readelf 文件来获取相关信息,示例如下:

 readelf -a /usr/bin/bash  | grep inter
  [ 1] .interp           PROGBITS         0000000000000318  00000318
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

链接器的位置放在 elf 的 interp section 中,可以通过修改此 section 的内容来实现修改链接器位置的功能,linux 内核运行进程时会首先加载链接器,然后由链接器统一加载动态库,最后执行程序。

动态库链接器检索库位置

  1. 判断依赖 so 名称包含一个 /,包含则按照绝对路径、相对路径检索
  2. 当未设置 DT_RUNPATH 时,从 DT_RPATH 指定的路径中检索(直接链接的与间接链接的都会用这个路径查找))
  3. 从 LD_LIBRARY_PATH 设置的路径中检索
  4. 从 DT_RUNPATH 设置的路径中检索(仅直接链接的库从这个路径查找)
  5. /etc/ld.so.cache 内容
  6. /lib、/usr/lib、/lib64、/usr/lib64 默认参数

patchelf 等工具能够修改 elf 文件的 DT_RPATH,通过修改这个可以让动态库链接器加载到指定目录中的 so,此修改需要对每个依赖的 so 都执行,避免某个 so 加载系统路径 so。

vdso 库的兼容性考虑

vdso 是 linux 内核实现的一个 so 库,它会在可执行程序运行的时候被映射到进程空间,主要功能是优化少数需要频繁使用的系统调用,将它们转化类似 c 库的简单函数调用。

程序编译时并不会依赖 vdso 库,它跟随内核,兼容性由内核保障,一般不需要额外进行处理。

修改命令示例

 patchelf --set-interpreter /3rd/lib64/ld-linux-x86-64.so.2  ./php
 patchelf --set-rpath /3rd/lib64/ ./php
 patchelf --set-rpath /3rd/lib64/ ./3rd/lib64/*so*

适用条件

  1. 如上技术方案仅使用与动态链接的程序
  2. 对于链接时指定 / 的组件,需要将依赖的库放到特定的位置
  3. 组件需要完全依赖相同版本的库,不能加载其它版本的库
  4. 需要使用组件发布的库进行编译的场景,此技术方案不支持,需要重新编译
  5. 对于存在 dlopen 方式的情况,需要运行进行采集,可能存在遗漏

测试示例

打包 openeuler 中的 python 组件,并修改相关的 elf 程序集成到低版本 libc rootfs 中进行测试验证。

python3 运行验证

Python 3.11.6 (main, Jul 30 2024, 11:48:06) [GCC 12.3.1 (openEuler 12.3.1-32.oe2403)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> ^Z
[1]+  Stopped                 ./bin/python3.11

python3 依赖的 so 的位置验证

bash-4.2# ps aux |grep python3
root       899  0.6  0.0  22164 11092 pts/3    T    15:32   0:00 ./bin/python3.11
root       901  0.0  0.0   6852   296 pts/3    S+   15:32   0:00 grep python3
bash-4.2# pmap -p 899
899:   ./bin/python3.11
000055bfa8393000      4K r---- /3rd/usr/bin/python3.11
000055bfa8394000      4K r-x-- /3rd/usr/bin/python3.11
000055bfa8395000      4K r---- /3rd/usr/bin/python3.11
000055bfa8396000      4K r---- /3rd/usr/bin/python3.11
000055bfa8397000      4K rw--- /3rd/usr/bin/python3.11
000055bfa8398000      4K rw--- /3rd/usr/bin/python3.11
000055bfa8399000      4K rw--- /3rd/usr/bin/python3.11
000055bfa914d000   1240K rw---   [ anon ]
00007f9476903000    204K rw---   [ anon ]
00007f9476936000      4K r---- /3rd/usr/lib64/python3.11/lib-dynload/_opcode.cpython-311-x86_64-linux-gnu.so
00007f9476937000      4K r-x-- /3rd/usr/lib64/python3.11/lib-dynload/_opcode.cpython-311-x86_64-linux-gnu.so
00007f9476938000      4K r---- /3rd/usr/lib64/python3.11/lib-dynload/_opcode.cpython-311-x86_64-linux-gnu.so
00007f9476939000      4K r---- /3rd/usr/lib64/python3.11/lib-dynload/_opcode.cpython-311-x86_64-linux-gnu.so
00007f947693a000      4K rw--- /3rd/usr/lib64/python3.11/lib-dynload/_opcode.cpython-311-x86_64-linux-gnu.so
00007f947693b000   3480K rw---   [ anon ]
00007f9476ca1000   8296K r---- /lib64/locale/locale-archive
00007f94774bb000     12K rw---   [ anon ]
00007f94774be000   1812K r-x-- /3rd/lib64/libc.so.6
00007f9477683000     16K r---- /3rd/lib64/libc.so.6
00007f9477687000      8K rw--- /3rd/lib64/libc.so.6
00007f9477689000     52K rw---   [ anon ]
00007f9477696000     52K rw--- /3rd/lib64/libc.so.6
00007f94776a3000    872K r-x-- /3rd/lib64/libm.so.6
00007f947777d000      4K r---- /3rd/lib64/libm.so.6
00007f947777e000      4K rw--- /3rd/lib64/libm.so.6
00007f947777f000     24K rw--- /3rd/lib64/libm.so.6
00007f9477785000    980K r---- /3rd/lib64/libpython3.11.so.1.0
00007f947787a000   2088K r-x-- /3rd/lib64/libpython3.11.so.1.0
00007f9477a84000    880K r---- /3rd/lib64/libpython3.11.so.1.0
00007f9477b60000    200K r---- /3rd/lib64/libpython3.11.so.1.0
00007f9477b92000   1220K rw--- /3rd/lib64/libpython3.11.so.1.0
00007f9477cc3000    264K rw---   [ anon ]
00007f9477d05000     52K rw--- /3rd/lib64/libpython3.11.so.1.0
00007f9477d12000      8K rw---   [ anon ]
00007f9477d14000    176K r-x-- /3rd/lib64/ld-linux-x86-64.so.2
00007f9477d40000      8K r---- /3rd/lib64/ld-linux-x86-64.so.2
00007f9477d42000      8K rw--- /3rd/lib64/ld-linux-x86-64.so.2
00007ffffe660000    132K rw---   [ stack ]
00007ffffe799000     16K r----   [ anon ]
00007ffffe79d000      8K r-x--   [ anon ]
ffffffffff600000      4K r-x--   [ anon ]
 total            22168K

**验证结论:**验证通过

测试问题记录

集成高版本 openssh 的时候发现,sshd 会依赖 pam 模块,同时需要将高版本中的 pam 模块进行打包,同时可以通过修改 pam 配置文件内容,指定从特定路径加载 pam 模块。

/etc/pam.d/sshd 内容修改示例如下:

#%PAM-1.0
auth       required     /3rd/lib64/security/pam_permit.so
account    required     /3rd/lib64/security/pam_permit.so
password   required     /3rd/lib64/security/pam_permit.so
session    required     /3rd/lib64/security/pam_permit.so

修改后密码校验通过,但是无法运行系统中依赖低版本 libc 的 /bin/bash 程序,测试记录如下:

bash-4.2# ssh longyu@localhost
longyu@localhost's password:
/bin/bash: Permission denied
Connection to localhost closed.
bash-4.2#

strace 跟踪发现,在 execve /bin/bash 的时候返回了无权限错误,判断是不兼容的动态库链接器触发的问题,对于存在会执行系统中命令的组件,要么同步更新依赖的系统命令,要么重新编译该组件。

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值