WordPress REST API 内容注入漏洞分析
0x00 漏洞简述
1. 漏洞简介
在REST API自动包含在Wordpress4.7以上的版本,WordPress REST API提供了一组易于使用的HTTP端点,可以使用户以简单的JSON格式访问网站的数据,包括用户,帖子,分类等。
检索或更新数据与发送HTTP请求一样简单。
上周,一个由REST API引起的影响WorePress4.7.0和4.7.1版本的漏洞被披露,该漏洞可以导致WordPress所有文章内容可以未经验证被查看,修改,删除,甚至创建新的文章,危害巨大。
2.漏洞影响版本
WordPress4.7.0
WordPress4.7.1
0x01 漏洞复现
Seebug上已经给出详细的复现过程,在复现过程中可以使用已经放出的POC来进行测试。
0x02 漏洞分析
其实漏洞发现者已经给出了较为详细的分析过程,接下来说说自己在参考了上面的分析后的一点想法。
WP REST API
首先来说一下REST API。
控制器
WP-API中采用了控制器概念,为表示自愿端点的类提供了标准模式,所有资源端点都扩展WPRESTController来保证其实现通用方法。
五种请求
之后,WP-API还有这么几种请求(也可以想成是功能吧):
- HEAD
 - GET
 - POST
 - PUT
 - DELETE
 
以上表示HTTP客户端可能对资源执行的操作类型。
HTTP客户端
WordPress本身在WP_HTTP类和相关函数中提供了一个HTTP客户端。用于从另一个访问一个WordPress站点。
资源
简单来说,就是文章,页面,评论等。
WP-API允许HTTP客户端对资源执行CRUD操作(创建,读取,更新,删除,这边只展示和漏洞相关的部分):
- GET /wp-json/wp/v2/posts获取帖子的集合:
 

GET /wp-json/wp/v2/posts/1获取一个ID为1的单独的Post:

可以看到ID为1的文章标题为Hello World,包括文章的路由也有。
路由
路由是用于访问端点的“名称”,在URL中使用(在非法情况下可控,就像这个漏洞一样)。
例如,使用URLhttp://example.com/wp-json/wp/v2/posts/123:
路由(route)是wp/v2/posts/123,不包括wp-json,因为wp-json是API本身的基本路径。
这个路由有三个端点:
GET触发一个get_item方法,将post数据返回给客户端。
PUT触发一个update_item方法,使数据更新,并返回更新的发布数据。
DELETE触发delete_item方法,将现在删除的发布数据返回给客户端。 静态追踪
知道了WP-API的路由信息以及其操作方式,可以根据其运行的思路来看一下具体实现的代码。
我们看一下/wp-includes/rest-api/endpoints/class-wp-rest-post-controller.php:

根据上面的信息,我们可以知道这是注册controller对象的路由,实现路由中端点方法。
在这里,如果我们向/wp-json/wp/v2/posts/1发送请求,则ID参数将被设置为1:

同时,注意一下这里:

可以看到在registerrestroute中对路由进行了正则限制:

也就是防止攻击者恶意构造ID值,但是我们可以发现$GET和$POST值优先于路由正则表达式生成的值:

这边没有找到ID为123hh的项目,所以返回rest_invalid。
现在我们可以忽略路由正则的限制,来传入我们自定义的ID。
接下来在审查各个端点方法中,找到了updateitem这个方法,及其权限检查方法updateitempermissionscheck:

可以看到,此函数通过检查文章是否实际存在,以及我们的用户是否有权限编辑这边文章来验证请求。
但是当我们发送一个没有响应文章的ID时,就可以通过权限检查,并允许继续执行对update_item方法的请求。
具体到代码,就是让$post为空,就可以通过权限检查,接下来跟进get_post方法中看一下:

从代码中可以看出,它是用wpposts中的getinstance静态方法来获取文章的,跟进wp_posts类,位于/wp-includes/class-wp-post.php中:
public static function get_instance( $post_id ) {  
    global $wpdb;
    if ( ! is_numeric( $post_id ) || $post_id != floor
     ( $post_id ) || ! $post_id ) {
        return false;
    }
回头再看一下可执行方法upload_item:
public function update_item( $request ) {  
        $id   = (int) $request['id'];
        $post = get_post( $id );
        if ( empty( $id ) || empty( $post->ID ) || $this->post_type !== $post->post_type ) {
        return new WP_Error( 'rest_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
    }
    $post = $this->prepare_item_for_database( $request );
    if ( is_wp_error( $post ) ) {
        return $post;
    }
    // convert the post object to an array, otherwise wp_update_post will expect non-escaped input.
    $post_id = wp_update_post( wp_slash( (array) $post ), true );
在这边将ID参数装换为一个整数,然后传递给get_post。而PHP类型转换的时候回出现这样的情况:

所以,也就是说,当攻击者发起/wp-json/wp/v2/posts/1?id=1hhh请求时,便是发起了对ID为1的文章的请求。
下面为利用[exploit-db][2]上的POC来进行测试:
新建文章:

测试:

测试结果:

多想了一下
乍一看,感觉这个洞并没有什么太大的影响,但是仔细想了一下,危害还是很大的。
先不说WordPress页面执行php代码的各种插件,还有相当一部分的WordPress文章可以调用短代码的方式来输出特定的内容,以及向日志中添加内容,这是一个思路。
另一个思路就是可以进行对原来文章中的指定超链接进行修改,从而进行钓鱼。
还有一个思路,就是利用WordPress文章中解析html以及JavaScript文件包含的做法,辅助其他方法,进行攻击。
0x03 diff比较
对于该漏洞,关键的修改在/wp-includes/class-wp-post.php中:

更改了对于$post_id的参数的传入顺序和判断条件,防止了我们传入数字+字母这样的格式进行绕过。
0x04 修补方案
将WordPress更新到最新版本。
0x05 参考链接
https://www.seebug.org/vuldb/ssvid-92637
https://www.exploit-db.com/exploits/41223/
原文链接地址:http://paper.seebug.org/208/,点击“阅读原文”,即可进入。
本文章转载微信公众号@悬镜安全说
热门API
- 1. AI文本生成
 - 2. AI图片生成_文生图
 - 3. AI图片生成_图生图
 - 4. AI图像编辑
 - 5. AI视频生成_文生视频
 - 6. AI视频生成_图生视频
 - 7. AI语音合成_文生语音
 - 8. AI文本生成(中国)
 
最新文章
- 解读 TaskMatrix.AI
 - API协议设计的10种技术
 - ComfyUI API是什么:深入探索ComfyUI的API接口与应用
 - 从架构设计侧剖析: MCP vs A2A 是朋友还是对手?
 - Kimi Chat API入门指南:从注册到实现智能对话
 - 免费查询公司注册信息API的使用指南
 - 防御 API 攻击:保护您的 API 和数据的策略
 - 香港支付宝实名认证:是什么?怎么用?
 - 如何获取 Coze开放平台 API 密钥(分步指南)
 - 如何保护您的API免受自动化机器人和攻击 | Zuplo博客
 - ASP.NET Core Minimal APIs 入门指南 – JetBrains 博客
 - 什么是 OpenReview