Hello everyoneπŸ’―! Today i will tell you the way to solve the ***Chatbot*** challenge in CSCV2025 The question: ![image](https://hackmd.io/_uploads/r1KnQ9zRxx.png) So at first you will have an file **main** like this below: ![image](https://hackmd.io/_uploads/HyCOV9zCgx.png) Let open this file in DIE to see the information about this file: ![image](https://hackmd.io/_uploads/Sk8yH9zAll.png) This is an ELF64 file and DIE has told us that this file have been packed with PyInstaller Let use IDA(in app use SHIFT+F12) to see strings of this file: ![image](https://hackmd.io/_uploads/ByuAB9MRxx.png) Scroll down a little bit and you will see this: ![image](https://hackmd.io/_uploads/rJ3ZLcf0xl.png) We see many file like Py_ so that means this file was built by python We will use **pyinstxtrator\.py** to export the content of this file(point in the **main** file to open terminal) Type: ``` python pyinstxtractor.py main ``` Output will be: ![image](https://hackmd.io/_uploads/S126P9fCge.png) Go back to the folder you put the **main** file and you will see there are an additional folder name **main_extracted**: ![image](https://hackmd.io/_uploads/Skoudcz0ll.png) Now we will use a python decompiler to make main\.pyc file to main\.py file I will use pylingual, link in here:https://www.pylingual.io/ The file after decompile to .py such like this: ``` # Decompiled with PyLingual (https://pylingual.io) # Internal filename: main.py # Bytecode version: 3.11a7e (3495) # Source timestamp: 1970-01-01 00:00:00 UTC (0) import base64 import json import time import random import sys import os from ctypes import CDLL, c_char_p, c_int, c_void_p from cryptography.hazmat.primitives import serialization, hashes from cryptography.hazmat.primitives.asymmetric import padding import ctypes def get_resource_path(name): if getattr(sys, 'frozen', False): base = sys._MEIPASS else: # inserted base = os.path.dirname(__file__) return os.path.join(base, name) def load_native_lib(name): return CDLL(get_resource_path(name)) if sys.platform == 'win32': LIBNAME = 'libnative.dll' else: # inserted LIBNAME = 'libnative.so' lib = None check_integrity = None decrypt_flag_file = None free_mem = None try: lib = load_native_lib(LIBNAME) check_integrity = lib.check_integrity check_integrity.argtypes = [c_char_p] check_integrity.restype = c_int decrypt_flag_file = lib.decrypt_flag_file decrypt_flag_file.argtypes = [c_char_p] decrypt_flag_file.restype = c_void_p free_mem = lib.free_mem free_mem.argtypes = [c_void_p] free_mem.restype = None except Exception as e: print('Warning: native lib not loaded:', e) lib = None check_integrity = None decrypt_flag_file = None free_mem = None def run_integrity_or_exit(): if check_integrity: ok = check_integrity(sys.executable.encode()) if not ok: print('[!] Integrity failed or debugger detected. Exiting.') sys.exit(1) PUB_PEM = b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsJftFGJC6RjAC54aMncA\nfjb2xXeRECiwHuz2wC6QynDd93/7XIrqTObeTpfBCSpOKRLhks6/nzZFTTsYdQCj\n4roXhWo5lFfH0OTL+164VoKnmUkQ9dppzpmV0Kpk5IQhEyuPYzJfFAlafcHdQvUo\nidkqcOPpR7hznJPEuRbPxJod34Bph/u9vePKcQQfe+/l/nn02nbfYWTuGtuEdpHq\nMkktl4WpB50/a5ZqYkW4z0zjFCY5LIPE7mpUNLrZnadBGIaLoVV2lZEBdLt6iLkV\nHXIr+xNA9ysE304T0JJ/DwM1OXb4yVrtawbFLBu9otOC+Gu0Set+8OjfQvJ+tlT/\nzQIDAQAB\n-----END PUBLIC KEY-----' public_key = None try: pub_path = get_resource_path('public.pem') if os.path.exists(pub_path): with open(pub_path, 'rb') as f: public_key = serialization.load_pem_public_key(f.read()) else: # inserted public_key = serialization.load_pem_public_key(PUB_PEM) except Exception as e: print('Failed loading public key:', e) public_key = None def b64url_encode(b): return base64.urlsafe_b64encode(b).rstrip(b'=').decode() def b64url_decode(s): s = s | ('=', 4, len(s) - 4) | 4 return base64.urlsafe_b64decode(s.encode()) def verify_token(token): if not public_key: return (False, 'no public key') try: payload_b64, sig_b64 = token.strip().split('.', 1) payload = b64url_decode(payload_b64) sig = b64url_decode(sig_b64) public_key.verify(sig, payload, padding.PKCS1v15(), hashes.SHA256()) j = json.loads(payload.decode()) if j.get('role')!= 'VIP': return (False, 'role != VIP') if j.get('expiry', 0) < int(time.time()): return (False, 'expired') else: # inserted return (True, j) except Exception as e: return (False, str(e)) def sample_token_nonvip(): payload = json.dumps({'user': 'guest', 'expiry': int(time.time()) + 3600, 'role': 'USER'}).encode() return b64url_encode(payload) def main(): run_integrity_or_exit() print('=== Bot Chat === \n 1.chat\n 2.showtoken\n 3.upgrade \n 4.quit') queries = 0 while True: cmd = input('> ').strip().lower() if cmd in ['quit', 'exit']: return if cmd == 'chat': if queries < 3: print(random.choice(['Hi', 'Demo AI', 'Hello!', 'How can I assist you?', 'I am a chatbot', 'What do you want?', 'Tell me more', 'Interesting', 'Go on...', 'SIUUUUUUU', 'I LOVE U', 'HACK TO LEARN NOT LEARN TO HACK'])) queries = queries | 1 else: # inserted print('Free queries exhausted. Use \'upgrade\'') else: # inserted if cmd == 'showtoken': print('Token current:' + sample_token_nonvip()) else: # inserted if cmd == 'upgrade': run_integrity_or_exit() token = input('Paste token: ').strip() ok, info = verify_token(token) if ok: if decrypt_flag_file is None: print('Native library not available -> cannot decrypt') else: # inserted flag_path = get_resource_path('flag.enc').encode() res_ptr = decrypt_flag_file(flag_path) if not res_ptr: print('Native failed to decrypt or error') else: # inserted flag_bytes = ctypes.string_at(res_ptr) try: flag = flag_bytes.decode(errors='ignore') except: flag = flag_bytes.decode('utf-8', errors='replace') print('=== VIP VERIFIED ===') print(flag) free_mem(res_ptr) return None print('Token invalid:', info) else: # inserted print('Unknown. Use chat/showtoken/upgrade/quit') if __name__ == '__main__': main() ``` Let analyze this python code: ``` def get_resource_path(name): if getattr(sys, 'frozen', False): base = sys._MEIPASS # PyInstaller-style temp dir else: base = os.path.dirname(__file__) # folder containing this .py return os.path.join(base, name) ``` That means all external files are searched next to the program ``` def load_native_lib(name): return CDLL(get_resource_path(name)) if sys.platform == 'win32': LIBNAME = 'libnative.dll' else: # inserted LIBNAME = 'libnative.so' lib = None check_integrity = None decrypt_flag_file = None free_mem = None try: lib = load_native_lib(LIBNAME) check_integrity = lib.check_integrity check_integrity.argtypes = [c_char_p] check_integrity.restype = c_int decrypt_flag_file = lib.decrypt_flag_file decrypt_flag_file.argtypes = [c_char_p] decrypt_flag_file.restype = c_void_p free_mem = lib.free_mem free_mem.argtypes = [c_void_p] free_mem.restype = None except Exception as e: print('Warning: native lib not loaded:', e) lib = None check_integrity = None decrypt_flag_file = None free_mem = None ``` This code tries to load a native library (libnative\.dll on Windows or libnative\.so on Linux) that exports: **check_integrity(char) -> int decrypt_flag_file(char) -> void free_mem(void) -> None** If loading fails, all three become None and the script prints a warning that native lib not loaded. ``` def run_integrity_or_exit(): if check_integrity: ok = check_integrity(sys.executable.encode()) if not ok: print('[!] Integrity failed or debugger detected. Exiting.') sys.exit(1) ``` "If" command show us **run_integrity_or_exit()** calls **check_integrity(sys.executable)**. If it returns 0, the program print **"Integrity failed or debugger detected. Exiting"** then exits. ``` PUB_PEM = b'-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsJftFGJC6RjAC54aMncA\nfjb2xXeRECiwHuz2wC6QynDd93/7XIrqTObeTpfBCSpOKRLhks6/nzZFTTsYdQCj\n4roXhWo5lFfH0OTL+164VoKnmUkQ9dppzpmV0Kpk5IQhEyuPYzJfFAlafcHdQvUo\nidkqcOPpR7hznJPEuRbPxJod34Bph/u9vePKcQQfe+/l/nn02nbfYWTuGtuEdpHq\nMkktl4WpB50/a5ZqYkW4z0zjFCY5LIPE7mpUNLrZnadBGIaLoVV2lZEBdLt6iLkV\nHXIr+xNA9ysE304T0JJ/DwM1OXb4yVrtawbFLBu9otOC+Gu0Set+8OjfQvJ+tlT/\nzQIDAQAB\n-----END PUBLIC KEY-----' public_key = None try: pub_path = get_resource_path('public.pem') if os.path.exists(pub_path): with open(pub_path, 'rb') as f: public_key = serialization.load_pem_public_key(f.read()) else: # inserted public_key = serialization.load_pem_public_key(PUB_PEM) except Exception as e: print('Failed loading public key:', e) public_key = None ``` It load an **RSA public key**: If a **public.pem** is next to the program it will use this file,else it turn back into **PUB_PEM**. The next explaination will be copied from chatGPT(because i so lazyπŸ₯±): **The β€œJWT” format used here (important!)** This is not a standard 3-part JWT. The code expects exactly two parts: ``` token = "<payload_b64url>.<signature_b64url>" ``` +)The signed content is just the raw payload bytes (no header). +)Signature algorithm: **RSA PKCS\#1 v1.5** with **SHA-256** (public_key.verify(sig, payload, PKCS1v15(), SHA256())). +)The payload is JSON and must include: **-"role": "VIP" -"expiry": <unix_time_in_future>** +)So a valid token = base64url(JSON) + β€œ.” + base64url(RSA_signature_over_raw_payload). Let go with our main job:Reverse!![image](https://hackmd.io/_uploads/r1AKa2GRxe.png) So in this code if we can reverse the .so file we can find the decode flag function To find the address of **decrypt_flag_file** we use: ``` nm -D libnative.so | grep -E 'decrypt_flag_file' ``` The output is: ``` 0000000000001610 T decrypt_flag_file ``` Let open **libnative\.so** in **radare2** Because cannot determine entrypoint, **radare2** will use **0x00001220** at first ``` ─$ r2 -w -e bin.cache=true -e bin.relocs.apply=true -A libnative.so ERROR: Cannot determine entrypoint, using 0x00001220 INFO: Analyze all flags starting with sym. and entry0 (aa) INFO: Analyze imports (af@@@i) INFO: Analyze entrypoint (af@ entry0) INFO: Analyze symbols (af@@@s) INFO: Analyze all functions arguments/locals (afva@@@F) INFO: Analyze function calls (aac) INFO: Analyze len bytes of instructions for references (aar) INFO: Finding and parsing C++ vtables (avrr) INFO: Analyzing methods (af @@ method.*) INFO: Recovering local variables (afva@@@F) INFO: Type matching analysis for all functions (aaft) INFO: Propagate noreturn information (aanr) INFO: Use -AA or aaaa to perform additional experimental analysis [0x00001220]> ``` Let see the **disassenbly decrypt_flag_file** and the **pseudo-c** of this function ``` s 0x00001610(point to 0x1610) af @ 0x00001610(define function) pdf @ 0x00001610(print disassembly function) pdc @ 0x00001610(decompile to pseudo-C) ``` The Output is: ``` [0x00001220]> s 0x00001610 [0x00001610]> af @ 0x00001610 [0x00001610]> pdf @ 0x00001610 ;-- rip: β”Œ 579: sym.decrypt_flag_file (int64_t arg1); β”‚ `- args(rdi) vars(4:sp[0x10..0x54]) β”‚ 0x00001610 4157 push r15 β”‚ 0x00001612 4156 push r14 β”‚ 0x00001614 4155 push r13 β”‚ 0x00001616 4154 push r12 β”‚ 0x00001618 55 push rbp β”‚ 0x00001619 53 push rbx β”‚ 0x0000161a 4889fb mov rbx, rdi ; arg1 β”‚ 0x0000161d 4883ec28 sub rsp, 0x28 β”‚ 0x00001621 e8bafcffff call sym.env_checks_ok β”‚ 0x00001626 85c0 test eax, eax β”‚ β”Œβ”€< 0x00001628 0f84b2010000 je 0x17e0 β”‚ β”‚ 0x0000162e 48c7442408.. mov qword [var_8h], 0 β”‚ β”‚ 0x00001637 488d7c2408 lea rdi, [var_8h] β”‚ β”‚ 0x0000163c e87ffaffff call fcn.000010c0 β”‚ β”‚ 0x00001641 4989c4 mov r12, rax β”‚ β”‚ 0x00001644 4885c0 test rax, rax β”‚ β”Œβ”€β”€< 0x00001647 0f8493010000 je 0x17e0 β”‚ β”‚β”‚ 0x0000164d 48837c24080f cmp qword [var_8h], 0xf β”‚ β”Œβ”€β”€β”€< 0x00001653 0f8677010000 jbe 0x17d0 β”‚ β”‚β”‚β”‚ 0x00001659 4889df mov rdi, rbx ; const char *filename β”‚ β”‚β”‚β”‚ 0x0000165c 488d35c609.. lea rsi, [0x00002029] ; "rb" ; const char *mode β”‚ β”‚β”‚β”‚ 0x00001663 e828fbffff call sym.imp.fopen ; file*fopen(const char *filename, const char *mode) β”‚ β”‚β”‚β”‚ 0x00001668 4889c3 mov rbx, rax β”‚ β”‚β”‚β”‚ 0x0000166b 4885c0 test rax, rax β”‚ β”Œβ”€β”€β”€β”€< 0x0000166e 0f845c010000 je 0x17d0 β”‚ β”‚β”‚β”‚β”‚ 0x00001674 31f6 xor esi, esi ; long offset β”‚ β”‚β”‚β”‚β”‚ 0x00001676 ba02000000 mov edx, 2 ; int whence β”‚ β”‚β”‚β”‚β”‚ 0x0000167b 4889c7 mov rdi, rax ; FILE *stream β”‚ β”‚β”‚β”‚β”‚ 0x0000167e e8edfaffff call sym.imp.fseek ; int fseek(FILE *stream, long offset, int whence) β”‚ β”‚β”‚β”‚β”‚ 0x00001683 4889df mov rdi, rbx ; FILE *stream β”‚ β”‚β”‚β”‚β”‚ 0x00001686 e8a5f9ffff call sym.imp.ftell ; long ftell(FILE *stream) β”‚ β”‚β”‚β”‚β”‚ 0x0000168b 31d2 xor edx, edx ; int whence β”‚ β”‚β”‚β”‚β”‚ 0x0000168d 31f6 xor esi, esi ; long offset β”‚ β”‚β”‚β”‚β”‚ 0x0000168f 4889df mov rdi, rbx ; FILE *stream β”‚ β”‚β”‚β”‚β”‚ 0x00001692 4889c5 mov rbp, rax β”‚ β”‚β”‚β”‚β”‚ 0x00001695 e8d6faffff call sym.imp.fseek ; int fseek(FILE *stream, long offset, int whence) β”‚ β”‚β”‚β”‚β”‚ 0x0000169a 4883fd10 cmp rbp, 0x10 β”‚ β”Œβ”€β”€β”€β”€β”€< 0x0000169e 0f8e64010000 jle 0x1808 β”‚ β”‚β”‚β”‚β”‚β”‚ 0x000016a4 4889ef mov rdi, rbp ; size_t size β”‚ β”‚β”‚β”‚β”‚β”‚ 0x000016a7 e804fbffff call sym.imp.malloc ; void *malloc(size_t size) β”‚ β”‚β”‚β”‚β”‚β”‚ 0x000016ac 4989c5 mov r13, rax β”‚ β”‚β”‚β”‚β”‚β”‚ 0x000016af 4885c0 test rax, rax β”‚ β”Œβ”€β”€β”€β”€β”€β”€< 0x000016b2 0f8450010000 je 0x1808 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016b8 4889d9 mov rcx, rbx ; FILE *stream β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016bb 4889ea mov rdx, rbp ; size_t nmemb β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016be be01000000 mov esi, 1 ; size_t size β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016c3 4889c7 mov rdi, rax ; void *ptr β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016c6 e8f5faffff call sym.imp.fread ; size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016cb 4889df mov rdi, rbx ; FILE *stream β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016ce 4d8d7510 lea r14, [r13 + 0x10] β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016d2 448d7df0 lea r15d, [var_10h] β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016d6 e855faffff call sym.imp.fclose ; int fclose(FILE *stream) β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016db f3410f6f4500 movdqu xmm0, xmmword [r13] β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016e1 0f29442410 movaps xmmword [var_sp_10h], xmm0 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016e6 e805faffff call sym.imp.EVP_CIPHER_CTX_new β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016eb 4889c3 mov rbx, rax β”‚ β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016ee 4885c0 test rax, rax β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€< 0x000016f1 0f8459010000 je 0x1850 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000016f7 48837c24081f cmp qword [var_8h], 0x1f β”‚ ────────< 0x000016fd 0f86f5000000 jbe 0x17f8 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001703 e8d8f9ffff call sym.imp.EVP_aes_256_cbc β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001708 4889c6 mov rsi, rax β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ ; CODE XREF from sym.decrypt_flag_file @ 0x1800(x) β”‚ ────────> 0x0000170b 31d2 xor edx, edx β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000170d 4c8d442410 lea r8, [var_sp_10h] β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001712 4c89e1 mov rcx, r12 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001715 4889df mov rdi, rbx β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001718 e833f9ffff call sym.imp.EVP_DecryptInit_ex β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000171d 85c0 test eax, eax β”‚ ────────< 0x0000171f 0f8423010000 je 0x1848 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001725 4863fd movsxd rdi, ebp ; size_t size β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001728 e883faffff call sym.imp.malloc ; void *malloc(size_t size) β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000172d 4889e2 mov rdx, rsp β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001730 4589f8 mov r8d, r15d β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001733 4c89f1 mov rcx, r14 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001736 4889c6 mov rsi, rax β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001739 4889df mov rdi, rbx β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000173c c704240000.. mov dword [rsp], 0 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001743 4889c5 mov rbp, rax β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001746 c744240400.. mov dword [var_4h], 0 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000174e e87df9ffff call sym.imp.EVP_DecryptUpdate β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001753 85c0 test eax, eax β”‚ ────────< 0x00001755 0f84c5000000 je 0x1820 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000175b 48633424 movsxd rsi, dword [rsp] β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000175f 488d542404 lea rdx, [var_4h] β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001764 4889df mov rdi, rbx β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001767 4801ee add rsi, rbp β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000176a e801f9ffff call sym.imp.EVP_DecryptFinal_ex β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000176f 85c0 test eax, eax β”‚ ────────< 0x00001771 0f84a9000000 je 0x1820 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001777 448b7c2404 mov r15d, dword [var_4h] β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000177c 44033c24 add r15d, dword [rsp] β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001780 418d7f01 lea edi, [r15 + 1] β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001784 4863ff movsxd rdi, edi ; size_t size β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001787 e824faffff call sym.imp.malloc ; void *malloc(size_t size) β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000178c 4989c6 mov r14, rax β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000178f 4885c0 test rax, rax β”‚ ────────< 0x00001792 0f8488000000 je 0x1820 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x00001798 4d63ff movsxd r15, r15d β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000179b 4889ee mov rsi, rbp ; const void *s2 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x0000179e 4889c7 mov rdi, rax ; void *s1 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017a1 4c89fa mov rdx, r15 ; size_t n β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017a4 e857f9ffff call sym.imp.memcpy ; void *memcpy(void *s1, const void *s2, size_t n) β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017a9 43c6043e00 mov byte [r14 + r15], 0 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017ae 4889df mov rdi, rbx β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017b1 e89af9ffff call sym.imp.EVP_CIPHER_CTX_free β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017b6 4889ef mov rdi, rbp ; void *ptr β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017b9 e8e2f9ffff call sym.imp.free ; void free(void *ptr) β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017be 4c89ef mov rdi, r13 ; void *ptr β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017c1 e8daf9ffff call sym.imp.free ; void free(void *ptr) β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017c6 4c89e7 mov rdi, r12 ; void *ptr β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ 0x000017c9 e8d2f9ffff call sym.imp.free ; void free(void *ptr) β”‚ ────────< 0x000017ce eb13 jmp 0x17e3 β”‚ β”‚β”‚β”‚β”‚β”‚β”‚β”‚ ; CODE XREFS from sym.decrypt_flag_file @ 0x1653(x), 0x166e(x) β”‚ │││└└───> 0x000017d0 4c89e7 mov rdi, r12 ; void *ptr β”‚ β”‚β”‚β”‚ β”‚β”‚ 0x000017d3 e8c8f9ffff call sym.imp.free ; void free(void *ptr) β”‚ β”‚β”‚β”‚ β”‚β”‚ 0x000017d8 0f1f840000.. nop dword [rax + rax] β”‚ β”‚β”‚β”‚ β”‚β”‚ ; CODE XREFS from sym.decrypt_flag_file @ 0x1628(x), 0x1647(x), 0x1818(x), 0x1840(x), 0x1860(x) β”‚ β”€β”€β”€β”Œβ”Œβ””β””β”€> 0x000017e0 4531f6 xor r14d, r14d β”‚ β”‚β”‚β”‚β•Žβ•Ž ; CODE XREF from sym.decrypt_flag_file @ 0x17ce(x) β”‚ ────────> 0x000017e3 4883c428 add rsp, 0x28 β”‚ β”‚β”‚β”‚β•Žβ•Ž 0x000017e7 4c89f0 mov rax, r14 β”‚ β”‚β”‚β”‚β•Žβ•Ž 0x000017ea 5b pop rbx β”‚ β”‚β”‚β”‚β•Žβ•Ž 0x000017eb 5d pop rbp β”‚ β”‚β”‚β”‚β•Žβ•Ž 0x000017ec 415c pop r12 β”‚ β”‚β”‚β”‚β•Žβ•Ž 0x000017ee 415d pop r13 β”‚ β”‚β”‚β”‚β•Žβ•Ž 0x000017f0 415e pop r14 β”‚ β”‚β”‚β”‚β•Žβ•Ž 0x000017f2 415f pop r15 β”‚ β”‚β”‚β”‚β•Žβ•Ž 0x000017f4 c3 ret .. β”‚ β”‚β”‚β”‚β•Žβ•Ž ; CODE XREF from sym.decrypt_flag_file @ 0x16fd(x) β”‚ ────────> 0x000017f8 e8b3f8ffff call sym.imp.EVP_aes_128_cbc β”‚ β”‚β”‚β”‚β•Žβ•Ž 0x000017fd 4889c6 mov rsi, rax β”‚ ────────< 0x00001800 e906ffffff jmp 0x170b .. β”‚ β”‚β”‚β”‚β•Žβ•Ž ; CODE XREFS from sym.decrypt_flag_file @ 0x169e(x), 0x16b2(x) β”‚ │└└─────> 0x00001808 4889df mov rdi, rbx ; FILE *stream β”‚ β”‚ β•Žβ•Ž 0x0000180b e820f9ffff call sym.imp.fclose ; int fclose(FILE *stream) β”‚ β”‚ β•Žβ•Ž 0x00001810 4c89e7 mov rdi, r12 ; void *ptr β”‚ β”‚ β•Žβ•Ž 0x00001813 e888f9ffff call sym.imp.free ; void free(void *ptr) β”‚ ────────< 0x00001818 ebc6 jmp 0x17e0 .. β”‚ β”‚ β•Žβ•Ž ; CODE XREFS from sym.decrypt_flag_file @ 0x1755(x), 0x1771(x), 0x1792(x) β”‚ ────────> 0x00001820 4889df mov rdi, rbx β”‚ β”‚ β•Žβ•Ž 0x00001823 e828f9ffff call sym.imp.EVP_CIPHER_CTX_free β”‚ β”‚ β•Žβ•Ž 0x00001828 4c89ef mov rdi, r13 ; void *ptr β”‚ β”‚ β•Žβ•Ž 0x0000182b e870f9ffff call sym.imp.free ; void free(void *ptr) β”‚ β”‚ β•Žβ•Ž 0x00001830 4c89e7 mov rdi, r12 ; void *ptr β”‚ β”‚ β•Žβ•Ž 0x00001833 e868f9ffff call sym.imp.free ; void free(void *ptr) β”‚ β”‚ β•Žβ•Ž 0x00001838 4889ef mov rdi, rbp ; void *ptr β”‚ β”‚ β•Žβ•Ž 0x0000183b e860f9ffff call sym.imp.free ; void free(void *ptr) β”‚ β”‚ └────< 0x00001840 eb9e jmp 0x17e0 .. β”‚ β”‚ β•Ž ; CODE XREF from sym.decrypt_flag_file @ 0x171f(x) β”‚ ────────> 0x00001848 4889df mov rdi, rbx β”‚ β”‚ β•Ž 0x0000184b e800f9ffff call sym.imp.EVP_CIPHER_CTX_free β”‚ β”‚ β•Ž ; CODE XREF from sym.decrypt_flag_file @ 0x16f1(x) β”‚ └───────> 0x00001850 4c89ef mov rdi, r13 ; void *ptr β”‚ β•Ž 0x00001853 e848f9ffff call sym.imp.free ; void free(void *ptr) β”‚ β•Ž 0x00001858 4c89e7 mov rdi, r12 ; void *ptr β”‚ β•Ž 0x0000185b e840f9ffff call sym.imp.free ; void free(void *ptr) β”” └───< 0x00001860 e97bffffff jmp 0x17e0 [0x00001610]> pdc @ 0x00001610 // callconv: rax amd64 (rdi, rsi, rdx, rcx, r8, r9, xmm0, xmm1, xmm2, xmm3, xmm4); void sym.decrypt_flag_file (int64_t arg1) { loc_0x00001610: push (r15) push (r14) push (r13) push (r12) push (rbp) push (rbx) rbx = rdi // arg1 rsp -= 0x28 // (cstr 0x00000020) "@" sym.env_checks_ok () v = eax & eax if (!v) goto loc_0x17e0 // likely goto loc_0x0000162e; loc_0x000017e0: // CODE XREFS from sym.decrypt_flag_file @ 0x1628(x), 0x1647(x), 0x1818(x), 0x1840(x), 0x1860(x) r14d = 0 loc_0x000017e3: // CODE XREF from sym.decrypt_flag_file @ 0x17ce(x) rsp += 0x28 rax = r14 rbx = pop () rbp = pop () r12 = pop () r13 = pop () r14 = pop () r15 = pop () return goto loc_0x0000164d; return rax; loc_0x0000164d: v = qword [var_8h] - 0xf if (((unsigned) v) <= 0) goto 0x17d0 // likely goto loc_0x00001659; loc_0x000017d0: // CODE XREFS from sym.decrypt_flag_file @ 0x1653(x), 0x166e(x) rdi = r12 // void *ptr sym.imp.free () // void free(0) goto loc_0x000017e0 loc_0x00001659: // orphan rdi = rbx // const char *filename rsi = rip + 0x9c6 // "rb" // 0x2029 // const char *mode sym.imp.fopen () // file*fopen(0, "rb") rbx = rax v = rax & rax if (!v) goto loc_0x000017d0 loc_0x00001674: // orphan esi = 0 // long offset edx = 2 // int whence rdi = rax // FILE *stream sym.imp.fseek () // int fseek(0, 0, 0x00000000) rdi = rbx // FILE *stream sym.imp.ftell () // long ftell(0) edx = 0 // int whence esi = 0 // long offset rdi = rbx // FILE *stream rbp = rax sym.imp.fseek () // int fseek(0, 0, 0) v = rbp - 0x10 if (v <= 0) goto loc_0x00001808 loc_0x000016a4: // orphan rdi = rbp // size_t size // rsp sym.imp.malloc () // void *malloc(0x00000000) r13 = rax v = rax & rax if (!v) goto loc_0x00001808 loc_0x000016b8: // orphan rcx = rbx // FILE *stream rdx = rbp // size_t nmemb // rsp esi = 1 // size_t size rdi = rax // void *ptr sym.imp.fread () // size_t fread(0, 0x00000000, 0x00000000, 0) rdi = rbx // FILE *stream r14 = r13 + 0x10 r15d = var_10h sym.imp.fclose () // int fclose(0) xmm0 = xmmword [r13] xmmword [var_sp_10h] = xmm0 sym.imp.EVP_CIPHER_CTX_new () rbx = rax v = rax & rax if (!v) goto loc_0x00001850 loc_0x000016f7: // orphan v = qword [var_8h] - 0x1f if (((unsigned) v) <= 0) goto loc_0x000017f8 loc_0x00001703: // orphan sym.imp.EVP_aes_256_cbc () rsi = rax loc_0x0000170b: // orphan // CODE XREF from sym.decrypt_flag_file @ 0x1800(x) edx = 0 r8 = var_sp_10h rcx = r12 rdi = rbx sym.imp.EVP_DecryptInit_ex () v = eax & eax if (!v) goto loc_0x00001848 loc_0x00001725: // orphan rdi = ebp // size_t size // rsp sym.imp.malloc () // void *malloc(0x00000000) rdx = rsp r8d = r15d rcx = r14 rsi = rax rdi = rbx dword [rsp] = 0 rbp = rax dword [var_4h] = 0 sym.imp.EVP_DecryptUpdate () v = eax & eax if (!v) goto loc_0x00001820 loc_0x0000175b: // orphan rsi = dword [rsp] rdx = var_4h rdi = rbx rsi += rbp // rsp sym.imp.EVP_DecryptFinal_ex () v = eax & eax if (!v) goto loc_0x00001820 loc_0x00001777: // orphan r15d = dword [var_4h] r15d += dword [rsp] edi = r15 + 1 rdi = edi // size_t size sym.imp.malloc () // void *malloc(0x00000000) r14 = rax v = rax & rax if (!v) goto loc_0x00001820 loc_0x00001798: // orphan r15 = r15d rsi = rbp // const void *s2 // rsp rdi = rax // void *s1 rdx = r15 // size_t n sym.imp.memcpy () // void *memcpy(0, 0x0000000000000000, 0) byte [r14 + r15] = 0 rdi = rbx sym.imp.EVP_CIPHER_CTX_free () rdi = rbp // void *ptr // rsp sym.imp.free () // void free(0x0000000000000000) rdi = r13 // void *ptr sym.imp.free () // void free(0) rdi = r12 // void *ptr sym.imp.free () // void free(0) goto loc_0x000017e3 loc_0x000017e3: // orphan // CODE XREF from sym.decrypt_flag_file @ 0x17ce(x) rsp += 0x28 rax = r14 rbx = pop () rbp = pop () r12 = pop () r13 = pop () r14 = pop () r15 = pop () return return rax; loc_0x000017f8: // orphan // CODE XREF from sym.decrypt_flag_file @ 0x16fd(x) sym.imp.EVP_aes_128_cbc () rsi = rax goto loc_0x0000170b loc_0x00001808: // orphan // CODE XREFS from sym.decrypt_flag_file @ 0x169e(x), 0x16b2(x) rdi = rbx // FILE *stream sym.imp.fclose () // int fclose(0) rdi = r12 // void *ptr sym.imp.free () // void free(0) goto loc_0x000017e0 loc_0x00001820: // orphan // CODE XREFS from sym.decrypt_flag_file @ 0x1755(x), 0x1771(x), 0x1792(x) rdi = rbx sym.imp.EVP_CIPHER_CTX_free () rdi = r13 // void *ptr sym.imp.free () // void free(0) rdi = r12 // void *ptr sym.imp.free () // void free(0) rdi = rbp // void *ptr // rsp sym.imp.free () // void free(0x0000000000000000) goto loc_0x000017e0 loc_0x00001848: // orphan // CODE XREF from sym.decrypt_flag_file @ 0x171f(x) rdi = rbx sym.imp.EVP_CIPHER_CTX_free () loc_0x00001850: // orphan // CODE XREF from sym.decrypt_flag_file @ 0x16f1(x) rdi = r13 // void *ptr sym.imp.free () // void free(0) rdi = r12 // void *ptr sym.imp.free () // void free(0) goto loc_0x000017e0 } ``` We have dump all the flow of **decrypt_flag_file**,let patch **env_checks_ok** directly to return 1: ``` cp libnative.so libnative.so.bak(backup) r2 -w -e bin.relocs.apply=true -q -c 's 0x000015a0; wx b801000000c3' libnative.so(patch) r2 -e bin.relocs.apply=true -qc 'pd 3 @ 0x000015a0' libnative.so(verify) ``` The output is: ``` ;-- env_checks_ok: 0x000012e0 b801000000 mov eax, 1 0x000012e5 c3 ret 0x000012e6 0d0000488d or eax, 0x8d480000 ``` Let use this python code i take from chatGPT to bypass the chatbot entirely and call the native decryptor directly. ``` # bypass_vip_and_dump_flag_diag.py import os, sys, ctypes from ctypes import c_char_p, c_void_p, c_int def get_resource_path(name): base = getattr(sys, "_MEIPASS", os.path.dirname(__file__)) return os.path.join(base, name) LIBNAME = "libnative.dll" if sys.platform == "win32" else "libnative.so" lib_path = os.path.abspath(get_resource_path(LIBNAME)) flag_path = os.path.abspath(get_resource_path("flag.enc")) exe_path = os.path.abspath(sys.executable) print("[i] lib path :", lib_path) print("[i] flag path:", flag_path) print("[i] exe path :", exe_path) if not os.path.exists(lib_path): raise SystemExit("[!] Library not found") if not os.path.exists(flag_path): raise SystemExit("[!] flag.enc not found here") lib = ctypes.CDLL(lib_path) # prototypes (match the app) try: check_integrity = lib.check_integrity check_integrity.argtypes = [c_char_p] check_integrity.restype = c_int except AttributeError: check_integrity = None decrypt_flag_file = lib.decrypt_flag_file decrypt_flag_file.argtypes = [c_char_p] decrypt_flag_file.restype = c_void_p free_mem = lib.free_mem free_mem.argtypes = [c_void_p] free_mem.restype = None # 1) Some builds require this call first. if check_integrity is not None: try: ok = check_integrity(exe_path.encode()) print(f"[i] check_integrity({exe_path}) -> {ok}") # If it *must* pass, ok should be nonzero. If it returns 0, the lib may refuse to decrypt. except Exception as e: print("[!] check_integrity raised:", e) # 2) Use ABSOLUTE path to flag.enc ptr = decrypt_flag_file(flag_path.encode()) if not ptr: raise SystemExit("[!] decrypt_flag_file returned NULL (lib refused / error)") try: flag_bytes = ctypes.string_at(ptr) try: flag = flag_bytes.decode() except UnicodeDecodeError: flag = flag_bytes.decode("utf-8", errors="replace") print("=== VIP VERIFIED ===") print(flag) finally: free_mem(ptr) ``` The output when we run this python code should be: ``` └─$ python3 bypass_vip_and_dump_flag_diag.py [i] lib path : /home/son/main_extracted/libnative.so [i] flag path: /home/son/main_extracted/flag.enc [i] exe path : /usr/bin/python3 [i] check_integrity(/usr/bin/python3) -> 1 === VIP VERIFIED === CSCV2025{reversed_vip*_chatbot_bypassed} ``` So the flag is CSCV2025{reversed_vip*_chatbot_bypassed}. Thank for reading this post!πŸ’–πŸ’–πŸ’–