2 ## web: protected-area (unsolved)
3 A web page is given which printed the content of a file from a directory to the html of the page.
5 First I looked up the source file of the webpage. It was importing the following JS script:
7 var file_check = function(file){
9 url: '/check_perm/readable/',
11 }).done(function(data){
20 var file_read = function(file){
24 }).done(function(data){
30 var update_page = function(text){
34 $(document).ready(function() {
35 console.log("ready!");
36 file_check('public.txt');
39 The first command `file_check` executed like this `http://66.172.33.148:8008/check_perm/readable/?file=public.txt` gives either `True` or `0`, depending if the file exists. If the file is not found it prints in the console `fail`. If it exists its being passed to the command `file_read`.
41 The second command `file_read` executed like this `http://66.172.33.148:8008/read_file/?file=public.txt` opens the file. It then passes itas an argument to `update_page`. If the file is not found `security` or `500` is being printed.
43 The trird command `update_page` takes the passed text and appends to the DOM.
45 I tried path triversal by encoding the ../ in UTF-8, by replacing ../ with ..././ or ..;/, but it seems the dots and slash are still being replace, bacause `http://66.172.33.148:8008/read_file/?file=../../../public.txt` is being interpreted the same as `http://66.172.33.148:8008/read_file/?file=public.txt`
47 There was also `private.txt` file in the same directory as `public.txt` with the content `lol haha`.
49 I tried to mess up with the console:
50 `update_page("<script>alert()</script>")` executes the script and the pop up shows, hints towards an XSS attack.
52 I tried looking for a way to list the directory via javascript
54 const testFolder = './public.txt';
55 const fs = require('fs');
57 fs.readdir(testFolder, (err, files) => {
58 files.forEach(file => {
64 ## web: cursed-app (unsolved)
66 An `cursed_app.elf` file is given.
67 The description of the challenge stated `If we want to say 100, should we start at 1 and count until 100?` and `sha256(flag) = dd791346264fc02ddcd2466b9e8b2b2bba8057d61fbbc824d25c7957c127250d` was given.
69 First I tried to brute force the hash using variuos websites. No luck there.
70 Hexdumping the file reveals some readable strings: `hexdump -C cursed_app.elf`.
71 After that I found a more useful command `strings`, which filters only the readable strings.
72 Intressting segment was
75 please locate license file, run ./app license_key
76 Congratz! You got the correct flag!!
77 Bummer! You got the wrong flag!!
80 So I tried executing file. If it's being run without arguments the first message is printed: `please locate license file, run ./app license_key`, if a file is specified it prited `Bummer! You got the wrong flag!!`.
82 So clearly the program was reading the flag file and computing and comparing the string.
84 I tried `strace` and `ltrace` to understand better what functions and libraries are being called.
86 Using `objdump -d cursed_app.elf` showed me that there was no main function.
88 I then installed `Hopper`, which is a reverse engineering tool that lets me disassemble, decompile and debug the applications. With it I tried to undestand the execution pattern and to find out how and when `Congratz!` is being printed. I figured out that after opening the flag/key file 59 characters are being read and most likely compared (although no cmp function was found). Those 59 chars could be brute forced and encrypted with `sha256` and then compared with the given solution in the description. A brute force of 59 chars seems impossible.
90 ## web: Golden_Delicios (unsolved)
92 A `.sage` file is given, containing the following python scrypt. And at the bottom of it, there were the results of the print function commented.
97 from sage.all import *
100 print '(A_1, B_1) =', (A_1, B_1)
101 print '(A_2, B_2) =', (A_2, B_2)
103 E_1 = EllipticCurve(Zmod(n_1), [A_1, B_1])
104 E_2 = EllipticCurve(Zmod(n_2), [A_2, B_2])
106 d_1 = int(flag[:len(flag)/2].encode('hex'), 16)
107 d_2 = int(flag[len(flag)/2:].encode('hex'), 16)
109 assert d_1 < n_1 and d_2 < n_2 and n_1 * n_2 % 2 == 1
121 The EllipticCurve function was interesting. After googling it I found out that there exists a very popular algorithm in cryptography for encrypting public keys.
122 The formula for an elliptic curve is as follows: `y^2=x^3+ax+b` and the parameters corresponded to those used in the script, so I guessed I was on the right track.
123 After that I spent time reading wikipedia articles and watching youtube youtibe videos explaining it. But after some time trying to understand it, I gave up... I decided to concetrate more on the other challenges.
125 ## web: Bit_game (unsolved)
126 The following script was given, together with a text file containing the resulting H and enc variables:
129 from Crypto.Util.number import *
130 #from flag import flag
132 def gen_rand(nbit, l):
135 r = random.randint(0, nbit-1)
143 rbit += (R[i+1] - R[i] - 1) * '0' + '1'
144 rbit += (nbit - R[-1] - 1) * '0'
149 f, skey = gen_rand(n, l), gen_rand(n, l)
150 pkey = f * inverse(skey, p) % p
151 return (p, pkey), skey
153 def encrypt(msg, pkey):
155 msg, enc, n = bytes_to_long(msg), [], len(bin(p)[2:])
157 for b in bin(msg)[2:]:
158 s, t = gen_rand(n, l), gen_rand(n, l)
166 p = 862718293348820473429344482784628181556388621521298319395315527974911
169 pkey, skey = genkey(p, l)
170 enc = encrypt(flag, pkey)
176 I think I was on the right track on this one. Sadly this challenge was released on the last day and I didn't get the opportunity to solve it during the CTF. The CTF was over and the challenge was left unsolved. I had an Idea how to begin, but applying what I found didn't produce meaningful output. I continued working on it afterwards, since I spent the most time on it and I wanted to finish it.
177 Nevertheless this is what I managed to find out during the CTF:
178 Note: A lot of time the was spent reading articles, researching and understanding the underlying theorems and math applied.
180 1) Numbers produced by the 'gen_rand()' function are not completely random. Their binary representation always have five '1's.
182 2) Equation of the form x^2 ≡ q (mod n) are called "Quadratic residues".
184 3) Reversing H = pkey[1] ** 2 % p is a hard problem if 'p' was a prime, but since it is not, we could split it into factors and try to find solutions for all the individual factors pi: pkey^2 ≡ H (mod pi).
186 4) Legendre symbol or Euler’s Criterion can be used to check if a number is a quadratic residue.
188 5) The solutions from the individual factors can be combined via the "Chinese remainder theorem" to find a solution to the original problem, meaning we will find the public key.
190 6) For finding solutions to the individual problems there are two approaches:
191 - If `'a'` is a quadratic residue (mod p) and `p ≡ 3 (mod 4)` then `a^((p+1)/4)` is a solution to `x^2 ≡ a (mod p)`.
192 - If `a ≡ 1 (mod 4)` there is no analogous formula. In that case, one may use the "Tonelli-Shanks algorithm".
194 PS: I posted a detailed solution for this challenge as well.