DVWA SQL Injection手工注入

SQL Injection

实验拓扑如图6.0.1

tp

6.0.1  实验拓扑

6.1 Low级别

首先连接此站点,网站样子如图6.1.1所示。

6.1.1

6.1.1 SQL Injection初始界面

测试一下,如图6.1.2

6.1.2

6.1.2 判断注入点

随后判断表中有多少字段,使用命令
1′ order by 5#
来判断。当猜测的值为5时系统报错,说明字段值应该小于5,如图6.1.3所示。

6.1.3

6.1.3  当猜测字段值为5时报错

经过试验,得到正确的字段值为2

接下来判断版本和数据库。输入
1′ and 1=2 union select user(),database() #
得到图6.1.4的结果,说明用户是root,数据库是dvwa。继续测试,输入
1′ and 1=2 union select version(),database() #
得到图6.1.5的结果,说明版本是MySql 5.0

6.1.4

6.1.4  判断用户和数据库

6.1.5

6.1.5  判断版本和数据库

接下来判断表。输入
1′ and 1=2 union select table_name,2 from information_schema.tables where
table_schema=’dvwa’ #
判断存在的表,返回结果如图6.1.6,可以发现存在guestbookusers表。

6.1.6

6.1.6  判断表名

接下来判断表中有哪些字段,使用users表,输入命令
1′ and 1=2 union select column_name,2 from information_schema.columns where
table_name=’users’ #
得到图6.1.7的结果,所有字段均显示出来。

6.1.7

6.1.7  user表中的字段

我们可以发现关键字段userpassword,直接使用
1′ and 1=2 union select user,password from users#
命令获取所有用户名和密码,如图6.1.8

6.1.8

6.1.8  爆用户名和密码

6.1.9

6.1.9  guestbook表的字段

测试另一张表,发现并没有什么有用的信息,如图6.1.9,故不再深测。

 

6.2 Medium级别

这个级别里,网站只允许从下拉列表中选取数据,且使用POST方法提交,杜绝了直接在地址栏和TextBox中写入注入语句的可能。鉴于此,我们使用BurpSuite来修改POST提交的数据,如图6.2.1所示。

6.2.1

6.2.1  BurpSuite中修改POST提交的数据

6.2.2

6.2.2  服务器对“”进行了转义

在提交数据之后得到图6.2.2的结果,由此结果可知,服务端对提交的数据进行了过滤,其中“”符号被转义为“\’”。将“”符号去掉,直接使用1 and 1=1语句时,能得到正常结果,如图6.2.3所示,当使用1 and 1=2时无结果。由此可以判断出只要我们不使用“”符号,仍然存在成功注入的可能。于是在Low级别的基础上,直接使用1 and 1=2 union select users,password
from users#
便得到了结果,如图6.2.4所示。

6.2.3

6.2.3 去掉单引号进行查询

6.2.4

6.2.4  得到结果

 

6.3 High级别

对于高级别的难度,咋一看挺高大上,如图6.3.1,还需要弹窗输入ID才能查看信息。

6.3.1

6.3.1  高级别难度界面

小试一下,发现仍能进行注入,如图6.3.2

6.3.2

6.3.2  高等级界面仍能进行注入

所以直接上大招,试了一下Level Medium里最后的语句,得到图6.3.3。然而这并不能实现图6.2.4的效果。这时注意到图6.3.2使用了“’”符号,因此可以判断出这次需要“’”,故使用Level Low里的最后的语句,成功获取全部数据,如图6.3.4。由于中间过程和Low级别类似,故不再累述,一些与Low级别出入的地方,在6.4节进行分析。

6.3.3

6.3.3 使用Medium级别的语句测试

6.3.4

6.3.4  获取全部数据

 

6.4 分析

因为High级别的过程过于简单,和想想中的高级别有一定的区别,因此我们来分析一下各级别的代码。

6.4.1 Low级别

Low级别的代码如代码段6.4.1所示。

代码段6.4.1  Low级别代码

1

<?php

2

3

if( isset( $_REQUEST‘Submit’ ] ) ) {

4

    // Get input

5

    $id $_REQUEST‘id’ ];

6

7

    // Check database

8

    $query  “SELECT first_name, last_name FROM users WHERE user_id = ‘$id’;”;

9

    $result mysql_query$query ) or die( ‘<pre>’ mysql_error() . ‘</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

20

        echo “<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>”;

21

22

        // Increase loop count

23

        $i++;

24

    }

25

26

    mysql_close();

27

}

28

29

?>

由此代码可知,这里对输入的内容没有进行任何过滤等操作,直接对输入的值进行构造SQL语句,并使用之进行查询(第5行到第9行)。例如,当我们执行1′ and exists(select * from admin)#时,id的值即为1′ and exists(select * from admin)#,由此构造的SQL语句querySELECT first_name, last_name FROM
users WHERE user_id = ‘1’ and exists(select * from admin)#’;
。这时我们可以看到,原本的获取id的语句变成了获取id并判断是否存在admin表,中间是and连接,即只有两个都为真才返回真。而最后的“#”符号又对剩下的语句进行了注释,确保只执行我们想要的部分。这一点在接下来的操作里非常重要。所以后来我们就可以根据这个原理进行语句构造,猜测表结构,获取我们想要的各种信息。

 

6.4.2 Medium级别

Medium级别的代码如代码段6.4.2

代码段6.4.2  Medium级别代码

1

 

<?php

2

 

 

3

 

if( isset( $_POST‘Submit’ ] ) ) {

4

 

    // Get input

5

 

    $id $_POST‘id’ ];

6

 

    $id mysql_real_escape_string$id );

7

 

 

8

 

    // Check database

9

 

    $query  “SELECT first_name, last_name FROM users WHERE user_id = $id;”;

10

 

    $result mysql_query$query ) or die( ‘<pre>’ mysql_error() . ‘</pre>’ );

11

 

 

12

 

    // Get results

13

 

    $num mysql_numrows$result );

14

 

    $i   0;

15

 

    while( $i $num ) {

16

 

        // Display values

17

 

        $first mysql_result$result$i“first_name” );

18

 

        $last  mysql_result$result$i“last_name” );

19

 

 

20

 

        // Feedback for end user

21

 

        echo “<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>”;

22

 

 

23

 

        // Increase loop count

24

 

        $i++;

25

 

    }

26

 

 

27

 

    //mysql_close();

28

 

}

29

 

 

30

 

?>

由此代码第6行可知,这里使用了 mysql_real_escape_string() 函数对输入的字符串进行简单的过滤,将一些字符进行转义,如“”、“”等,并返回转义后的字符串。然而由第9行的代码可知,这里在进行查询时并没有对转义后的结果加上单引号,因此我们可以不使用引号进行注入。由此便得到了6.2节里的内容。

如果不在Low级别的基础上进行操作,则基本过程也和Low级别一样,只不过变成了在BurpSuite中修改POST的数据,再进行转发。

 

6.4.3 High级别

再来看High级别的内容,代码如代码段6.4.3所示。

代码段6.4.3  High级别代码

1

 

<?php

2

 

 

3

 

if( 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

20

 

        echo “<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>”;

21

 

 

22

 

        // Increase loop count

23

 

        $i++;

24

 

    }

25

 

 

26

 

    mysql_close();

27

 

}

28

 

 

29

 

?>

由第3行、第5行可知,这里使用SESSION方法来传递id的值,在第9行使用mysql_query()die()函数,使得当查询不成功时整个页面直接down掉,如图6.4.1,退出重新登录也没用,除非退出并把cookie清空,再刷新登录。

6.4.1

6.4.1  die掉的页面

观察query语句构造代码(第8行)可知,这里的查询语句和以往的不同,这里添加了LIMIT 1的限制,只返回第一列的内容。然而我们使用“#”直接把这句注释掉了,所以这个限制并不是很大,我们依然可以拿到想要的数据。

这里由于三个等级在一起实验,所以借鉴了前两个等级的代码,而如果真正要从0开始的话肯定要有一些查询失败的地方,这时就会不断出现图6.4.1的现象,需要不停地清除cookie才能成功。但基本思路和原理类似。

原创文章,91tfboys.com版权所有,转载请注明出处。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据