Note:
I did not finish the last part of the challenge as it clashed with CNY 2025 and I got covid mid-way and was down for almost a week.. I would say I'm 90% completed though.. what a waste..
Pre-requistes
Before we start reverse engineering the executables in ancient-runes.zip, we have to download a few tools first.
WireShark
Download the appropriate version from https://www.wireshark.org/download.html.
Pyinstaller Extractor
First clone the Github repository at https://github.com/extremecoders-re/pyinstxtractor.git.
git clone https://github.com/extremecoders-re/pyinstxtractor.git
Decompyle++
First clone the Github repository at https://github.com/zrax/pycdc.git.
git clone https://github.com/zrax/pycdc.git
Since I have homebrew installed on my Macbook, I will install CMake and make to compile the source code above.
brew install cmake make
Once the formulas are installed, we are ready to go!
cmake -DCMAKE_BUILD_TYPE=Debug
make
sudo cp pycdc /usr/local/bin
sudo cp pycdas /usr/local/bin
Analysis of the Artifact
After downloading the file, I extracted the zip file and found the following content:
➜ pyinstxtractor git:(master) ✗ ll ../ancient-runes
total 35792
-rwxr-xr-x@ 1 alex staff 8.7M Jan 13 22:23 client
-rwxr-xr-x@ 1 alex staff 8.7M Jan 13 22:22 dev_server
-rwxrwxrwx@ 1 alex staff 29K Jan 20 22:24 蛇年吉祥.pcapng
Let's try to run the executables. Since I'm using M2 Macbook Pro, I'm on the ARM64 architecture. I need to check the executable to see if it's compiled for ARM64 or x64.
➜ ancient-runes file client
client: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8485f6953c06d12b9865185ba3466fdbf9b4a65c, for GNU/Linux 2.6.32, stripped
➜ ancient-runes file dev_server
dev_server: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=8485f6953c06d12b9865185ba3466fdbf9b4a65c, for GNU/Linux 2.6.32, stripped
In this case, I will have to use a docker container in x64 arch to execute the files. Let's start by creating a network:
docker network create csit-test-network
Then create the container to examine the executables:
docker run -it --platform linux/amd64 --network csit-test-network -v <path to folder>/ancient-runes:/files debian /bin/bash
Running client:
root@a94da9751b86:/files# ./client
2025-01-23 20:27:56,979 - INFO - DEV_client.crt not found. Downloading...
2025-01-23 20:27:57,427 - INFO - Downloaded file from http://34.57.139.144:80/REVW/DEV_client.crt to DEV_client.crt
2025-01-23 20:27:57,428 - INFO - DEV_client.key not found. Downloading...
2025-01-23 20:27:57,987 - INFO - Downloaded file from http://34.57.139.144:80/REVW/DEV_client.key to DEV_client.key
2025-01-23 20:27:57,987 - INFO - DEV_ca.crt not found. Downloading...
2025-01-23 20:27:58,455 - INFO - Downloaded file from http://34.57.139.144:80/REVW/DEV_ca.crt to DEV_ca.crt
Running server:
root@a94da9751b86:/files# ./dev_server
2025-01-23 20:28:24,684 - INFO - DEV_server.crt not found. Downloading...
2025-01-23 20:28:25,301 - INFO - Downloaded file from http://34.57.139.144:80/REVW/DEV_server.crt to DEV_server.crt
2025-01-23 20:28:25,302 - INFO - DEV_server.key not found. Downloading...
2025-01-23 20:28:25,876 - INFO - Downloaded file from http://34.57.139.144:80/REVW/DEV_server.key to DEV_server.key
2025-01-23 20:28:25,877 - INFO - DEV_ca.crt found.
2025-01-23 20:28:25,952 - INFO - server listening on 0.0.0.0:45605
2025-01-23 20:28:25,952 - INFO - server listening on [::]:45025
2025-01-23 20:28:25,952 - INFO - Server started...
That's a good start. It downloaded DEV_client.crt, DEV_client.key, DEV_server.crt, DEV_server.key, DEV_ca.crt from http://34.57.139.144:80/REVW/<certs>.
Let's try to analyze the executables by decompiling to understand how it works.
Since the hint was given these 2 executable was created with PyInstaller, we can use PyInstaller Extractor to extract the content of the executables (client & dev_server).
➜ pyinstxtractor git:(master) python3 pyinstxtractor.py ../ancient-runes/client
[+] Processing ../ancient-runes/client
[+] Pyinstaller version: 2.1+
[+] Python version: 3.10
[+] Length of package: 9084051 bytes
[+] Found 98 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: pyi_rth_pkgutil.pyc
[+] Possible entry point: pyi_rth_multiprocessing.pyc
[+] Possible entry point: client.pyc
[!] Warning: This script is running in a different Python version than the one used to build the executable.
[!] Please run this script in Python 3.10 to prevent extraction errors during unmarshalling
[!] Skipping pyz extraction
[+] Successfully extracted pyinstaller archive: ../ancient-runes/client
You can now use a python decompiler on the pyc files within the extracted directory
➜ pyinstxtractor git:(master) ✗ python3 pyinstxtractor.py ../ancient-runes/dev_server
[+] Processing ../ancient-runes/dev_server
[+] Pyinstaller version: 2.1+
[+] Python version: 3.10
[+] Length of package: 9084774 bytes
[+] Found 98 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: pyi_rth_pkgutil.pyc
[+] Possible entry point: pyi_rth_multiprocessing.pyc
[+] Possible entry point: dev_server.pyc
[!] Warning: This script is running in a different Python version than the one used to build the executable.
[!] Please run this script in Python 3.10 to prevent extraction errors during unmarshalling
[!] Skipping pyz extraction
[+] Successfully extracted pyinstaller archive: ../ancient-runes/dev_server
You can now use a python decompiler on the pyc files within the extracted directory
PyInstaller Extractor is telling us that the client and dev_server is using Python 3.10, so I re-did the extraction again with Python 3.10.
➜ pyinstxtractor git:(master) ✗ python3.10 pyinstxtractor.py ../ancient-runes/dev_server
[+] Processing ../ancient-runes/dev_server
[+] Pyinstaller version: 2.1+
[+] Python version: 3.10
[+] Length of package: 9084774 bytes
[+] Found 98 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: pyi_rth_pkgutil.pyc
[+] Possible entry point: pyi_rth_multiprocessing.pyc
[+] Possible entry point: dev_server.pyc
[+] Found 282 files in PYZ archive
[+] Successfully extracted pyinstaller archive: ../ancient-runes/dev_server
You can now use a python decompiler on the pyc files within the extracted directory
➜ pyinstxtractor git:(master) ✗ python3.10 pyinstxtractor.py ../ancient-runes/client
[+] Processing ../ancient-runes/client
[+] Pyinstaller version: 2.1+
[+] Python version: 3.10
[+] Length of package: 9084051 bytes
[+] Found 98 files in CArchive
[+] Beginning extraction...please standby
[+] Possible entry point: pyiboot01_bootstrap.pyc
[+] Possible entry point: pyi_rth_inspect.pyc
[+] Possible entry point: pyi_rth_pkgutil.pyc
[+] Possible entry point: pyi_rth_multiprocessing.pyc
[+] Possible entry point: client.pyc
[+] Found 282 files in PYZ archive
[+] Successfully extracted pyinstaller archive: ../ancient-runes/client
You can now use a python decompiler on the pyc files within the extracted directory
Inspection of Client
Let's inspect the files of client executable:
➜ pyinstxtractor git:(master) ✗ cd client_extracted
➜ client_extracted git:(master) ✗ ll
total 29232
drwxr-xr-x@ 8 alex staff 256B Jan 24 01:39 Crypto
-rw-r--r--@ 1 alex staff 1.6M Jan 24 01:38 PYZ-00.pyz
drwxr-xr-x@ 2 alex staff 64B Jan 24 01:38 PYZ-00.pyz_extracted
-rw-r--r--@ 1 alex staff 860K Jan 24 01:38 base_library.zip
-rw-r--r--@ 1 alex staff 2.5K Jan 24 01:38 client.pyc
drwxr-xr-x@ 26 alex staff 832B Jan 24 01:39 lib-dynload
-rw-r--r--@ 1 alex staff 73K Jan 24 01:38 libbz2.so.1.0
-rw-r--r--@ 1 alex staff 4.2M Jan 24 01:38 libcrypto.so.3
-rw-r--r--@ 1 alex staff 190K Jan 24 01:38 libexpat.so.1
-rw-r--r--@ 1 alex staff 47K Jan 24 01:38 libffi.so.8
-rw-r--r--@ 1 alex staff 166K Jan 24 01:38 liblzma.so.5
-rw-r--r--@ 1 alex staff 183K Jan 24 01:38 libmpdec.so.3
-rw-r--r--@ 1 alex staff 5.6M Jan 24 01:38 libpython3.10.so
-rw-r--r--@ 1 alex staff 328K Jan 24 01:38 libreadline.so.8
-rw-r--r--@ 1 alex staff 652K Jan 24 01:38 libssl.so.3
-rw-r--r--@ 1 alex staff 195K Jan 24 01:38 libtinfo.so.6
-rw-r--r--@ 1 alex staff 30K Jan 24 01:38 libuuid.so.1
-rw-r--r--@ 1 alex staff 106K Jan 24 01:38 libz.so.1
-rw-r--r--@ 1 alex staff 909B Jan 24 01:38 pyi_rth_inspect.pyc
-rw-r--r--@ 1 alex staff 1.1K Jan 24 01:38 pyi_rth_multiprocessing.pyc
-rw-r--r--@ 1 alex staff 964B Jan 24 01:38 pyi_rth_pkgutil.pyc
-rw-r--r--@ 1 alex staff 873B Jan 24 01:38 pyiboot01_bootstrap.pyc
-rw-r--r--@ 1 alex staff 3.0K Jan 24 01:38 pyimod01_archive.pyc
-rw-r--r--@ 1 alex staff 21K Jan 24 01:38 pyimod02_importers.pyc
-rw-r--r--@ 1 alex staff 3.6K Jan 24 01:38 pyimod03_ctypes.pyc
-rw-r--r--@ 1 alex staff 287B Jan 24 01:38 struct.pyc
drwxr-xr-x@ 3 alex staff 96B Jan 24 01:39 websockets
drwxr-xr-x@ 9 alex staff 288B Jan 24 01:39 websockets-14.1.dist-info
Let's decompile the bytecode for client.pyc.
➜ client_extracted git:(master) ✗ pycdc client.pyc > client.py
Unsupported opcode: BEFORE_ASYNC_WITH (94)
Unsupported opcode: BEFORE_ASYNC_WITH (94)
Let's look at the content for client.py
# Source Generated with Decompyle++
# File: client.pyc (Python 3.10)
from dotenv import load_dotenv
import asyncio
import websockets
import logging
import threading
import ssl
import os
import urllib.request as urllib
import sys
import socket
from common import validate, generate_client_context, generate_random_string
from constants import LOGGING_VERBOSITY, HINT_3
load_dotenv()
logging.basicConfig(LOGGING_VERBOSITY, '%(asctime)s - %(levelname)s - %(message)s', **('level', 'format'))
SERVER_IP = os.getenv('SERVER_IP', '127.127.127.127')
SERVER_PORT = os.getenv('SERVER_PORT', '9999')
WS_IP = os.getenv('WS_IP', '0.0.0.0')
WS_PORT = os.getenv('WS_PORT', '8000')
TIMEOUT = 5
CA_CERT = 'DEV_ca.crt'
CLIENT_CERT = 'DEV_client.crt'
CLIENT_KEY = 'DEV_client.key'
CLIENT_SERVER = f'''http://{SERVER_IP}:{SERVER_PORT}/REVW/'''
CLIENT_PATH = ''
CERT_URL = f'''{CLIENT_SERVER}{CLIENT_CERT}'''
CERT_PATH = f'''{CLIENT_PATH}{CLIENT_CERT}'''
KEY_URL = f'''{CLIENT_SERVER}{CLIENT_KEY}'''
KEY_PATH = f'''{CLIENT_PATH}{CLIENT_KEY}'''
CA_URL = f'''{CLIENT_SERVER}{CA_CERT}'''
CA_PATH = f'''{CLIENT_PATH}{CA_CERT}'''
validate(CERT_URL, CERT_PATH, logging)
validate(KEY_URL, KEY_PATH, logging)
validate(CA_URL, CA_PATH, logging)
context = generate_client_context(CERT_PATH, KEY_PATH, CA_PATH, logging)
async def listen_messages(uri, headers, log):
pass
# WARNING: Decompyle incomplete
async def send_commands(uri, headers, log):
pass
# WARNING: Decompyle incomplete
def start_client():
_string = generate_random_string()
headersl = {
'X-ARBOC': f'''{_string}-listener''' }
headerss = {
'X-ARBOC': f'''{_string}-sender''' }
uri = f'''wss://{WS_IP}:{WS_PORT}/'''
listener_thread = None((lambda : asyncio.run(listen_messages(uri, headersl, logging))), **('target',))
listener_thread.daemon = True
listener_thread.start()
asyncio.run(send_commands(uri, headerss, logging))
if __name__ == '__main__':
start_client()
return None
We can see that the async functions listen_messages() and send_commands() fails to decompile.
Let's try with another tool PyLingual. I get the following. It looks like a very simple websocket client.
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: client.py
# Bytecode version: 3.10.0rc2 (3439)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)
from dotenv import load_dotenv
import asyncio
import websockets
import logging
import threading
import ssl
import os
import urllib.request
import sys
import socket
from common import validate, generate_client_context, generate_random_string
from constants import LOGGING_VERBOSITY, HINT_3
load_dotenv()
logging.basicConfig(level=LOGGING_VERBOSITY, format='%(asctime)s - %(levelname)s - %(message)s')
SERVER_IP = os.getenv('SERVER_IP', '127.127.127.127')
SERVER_PORT = os.getenv('SERVER_PORT', '9999')
WS_IP = os.getenv('WS_IP', '0.0.0.0')
WS_PORT = os.getenv('WS_PORT', '8000')
TIMEOUT = 5
CA_CERT = 'DEV_ca.crt'
CLIENT_CERT = 'DEV_client.crt'
CLIENT_KEY = 'DEV_client.key'
CLIENT_SERVER = f'http://{SERVER_IP}:{SERVER_PORT}/REVW/'
CLIENT_PATH = ''
CERT_URL = f'{CLIENT_SERVER}{CLIENT_CERT}'
CERT_PATH = f'{CLIENT_PATH}{CLIENT_CERT}'
KEY_URL = f'{CLIENT_SERVER}{CLIENT_KEY}'
KEY_PATH = f'{CLIENT_PATH}{CLIENT_KEY}'
CA_URL = f'{CLIENT_SERVER}{CA_CERT}'
CA_PATH = f'{CLIENT_PATH}{CA_CERT}'
validate(CERT_URL, CERT_PATH, logging)
validate(KEY_URL, KEY_PATH, logging)
validate(CA_URL, CA_PATH, logging)
context = generate_client_context(CERT_PATH, KEY_PATH, CA_PATH, logging)
async def listen_messages(uri, headers, log):
try:
async with websockets.connect(uri, additional_headers=headers, ssl=context) as websocket:
while True:
message = await websocket.recv()
print(f'>> {message}0')
except asyncio.TimeoutError:
return None
except Exception as E:
log.debug(f'{HINT_3}')
async def send_commands(uri, headers, log):
try:
async with websockets.connect(uri, additional_headers=headers, ssl=context) as websocket:
while True:
message = input('')
if message.strip():
await websocket.send(message)
except asyncio.TimeoutError:
return None
except Exception as E:
log.debug(f'{HINT_3}')
def start_client():
_string = generate_random_string()
headersl = {'X-ARBOC': f'{_string}0-listener'}
headerss = {'X-ARBOC': f'{_string}0-sender'}
uri = f'wss://{WS_IP}:{WS_PORT}/'
listener_thread = threading.Thread(target=lambda: asyncio.run(listen_messages(uri, headersl, logging)))
listener_thread.daemon = True
listener_thread.start()
asyncio.run(send_commands(uri, headerss, logging))
if __name__ == '__main__':
start_client()
We can see that there's a common module that was imported and not part of the standard python module. Let's try to decompile common.pyc bytecode!
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: common.py
# Bytecode version: 3.10.0rc2 (3439)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)
import os
import urllib.request
import ssl
import random
import string
from Crypto.Cipher import DES3
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from constants import HINT_1, HINT_2
def generate_random_string(length=8):
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
def download_file(url, dest_path, log):
try:
with urllib.request.urlopen(url) as response:
with open(dest_path, 'wb') as f:
f.write(response.read())
log.info(f'Downloaded file from {url}0 to {dest_path}0')
return True
except Exception as e:
return False
def validate(URL, FILE, log):
if not os.path.exists(FILE):
log.info(f'{FILE} not found. Downloading...')
if not download_file(URL, FILE, log):
log.debug(f'{HINT_1}')
else:
log.info(f'{FILE} found.')
def decrypt_3des(ciphertext: bytes, key: bytes):
iv = ciphertext[:DES3.block_size]
cipher = DES3.new(key, DES3.MODE_CBC, iv=iv)
decrypted_data = unpad(cipher.decrypt(ciphertext[DES3.block_size:]), DES3.block_size)
return decrypted_data.decode()
def generate_client_context(CERT_PATH, KEY_PATH, CA_PATH, log):
try:
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile=CA_PATH)
context.options |= ssl.OP_NO_SSLv2
context.options |= ssl.OP_NO_SSLv3
context.load_cert_chain(certfile=CERT_PATH, keyfile=KEY_PATH)
context.options |= ssl.OP_NO_TLSv1_3
context.set_ciphers('RSA')
return context
except Exception as e:
print(e)
log.debug(f'{HINT_2}')
def generate_server_context(CERT_PATH, KEY_PATH, CA_PATH, log):
try:
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain(certfile=CERT_PATH, keyfile=KEY_PATH)
context.load_verify_locations(cafile=CA_PATH)
context.verify_mode = ssl.CERT_REQUIRED
context.options |= ssl.OP_NO_TLSv1_3
context.set_ciphers('RSA')
return context
except Exception as e:
log.debug(f'{HINT_2}')
def init():
if not os.path.exists('.env'):
env_content = '\nLOGGING_VERBOSITY=REG0D\n\nSERVER_IP=34.57.139.144\nSERVER_PORT=80\n\nWS_IP=\nWS_PORT=\n'
with open('.env', 'w') as env_file:
env_file.write(env_content.strip())
init()
We see that there's also a constants module that is not standard. Let's try to decompile that too!
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: constants.py
# Bytecode version: 3.10.0rc2 (3439)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)
import logging
import os
from dotenv import load_dotenv
load_dotenv()
LOGGING_VERBOSITY = os.getenv('LOGGING_VERBOSITY', 'NONE')
LOGGING_MORE_VERBOSITY = os.getenv('LOGGING_MORE_VERBOSITY', 'NONE')
if LOGGING_VERBOSITY == 'REG0D' and LOGGING_MORE_VERBOSITY == 'y0uReOnToSomEThinG':
LOGGING_VERBOSITY = logging.DEBUG
elif LOGGING_VERBOSITY == 'REG0D':
LOGGING_VERBOSITY = logging.INFO
else:
LOGGING_VERBOSITY = logging.ERROR
HINT_1 = '[HINT] Have you tried REading the binaries? Where are you downloading them from?'
HINT_2 = '[HINT] Have you tried REading the binaries? Is your SSL Context malformed or what?'
HINT_3 = "[HINT] Have you tried REading the binaries? Bruh we can't make a connection..."
Ahh.. sneaky. We can see that in the init sequence, a .env file is automatically created with LOGGING_VERBOSITY=REG0D. However, analyzing constants.py, we realised that that will configure the logger to INFO only. If we set the LOGGING_VERBOSITY to any other values, it will set to ERROR only.
def init():
if not os.path.exists('.env'):
env_content = '\nLOGGING_VERBOSITY=REG0D\n\nSERVER_IP=34.57.139.144\nSERVER_PORT=80\n\nWS_IP=\nWS_PORT=\n'
with open('.env', 'w') as env_file:
env_file.write(env_content.strip())
init()
We will need to enable DEBUG level as many hints and errors are reported at DEBUG level based on the decompiled code. It's also sneaky that it will try to get ENV variables for LOGGING_VERBOSITY and LOGGING_MORE_VERBOSITY and set to NONE if no ENV variables exists. This means setting it in .env will not work!
Let's add the ENV variables!
export LOGGING_VERBOSITY=REG0D
export LOGGING_MORE_VERBOSITY=y0uReOnToSomEThinG
Now, let's run the client!
Note: I have already ran the dev_server executable when I'm writing this. Please run the executable below to get the same output as me.
root@4b3dc7eb6580:/files# export LOGGING_VERBOSITY=REG0D
export LOGGING_MORE_VERBOSITY=y0uReOnToSomEThinG
root@4b3dc7eb6580:/files# ./client
2025-01-27 17:59:55,906 - INFO - DEV_client.crt found.
2025-01-27 17:59:55,907 - INFO - DEV_client.key found.
2025-01-27 17:59:55,907 - INFO - DEV_ca.crt found.
2025-01-27 17:59:55,927 - DEBUG - Using selector: EpollSelector
2025-01-27 17:59:55,927 - DEBUG - Using selector: EpollSelector
2025-01-27 17:59:55,951 - DEBUG - = connection is CONNECTING
2025-01-27 17:59:55,952 - DEBUG - = connection is CONNECTING
2025-01-27 17:59:56,012 - DEBUG - > GET / HTTP/1.1
2025-01-27 17:59:56,012 - DEBUG - > Host: 0.0.0.0:8000
2025-01-27 17:59:56,012 - DEBUG - > Upgrade: websocket
2025-01-27 17:59:56,012 - DEBUG - > Connection: Upgrade
2025-01-27 17:59:56,012 - DEBUG - > Sec-WebSocket-Key: WZfM4iInYIqmmdTuKKo01A==
2025-01-27 17:59:56,012 - DEBUG - > Sec-WebSocket-Version: 13
2025-01-27 17:59:56,012 - DEBUG - > Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
2025-01-27 17:59:56,012 - DEBUG - > X-ARBOC: jIkMgQPk-sender
2025-01-27 17:59:56,013 - DEBUG - > User-Agent: Python/3.10 websockets/14.1
2025-01-27 17:59:56,013 - DEBUG - > GET / HTTP/1.1
2025-01-27 17:59:56,013 - DEBUG - > Host: 0.0.0.0:8000
2025-01-27 17:59:56,013 - DEBUG - > Upgrade: websocket
2025-01-27 17:59:56,014 - DEBUG - > Connection: Upgrade
2025-01-27 17:59:56,014 - DEBUG - > Sec-WebSocket-Key: 9rd5iIBBz9Hr1mGfEOxj2A==
2025-01-27 17:59:56,014 - DEBUG - > Sec-WebSocket-Version: 13
2025-01-27 17:59:56,014 - DEBUG - > Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
2025-01-27 17:59:56,014 - DEBUG - > X-ARBOC: jIkMgQPk-listener
2025-01-27 17:59:56,014 - DEBUG - > User-Agent: Python/3.10 websockets/14.1
2025-01-27 17:59:56,018 - DEBUG - < HTTP/1.1 101 Switching Protocols
2025-01-27 17:59:56,018 - DEBUG - < Date: Mon, 27 Jan 2025 17:59:56 GMT
2025-01-27 17:59:56,018 - DEBUG - < Upgrade: websocket
2025-01-27 17:59:56,018 - DEBUG - < Connection: Upgrade
2025-01-27 17:59:56,018 - DEBUG - < Sec-WebSocket-Accept: zCaEoA1aGh0BW8glRw0+cLy48+w=
2025-01-27 17:59:56,018 - DEBUG - < Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=12; client_max_window_bits=12
2025-01-27 17:59:56,018 - DEBUG - < Server: Python/3.10 websockets/14.1
2025-01-27 17:59:56,019 - DEBUG - = connection is OPEN
2025-01-27 17:59:56,019 - DEBUG - < TEXT 'Welcome to the chat!' [20 bytes]
2025-01-27 17:59:56,020 - DEBUG - < HTTP/1.1 101 Switching Protocols
2025-01-27 17:59:56,021 - DEBUG - < Date: Mon, 27 Jan 2025 17:59:56 GMT
2025-01-27 17:59:56,021 - DEBUG - < Upgrade: websocket
2025-01-27 17:59:56,021 - DEBUG - < Connection: Upgrade
2025-01-27 17:59:56,021 - DEBUG - < Sec-WebSocket-Accept: uOuFSadFGyGUkr11vK01ly0scFU=
2025-01-27 17:59:56,021 - DEBUG - < Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=12; client_max_window_bits=12
2025-01-27 17:59:56,021 - DEBUG - < Server: Python/3.10 websockets/14.1
2025-01-27 17:59:56,021 - DEBUG - = connection is OPEN
2025-01-27 17:59:56,021 - DEBUG - < TEXT 'Welcome to the chat!' [20 bytes]
>> Welcome to the chat!
Inspection of Dev_Server
Let's inspect the files of dev_server executable:
➜ dev_server_extracted git:(master) ✗ ll
total 29240
drwxr-xr-x@ 8 alex staff 256B Jan 24 01:39 Crypto
-rw-r--r--@ 1 alex staff 1.6M Jan 24 01:38 PYZ-00.pyz
drwxr-xr-x@ 2 alex staff 64B Jan 24 01:38 PYZ-00.pyz_extracted
-rw-r--r--@ 1 alex staff 860K Jan 24 01:38 base_library.zip
-rw-r--r--@ 1 alex staff 3.5K Jan 24 01:38 dev_server.pyc
drwxr-xr-x@ 26 alex staff 832B Jan 24 01:39 lib-dynload
-rw-r--r--@ 1 alex staff 73K Jan 24 01:38 libbz2.so.1.0
-rw-r--r--@ 1 alex staff 4.2M Jan 24 01:38 libcrypto.so.3
-rw-r--r--@ 1 alex staff 190K Jan 24 01:38 libexpat.so.1
-rw-r--r--@ 1 alex staff 47K Jan 24 01:38 libffi.so.8
-rw-r--r--@ 1 alex staff 166K Jan 24 01:38 liblzma.so.5
-rw-r--r--@ 1 alex staff 183K Jan 24 01:38 libmpdec.so.3
-rw-r--r--@ 1 alex staff 5.6M Jan 24 01:38 libpython3.10.so.1.0
-rw-r--r--@ 1 alex staff 328K Jan 24 01:38 libreadline.so.8
-rw-r--r--@ 1 alex staff 652K Jan 24 01:38 libssl.so.3
-rw-r--r--@ 1 alex staff 195K Jan 24 01:38 libtinfo.so.6
-rw-r--r--@ 1 alex staff 30K Jan 24 01:38 libuuid.so.1
-rw-r--r--@ 1 alex staff 106K Jan 24 01:38 libz.so.1
-rw-r--r--@ 1 alex staff 909B Jan 24 01:38 pyi_rth_inspect.pyc
-rw-r--r--@ 1 alex staff 1.1K Jan 24 01:38 pyi_rth_multiprocessing.pyc
-rw-r--r--@ 1 alex staff 964B Jan 24 01:38 pyi_rth_pkgutil.pyc
-rw-r--r--@ 1 alex staff 873B Jan 24 01:38 pyiboot01_bootstrap.pyc
-rw-r--r--@ 1 alex staff 3.0K Jan 24 01:38 pyimod01_archive.pyc
-rw-r--r--@ 1 alex staff 21K Jan 24 01:38 pyimod02_importers.pyc
-rw-r--r--@ 1 alex staff 3.6K Jan 24 01:38 pyimod03_ctypes.pyc
-rw-r--r--@ 1 alex staff 287B Jan 24 01:38 struct.pyc
drwxr-xr-x@ 3 alex staff 96B Jan 24 01:39 websockets
drwxr-xr-x@ 9 alex staff 288B Jan 24 01:39 websockets-14.1.dist-info
Let's decompile the bytecode for dev_server.pyc.
➜ dev_server_extracted git:(master) ✗ pycdc dev_server.pyc > dev_server.py
Unsupported use of GET_AITER outside of SETUP_LOOP
Unsupported opcode: END_ASYNC_FOR (98)
Unsupported opcode: JUMP_IF_NOT_EXC_MATCH (210)
Unsupported Node type: 28
Let's look at the content for dev_server.py
# Source Generated with Decompyle++
# File: dev_server.pyc (Python 3.10)
from dotenv import load_dotenv
import asyncio
import websockets
import logging
import os
from common import validate, generate_server_context, decrypt_3des
from constants import LOGGING_VERBOSITY
load_dotenv()
logging.basicConfig(LOGGING_VERBOSITY, '%(asctime)s - %(levelname)s - %(message)s', **('level', 'format'))
SERVER_IP = os.getenv('SERVER_IP', '127.127.127.127')
SERVER_PORT = os.getenv('SERVER_PORT', '9999')
WS_IP = os.getenv('WS_IP', '0.0.0.0')
WS_PORT = os.getenv('WS_PORT', '8000')
CA_CERT = 'DEV_ca.crt'
SERVER_CERT = 'DEV_server.crt'
SERVER_KEY = 'DEV_server.key'
SERVER_SERVER = f'''http://{SERVER_IP}:{SERVER_PORT}/REVW/'''
SERVER_PATH = ''
CERT_URL = f'''{SERVER_SERVER}{SERVER_CERT}'''
CERT_PATH = f'''{SERVER_PATH}{SERVER_CERT}'''
KEY_URL = f'''{SERVER_SERVER}{SERVER_KEY}'''
KEY_PATH = f'''{SERVER_PATH}{SERVER_KEY}'''
CA_URL = f'''{SERVER_SERVER}{CA_CERT}'''
CA_PATH = f'''{SERVER_PATH}{CA_CERT}'''
validate(CERT_URL, CERT_PATH, logging)
validate(KEY_URL, KEY_PATH, logging)
validate(CA_URL, CA_PATH, logging)
context = generate_server_context(CERT_PATH, KEY_PATH, CA_PATH, logging)
clients = { }
FLAG = '7eb66acfb3652e80ef006143b4e5b6565b84b51355b26e39d2979a3bc873ba394ecae0061bd9522a9639ac4488733ad97d5e5acfb1e3e6f7'
def header_extraction(websocket):
headers = websocket.request.headers['X-ARBOC']
parts = headers.split('-', 1)
clientId = parts[0]
clientType = parts[1]
return [
clientId,
clientType]
async def handler(websocket):
parts = header_extraction(websocket)
clientId = parts[0]
clientType = parts[1]
if clientId not in clients:
clients[clientId] = {
clientType: websocket }
else:
clients[clientId][clientType] = websocket
# WARNING: Decompyle incomplete
async def broadcast(message, websocket):
to_remove = []
senderId = header_extraction(websocket)[0]
# WARNING: Decompyle incomplete
async def main():
if context != None:
await websockets.serve(handler, WS_IP, WS_PORT, context, 3600, 3600, 600, **('ssl', 'ping_interval', 'ping_timeout', 'close_timeout'))
server = <NODE:28>
logging.info('Server started...')
await server.wait_closed()
return None
if __name__ == '__main__':
asyncio.run(main())
return None
We can see that the async functions handler() and broadcast() fails to decompile.
Let's try with PyLingual again. I get the following:
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: dev_server.py
# Bytecode version: 3.10.0rc2 (3439)
# Source timestamp: 1970-01-01 00:00:00 UTC (0)
from dotenv import load_dotenv
import asyncio
import websockets
import logging
import os
from common import validate, generate_server_context, decrypt_3des
from constants import LOGGING_VERBOSITY
load_dotenv()
logging.basicConfig(level=LOGGING_VERBOSITY, format='%(asctime)s - %(levelname)s - %(message)s')
SERVER_IP = os.getenv('SERVER_IP', '127.127.127.127')
SERVER_PORT = os.getenv('SERVER_PORT', '9999')
WS_IP = os.getenv('WS_IP', '0.0.0.0')
WS_PORT = os.getenv('WS_PORT', '8000')
CA_CERT = 'DEV_ca.crt'
SERVER_CERT = 'DEV_server.crt'
SERVER_KEY = 'DEV_server.key'
SERVER_SERVER = f'http://{SERVER_IP}:{SERVER_PORT}/REVW/'
SERVER_PATH = ''
CERT_URL = f'{SERVER_SERVER}{SERVER_CERT}'
CERT_PATH = f'{SERVER_PATH}{SERVER_CERT}'
KEY_URL = f'{SERVER_SERVER}{SERVER_KEY}'
KEY_PATH = f'{SERVER_PATH}{SERVER_KEY}'
CA_URL = f'{SERVER_SERVER}{CA_CERT}'
CA_PATH = f'{SERVER_PATH}{CA_CERT}'
validate(CERT_URL, CERT_PATH, logging)
validate(KEY_URL, KEY_PATH, logging)
validate(CA_URL, CA_PATH, logging)
context = generate_server_context(CERT_PATH, KEY_PATH, CA_PATH, logging)
clients = {}
FLAG = '7eb66acfb3652e80ef006143b4e5b6565b84b51355b26e39d2979a3bc873ba394ecae0061bd9522a9639ac4488733ad97d5e5acfb1e3e6f7'
def header_extraction(websocket):
headers = websocket.request.headers['X-ARBOC']
parts = headers.split('-', 1)
clientId = parts[0]
clientType = parts[1]
return [clientId, clientType]
async def handler(websocket):
parts = header_extraction(websocket)
clientId = parts[0]
clientType = parts[1]
if clientId not in clients:
clients[clientId] = {clientType: websocket}
else:
clients[clientId][clientType] = websocket
try:
await websocket.send('Welcome to the chat!')
async for message in websocket:
logging.debug(f'Received message: {message}0')
await broadcast(message, websocket)
except websockets.exceptions.ConnectionClosed as e:
logging.error(f'Connection closed: {e}0')
async def broadcast(message, websocket):
to_remove = []
senderId = header_extraction(websocket)[0]
for client in clients:
try:
if '5n@k3' not in message:
if client == senderId:
await clients[client]['listener'].send(f'r00t: {message}0')
else:
await clients[client]['listener'].send(f'{senderId}: {message}0')
elif len(message) <= 5:
if client == senderId:
await clients[client]['listener'].send('s()p3rR00+: run --help')
else:
parts = message.split('#')
if len(parts) > 2:
command = parts[1]
args1 = parts[2]
if command == '90' and len(args1) > 5:
os.system(f'ls {args1[:5]}0')
if command == '01':
os.system('ip addr')
if command == '33':
os.system('hostname')
if command == '27':
os.system('cat /proc/cpuinfo')
if command == '18':
os.system('whoami ')
if command == '88':
try:
decrypted_message = decrypt_3des(bytes.fromhex(FLAG), bytes.fromhex(args1))
if client == senderId:
await clients[client]['listener'].send(f's()p3rR00+: {decrypted_message}0')
except Exception as E:
logging.debug(E)
if client == senderId:
await clients[client]['listener'].send('s()p3rR00+: flag{this_is_real_definitely_not_fake}')
if command == '79':
os.system('dmesg | tail -n 10')
if command == '61':
os.system('ps aux')
if command == '49':
os.system('uptime -p')
if command == '55':
os.system('lscpu')
if command == '72':
os.system(f'{args1[:2]}')
except websockets.exceptions.ConnectionClosed:
to_remove.append(client)
for client in to_remove:
del clients[client]
async def main():
if context != None:
server = await websockets.serve(handler, WS_IP, WS_PORT, ssl=context, ping_interval=3600, ping_timeout=3600, close_timeout=600)
logging.info('Server started...')
await server.wait_closed()
if __name__ == '__main__':
asyncio.run(main())
Since we already have commons.pyc and constants.pyc decompiled earlier, we will set the same ENV variables and start our executable!
root@4b3dc7eb6580:/files# export LOGGING_VERBOSITY=REG0D
root@4b3dc7eb6580:/files# export LOGGING_MORE_VERBOSITY=y0uReOnToSomEThinG
root@4b3dc7eb6580:/files# echo $LOGGING_MORE_VERBOSITY
y0uReOnToSomEThinG
root@4b3dc7eb6580:/files# ./dev_server
2025-01-27 17:57:05,826 - INFO - DEV_server.crt found.
2025-01-27 17:57:05,827 - INFO - DEV_server.key found.
2025-01-27 17:57:05,827 - INFO - DEV_ca.crt found.
2025-01-27 17:57:05,845 - DEBUG - Using selector: EpollSelector
2025-01-27 17:57:05,868 - INFO - server listening on 0.0.0.0:8000
2025-01-27 17:57:05,868 - INFO - Server started...
Let's inspect the methods in server.py. We know that if the client send a text that starts with "5n@k3", it will trigger the ELSE in the if-else statement. The messages are delimited by "#" and there are 2 parts. I think the part we are interested about is command 88 where it will trigger a method called decrypt_3des, which I assume it's Triple DES. We can see that 2 arguments (bytes.fromhex(FLAG), bytes.fromhex(args1)) were sent to the method, so let's go back to our decompiled common.py to understand the method:
def decrypt_3des(ciphertext: bytes, key: bytes):
iv = ciphertext[:DES3.block_size]
cipher = DES3.new(key, DES3.MODE_CBC, iv=iv)
decrypted_data = unpad(cipher.decrypt(ciphertext[DES3.block_size:]), DES3.block_size)
return decrypted_data.decode()
With this, we know that the ciphertext is the flag, the key is our args1 which we will send via the websocket client.
5n@k3#88#<key>
It's using PyCryptodome's DES3 implementation at https://pycryptodome.readthedocs.io/en/latest/src/cipher/des3.html. The Initialization Vector (IV) is taking the ciphertext which is the flag, slice it and take only 8 characters which is Triple DES's block size of 8 bytes (DES3.block_size=8).
IV = 7eb66acf
Initialization Vector (IV)
Let's try to obtain the key from investigating further!
Check connection to 34.123.42.200:80
Check connection to Given IP (34.123.42.200:80)
Since we know mTLS is used, let's try to get the server certificate with openssl.
echo | \
openssl s_client -servername 34.123.42.200 -connect 34.123.42.200:80 2>/dev/null | \
openssl x509 -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
64:6b:ba:54:a7:2c:45:e5:b2:f3:c4:ba:dc:a4:7b:9b:6d:b6:5e:d4
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=JP, ST=Okohamaya Prefecture, L=Mount GXFC, O=Some Legit Company Name Pty Ltd, OU=Snake, Cobras, Serpents , CN=www.venom.com, [email protected]
Validity
Not Before: Jan 20 14:11:49 2025 GMT
Not After : Feb 19 14:11:49 2025 GMT
Subject: CN=34.123.42.200
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:ab:fc:d5:7a:7b:fa:2f:ac:a3:e5:e0:00:6c:96:
99:44:ef:ed:1f:03:ee:4f:ae:d3:0f:d4:d6:0f:ef:
3d:15:40:ff:8f:49:43:20:47:af:84:eb:bc:7c:96:
48:ce:22:60:90:fd:31:43:eb:c7:eb:4d:e9:57:d8:
69:e5:15:70:d6:a5:15:a0:74:8b:5a:a5:ae:b6:e9:
4f:43:fb:2a:b6:45:a7:aa:49:e9:68:18:58:fe:c8:
e4:7c:fc:f7:d5:15:b8:7e:5d:7a:54:84:bb:8c:a7:
56:d1:15:28:30:03:85:f4:42:a8:98:35:31:1b:ca:
27:50:6f:d7:de:4d:62:15:dc:ac:d9:75:dc:39:4a:
38:7a:3c:b6:d6:73:42:ed:c8:39:3e:b7:46:e8:38:
92:f8:3b:43:90:5d:c3:d9:7c:66:e9:ab:60:bc:1b:
c9:1d:f3:e7:a7:30:38:b4:cc:0e:ab:41:0e:a8:2c:
77:22:21:4b:a6:4b:a6:9e:4d:6e:be:ca:7d:5b:b1:
b0:c9:88:b8:bb:42:9d:1c:a3:5c:a6:bb:d3:42:11:
65:0b:88:be:bb:23:f5:d4:cc:76:a7:bc:9e:5f:0d:
bc:b7:17:93:75:7a:7a:37:1f:7e:ec:57:61:f7:ce:
15:be:3e:74:83:cd:b0:ac:6c:25:57:7d:22:99:bd:
67:3f
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Subject Alternative Name:
IP Address:34.123.42.200, IP Address:0.0.0.0
X509v3 Extended Key Usage:
TLS Web Server Authentication
1.2.12.5:
...PBFEJJDMPMFJNMHJJADFPBFEICCHPGFDNGHDJADFPJFMIPCKOKEPMKGPIICNPBFEIFCAOAEFJDDGLDBGPCFHIACFOFEAMFGAJCDHOAEFIPCKOBEEIGCDKGADPCFHJKDPPP
1.2.12.6:
...FKNPHKJMDJPDFGIBCEPDFGJGDDPFFAIBCEKBAEODEGJKDPOOELILCOPIFNNIHNJLDOPEFBJJDMPLFOJCDHPMFJJJDMPNFINNHIIJCMOGEDIBCEOEEBJADFPIFNJNDIOPEK
1.2.12.7:
...MPGKIGCDPFFANFHAJEDBPHFCIDCGPGFDJHDCPLFOJHDCOOELMOGLJKDPPCFHJHDCLHBCPCFHICCHONEIIOCLOGEDMGGDJDDGPNFIJEDBOMEJMMGJJIDNPBFEJMDJPJFMIK
1.2.12.8:
...CPPOFLJPDKPCFHICCHKCAHOLEOIFCAKFAAPGFDJDDGPAFFJPDKPBFEJFDAOGEDMGGDIACFOPEKJNDILNBIPOFLJGDDPPFKJBDEPEFBIHCCOCEHMCGHIMCJOJEMJODLLOBL
1.2.12.9:
.$OHECICCHODEGJBDELBBEIDCGLDBGIBCELEBB
X509v3 Subject Key Identifier:
AC:51:47:8A:4E:5A:4E:A1:AA:58:23:A4:94:BC:5A:1C:ED:D4:70:C9
X509v3 Authority Key Identifier:
62:C5:A5:21:18:81:E1:93:B5:5C:35:F2:74:21:4C:D2:0F:15:FA:93
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
2f:3a:d9:7b:fd:55:7e:4f:ec:10:39:a4:df:41:4a:e7:04:77:
e0:59:fc:8e:37:bd:7e:08:ee:2a:b1:06:32:16:e6:cd:44:fc:
02:29:9f:1f:5b:68:44:92:1e:8f:22:8a:b9:84:cb:1e:1e:27:
f9:a1:a2:69:85:15:f1:48:3b:f3:f3:81:4b:09:5f:67:fe:63:
fd:da:34:05:02:35:29:3d:7e:43:1b:d0:8c:f1:87:1a:7b:03:
f1:dc:5d:5e:35:bd:3b:66:4f:14:08:66:aa:e5:6f:5c:f3:e1:
5a:d7:c7:30:10:07:12:21:fc:d4:d8:80:b8:d5:2c:1e:b9:2d:
1c:c3:82:cc:0e:ec:ad:69:9f:67:3a:92:c2:35:1d:2c:e5:ea:
61:99:44:82:bf:20:e8:9b:f9:d9:51:49:fe:da:0a:d3:51:10:
d3:bb:2f:10:ad:f6:47:1d:40:4e:60:af:ff:6d:c8:3a:b3:21:
c8:26:e8:ec:29:06:53:6f:66:d1:31:20:26:88:c5:61:9f:36:
79:b3:d4:46:64:63:3a:18:01:ae:47:cb:13:e5:c5:e2:7c:ff:
b9:41:6d:d4:89:84:a9:1c:c7:53:62:1e:f5:79:a8:2d:3e:07:
2f:44:a8:7d:69:51:fb:94:10:2d:a4:70:22:61:ab:c1:35:eb:
fd:5c:7d:92
-----BEGIN CERTIFICATE-----
MIIGZzCCBU+gAwIBAgIUZGu6VKcsReWy88S63KR7m222XtQwDQYJKoZIhvcNAQEL
BQAwgccxCzAJBgNVBAYTAkpQMR0wGwYDVQQIDBRPa29oYW1heWEgUHJlZmVjdHVy
ZTETMBEGA1UEBwwKTW91bnQgR1hGQzEoMCYGA1UECgwfU29tZSBMZWdpdCBDb21w
YW55IE5hbWUgUHR5IEx0ZDEhMB8GA1UECwwYU25ha2UsIENvYnJhcywgU2VycGVu
dHMgMRYwFAYDVQQDDA13d3cudmVub20uY29tMR8wHQYJKoZIhvcNAQkBFhBvYmFu
YWlAdmVub20uY29tMB4XDTI1MDEyMDE0MTE0OVoXDTI1MDIxOTE0MTE0OVowGDEW
MBQGA1UEAwwNMzQuMTIzLjQyLjIwMDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
AQoCggEBAKv81Xp7+i+so+XgAGyWmUTv7R8D7k+u0w/U1g/vPRVA/49JQyBHr4Tr
vHyWSM4iYJD9MUPrx+tN6VfYaeUVcNalFaB0i1qlrrbpT0P7KrZFp6pJ6WgYWP7I
5Hz899UVuH5delSEu4ynVtEVKDADhfRCqJg1MRvKJ1Bv195NYhXcrNl13DlKOHo8
ttZzQu3IOT63Rug4kvg7Q5Bdw9l8ZumrYLwbyR3z56cwOLTMDqtBDqgsdyIhS6ZL
pp5Nbr7KfVuxsMmIuLtCnRyjXKa700IRZQuIvrsj9dTMdqe8nl8NvLcXk3V6ejcf
fuxXYffOFb4+dIPNsKxsJVd9Ipm9Zz8CAwEAAaOCAvcwggLzMAkGA1UdEwQCMAAw
CwYDVR0PBAQDAgXgMBUGA1UdEQQOMAyHBCJ7KsiHBAAAAAAwEwYDVR0lBAwwCgYI
KwYBBQUHAwEwgY0GAyoMBQSBhQyBglBCRkVKSkRNUE1GSk5NSEpKQURGUEJGRUlD
Q0hQR0ZETkdIREpBREZQSkZNSVBDS09LRVBNS0dQSUlDTlBCRkVJRkNBT0FFRkpE
REdMREJHUENGSElBQ0ZPRkVBTUZHQUpDREhPQUVGSVBDS09CRUVJR0NES0dBRFBD
RkhKS0RQUFAwgY0GAyoMBgSBhQyBgkZLTlBIS0pNREpQREZHSUJDRVBERkdKR0RE
UEZGQUlCQ0VLQkFFT0RFR0pLRFBPT0VMSUxDT1BJRk5OSUhOSkxET1BFRkJKSkRN
UExGT0pDREhQTUZKSkpETVBORklOTkhJSUpDTU9HRURJQkNFT0VFQkpBREZQSUZO
Sk5ESU9QRUswgY0GAyoMBwSBhQyBgk1QR0tJR0NEUEZGQU5GSEFKRURCUEhGQ0lE
Q0dQR0ZESkhEQ1BMRk9KSERDT09FTE1PR0xKS0RQUENGSEpIRENMSEJDUENGSElD
Q0hPTkVJSU9DTE9HRURNR0dESkRER1BORklKRURCT01FSk1NR0pKSUROUEJGRUpN
REpQSkZNSUswgY0GAyoMCASBhQyBgkNQUE9GTEpQREtQQ0ZISUNDSEtDQUhPTEVP
SUZDQUtGQUFQR0ZESkRER1BBRkZKUERLUEJGRUpGREFPR0VETUdHRElBQ0ZPUEVL
Sk5ESUxOQklQT0ZMSkdERFBQRktKQkRFUEVGQklIQ0NPQ0VITUNHSElNQ0pPSkVN
Sk9ETExPQkwwLQYDKgwJBCYMJE9IRUNJQ0NIT0RFR0pCREVMQkJFSURDR0xEQkdJ
QkNFTEVCQjAdBgNVHQ4EFgQUrFFHik5aTqGqWCOklLxaHO3UcMkwHwYDVR0jBBgw
FoAUYsWlIRiB4ZO1XDXydCFM0g8V+pMwDQYJKoZIhvcNAQELBQADggEBAC862Xv9
VX5P7BA5pN9BSucEd+BZ/I43vX4I7iqxBjIW5s1E/AIpnx9baESSHo8iirmEyx4e
J/mhommFFfFIO/PzgUsJX2f+Y/3aNAUCNSk9fkMb0Izxhxp7A/HcXV41vTtmTxQI
Zqrlb1zz4VrXxzAQBxIh/NTYgLjVLB65LRzDgswO7K1pn2c6ksI1HSzl6mGZRIK/
IOib+dlRSf7aCtNRENO7LxCt9kcdQE5gr/9tyDqzIcgm6OwpBlNvZtExICaIxWGf
Nnmz1EZkYzoYAa5HyxPlxeJ8/7lBbdSJhKkcx1NiHvV5qC0+By9EqH1pUfuUEC2k
cCJhq8E16/1cfZI=
-----END CERTIFICATE-----
Inspection of Packet Capture
Since there's a packet capture included in the ancient_runes.zip, we will use wireshark to inspect the file.
The decompilation of client and dev_server seems to indicate the presence of 3 certificates for the communication (DEV_server.crt, DEV_client.crt and DEV_ca.crt). This signify the use of mTLS. Let's try to extract the certificate from the packet capture.
Opening the file in Wireshark:

Let's try to filter the packets to see only TLS handshake with the following filter applied:
tls.handshake.type == 11

With the 4 packets, we can see the following:
- Line 11 and 13: Client Hello, Server Certificate, Client Certificate Request
- Line 15 and 16: Client sent Certificate, Client Key Exchange, Certificate Verify, etc
Looking at the mTLS authentication sequence:

We can see the same authentication sequence in our packet capture. Let's try to export the certificate now.

The second certificate length of 879 in Line 11 and Line 13 is the same for Line 15 and 16. We can assume that this is the CA certificate in the certificate chain.
Let's try to inspect the exported certificate with openssl:
openssl x509 -in DEV_ca.crt -inform der -text -noout
openssl x509 -in DEV_client.crt -inform der -text -noout
openssl x509 -in DEV_server.crt -inform der -text -noout
We get the following certificates:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
4d:dc:7d:da:90:4a:54:6f:c4:e7:4b:be:cf:31:be:6d:9b:0e:c7:90
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Validity
Not Before: Jan 20 14:06:22 2025 GMT
Not After : Feb 19 14:06:22 2025 GMT
Subject: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:db:e0:a6:6d:07:1b:f6:29:6c:83:3a:6f:a7:4b:
61:f0:cd:b2:44:e9:0e:2a:6f:36:4b:1f:5c:36:a3:
54:df:65:c5:16:e9:b1:bc:21:65:bd:d3:9f:a4:7a:
d1:4f:05:48:a2:33:2e:ed:bf:e3:33:81:67:1d:4b:
8e:c7:c6:d3:9c:27:0b:9e:eb:ce:84:06:4d:f6:4e:
2b:58:bd:aa:a7:7b:17:cd:cc:87:e3:8b:b5:8f:2a:
64:4b:68:46:13:da:c6:3b:a9:27:1a:83:6a:db:8d:
e8:2e:7d:57:50:5b:e3:73:0c:7d:aa:ba:27:df:2c:
47:97:f4:e1:5b:f8:05:77:7d:53:0e:32:d7:5e:b8:
00:fc:13:c5:69:6c:4c:f4:7f:91:02:20:50:59:9d:
df:3b:f1:e8:0c:00:eb:bd:2a:77:62:7c:42:b2:b9:
71:f7:5f:4d:c8:14:f8:6e:13:1d:f2:51:c2:74:ad:
d6:e4:fe:40:3a:b1:dd:fe:8e:1c:20:c7:df:58:1c:
89:d5:a1:19:5a:af:f7:aa:74:47:a6:a9:7d:a6:a1:
25:2e:24:0c:5b:eb:c6:38:15:76:47:80:11:30:ef:
b1:39:ff:fe:03:01:dd:f6:4e:20:ea:00:b7:bc:8c:
92:29:96:70:15:97:74:89:0d:cf:b6:0c:c1:32:d8:
b1:8d
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
3F:B6:7E:B3:C4:1D:0F:53:C8:FE:38:BC:AD:88:42:5F:B8:CF:22:B2
X509v3 Authority Key Identifier:
3F:B6:7E:B3:C4:1D:0F:53:C8:FE:38:BC:AD:88:42:5F:B8:CF:22:B2
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
30:1a:6f:06:1f:be:42:75:06:be:b3:ba:ea:30:cf:04:1f:4d:
75:1e:2d:53:3c:0d:bb:c3:fa:ad:b7:f1:99:df:f0:49:2f:0e:
4c:1d:5e:93:2b:8a:e7:f2:93:24:e4:30:9d:b2:92:87:b9:ff:
0d:1c:d1:a3:96:06:0e:d0:b7:8a:2e:12:fb:65:9f:08:b7:8a:
71:7e:da:16:bd:fd:44:b4:f9:e6:34:cb:ed:7b:53:f3:ab:6b:
56:8a:db:9e:dc:ff:53:e5:10:43:26:cc:80:5d:dc:ea:fd:af:
36:91:e8:1d:27:20:58:2a:18:86:58:e2:12:5e:21:43:59:46:
5f:fc:78:3c:d5:db:5e:b7:00:82:a5:fa:64:d1:2e:c5:d0:cb:
6f:5a:5c:d5:be:00:ab:04:14:09:9e:0a:4f:80:96:ba:c5:a5:
59:6e:bb:b4:13:66:ec:3a:fd:96:1b:cf:84:df:d6:16:21:56:
1a:aa:fa:cd:78:df:75:3f:bf:1f:5e:9e:d5:a3:78:a8:71:7f:
9b:af:f9:7e:a7:d2:c5:4f:37:6b:c6:8b:d9:e2:08:f5:76:1e:
db:09:c8:c9:5f:ab:b4:68:97:04:46:af:bd:d4:d2:dd:76:ba:
45:d3:1d:f1:90:e6:86:50:22:33:e7:7b:d0:7b:d7:29:99:eb:
c3:00:85:d5
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
47:01:c2:02:98:ee:65:f5:ac:52:be:ee:67:94:59:24:82:22:5d:6b
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Validity
Not Before: Jan 20 14:06:58 2025 GMT
Not After : Feb 19 14:06:58 2025 GMT
Subject: C=JP, ST=Osaka, L=Japan, O=Yamashita Bookoshita, OU=Yamashita Bookoshita, CN=theflag.isnot.here, emailAddress=do.not@bother
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:e1:8e:56:c1:81:31:92:3d:c6:95:a3:38:5d:be:
8e:c8:c9:21:22:f1:54:b4:a9:72:30:3c:f4:11:ab:
db:cd:91:b6:2d:ad:a3:90:35:1e:23:01:6a:76:76:
bb:fc:0c:94:ca:41:7f:ce:5d:3c:ef:16:52:9b:18:
8a:fb:eb:cd:1e:c7:d9:87:1b:3e:81:f4:1d:9d:7b:
71:34:12:a9:15:8b:56:d0:71:d9:a2:e0:c2:59:3a:
5d:22:0b:34:f6:b0:eb:08:6a:8a:51:1a:19:48:cb:
d7:7f:88:40:30:f1:fb:75:82:a8:88:50:06:0e:4f:
e0:b2:56:13:fd:ab:c2:f5:e0:e6:32:bf:82:97:15:
d9:e1:68:07:e0:a2:48:e7:c0:d2:9a:0d:6e:85:e3:
b6:8a:8a:36:dd:48:38:99:06:1c:bd:dd:8e:0a:80:
53:5d:6c:3e:97:0f:dd:b3:dc:8e:55:a0:df:09:e7:
9f:81:0a:4f:88:d6:c2:34:e4:05:af:8c:db:b8:2c:
ff:4b:71:e0:fa:67:3f:13:13:22:9d:fd:e4:84:b3:
8f:0e:df:5a:55:d4:16:a6:c1:de:5b:bf:b1:b5:7f:
f4:a4:af:2c:4f:ae:dc:e1:35:8a:c0:36:72:ff:47:
2c:43:bf:3e:66:5d:09:a1:c1:65:80:e1:71:13:e2:
7d:d1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication
X509v3 Subject Key Identifier:
6F:BE:DD:CC:DC:AC:42:EE:5A:AB:78:17:01:D5:3C:88:50:E1:BC:ED
X509v3 Authority Key Identifier:
3F:B6:7E:B3:C4:1D:0F:53:C8:FE:38:BC:AD:88:42:5F:B8:CF:22:B2
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
61:6e:73:eb:c2:17:3a:4a:65:f7:31:f6:2a:96:83:e2:18:05:
e9:84:03:d7:f9:30:0b:ce:4c:bb:ed:df:8f:5a:c2:e7:02:69:
ee:61:ac:3f:1b:82:df:b9:06:3b:73:5a:88:07:50:98:8a:ea:
55:47:87:6a:a5:8f:a9:3a:7c:06:ce:3c:62:b3:5b:79:f1:a5:
c7:d1:b1:55:37:04:c9:2e:35:d8:c5:da:28:a8:3c:b8:c6:2a:
35:72:91:57:c7:a0:38:b5:b5:5e:15:6b:ad:e0:32:45:36:41:
7b:e7:d1:1c:bb:d4:9c:4e:c1:6f:9b:21:8e:9a:3d:65:eb:44:
d8:1b:b8:8a:f6:53:a3:be:75:f5:4b:37:b6:02:11:0e:03:a2:
da:f2:98:77:0f:d1:59:c7:50:09:3f:f8:93:b9:34:f2:c7:38:
df:bb:53:05:55:25:3d:b5:a8:e0:53:73:5f:4b:65:6d:23:73:
ce:3d:97:bd:91:bd:08:80:d4:9c:ea:3e:28:a2:e9:20:26:70:
d4:f4:a0:63:f3:0d:7c:fa:84:21:7b:5a:b0:25:83:69:2d:c1:
cf:69:7f:0b:68:2b:ac:35:16:59:53:56:f6:06:48:4c:c1:af:
a4:91:c9:fe:c2:a1:e3:70:e5:24:35:61:f9:c8:a5:52:17:b0:
61:39:43:a2
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
31:7d:8a:ad:56:5b:ed:0c:b9:98:4c:9c:4d:f5:4b:d1:cc:c0:ba:d0
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Validity
Not Before: Jan 20 14:06:45 2025 GMT
Not After : Feb 19 14:06:45 2025 GMT
Subject: CN=0.0.0.0
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:c9:0c:01:e5:51:5c:4c:b5:b5:17:11:4d:d7:4b:
74:ad:ba:ed:9e:3c:3f:8e:4b:e2:3e:c7:40:b2:79:
13:e5:19:6f:a2:f2:e4:d0:9f:4b:f0:d3:e2:cb:01:
7e:da:50:72:e4:98:8e:cf:43:7d:40:0d:20:37:3c:
23:d9:21:78:a0:f5:5b:01:f7:53:60:84:2a:4e:ab:
1c:40:80:fe:31:cb:35:8a:63:78:2a:25:2f:f8:5c:
62:5c:81:38:6d:80:37:82:ce:67:b3:6c:b3:08:74:
c1:a9:2f:e5:d3:19:c4:88:06:4b:37:53:e5:a6:e9:
d8:a6:66:da:2a:01:28:51:f6:4c:b1:5b:79:cd:59:
4f:58:33:67:df:8a:e4:bc:4c:52:39:16:4e:90:6c:
71:ec:ee:67:d6:92:11:72:14:c0:cc:00:dc:21:a3:
bb:75:ab:6a:8a:3d:56:3d:29:96:53:9b:37:47:f6:
e3:53:5a:91:14:92:02:50:0e:82:bf:fd:ba:4b:6f:
f6:f3:13:20:b3:6f:b3:1c:eb:c5:92:1c:d7:f0:d6:
5c:a3:9a:c5:8a:ac:4a:c2:45:26:c2:ee:78:84:8c:
dd:a4:72:ad:1f:90:13:b7:3c:ba:04:43:cf:9d:10:
00:15:20:6c:3e:38:a8:b9:c9:2d:e9:f9:f4:b5:c6:
a4:e9
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Subject Alternative Name:
IP Address:0.0.0.0
X509v3 Extended Key Usage:
TLS Web Server Authentication
1.2.12.5:
...PBFEJJDMPMFJNMHJJADFPBFEICCHPGFDNGHDJADFPJFMIPCKOKEPMKGPIICNPBFEIFCAOAEFJDDGLDBGPCFHIACFOFEAMFGAJCDHOAEFIPCKOBEEIGCDKGADPCFHJKDPPP
1.2.12.6:
...FKNPHKJMDJPDFGIBCEPDFGJGDDPFFAIBCEKBAEODEGJKDPOOELILCOPIFNNIHNJLDOPEFBJJDMPLFOJCDHPMFJJJDMPNFINNHIIJCMOGEDIBCEOEEBJADFPIFNJNDIOPEK
1.2.12.7:
...MPGKIGCDPFFANFHAJEDBPHFCIDCGPGFDJHDCPLFOJHDCOOELMOGLJKDPPCFHJHDCLHBCPCFHICCHONEIIOCLOGEDMGGDJDDGPNFIJEDBOMEJMMGJJIDNPBFEJMDJPJFMIK
1.2.12.8:
...CPPOFLJPDKPCFHICCHKCAHOLEOIFCAKFAAPGFDJDDGPAFFJPDKPBFEJFDAOGEDMGGDIACFOPEKJNDILNBIPOFLJGDDPPFKJBDEPEFBIHCCOCEHMCGHIMCJOJEMJODLLOBL
1.2.12.9:
.$OHECICCHODEGJBDELBBEIDCGLDBGIBCELEBB
X509v3 Subject Key Identifier:
EF:33:C2:24:F8:12:16:EC:6F:1B:E4:BE:77:C0:E3:DB:72:3D:C8:09
X509v3 Authority Key Identifier:
3F:B6:7E:B3:C4:1D:0F:53:C8:FE:38:BC:AD:88:42:5F:B8:CF:22:B2
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
55:4d:bd:18:04:0d:02:05:f7:b1:4a:a1:96:40:e3:33:80:b5:
5c:1e:94:0f:3a:af:06:c4:8d:31:a8:7a:1d:6a:3d:ee:86:dc:
d6:4b:8a:b9:6f:fe:5b:05:85:b9:7d:c7:cc:2c:80:de:dd:40:
cf:b2:69:43:7d:b8:26:eb:3c:7c:97:57:a7:0c:3b:41:13:77:
26:c9:ff:01:fd:6f:b7:92:80:82:ba:03:fa:d5:48:0e:d2:ba:
51:d4:64:9c:16:49:94:98:18:9f:d0:b1:df:9a:36:19:4a:e9:
51:da:f5:ea:7f:78:04:2b:9a:09:29:29:58:7b:48:32:32:b1:
0e:d1:d5:9f:09:59:b0:98:5b:fc:a7:d5:aa:9e:ce:c7:8b:10:
a7:09:75:dc:18:8f:2e:57:d3:81:cd:34:be:82:16:8a:5b:e7:
e3:f6:55:b5:b0:a7:b9:de:55:2e:6e:ce:b6:a2:ce:6f:e5:5f:
5d:3a:5e:39:60:d8:e5:b2:b1:8b:c1:6c:c5:d9:7b:ea:99:6b:
1e:ad:32:23:8c:58:cf:25:d4:fa:1a:62:21:9e:c6:2f:fb:fe:
e3:97:92:c0:04:3e:da:32:db:0d:11:14:95:bf:2b:4f:bb:7f:
bc:65:b1:49:76:c8:fd:85:a6:5a:e7:39:a6:b1:31:41:1d:1d:
4c:07:2a:07
Now, since we have the DEV_client.key and DEV_server.key from before, let's try to decrypt the packet.
Add the DEV_server.key into wireshark under TLS.

Now let's try to inspect the packet. Apply the filter below:
tls
Now we are seeing something!

Let me summarize the transcript:
Welcome to the chat!
r00t: hi there
r00t: welcome
r00t: so you are here because nian is coming to kill us all?
r00t: i see
r00t: well i can help you with that
r00t: no worries
r00t: :)
r00t: i hope you have REad enough into the ancient runes
r00t: those are your keys to success
r00t: as for the key itself, it is the weakness of nian: 红 火 热闹
r00t: take the 汉语拼音 of those 4 characters, then separate them with a "_" and pad them with 4 pluses
r00t: oh?
r00t: missing some bytes?
r00t: well...
r00t: oh darn, nian has come
r00t: it has bit off both my legs
r00t: im dying...
r00t: i dont have time to tell you the last part of the key... but maybe if you look into the SSL certificates...
r00t: encoding???? i cant remember... it sounds like the acid found in mandarin oranges and other citrus fruits...
r00t: urgh... im sorry... im losing too much blood
r00t: we are counting on you...
With the above transcript, we can piece the first part of they key as:
红 = hong
火 = huo
热闹 = re_nao
padding = ++++
compiled_key = hong_huo_re_nao++++
The other hint given is it sound like the acid found in mandarin oranges and other citrus fruits, which should be Citric Acid.
Let's try to inspect the SSL certificates for more information.. The hint given was in the encoding..
I will take the key obtained from the client and server executable. I tried the wireshark method before trying the executables.
openssl x509 -in DEV_server.crt -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
31:7d:8a:ad:56:5b:ed:0c:b9:98:4c:9c:4d:f5:4b:d1:cc:c0:ba:d0
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
Validity
Not Before: Jan 20 14:06:45 2025 GMT
Not After : Feb 19 14:06:45 2025 GMT
Subject: CN=0.0.0.0
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:c9:0c:01:e5:51:5c:4c:b5:b5:17:11:4d:d7:4b:
74:ad:ba:ed:9e:3c:3f:8e:4b:e2:3e:c7:40:b2:79:
13:e5:19:6f:a2:f2:e4:d0:9f:4b:f0:d3:e2:cb:01:
7e:da:50:72:e4:98:8e:cf:43:7d:40:0d:20:37:3c:
23:d9:21:78:a0:f5:5b:01:f7:53:60:84:2a:4e:ab:
1c:40:80:fe:31:cb:35:8a:63:78:2a:25:2f:f8:5c:
62:5c:81:38:6d:80:37:82:ce:67:b3:6c:b3:08:74:
c1:a9:2f:e5:d3:19:c4:88:06:4b:37:53:e5:a6:e9:
d8:a6:66:da:2a:01:28:51:f6:4c:b1:5b:79:cd:59:
4f:58:33:67:df:8a:e4:bc:4c:52:39:16:4e:90:6c:
71:ec:ee:67:d6:92:11:72:14:c0:cc:00:dc:21:a3:
bb:75:ab:6a:8a:3d:56:3d:29:96:53:9b:37:47:f6:
e3:53:5a:91:14:92:02:50:0e:82:bf:fd:ba:4b:6f:
f6:f3:13:20:b3:6f:b3:1c:eb:c5:92:1c:d7:f0:d6:
5c:a3:9a:c5:8a:ac:4a:c2:45:26:c2:ee:78:84:8c:
dd:a4:72:ad:1f:90:13:b7:3c:ba:04:43:cf:9d:10:
00:15:20:6c:3e:38:a8:b9:c9:2d:e9:f9:f4:b5:c6:
a4:e9
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment
X509v3 Subject Alternative Name:
IP Address:0.0.0.0
X509v3 Extended Key Usage:
TLS Web Server Authentication
1.2.12.5:
...PBFEJJDMPMFJNMHJJADFPBFEICCHPGFDNGHDJADFPJFMIPCKOKEPMKGPIICNPBFEIFCAOAEFJDDGLDBGPCFHIACFOFEAMFGAJCDHOAEFIPCKOBEEIGCDKGADPCFHJKDPPP
1.2.12.6:
...FKNPHKJMDJPDFGIBCEPDFGJGDDPFFAIBCEKBAEODEGJKDPOOELILCOPIFNNIHNJLDOPEFBJJDMPLFOJCDHPMFJJJDMPNFINNHIIJCMOGEDIBCEOEEBJADFPIFNJNDIOPEK
1.2.12.7:
...MPGKIGCDPFFANFHAJEDBPHFCIDCGPGFDJHDCPLFOJHDCOOELMOGLJKDPPCFHJHDCLHBCPCFHICCHONEIIOCLOGEDMGGDJDDGPNFIJEDBOMEJMMGJJIDNPBFEJMDJPJFMIK
1.2.12.8:
...CPPOFLJPDKPCFHICCHKCAHOLEOIFCAKFAAPGFDJDDGPAFFJPDKPBFEJFDAOGEDMGGDIACFOPEKJNDILNBIPOFLJGDDPPFKJBDEPEFBIHCCOCEHMCGHIMCJOJEMJODLLOBL
1.2.12.9:
.$OHECICCHODEGJBDELBBEIDCGLDBGIBCELEBB
X509v3 Subject Key Identifier:
EF:33:C2:24:F8:12:16:EC:6F:1B:E4:BE:77:C0:E3:DB:72:3D:C8:09
X509v3 Authority Key Identifier:
3F:B6:7E:B3:C4:1D:0F:53:C8:FE:38:BC:AD:88:42:5F:B8:CF:22:B2
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
55:4d:bd:18:04:0d:02:05:f7:b1:4a:a1:96:40:e3:33:80:b5:
5c:1e:94:0f:3a:af:06:c4:8d:31:a8:7a:1d:6a:3d:ee:86:dc:
d6:4b:8a:b9:6f:fe:5b:05:85:b9:7d:c7:cc:2c:80:de:dd:40:
cf:b2:69:43:7d:b8:26:eb:3c:7c:97:57:a7:0c:3b:41:13:77:
26:c9:ff:01:fd:6f:b7:92:80:82:ba:03:fa:d5:48:0e:d2:ba:
51:d4:64:9c:16:49:94:98:18:9f:d0:b1:df:9a:36:19:4a:e9:
51:da:f5:ea:7f:78:04:2b:9a:09:29:29:58:7b:48:32:32:b1:
0e:d1:d5:9f:09:59:b0:98:5b:fc:a7:d5:aa:9e:ce:c7:8b:10:
a7:09:75:dc:18:8f:2e:57:d3:81:cd:34:be:82:16:8a:5b:e7:
e3:f6:55:b5:b0:a7:b9:de:55:2e:6e:ce:b6:a2:ce:6f:e5:5f:
5d:3a:5e:39:60:d8:e5:b2:b1:8b:c1:6c:c5:d9:7b:ea:99:6b:
1e:ad:32:23:8c:58:cf:25:d4:fa:1a:62:21:9e:c6:2f:fb:fe:
e3:97:92:c0:04:3e:da:32:db:0d:11:14:95:bf:2b:4f:bb:7f:
bc:65:b1:49:76:c8:fd:85:a6:5a:e7:39:a6:b1:31:41:1d:1d:
4c:07:2a:07
Lets try to use asn1parse to extract the custom fields (1.2.12.x) in the certificate:
openssl asn1parse -in DEV_server.crt
The output is as follows:
0:d=0 hl=4 l=1496 cons: SEQUENCE
4:d=1 hl=4 l=1216 cons: SEQUENCE
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :02
13:d=2 hl=2 l= 20 prim: INTEGER :317D8AAD565BED0CB9984C9C4DF54BD1CCC0BAD0
35:d=2 hl=2 l= 13 cons: SEQUENCE
37:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
48:d=3 hl=2 l= 0 prim: NULL
50:d=2 hl=2 l= 69 cons: SEQUENCE
52:d=3 hl=2 l= 11 cons: SET
54:d=4 hl=2 l= 9 cons: SEQUENCE
56:d=5 hl=2 l= 3 prim: OBJECT :countryName
61:d=5 hl=2 l= 2 prim: PRINTABLESTRING :AU
65:d=3 hl=2 l= 19 cons: SET
67:d=4 hl=2 l= 17 cons: SEQUENCE
69:d=5 hl=2 l= 3 prim: OBJECT :stateOrProvinceName
74:d=5 hl=2 l= 10 prim: UTF8STRING :Some-State
86:d=3 hl=2 l= 33 cons: SET
88:d=4 hl=2 l= 31 cons: SEQUENCE
90:d=5 hl=2 l= 3 prim: OBJECT :organizationName
95:d=5 hl=2 l= 24 prim: UTF8STRING :Internet Widgits Pty Ltd
121:d=2 hl=2 l= 30 cons: SEQUENCE
123:d=3 hl=2 l= 13 prim: UTCTIME :250120140645Z
138:d=3 hl=2 l= 13 prim: UTCTIME :250219140645Z
153:d=2 hl=2 l= 18 cons: SEQUENCE
155:d=3 hl=2 l= 16 cons: SET
157:d=4 hl=2 l= 14 cons: SEQUENCE
159:d=5 hl=2 l= 3 prim: OBJECT :commonName
164:d=5 hl=2 l= 7 prim: UTF8STRING :0.0.0.0
173:d=2 hl=4 l= 290 cons: SEQUENCE
177:d=3 hl=2 l= 13 cons: SEQUENCE
179:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption
190:d=4 hl=2 l= 0 prim: NULL
192:d=3 hl=4 l= 271 prim: BIT STRING
467:d=2 hl=4 l= 753 cons: cont [ 3 ]
471:d=3 hl=4 l= 749 cons: SEQUENCE
475:d=4 hl=2 l= 9 cons: SEQUENCE
477:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Basic Constraints
482:d=5 hl=2 l= 2 prim: OCTET STRING [HEX DUMP]:3000
486:d=4 hl=2 l= 11 cons: SEQUENCE
488:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Key Usage
493:d=5 hl=2 l= 4 prim: OCTET STRING [HEX DUMP]:030205E0
499:d=4 hl=2 l= 15 cons: SEQUENCE
501:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Subject Alternative Name
506:d=5 hl=2 l= 8 prim: OCTET STRING [HEX DUMP]:3006870400000000
516:d=4 hl=2 l= 19 cons: SEQUENCE
518:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Extended Key Usage
523:d=5 hl=2 l= 12 prim: OCTET STRING [HEX DUMP]:300A06082B06010505070301
537:d=4 hl=3 l= 141 cons: SEQUENCE
540:d=5 hl=2 l= 3 prim: OBJECT :1.2.12.5
545:d=5 hl=3 l= 133 prim: OCTET STRING [HEX DUMP]:0C8182504246454A4A444D504D464A4E4D484A4A4144465042464549434348504746444E4748444A414446504A464D4950434B4F4B45504D4B47504949434E50424645494643414F4145464A4444474C44424750434648494143464F4645414D4647414A4344484F4145464950434B4F424545494743444B474144504346484A4B44505050
681:d=4 hl=3 l= 141 cons: SEQUENCE
684:d=5 hl=2 l= 3 prim: OBJECT :1.2.12.6
689:d=5 hl=3 l= 133 prim: OCTET STRING [HEX DUMP]:0C8182464B4E50484B4A4D444A5044464749424345504446474A47444450464641494243454B4241454F4445474A4B44504F4F454C494C434F5049464E4E49484E4A4C444F504546424A4A444D504C464F4A434448504D464A4A4A444D504E46494E4E4849494A434D4F474544494243454F4545424A4144465049464E4A4E44494F50454B
825:d=4 hl=3 l= 141 cons: SEQUENCE
828:d=5 hl=2 l= 3 prim: OBJECT :1.2.12.7
833:d=5 hl=3 l= 133 prim: OCTET STRING [HEX DUMP]:0C81824D50474B49474344504646414E4648414A4544425048464349444347504746444A484443504C464F4A4844434F4F454C4D4F474C4A4B4450504346484A4844434C48424350434648494343484F4E4549494F434C4F4745444D4747444A444447504E46494A4544424F4D454A4D4D474A4A49444E504246454A4D444A504A464D494B
969:d=4 hl=3 l= 141 cons: SEQUENCE
972:d=5 hl=2 l= 3 prim: OBJECT :1.2.12.8
977:d=5 hl=3 l= 133 prim: OCTET STRING [HEX DUMP]:0C81824350504F464C4A50444B50434648494343484B4341484F4C454F494643414B464141504746444A444447504146464A50444B504246454A4644414F4745444D474744494143464F50454B4A4E44494C4E4249504F464C4A4744445050464B4A42444550454642494843434F4345484D434748494D434A4F4A454D4A4F444C4C4F424C
1113:d=4 hl=2 l= 45 cons: SEQUENCE
1115:d=5 hl=2 l= 3 prim: OBJECT :1.2.12.9
1120:d=5 hl=2 l= 38 prim: OCTET STRING [HEX DUMP]:0C244F484543494343484F4445474A4244454C424245494443474C444247494243454C454242
1160:d=4 hl=2 l= 29 cons: SEQUENCE
1162:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Subject Key Identifier
1167:d=5 hl=2 l= 22 prim: OCTET STRING [HEX DUMP]:0414EF33C224F81216EC6F1BE4BE77C0E3DB723DC809
1191:d=4 hl=2 l= 31 cons: SEQUENCE
1193:d=5 hl=2 l= 3 prim: OBJECT :X509v3 Authority Key Identifier
1198:d=5 hl=2 l= 24 prim: OCTET STRING [HEX DUMP]:301680143FB67EB3C41D0F53C8FE38BCAD88425FB8CF22B2
1224:d=1 hl=2 l= 13 cons: SEQUENCE
1226:d=2 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
1237:d=2 hl=2 l= 0 prim: NULL
1239:d=1 hl=4 l= 257 prim: BIT STRING
Let's try to extract the string:
openssl asn1parse -in DEV_server.crt -strparse 545
openssl asn1parse -in DEV_server.crt -strparse 689
openssl asn1parse -in DEV_server.crt -strparse 833
openssl asn1parse -in DEV_server.crt -strparse 977
openssl asn1parse -in DEV_server.crt -strparse 1120
These are the strings obtained:
0:d=0 hl=3 l= 130 prim: UTF8STRING :PBFEJJDMPMFJNMHJJADFPBFEICCHPGFDNGHDJADFPJFMIPCKOKEPMKGPIICNPBFEIFCAOAEFJDDGLDBGPCFHIACFOFEAMFGAJCDHOAEFIPCKOBEEIGCDKGADPCFHJKDPPP
0:d=0 hl=3 l= 130 prim: UTF8STRING :FKNPHKJMDJPDFGIBCEPDFGJGDDPFFAIBCEKBAEODEGJKDPOOELILCOPIFNNIHNJLDOPEFBJJDMPLFOJCDHPMFJJJDMPNFINNHIIJCMOGEDIBCEOEEBJADFPIFNJNDIOPEK
0:d=0 hl=3 l= 130 prim: UTF8STRING :MPGKIGCDPFFANFHAJEDBPHFCIDCGPGFDJHDCPLFOJHDCOOELMOGLJKDPPCFHJHDCLHBCPCFHICCHONEIIOCLOGEDMGGDJDDGPNFIJEDBOMEJMMGJJIDNPBFEJMDJPJFMIK
0:d=0 hl=3 l= 130 prim: UTF8STRING :CPPOFLJPDKPCFHICCHKCAHOLEOIFCAKFAAPGFDJDDGPAFFJPDKPBFEJFDAOGEDMGGDIACFOPEKJNDILNBIPOFLJGDDPPFKJBDEPEFBIHCCOCEHMCGHIMCJOJEMJODLLOBL
0:d=0 hl=2 l= 36 prim: UTF8STRING :OHECICCHODEGJBDELBBEIDCGLDBGIBCELEBB
Combining all the custom OIDs, i get the following UTF8STRING:
PBFEJJDMPMFJNMHJJADFPBFEICCHPGFDNGHDJADFPJFMIPCKOKEPMKGPIICNPBFEIFCAOAEFJDDGLDBGPCFHIACFOFEAMFGAJCDHOAEFIPCKOBEEIGCDKGADPCFHJKDPPPFKNPHKJMDJPDFGIBCEPDFGJGDDPFFAIBCEKBAEODEGJKDPOOELILCOPIFNNIHNJLDOPEFBJJDMPLFOJCDHPMFJJJDMPNFINNHIIJCMOGEDIBCEOEEBJADFPIFNJNDIOPEKMPGKIGCDPFFANFHAJEDBPHFCIDCGPGFDJHDCPLFOJHDCOOELMOGLJKDPPCFHJHDCLHBCPCFHICCHONEIIOCLOGEDMGGDJDDGPNFIJEDBOMEJMMGJJIDNPBFEJMDJPJFMIKCPPOFLJPDKPCFHICCHKCAHOLEOIFCAKFAAPGFDJDDGPAFFJPDKPBFEJFDAOGEDMGGDIACFOPEKJNDILNBIPOFLJGDDPPFKJBDEPEFBIHCCOCEHMCGHIMCJOJEMJODLLOBLOHECICCHODEGJBDELBBEIDCGLDBGIBCELEBB
Now, let's visit my favourite tool for decoding ciphertext, CyberChef! (https://gchq.github.io/CyberChef/). Since the hint given in the chat exchange previously was the encoding sound like "Citric", let's look through the different encoding available.
I found this!

To be honest, it's my first time hearing this cipher. I learnt all about Caeser Ciphers, Substitution Ciphers, AES, DES, RSA encryptions during my bachelor, I have never venture further into exploring other encryptions after university.. Partly due to the PTSD from memorizing the formulas for the different encryptions, performing modulus operations.. HAHA
I tried finding some information on Citrix CTX1 cipher and only managed to learn that it's a propiarty cipher created by Citrix for use on its Netscaler Gateways. The encryption and decryption algorithm is described by this website https://asecuritysite.com/cipher/citrix.

I tried running Citrix CTX1 Decode on the ciphertext above in the custom OIDs on the SSL certificate and guess what?

The Last Five Bytes Are Wrong The Correct Bytes Combined Together Is Actually The Epoch Unix Timestamp In Seconds For Chinese New Year 2025
I then went to https://www.epochconverter.com/ and keyed in 29 Jan 2025 00:00:00 to get the Epoch Unix Timestamp in Seconds.

Thus, the Epoch timestamp in seconds for CNY 2025 is:
GMT: 1738080000
From the previous finding, we know the key is 'hong_huo_re_nao++++'. Converting to hex, we get the following hex '686f6e675f68756f5f72655f6e616f2b2b2b2b'.

Now, we need to connect to the final URL (wss://34.123.42.200:80) using mTLS to issue the key to get the flag.