BountyCon CTF 2019

Exploits of a Mom

Recently, Facebook and Google partnered up and launched a capture-the-flag competition called BountyCon. Unlike most CTFs, you couldn't simply solve a list of challenges and find all the flags. The flags were hidden creatively across multiple FB and Google products.

FB also made a group for this called Bobby's Daily Thoughts run by a fictional engineer named "Bobby Tables", where they posted some hints and challenges so that you could at least find some flags if you didn't feel like finding them in the dark, and also to enable them to post more complex challenges. Flags would be worth 100 points if only one person found it, and would decrease by 1 every time it's found, up to a minimum of 25 points.

This is a writeup of the CTF and how I found 30 flags (out of 36 total flags according to Gerben @ FB) totalling 1949 points. I ranked first which got me invited to the BountyCon event held in Singapore. I also gave a lightning talk at Facebook APAC HQ about the highest scoring flags and some flag finding tips. You can find the slides here.

The CTF began when FB's Bug Bounty page posted the note Introducting BountyCon. Soon @GoogleVRP tweeted about the competition:

I was scrolling through my Twitter feed when I saw that someone had tweeted a link to FB's post. It looked like a nice challenge and I was immediately hooked.

[Google] Security.txt

I began finding flags by googling "BountyCon". This led to https://www.google.com/.well-known/security.txt which had a flag:

# Flag: BountyCon{075████████████████████████}

[FB] Open Source Challenge

I searched "BountyCon" on a few sites, one of which was GitHub. Searching on GitHub showed lots of instances of the security.txt flag in a repo called securitytext which had multiple files containing Google's security.txt file. This search also found a commit in FB's HHVM repo which had a flag:

BountyCon{Aau████████████████████████}

Later I discovered that the introduction post in FB's group hinted about this flag:

[…] Bobby still has time left over for some his hobbies, including cycling, singing karaoke and contributing to big Facebook open source projects to better the performance of code that people write.

[FB] App Secret Challenge

This was the first post in the group with an attached file:

Bobby stumbled across this old YAML snippet from one of his colleagues' test projects. Bobby recalls linking a signature to this client, but can't find it. [download config.yaml file]

The config.yaml file contained a client_id and client_secret. Only the client_id was needed to solve this. You could simply visit https://www.facebook.com/games/?app_id=901417563579056 which showed an error message:

Sorry, BountyCon{61v3_m3_4cc355} hasn't been approved for display in App Center.

The client/app's name was the flag. You could also have used the Graph API Explorer to get info on the app which displayed the name/flag as well.

[FB] Regex Challenge

This was another posted challenge:

To mix things up Bobby hid his signature amongst thousands of fake signatures. It is your task to find the correct one using the following requirements.

- The password must start with 'BountyCon{' and end with a '}'. - The password consists of three or more groups where each group consists of three parts: (1) 3 to 15 (inclusive) alphanumeric characters. (2) A word, either 'correct', 'horse', 'battery', or 'staple' (3) One optional symbol, '+', '*', '.' or '!'. The first two parts must be separated with an underscore - Each group must be separated by an underscore - After the last group there must be an underscore followed by an ASCII emoji, where the eyes are either ':' or ';' and the mouth is either ')' or '('. [download flags.txt file]

This challenge required you to craft a regex with the help of the requirements listed in the post and find a single flag among 10k flags.

The correct regex is:

^BountyCon\{([a-zA-Z0-9]{3,15}_((correct)?(horse)?(battery)?(staple)?)[+*.!]?_){4,}[:;][()]\}$

which would lead to this flag:

BountyCon{Qzk████████████████████████}

Brute-forcing the flag submission endpoint was against the rules or you could have solved this without constructing the regex.

[FB] Group Comment Challenge

Bobby's intro post said:

If you're stuck, look out for hints in the comments

And the comments on that page (specifically any page starting with facebook.com/groups/) contained a flag:

<div style="display:none">BountyCon{Aas████████████████████████}</div>

Static and dynamic flags

Some FB flags were static (like the App Secret one above) while most were dynamic. The dynamic flags were generated on each page load probably containing the FB user ID to help them catch unsuspecting cheaters who shared flags. This was great thinking. Some of FB's static flags looked like dynamic ones but most static flags were simply written in leet speak like other CTFs. All of Google's flags were static however and randomly generated.

Automated searching tools

Eventually I got tired of manually crawling the pages and searching for "BountyCon" so I fired up Burp Proxy and opened up a few search windows for BountyCon, Qm91bnR5Q29u (base64 encoded), etc. This became a bit overkill and Burp ate a lot of memory. To replace this, I wrote a custom searching script for mitmdump which had about 50 different search strings (all "BountyCon" in different encodings, list here).


[Google] VRP Rules Page Comment

If you visited Google's Vulnerability Reward Program rules page, which was linked to on multiple pages including the BountyCon info page, you'd find a flag in the comments:

<!-- BountyCon{ace████████████████████████} -->

[FB] Hash Cracking Challenge

Group Post:

Bobby split one of his signatures into three hashes. Use the endpoint and crack the hashes to find it:

  1. a9e534c4287509dc9588caf5fe76be67
  2. 4dcc4173d80a2817206e196a38f0dbf7850188ff
  3. 39c63ddb96a31b9610cd976b896ad4f0

https://www.facebook.com/whitehat/ctf/bountycon/hashes/?p1=&p2=&p3=

You could reverse these hashes by simply googling them. I used CrackStation to identify and crack the hashes and it identified the first hash as NTLM and cracked the last two hashes as "hacker" and "man". I googled "ntlm hash cracker" and cracked the first hash as "mr" using some random site.

After cracking the hashes, you can access https://www.facebook.com/whitehat/ctf/bountycon/hashes/?p1=mr&p2=hacker&p3=man and get the flag in the response.

for (;;);{
   "flag": "BountyCon{Aav████████████████████████}"
}

[FB] Brute-force Challenge

Group Post:

Bobby found some of his old notes about a "secret" POST endpoint that, as well as the usual parameters, takes a "secret_id" value:

<form action="https://www.facebook.com/whitehat/ctf/bountycon/secret/" method="POST">
[...other params...]
<input name="secret_id" placeholder="Integer below 10000" value="REDACTED" />
</form>

To find his signature, make a valid async request and supply the correct secret value.

You can do this with Burp Repeater, that's probably the simplest way. Simply take any existing POST request to Facebook to find the extra params needed to make a valid request. I did this with Chrome Network Tools to get a valid POST request, curl and shell scripting to automate the request. I removed the redundant params with trial and error.

curl --cookie cookies.txt https://www.facebook.com/whitehat/ctf/bountycon/secret/ -F fb_dtsg="redacted:redacted" -F \__a=1 -F \__req=1 secret_id=123

(to export the cookies.txt file you can use a browser extension)

for (;;); {
    "__ar": 1,
    "payload": {
        "Error": "Desktop response to be implemented, mobile should be ready. ~ Bob"
    },
    "bootloadable": {},
    "ixData": {},
    "bxData": {},
    "gkxData": {},
    "qexData": {},
    "lid": "6653726769552872881"
}

Bob will ask you to try the mobile endpoint instead so simply replace www.facebook.com with m.facebook.com

Here's the fish script I used:

for i in (seq 1 9999)
    if not test -e $i
        curl -s https://m.facebook.com/whitehat/ctf/bountycon/secret/ --cookie cookies.txt -H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36' -F fb_dtsg="redacted:redacted" -F secret_id=$i > $i
        la $i
        grep -i BountyCon $i
    end
end

This will save the responses to disk and will also continue where it left off if you press Ctrl+C and run it later.

The secret_id turned out to be 5391:

for (;;); {
    "payload": {
        "actions": [{
            "cmd": "append",
            "target": "static_templates",
            "html": "",
            "replaceifexists": true
        }],
        "contentless_response": false,
        "bootloadable": {},
        "ixData": {},
        "bxData": {},
        "gkxData": {},
        "qexData": {},
        "resource_map": {},
        "payload": {
            "flag": "BountyCon{Aau████████████████████████}"
        }
    }
}

[FB] Crypto Challenge

Group Post:

What treasures can this encrypted string hold? Hint: the decrypted text and the key contain [a-zA-Z0-9] only. Good luck!
667763476f5a3c3b5070250160223f4355776f487c3a3409560b2615633d3b59542b3645512e20387d70211d63292f476d1d14486c3e123f7d703d436048235f5476145f513e0e20516b47156a3d3b4a6d16184256103466560b250b703d37406e1239047c2c3c25506f46425c2f2345552b2e706c312c006153463e632220025303637750073065560825276a4a280360130c065605301f500a1c0a772d0e4b630118646659240a7942210b5d3c097662281c436613243d666f2936743f195c6e75170163213329667e3d1e5d3f190564102d006105123c65561f226b3c2771620300487b594260506f4742681701665476085e6005332a61084a315d4b2b6156131b456c312422507e354b703d2b466d061848575b473f7d7f46045822235f782c1b4064020e1a666d4a206a323f706d7717036005301556550b1c7629547861006e016c051203605503036f3d34497b3f1c48783e1205797f251a773d15656501087e7a213360615231195c2d54076212620064053411796e31215d03197f561d005d632e19677842254360115c6660006b4b60592f667a6e3d39772e2743562900717a2e0121797f1b286d2e2841612f1b44672d1e0462701703762e384b5500325c515902236009210674163f7163752e7e663e3c1d637f421b5d491d7d613f32747a3f06196d6c213f584b0978563c6365572c3c2b650b1808604a1a017a0108487a21112a57553d366c3d06017a2b1f066602422b650a17206a2201027a00226b6f0224187a554204772c015d7a106353563f2429666035086317587f55020c5a64031a146d6f250077160a4b637608436c024e2957521f34584a1975652b0066542f383d516f4b466c49050360293e476731242b7a421723743d375d6e2b0871545b06177a7026086d4923526d29325f6f043b61650935416810236a6276314851044f0361542506604a2b5e53130847662c430960523144724b1d4b6002130263044710517e1b18763e3f5d650629017803126550530b01763c017a56106f03785b30016e42434b
[download bob_crypt.cpp file]

The hint and the file are crucial to decrypting the text. When inspecting the bob_crypt.cpp file, you'd find that all it does is a simple XOR encryption and accepts a key of 16 characters only. Since the key length is small and ciphertext is long, and you know all the valid characters in the key and ciphertext, you can write a script to generate keys and eliminate them if the output plaintext contains any invalid characters. I didn't find any existing scripts for this so I ended up writing my own in JavaScript, which I've also published on this site: XOR Key Bruteforce

This will find a single key and the output plaintext will be in base64, which after decoding will say:

Congratulations! You decrypted the message successfully. Here is your flag: BountyCon{Aav████████████████████████} and some noise: +BLHEOQHtBgc5RQEnlg9/JTN7bWONjjTf3/1s1gU0ea4lUD4K8q7R0dyoxQo6BPA1PRw+KivhTj8/5ubMTXMrSF95SJ52ijVB8l+0hYM1+V0/D8VTwj951lHgwKiOTm2TAOKKIbMXmbwzMW8E9ZIa4LkWJk/VlBqCy3cL604s8x2rcCPi62q8JN1CgQIyu0LXfDJ6mo5Yg1OZqTsEqsfnKleaB9Faek6X1OtjbO3r9EkKEFvShSgyo8SxtZWnEtq77P0fgbtBkjC4u3OsZfXffc6CQwB3XSy3voORUmcAfuTnHNYN0z+ZqXb4VmBxXc84gD+727zvLl8YHiNu3aQg==

[FB] Static Analysis Tool Challenge

Group Post:

In 2015, the Product Security team started developing a new static analysis tool. Bobby remembered that the tool runs over Facebook's entire web codebase and was designed to run at scale, but can't think of the name.

Find the name of the tool and give your answer in lowercase using the format: BountyCon{}

A quick google search would tell you that FB only has a single static code analysis tool, called Infer. I was under this false impression for a while and tried BountyCon{infer} and its variations like BountyCon{fbinfer}. After a while, when I reread the post, I realized that the said tool was developed by the Product Security team, and Infer was developed by a different team. After a few complex google searches, I found that the tool is "Zoncolan". It has very few mentions on the web. You can find more info about it here.

[FB] Android Manifest Challenge

Bobby's intro post hints:

A lesser known fact, though, is that Bobby is the only person in the Singapore office that is part of two teams at the same time: the team that works on Facebook for Android, and the team that works on some of our web services […]

This made me certain that there were some flags in the Facebook for Android app. I downloaded the latest version of the apk from apkpure.com, extracted it using apktool and recursively searched for BountyCon in the directory:

apktool d com.facebook.katana.apk
grep -ri bountycon .

This found two matches, one in the AndroidManifest.xml file:

<meta-data android:name="bountycon_flag" android:value="BountyCon{m4n1f357_r3c0nn41554nc3}"/>

And the other match was in smali_classes12/com/facebook/katana/activity/PsaaActivity.smali which was slightly obfuscated. apktool extracted smali code which was quite complex so I tried a few different tools to get decompiled Java code for the PsaaActivity class:

public final void S(Bundle paramBundle) {
  String str = getIntent().getStringExtra("secret");
  if (str.equals("bountycon_secret_53cur3dbycryp70!"))
  {
  	byte[] arrayOfByte;
    int[] arrayOfInt = { 264, 444, 468, 440, 464, 484, 268, 444, 440, 492, 396, 456, 208, 488, 484, 380, 400, 204, 204, 448, 432, 196, 440, 428, 380, 436, 208, 216, 196, 396, 500 };
    arrayOfByte = new byte[arrayOfInt.length];
    for (int i = 0; i < arrayOfInt.length; i++) {
      arrayOfByte[i] = ((byte)(arrayOfInt[i] >> 2));
    }
  	Toast.makeText(getApplicationContext(), new String(arrayOfByte), 0).show();
  } else {
  	Toast.makeText(getApplicationContext(), "Almost there, something is missing", 0).show();
  }
}

The flag is present in the array and you can simply copy the relevant code and run it to get the flag.

I expect the FB guys obfuscated it because they wanted you to go to the deep link to find the flag. The deep link is fb://bountycon_flag_activity?secret=bountycon_secret_53cur3dbycryp70!. Without the secret param, you'll see a toast notification that says "Almost there, something is missing".

You might have noticed that there was no match for bountycon_flag_activity. This is because that string was part of a switch-case statement and got optimized by the compiler as ntycon_flag_activity in smali_classes5/X/4Si.smali.

If you're an iOS user and have no accessible Android device, you can install BlueStacks on your Mac/PC. Then you can click the above deep link on this webpage (in the Android browser) which will take you to the FB app and display the flag: BountyCon{cr4zy_d33pl1nk_m461c}

You can also use adb to go to the deep link directly:

adb shell am start -W -a android.intent.action.VIEW -d "fb://bountycon_flag_activity?secret=bountycon_secret_53cur3dbycryp70!"

[Google] VRP Report Page -> Name computations using aria-*

The page where you can report security bugs to Google, https://goo.gl/vulnz, had some hidden HTML which wouldn't be easily caught if you simply searched for "BountyCon":

<div style="height: 1px; width: 1px; position: fixed; opacity: 0.01;">
  <textarea class="hidden" readonly aria-labelledby="bc_3ba">Flag</textarea>
  <div id="bc_180" aria-owns="bc_56c bc_e81 bc_5c1">Con{73</div>
  <div id="bc_4f4">11392f</div>
  <div id="bc_56c">775676</div>
  <div id="bc_873" aria-owns="bc_f0f">88c21f</div>
  <div id="bc_e81" aria-owns="bc_4f4 bc_873">a022a6</div>
  <div id="bc_5c1">585c3a</div>
  <div id="bc_3ba" aria-owns="bc_d5b bc_180 bc_9f3"></div>
  <div id="bc_d5b">Bounty</div>
  <div id="bc_f0f">80f689</div>
  <div id="bc_9f3">66}</div>
</div>

To get the flag, you can simply rearrange the parts of the string with the help of the aria-* and id attributes. My first impulse was to write a script for this but that was overkill and I did it by hand.

Some time later, I found a much simpler method. You can open Chrome Dev Tools, select the hidden <textarea> and switch to the Accessibility tab to find the flag (just remove the extra spaces): Chrome Dev Tools Accessibility Tab

[Google] DNS Record

I expected them to hide at least one flag in DNS records and although bountycon.facebook.com and a lot of other variations like bountycon.fb.com or bountycon.goo.gl didn't yield anything, bountycon.google.com did:

$ dig ANY bountycon.google.com
[...]
bountycon.google.com.	1336	IN	TXT	"flag=BountyCon{9de████████████████████████}"
[...]

Later FB ended up hiding a flag in DNS as well and posted a hint in the group.

[FB] IDOR Challenge

Initially, I expected the submit flag endpoint to be well tested and have no simple vulnerabilities like SQL injections so I didn't test it for anything. Some time later, as it turned out FB was hiding flags mostly in security related pages, I figured the submit page should have a flag hidden. So I tried submitting Robert'); DROP TABLE Students;-- (and few other strings) since they referenced Bobby Tables but that didn't work.

Inspecting Submit Form The form had a hidden user_id field containing your user ID, which if you modified to another valid user ID (I used 4, which is the smallest valid user ID), and submitted the form, you'd get a popup containing another flag: IDOR Flag

[FB] Edit History Challenge

I was reading Bobby's intro post multiple times and on closer inspection, found that it was edited. All you had to do was click "View Edit History" and you'd find the flag: View Edit History Edit History Flag

Quite cleverly hidden in plain sight.

[Google] Hall of Fame

Google's Hall of Fame page is one of the primary links on their VRP site. It had a flag hidden in a JS file. You wouldn't find this simply by searching "BountyCon" in the page source, but by searching it in all requests (using the browser's developer tools or a proxy etc.)

https://bughunter.withgoogle.com/static/js/app_compiled.js had this piece of code:

"https://placeholder.bountycon" == d.website && alert(atob("Qm91bnR5Q29uezVmOTAxZjUyZmU0ODA2YjgyYjViZWRjMzlhNDQyMWFmfQ=="));

If in your HoF profile, you set your website address to https://placeholder.bountycon, you'd get an alert with the flag. Instead you can simply copy the code and run it in the browser console, or copy the base64 encoded string and decode it using your favorite tool:

atob("Qm91bnR5Q29uezVmOTAxZjUyZmU0ODA2YjgyYjViZWRjMzlhNDQyMWFmfQ==")
> "BountyCon{5f9████████████████████████}"

[Google] Bug Hunter University ICS file

Google linked to their Bug Hunter University site a bunch of times on their VRP site. It has a search box where if you search for "BountyCon", you'd find a BountyCon.ics file.

You can open the file in any text editor to find the flag:

X-SECRET-FLAG: BountyCon{198████████████████████████}

Normally people might just double-click the ICS file, which will open up your calendar app but not show you the flag.

[Google] VRP Home Icon

VRP Home Icon

The VRP home page has a little flag icon on the top right section of the page. Another flag cleverly hidden in plain sight. Since it was an image file, I downloaded it expecting it to have the flag hidden with steganography. However that wasn't the case and when I went to look for hints in the source code, I found the alt attribute contained the flag in base64 encoding.

<img src="images/flag.jpg" alt="Qm91bnR5Q29ue2JlYjkzZjY4ZmNmMDQ1N2M1OTdkYTg0YTZjZGI0MGMzZTk4NWQxYjB9">

BountyCon{beb████████████████████████}

[FB] Stegnography Challenge

Bobby Tables, the user operating FB's group, had a pretty blank profile except for a cover photo with "F5" as the description.

I looked at this image and the "F5" text but it didn't make any sense. Does it want me to press the F5 key? (I tried that.) While researching steganography methods for the previous flag (Google's VRP home icon), I found an interesting steganography algorithm called F5. Things immediately clicked and it became certain that Bobby's cover image had a flag hidden with the F5 steganography algorithm.

I saved the full resolution version of the image from Facebook (make sure the file name ends with _o.jpg). Then I downloaded a F5 decoding script (f5.jar, VSL) and tried "BountyCon", "" etc. as the password but that didn't work so I tried another decoding script written in a different language (f5stegojs). None of that worked, so I tried poking through the image in other ways. I uploaded it to this online tool that hides an image in another image: https://incoherency.co.uk/image-steganography/ and found the password. You might be better off using stegsolve for this since it outputs a clearer image:

Finally, you can install f5stegojs and run this to get the flag:

$ f5stego -x -p BountyConStegan0graphy f5.jpg output.txt
130 bytes was extracted to "output.txt".
$ cat output.txt
BountyCon{Aav████████████████████████}

There's also an in-browser version of this tool.

[Google] Issue Tracker -> Hidden Sheet

I was reading a bug bounty write-up or a presentation where I first saw Issue Tracker being mentioned. It's the site where you can see your submitted bug reports and respond to replies by the Google security team. People who have submitted security bugs to Google must be familiar with it (I wasn't). It's related to security and seemed liked another place where Google would hide a flag.

I searched for "BountyCon" in the search box but that resulted in zero matches. Some days later, I thought of trying it again which was a good idea because Google ended up creating an issue with the title "BountyCon". After this happened, whenever I had an idea about where they might hide flags and I wouldn't find it there, I would save the idea and try it again a few days later so that I don't miss any flag if they hide it in the future (not a fun activity).

The "BountyCon" issue only had the following text:

16VFAH9dkFwQR7t1zRDuiaTgqe0j0C6fVxKQjVYETqMA

(this is not the flag)

The string looked like it was encoded or part of a link. I attempted base64 decoding first and then began treating it as a link fragment. https://goo.gl/16VFAH9dkFwQR7t1zRDuiaTgqe0j0C6fVxKQjVYETqMA and https://g.co/16VFAH9dkFwQR7t1zRDuiaTgqe0j0C6fVxKQjVYETqMA were 404ing.
Google docs/sheets/slides URLs have IDs starting with "1" and the string looked like a document ID. So you just need to get an existing URL and swap the ID: https://docs.google.com/spreadsheets/d/16VFAH9dkFwQR7t1zRDuiaTgqe0j0C6fVxKQjVYETqMA/ I tried it as a "spreadsheet" first (other options are "document", "presentation" etc.) and luckily it worked.

The title of the spreadsheet is "BountyCon flag is hidden in this spreadsheet". There's a hidden sheet with the flag as its name: Google Hidden Sheet

You can simply go to the page source, search for "BountyCon{" and get the flag.

Initially when I submitted this flag, it didn't accept it as valid, so I DM'd @GoogleVRP on Twitter and told them about it. They said they fixed it and told me to try again but it didn't work at least until 15 hours after their response. I had been trying it every hour to make sure I was the first person to submit it. (I was. 💯)

[FB] ROT13 Whitehat Submission Page Challenge

Just like Google, FB also hid a flag in the page where you can report security bugs. You can simpy go to the Report Vulnerability Form and search for "BountyCon" in its source:

"bountyconFlag":"ObhaglPba{Nnf████████████████████████}"

After the page is rendered by React, this input element is also added to the DOM:

<input name="flag" type="hidden" value="ObhaglPba{Nnf████████████████████████}">

The twist here is that the flag is encoded with a very common substitution cipher called ROT13. I had pre-computed "BountyCon" in different encodings some flags ago and saved them so I immediately recognized "ObhaglPba" as being in ROT13.

[FB] Console Log Challenge

After finding a flag in the Whitehat pages, I figured there would be more flags in other Whitehat pages. The Whitehat Info page had a script that logged the flag to the console:

<script>console.log("Log error information: \"Qm91bnR5Q29u████████████████████████\"")</script>

You must be familiar with Qm91bnR5Q29u by now. It's just another flag encoded in base64.

[Google] Public Cloudstore Bucket

One of the places I thought Google could hide a flag was in a public storage bucket. I tried http://storage.googleapis.com/bountycon/ but no bucket with that name existed. I tried it again a few days later and it said permission denied, which meant someone had registered the bucket name. A few more days later, it was accessible and had a single 1.55 GiB file named "dec-5-data.csv". At first glance, it looked like someone not FB/Google had uploaded a file to that bucket, but I still had to be sure so I downloaded the file on a server and ran grep on it.

$ wget http://storage.googleapis.com/bountycon/dec-5-data.csv
$ grep -i bountycon dec-5-data.csv
> Hyponephele maroccana,Male ,,Ecuador,"quiet steps behind him. That didn't bode well. Who could be following him this late at night and in this deadbeat part of town? And at this particular moment, just after he pulled off the big time and was making off with the greenbacks. Was there another crook who'd had the same idea, and was now watching him and waiting for a chance to grab the fruit of his labor? Or did the steps behind him mean that one of many law officers in town was on to him and just waiting to pounce and snap those cuffs on his wrists? He nervously looked all around. Suddenly he saw the alley. Like lightning he darted off to the left and disappeared between the two warehouses almost falling over the trash can lying in the middle of the sidewalk. He tried to nervously tap his way along in the inky darkness and suddenly stiffened: it was a dead-end, he would have to go back the way he had come. The steps got louder and louder, he saw the black outline of a figure coming around the corner. Is this the end of the line? he thought pressing himself back against the wall trying to make himself invisible in the dark, was all that planning and energy wasted? He was dripping with sweat now, cold and wet, he could smell the fear coming off his clothes. Suddenly next to him, with a barely noticeable squeak, a door swung quietly to and fro in the night's breeze. Could this be the haven he'd prayed for? Slowly he slid toward the door, pressing himself more and more into the wall, into the dark, away from his enemy. Would this door save his hide? BountyCon{dfb████████████████████████}",Michael,678

The file is full of random text taken from multiple sources including this.

[FB] m.facebook.com Page Comment Challenge

If you go to Facebook's mobile site and visit any FB page (page as in a Facebook Page, not any page) like this one, you'll find a flag in the source just like the Group Comment Challenge one:

<div style="display:none">BountyCon{Aas████████████████████████}</div>

[FB] Header Challenge

I was googling something similar to site:facebook.com security when I found this link: https://www.facebook.com/workplace/security

My response searching tool found a flag in the header:

x-bountycon: BountyCon{Aau████████████████████████}

[Google] Bug Hunter University Steganography

After finding BountyCon.ics in the BHU site, I checked out its RSS feed: https://sites.google.com/feeds/content/site/bughunteruniversity to check out if there's anything else that was recently updated or added. The ics file was added on 2018-12-12, the home page was updated on 2018-12-11 and a few days ago two files were added: bug200_new (1).jpg and bug200_new.jpg. The home page referenced the former image. The file name seemed suspicious because of the "(1)" and "_new". I checked out both files in two steganography tools, this one (nothing there) and this online wrapper of steghide. There was a flag hidden in bug200_new (1).jpg with steghide:

"steganoin29193.jpg":
  format: jpeg
  capacity: 611.0 Byte
  embedded file "flag.txt":
    size: 52.0 Byte
    encrypted: no
    compressed: no

> BountyCon{f67████████████████████████}

This was the second flag that wasn't accepted as valid. I looked up who uploaded the image in the feed (Eduardo Vela Nava) and emailed him about this. He responded almost instantly that he'll have a look. About a day later the flag finally got accepted and he replied that they had fixed it.

[Google] VRP Twitter Bio

This one was very cleverly hidden in plain sight. The @GoogleVRP account had a link in their bio:

@GoogleVRP bio

This link led to https://www.google.com/about/appsecurity/?flag=Qm91bnR5Q29uezEyYzA2NTU1MTM3NTdhYmIyZWYxODk5ZWZlMzc2YTUyZjcxMTFjY2J9

The flag is present as a query param in the URL in base64 encoding:

BountyCon{12c████████████████████████}

This one wouldn't have been caught by the searching tool unless you clicked the link in the bio.

[Google] TLS Certificate

While googling "BountyCon", I found bountycon.corp.google.com being mentioned in one of the results leading to malwaredog.com. *.corp.google.com addresses are only accessible over Google's internal networks so I assumed it was just an internal site related to BountyCon that Google staff were using for themselves. Some days later, I searched "BountyCon" on crt.sh, which is a tool to search certificate transparency (CT) logs. A certificate was issued for this domain by Google. I looked into the certificate and found that it was registered for another two domains:

bountycon.corp.google.com ijxxk3tupfbw63t3mu2dkzdemfstkyrzgm4tim3bg.corp.google.com y4tqylbmjrtqm3fmvqwmn3fmi3dszlegbstkylbpu.corp.google.com

I figured ijxxk3tupfbw63t3mu2dkzdemfstkyrzgm4tim3bg and y4tqylbmjrtqm3fmvqwmn3fmi3dszlegbstkylbpu were encoded strings or link fragments and since base64 requires upper case characters, it might be encoded with base32. I tried CyberChef to base32 decode the text but had no luck. I began trying more complex ideas like a custom implementation of base36, manually adding DNS entries for those domains to my /etc/hosts file, tweaking the Host header etc. I even had the crazy idea of trying out all IPs owned by Google (I generated an IP list of about 350k) until one resolves the page for that domain or bountycon.google.com. After a while, my suspicion that it's just an internal site only increased.

A few more days later, I ended up looking into this again and tried base32 decoding with a different tool and it decoded the text successfully! I went back to CyberChef and realized that it ignored all lower case chars and was expecting upper case chars instead. I was 99% there but a tiny oversight delayed me. (Although I was still first to find the flag.)

Base32 decoding ijxxk3tupfbw63t3mu2dkzdemfstkyrzgm4tim3bgy4tqylbmjrtqm3fmvqwmn3fmi3dszlegbstkylbpu:

BountyCon{e45████████████████████████}

[FB] DNS Challenge

Bobby posted another challenge to the group about 19 days after the last post:

Bobby recently worked on a DNS entry in *.facebook.com. Can you figure out what entry it is?

My initial thought was that I'd find something in bountycon.facebook.com, just like the Google flag, which would have been quite uninteresting of a challenge. I tried a bunch of variations of bountycon.facebook.com but none yielded anything.

I ran dnsrecon to enumerate the subdomains but soon realized it didn't fetch the TXT records so I wrote a shell (fish) script to enumerate over a wordlist and extract all DNS records:

mkdir dns
for i in (cat subdomains.txt)
    if not test -e dns/$i
        dig +short ANY $i @8.8.8.8 > dns/$i
        la dns/$i
        grep -i BountyCon dns/$i
    end
end

The wordlist that came with dnsrecon had 1 million entries and that's the one I used. Quite soon enough, the script found the flag in push.facebook.com:

push.facebook.com.	21599	IN	TXT	"BountyCon{h1dd3n_1n_dns_r3c0rds}"

This was the third flag that wasn't being accepted so I emailed [email protected] (bounced), emailed [email protected], emailed and DM'd Neal Poole, messaged Dan Gurfinkel (Security Engineering Manager) on FB. There was no response from anyone for over 6 hours so I emailed Eduardo again to relay the message to FB. I assumed he'd contact them over a more direct channel but he simply forwarded my email to [email protected], which was the email address I should have originally used here. As with the previous flags, I had been submitting this flag every hour to be the fastest to submit it, and about an hour after the flag got submitted, I got a response from [email protected] that it's been fixed.


That's all the flags I found. The last flag I submitted was on Jan 29 (the FB DNS one). There was still ~22 days remaining but I didn't spend much time on finding more flags.

If you found a flag not listed here, let me know and I'll add it here.

[Google] Clickjacking script

There was a flag that 3 other people found (98 points) but I didn't. Two of the people don't exactly know how they found it, it got caught in their Burp history. The one remaining person said they found it on Google's Law Enforcement Request System website.

There might have been a script on that page that made a clickjacking attempt. It seems like the script has since been removed from that site since the CTF ended so this cannot be verified. The iframe's source was https://www.google.com/analytics/web/optout?token=redacted&email=redacted

If you check out the source of that link, you'll find the flag:

<xmp data-flag=BountyCon{0bf████████████████████████}>.</xmp>

Flags Page Enhancement Userscript

The flags page was pretty rudimentary so I wrote a quick userscript to enhance it: Flags Page Normal Flags Page Enhanced

It sorted the flags by points, added a Google/FB icon, colored the points cell and added some stats (count, total, max, avg). Flags with 90 or higher points were marked green. I could easily see how many flags were found by 11 people or less. The total let me track how active other participants were. There was no indication in the page source if a flag belonged to FB or Google, but FB had been consistent in ending the flag descriptions with the word "Challenge" so the script simply checked for that.

If you'd like to install it, install Tampermonkey then click this link


Here's some things I tried that didn't get me any flags:

  1. Asked Google Assistant to give me a flag and rephrased the question multiple times.
  2. Wrote a small crawler for recursively finding all sitemap.xml files leading from https://www.google.com/sitemap.xml. FB didn't have any sitemap.xml files.
  3. Checked out http://bountycon.blogspot.com, http://bountycon.appspot.com, http://bountycon.firebaseapp.com, http://bountycon.withgoogle.com
  4. Probed the Firebase database used by Google CTF 2018: https://angels-captured-the-tardis.firebaseio.com/. Also checked https://bountycon.firebaseio.com/.json
  5. Checked out http://vrp-game.appspot.com, Google's older VRP site
  6. Emailed Google (Eduardo and Jan Keller): "Is there a flag that's not hidden anywhere but in the possession of you guys only and to retrieve it all someone has to do is just ask? :) Perhaps to somewhat simulate a social engineering attack? 🤞". They replied "lol, no, but nice try" and "Haha, good idea for next time! :)"
  7. Sent empty emails to [email protected], [email protected], [email protected], [email protected] hoping to get a flag in response but most of the mails bounced. Headers didn't have anything either.
  8. Connected over Gmail's IMAP, POP, SMTP protocols:
    openssl s_client -connect imap.gmail.com:993
    openssl s_client -connect pop.gmail.com:995
    openssl s_client -starttls smtp -connect smtp.gmail.com:587 -crlf -ign_eof
    

    and poked around with the "BountyCon" string (although it was extremely unlikely they would modify critical services).

  9. Attempted steganography on @GoogleVRP's profile pic.
  10. Checked out Security Team Victim accounts: GitHub, YouTube etc.
  11. Searched for "BountyCon" on Shodan to find any hidden servers that might have a flag.
  12. Checked out /bountycon for these URL shorteners: goo.gl, g.co, youtu.be, fb.me, fb.gg, m.me
  13. Made sure that Bobby Tables actually doesn't have a profile picture set instead of having the default image reuploaded with data hidden using steganography. Checked the same with Bobby's Daily Thoughts' cover image.
  14. Checked out the test users (Bob, Alice) and group mentioned on the FB Whitehat resources page.
  15. Modified the form method to PUT/DELETE on the submit flag endpoint and uploaded different harmless binary files in the flag_value field.
  16. Tried requesting JS modules named BountyCon, XWhitehatCTFWinnersController, XWhitehatCTFAdminController etc.: https://www.facebook.com/ajax/bootloader-endpoint/?modules=BountyCon Also tried requiring the modules by calling the require/requireLazy functions in the browser console: require('BountyCon')
  17. Accessed Facebook's pages over Tor: facebookcorewwwi.onion

Overall, the CTF and event was quite fun. Getting a chance to give a talk at the event was amazing as well. Kudos to the people who made it happen.