2 I tried multiple challenges that day, mainly together with @smashing and @HaH__. Below are four challenges I attempted the longest most of which were rather frustrating, especially looking at the bash documentation/PHP sourcecode for quite a long time.
3 Sadly, I didn't notice that the event ended early Sunday since I wanted to do a second take on the bash challenge but I was too late.
5 All in all it was interesting but in my opinion the CTF wasn't as fun as other CTFs in the past since it kinda seemed a bit reliant on guesswork (Sandstorm: Hinted (for me unknown) algorithm in the image, ZKPay: crypto but no source).
7 Time spent: ~10 hours total (I don't remember the breakdown exactly)
9 # Option-Cmd-U (Successfully sovled by a teammate)
11 The enables one to view the pagesource of an http webpage. The flag is located at `\flag.php`, however it is only accessible on the local network.
12 The challenge is powered by `nginx` and `php-fpm`.
15 A look at the sourcecode (see below) immediately hinted at a potential problem.
17 if($parsed_url["scheme"] !== "http"){
18 // only http: should be allowed.
19 echo 'URL should start with http!';
20 } else if (gethostbyname(idn_to_ascii($parsed_url["host"])) === gethostbyname("nginx")) {
21 // local access to nginx from php-fpm should be blocked.
22 echo 'Oops, are you a robot or an attacker?';
24 // file_get_contents needs idn_to_ascii(): https://stackoverflow.com/questions/40663425/
25 highlight_string(file_get_contents(idn_to_ascii($url),
27 stream_context_create(array(
29 'follow_location' => false,
35 Firstly, `Oops, are you a robot or an attacker?` is a strong indication that one should bypass this if check.
36 Secondly, `idn_to_ascii()` is used differently than in the provided stackoverflow link.
38 On a second look it became apparent that `idn_to_ascii` is used twice, however different parts of the URL are converted.
39 This seemed to hint at a potential attack vector.
41 Thus, I tried to trigger `Oops, are you a robot or an attacker?`. After lots of trial and error I found the rather obvious solution `http://nginx`.
42 Consequently, I tried various UTF-8 characters, such as `http://nginxä/flag.php` which bypassed the filter but resulted in errors as it was parsed as `xn--http://nginx/flag-1qb.php` (aka Punycode).
43 I had at look at the PHP sourcecode to find out if one could discard the `xn--` part by tricking the parser somehow but it didn't seem possible, especially since the `scheme` is checked at first.
44 Next, I tried `http://nginxß/flag.php` which was parsed as `http://nginxss/flag.php` so some unicode characters did not result in punycode starting with `xn--` but were converted to ASCII.
46 After not finding any fitting UTF-8 characters I gave up and tried different challenges. However, @smashing later solved it by using some obscure features of `file_get_contents`.
47 A host starting with `@` is accepted by `file_get_contents` and there exists a UTF-8 character that is parsed to `@`. Thereby bypassing the check and but still being parsed correctly by `file_get_contents`.
48 The final payload was `http://ocu.chal.seccon.jp:10000/index.php?url=http%3A%2F%2F%EF%B9%ABnginx%2Fflag.php`.
50 # ZKPay (Unsuccessful)
52 It is basically some sort of banking app. One can create an account (which is heavily delayed to make the creation of many accounts difficult) and get 500 starting cash by the admin.
53 To receive the flag one has to accrue 1000000 cash. Transactions are handled by generating QR codes in order to send money and scanning (i.e. uploading them) in order to receive money.
56 As it was listed as a crypto challenge and its name starts with `ZK` I assumed that it somehow is connected to zero-knowledge proofs.
57 First, I used the software barcode reader `zbar` to read some QR codes that I generated from my account (see below).
59 username=blurp&amount=1&proof=MHhCzoo/gCGDFP6tUp12doDKTC/p3uiklZ6ykG/VyKQLMCAwYQI7Q0zjyGerFCUqDqPb35cPXWcyd+dGaNy99JcJYCowCjC1GCIDWa/iVQbE45IBHChrkS4MC68BLHGcApiKSJkiLlkMiqWV3ALjj5h3WCxb5pqr9JAJzqoco6uu4AUEpvsdMSAwQYzKUGN6UX147Gm9rPvqn9UDF15HOTx+eyw02xX54BUwCjBRAyqPlz/XW4JFdKQ/KD3vI3l8iaCxQEf7y6KT31VNLDEgMKj4Wx+JLFMwfY7EvLdhHyHjr8JP8zwG/dbx7ivhcFAUMQow3AuO8oJxO002ulEHZ1Oi9DPiWnDhmX+o5zC35Qn8eRExCjC6XHq+/PgTI42EilzICJ+PDmyWv3Tn0nkjg/jhhNaAETAK&hash=fbea46e3ff4db1cb46199fb5e64771240709870628739b46e534f66abf730fb2
60 username=blurp&amount=2&proof=MHhCzoo/gCGDFP6tUp12doDKTC/p3uiklZ6ykG/VyKQLMCAwYQI7Q0zjyGerFCUqDqPb35cPXWcyd+dGaNy99JcJYCowCjC1GCIDWa/iVQbE45IBHChrkS4MC68BLHGcApiKSJkiLlkMiqWV3ALjj5h3WCxb5pqr9JAJzqoco6uu4AUEpvsdMSAwQYzKUGN6UX147Gm9rPvqn9UDF15HOTx+eyw02xX54BUwCjBRAyqPlz/XW4JFdKQ/KD3vI3l8iaCxQEf7y6KT31VNLDEgMKj4Wx+JLFMwfY7EvLdhHyHjr8JP8zwG/dbx7ivhcFAUMQow3AuO8oJxO002ulEHZ1Oi9DPiWnDhmX+o5zC35Qn8eRExCjC6XHq+/PgTI42EilzICJ+PDmyWv3Tn0nkjg/jhhNaAETAK&hash=fbea46e3ff4db1cb46199fb5e64771240709870628739b46e534f66abf730fb2
61 username=blurp&amount=3&proof=MHhCzoo/gCGDFP6tUp12doDKTC/p3uiklZ6ykG/VyKQLMCAwYQI7Q0zjyGerFCUqDqPb35cPXWcyd+dGaNy99JcJYCowCjC1GCIDWa/iVQbE45IBHChrkS4MC68BLHGcApiKSJkiLlkMiqWV3ALjj5h3WCxb5pqr9JAJzqoco6uu4AUEpvsdMSAwQYzKUGN6UX147Gm9rPvqn9UDF15HOTx+eyw02xX54BUwCjBRAyqPlz/XW4JFdKQ/KD3vI3l8iaCxQEf7y6KT31VNLDEgMKj4Wx+JLFMwfY7EvLdhHyHjr8JP8zwG/dbx7ivhcFAUMQow3AuO8oJxO002ulEHZ1Oi9DPiWnDhmX+o5zC35Qn8eRExCjC6XHq+/PgTI42EilzICJ+PDmyWv3Tn0nkjg/jhhNaAETAK&hash=fbea46e3ff4db1cb46199fb5e64771240709870628739b46e534f66abf730fb2
62 username=blurp&amount=4&proof=MHhCzoo/gCGDFP6tUp12doDKTC/p3uiklZ6ykG/VyKQLMCAwYQI7Q0zjyGerFCUqDqPb35cPXWcyd+dGaNy99JcJYCowCjC1GCIDWa/iVQbE45IBHChrkS4MC68BLHGcApiKSJkiLlkMiqWV3ALjj5h3WCxb5pqr9JAJzqoco6uu4AUEpvsdMSAwQYzKUGN6UX147Gm9rPvqn9UDF15HOTx+eyw02xX54BUwCjBRAyqPlz/XW4JFdKQ/KD3vI3l8iaCxQEf7y6KT31VNLDEgMKj4Wx+JLFMwfY7EvLdhHyHjr8JP8zwG/dbx7ivhcFAUMQow3AuO8oJxO002ulEHZ1Oi9DPiWnDhmX+o5zC35Qn8eRExCjC6XHq+/PgTI42EilzICJ+PDmyWv3Tn0nkjg/jhhNaAETAK&hash=fbea46e3ff4db1cb46199fb5e64771240709870628739b46e534f66abf730fb2
63 username=blurp&amount=5&proof=MHhCzoo/gCGDFP6tUp12doDKTC/p3uiklZ6ykG/VyKQLMCAwYQI7Q0zjyGerFCUqDqPb35cPXWcyd+dGaNy99JcJYCowCjC1GCIDWa/iVQbE45IBHChrkS4MC68BLHGcApiKSJkiLlkMiqWV3ALjj5h3WCxb5pqr9JAJzqoco6uu4AUEpvsdMSAwQYzKUGN6UX147Gm9rPvqn9UDF15HOTx+eyw02xX54BUwCjBRAyqPlz/XW4JFdKQ/KD3vI3l8iaCxQEf7y6KT31VNLDEgMKj4Wx+JLFMwfY7EvLdhHyHjr8JP8zwG/dbx7ivhcFAUMQow3AuO8oJxO002ulEHZ1Oi9DPiWnDhmX+o5zC35Qn8eRExCjC6XHq+/PgTI42EilzICJ+PDmyWv3Tn0nkjg/jhhNaAETAK&hash=fbea46e3ff4db1cb46199fb5e64771240709870628739b46e534f66abf730fb2
64 username=blurp&amount=10&proof=MHhCzoo/gCGDFP6tUp12doDKTC/p3uiklZ6ykG/VyKQLMCAwYQI7Q0zjyGerFCUqDqPb35cPXWcyd+dGaNy99JcJYCowCjC1GCIDWa/iVQbE45IBHChrkS4MC68BLHGcApiKSJkiLlkMiqWV3ALjj5h3WCxb5pqr9JAJzqoco6uu4AUEpvsdMSAwQYzKUGN6UX147Gm9rPvqn9UDF15HOTx+eyw02xX54BUwCjBRAyqPlz/XW4JFdKQ/KD3vI3l8iaCxQEf7y6KT31VNLDEgMKj4Wx+JLFMwfY7EvLdhHyHjr8JP8zwG/dbx7ivhcFAUMQow3AuO8oJxO002ulEHZ1Oi9DPiWnDhmX+o5zC35Qn8eRExCjC6XHq+/PgTI42EilzICJ+PDmyWv3Tn0nkjg/jhhNaAETAK&hash=fbea46e3ff4db1cb46199fb5e64771240709870628739b46e534f66abf730fb2
65 username=blurp&amount=100&proof=MHhCzoo/gCGDFP6tUp12doDKTC/p3uiklZ6ykG/VyKQLMCAwYQI7Q0zjyGerFCUqDqPb35cPXWcyd+dGaNy99JcJYCowCjC1GCIDWa/iVQbE45IBHChrkS4MC68BLHGcApiKSJkiLlkMiqWV3ALjj5h3WCxb5pqr9JAJzqoco6uu4AUEpvsdMSAwQYzKUGN6UX147Gm9rPvqn9UDF15HOTx+eyw02xX54BUwCjBRAyqPlz/XW4JFdKQ/KD3vI3l8iaCxQEf7y6KT31VNLDEgMKj4Wx+JLFMwfY7EvLdhHyHjr8JP8zwG/dbx7ivhcFAUMQow3AuO8oJxO002ulEHZ1Oi9DPiWnDhmX+o5zC35Qn8eRExCjC6XHq+/PgTI42EilzICJ+PDmyWv3Tn0nkjg/jhhNaAETAK&hash=fbea46e3ff4db1cb46199fb5e64771240709870628739b46e534f66abf730fb2
67 `username` is the username, `hash` is the address of the user, `amount` is the amount transferred and `proof` is presumably a zero knowledge proof. However, as one can notice the proof is always the same.
68 One can get the `hash` of the admin by clicking on the name of the admin in the transaction overview. Therefore, I thought one should generate a proof for the admin to transfer the money.
69 However, as there was no sourcecode and crypto challenges are often times pretty hard without the right knowledge I gave up.
71 Later it turned out that they (probably) had an error in their challenge and one could just send a negative value from ones account to another and therby increase the balance of the first account `(balance - (-100000000)`..
73 # SECCON_multiplicater (Unsuccessful)
75 It's a webinterface that multiplies two numbers. The multiplication is powered by bash.
78 Part of the challenge below
85 var1="$(echo $POST_STRING|awk -F'&' '{print $1}'|awk -F'=' '{print $2}'| nkf -w --url-input|tr -d a-zA-Z)"
86 var2="$(echo $POST_STRING|awk -F'&' '{print $2}'|awk -F'=' '{print $2}'| nkf -w --url-input|tr -d a-zA-Z)"
88 echo "$var1" '*' "$var2 = $((var1 * var2))"
90 Firstly, the program receives the variables in a string of the form `var1=10&var2=42`.
91 It filters the first (and afterwards the second) variable using `awk`, decodes characters such as `%20` to ASCII by using `nkf` and then removes all letters using `tr`.
93 After reading the bash manpage it turns out that the variables are typeset to be integers and are subject to `arithmetic evaluation`.
94 I further studied the manpage to find out if I could somehow use arithmetic expansion to parse conditional expressions and expand shell commands to conduct something similar to a blind SQL injection.
95 However, I didn't manage to get around the letter filter and it seemed like arithmetic evaluation does not expand and execute shell commands (to for example read filecontents) on my local test setup.
97 After spending way too much time with the bash manpage I decided I was making no progress and quit hoping to try it on the next day but when I returned the CTF was already over.
99 # Sandstorm (Unsuccessful)
101 It's a steganography challenge. The image is mainly black and white noise with some text inside. I assumed that the text was a hint but I sadly didn't get it (it later turned out to be vital in order to solve the challenge).
104 @HaH__ mentioned that one could try to parse the image as a binary string (white = 0, black = 1). I tried that and implemented it in python parsing from top left to bottom right.
111 return (l[i:i+n] for i in range(0, len(l), n))
113 img = cv2.imread("sandstorm.png", 0)
117 for byte in chunks(list(map(lambda x: "1" if x == 255 else "0", row)), 8):
118 counter = counter + 1
119 res = res + (chr(int("".join(byte), 2)))
120 print(res.find("SECCON"))
122 However, it was only gibberish and no flag could be found.