HTB Writeups
  • HTB Writeups
  • Boxes: Very Easy
    • Academy
    • Archetype
    • Arctic
    • Base
    • Bike
    • Blue
    • Explosion
    • Included
    • Markup
    • Oopsie
    • Redeemer
    • Responder
    • Shield
    • Unified
    • Vaccine
  • Boxes: Easy
    • Analytics
    • Armageddon
    • Bashed
    • Beep
    • Blocky
    • Bounty Hunter
    • Buff
    • Cap
    • CozyHosting
    • Devel
    • Explore
    • Forest
    • Grandpa
    • Granny
    • Horizontall
    • Jerry
    • Keeper
    • Knife
    • Lame
    • Late
    • Legacy
    • Mirai
    • Netmon
    • Nibbles
    • Optimum
    • Paper
    • Photobomb
    • Precious
    • RedPanda
    • Return
    • Sau
    • ScriptKiddie
    • Sense
    • Servmon
    • Shocker
    • Shoppy
    • Squashed
    • Trick
  • Boxes: Medium
    • Poison
  • Challenges
    • Behind the Scenes
    • Canvas
    • Debugging Interface
    • Digital Cube
    • Easy Phish
    • Find the Easy Pass
    • Forest
    • Infiltration
    • misDIRection
    • Pusheen Loves Graphs
    • Retro
    • Signals
    • The Secret of a Queen
    • Wrong Spooky Season
  • Fortresses
  • Cyber Apocalypse 2023: The Cursed Mission
    • The Cursed Mission
    • Alien Cradle
    • Critical Flight
    • Debug
    • Extraterrestrial Persistence
    • Getting Started
    • Needle in the Haystack
    • Orbital
    • Packet Cyclone
    • Passman
    • Perfect Sync
    • Persistence
    • Plaintext Tleasure
    • Questionnaire
    • Reconfiguration
    • Relic Maps
    • Roten
    • Secret Code
    • Shattered Tablet
    • Small StEps
  • Hack the Boo 2023
    • Hauntmart
    • Spellbrewery
    • Trick or Treat
    • Valhalloween
  • Cyber Apocalypse 2024: Hacker Royale
    • Hacker Royale
    • An Unusual Sighting
    • BoxCutter
    • BunnyPass
    • Character
    • Data Siege
    • Delulu
    • Dynastic
    • Fake Boost
    • Flag Command
    • Game Invitation
    • It has begun
    • KORP Terminal
    • Labyrinth Linguist
    • LockTalk
    • Lucky Faucet
    • Makeshift
    • Maze
    • Packed Away
    • Phreaky
    • Primary Knowledge
    • Pursue the Tracks
    • Rids
    • Russian Roulette
    • Stop Drop and Roll
    • Testimonial
    • TimeKORP
    • Unbreakable
    • Urgent
  • CYBER APOCALYPSE 2025: Tales from Eldoria
    • Tales from Eldoria
    • A New Hire
    • Cave Expedition
    • Echoes in Stone
    • Eldorion
    • Embassy
    • EncryptedScroll
    • HeliosDEX
    • Quack Quack
    • Silent Trap
    • Stealth Invasion
    • Tales for the Brave
    • The Ancient Citadel
    • The Hillside Haven
    • The Stone That Whispers
    • Thorins Amulet
    • ToolPie
    • Traces
    • Trial by Fire
    • Whispers of the Moonbeam
Powered by GitBook
On this page
  • Getting an Overview
  • Analyzing the Python Script
  • Disas initial:
  • Disas enc_mes:
  • Disas dec_file_mes:
  • Disas dec_mes:
  • Disas receive_file:
  • Disas <genexpr>:
  • Disas receive:
  • Decrypting
  • Challenge Questions
  • What is the IP address responsible for compromising the website?
  • What is the name of the endpoint exploited by the attacker?
  • What is the name of the obfuscation tool used by the attacker?
  • What is the IP address and port used by the malware to establish a connection with the Command and Control (C2) server?
  • What encryption key did the attacker use to secure the data?
  • What is the MD5 hash of the file exfiltrated by the attacker?
  1. CYBER APOCALYPSE 2025: Tales from Eldoria

ToolPie

Personal Rating: Medium

Getting an Overview

According to the story, the designs of a toolmaker have been stolen. We have to investigate the incident and expose Lord Malakar as guilty. This forensics challenge starts off with a pcap file. The first thing that looked interesting was a request to a web page with a nice tale, related to the lore of the CTF:

      <p>In the heart of Eastmarsh, where traders and adventurers alike gather, there stood a humble yet revered figure...Garrick Stoneforge, the Guardian Smith. His forge, a beacon of unwavering craftsmanship, glowed with an intensity that matched his dedication. While most saw only the glimmer of his enchanted locks and finely wrought tools, those who paid closer attention whispered of something more...something hidden beneath the clang of hammer against steel.</p>

            <p>For years, Garrick...s work was sought after, not just for its quality but for the intricate designs etched into every piece he created. Patterns of runes, symbols long forgotten, whispered secrets only those willing to listen could understand. Some say his trinkets were more than just adornments; they were keys...keys that could open more than doors, revealing secrets long buried beneath the foundation of Eastmarsh itself.</p>
            
            <p>One particular evening, under the glow of a waning moon, a mysterious traveler arrived at Garrick...s forge. Draped in a cloak darker than the night, they carried with them a request...an artifact locked away, sealed behind a mechanism of Garrick...s own making. It was then that the Guardian Smith faced a choice: to protect the secrets he had crafted or aid in their unraveling.</p>

            <p>What happened that night remains unknown, but the following dawn brought unease to Eastmarsh. Garrick, a man of unshakable principle, seemed distant. The forge burned lower, the usual rhythm of his hammer faltered. Whispers filled the town square...had the Guardian Smith unlocked something never meant to be found?</p>
            
            <p>Days passed, and then weeks, until the forge fell silent altogether. Some claim Garrick vanished, while others believe his presence still lingers in the very locks he forged. His creations remain scattered across Eastmarsh, treasured by those who value craftsmanship and mystery alike.</p>

            <p>Even now, his legend endures...his work a lasting testament to a craftsman who dared to shape more than metal. The Guardian Smith of Eastmarsh may be gone, but his story, like the artifacts he left behind, will never be forgotten.</p>

Another interesting request was made to /script.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Medieval Script Executor - Medieval Chronicles</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <header>
            <div class="corner-decoration">.......</div>
            <div class="corner-decoration">.......</div>
            <div class="corner-decoration">......</div>
            <div class="corner-decoration">.......</div>
            <h1>Magical Script Executor</h1>
            <div class="divider">....</div>
            <nav class="medieval-nav">
                <a href="index.html">Chronicles</a>
                <span class="nav-divider">......</span>
                <a href="script.html">Script Executor</a>
            </nav>
        </header>
        
        <article class="blog-post">
            <h2>The Mystical Python Scroll</h2>
            <div class="scroll-container">
                <textarea id="scriptInput" class="scroll-text" placeholder="Inscribe thy Python incantations here..."></textarea>
            </div>
            
            <div class="action-container">
                <button onclick="executeScript()" class="medieval-button">Execute Thy Script To Create Thy Tool</button>
            </div>
            
            <div class="quote output-container">
                <h3>Prophecy Output</h3>
                <pre id="outputArea">The tool shall appear here...</pre>
            </div>
        </article>
    </div>

    <script>
        async function executeScript() {
            const scriptText = document.getElementById('scriptInput').value;
            const outputArea = document.getElementById('outputArea');
            
            try {
                const response = await fetch('/execute', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({ script: scriptText }),
                });
                
                const result = await response.json();
                outputArea.textContent = result.output || 'No output was produced by thy script.';
            } catch (error) {
                outputArea.textContent = 'A mystical error has occurred: ' + error.message;
            }
        }
    </script>
</body>
</html>

This html suggests that there was a web page that could be used to execute code in some way.

A POST in json format to /execute can be made with the script, apparently providing the means to execute Python code on the server. And indeed, a POST to the server can be found!

At first glance, the script bz2-decompresses a bytestream and then loads and executes it. The necessary libraries are imported first and a keyboard interrupt stops the execution.

I saved the compressed stream in raw hex format.

The next TCP stream looks like something that happened on the server, which seems to be a Windows host:

Almost nothing of this can be determined, except the machine and user at the start

ec2amaz-bktvi3e\administrator

The blue parts at the beginning are the only server data transmitted. The rest is 8.504 kB of client data.

This is the next stream:

And this is the final related one:

The first and last two lines do not seem encrypted, the rest does.

Start: ec2amaz-bktvi3e\administrator <SEPARATOR>5UUfizsRsP7oOCAq

End: ec2amaz-bktvi3e\administrator <SEPARATOR>LFca75ceNdmiGtrZ

I had the suspicion that these requests are the start and end requests of an encrypted C2 communication. The two seemingly random strings could be encryption keys.

From the investigation of the aforementioned requests in Wireshark, I could already determine the relevant IPs that were involved in the suspicious traffic:

Server IP: 172.31.47.152 Sent Mal. Script: 194.59.6.66 Potential C2: 13.61.7.218:55155

Analyzing the Python Script

I stored the script and cleaned it up in an effort to see what it does.

From the start of the script you can tell that the following data must be a bz2 steam with a block size of 9, as it starts with BZh9

\x is the Python Byte delimiter. The extra \ comes from html encoding. This could be reversed:

The resulting script worked if you replaced "exec" with "print".

python malscriptprint.py
<SNIP>
<code object <module> at 0x5bc7acd4bbe0, file "Py-Fuscate", line 1>
<SNIP>

This also reveals the program that was used to obfuscate the code. The goal now is to recover the obfuscated code from the marshal object.

Unescaping the result again and extracting strings from it gave some insights into what it might be doing.

The results indicate Py-Fuscate was used to obfuscate code that can send AES-encrypted files to or from the IP 13.61.7.218

I created a script to output the disassembled version that yielded some more info.

import marshal
import dis

marshal_object = b'''c\x00\x00\x00\x00\x00<SNIP>\x03'''

code_object = marshal.loads(marshal_object)
dis.dis(code_object)

import marshal,lzma,gzip,bz2,binascii,zlib indicates that lzma, binascii, lzma and zlib are potentially used inside the marshal.

I read through the disassembly and noted any relevant information that caught my eye.

Disas initial:

  • Process opened

  • AES Crypto.Cipher and Crypto.Util.Padding are used/created

  • There is the '<SEPARATOR>' string

  • There are the code objects enc_mes, dec_file_mes, dec_mes, receive_file, receive

  • The main function is created. A socket is opened for a client. A connection to ('13.61.7.218', 55155) is prepared. encode is used

  • receive_thread with ('target', 'args') is used

  • A sleep time of 50 is done

Disas enc_mes:

  • An AES CBC encryption is prepared with the variables cypher, key and cypher_block and padding

Disas dec_file_mes:

  • The key is encoded and used for AES CBC decryption. An unpadding is done and the vars key, cypher, cypher_block are used. The cipher is a byte object initially

Disas dec_mes:

  • The cipher is a byte object. The vars cypher, cypher_block and key are used to unpad and AES CBC decrypt the byte object

Disas receive_file:

  • A socket is opened, ('13.61.7.218', 54163). The vars k, client2 and more are used.

  • enc_received, dec_mes, enc_received and ok_enc are used. a SEPARATOR var is used

  • enc_mes and ok_enc are used. A byte object is received and read. files can be written.

  • There is the error ('Error transporting file')

Disas <genexpr>:

  • A random ASCII letter string apperas to be generated

Disas receive:

  • A message is received and decrpyted. The vars msg and message are used. The message is encrypted again to check if decryption was correct

  • enc_answ is used. A file can be sent to "target". A file can be received and get_file is used, as well as enc_mes

  • dec_mes is used and a file can be written.

  • enc_mes, dec_mes and a send operation can be seen again

  • The message + \n is set

  • An answer is encoded and send. The error message ('Bad command!') appears. The error ('Error uploading file') also appears.

  • A client connect to ('13.61.7.218', 55155) can be seen again

From what we have seen before, we have the encryption key(s) from the network traffic.

Decrypting

Testing around with different Key-IV combinations in Cyberchef, it appeared that the first 16 Bytes after the SEPARATOR were the key and the second string was the IV.

I changed the Wireshark view to raw bytes. TCP stream 4 could be decrypted to a PDF file this way!

Using the "save as file" function of CyberChef worked perfectly to retrieve the PDF.

Here is a comparison of the start bytes of the decrypted file and the sample PDF:

Extract:

Sample:

Replacing the first Bytes to the ones of the sample report worked and the challenge was solved.

Challenge Questions

What is the IP address responsible for compromising the website?

194.59.6.66 - This is clearly the IP that sent the malicious script to the server when you inspect the http streams

What is the name of the endpoint exploited by the attacker?

execute - You can see that the malicious script was POSTed to /execute

What is the name of the obfuscation tool used by the attacker?

PyFuscate - This could be seen after decompressing the initial byte object

What is the IP address and port used by the malware to establish a connection with the Command and Control (C2) server?

13.61.7.218:55155 - This could also be found in the decompressed string and also in Wireshark

What encryption key did the attacker use to secure the data?

5UUfizsRsP7oOCAq - This can be found at the start of the encrypted data in Wireshark

What is the MD5 hash of the file exfiltrated by the attacker?

- This can be generated after the PDF has been AES CBC decrypted and the first Bytes of the PDF corrected with the ones from a sample pdf

PreviousThorins AmuletNextTraces

Last updated 1 month ago

The md5sum of the pdf (that was required to solve the challenge) was wrong though. A friend saw the string "yeportLab Generated PDF document" and downloaded a reference file from

https://www.reportlab.com/docs/reportlab-userguide.pdf
Suspicious POST Request
Suspicious Encrypted Requests
Stream 2 of Encrypted Requests
Ending Request
Stored Python Script Start
Stored Python Script End
Unescaping the Bytestring
Bytestring String Recovery
Marshal Disassembly
Encryption Key(s)
Decryption in CyberChef
TCP Stream 4 Raw
PDF Decryption
Recovered PDF File
Recovered PDF Start
Sample PDF Start