1 # OTW Advent Bonanza 2019
5 In total I spent 2+4+3+1+1 for the challenges plus 1 hour for this writeup and also at least half a day to look through the challenges' solutions and writeups after the bonanza was over. __Total 16 hours__
7 ## day-01: `7110` (keylogger programming pwned!)
11 Santa is stranded on the Christmas Islands and is desperately trying to reach his trusty companion via cellphone. We've bugged the device with a primitive keylogger and have been able to decode some of the SMS, but couldn't make much sense of the last one. Can you give us a hand?
13 Download: f01d32e3f32957cf42f9672e78fcb415c6deac398fdacbd69531a322b08a39c8-7110.tar.gz
29 It took me 2 hours to write the script parsing the sms messages.
33 Basically we can use the logged keys incl. timestamps to reconstruct text (but we need to know which number how often resolves to which key - what we have in keys.h - and the timelimit/interval which we decide on change actual character/write next character - which we need to guess, I used a value of 900ms).
40 #!/usr/bin/env python3
43 N7110_KEYPAD_ZERO = 0,
46 N7110_KEYPAD_THREE = 3,
47 N7110_KEYPAD_FOUR = 4,
48 N7110_KEYPAD_FIVE = 5,
50 N7110_KEYPAD_SEVEN = 7,
51 N7110_KEYPAD_EIGHT = 8,
52 N7110_KEYPAD_NINE = 9,
53 N7110_KEYPAD_STAR = 10,
54 N7110_KEYPAD_HASH = 11,
55 N7110_KEYPAD_MENU_LEFT = 100,
56 N7110_KEYPAD_MENU_RIGHT = 101,
57 N7110_KEYPAD_MENU_UP = 102,
58 N7110_KEYPAD_MENU_DOWN = 103,
59 N7110_KEYPAD_CALL_ACCEPT = 104,
60 N7110_KEYPAD_CALL_REJECT = 105
63 N7110_IME_T9_CAPS = 1,
65 N7110_IME_ABC_CAPS = 3
67 N7110_KEYPAD_ZERO_ABC_CHARS = " 0"
68 N7110_KEYPAD_ONE_ABC_CHARS = ".,'?!\"1-()@/:"
69 N7110_KEYPAD_TWO_ABC_CHARS = "abc2"
70 N7110_KEYPAD_THREE_ABC_CHARS = "def3"
71 N7110_KEYPAD_FOUR_ABC_CHARS = "ghi4"
72 N7110_KEYPAD_FIVE_ABC_CHARS = "jkl5"
73 N7110_KEYPAD_SIX_ABC_CHARS = "mno6"
74 N7110_KEYPAD_SEVEN_ABC_CHARS = "pqrs7"
75 N7110_KEYPAD_EIGHT_ABC_CHARS = "tuv8"
76 N7110_KEYPAD_NINE_ABC_CHARS = "wxyz9"
77 N7110_KEYPAD_STAR_ABC_CHARS = "@/:_;+&%*[]{}"
81 '1': ".,'?!\"1-()@/:",
95 # https://stackoverflow.com/a/38370569/1380748
96 reader = csv.reader(open(name, 'r'))
100 l.append((time, key))
111 for i in range(len(l)):
113 if j < len(l) and keyx(l[j])==keyx(l[i]) and int(time(l[j]))-int(time(l[i])) < timeout:
114 continue # next char may be the real
119 if keyx(l[counter+1]) == keyx(l[counter]) and int(time(l[counter+1]))-int(time(l[counter])) < timeout:
121 counter = counter - 1
124 out.append((key, keycount))
131 if int(char) == 101: # N7110_KEYPAD_MENU_RIGHT looks like the deletion key
132 result = result[:-cnt]
134 result += repr((char,cnt))
136 result += mapping[char][(cnt-1) % len(mapping[char])]
140 if __name__ == "__main__":
141 files = ["sms1.csv", "sms2.csv", "sms3.csv", "sms4.csv"]
143 print(mapit(key(read_file(f))))
146 script above prints following output:
149 ('100', 3)('11', 2)rudolf where are you brrr('100', 1)('100', 1)0m, .l ,p('100', 1)
150 ('100', 3)('11', 2)its too damn cold here and im out of eggnog lul('100', 2)0m.. .l ,p('100', 1)
151 ('100', 3)('11', 2)sorri bout my last 2msg but i could realy need your help bud('102', 20)l('102', 36y('103', 56) :*('100', 2)0m, .l ,p('100', 1)
152 ('100', 3)('11', 2)alright pal hers('102', 1)e('103', 1) ye flag good lucj enter('102', 7)('103', 1k('103', 6)ing it with those hooves lol its aotw{l3ts_dr1nk_s0m3_eggn0g_y0u_cr4zy_d33r}('100', 2)0m.. .l ,p('100', 1)
155 including the flag `aotw{l3ts_dr1nk_s0m3_eggn0g_y0u_cr4zy_d33r}` :D
157 ## day-02: `Summer ADVENTure` (crypto rev network misc pwned!)
161 An elf is tired of snow and wanted to visit summer... in an online RPG at least. Could you help him beat the game?
163 Service: <http://3.93.128.89:12020>
165 Server setup description: <https://docs.google.com/document/d/1wYlM2ideh5R5I7KDTLFTu_NLQmAJAmV-hVjNlmAOIEY/edit>
169 I spent about 4 hours on this challenge.
173 I started by playing the game for some time. Navigation is by WASD, you can buy stuff and have to fight monsters (for which you get money). You can level up and have experience and Health points.
175 There is a scroll of secrets to buy which costs 10.000.000 money. Phew. Is the flag in there?
177 I notices the following things: When running a fake server, the only information I can receive (from the fake client on their server) is always something like 0x84f91202e7062ec4d1ca8e50a6b0a1aa - after I send "success" and some newlines in plaintext and after I log in in the web browser. This response varies depending on what I type as username/password in the browser. For example, for user:pw 0000:0000 I receive b'\x84\xf9\x12\x02\xe7\x06.\xc4\xd1\xca\x8eP\xa6\xb0\xa1\xaa', for 1111:1111 I get b'\x84\xf9\x12\x02\xe7\x06/\xc5\xd0\xcb\x8eP\xa7\xb1\xa0\xab'. I could not spot a real pattern here - the username/pw is not directly in this response. But indirectly it is, because the last 4 bytes XORed with the bits 10010110 10000000 10010001 10011010 are the password (I don't know what this pattern should mean, I extracted it from trying different passwords) and changing user/pw on only 1 character changes only one byte. Changing length of user/pw changes more bits, so maybe there is length encoded.
179 I cannot trigger any more messages, and I cannot manage the browser to come further than "loading the game" (it only displays "Loading..." in the right bottom corner after I send success).
181 We have to reverse-engineer the protocol server-client (they posted this as a hint: ) but have nothing to start. I did not manage to guess anything noteworthy than "success", where you get the encoded (?) login data back. I did not try any fuzzer.
183 I also wanted to look at the websocket traffic to the browser, but the challenge makers write in a hint that this does not reveal any information we need (because only blazor protocol/HTML elements, important transmission is between server-client and server-server) so I did not try further.
187 I did not manage to get anywhere near a solution here because I only spent a few hours on this.
189 Fellow w0yers managed to get the flag the weeks after the challenge started. The communication was encrypted using some cipher.
190 The flag was splitted into client/server-side flag.
191 Writeup: <https://github.com/nononovak/otwadvent2019-ctfwriteup/blob/master/day2_summer_adventure.md>
193 ## day-03: `northpole airwaves`
197 During our latest expedition to the north pole, one of our scientists tuned her radio to 88Mhz and captured a slice of 1MHz (samplerate = 1M/s) to a GNURadio file sink. Surprisingly, there was more than just silence. We need your help to figure out what's going on.
199 Download: d6d0f728ac3ff4848f849b8cf222fb38a14aee870184cc22f2efe01336b2ec17-northpole-airwaves.bin
203 I spent (wasted) 3 hours for this challenge, most of the time downloading, converting and playing around/learning with GNU-Radio.
207 I downloaded the challenge file and found out that it was around 740MB in size. I always wanted to play around with GNURadio so I downloaded it and tried to find need information in gnuradio tutorials online. Although I spent a few hours with this because the conversions took so long and I did not really know what I am doing, I found out absolutely nothing.
211 I did not manage to get ANY information for this challenge. Apparently a third of the flag was in the RDS data.
213 Writeup: <https://github.com/nononovak/otwadvent2019-ctfwriteup/blob/master/day3_northpole_airwaves.md>
215 ## day-04: `mooo` (web pwned!)
219 'Moo may represent an idea, but only the cow knows.' - Mason Cooley
221 Service: <http://3.93.128.89:1204>
229 We are provided with a web interface where we can type a message and choose a Cow type.
230 I tried various special characters in the message field but there was no sign of some injection happening. When selecting different Cow types, the ascii output looked different. I tried to change the value of the combobox to some non-provided values but this also not worked.
232 By using the 'custom' Cow type, I got redirected to another page called cow designer (`/cow_designer`), where you could design your own cow including eyes and tongue. Okay, another input field for injection possibility. I played around but could not really do anything. Then I googled for some of the Cow type names and found out that those are from the `cowsay` program, which exists to print ASCII cows. :D
234 I looked at the sourcecode on Github (as <https://github.com/tnalpgge/rank-amateur-cowsay/blob/master/cows/TuxStab.pm> and <https://github.com/tnalpgge/rank-amateur-cowsay/blob/master/cows/stegosaurus.cow>), found out this was Perl code and the cow paintings always ended with `EOC`.
236 I typed this EOC into the custom cow designer and a basic perl print command. It worked, so command injection was here. I googled for perl system functions and found system() which worked out of the box.
240 See above. Flag: `AOTW{th3_p3rl_c0w_s4ys_M0oO0o0O}`
242 Writeup with more technical details: <https://github.com/nononovak/otwadvent2019-ctfwriteup/blob/master/day4_mooo.md>
244 ## day-05: `Sudo Sudoku` (misc sudoku pwned)
248 Santa's little helpers are notoriously good at solving Sudoku puzzles, so they made a special variant...
250 Download: 2a3fad4ea8987eb63b5abdea1c8bdc75d4f2e6b087388c5e33cec99136b4257a-sudosudoku.tar.xz
256 which includes a Sudoku board as below as well as the additional rules:
259 In addition to the standard Sudoku puzzle above,
260 the following equations must also hold:
262 B9 + B8 + C1 + H4 + H4 = 23
263 A5 + D7 + I5 + G8 + B3 + A5 = 19
264 I2 + I3 + F2 + E9 = 15
265 I7 + H8 + C2 + D9 = 26
266 I6 + A5 + I3 + B8 + C3 = 20
267 I7 + D9 + B6 + A8 + A3 + C4 = 27
268 C7 + H9 + I7 + B2 + H8 + G3 = 31
269 D3 + I8 + A4 + I6 = 27
270 F5 + B8 + F8 + I7 + F1 = 33
271 A2 + A8 + D7 + E4 = 21
272 C1 + I4 + C2 + I1 + A4 = 20
273 F8 + C1 + F6 + D3 + B6 = 25
278 +-------+-------+-------+
279 A | . . . | . . . | . . 1 |
280 B | . 1 2 | . . . | . . . |
281 C | . . . | . . . | 2 . . |
282 +-------+-------+-------+
283 D | . . . | . . . | . . 2 |
284 E | . 2 . | . . . | . . . |
285 F | . . . | . . . | . . . |
286 +-------+-------+-------+
287 G | . . . | . . . | 1 2 . |
288 H | 1 . . | . . 2 | . . . |
289 I | . . . | 1 . . | . . . |
290 +-------+-------+-------+
295 I spent 1 hour for this challenge
299 We have to solve the sudoku. Best way would be an SMT/SAT solver.
303 I tried customizing the Python Sudoku solver script at <http://norvig.com/sudoku.html> by extending the eliminate function with very naive if statements
306 def eliminate(values, s, d):
308 if len(values['B9']) == 1 and len(values['B8']) == 1 and len(values['C1']) == 1 and len(values['H4']) == 1: # len 1 means only 1 value possible for those fields
309 if int(values['B9'])+int(values['B8'])+int(values['C1'])+int(values['H4'])*2 != 23:
310 return False # reject solution if B9 + B8 + C1 + H4 + H4 != 23
311 if len(values['A5']) == 1 and len(values['D7']) == 1 and len(values['I5']) == 1 and len(values['G8']) == 1 and len(values['B3']) == 1:
312 if int(values['D7'])+int(values['I5'])+int(values['G8'])+int(values['B3'])+int(values['A5'])*2 != 19:
314 if len(values['I2']) == 1 and len(values['I3']) == 1 and len(values['F2']) == 1 and len(values['E9']) == 1:
315 if int(values['I2'])+int(values['I3'])+int(values['F2'])+int(values['E9']) != 15:
317 if len(values['I7']) == 1 and len(values['H8']) == 1 and len(values['C2']) == 1 and len(values['D9']) == 1:
318 if int(values['I7'])+int(values['H8'])+int(values['C2'])+int(values['D9']) != 26:
320 if len(values['I6']) == 1 and len(values['A5']) == 1 and len(values['I3']) == 1 and len(values['B8']) and len(values['C3']) == 1:
321 if int(values['I6'])+int(values['A5'])+int(values['I3'])+int(values['B8'])+int(values['C3']) != 20:
323 # ... + the other rules
327 But this was highly inefficient even with only the first if, although it works.
329 I then saw in Mattermost that we already solved this challege using Z3 and stopped trying improving this python script.
331 Writeup: <https://github.com/nononovak/otwadvent2019-ctfwriteup/blob/master/day5_sudo_sudoku.md>