--- tags: re, ctf --- # LINE CTF 2022 - Reversing ## Rolling At the first time, we spent a lot of time to try run blow script (using `dlopen`, `aarch64` simulation) but it's segfaulted all times 😢 ```cpp #include <stdio.h> #include <stdlib.h> #include <dlfcn.h> #include <unistd.h> int main() { printf("Hello World!\n"); /* char cwd[255]; if (getcwd(cwd, sizeof(cwd)) != NULL) { printf("[+] Current working dir: %s\n", cwd); } else { perror("[-] getcwd() error"); return 1; }*/ // declare a function pointer for the function to import typedef long (*h_meatbox)(char*); // open the shared object void *dlh = dlopen("libnative-lib.so", RTLD_LAZY); // void *dlh=NULL; if (dlh == NULL) { printf("[-] open library fail\n"); exit(1); } // resolve the function symbol h_meatbox meatbox = dlsym(dlh, "_Z7meatboxPc"); printf("[+] meatbox: 0x%lx\n", meatbox); for (int i=0; i<4; i++) printf("%x ", (char*)(meatbox+i)); if (meatbox==NULL){ printf("[-] resolve function fail\n"); exit(1); } /* // call function char arr[]="abc\0"; printf("[+] Result meatbox: %ld\n", (*meatbox)(arr));*/ printf("[+] here\n"); return 0; } // aarch64-linux-gnu-gcc test.c -o test -ldl // sudo qemu-aarch64 -L /usr/aarch64-linux-gnu ./test ``` Finally, we decided to use `frida` hooking `libnative` in the android mobiles. Here's my script that extracts the required output for the decryption flag. **Script hooking** ```jsx Interceptor.attach(Module.getExportByName('libnative-lib.so', '_Z7meatboxPc'), { onEnter: function(args) { console.log("attach meatbox...") }, onLeave: function(retval) { // simply replace the value to be returned with 0 var buf = Memory.readByteArray(ptr(retval), 1) // console.log(retval); // console.log(buf); var b = new Uint8Array(buf); var str = ""; for(var i = 0; i < b.length; i++) { str += (b[i].toString(16) + " "); } console.log(str); } }); Interceptor.attach(Module.getExportByName('libnative-lib.so', '_Z7soulboxPc'), { onEnter: function(args) { console.log("attach soulbox...") }, onLeave: function(retval) { // simply replace the value to be returned with 0 var buf = Memory.readByteArray(ptr(retval), 1) // console.log(retval); // console.log(buf); var b = new Uint8Array(buf); var str = ""; for(var i = 0; i < b.length; i++) { str += (b[i].toString(16) + " "); } console.log(str); } }); Interceptor.attach(Module.getExportByName('libnative-lib.so', '_Z6godboxPc'), { onEnter: function(args) { console.log("attach godbox...") }, onLeave: function(retval) { // simply replace the value to be returned with 0 var buf = Memory.readByteArray(ptr(retval), 1) // console.log(retval); // console.log(buf); var b = new Uint8Array(buf); var str = ""; for(var i = 0; i < b.length; i++) { str += (b[i].toString(16) + " "); } console.log(str); } }); ``` ## RES The challenge’s webpage displays an input box that we can supply the plaintext and get the ciphertext. Our goal is to decrypt the password and get flag. The challenge is written in WebAssembly. Like other Wasm challenges, our team had encountered before, our approach was just debug the crap out of the program and try to understand the workflow. After a while we figured out that the RES will generate 5 random numbers form 0 to 4 and each number will be correspond to a different encryption algorithm. Our plaintext will get passed into each algorithms followed the order of the random numbers. Based on the strings and the constants in the binary, we uncovered all 5 encryption algorithms as followed: - func10 → Camellia - func11 → AES - func12 → 3DES - func13 → SEED - func14 → RC4 For the key for each algorithms, we continue to debug the program and also took some leap of faith to figured out the correct keys. This is the most consumed part of the challenge for our team and it was 3A.M so we were exhausted . Luckily, we managed to recovered all the keys. Finally we wrote a Python script to iterate through all the permutations and try to decrypt the password ```python from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.backends import default_backend from Crypto.Cipher import ARC4, DES3, AES import itertools import base64 def solveCamellia(ciphertext): key = bytes.fromhex('112233445566778899aabbccddeeff00') cipher = Cipher(algorithms.Camellia(key), modes.ECB(), default_backend()) decryptor = cipher.decryptor() return decryptor.update(ciphertext) + decryptor.finalize() def solveSEED(ciphertext): key = bytes.fromhex('ff04286121ea1be86d71ccb1fda74382') cipher = Cipher(algorithms.SEED(key), modes.ECB(), default_backend()) decryptor = cipher.decryptor() return decryptor.update(ciphertext) + decryptor.finalize() def solveRC4(ciphertext): key = bytes.fromhex('4706480851e61be85d74bfb3fd956185') cipher = ARC4.new(key) return cipher.decrypt(ciphertext) def solveDES3(ciphertext): key = bytes.fromhex('48454c504d4521005448414e4b5321001020304050607080') return DES3.new(key=key, mode=DES3.MODE_ECB).decrypt(ciphertext) def solveAES(ciphertext): key = bytes.fromhex('a741be1431dd82496357baf131aecfd5') iv = bytes.fromhex('c91928c84fc61be85d79cf83fd95c185') return AES.new(key=key, mode=AES.MODE_CBC, IV=iv).decrypt(ciphertext) ciphertext = "N9Nb2sPYFl6sEbVORzuK1kUXMvs+/LbyrTpJaxQj3fdDhXyKN8mBELPRTX5904o9" ciphertext = base64.b64decode(ciphertext) for funcs in itertools.permutations([solveCamellia, solveSEED, solveRC4, solveDES3, solveAES]): func1, func2, func3, func4, func5 = funcs result = func1(func2(func3(func4(func5(ciphertext))))) if b'LINECTF' in result: print(result) ``` Flag: **LINECTF{RE7ard3d_3ncryp710n_53rv1c3_x0x}**