DVWA(七):SQL-Injection(sql注入)
目录
SQL注入定义
SQL是操作数据库数据的结构化查询语言,网页的应用数据和后台数据库中的数据进行交互时会采用SQL。而SQL注入是将Web页面的原URL、表单域或数据包输入的参数,修改拼接成SQL语句,传递给Web服务器,进而传给数据库服务器以执行数据库命令。如Web应用程序的开发人员对用户所输入的数据或cookie等内容不进行过滤或验证(即存在注入点)就直接传输给数据库,就可能导致拼接的SQL被执行,获取对数据库的信息以及提权,发生SQL注入攻击。(来自百度百科)
SQL注入流程
当我们在web页面中遇到用户输入框,或者url上显示了输入数据情况,怀疑存在sql注入,可以执行以下步骤:
- 寻找注入点,确认是字符串注入还是数字注入
- 查询该表中字段数
- 查询各字段含义
- 找到当前数据库名称
- 找出数据库中的表名
- 找出表中字段名和字段值
- 写入webshell
安全级别Low
源码分析
1 | <?php |
关键数据库查询语句如下,id传入是字符串型:
1 | SELECT first_name, last_name FROM users WHERE user_id = '$id'; |
DVWA中注入位置如下,是数据库id查询框:
其中$id就是用户的输入内容,可以看出,该代码并未对用户输入进行处理,而是直接拼接成sql语句执行,因此很容易注入。
漏洞复现
输入框输入1,显示信息是该id的First name和Surname:
输入框输入1’,测试是否有引号闭合:
提示引号错误,因为多了一个单引号,查询语句变为:
1 | SELECT first_name, last_name FROM users WHERE user_id = '1''; |
说明输入是字符串型输入,要闭合单引号。这和我们看到的代码吻合,sql查询语句中使用‘id’,输入内容被当做字符串。
闭合单引号,输入框输入1’ and 1=1#,结果如下:
该语句中1=1为永真条件,sql中#为注释符,因此原语句单引号被注释掉,不会发生错误,查询语句变为:
1 | SELECT first_name, last_name FROM users WHERE user_id = '1' and 1=1 #'; |
在mysql语句中使用order by进行查询的排序,order by后可以接字段名,也可以接字段号,如按照第一个字段排序就是order by 1。因此可以利用该特性找出表中的字段数,当order by 报错时,说明超出表中的字段数。输入1’ order by 2#,结果如下:
当我们输入1’ order by 3# 时显示:
说明该表中只有两个字段,即显示的First name和Surname。
在mysql中可以使用union联合查询多条信息,而select有一个特性,select直接加数字串时,可以不写后面的表名,那么它输出的内容就是我们select后的数字。我们利用该特性来注入,输入1’ union select 1,2#,结果如下:
如果我们替换select后面的1和2为sql语句,那么就会先执行语句再显示。mysql中可以使用database()显示当前数据库名,使用version()显示版本信息。那么我们输入:
1’ union select version(),database()#
结果如下:
以上都是一些比较基础和简单的信息,接下来我们需要爆出库中的表、表中的各项数据。首先需要知道mysql中有一个信息数据库 information_schema ,记录了该数据库的各种信息。
information_schema.schemata表,表中schema_name记录了数据库名称;information_schema.tables表,表中table_schema记录数据表所属的数据库名,table_name记录表名称;information_schema.columns表,表中table_name记录该列的表名,column_name 记录列名。利用这三个表可以获得数据库中的各种信息。
输入:
1’ union select 1,group_concat(schema_name) from information_schema.schemata#
获取数据库的名称:
现在知道有一个名为dvwa的数据库,输入:
1’ union select 1,group_concat(table_name) from information_schema.tables where table_schema=’dvwa’#
查找属于dvwa库的表名称:
查询到dvwa数据库中有guestbook和users表,因此输入:
1’ union select 1,group_concat(column_name) from information_schema.columns where table_name=’users’#
查询users表中的字段名称:
现在已经获取到关键字段名称,如first_name、last_name,就是正常显示的字段。其中有user、password两个字段,获取这两个字段信息,现在已经知道表名和字段名,直接查询即可,输入:
1’ union select user,password from users#
结果如下:
安全级别Medium
源码分析
1 | <?php |
代码和初级有所不同,在获取id传入数据后,调用mysqli_real_escape_string函数将x00,\n,\r,\,’,”,x1a这些特殊字符转义,防SQL注入。同时查询语句中id直接作为数字传入,没有单引号。
DVWA中中级显示如下,这次连输入框都没有了,通过下拉列表来实现id传入。
漏洞复现
在下拉列表中选择1,并点击Submit提交,获取到如下信息:
此时我们的url也没有发生变化,因此找不到注入点。我们通过burpsuit抓包看看:
发现Submit是通过POST方法提交的,post传入的数据为id=1&Submit=Submit。因此我们可以更改post传入的id值来进行注入,使用hackbar插件,直接填充post的数据:
Submit=Submit&id=1 union select 1,2
剩余步骤和级别low相同,一步步找出数据库名、表名、列名,最后获得用户和密码。需要注意的是单引号等特殊字符被转义,因此不能使用,当我们需要使用’dvwa’或者’users’时,可以使用16进制来绕过:
1 | 1 union select 1,group_concat(schema_name) from information_schema.schemata# |
最后结果如下:
安全级别High
源码分析
1 | <?php |
DVWA中高级显示如下,点击”here to change your ID”,页面自动跳转,防御了自动化的SQL注入。分析源码可可知,对参数没有做防御,和级别low类似。
点击链接后:
漏洞复现
在输入框中输入1’ union select 1,2#,可以直接执行,结果如下:
因此使用和low一样的步骤,可以依次得到数据库、表、列等数据,这里不再演示。
安全级别Impossible
1 | <?php |
impossible中使用了两处安全防护措施:
- Anti-CSRF token
1 | // Check Anti-CSRF token |
使用token验证用户,防止CSRF攻击
- 数字检查
使用is_numeric( $id )判断输入的id是数字还是字符串,只有输入为数字才执行sql查询。
- sql预处理
1 | $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' ); |
预处理和PDO会将传入的id值当做一个整体来处理,在运行时才替换参数,因此就算id中使用了引号或者sql语句,执行时会将其当做一整个字符串,不会有拼接动作,也就不能sql注入。
同时最后使用了$data->rowCount() == 1,限制查询结果为一条时才输出,有效防止信息泄露。