2024 SANS Holiday Hack Challenge

Act II

Wombley’s getting desparate. Out-elved by Alabaster’s faction, he’s planning a gruesome snowball fight to take over present delivery!

Microsoft KC7 KQL

Pepper Minstix warns us about a serious cyber threat, and Team Wombley is not so great about OpSec.

The link in the objective takes us to the Microsoft KC7 portal. After creating a new account we are given the background for this challenge.
https://kc7cyber.com/go/hhc24

“Welcome to your mission to solve the The Great Elf Conflict! To do so, you’ll need to harness the power of KQL (Kusto Query Language) to navigate through the data and uncover crucial evidence. Your next step is to meet with Eve Snowshoes, Cyber Engineer, at the at the North Pole Cyber Defense Unit. Eve is known for unmatched expertise in KQL and has been eagerly awaiting your arrival.”

As sections are finished, the final answer needs to be entered into the relevant objective in the Holiday Hack Challenge site in order to get credit for completion. Each section has it’s own “achievement”, points and the overall Silver and Gold trophy are awarded when 2 and 4 challenges have been completed.

KQL is pretty simple to get started with if you haven’t used it before, and the challenge provides lots of direction and hints as you go.

The interface allows the use of tabs, and you can have different snippets and queries typed into the same workspace…only the piece where the cursor is at will execute. So when you find something that works it’s a good idea to keep in in the workspace or in one of the tabs.

The ‘Tables’ option above the workspace gives us an idea of what logs we have to work with.

Questions and Answers follow

Section 1: KQL 101

  1. Type let’s do this to begin your KQL training.
    • Answer: let’s do this.
    • No query needed.
  2. Once you’ve examined all the tables, type when in doubt take 10 to proceed.
    • Answer: When in doubt take 10.
    • No query needed
  3. How Many Elves did you find?
    • Answer: 90
    • Query
      • Employees
        | count
  4. Can you find out the name of the Chief Toymaker?
    • Shinny Upatree
    • Query
      • // Find the correct column
        namesEmployees
        | take 10
      • // Find Chief Toy Maker
        Employees
        | where role == "Chief Toy Maker"
  5. Type operator to continue.
    • Operator
  6. How many emails did Angel Candysalt receive?
    • 31
    • Query
      • Email
        | where sender == "twinkle_frostington@santaworkshopgeeseislands.org"
        | distinct recipient| count
  7. How many distinct recipients were seen in the email logs from twinkle_frostington@santaworkshopgeeseislands.org?
    • 32
    • Query
      • Email
        | where sender == "twinkle_frostington@santaworkshopgeeseislands.org"
        | distinct recipient| count
  8. How many distinct websites did Twinkle Frostington visit?
    • 4
    • Query
      • // Get Twinkle's source IP
        Employees
        | where name contains "Twinkle Frostington"

        //get info about outbound table
        OutboundNetworkEvents
        | take 5

        //combined query
        OutboundNetworkEvents
        | where src_ip == "10.10.0.36"
        | distinct url
        | count
  9. How many distinct domains in the PassiveDns records contain the word green?
    • 10
    • Query
      • PassiveDns
        | where domain contains "green"
        | distinct domain
        | count
  10. How many distinct URLs did elves with the first name Twinkle visit?
    • 8
    • Query
      • //Save the info we need as a variable
        let twinkle_ips = Employees
        | where name has "twinkle"
        | distinct ip_addr;
        // blank lines here will result in errors since the query has multiple parts.
        // So we make sure the variable block and the query block are connected.
        // Either directly or separated only by comments, as with this explanation.
        // Combined query
        OutboundNetworkEvents
        | where src_ip in (twinkle_ips)
        | distinct url
        | count

Section 2
Operation Surrender: Alabaster’s Espionage

  1. Type surrender to get started.
    • Surrender
  2. Who was the sender of the phishing email that set this plan into motion?
    • surrender@northpolemail.com
      • Email
        | where subject contains "surrender"
        | distinct sender
  3. How many elves from Team Wombley received the phishing email?
    • 22
    • Query
    • Email
      | where subject contains "surrender"
      | distinct recipient| count
  4. What was the filename of the document that Team Alabaster distributed in their phishing email?
    • Team_Wombley_Surrender.doc
    • Query
      • Email
        | where subject contains "surrender"
        | distinct recipient
        | count
  5. Who was the first person from Team Wombley to click the URL in the phishing email?
    • Joyelle Tinseltoe
    • Query
      • We are given the query solution for this question:
        Employees
        // condition to match rows
        | join kind=inner ( OutboundNetworkEvents) on $left.ip_addr == $right.src_ip
        | where url contains "Team_Wombley_Surrender"
        | project name, ip_addr, url, timestamp // project returns only the information you select
        | sort by timestamp asc //sorts time ascending
  6. What was the filename that was created after the .doc was downloaded and executed?
    • keylogger.exe
    • Query
      • We are given most of the query to solve this. I didn’t bother dialing it in very closely…I just changed the 2nd date/time stamp so the event we want isn’t cut off. It’s easy to pick out the intended file from the results.
      • ProcessEvents
        | where timestamp between(datetime("2024-11-25T09:00:37Z") .. datetime("2024-11-28T17:20:37Z"))
        | where hostname == "Elf-Lap-W-Tinseltoe"
  7. To obtain your flag use the KQL below with your last answer!
    • A2V5bG9nZ2VyLmV4ZQ==
    • Query
      • let flag = "keylogger.exe";
        let base64_encoded = base64_encode_tostring(flag);
        print base64_encoded


Section 3
Operation Snowfall: Team Wombley’s Ransomware Raid

  1. Type Snowfall to begin.
    • Snowfall
  2. What was the IP address associated with the password spray?
    • 59.171.58.12
    • Query
      • We are given the query to solve the challenge.
        AuthenticationEvents
        | where result == "Failed Login"
        | summarize FailedAttempts = count() by username, src_ip, result
        | where FailedAttempts >= 5
        | sort by FailedAttempts
  3. How many unique accounts were impacted where there was a successful login from 59.171.58.12?
    • 23
    • Query
    • AuthenticationEvents
      | where result == "Successful Login"
      | where src_ip contains "59.171.58.12"
      | distinct username
      | count
  4. What service was used to access these accounts/devices?
    • RDP
    • Query
      • AuthenticationEvents
        | where result == "Successful Login"
        | where src_ip contains "59.171.58.12"
        | distinct description
  5. What file was exfiltrated from Alabaster’s laptop?
    • secret_files.zip
    • Query
      • We are given a hint to look in the ProcessEvents table.
        ProcessEvents
        | where hostname == "Elf-Lap-A-Snowball"
    • Scanning through the events we can see where the attackers exfiltrated plans for a Snowball Cannon and a Drone to Wombley’s pc as ‘Secret_Files.zip’.
  6. What is the name of the malicious file that was run on Alabaster’s laptop.
    • EncryptEverything.exe
    • Query
      • ProcessEvents
        | where hostname == "Elf-Lap-A-Snowball"
    • Scanning through the results of the same query we used for question 5, we see that ‘EncryptEverything.exe was executed after the file was exfiltrated.
  7. To obtain your flag use the KQL below with your last answer!
    • RW5jcnlwdEV2ZXJ5dGhpbmcuZXhl
    • Query
      • let flag = "EncryptEverything.exe";
        let base64_encoded = base64_encode_tostring(flag);
        print base64_encoded


Section 4
Echoes in the frost: Tracking the Unknown Threat

  1. Type stay frosty to continue
    • stay frosty
  2. What was the timestamp of first phishing email about the breached credentials received by Noel Boetie?
    • 2024-12-12T14:48:55Z
    • Query
      • Email
        | where subject contains "breach"
        | sort by timestamp asc
  3. When did Noel Boetie click the link to the first file?
    • 2024-12-12T15:13:55Z
    • Query
      • OutboundNetworkEvents
        | where url contains “https://holidaybargainhunt.io/published/files/files/echo.exe”
    • We get the link from the previous query. Filtering on this we can find the timestamp for when it was visited.
  4. What was the IP for the domain where the file was hosted?
    • 182.56.23.122
    • Query
      • PassiveDns
        | where domain contains “holidaybargainhunt”
    • We have the domain from the prior query.
  5. Let’s take a closer look at the authentication events. I wonder if any connection events from 182.56.23.122. If so what hostname was accessed?
    • WebApp-ElvesWorkshop
    • Query
      • AuthenticationEvents
        | where src_ip contains “182.56.23.122”
  6. What was the script that was run to obtain credentials?
    • Invoke-Mimikatz.ps1
    • Query
      • ProcessEvents
        | where hostname contains “WebApp-ElvesWorkshop”
  7. What is the timestamp where Noel executed the file?
    • 2024-12-12T15:14:38Z
    • Query
      • ProcessEvent
        | where hostname == "Elf-Lap-A-Boetie
        | where process_commandline contains "echo.exe"
    • We have the name of the file we’re looking (echo.exe) from from an earlier question.
  8. What domain was the holidaycandy.hta file downloaded from?
    • compromisedchristmastoys.com
    • Query
      • OutboundNetworkEvents
        | where url contains “holidaycandy.hta”
  9. What was the first file that was created after extraction?
    • Sqlwriter.exe
    • Query
      • FileCreationEvents
        | where hostname contains "Elf-Lap-A-Boetie"
        | where path contains "Tasks"
    • We are given helpful context for this. “An interesting series of events has occurred: the attacker downloaded a copy of frosty.txt, decoded it into a zip file, and used tar to extract the contents of frosty.zip into the Tasks directory. “
  10. What is the name of the property assigned to the new registry key?
    • frosty
    • Query
      • ProcessEvents
        | where hostname == “Elf-Lap-A-Boetie”
        | where process_commandline contains “hk”
  11. To obtain your FINAL flag use the KQL below with your last answer!
    • ZnJvc3R5
    • Query
      • let finalflag = "frosty";
        let base64_encoded = base64_encode_tostring(finalflag);
        print base64_encoded

This gives us the final achievement and the gold trophy.

Logging out is hard, but we’ve got more challenges to solve!

Snowball Showdown

As with other challenges three medals are available, but this time Bronze isn’t automatic.

  • Bronze: Win the game without any detectable kind of cheating.
  • Silver: If you aren’t cheating are you really even trying?
  • Gold: How about a nice game of Snowballs?


The game initially appears to require two players, but single player is possible by adjusting the parameter at the end of the URI.


This is not ‘hackery’ enough to get Silver on it’s own, but it makes experimentation easier for the other medals.

Bronze

For Bronze we have to win normally or as single player. A pretty solid winning strategy is to line up and throw snowballs so that you are hitting one of the elves above while also periodically hitting Wombley near his laptop.

Because of the random and regenerating terrain, not all maps were great for this. The one shown seemed to have been the most optimal.

Silver

The Silver trophy requires that we hack the game to win. Examining the phaser-snowball-game.js file we find several options we can tweak in our favor. The items shown here aren’t the only ones available, but these are the ones I spent the most time adjusting.

To do so, first we need to override the .js file we want to manipulate.

Not all the options do what they seem like they should so some experimentation is required. For example…

  • this.throwRateOfFire = 0; Results in throwing snowballs at super speed!
  • this.throwRateOfFire = 1000; = Results in throwing snowballs suuuuuper slooooooowww.

Some combinations resulted in an inability to aim entirely.


“Flying”, teleportation, and manipulation of the play field are all possible. My personal approach was to stop the terrain from regenerating and enable rapid fire snowballs of doom, raining down death and destruction from several inches to the left. If you change the game parameters enough, it gives you this encouraging message. Beating Wombley after receiving this message earns the Silver Trophy.

Gold
We’re told the elves have misplaced a giant snow bomb. We need to find and trigger that to get the gold trophy.

We can find a few clues by simply searching for ‘bomb’ in the game files. Along with ‘MOASB’.

And a bit of poking around in the console reveals the option to trigger the Mother of all Snow Bombs.

this.mainScene.moasb()

The gold trophy is ours!

Powershell

Piney Sappington needs our help with a powershell predicament.

The challenge has us iteratively build a set of increasingly complex powershell commands primarily focusing around Invoke-Webrequest and MFA.

Powershell Silver Solution

  1. There is a file in the current directory called ‘welcome.txt’. Read the contents of this file.
    • Get-Content ./welcome.txt
  2. Geez that sounds ominous, I’m sure we can get past the defense mechanisms. We should warm up our PowerShell skills. How many words are there in the file?
    • get-content ./welcome.txt | Measure-Object -word
      • 180 words
  3. There is a server listening for incoming connections on this machine, that must be the weapons terminal. What port is it listening on?
    • netstat -an –numeric-ports
      • 1225
  4. You should enumerate that webserver. Communicate with the server using HTTP, what status code do you get?
  5. It looks like defensive measures are in place, it is protected by basic authentication. Try authenticating with a standard admin username and password.
    • Not needed specifically at this point, but it’s worth setting up a reusable credential at this point.
      • $credential = New-Object System.Management.Automation.PSCredential("admin", (ConvertTo-SecureString "admin" -AsPlainText -Force))
    • Invoke-Webrequest http://127.0.0.1:1225 -credential $credential -allowunencryptedauth
  6. There are too many endpoints here. Use a loop to download the contents of each page. What page has 138 words? When you find it, communicate with the URL and print the contents to the terminal.
    • $response = Invoke-WebRequest http://127.0.0.1:1225 -Credential $credential -AllowUnencryptedAuthentication
      $response.Links | ForEach-Object {
      $page = Invoke-WebRequest $_.Href
      if (($page.Content -split '\s+').Count -eq 138) {
      Write-Output $_.Href
      }
      }
    • Endpoint 13
  7. There seems to be a csv file in the comments of that page. That could be valuable, read the contents of that csv-file!
  8. Luckily the defense mechanisms were faulty! There seems to be one api-endpoint that still isn’t redacted! Communicate with that endpoint!
  9. It looks like it requires a cookie token, set the cookie and try again.
    • $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession
    • $cookie = New-Object System.Net.Cookie("token", "5f8dd236f862f4507835b0e418907ffc", "/", "127.0.0.1")
    • $session.Cookies.Add($cookie)
    • (Invoke-WebRequest http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C -credential $credential -allowunencryptedauthentication -websession $session)
    • At this point the question sometimes hangs at 9 even if it gives you an MFA code. If you get the code, go ahead and do what it says and pass the token. Command for that is the one given next for question 10.
  10. Sweet we got a MFA token! We might be able to get access to the system. Validate that token at the endpoint!
    • The token will be the item given under href formatted like this: xxxxxxxx.yyyyyyyy
    • If that looks familiar, that’s because it’s just a unix timestamp.
      • $mfaCookie = New-Object System.Net.Cookie("mfa_token", "1732062090.4975593", "/", "127.0.0.1")
      • $session.Cookies.Add($mfaCookie)
      • Invoke-WebRequest -Uri "http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -WebSession $session -credential $credential -allowunencryptedauthentication
    • We’ll get a message in the content saying: “[!] System currently in lock down. Failure, token has expired. [*] Default timeout set to 2s for security reasons”. That 2 second timeout is why we either have to generate our own MFA code or pull and pass the token all at once. We’ll generate our own for gold, but we’ll pull and pass all at once for this step.
      • $response = Invoke-WebRequest -Uri "http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -Credential $credential -AllowUnencryptedAuthentication -WebSession $session; $mfaTokenValue = ($response.Content -match "mfa_code.+?href='([^']+)'") | Out-Null; $session.Cookies.Add((New-Object System.Net.Cookie("mfa_token", $matches[1], "/", "127.0.0.1"))); (Invoke-WebRequest -Uri "http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -WebSession $session -Credential $credential -AllowUnencryptedAuthentication).content
    • Response received will be:
      • <h1>[+] Success</h1><br><p>Q29ycmVjdCBUb2tlbiBzdXBwbGllZCwgeW91IGFyZSBncmFudGVkIGFjY2VzcyB0byB0aGUgc25vdyBjYW5ub24gdGVybWluYWwuIEhlcmUgaXMgeW91ciBwZXJzb25hbCBwYXNzd29yZCBmb3IgYWNjZXNzOiBTbm93TGVvcGFyZDJSZWFkeUZvckFjdGlvbg==</p>
  11. That looks like base64! Decode it so we can get the final secret!
    • [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("Q29ycmVjdCBUb2tlbiBzdXBwbGllZCwgeW91IGFyZSBncmFudGVkIGFjY2VzcyB0byB0aGUgc25vdyBjYW5ub24gdGVybWluYWwuIEhlcmUgaXMgeW91ciBwZXJzb25hbCBwYXNzd29yZCBmb3IgYWNjZXNzOiBTbm93TGVvcGFyZDJSZWFkeUZvckFjdGlvbg=="))
  12. Hurray! You have thwarted their defenses! Alabaster can now access their weaponry and put a stop to it. Once HHC grants your achievement, you can close this terminal.

Silver trophy with a single command

To achieve Silver all in one go, expand this command then copy and paste it into the terminal.

$credential = New-Object System.Management.Automation.PSCredential("admin", (ConvertTo-SecureString "admin" -AsPlainText -Force)); get-content ./welcome.txt; Start-Sleep -s 2; Write-Output "180"; Start-Sleep -s 2; Write-Output "1225"; Start-Sleep -s 2; Invoke-Webrequest http://127.0.0.1:1225; Start-Sleep -s 2; Invoke-Webrequest http://127.0.0.1:1225 -credential $credential -allowunencryptedauthentication; Start-Sleep -s 5; (Invoke-Webrequest http://127.0.0.1:1225/endpoints/13).content; Start-Sleep -s 2; (Invoke-Webrequest http://127.0.0.1:1225/token_overview.csv -credential $credential -allowunencryptedauthentication).content ; Start-Sleep -s 2; Invoke-WebRequest http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C -credential $credential -allowunencryptedauthentication; $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession; start-sleep -s 2; $cookie = New-Object System.Net.Cookie("token", "5f8dd236f862f4507835b0e418907ffc", "/", "127.0.0.1"); Start-Sleep -s 2; $session.Cookies.Add($cookie); Start-Sleep -s 2; $response = Invoke-WebRequest -Uri "http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -Credential $credential -AllowUnencryptedAuthentication -WebSession $session; start-sleep -s 2; (Invoke-WebRequest http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C -credential $credential -allowunencryptedauthentication -websession $session).content; start-sleep 2; $response = Invoke-WebRequest -Uri "http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -Credential $credential -AllowUnencryptedAuthentication -WebSession $session; $mfaTokenValue = ($response.Content -match "mfa_code.+?href='([^']+)'") | Out-Null; $session.Cookies.Add((New-Object System.Net.Cookie("mfa_token", $matches[1], "/", "127.0.0.1"))); Start-Sleep -Seconds 1; $rawContent = (Invoke-WebRequest -Uri "http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -WebSession $session -Credential $credential -AllowUnencryptedAuthentication).Content; $response = Invoke-WebRequest -Uri "http://127.0.0.1:1225/tokens/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -Credential $credential -AllowUnencryptedAuthentication -WebSession $session; $mfaTokenValue = ($response.Content -match "mfa_code.+?href='([^']+)'") | Out-Null; $session.Cookies.Add((New-Object System.Net.Cookie("mfa_token", $matches[1], "/", "127.0.0.1"))); (Invoke-WebRequest -Uri "http://127.0.0.1:1225/mfa_validate/4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C" -WebSession $session -Credential $credential -AllowUnencryptedAuthentication).content; start-sleep -s 2;[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String("Q29ycmVjdCBUb2tlbiBzdXBwbGllZCwgeW91IGFyZSBncmFudGVkIGFjY2VzcyB0byB0aGUgc25vdyBjYW5ub24gdGVybWluYWwuIEhlcmUgaXMgeW91ciBwZXJzb25hbCBwYXNzd29yZCBmb3IgYWNjZXNzOiBTbm93TGVvcGFyZDJSZWFkeUZvckFjdGlvbg=="))

Gold
For Gold we need to bypass the EDR.

We are given two hints.

  • They also mentioned this lazy elf who programmed the security settings in the weapons terminal. He created a fakeout protocol that he dubbed Elf Detection and Response “EDR”. The whole system is literally that you set a threshold and after that many attempts, the response is passed through… I can’t believe it. He supposedly implemented it wrong so the threshold cookie is highly likely shared between endpoints!
  • I overheard some of the other elves talking. Even though the endpoints have been redacted, they are still operational. This means that you can probably elevate your access by communicating with them. I suggest working out the hashing scheme to reproduce the redacted endpoints. Luckily one of them is still active and can be tested against. Try hashing the token with SHA256 and see if you can reliably reproduce the endpoint. This might help, pipe the tokens to Get-FileHash -Algorithm SHA256.
  1. First we need to figure out the rest of the endpoint info. The redacted endpoints are just the sha256 of the MD5 in the csv, so that’s easy enough.
  2. The script I used for this will generate the unix timestamps and pass those inline so we only need to worry about the validation endpoints.
  3. The first time we hit an endpoint, we’ll see a message about a ‘canary tripwire’
    • This command will generate and example of that.
      • $credential = New-Object System.Management.Automation.PSCredential("admin", (ConvertTo-SecureString "admin" -AsPlainText -Force)); $session = New-Object Microsoft.PowerShell.Commands.WebRequestSession; $cookie = New-Object System.Net.Cookie("token", "04886164e5140175bafe599b7f1cacc8", "/", "127.0.0.1"); $session.Cookies.Add($cookie); $response = (Invoke-WebRequest -Uri "http://127.0.0.1:1225/tokens/DFD05F3B46D21BC8556CDBF544325A945ED0304EC0BB7DBFD68ED5931E7FF6EE" -Credential $credential -AllowUnencryptedAuthentication -WebSession $session); write-output $response.content; $mfaTokenValue = ($response.Content -match "href='([^']+)'"); $mfaTokenValue = $matches[1]; write-output $mfatokenvalue; $session.Cookies.Add((New-Object System.Net.Cookie("mfa_token", $matches[1], "/", "127.0.0.1")));$rawContent = (Invoke-WebRequest -Uri "http://127.0.0.1:1225/mfa_validate/DFD05F3B46D21BC8556CDBF544325A945ED0304EC0BB7DBFD68ED5931E7FF6EE" -WebSession $session -Credential $credential -AllowUnencryptedAuthentication)
  4. We need to hit each endpoint in order, 11 times each to bypass the threshold. Hit the first endpoint 11 times, then the second, etc. The resonse changes once we hit it enough times.
    To do so, we’ll run two commands.
  5. First we create a file called endpoints.txt.
The command for this step is quite large – expand this for the full command.

Set-Content -Path "./endpoints.txt" -Value @'

04886164e5140175bafe599b7f1cacc8,DFD05F3B46D21BC8556CDBF544325A945ED0304EC0BB7DBFD68ED5931E7FF6EE
664f52463ef97bcd1729d6de1028e41e,1F3C45D7E7B1F7621F67136C538C6933791D3392648C7B0F8B17FB1A6343EBD5
3e03cd0f3d335c6fb50122553f63ef78,e2dbbdbcc7e57e526841899975b6621105710e76c203c1dc30419e7f1cba5297
f2aeb18f5b3f08420eed9b548b6058c3,bc83a2c7a6279ead36370ab3509fea7483eff83c164fc7cfe3c680d879b9f9d2
32b9401a6d972f8c1a98de145629ea9d,b93772d2393029424049564b366f08b21e66282ce3d7b9da4f7a69c8891012a0
3a79238df0a92ab0afa44a85f914fc3b,989fde082fb901ae5edfd873bf41a76bf47b318f76968d0275dad99abd5894b
49c2a68b21b9982aa9fd64cf0fd79f72,e2b80b2c70ba5982814b758849472c8711af8051e5261ce827c02818d7e1de8c
f8142c1304efb9b7e9a7f57363c2d286,704fecc829a3acb5e50e3277eafc03ca902e515cfd81bfa94670a44aca551d6a
706457f6dd78729a8bed5bae1efaeb50,f5251b4ca89f3c4eb6bbcf40ad41abc870ffb8bfc4d1e170df7ed88f23e20568
bb0564aa5785045937a35a9fa3fbbc73,a9fba920d17e1b98f393e691ec837a0ca41be8115389f2a634f77dceeaf20725
4173a7bc22aee35c5fc48261b041d064,af2b19cc9cee8005c95a321605fb2aab8577c66ebd762cc2df943fee47987d86
198b8bf2cd30a7c7fed464cca1720a88,0f8b6bd4b5d006bd7a0fceeba10a7726430ea9af7fa4314c0a83e17acaea32b6
3a7c8ecffeeadb164c31559f8f24a1e7,38810a7bea5587438cbfa6a0c364619ca4f886ec403be951cf38f5158d7da4f9
288e60e318d9ad7d70d743a614442ffc,a93f039a2d5158251d509afb2bf6bc45dec47d8ac189a170a23caea07f009476
87ab4cb29649807fdb716ac85cf560ea,89298b15becc2f6c98c53ebfebf1c91d7d569cb0895b22f840bd3b9ca400e9f7
89f3ec1275407c9526a645602d56e799,f6bc096e3570c6622ea951b3cfb852e116d0e1460576fb9fbe6f6b75dccb46c7
33539252b40b5c244b09aee8a57adbc9,3877cd419e2d8907c09de27c68babfbcd88a9d8f7bebdb4385aff288d0a39631
152899789a191d9e9150a1e3a5513b7f,1e257fca2443492c6b8621bee3117e9de40d6d6e1a7ca4861079fb74cb9dc164
7cd48566f118a02f300cdfa75dee7863,ac7458460e359b3b940540168affc7fa354c3cc05a52a52a1a6de0538ae60ec8
d798a55fca64118cea2df3c120f67569,65d70badac127dac0bcc4962107b708a37cf24b8dcd327c328a3c59a610f18ee
6ef5570cd43a3ec9f43c57f662201e55,e2692776b6ecd2c39373ced96f7ff8b64aeae56404352cf06aefc724cc4c7cf1
bf189d47c3175ada98af398669e3cac3,e433feb9de55166463e0bd4ba14a3cc099bd1ba1abf20fd290491bda3d3cf2e6
743ac25389a0b430dd9f8e72b2ec9d7f,cf056ae6605f7909a7e6126716e528b863807d2dc153a4d5aa00714ac72aadc4
270aabd5feaaf40185f2effa9fa2cd6e,f732a928cda0c4a51284f8b28401eac228ca2769804cfbcc16f2c8a0c58713f2
8b58850ee66bd2ab7dd2f5f850c855f8,d2e0f89c295194775b9835f646f5bdd950eb05cb6d912602ce4ff62a30ebe437
6fd00cbda10079b1d55283a88680d075,7a50d248e3991bf55c361e119faa292c8661f0eb6476c868db3289793391e12f
612001dd92369a7750c763963bc327f0,bc9f1a7214052f7ad3fb5076a1862e24dc71808ba27c5d9c584d681f6c3afe6b
010f2cc580f74521c86215b7374eead6,5bbe0d45c6a1fd01a7641ff4807009efc91fd7f5637dded8b71ad8e2ada5d086
29860c67296d808bc6506175a8cbb422,8fcff56c3c425a8a7163493b1f17fb330ad89fbbefddd9c5d226196c4c327bbb
7b7f6891b6b6ab46fe2e85651db8205f,f892f608096e56013c3ed96e4f984c6376617c42e328e20382660370ff181ce4
45ffb41c4e458d08a8b08beeec2b4652,d040356e54ca166cd0479d9878fc9ba1012a80419a846a58a24649f4f01b63f8
d0e6bfb6a4e6531a0c71225f0a3d908d,ff1a98e7bb3d9f87c4495ee7d62c455cbad6c6a6ad4abba25a503b7c1ae113c1
bd7efda0cb3c6d15dd896755003c635c,6297f422314ea9e9172752f3a891bd505752c5f14fcab93153bcb1038fb5ab87
5be8911ced448dbb6f0bd5a24cc36935,04519f7af02a977a27a8d9398e86354117537fa896c437019a61f46d31bfd339
1acbfea6a2dad66eb074b17459f8c5b6,5a4b06401ad22fa4cf75fe22829cb14549c4f18a18c67beb16d314b80a4f44d9
0f262d0003bd696550744fd43cd5b520,abf98108dbd1095a59bae36726fac48e3ab23da2cf2effeae02ef5c142cb76fe
8cac896f624576d825564bb30c7250eb,2dda00da692764113b97787241658a1911aeb4c0bd9b92e4fab4ffcb1fe15a33
8ef6d2e12a58d7ec521a56f25e624b80,ce92890075a6232a039bcb06b83adf90355119a99da8d694c85681e310191bb9
b4959370a4c484c10a1ecc53b1b56a7d,ae311ebe101c6ac4e11e4e338906ecedc22583fad68b85834eed3cb684fc4383
38bdd7748a70529e9beb04b95c09195d,acab8d777a0c5f9d631b4fe6e07cc14b7e063c1e48ee8a0ab876d12082ad3fea
8d4366f08c013f5c0c587b8508b48b15,01b1f4132e95b847d89fcdc599fd685e26198aa684196663c4c0b51b1a410dc4
67566692ca644ddf9c1344415972fba8,368ef0eb3bd563c81f658e6c47182524256f09212010774b00939a440dabd9f6
8fbf4152f89b7e309e89b9f7080c7230,b3395dc1f995f897f36e5ab99b7a3eada186713cc40785bb33f06c4d7c989673
936f4db24a290032c954073b3913f444,9992b3b85730ea436a546086036520bcf15d86b1a2a8f96e4d3c569148f770d3
c44d8d6b03dcd4b6bf7cb53db4afdca6,35138166ced99d6a600b52f70c818cf44826170fd88b8234107ec0bd51cc276d
cb722d0b55805cd6feffc22a9f68177d,15d6e335c167dea490ac4cd91815c56d7ecef3cd4b019588567710d839a9ea45
724d494386f8ef9141da991926b14f9b,2ae009872b65d788e44f634736689af1570333fd802b93b41df09a3f7d02c0e1
67c7aef0d5d3e97ad2488babd2f4c749,bac2f3580b6491cbf26c84f5dcf343d3f48557833c79cf3efb09f04be0e31b60
5f8dd236f862f4507835b0e418907ffc,4216B4FAF4391EE4D3E0EC53A372B2F24876ED5D124FE08E227F84D687A7E06C
'@


Continuing…

  1. Then we’ll run the command below to hit each endpoint 11 times.
  2. That should do it!

Mobile Analysis

Eva Snowshoes needs our help with an Android app


We’re given a test and production version of the app. Each has a name present in the database that is missing from the app after it loads.

The write up for this challenge ended up being very short, but this was particularly interesting as almost all of the tools and file formats used were new to me. Like a lot of these solution write ups, I am leaving out the step of ‘clicked around on the wrong things for several hours with a puzzled expression on my face’.

Silver

  1. Extract the .apk file using keka or another tool that supports extracting these archives.
  2. Making a few guesses about the contents based on the info we got from the Elf and the hints, I ran grep -r Canada ./SantaSwipe/* this got a few hits in classes3.dex, so I knew where to look.
  3. Examining the file closer we find the answer for the Silver medal.
    1. hideouspenguin@C1-10P test % strings ./SantaSwipe/classes3.dex | grep WHERE &DELETE FROM NaughtyList WHERE Item = ‘ #DELETE FROM NiceList WHERE Item = ‘ %DELETE FROM NormalList WHERE Item = ‘ 9SELECT Item FROM NormalList WHERE Item NOT LIKE ‘%Ellie%’
  4. The answer for silver is Ellie, Alabama, USA

Gold

Solving Silver gives us two hints.

  1. I used Android Studio for this challenge. We can’t ‘open’ the aab file with that tool, but we can generate an apk file and import that.
    1. bundletool build-apks –bundle=SantaSwipeSecure.aab –output=output.apks –mode=universal
    2. unzip ./output.apks
    3. Output from unzipping the apks file includes an apk.
  2. Import the APK into Android Studio with the ‘profile or debug APK’ option.
  3. Once imported we can explore the files and search. Searching for “database”, we quickly find that DatabaseHelper.smali which builds the database contains many encrypted strings. We need to decrypt these.
  4. Examining the file further we see references to an encrypted key and an IV.
  5. The resources for this file are compiled into an arsc file in the res directory. We can find them as strings there using Android Studio.
  6. Now that we have the key and the iv, we can decrypt the strings.
  7. This proved more challenging than expected. Several tools I tried produced errors. Ultimately I used https://www.lddgo.net/en/encrypt/aes
  8. Examining the strings in the DatabaseHelper.smali file, we find one encrypted string much longer than the others. When decrypted, it points to a specific encrypted string as shown in the image above.
  9. Decrypting KGfb0vd4u/4EWMN0bp035hRjjpMiL4NQurjgHIQHNaRaDnIYbKQ9JusGaa1aAkGEVV8= gives us the answer for the Gold trophy: Joshua, Birmingham, United Kingdom
    • Joshua

Drone Analysis

Chimney Scissorsticks needs our help getting important info to Alabastor.

We don’t have anything in the hints section, but Chimney gives us several important pieces of information.

  • I think they hide the admin password in the drone flight logs.
  • You’ll be working with KML files, tracking drone flight paths.
  • Use tools like Google Earth and some Python scripting to decode the hidden passwords and codewords locked in those files.

Drone Silver Solution
The application URL is https://hhc24-dronepath.holidayhackchallenge.com


I found nothing in the JS or html comments this time. Exploring the application reveals a login page and a file share with a .kml file.

The doc format is XML with some google earth links and what appears to be coordinates.


Uploading the doc to Google Earth (or another tool for viewing KML files) we are presented with our first password: GUMDROP1

We can log in with this password and the UserID suggested by the file name. Fritjolf: GUMDROP1


The workshop wants a drone name which we don’t have yet.


And we don’t have the admin portal code word yet.


The profile gives us our next target.

The file is a CSV.
https://hhc24-dronepath.holidayhackchallenge.com/files/secret/Preparations-drone-name.csv

But we can easily convert it. I used the tool at this link.
https://www.convertcsv.com/csv-to-kml.htm
The page is full of ads if you open it in a vanilla browser config. Privacy Badger or some other ad blocker makes it usable.
https://privacybadger.org/


The various fields are automatically detected and the output is a KML file we can download.


After loading it into Google Earth, no immediate pattern is apparent. The pins themselves also don’t have much data associated with them.



Zooming in on a random pin to examine it clarifies the intent.
ELF-HAWK…that would be a cool name for a drone…

And it is!


This file is a CSV format again, but with an extra row appended to the end of the first line. And why are there so many columns anyway? The converter only seemed to need a few? (We’ll come back to that.) For now…

First we move the seemingly misplaced row of data to the bottom of the chart where it will make sense.

It’s not quite perfect so we have to populate the date manually.

Upload the fixed ELF-HAWK.kml file to google earth and…

Wait…what?

Resemblance to the death star notwithstanding, this looks…not right.

After re-applying the conversion and examining the contents for metadata I was missing, I noticed some conversation in the discord chat about tools that I hadn’t paid any attention to so far. This led me in the direction of ‘map’ vs. ‘globe’.

I initially had ChatGPT do the work for me on this one, but going back I was unable to duplicate that.

To process it the second time around I extracted the latitude and longitude Data, then used python from ChatGPT to plot that. This is the python I used to map the coordinates.

import pandas as pd
import folium

# Filepath to your CSV file
csv_path = 'ELF-HAWK-dump.csv'  # Replace 'your_file.csv' with the actual file name

# Read the CSV file
try:
    data = pd.read_csv(csv_path)
    
    # Check if the required columns exist
    if 'Longitude' in data.columns and 'Latitude' in data.columns:
        # Calculate the map's center
        map_center = [data['Latitude'].mean(), data['Longitude'].mean()]
        map_plot = folium.Map(location=map_center, zoom_start=5)
        
        # Add markers for each coordinate
        for _, row in data.iterrows():
            folium.Marker(
                location=[row['Latitude'], row['Longitude']],
                popup=f"Lat: {row['Latitude']}, Lon: {row['Longitude']}"
            ).add_to(map_plot)
        
        # Save the map as an HTML file
        output_map_path = 'coordinates_map2.html'  # The output file name
        map_plot.save(output_map_path)
        print(f"Map successfully saved to {output_map_path}. Open it in your browser to view the map.")
    else:
        print("The CSV file must contain 'Longitude' and 'Latitude' columns.")
except Exception as e:
    print(f"An error occurred: {e}")


Partial image is below…getting the whole thing requires some zooming in and out with the original. The full image says ‘DroneDataAnalystExpertMedal’

Silver trophy achieved!

Drone Gold Solution
For the Gold medal, we need to find hidden data from the last drone file. There is a hint given that the web app may be vulnerable to injection.

The vulnerable page is the Drone Workshop.

Injection payload:
ELF-HAWK’ OR ‘1’=’1
URL encoded:
ELF-HAWK%27OR%271%27=%271D

Searching the individual drone names gives us a new clue…specifically Pigeon-Lookalike-V4

The clue is: “I heard a rumor that there is something fishing with some of the files. There was some talk about only TRUE carvers would find secrets and that FALSE ones would never find it.

Which brings us back to all those extra columns in the file.

My initial attempts at extracting data from that failed because I had made changes to the original download file that negated the pattern required. After getting a hint about that I was able to extract the data correctly. To do so…

  1. Download the original file again to be sure it’s unchanged.
  2. Load into CyberChef
  3. Regex out everything but the TRUE and FALSE
  4. Convert True/False to 1/0
  5. Add ‘From Binary’ to the recipe and…

Inputting EXPERTTURKEYCARVERMEDAL on the Admin page gives the gold medal.

Pretty cool right? Act II complete!