使用LD_PRELOAD欺骗注入程序

序言

TCTF 出了 Web 的 writeup 后,发现自己菜的抠脚,对于动态链接库感觉一窍不通,所以借这个机会简单学习一下如何利用 LD_PRELOAD 去欺骗影响某个已定的程序。

什么是 LD_PRELOAD?

LD_PRELOAD 是用于动态链接库的环境变量,并且其加载优先级对于其他动态连接的环境变量而言,是最高的,加载优先级可知:LD_PRELOAD>LD_LIBRARY_PATH>/etc/ld.so.cache>/lib>/usr/lib。

其作用是假设预先编译一个函数为动态库,并且使用 LD_PRELOAD 加载,那么如果在其他程序中调用这个函数时,则会优先调用自定义函数。

(比如说,自定义 rand()函数并且编译为动态库,使用 LD_PRELOAD 加载,那么在其他程序调用 rand()函数之时,不再是调用系统的随机数函数,而是自定义的 rand()函数)。

使用 LD_PRELOAD 实例

这里使用自己搭建的阿里云服务器做实验环境
环境概要

系统:Centos 7.6
gcc version: 4.8.5
g++ version: 4.8.5

这里先写一个简单的程序,起名为cos.c,然后进一步学习利用 LD_PRELOAD 来影响这个程序

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define PI 3.1415926

int main(){
double x,val,ret;
x = 60.0;
val = PI / 180.0;
ret = cos (x*val);
printf("%lf 的余弦是 %lf 度 n", x, ret);
return 0;
}

让我来运行一下这个程序,输出了我想要的结果 60度的余弦值为0.5,Nice!

2019-03-29 23-04-16 的屏幕截图.png
2019-03-29 23-04-16 的屏幕截图.png

现在编译一个 cos_change.c 文件,里面写入:

double cos(double x){
  return x;
}

然后把他使用命令 gcc -shared -fPIC cos_change.c -o cos_change.so 把源文件编译为动态库文件

这里简单介绍一下上面这条命令的几个参数的含义

如果想创建一个动态链接库,可以使用 GCC 的-shared选项。输入文件可以是源文件、汇编文件或者目标文件。

另外还得结合-fPIC选项。-fPIC 选项作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code);这样一来,产生的代码中就没有绝对地址了,全部使用相对地址,所以代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。

例如,从源文件生成动态链接库:

gcc -fPIC -shared func.c -o libfunc.so

从目标文件生成动态链接库:

gcc -fPIC -c func.c -o func.o
gcc -shared func.o -o libfunc.so

-fPIC 选项作用于编译阶段,在生成目标文件时就得使用该选项,以生成位置无关的代码。

然后执行 LD_PRELOAD=$PWD/cos_change.so ./cos 来使用我们新定义的动态库来执行我们之前生成的程序

2019-03-29 23-09-07 的屏幕截图.png
2019-03-29 23-09-07 的屏幕截图.png

嗯??为啥输出的不是0.5000,而是另一个数值?

事实证明,此时如果 把之前 cos(x) 中的参数 x 带入值为 60.0*3.1415926/180.0,此时的输出结果为 1.047198,符合刚才注入执行后的结果。

此时使用 ldd cos 来看看程序在执行时候,加载了哪些动态库

sky@blue:~/LD_PRELOAD$ ldd cos
    linux-vdso.so.1 (0x00007ffe8b3eb000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f80a18fe000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f80a150d000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f80a1c9c000)

很明显能看到,cos程序执行调用了 linux-vdso.so libm.so libc.so ld-linux-x86-64.so 这四个动态库,而我们系统本身的cos()函数则包括在 libm.so文件中。

此时使用 nm -D /lib/x86_64-linux-gnu/libm.so.6 | grep cos 命令可以看到我们调用的真正的 cos() 函数就包含在里面,这里太多就不罗列,但事实证明 cos() 是包含在里面 ,nm 命令列出了在一个二进制文件中找到的符号, 而 -D 参数告诉它去查找动态符号(因为libm.so文件是动态文件)

所以,由上可知,设置了环境变量 LD_PRELOAD 后,这个变量为一个程序强制加载一些库

这里使用 ldd 查看链接了自己的 cos_change.so 动态库后的原程序的加载动态库列表。

LD_PRELOAD=$PWD/cos_change.so ldd cos

加载动态库信息如下:

sky@blue:~/LD_PRELOAD$ LD_PRELOAD=$PWD/cos_change.so ldd cos
    linux-vdso.so.1 (0x00007ffe2c5e7000)
    /home/sky/LD_PRELOAD/cos_change.so (0x00007fcc6fcbb000)
    libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fcc6f91d000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fcc6f52c000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fcc6febd000)

可以明显看到,我们自己写的cos_change.so被加载执行,从而覆盖了原来定义在libm.so中的cos()函数定义,使用我们自己定义的cos(x)函数。

使用LD_PRELOAD实现恶意注入劫持

待这个周末复现TCTF Web2 后更新 LD_PRELOAD 在CTF比赛中的应用。

腾讯把题目连接关了,我复现不了了...难受,我枯了(。•ˇ‸ˇ•。)

参考链接

GCC编译和链接多个文件(包括源文件、目标文件、汇编文件等)
动态连接的诀窍:使用 LD_PRELOAD 去欺骗、注入特性和研究程序

Comments

添加新评论