]> git.somenet.org - pub/jan/ctf-seminar.git/blob - writeups/ilm0/hack_lu.md
Add hxp36c3 writeup
[pub/jan/ctf-seminar.git] / writeups / ilm0 / hack_lu.md
1 # <u>Hack.lu 2019</u>\r
2 \r
3 \r
4 \r
5 ## Cobol OTP\r
6 \r
7 **type:** crypto\r
8 \r
9 **description:**\r
10 \r
11 To save the future you have to look at the past. Someone from the inside sent you an access code to a bank account with a lot of money. Can you  handle the past and decrypt the code to save the future?\r
12 \r
13 ___\r
14 \r
15 \r
16 \r
17 #### Recon\r
18 \r
19 We receive two files: the code of a COBOL program and an unusual looking output file.\r
20 \r
21 \r
22 \r
23 **otp.cob**\r
24 \r
25 ```cobol\r
26  identification division.\r
27  program-id. otp.\r
28 \r
29  environment division.\r
30  input-output section.\r
31  file-control.\r
32      select key-file assign to 'key.txt'\r
33      organization line sequential.\r
34 \r
35  data division.\r
36  file section.\r
37  fd key-file.\r
38  01 key-data pic x(50).\r
39 \r
40  working-storage section.\r
41  01 ws-flag pic x(1).\r
42  01 ws-key pic x(50).\r
43  01 ws-parse.\r
44      05 ws-parse-data pic S9(9).\r
45  01 ws-xor-len pic 9(1) value 1.\r
46  77 ws-ctr pic 9(1).\r
47 \r
48  procedure division.\r
49      open input key-file.\r
50      read key-file into ws-key end-read.\r
51 \r
52  display 'Enter your message to encrypt:'.\r
53  move 1 to ws-ctr.\r
54  perform 50 times\r
55      call 'getchar' end-call\r
56      move return-code to ws-parse\r
57      move ws-parse to ws-flag\r
58 \r
59      call 'CBL_XOR' using ws-key(ws-ctr:1) ws-flag by value\r
60      ws-xor-len end-call\r
61 \r
62      display ws-flag with no advancing\r
63      add 1 to ws-ctr end-add\r
64  end-perform.\r
65 \r
66  cleanup.\r
67      close key-file.\r
68      goback.\r
69  end program otp.\r
70 ```\r
71 \r
72 The first hurdle is to understand the basic functioning of COBOL, it is very different from the programming languages we are used to today. What one can tell on the first glimpse:\r
73 \r
74 - A message is read from user input to be encrypted\r
75 - There is a key read from a key file, that is used for the encryption\r
76 - XOR is used for some kind of encryption (OTP?!)\r
77 \r
78 \r
79 \r
80 ```cobol\r
81            01 ws-flag pic x(1).\r
82        01 ws-key pic x(50).\r
83        01 ws-xor-len pic 9(1) value 1.\r
84        77 ws-ctr pic 9(1).\r
85 ```\r
86 \r
87 Variables are initialized, 1 character for a flag???, 50 characters for a key, 1 digit for ws-xor-len and 1 digit for ws-ctr, most likely a counter.\r
88 \r
89 \r
90 \r
91 ```cobol\r
92        fd key-file.\r
93        01 key-data pic x(50).\r
94 ```\r
95 \r
96 ```cobol\r
97        procedure division.\r
98            open input key-file.\r
99            read key-file into ws-key end-read.\r
100 ```\r
101 \r
102 50 characters are to be read from a key file.\r
103 \r
104 `procedure division.` marks the beginning of the code real process body\r
105 \r
106 \r
107 \r
108 ```cobol\r
109                 perform 50 times\r
110             call 'getchar' end-call\r
111             move return-code to ws-parse\r
112             move ws-parse to ws-flag\r
113 \r
114             call 'CBL_XOR' using ws-key(ws-ctr:1) ws-flag by value\r
115             ws-xor-len end-call\r
116 \r
117             display ws-flag with no advancing\r
118             add 1 to ws-ctr end-add\r
119         end-perform.\r
120 ```\r
121 \r
122 The main loop where the 'magic' happens, we read 50 characters individually, xor it with the respective key character\r
123 \r
124 \r
125 \r
126 Here I turned to look at the output:\r
127 \r
128 **out**\r
129 \r
130 > Enter your message to encrypt:\r
131 > ¦Ò\13–y;\10dhuŸÝFŸ]\17UjhC\8fŒ-’1\aT`h&ŸÍF‡1*T{\ 4_¦ë\ 6¤p0\112J\r
132 \r
133 \r
134 \r
135 The second line of the output does not seem human-readable. A hex viewer shows:\r
136 \r
137 A6 D2 13 96 79 3B 10 64 68 75 9F DD 46 9F 5D 17 55 6A 68 43 8F 8C 2D 92 31 07 54 60 68 26 9F CD 46 87 31 2A 54 7B 04 5F A6 EB 06 A4 70 30 11 32 4A 0A\r
138 \r
139 The 50-step loop we saw in the main code body corresponds to the length of the encrypted text, as we are doing OTP encryption, the key and cyphertext lengths are the same.\r
140 \r
141 It seemed like what we see is a pure one-time-pad realisation in COBOL. The code seems straightforward, brute-forcing would take ages. I got stuck here.\r
142 \r
143 \r
144 \r
145 #### Technical background\r
146 \r
147 Functioning of the one-time-pad:\r
148 \r
149 XOR-ing each bit with the corresponding bit of the key. This is a perfectly secure crypto scheme, for an attack to be realised one would need access to at least 2 pieces of ciphertext or preferably an oracle.\r
150 \r
151 #### Lessons learned\r
152 \r
153 COBOL is still used, the last revision was released in 2014\r
154 \r
155 \r
156 \r
157 ## Lamport Verify\r
158 \r
159 **type:** crypto, rev\r
160 \r
161 **description:**\r
162 \r
163 I finally managed to create a signature verification service  powered by time, artificial intelligence, the power of music and the  randomness of entanglement (well, Leslie and the Emperor Penguin’s  randomness also helped). It is resistent to all known and unknown  attacks and will always be uncrackable. Leave and believe!\r
164 \r
165 nc lamport.forfuture.fluxfingers.net 1337\r
166 \r
167 ___\r
168 \r
169 #### Recon\r
170 \r
171 The description first seemed to include some clues, such as "Leslie" and "Emperor Penguin", but I did not succeed in finding anything meaningful relating to crypto or signatures. We are provided with a binary called "verify" apart from the service. The name of the challenge hints, that we are dealing with Lamport signatures. (see TD) We connect to the service, which gives us the functionality of signing and verifying something. \r
172 \r
173 The functioning of the service is not clear at first, let's take a lot at the provided binary with Ghidra. \r
174 \r
175 The analysis provides us with an entry point:\r
176 \r
177 ```c\r
178 void entry(undefined8 param_1,undefined8 param_2,undefined8 param_3)\r
179 {\r
180   undefined8 in_stack_00000000;\r
181   undefined auStack8 [8];\r
182 \r
183   __libc_start_main(FUN_00401400,in_stack_00000000,&stack0x00000008,FUN_00401720,FUN_00401790,param_3,auStack8);\r
184   do {\r
185                     /* WARNING: Do nothing block with infinite loop */\r
186   } while( true );\r
187 }\r
188 ```\r
189 \r
190 We find unnamed functions.  FUN_00401400 looks promising, lets take a look at it!\r
191 \r
192 \r
193 \r
194 ```c\r
195 ulong FUN_00401400(int param_1,char **param_2)\r
196 {\r
197   char *__filename;\r
198   char cVar1;\r
199   int iVar2;\r
200   FILE *__stream;\r
201   size_t sVar3;\r
202   ulong uVar4;\r
203   int local_38;\r
204   uint local_c;\r
205 \r
206   _DAT_004080c8 = 0;\r
207   while( true ) \r
208   {\r
209     iVar2 = getopt(param_1,param_2,"hv");\r
210     cVar1 = (char)iVar2;\r
211       \r
212     if (cVar1 == -1) break;\r
213       \r
214     if (cVar1 == 'h') \r
215     {\r
216       printf("Usage: verify [-h|-v] secret_key\n");\r
217       local_c = 0;\r
218       goto LAB_00401713;\r
219     }\r
220       \r
221     if (cVar1 != 'v') \r
222     {\r
223       fprintf(stderr,"Usage: verify [-h|-v] secret_key\n");\r
224       local_c = 1;\r
225       goto LAB_00401713;\r
226     }\r
227       \r
228     _DAT_004080c8 = 1;\r
229   }\r
230     \r
231   if (param_1 - optind < 1) \r
232   {\r
233     fprintf(stderr,"Usage: verify [-h|-v] secret_key\n");\r
234     local_c = 1;\r
235   }\r
236   else \r
237   {\r
238     __filename = param_2[optind];\r
239     DAT_004080b0 = &DAT_00408070;\r
240     DAT_004080b8 = &DAT_00408090;\r
241     DAT_004080c0 = &DAT_00404070;\r
242     __stream = fopen("flag","r");\r
243       \r
244     if (__stream == (FILE *)0x0) \r
245     {\r
246       perror("Could not open the flag.\n");\r
247       local_c = 1;\r
248     }\r
249     else \r
250     {\r
251       sVar3 = fread(DAT_004080b0,1,0x20,__stream);\r
252         \r
253       if ((sVar3 < 0x20) && (iVar2 = ferror(__stream), iVar2 != 0)) \r
254       {\r
255         fprintf(stderr,"Could not read the flag.\n");\r
256         local_c = 1;\r
257       }\r
258       else \r
259       {\r
260         __stream = fopen(__filename,"r");\r
261           \r
262         if (__stream == (FILE *)0x0) \r
263         {\r
264           perror("Could not open the secret key.\n");\r
265           local_c = 1;\r
266         }\r
267         else \r
268         {\r
269           sVar3 = fread(DAT_004080c0,1,0x4000,__stream);\r
270             \r
271           if (sVar3 < 0x4000) \r
272           {\r
273             fprintf(stderr,"Could not read the secret key.\n");\r
274             local_c = 1;\r
275           }\r
276           else \r
277           {\r
278             local_38 = 0;\r
279               \r
280             while (local_38 < 0x20) \r
281             {\r
282               DAT_004080b8[local_38] = 0;\r
283               local_38 = local_38 + 1;\r
284             }\r
285             local_38 = 0;\r
286               \r
287             while (local_38 < 0x20) \r
288             {\r
289               uVar4 = FUN_004011a0(DAT_004080b8 + local_38);\r
290               if ((int)uVar4 == 0) break;\r
291               local_38 = (int)uVar4 + local_38;\r
292             }\r
293               \r
294             uVar4 = FUN_00401290((long)&DAT_00404070,0x20);\r
295               \r
296             if ((int)uVar4 == 0) \r
297             {\r
298               printf("The message is not correct.\n");\r
299             }\r
300             else \r
301             {\r
302               printf("The message is correct.\n");\r
303             }\r
304               \r
305             local_c = 0;\r
306           }\r
307         }\r
308       }\r
309     }\r
310   }\r
311   LAB_00401713:\r
312   return (ulong)local_c;\r
313 }\r
314 ```\r
315 \r
316 We recognize a simple c-program (I reformatted the code for easier reading). The hardcoded usage warnings tell us how the program functions, even though the parameter "-h" and "-v" always jump to the end of the program and returns 0.\r
317 \r
318 \r
319 \r
320 Read 0x20 bytes from a file named `flag`.\r
321 \r
322 Read 0x4000 files from a file named `secret_key`.\r
323 \r
324 Get 0x20 utf-8 characters from user (@ 0x4011A0). We’ll call it `user_input`.\r
325 \r
326 Verify if the `user_input == flag` (Time-safe memcmp) (function @ 0x401290).\r
327 \r
328 If verbose (`-v`) flag is on – print “the signature” (function @ 0x401290)\r
329 \r
330 \r
331 \r
332 ```c\r
333 ulong FUN_00401290(long param_1,int param_2)\r
334 {\r
335   char cVar1;\r
336   byte bVar2;\r
337   int local_20;\r
338   int local_1c;\r
339 \r
340   bVar2 = 0;\r
341   local_1c = 0;\r
342     \r
343   while (local_1c < param_2) \r
344   {\r
345     bVar2 = *(byte *)(*(long *)(param_1 + 0x4040) + (long)local_1c) ^\r
346             *(byte *)(*(long *)(param_1 + 0x4048) + (long)local_1c) | bVar2;\r
347     local_1c = local_1c + 1;\r
348   }\r
349     \r
350   if (_DAT_004080c8 != 0) \r
351   {\r
352     printf("[+] Signature:\n");\r
353     local_1c = 0;\r
354       \r
355     while (local_1c < param_2 * 8)\r
356     {\r
357       cVar1 = *(char *)(*(long *)(param_1 + 0x4040) + (long)(local_1c / 8));\r
358       local_20 = 0;\r
359         \r
360       while (local_20 < 0x20) \r
361       {\r
362         printf("%02x",(ulong)*(byte *)(*(long *)(param_1 + 0x4050) +\r
363                                       (long)(int)(local_1c * 0x40 +\r
364                                        ((int)cVar1 >> (7U - (char)(local_1c % 8) & 0x1f)\r
365                                        & 1U) * 0x20 + local_20)));\r
366         local_20 = local_20 + 1;\r
367       }\r
368       printf("\n");\r
369       local_1c = local_1c + 1;\r
370     }\r
371   }\r
372   return (ulong)((bVar2 != 0 ^ 0xffU) & 1);\r
373 }\r
374 ```\r
375 \r
376 Here I reached a roadblock, as I could not disassemble the used data object. I looked up writeups online:\r
377 \r
378 ```c\r
379 struct glob\r
380 {\r
381 \r
382   char key[16384];\r
383   char flag[32];\r
384   char user_input[32];\r
385   unsigned __int64 ptr_flag;\r
386   unsigned __int64 ptr_user_input;\r
387   unsigned __int64 ptr_key;\r
388   unsigned __int64 verbose;\r
389 };\r
390 ```\r
391 \r
392 [https://blog.nsogroup.com/hacklu-2019-ctf-lamport-verify/]\r
393 \r
394 Apparently it is possible to dissassemble them, but I did not find a solution in ghidra.\r
395 \r
396 #### Technical background\r
397 \r
398 > In cryptography, a Lamport signature or Lamport one-time signature scheme is a method for constructing a digital signature. Lamport signatures can be built from any cryptographically secure one-way function; usually a cryptographic hash function is used.\r
399 \r
400 [https://en.wikipedia.org/wiki/Lamport_signature]\r
401 \r
402 \r
403 \r
404 ![lamport.png](hack_lu\lamport.png)\r
405 \r
406 [https://asecuritysite.com/encryption/lamport]\r
407 \r
408 2 random numbers (A and B, to be used as the private key) of the same bit-length are sampled, and depending on the parity of the hash to be signed either the value of A or B at that particular bit position is used. The public key is constructed similarly, but with the hashes of A and B.\r
409 \r
410 \r
411 \r
412 Lamport signatures are also often mentioned among post-quantum crypto solutions, however, in this case their limitation is that they can only be used once.\r
413 \r
414 #### Lessons (to be) learned\r
415 \r
416 - Lamport crypto scheme\r
417 \r
418 - Disassembling complex data objects (C-structs).\r
419 \r
420   \r
421 \r
422 ## Evil corp\r
423 \r
424 **type:** crypto\r
425 \r
426 **description:**\r
427 \r
428 You were called by the Incident Response Team of Evil Corp, the urgently need your help. Somebody broke into the main server of the company, bricked the device and stole all the files! Nothing is left! This should have been impossible. The Hacker used some secret backdoor to bypass authentication. Without the knowledge of the secret backdoor other servers are at risk as well! The Incident Response Team has a full packet capture of the incident and performed an emergency cold boot attack on the server to retrieve the contents of the memory (its a really important server, Evil Corp is always ready for such kinds of incidents. However they were unable to retrieve much information from the RAM, what's left is only some parts of the "key_block" of the TLS server. Can you help Evil Corp to analyze the exploit the attacker used?\r
429 \r
430 (Flag is inside of the attackers' secret message).\r
431 \r
432 \r
433 TT = Could not recover\r
434 \r
435 Keyblock:\r
436 6B 4F 93 6A TT TT TT TT TT TT 00 D9 F2 9B 4C B0\r
437 2D 88 36 CF B0 CB F1 A6 7B 53 B2 00 B6 D9 DC EF\r
438 66 E6 2C 33 5D 89 6A 92 ED D9 7C 07 49 57 AD E1\r
439 TT TT TT TT TT TT TT TT 56 C6 D8 3A TT TT TT TT\r
440 TT TT TT TT TT TT TT TT 94 TT 0C EB 50 8D 81 C4\r
441 E4 40 B6 26 DF E3 40 9A 6C F3 95 84 E6 C5 86 40\r
442 49 FD 4E F2 A0 A3 01 06\r
443 \r
444 ___\r
445 \r
446 #### Recon\r
447 \r
448 This seemed like a very interesting challenge from the start, but also deeply technical. We are additionally provided with a .pcap packet capture file. \r
449 \r
450 The first step was to find out what is key_block and how does it relate to TLS. A quick google search returns promising results:\r
451 \r
452 >  To generate the key material, compute\r
453 >\r
454 > ```\r
455 >       key_block = PRF(SecurityParameters.master_secret,\r
456 >                       "key expansion",\r
457 >                       SecurityParameters.server_random +\r
458 >                       SecurityParameters.client_random);\r
459 > ```\r
460 >\r
461 >    until enough output has been generated.  Then, the key_block is\r
462 >    partitioned as follows:\r
463 >\r
464 > ```\r
465 >      client_write_MAC_key[SecurityParameters.mac_key_length]\r
466 >      server_write_MAC_key[SecurityParameters.mac_key_length]\r
467 >      client_write_key[SecurityParameters.enc_key_length]\r
468 >      server_write_key[SecurityParameters.enc_key_length]\r
469 >      client_write_IV[SecurityParameters.fixed_iv_length]\r
470 >      server_write_IV[SecurityParameters.fixed_iv_length]\r
471 > ```\r
472 \r
473 [https://tools.ietf.org/html/rfc5246]\r
474 \r
475 The TLS 1.2 specification uses this block as a PRNG function to generate random bits, which are then used as MAC keys, encryption keys and IVs. The seed for this PRNG is a parameter "master_secret".\r
476 \r
477 Let's open the packet capture and see how we can apply this knowledge!\r
478 \r
479 Looking at he capture we can see a succesfully executed TLS 1.1 handshake between client and server.  \r
480 \r
481 As a side note: I tried to copy a packet as text from Wireshark and noticed this strange hidden message:\r
482 \r
483 ```\r
484 E?2@@   ¨BQh³¼Mÿ3\r
485 %@5V%@4ÇNobodyknowsimacat Stop looking nothing to findhere5S7EvilCorp kills people\r
486 ,*ííîîïïÿ\r
487 ```\r
488 \r
489 The packet capture is very short, it includes 11 packets. After the initial handshake we have some "heartbeats", after some quick googling I and looking at the RFC once again, I found out what they were:\r
490 \r
491 > Zero-length fragments of Application data MAY be sent as they are potentially useful as a     traffic analysis countermeasure.\r
492 \r
493 But, what I also found is that they are the responsible entities in TLS for the infamous Heartbleed security bug.\r
494 \r
495 The alerts do not say anything meaningful, only "Internal Error".\r
496 \r
497 I tried looking for the expression "key" in the traffic, but did not find anything.\r
498 \r
499 I looked into the Heartbleed exploit, but for it to work one would need access to the keys, because the heartbeat packets are encrypted.\r
500 \r
501 #### Technical background\r
502 \r
503 The Heartbleed bug explained:\r
504 \r
505 > Here's how it worked: the SSL standard includes a heartbeat option,  which allows a computer at one end of an SSL connection to send a short  message to verify that the other computer is still online and get a  response back. Researchers found that it's possible to send a cleverly  formed, malicious heartbeat message that tricks the computer at the  other end into divulging secret information. Specifically, a vulnerable  computer can be tricked into transmitting the contents of the server's  memory, known as RAM.\r
506 \r
507 [https://www.vox.com/2014/6/19/18076318/heartbleed]\r
508 \r
509 This could make it possible in this instance to leak the full key_block from the server, the description itself mentions that the incomplete `key_block` fragment is from there. However, one would need network access to the server, what was not given here.\r
510 \r
511 #### Lessons learned\r
512 \r
513 - Deeper functioning of TLS\r
514 - Heartbleed bug\r
515 \r
516 \r
517 \r
518 ## Shiny code\r
519 \r
520 **type:** misc, steg\r
521 \r
522 **description:**\r
523 \r
524 We got a video leaked of a new transmission technique working only with lights and colors. The company using it says it is completely secure because of its secret encoding. However, the whistleblower also says that the secret encoding is somehow encoded in the message itself. It uses an old, historical technique based on short and long signals. I have no fucking clue what he is talking about. Perhaps you can help us?\r
525 \r
526 Download challenge files\r
527 \r
528 Remark: This challenge has a non-standard flag format: FLAG{.*}\r
529 \r
530 ___\r
531 \r
532 #### Recon\r
533 \r
534 We receive a 10-minute long video of a custom made PCB board with blinking LEDs. The surface of the board is divided up exactly like the FluxFingers logo into 6 areas. Not only the pattern of the areas is changing, but also the color of the LEDs, however they do not mix colors. Each color is changed after 6 seconds, I could not find any pattern regarding the colors. There are "steps" with no LEDs lit and also with all the LEDs lit up, and of course everything in between. Each blink last for 500ms, so one color is present for 3 secs and colors never repeat right after each other.\r
535 \r
536 My first tries were interpreting the signals as a form of senary encoding, but senary is not really used anywhere apart from certain representations of base36 and base64 encodings. Base36 seemed very promising, we have always 6 frames of consecutive colors, it would have made sense, but as I tried to decode the message only garbage came out.\r
537 \r
538 Trying to interpret the code as some form of Morse was a sensible next step, as the fixed length pulses provided a reliable way of differentiating between short and long signals used in Morse. However, it was still not clear how to divide up the signals, interpret them grouped by color, side, image region, channel.\r