SQL Injection,即SQL注入,是指攻击者通过注入恶意的SQL命令,破坏SQL查询语句的结构,从而达到执行恶意SQL语句的目的。SQL注入漏洞的危害是巨大的,常常会导致整个数据库被“脱裤”,尽管如此,SQL注入仍是现在最常见的Web漏洞之一。
Low
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<?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
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>" ;
}
mysqli_close( $GLOBALS [ "___mysqli_ston" ]);
} ?> |
可以看出,Low级别代码对来自客户端的参数id没有进行任何的检查与过滤
漏洞利用
在看不到源码的前提下,我们需要自己手工注入来进行判断
1.判断是否存在注入点,数字型还是字符型。
①我们分别输入 1 and 1=1 以及 1 and 1=2 (先1 and 1=1
再1 and 1=2
来判断页面是否有变化,如果有变化那么肯定就是数字型
注入)
如果 1 and 1=2 时数据消失了,就是字符型注入。
如图判断得不是数字型注入。
②输入 1’ 查询失败 1’ # 查询成功 所以ID是被两个单引号包裹,字符型注入。
1’
1’#
③判断回显位置
④查询数据库名
⑤查询数据库中的表
⑥查询表中字段名(不演示中间部分了,老生常谈的东西)
⑦查询字段值
通过 www.cmd5.com 解码
我们将抓到的数据包发送至Repeater模块进行操作更加方便,这里给出最后一步,因为都是重复的。
Medium
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
<?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
echo "<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进行转义,同时前端页面设置了下拉选择表单,希望以此来控制用户的输入。
漏洞利用
虽然前端使用了下拉选择菜单,但我们依然可以通过抓包改参数,提交恶意构造的查询参数。
大部分步骤和低级一样,只是需要将页面在Burpsuite中发送到Repeater模块,不停的改提交的参数即可,注意的只有一点,由于将’和“符号转义了,所以不可以直接写库名和表名了,需要将库名和表名换为十六进制。
High
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
<?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
echo "<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也是可以绕过的),因为sqlmap在注入过程中,无法在查询提交页面上获取查询的结果,没有了反馈,也就没办法进一步注入。
Impossible
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<?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
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>" ;
}
}
} // Generate Anti-CSRF token generateSessionToken(); ?> |
可以看到,Impossible级别的代码采用了PDO技术,划清了代码与数据的界限,有效防御SQL注入,同时只有返回的查询结果数量为一时,才会成功输出,这样就有效预防了“脱裤”,Anti-CSRFtoken机制的加入了进一步提高了安全性。