Linux的LD_PRELOAD相关知识学习


=Start=

缘由:

一提起LD_PRELOAD就会让我想起之前犯的一个错误——直接替换系统的glibc动态链接库导致系统无法正常登录。在那个时候才开始认识以及了解LD_PRELOAD还有它身后的相关Linux底层知识。

正文:

参考解答:
0.预备知识

编辑——(预编译)编译(汇编)——链接——运行

静态链接:在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。

动态链接:在静态情况下,它把库直接加载到程序里,而在动态链接的时候,它只是保留接口,将动态库与程序代码独立。

1.什么是LD_PRELOAD?

在UNIX的动态链接库的世界中,LD_PRELOAD就是这样一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。

我们知道,Linux的用的都是glibc,有一个叫libc.so.6的文件,这是几乎所有Linux下命令的动态链接中,其中有标准C的各种函数。对于GCC而言,默认情况下,所编译的程序中对标准C函数的链接,都是通过动态链接方式来链接libc.so.6这个函数库的。

loader在进行动态链接的时候,会将有相同符号名的符号覆盖成LD_PRELOAD指定的so文件中的符号。换句话说,可以用我们自己的so库中的函数替换原来库里有的函数,从而达到hook的目的。这和Windows下通过修改import table来hook API很类似。相比较之下,LD_PRELOAD更方便了,都不用自己写代码了,系统的loader会帮我们搞定。但是LD_PRELOAD有个限制:只能hook动态链接的库,对静态链接的库无效,因为静态链接的代码都写到可执行文件里了嘛,没有坑让你填。

2.它能起到什么作用?

它的主要功能就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入恶意程序,从而达到那不可告人的罪恶的目的。

3.该如何使用它?
main.c
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
    if( strcmp(argv[1], "test") )
    {
        printf("Incorrect password\n");
    }
    else
    {
        printf("Correct password\n");
    }
    return 0;
}

&

hook.c
#include <stdio.h>
#include <string.h>
#include <dlfcn.h>
typedef int(*STRCMP)(const char*, const char*);
int strcmp(const char *s1, const char *s2)
{
    static void *handle = NULL;
    static STRCMP old_strcmp = NULL;
    if( !handle )
    {
        handle = dlopen("libc.so.6", RTLD_LAZY);
        old_strcmp = (STRCMP)dlsym(handle, "strcmp");
    }
    printf("hack function invoked. s1=<%s> s2=<%s>\n", s1, s2);
    return old_strcmp(s1, s2);
}

因为hook的目标是strcmp,所以typedef了一个STRCMP函数指针。由于hook的目的是要控制函数行为,所以需要从原库libc.so.6中拿到“正版”strcmp指针,保存成old_strcmp以备调用。

&

$ gcc -o test main.c
$ gcc -fPIC -shared -o hook.so hook.c -ldl
 
$ LD_PRELOAD=./hook.so ./test 123
hack function invoked. s1=<123> s2=<test>
Incorrect password
$ LD_PRELOAD=./hook.so ./test test
hack function invoked. s1=<test> s2=<test>
Correct password

其中有一点不理解的是,dlopen打开libc.so.6能拿到“正版”strcmp地址,打开libc.so就是hook后的地址。照理说libc.so不是libc.so.6的一个软链吗?为什么结果会不一样呢?

&

hook2.c
#include <stdio.h>
#include <string.h>
 
int strcmp(const char *s1, const char *s2)
{
    printf("hack function invoked. s1=<%s> s2=<%s>\n", s1, s2);
    /* 永远返回0,表示两个字符串相等 */
    return 0;
}

&

$ gcc -o test main.c
$ gcc -fPIC -shared -o hook2.so hook2.c -ldl
$ ./test 123
hack function invoked. s1=<123> s2=<test>
Correct password
$ LD_PRELOAD=./hook2.so ./test test
hack function invoked. s1=<test> s2=<test>
Correct password
4.总结

在我们编程时,我们要随时警惕着LD_PRELOAD。

如何避免

不可否认,LD_PRELOAD是一个很难缠的问题。目前来说,要解决这个问题,只能想方设法让LD_PRELOAD失效。目前而言,有以下面两种方法可以让LD_PRELOAD失效。

1)通过静态链接。使用gcc的-static参数可以把libc.so.6静态链入执行程序中。但这也就意味着你的程序不再支持动态链接。

2)通过设置执行文件的setgid / setuid标志。在有SUID权限的执行文件,系统会忽略LD_PRELOAD环境变量。也就是说,如果你有以root方式运行的程序,最好设置上SUID权限。(如:chmod 4755 daemon)

在一些UNIX版本上,如果你想要使用LD_PRELOAD环境变量,你需要有root权限。但不管怎么说,这些个方法目前来看并不是一个彻底的解决方案,只是一个Workaround的方法,是一种因噎废食的做法,为了安全,只能禁用。

参考链接:

==

=END=


《 “Linux的LD_PRELOAD相关知识学习” 》 有 11 条评论

  1. Linux Hook 笔记
    http://www.cnblogs.com/pannengzhi/p/5203467.html
    http://www.cnblogs.com/LittleHann/p/3854977.html
    `
    1. 系统调用Hook简介
    2. Ring3中Hook技术
    0x1: LD_PRELOAD动态连接.so函数劫持
    0x2: 使用snoopy进行execve/execv、connect、init_module的hook
    0x3: 绕过基于Linux消息队列(Message Queue)通信的Hook模块
    0x4: 基于PD_PRELOAD、LD_LIBRARY_PATH环境变量劫持绕过Hook模块
    0x5: 基于ptrace()调试技术进行API Hook
    0x6: 绕过C库LD_PRELOAD机制的技术方案
    0x7: 基于PLT劫持、PLT重定向技术实现Hook
    3. Ring0中Hook技术
    0x1: Kernel Inline Hook
    0x2: 利用0x80中断劫持system_call->sys_call_table进行系统调用Hook
    0x3: 获取sys_call_table的常用方法
    0x4: 利用Linux内核机制kprobe机制(kprobes, jprobe和kretprobe)进行系统调用Hook
    0x5: LSM(linux security module) Security钩子技术(linux原生机制)
    0x6: LSM Function Replace Hook劫持技术
    0x7: int 80中断劫持技术
    0x8: 利用从PAGE_OFFSET起始位置搜索特征码劫持system_call_sys_call_table进行系统调用hook
    0x9: Linux LSM(Linux Security Modules) Hook技术
    4. 后记
    `

  2. GoAead RCE(CVE–2017–17562)预警分析
    https://cert.360.cn/warning/detail?id=e805d0e158822ff12fba69473d563fb2
    `
    12月12日,MITRE披露一枚GoAhead的漏洞,编号为CVE-2017-17562,受影响的GoAhead,如果启用CGI动态链接,会有远程代码执行的危险。GoAhead广泛应用于嵌入式设备中。360CERT经过评估,确认该漏洞属于高危,建议用户尽快进行修复。
    `
    https://www.elttam.com.au/blog/goahead/
    https://nvd.nist.gov/vuln/detail/CVE-2017-17562
    https://cxsecurity.com/issue/WLB-2017120131
    https://security-tracker.debian.org/tracker/CVE-2017-17562
    https://vulners.com/exploitdb/EDB-ID:43360

  3. 如何增强Linux内核中的访问控制安全
    https://insights.thoughtworks.cn/access-control-security-of-linux/
    `
    Linux中常见的拦截过滤
    本文着重介绍Linux平台上常见的拦截:
    1、用户态动态库拦截。
    2、内核态系统调用拦截。
    3、堆栈式文件系统拦截。
    4、inline hook拦截。
    5、LSM(Linux Security Modules)。

    对于以上几种Hook方式,有其不同的应用场景:
    1、动态库劫持不太完全,劫持的信息有可能满足不了我们的需求,还有可能别人在你之前劫持了,一旦禁用LD_ PRELOAD就失效了。
    2、系统调用劫持,劫持的信息有可能满足不了我们的需求,例如不能获取struct file结构体,不能获取文件的绝对路径等。
    3、堆栈式文件系统,依赖于Mount,可能需要重启系统。
    4、inline hook,灵活性高,随意Hook,即时生效无需重启,但是在不同内核版本之间通用性差,一旦某些函数发生了变化,Hook失效。
    5、LSM,在早期的内核中,只能允许一个LSM内核模块加载,例如加载了SELinux,就不能加载其他的LSM模块,在最新的内核版本中不存在这个问题。
    `

  4. OrBit:未被检测到的采用独特控制流劫持技术的新型Linux威胁
    https://mp.weixin.qq.com/s/Om8Qsrqos2gjAD6jmXaRiQ
    `
    Interzer对一种新的完全未被检测到的Linux威胁进行深入的技术分析,将其命名为OrBit,因为这是恶意软件用来临时存储已执行命令输出的文件名之一。它可以安装为具有持久性能力或作为一个易失性的植入。该恶意软件实现了高级规避技术,并通过挂钩关键函数在机器上获得持久性能力,它还为威胁行动者提供了SSH远程访问、凭证获取、并记录TTY命令记录等能力。一旦安装了该恶意软件,它将感染所有正在机器上运行的、包括新进程在内的进程。

    与其他威胁通过修改环境变量LD_PRELOAD来劫持共享库不同,这个恶意软件使用两种不同的方式来加载恶意库。第一种方法是将共享对象添加到加载器使用的配置文件中。第二种方法是对加载器本身的二进制文件打补丁,这样它就会加载恶意的共享对象。

    1. OrBit下载程序
    2. OrBit有效负载
    3. SSH连接
    4. OrBit的防御规避技术
    5. 实现持久性
    该恶意软件使用两种方法来实现持久性,同时使用这两种方法的原因是为了更加难以从受感染机器中移除该恶意软件。第一种方法将恶意软件的路径添加到 /etc/ld.so.preload 配置文件中。这指示加载程序应首先加载恶意软件并为所有新进程加载。在这种方法被阻止的情况下,例如删除受感染机器上的配置文件,恶意软件有第二种方法,即通过对加载程序二进制文件打补丁的方式来实现。

    恶意软件首先会复制加载程序的二进制文件,以便对其进行打补丁。它在二进制文件中执行简单的搜索字符串“/etc/ld.so.preload”。找到后,它将字符串替换为 %MALWARE_FOLDER% 中文件的路径。该文件的内容含有恶意软件库的路径,可起到 ld.so.preload 配置文件的作用。这意味着当补丁加载器被执行时,它使用的是 %MALWARE_FOLDER% 中的文件而不是“/etc”下的文件。

    恶意软件作者设置了这两种方法,以防其中一种方法失效。例如,如果管理员想通过删除“/etc”下的配置文件来阻止恶意软件的加载,隐藏文件就会显示,由于打过补丁的加载程序不使用该文件,它只会加载恶意软件并重新创建配置文件。如果管理员改用干净版本覆盖已打补丁的加载程序,则干净加载程序会通过“ld.so.preload”配置文件加载恶意软件并重新对加载程序打补丁。

    6. 信息窃取
    7. 网络能力
    8. 与其他Linux威胁比较
    9. 总结
    针对 Linux 的威胁在不断发展,同时成功地躲在安全工具的关注范围之外,现在 OrBit 是又一个说明新恶意软件是如何躲避监测和实现持久化的例子。
    `
    https://www.intezer.com/blog/incident-response/orbit-new-undetected-linux-threat/

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注