1 # OverTheWire Advent Bonanza 2019
5 There were some `.csv` and `.txt` files given. The `.csv` files contained timestamps, I assumed (which turned out to be correct) and the keycodes of the keys which were pressed at the corresponding timestamps. The `.txt` files contained the translated SMS, but only for three of the four `.csv` files. The challenge was to translate the last `.csv` file. Also, there was a `.c` file, which explained how the keycodes work.
6 With the help of the challenge description I knew that the keys were pressed on an old phone, where you had to press one button multiple times to get a single letter and the letter changes depending on how many times you press the button. Now I could write a simple python program, which parses the `.csv` file.
9 Here is the script I wrote, explanation is below:
11 keys= [" 0", ".,'?!\"1-()@/:", "abc2", "def3", "ghi4", "jkl5", "mno6", "pqrs7", "tuv8", "wxyz9", "@/:_;+&%*[]{}"]
14 with open(filepath) as fp:
18 line = fp.readline().strip()
22 timestamp = int(arr[0])
24 # Either another key was pressed or the time between the key presses was high enough that two separate characters should be typed
25 if (last_keycode != keycode or timestamp - last_timestamp > 1200) and last_keycode != -1:
26 out += keys[last_keycode][last_keycount % len(keys[last_keycode])]
28 elif last_keycode == keycode:
30 last_keycode = keycode
31 last_timestamp = timestamp
32 if last_keycode < 0 or last_keycode > 10:
33 if last_keycode == 101: # Backspace
38 line = fp.readline().strip()
41 The script reads the file line by line. In every line, it checks if the keycode is other than the previous one. If so, the letter (according to the amount of presses of this button) is added to the output string. The same thing happens when the time which passed since the last button was pressed exceeds a threshold (1200 in this case). This means that a new character is meant to be typed. We need to use modulo to get the correct character, as there is a "wrap-around" when you press a button more often than there are characters. For example, if you press "2" five times it should result in the letter a.
42 If the last keycode does not differ from the previous one, we simply add one to the current keycount.
43 Then, the last keycode and last timestamp variables are set. After this, it is checked if the keycode is one of the standard keys (like "1", "2", etc.). If not, ignore it. There is an exception with keycode "101", as this code represents the "Backspace" or "Delete character" button. If this button was pressed we simply delete the last character of our output string. At the end the output string is printed.
45 The output of the script is the following: `alright pal herse ye flag gnd lucj enteking it with those hnves lol its aotw{l3ts_dr1nk_s0m3_eggn0g_y0u_cr4zy_d33r}0m.. .l ,p`
46 I don't think I got the message 100% correct but it was enough for the flag:
48 Flag: `AOTW{l3ts_dr1nk_s0m3_eggn0g_y0u_cr4zy_d33r}`
50 #### day-03 Northpole Airwaves (~3h)
52 This is a forensics challenge. Given was a ~700MB large file where you probably have to extract some information.
55 First I looked for some strings in the file, but of course this would have been too easy, so nothing useful here.
56 I then used `binwalk` to get an idea of the files which hide in here. I got the following output:
59 DECIMAL HEXADECIMAL DESCRIPTION
60 --------------------------------------------------------------------------------
61 25161462 0x17FEEF6 Windows Script Encoded Data (screnc.exe)
62 30103466 0x1CB57AA Windows Script Encoded Data (screnc.exe)
63 104543102 0x63B337E Windows Script Encoded Data (screnc.exe)
64 220329450 0xD21F5EA Windows Script Encoded Data (screnc.exe)
65 492700718 0x1D5E042E Windows Script Encoded Data (screnc.exe)
68 I did some research on what this "Windows Script Encoded Data (screnc.exe)" is and found out that `screnc.exe` is some tool to obfuscate different types of scripts, including Visual Basic Script, for example.
69 After this, I was looking for a way to decode such an encrypted script. I found a script: https://www.interclasse.com/scripts/decovbe.php
70 This looked very promising, so I tried it out. Unfortunately, I got only gibberish as an output.
72 My next idea was to split the large file up into the different files it contains (I could tell this by the binwalk output). To do so, I used `binwalk` again:
74 binwalk --dd='.*' d6d0f728ac3ff4848f849b8cf222fb38a14aee870184cc22f2efe01336b2ec17-northpole-airwaves.bin
76 The `--dd` option extracts the given type signatures. In this case (`.*`) all of them. This creates a new directory containing all the files. I then used the decoding script I found before on all the files separately. Again only gibberish...
78 Well, turned out that what I did was nonsense and I should've read the description. I didn't get any further though.
80 #### day-04 mooo (~4h)
82 Given was a simple webpage with one textfield, one dropdown and one button. The textfield lets you specify a message and you can choose a "Cow" in the dropdown. The whole thing is powered by `cowsay` as the footer states which is a tool to generate ASCII pictures of a cow saying the message you provided.
84 ##### Attempts and Solution
85 First, I looked up the man pages of cowsay [here](https://linux.die.net/man/1/cowsay). There is a very interesting section:
88 A cowfile is made up of a simple block of perl(1) code, which assigns a picture of a cow to the variable $the_cow. Should you wish to customize the eyes or the tongue of the cow, then the variables $eyes and $tongue may be used. The trail leading up to the cow's message balloon is composed of the character(s) in the $thoughts variable. Any backslashes must be reduplicated to prevent interpolation. The name of a cowfile should end with .cow, otherwise it is assumed not to be a cowfile. Also, at-signs (''@'') must be backslashed because that is what Perl 5 expects.
91 "A cowfile is made up of perl code" sounds really promising. I thought that maybe we can execute arbitrary perl code somehow which would also allow us to execute system commands like `ls` or something.
93 In the dropdown you can choose different cows which are all predefined except one. The "custom" entry lets you create your own cow. I expected that this is done by using cowfiles.
94 So here I could change the cow's appereance, but was not able to inject perl code yet. I took a look at an example cowfile from [here](https://github.com/piuccio/cowsay/blob/master/cows/default.cow):
99 $thoughts ($eyes)\\_______
106 I don't know perl, but `EOC` remembered me of `EOF` or something, so I just tried to use `EOC` as a separator. I quickly looked up how to execute commands in perl (`system()`) and tried it with the following input.
110 $thoughts ($eyes)\\_______
118 It worked! I got the directory listing and the only thing I had to do now is to execute `cat flag` in the same way and I got the flag.
120 Flag: `AOTW{th3_p3rl_c0w_s4ys_M0oO0o0O}`
122 #### day-05 Sudo Sudoku (~3h)
123 The solution was already solved by our team when I got to it, but I used the chance to take a look at Z3 which I never did before.
126 Given was a `.txt` file which contained a special sudoku. It had very few prefilled numbers but instead there were some formulas given which must be fullfilled in addition to the standard sudoku rules. The formulas where basically just additions of different cells where the result has to be a specific number.
129 The solution is pretty straight forward - just solve the sudoku. Of course this would take ages to do by hand, so I used Z3 in python. To do so, I used the sudoku example from the [Z3Py Guide](https://ericpony.github.io/z3py-tutorial/guide-examples.htm) and added the additional constraints:
134 # 9x9 matrix of integer variables
135 X = [ [ Int("x_%s_%s" % (i+1, j+1)) for j in range(9) ]
138 # each cell contains a value in {1, ..., 9}
139 cells_c = [ And(1 <= X[i][j], X[i][j] <= 9)
140 for i in range(9) for j in range(9) ]
142 # each row contains a digit at most once
143 rows_c = [ Distinct(X[i]) for i in range(9) ]
145 # each column contains a digit at most once
146 cols_c = [ Distinct([ X[i][j] for i in range(9) ])
149 # each 3x3 square contains a digit at most once
150 sq_c = [ Distinct([ X[3*i0 + i][3*j0 + j]
151 for i in range(3) for j in range(3) ])
152 for i0 in range(3) for j0 in range(3) ]
154 # formulas from challenge specification
155 formulas = [X[1][8] + X[1][7] + X[2][0] + X[7][3] + X[7][3] == 23,
156 X[0][4] + X[3][6] + X[8][4] + X[6][7] + X[1][2] + X[0][4] == 19,
157 X[8][1] + X[8][2] + X[5][1] + X[4][8] == 15,
158 X[8][6] + X[7][7] + X[2][1] + X[3][8] == 26,
159 X[8][5] + X[0][4] + X[8][2] + X[1][7] + X[2][2] == 20,
160 X[8][6] + X[3][8] + X[1][5] + X[0][7] + X[0][2] + X[2][3] == 27,
161 X[2][6] + X[7][8] + X[8][6] + X[1][1] + X[7][7] + X[6][2] == 31,
162 X[3][2] + X[8][7] + X[0][3] + X[8][5] == 27,
163 X[5][4] + X[1][7] + X[5][7] + X[8][6] + X[5][0] == 33,
164 X[0][1] + X[0][7] + X[3][6] + X[4][3] == 21,
165 X[2][0] + X[8][3] + X[2][1] + X[8][0] + X[0][3] == 20,
166 X[5][7] + X[2][0] + X[5][5] + X[3][2] + X[1][5] == 25]
168 sudoku_c = cells_c + rows_c + cols_c + sq_c + formulas
170 # sudoku instance, we use '0' for empty cells
171 instance = ((0,0,0,0,0,0,0,0,1),
181 instance_c = [ If(instance[i][j] == 0,
183 X[i][j] == instance[i][j])
184 for i in range(9) for j in range(9) ]
187 s.add(sudoku_c + instance_c)
190 r = [ [ m.evaluate(X[i][j]) for j in range(9) ]
195 print("AOTW{", end='')
198 for i in range(1,len(r)):
199 print(r[i][len(r)-1], end='')
200 for i in range(len(r[len(r)-1])-2, 0, -1):
201 print(r[len(r)-1][i], end='')
202 for i in range(len(r)-1, 0, -1):
203 print(r[i][0], end='')
206 print("failed to solve")
209 This takes a while to solve but then the solved sudoku is printed. Also, the flag is printed which can be obtained by reading the numbers of the solved sudoku clockwise.
211 Flag: `AOTW{86472953189247356794813521457639}`
213 #### day-16 Musical Stegano (~10h)
214 This challenge was particularly interesting for me because I play guitar and the piano, so I know how to read sheet music which was helpful for this challenge. Also, this challenge was very special compared to other CTF challenges which was a nice variety.
217 Given was a midi file. This can be opened with a music notation program, like [Musescore](https://musescore.org/) for example, to get the corresponding sheet music. The piece is written in G major.
219 ##### Attempts and Solution
220 My first attempt was to somewhow get a character based on the note and its duration. So for example, if there is a A as a half note, I calculated 2 (second note in G major) * 4 (one half note contains 4 eighth notes). Don't know how I came to this, just tried some random things out.
222 After a few unsuccessful attempts, I was trying a more systematical approach. We know that the flag starts with `A` and ends with `}`. So the `}` character has to be encoded somewhere at the end of the sheet music. In the challenge description there is something written like "BASS SEVEN TRIGRAPHS". I interpreted "Bass" as "Base" and this could make sense (base of number system, 7 in this case), as we have 7 different notes in the G major scale. Also, I knew that G should be translated to 0, A to 1, B to 2, C to 3, D to 4, E to 5 and F# to 6 based on a hint.
223 Now I looked up the `}` in an ASCII table. The decimal ASCII value for `}` is 125. I converted this decimal value into base 7 which results in 236. After this, I translated this value to its "note value" based on the mapping I mentioned before. This results in BCF. (We can ommit the # in F# as it is not relevant for this challenge)
224 Now I was looking for the notes B, C and F somewhere at the end of the sheet music - and I found them. It turned out that every second sixteenth note is relevant (doesn't matter if it's in treble or bass clef). Now also the term "trigraph" in the challenge description makes sense. Three relevant notes next to each other represent one character of the flag. I also tried this with the character `A` at the beginning of the sheet music to confirm my assumption.
226 In summary you have to do the following:
227 - Take every second sixteenth note (Those are the relevant notes)
228 - Form groups of three out of those relevant notes (Each group represents one character)
229 - Convert the notes of each group to the corresponding number representation (as mentioned above, G = 0, A = 1, etc.)
230 - Convert this 3-digit number to base 10 and look up the corresponding ASCII character in an ASCII table
232 I wrote a very small python script to do so:
235 notes = ['G', 'A', 'B', 'C', 'D', 'E', 'F']
237 # Every second sixteenth note from the sheet music
238 music = 'ABB ADB AEG AEC BCD ADG BBE BBC AGG ABD AFF BAC AFD BGD AEA ADA BCF'.replace(' ', '')
241 for i in range(0, len(music), 3):
242 # Maps the notes to the number representations and converts it to a decimal integer
243 char_code = int(str(notes.index(music[i])) + str(notes.index(music[i+1])) + str(notes.index(music[i+2])), 7)
244 print(chr(char_code), end='') # Print the char with this character code
249 Flag: `AOTW{Mus1Cal_fUN}`
252 #### day-19 santa's signature (~4h)
254 Given was a service to connect to. Upon connecting, it outputs a public key and it says we should sign 3 different messages with our private key (which belongs to the given public key, we don't have this one obviously). Additionaly, the python source code of this service is given.
256 ##### Attempts and Solution
257 After taking a look at the source code, I googled what crypto library was used. Turned out it was `pycrypto`. I also looked for the source code and found it on GitHub [here](https://github.com/dlitz/pycrypto).
258 After analyzing the source code a bit further, it was obvious that the function we have to exploit is `verify`. I looked up the source code of this function in the GitHub repo.
261 def verify(self, M, signature):
262 """Verify the validity of an RSA signature.
263 :attention: this function performs the plain, primitive RSA encryption
264 (*textbook*). In real applications, you always need to use proper
265 cryptographic padding, and you should not directly verify data with
266 this method. Failure to do so may lead to security vulnerabilities.
267 It is recommended to use modules
268 `Crypto.Signature.PKCS1_PSS` or `Crypto.Signature.PKCS1_v1_5` instead.
270 :Parameter M: The expected message.
271 :Type M: byte string or long
272 :Parameter signature: The RSA signature to verify. The first item of
273 the tuple is the actual signature (a long not larger than the modulus
274 **n**), whereas the second item is always ignored.
275 :Type signature: A 2-item tuple as return by `sign`
276 :Return: True if the signature is correct, False otherwise.
278 return pubkey.pubkey.verify(self, M, signature)
281 As you can see above, this says that this method uses textbook RSA (without padding), which means we can exploit the weaknesses of it. I looked up how the verifying of signatures works in textbook RSA. I found [this](https://people.eecs.berkeley.edu/~luca/cs276/lecture20.pdf) document of Berkely University.
282 It basically says that a message is verified by checking if the following equation holds: $$S^e~mod~n = M~mod ~n$$ where $$S$$ is the signature, $$M$$ the message and $$n$$ and $$e$$ the integers of the public key.
283 Trivially, this equation holds for $$M=S=0$$ and $$M=S=1$$. So now we have to find a third (different) message and signature which fulfills the equation. This isn't a problem tho, as we can extract $$n$$ and $$e$$ out of the public key (`key.e`/`key.n` in python). Now we can basically take a message (like `-1` for example) take it to the power of $$e$$ and mod $$n$$ ($$S=M^e~mod~n$$). The result of this formula is a signature $$S$$ which is valid for the message $$M$$.
285 Finally, I wrote a python script to calculate this and also to solve the challenge (using `pwntools`):
289 from Crypto.PublicKey import RSA
291 public_key = RSA.importKey(open('key.pub', 'r').read()) # key.pub contains the public key which I copied from the output when connecting to the service
292 msg = hex(pow(-1, public_key.e, public_key.n)) # pow(a, b, c) = a^b % c
294 r = remote("3.93.128.89", 1219)
295 r.recvuntil("Message 1 you signed (hex encoded):")
305 Flag: `AOTW{RSA_3dg3_c4s3s_ftw}`