Skip to main content

Write-up for Chatbot CSCV2025

·3693 words·18 mins

Hello everyoneπŸ’―! Today i will tell you the way to solve the Chatbot challenge in CSCV2025

The question:

image

So at first you will have an file main like this below:

image
Let open this file in DIE to see the information about this file:
image
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
Scroll down a little bit and you will see this:
image
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
Go back to the folder you put the main file and you will see there are an additional folder name main_extracted:
image

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
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!πŸ’–πŸ’–πŸ’–