Steeped in AI and the security risks of its use, the 2023 SANS Holiday Hack Challenge was an enrichening experience of navigating a series of 21 objectives that tested and broadened multiple cybersecurity skills.
The best challenges for me were hunting down AI hallucinations in a pentest report, escalating privileges on a Linux system, searching for cheats in Game Boy games, using the Azure REST API to search for an Azure Function app’s source code and ultimately to exploit a vulnerable SSH certificate service, practicing use of the Impacket tool suite and Certipy to exploit vulnerable Active Directory Certificate Services, and exploiting SQL injection and Java deserialization vulnerabilities in space apps.
Below, I share the path I followed to crack some of the most notable challenges.
ChatNPT, a large language model (LLM) used for the creation of some challenges, generated a penetration test report on vulnerabilities discovered in North Pole Systems’ network, some featuring as a part of upcoming challenges. However, ChatNPT hallucinated some of the details in the report. Using ChatGPT, or another favored LLM, the task was to flag the sections with hallucinated info. My approach was to ask ChatGPT specific questions about the content to explain what I did not understand at first and ultimately to discover the anomalies. Three of the nine sections contained errors.
As confirmed by ChatGPT, this section had an invalid port number of 88,555, far above the highest valid port number of 65,535:
Here I noticed immediately that SEND is not an HTTP request method.
ChatNPT confused the PHP version number mentioned in section eight of the report either as an HTTP protocol version or as mistaken text for what should have been “HTTP HEAD request” in this section. In addition, revealing Windows registration or product keys in the Location header is a bad piece of advice.
In this challenge, the final objective was to answer a question but that question was hidden in an inaccessible executable:
While there are various methods to escalate privileges on a Linux machine, this challenge allowed a custom executable called simplecopy with the SUID bit set to be abused. If the SUID bit for the owner of a file is set and the owner is root, then that file is always executed with root privileges even by non-root users on the system. I used the following command to search the entire system for regular files that have the SUID bit set for the owner, while discarding any error output:
simplecopy seemed to be a vulnerable, but simplified, version of the standard cp utility. Indeed the help message suggested the same:
Usage: simplecopy <source> <destination>
My approach was the following: create the information for a user with root privileges, append this information to a copy of the /etc/passwd file, then replace the old passwd file with the copy. Next, use su to login as the new user.
With root access to the system, I was able to find the executable runmetoanswer in /root, run it, and guess the answer: santa.
The answer was also given in the config file /etc/runtoanswer.yaml, but this file could only be read with root privileges or by using simplecopy to copy it to /dev/stdout.
Game cartridges: vol 2 and vol 3
Two challenges involved light reverse engineering of Game Boy ROM files. The first was a game where the objective was to get past a guard, reveal a portal, and decode the airwave answer. We were given two versions of the game along with a hint to look at the diff between them. Copying a few of the differing hex bytes from one version to the other was enough to reveal the portal, which led to a room with a radio broadcasting the answer in Morse code:
The second was a game where you could earn points by jumping to collect coins; however, earning over 998 points would wind your points around to 0 and, under certain conditions, trigger a message about an overflow error. The objective was to reveal floating steps to the next part of the map where the flag was stored, but this required adeptness at jumping. Instead, I figured out how to fly with the help of the BGB Game Boy emulator and a combination of its cheat searcher function and visual inspection of RAM during gameplay to find the hex byte that controls the y-position of the player on the map – basically, I sussed out a GameShark code.
The flag was !tom+elf!.
Although using certificates in place of public-private key pairs improves the security of authenticating over SSH, a misconfigured SSH certificate signing service may allow an attacker to illegitimately obtain a certificate to authenticate as another user. The challenge was set up in the following way.
An Azure Function app deployed on northpole-ssh-certs-fa.azurewebsites.net returns SSH certificates to anyone who provides an SSH public key. These certificates can be used to authenticate over SSH to ssh-server-vm.santaworkshopgeeseislands.org as the user monitor.
The host at this domain is an Azure virtual machine, so after logging in my first step was to collect information from the instance metadata as that would be needed for calls to the Azure REST API later, specifically, I needed the subscription ID and resource group name. I also needed an access token to use this API, which I was able to acquire by using a managed identity. This acquired token must then be used in an HTTP Authorization header when making calls to the Azure REST API.
At this point, I had everything needed to make the API call to get the source control configuration of the Azure Function app. I made the call and among the configuration properties I spotted a URL to the app’s source code on GitHub.
Inspection of the source code revealed that the app accepts a second parameter: principal. If the HTTP POST request to the /api/create-cert endpoint does not send a value for principal, then a default of elf is returned, but here lies a vulnerability. Using Burp Suite I can intercept the HTTP POST request and insert the value admin. I knew to request admin because it was the principal in the /etc/ssh/auth_principals/alabaster file on the virtual machine and I wanted to obtain access to Alabaster’s home directory.
With an SSH certificate for the admin principal in hand, I logged into the same virtual machine as alabaster and found Alabaster’s TODO list in his home directory. The list contained the flag word: gingerbread.
Starting on the same virtual machine as the previous challenge, this challenge looked at how a misconfigured Active Directory Certificate Service can be abused by an attacker to authenticate as another user. As alabaster I had a directory full of Impacket tools but most of them require a target server’s domain name and IP address, and a username and password for logging in – information I did not yet have.
So a good first step was to figure out my permissions for the Azure REST API as there is no need to call one API after another only to meet an authorization denied message. Thus, I listed all the permissions for the resource group that I discovered in the previous challenge.
Since I saw I had several permissions around reading key vaults, I moved on to listing them and found two: northpole-it-kv and northpole-ssh-certs-kv.
Time to switch APIs. Until now I had been making calls to endpoints on management.azure.com but some parts of the Azure Key Vault are on vault.azure.net and this resource requires its own access token. Once again I used my managed identity to acquire an access token but this time switching the resource to vault.azure.net.
In northpole-it-kv, I found the name for a secret. Using that name, I requested the value for this secret, which turned out to be a PowerShell script for creating an Active Directory user called elfy. Critically, I now had all the information needed to leverage the Impacket tool suite.
Using GetADUsers.py revealed another user in the domain that could be of interest: wombleycube. I was also able to connect via SMB to the Active Directory server using smbclient.py. The file share of interest contained a super_secret_research directory but I could not read it as elfy.
Luckily, I had access to another tool: Certipy. This is used to find misconfigured certificate templates for Active Directory Certificate Services and abuse them. The tool listed one vulnerable template due to it allowing a certificate requestor to supply an arbitrary subject alternative name and the issued certificate granting client authentication for the supplied name.
After requesting a certificate with wombleycube inserted into the subject alternative name field, I also used Certipy to get the NT hash for wombleycube using that certificate. Then, by passing Wombley’s hash to smbclient.py, I was able to connect via SMB to the Active Directory server as wombleycube and gain access to the super_secret_research directory, which contained the instructions for the next challenge in InstructionsForEnteringSatelliteGroundStation.txt.
Space Island door access speaker
To gain access to the challenges at the space system ground segment, it was required to use an LLM to generate a fake voice of Wombley Cube speaking the passphrase. Given an audio file of Wombley telling a story and the passphrase, it was trivial to use LOVO AI to generate a voice simulating Wombley’s to speak the passphrase and authenticate successfully.
Without additional safeguards, voice authentication faces serious challenges as a security mechanism in the age of LLMs.
After speaking the passphrase, I boarded a train that whizzed me away to the ground segment responsible for communication with an in-game CubeSat, a type of small satellite. In the ground station we were given a Wireguard configuration to set up an encrypted connection to this CubeSat.
The software on this satellite is compatible with the NanoSat MO Framework (NMF), a software framework developed by the European Space Agency for CubeSats. This framework comes with an SDK for developing and testing space apps. It also provides the Consumer Test Tool (CTT), both as a ground app and as a command line tool, to connect to the onboard supervisor, a software orchestrator that takes care of starting and stopping space apps as well as coordinating other tasks.
The challenge was to figure out how to instruct the onboard camera app to take a picture, then retrieve the snapshot. I took the following steps.
After booting up the CTT interface, I entered the supervisor’s URI to connect to the supervisor. Then I checked the available apps, found the camera app, and started it. The camera app returned its URI, which I used to connect to it. Next, I executed the Base64SnapImage action, which instructed the onboard camera to take a picture.
The camera app also offers a parameter service that can return two values: the number of snaps taken and the JPG snapshot encoded in base64. However, the CTT interface did not seem to provide a way to view the image nor to copy parameter values directly from the interface, although I could see the desired value was present. So I needed a roundabout method of acquiring the image.
I discovered that the CTT interface has an enableGeneration button that triggers regularly scheduled publishing of a parameter value. From the CTT command line, I could then subscribe to the desired parameter, receive the value when it was published, and redirect it into a file.
Since I was running CTT in a Docker container, I copied the file onto my host system with docker cp, removed the cruft from the file content, then base64 decoded the image to view the flag: CONQUER HOLIDAY SEASON!.
The final challenge was to use the missile-targeting-system app on the in-game CubeSat to redirect a missile from the earth to the sun. This app provided only one action: Debug. Running it didn’t seem to do much except print out the SQL VERSION command and its output as if it had been run by a database used by the app:
I immediately wondered if there was a SQL injection vulnerability at play. The CTT interface provided a field to enter an argument for the Debug action, so I tried injecting another command:
; SHOW GRANTS FOR CURRENT_USER();
Grants for targeter@%: GRANT USAGE ON *.* TO `targeter`@`%` IDENTIFIED BY PASSWORD ‘*41E2CFE844C8F1F375D5704992440920F11A11BA’
Grants for targeter@%: GRANT SELECT ON `missile_targeting_system`.`target_coordinates` TO `targeter`@`%`