背景
内部项目需要升级 php、python 等组件以修复漏洞,需要从标准发行版中导入这些组件版本,且尽可能不重新进行编译,需要进行技术调研。
二进制修改技术
openssl、openssh 这些开源组件一般都是动态链接依赖的其它库,可以通过修改 elf 文件的方式指定这些新导入的项目使用高版本中的依赖(依赖同时也要打包)。
对于动态链接的程序来说,主要涉及如下两个方面的修改:
- 动态库链接器的修改
- 动态库检索路径的修改
二进制程序使用动态库链接器
编译后此路径已经被确定,可以通过 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 内核运行进程时会首先加载链接器,然后由链接器统一加载动态库,最后执行程序。
动态库链接器检索库位置
- 判断依赖 so 名称包含一个 /,包含则按照绝对路径、相对路径检索
- 当未设置 DT_RUNPATH 时,从 DT_RPATH 指定的路径中检索(直接链接的与间接链接的都会用这个路径查找))
- 从 LD_LIBRARY_PATH 设置的路径中检索
- 从 DT_RUNPATH 设置的路径中检索(仅直接链接的库从这个路径查找)
- /etc/ld.so.cache 内容
- /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*
适用条件
- 如上技术方案仅使用与动态链接的程序
- 对于链接时指定 / 的组件,需要将依赖的库放到特定的位置
- 组件需要完全依赖相同版本的库,不能加载其它版本的库
- 需要使用组件发布的库进行编译的场景,此技术方案不支持,需要重新编译
- 对于存在 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 的时候返回了无权限错误,判断是不兼容的动态库链接器触发的问题,对于存在会执行系统中命令的组件,要么同步更新依赖的系统命令,要么重新编译该组件。