================================================== -->

An interesting Google vulnerability that got me 3133.7 reward.

An interesting Google vulnerability that got me 3133.7 reward.

Note: This blog post doesn’t represent my employer by any meaning and was performed during my free time.

Hi All 🙂

Before we start, 3133.7 stands for Elite. It is a leet/1337 way of Google to reward researchers 😀

While doing security researching on Google. Decided to focus on “Cloud.google.com” as it sounds interesting with lots of features.

During my testing, saw the following very interesting request in the Burp Proxy going to:

https://cloudusersettings-pa.clients6.google.com/batch?%24ct=multipart%2Fmixed%3B%20boundary%3Dbatch392541909285510985

Note: Page original response is “Not found” within a JSON format. Not that important to include here.

Immediately 2 things got my attention. First, the GET request within the POST request body along with the request headers as well. Second is being able to control the content-type via the value in the URL of the main request. Later on, noticed that there is a KEY being sent within the POST request body.

My expectation: 

The server of “cloudusersettings-pa.clients6.google.com” is relaying the GET request via the POST request body to an intermediary server (reverse proxy / load balancer) that would parse it somehow and process it or pass it over to a third-server. The intermediary server doesn’t care about the headers of the original POST request, but only parses the GET request headers that are being sent within the POST request body.

Well, few things to conclude this is:

1) I’m able to control the headers that the intermediary server will process;

2) It is possible to control the response type via the value below marked in red:

https://cloudusersettings-pa.clients6.google.com/batch?%24ct=multipart%2Fmixed%3B%20boundary%3Dbatch392541909285510985

3) There is a key and authorization header values in the request.

To get directly to the point, number 2 and 3 led to nothing. But the good point is, it was not being validated. lets keep a note of that.

Now to the interesting part, which is manipulating the HTTP request headers itself. My approach was to perform the following test cases against the GET request in the POST body:

1) Play around the HOST header “Virtual Hostname Enumeration”. Like setting the host header to dev, localhost, portal, and so on. So that the webserver is tricked into disclosing locally configured virtual hostnames. Thus, allowing access to internal environments. (Didn’t work)

2) Spoofing my IP address by the following headers. Trying to induce the backend server that receives the request to treat it in a different way. (Didn’t work)

  • X-Forwarded-For: 127.0.0.1
  • X-Client-IP: 127.0.0.1
  • Client-IP: 127.0.0.1

3) Forcing the backend server to through some errors. Have tried this by sending large input of AAAAA’s, changing the HTTP protocol version instead of 1.1 to something else, adding some random headers or manipulate the existing once, manipulating the content-type and so on (Didn’t work)

4) Blind XSS/SQLI in the userAgent value, it might get processed at some point! (Didn’t work)

5) Have also tried to manipulate the origin header to hopefully find a misconfigured CORS implementation, but the origin was well validated.

Was out of options, till I thought of why don’t I directly “talk” to the backend server the way it understands?! A web server spoken language is basically headers! It sees header, it parse it, then process it, Simple!

One of the interesting headers that allows you to “talk” to the web server is “X-HTTP-Method-Override“. This header basically allows some magic stuff. For example, you can send GET request to the server, but the server would treat it as PUT, POST, DELETE or whatever the method you declared! yes, although it is originaly a GET request 😀

An example is:

GET /test.php HTTP/1.1
Host: google.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
X-HTTP-Method-Override: PUT
Accept-Encoding: gzip, deflate
Connection: close

When the web server receives the above GET request and parse it, it will find the XHMO header in there and would say

“Oh! this is a GET request but the user wants me to treat it as PUT, consider it done, Sir!” and treat it as PUT instead 😀

The reason of why this behavior is needed, some intermediary servers (reverse proxy/load balancers, WAF’s) are limited in the HTTP methods it supports (i.e. Only GET and POST is supported). However, the developers might still want to send a “PATCH, DELETE, PUT” methods. Here is where the XHMO comes to play.

Oh, and did i mention that I have once triggered RCE via the HTTP PUT method and webdav was enabled, while there was F5 WAF watching all the traffic?

My burp repeater —-> Hey Web server, I want you to create a file via PUT method —-> F5 says: You have to pass through me first, and I only support GET/POST! —-> FAIL!

My burp repeater —-> Hey F5, I know that the backend server supports PUT, so here is your GET request with XHMO set to PUT. Please pass it to the backend server —-> F5 says: sure, seems legit! —-> Backend webserver: yea, another PUT request to handle. HYG, your file is created because I do support PUT and I understood your call. —-> Bingo!

With all details being said, have added the XHMO header to the request with HTTP method PUT hoping that webdav is enabled on the backend webserver so that I could somehow get RCE. And no, it didn’t work, Buuuuut!

The middle webserver couldn’t understand what to do with that header! so it decided to through a very much detailed error message that discloses:

  • HTTPOnly and Secure cookies!
  • All communication details of the connection between the intermediary server and the backend server. Including SSL hello message, IP’s, ports and so on;
  • Also the authorization headers and few more interesting messages too.

Note: the screenshot is being in bad quality on purpose.

Remember when I said that nothing in the request was being validated? based on that, have created a CSRF POC that would trigger the same exact response for other Google users. And it is possible to steal these information using a reflected XSS!

In theory, HTTPOnly cookies are well secure against XSS attacks. But using this vulnerability, it is possible to steal the HTTPOnly cookies from Google users who are tricked into clicking the malicious link.

And that only happened when adding the XHMO header. The page originally returns a ‘not found’ response.

Have reported the vulnerability to Google, the team have set the priority to P1. They were fast in responses and handled the vulnerability professionally. Later on, received below Email from Google:

Hope you enjoyed reading  🙂

Cheers,

Ebrahem Hegazy – @Zigoo0

 

3 Comments

  1. xss - October 5, 2018

    Nice.. Have a query though..

    How did you inject XHMO header via a CSRF POC? I not sure on how it can be exploited against other users in real world. Can you please explain more on that. Thanks.

    • Ebrahim - February 5, 2019

      Hi XSS,
      I did not add it as a header to the original request, but as a normal POST parameter&value. The reason is as you see in the first screenshot, there are 2 different requests within the same request, the preferred one is sent as a POST data within the original request.

    • Ebrahim Hegazy - June 11, 2019

      Hi “XSS”,
      That was easy indeed, because it was treated as a POST body, and not a new Request Header.

Leave a reply