忆往昔,峥嵘岁月稠!大学已经到了大三了,打了很多比赛,回顾还是挺欣慰!此系列来由是想留一点东西,把所学知识整理一下,同时也是受github上Micro8分享的启发,故想做一些工作,以留后人参考,历时两个星期,第一系列SQL注入回顾篇出炉!内容分四节发布,其中SQL注入代码审计为两节,WAF绕过总结为1节,SQLMAP使用总结为1节!此为SQL注入代码审计第二部分。欢迎各位斧正,交流!
SQL注入就是web应用程序对用户输入数据的合法性没有判断,前端传入后端的参数是攻击者可控的,并且参数带入数据库查询攻击者可以通过构造不同的sql语句来实现对数据库的任意操作。
Sql注入的产生需要满足以下两个条件:
当传入的参数ID为1’时,数据库执行的代码如下所示。
Select * from users where id=1’
这个语句不符合数据库语法规范,所以会报错。当传入的参数为and 1=1时,执行的sql语句如下所示
Select * from users where id=1 and 1=1
因为1=1为真,且where语句中的id=1也为真,所以页面会返回与id=1相同的结果。当传入的id参数为and 1=2时,此时sql语句恒为假,所以服务器会返回与id=1不同的结果
在实际环境中,sql注入会导致数据库的数据泄露,在安全配置不当的情况下还可能会被攻击者拿到系统权限,进行文件的读写操作等。
普通的注入审计,可以通过$_GET
,$_POST
等传参追踪数据库操作,也可以通过select , delete , update,insert 数据库操作语句反追踪传参。
在Mysql 5.0版本之后,Mysql默认在数据库中存放一个”information_shcema”的数据库,在该库中,读者需要记住三个表名,分别是SCHEMATA,TABLES和COLUMNS。分存储该用户创建的所有数据库的库名,库名和表名,库名和表名,字段名。
Limit的用法:使用格式为limit m,n,其中m是指记录开始的位置,从0开始,表示第一条记录:n是指取n条记录。例如limit 0,1表示从第一条记录开始,取一条记录。
需要记住的几个函数
database()
:当前网站使用的数据库。version()
:当前MYSQL的版本。user()
:当前MySQL的用户。注释符
在MYSQL中,常见的注释符的表达式为:#或–空格或/**/,
,//,-- , --+,,%00,--a。
内联注释
内联注释的形式:/*!code */。内联注释可以用于整个SQL语句中用来执行我们的SQL语句,
举个栗子:
Index.php?id=-15 /*!UNION*/ /*!SELECT*/ 1,2,3
MYSQL对大小写不敏感,所以存在大小绕过。
注入测试地址http://192.168.23.134/time/time.php?id=1
访问该网址,页面返回yes。,在网址的后面加上一个单引号,再次访问,页面返回no。这个结果与boolean注入非常相似,与之不同的是,时间注入是利用sleep()
或benchmark()
等函数让Mysql的执行时间更长。时间盲注多与IF(expr1,expr2,expr3)
结合使用,此if语句含义是:如果expr1是TRUE,则IF()的返回值为expr2
;否则值则为expr3
。所以判断数据库库名长度的语句应为:
if(length(database())>1,sleep(5),1)
这条语句的意思是,如果数据库名的长度大于1,则mysql查询休眠5秒,否则查询1。
而查询1的结果大约只有几十毫秒,所以可以根据Burp Suite页面的响应时间,判断条件时候正确。
页面的响应时间为6154毫秒,也就是6.154秒,表明页面成功执行了sleep(5)
,所以数据库名长度是大于1的,我们尝试将判断数据库库名长度语句中的长度改为10。
执行时间为1051毫秒,也就是1.051秒,也就是说服务器并没有执行sleep(5)
,而是执行了select 1,
所以返回yes。经过多次测试后就可以确定数据库库名的长度,接下来我们可以查询数据库库名的第一位字母,查询语句与Boolean盲注相似,使用substr
函数,这时的语句应为
if(substr(database(),1,1)=’d’,sleep(5),1)
程序延迟了6.024秒才返回,说明数据库库名的第一位字母是d,依此类推即可得出完整的数据库库名,表名,字段名和具体数据。此外,时间盲注受网速影响,所以有时候会出现偏差,建议选在网站网速较好的时候测试!
在这个程序页面,程序获取GET参数ID,通过preg_match判断参数ID中是否存在Union危险字符,然后将参数ID拼接到SQL语句中。将数据库中查询SQL语句,如果有结果,则返回yes,否则返回no。当访问该页面时,代码根据数据库查询结果返回yes或no,而不返回数据库的任何数据在页面上,与Boolean注入不同的是,此处没有过滤sleep等字符,代码如下:
$con=mysqli_connect("localhost","root","root","dvwa");
// 检测连接
if (mysqli_connect_errno())
{
echo "连接失败: " . mysqli_connect_error();
}
$id = $_GET['id'];
if (preg_match("/union/i", $id)) {
exit("no