項(xiàng)目11:SkyTower-1(下:SQL注入回顧)
##SQL基本原理
---數(shù)據(jù)庫(kù)查詢(xún)流程

---拼接GET請(qǐng)求參數(shù)id直接拼接在SQL語(yǔ)句中,未經(jīng)過(guò)任何過(guò)濾或處理
---并且使用了 LIMIT
子句來(lái)限制結(jié)果集只返回一行(即第一行)

---如何發(fā)現(xiàn)SQL注入(輸入異常數(shù)據(jù),會(huì)報(bào)錯(cuò))
---如果未報(bào)錯(cuò),不代表不存在 Sql 注入
---因?yàn)橛锌赡茼?yè)面對(duì)單引號(hào)做了過(guò)濾,這時(shí)可以使用判斷語(yǔ)句進(jìn)行注入
---sQL注入的發(fā)現(xiàn),核心就是輸入錯(cuò)誤的數(shù)據(jù),會(huì)發(fā)生報(bào)錯(cuò)
---這里是預(yù)定的是傳入數(shù)字型變量,所以沒(méi)有對(duì)參數(shù)加 ''

---SQL的注釋語(yǔ)句:
/**/
注釋?zhuān)哼@是MySQL中的多行注釋語(yǔ)句,單行注釋則是 -- 或者 #;%00
終止查詢(xún):在某些情況下,;%00
被用于嘗試終止查詢(xún)的執(zhí)行。這是因?yàn)樵谀承㎝ySQL客戶(hù)端和服務(wù)器配置中,分號(hào) ?;
被用作查詢(xún)分隔符,%00
是空字符的編碼/*letmetest*/
注釋?zhuān)侯?lèi)似于普通注釋?zhuān)@是一種用戶(hù)可能用來(lái)在SQL語(yǔ)句中添加自己的解釋性注釋的方式。這樣做可以幫助其他人理解SQL語(yǔ)句的目的
---總結(jié):--是起到注釋的作用,+是啟動(dòng)空格的作用,在sql中用--注釋后面必須加一個(gè)空格--才生效,所以我們不一定要用--+,可以用-- 加上空格在加上任意字符,都行
---SQL注入的類(lèi)型

---例如在一個(gè)數(shù)據(jù)庫(kù)里面存在

---ORDER BY column1 [ASC|DESC], column2 [ASC|DESC],
---order by根據(jù)指定類(lèi)的升序或者降序排列(這里是倒序排列)

---limt 0,2是只顯示第1列和第二列

---可以使用union進(jìn)行多個(gè)表的查詢(xún)

##SQL有回顯注入之union注入
---step 1 :判斷是否存在注入漏洞(注意:+輸入在MYSQL里面是空格)
---兩次回顯不一樣,說(shuō)明存在SQL注入

---step 2:判斷有當(dāng)前的表多少個(gè)字段(多少列):
---注意:ORDER BY
后面應(yīng)該跟著列名,以便指定要根據(jù)哪一列(字母升序排序)進(jìn)行排序
---那么,就可以測(cè)試表里面存在多少列(說(shuō)明這里存在三列)

---通過(guò)構(gòu)造語(yǔ)句,查看顯示位是哪一列
---前面為假,所以不執(zhí)行,只執(zhí)行union后面的select 1,2,3語(yǔ)句
---select 1,2,3發(fā)現(xiàn)顯示的3列,通過(guò)123來(lái)判斷前端頁(yè)面回顯的是那一列數(shù)據(jù)

---這里顯示的是第二三列的數(shù)據(jù)
---注意:--后門(mén)存在數(shù)據(jù),所有我們不需要--+,只需要--就行

---step 4 : 查詢(xún)基本信息
concat(...)
:這是一個(gè)MySQL函數(shù),用于將多個(gè)字符串連接起來(lái)。0x3a
:這是一個(gè)十六進(jìn)制表示的冒號(hào)字符:
,在這個(gè)查詢(xún)中,它被用作分隔符。user()
:這是一個(gè)MySQL函數(shù),用于返回當(dāng)前用戶(hù)的用戶(hù)名。database()
:這是一個(gè)MySQL函數(shù),用于返回當(dāng)前數(shù)據(jù)庫(kù)的名稱(chēng)。version()
:這是一個(gè)MySQL函數(shù),用于返回當(dāng)前MySQL服務(wù)器的版本號(hào)。@@version_compile_os
:這是一個(gè)MySQL系統(tǒng)變量,用于返回MySQL服務(wù)器的操作系統(tǒng)編譯信息
---這里就查詢(xún)到數(shù)據(jù)的敏感信息
---數(shù)據(jù)庫(kù)版本5.726;操作系統(tǒng)是Win64;用戶(hù)是root,數(shù)據(jù)庫(kù)名稱(chēng)(security)

---下面是SQL注入常用的信息收集
##MYSQL的體系結(jié)構(gòu)
INFORMATION_SCHAME庫(kù):存放整個(gè)庫(kù)的結(jié)構(gòu)
MYSQL庫(kù):存放用戶(hù)配置等數(shù)據(jù)
其他庫(kù):用戶(hù)自主創(chuàng)建

#INFORMATION_SCHAME庫(kù)
----TABLES表:提供了關(guān)于數(shù)據(jù)庫(kù)中的表的信息(包括視圖)。詳細(xì)表述了某個(gè)表屬于哪個(gè)SCHEMA,表類(lèi)型,表引擎,創(chuàng)建時(shí)間等信息
---COLUMNS表:提供了表中的列信息。詳細(xì)表述了某張表的所有列以及每個(gè)列的信息
---STATISTICS表:提供了關(guān)于表索引的信息
---USER_PRIVILEGES(用戶(hù)權(quán)限)表:關(guān)于全程權(quán)限的信息源自MYSQL.USER授權(quán)表
---SCHEMA_PRIVILEGES:給出了關(guān)于方案(數(shù)據(jù)庫(kù))權(quán)限的信息
---TABLE_PRIVILEGES(表權(quán)限);COLUMN_PRIVILEGES(列權(quán)限)
---CHARACTER_SETS(字符集)表:提供了MYSQL實(shí)例可用字符集的信息

---在information_schema.SCHEMATA的schema_name里面存在數(shù)據(jù)庫(kù)的信息
---information_schema.tables的table_name存儲(chǔ)表的信息

---ROUP_CONCAT
是一種查詢(xún)的聚合函數(shù)(注意是查詢(xún))。它用于將一列中的多個(gè)值連接成一個(gè)單獨(dú)的字符串,并且可以在查詢(xún)的結(jié)果中返回這個(gè)連接后的字符串(默認(rèn)以 , 分隔)
---查看存在的數(shù)據(jù)庫(kù),通過(guò)group_comcat將查詢(xún)的結(jié)果以,(逗號(hào))鏈接成字符串
---但是:where table_schema=database()換成where table_schema=securty會(huì)報(bào)錯(cuò)

---當(dāng)前數(shù)據(jù)庫(kù)的表
---security數(shù)據(jù)庫(kù)中存在4個(gè)表,users和emails等

---查詢(xún)users表里面的列字段(注意:要添加AND去區(qū)分才行)
---顯示user表的列名

---原理是information_schema.COLUMNS里面存在表面、數(shù)據(jù)庫(kù)名以及列名

---現(xiàn)在我們獲取到:數(shù)據(jù)庫(kù)'security',表'users',以及列:id,username,password
---構(gòu)建查詢(xún)語(yǔ)句查詢(xún)用戶(hù)名和密碼(where的列名要加'',from后門(mén)不用加)
---查詢(xún)出用戶(hù)名和密碼

---當(dāng)然,也可以使用group_concat()將顯示集中在一點(diǎn)(多個(gè)查詢(xún))
----效果如下

##基于報(bào)錯(cuò)的注入
---報(bào)錯(cuò)注入用在數(shù)據(jù)庫(kù)的錯(cuò)誤會(huì)回顯在網(wǎng)頁(yè)中的情況,如果聯(lián)合查詢(xún)不能使用,首選報(bào)錯(cuò)注入
---報(bào)錯(cuò)注入利用的是數(shù)據(jù)庫(kù)的報(bào)錯(cuò)信息得到數(shù)據(jù)庫(kù)的內(nèi)容,這里需要構(gòu)造語(yǔ)句讓數(shù)據(jù)庫(kù)報(bào)錯(cuò)
#方法1:函數(shù)參數(shù)格式錯(cuò)誤【updatexml()】
---updatexml()函數(shù)語(yǔ)法:用于在 XML 數(shù)據(jù)中更新指定路徑的元素值
xml_target
是待更新的 XML 數(shù)據(jù)字段。xpath_expr
是用于指定要更新的 XML 元素的路徑。new_value
是新的值,用于替換選定的 XML 元素
---例如,假設(shè)你有一個(gè)名為 my_table
的表,其中包含一個(gè)名為 books
的 XML 類(lèi)型列
---你可以使用 UPDATEXML()
函數(shù)來(lái)更新該列中的某個(gè) XML 元素的值,如下所示
---book是 XML 列,/book/title 是要更新的 XML元素的路徑
---把表中id為1的記錄的title節(jié)點(diǎn)的值修改為"Learning MySQL"
---構(gòu)造Xpath_string格式錯(cuò)誤,將Xpath_string傳遞成不符合格式的數(shù),mysql就會(huì)報(bào)錯(cuò)
---查詢(xún)當(dāng)前用戶(hù)和版本信息

---獲取當(dāng)前數(shù)據(jù)庫(kù)下數(shù)據(jù)表信息:
--這里只能顯示一行,所以在?table_schema='security' 后加上 limit 0,1,顯示第一行
---如果要看第二行則,limit1,1(第一行的往下一行,不包括第一行,即顯示第二行),看第三行則limit2,1,以這個(gè)方法獲取第四個(gè)表為users
---如圖所示

---爆表中的字段
---如下(查詢(xún)數(shù)據(jù)出現(xiàn)了問(wèn)題,只能出現(xiàn)前2個(gè)用戶(hù)名和密碼)

#方法2:extractvalue() 函數(shù)
---EXTRACTVALUE()
函數(shù)通常是用于從 XML 類(lèi)型的數(shù)據(jù)中提取特定值的函數(shù)
xml_document
是包含 XML 數(shù)據(jù)的字段或變量。xpath_expression
是一個(gè) XPath 表達(dá)式,用于指定您希望從 XML 中提取的特定數(shù)據(jù)
---舉例來(lái)說(shuō),如果您有一個(gè)存儲(chǔ)有以下 XML 數(shù)據(jù)的列:
---可以使用 EXTRACTVALUE()
函數(shù)來(lái)提取 <title>
元素的內(nèi)容
---查詢(xún)數(shù)據(jù)庫(kù)
---效果如下

#方法3:floor()函數(shù)
---floor()報(bào)錯(cuò)注入在MySQL版本8.0 已失效,經(jīng)過(guò)測(cè)試7.3.4nts也已失效
---FLOOR()
函數(shù)用于將一個(gè)浮點(diǎn)數(shù)或雙精度數(shù)向下取整為最接近的整數(shù)
---設(shè)有一個(gè)名為 sales
的表,包含銷(xiāo)售訂單和訂單金額。您想要對(duì)訂單金額向下取整,以便進(jìn)行更精確的金額比較或統(tǒng)計(jì)。您可以這樣使用 FLOOR()
函數(shù)
----假設(shè)有一個(gè)名為 orders
的表,包含訂單信息,如訂單編號(hào)、客戶(hù)編號(hào)和訂單金額:

---如果想要按客戶(hù)分組,并計(jì)算每個(gè)客戶(hù)的訂單總金額,可以使用以下查詢(xún):
---結(jié)果將是:
GROUP BY
子句中的列必須是SELECT
中列出的列或聚合函數(shù)的參數(shù)。在
GROUP BY
子句中,您可以使用多個(gè)列來(lái)定義分組,從而創(chuàng)建多維分組。GROUP BY
子句通常與聚合函數(shù)一起使用,以在分組上執(zhí)行匯總計(jì)算

---RAND()
函數(shù)用于生成一個(gè)介于 0 和 1 之間的隨機(jī)浮點(diǎn)數(shù)。每次調(diào)用該函數(shù)都會(huì)生成不同的隨機(jī)數(shù)。示例:從一個(gè)名為 products
的表中隨機(jī)選擇一個(gè)產(chǎn)品
---COUNT()
函數(shù)用于計(jì)算滿(mǎn)足特定條件的行數(shù),示例:統(tǒng)計(jì)一個(gè)名為 users
的表中的用戶(hù)數(shù)量
----主要報(bào)錯(cuò)原因?yàn)椋?strong>count()+rand()+group_by()導(dǎo)致主鍵重復(fù)(不是很懂)
---因?yàn)閒loor(rand(0)*2)的重復(fù)性(生成0|1),導(dǎo)致group by語(yǔ)句出錯(cuò)
---group by key的原理是循環(huán)讀取數(shù)據(jù)的每一行,將結(jié)果保存于臨時(shí)表中
---讀取每一行的key時(shí),如果key存在于臨時(shí)表中,則不在更新臨時(shí)表的數(shù)據(jù)
---如果key不在臨時(shí)表中,則在臨時(shí)表中插入key所在行的數(shù)據(jù)
#構(gòu)建報(bào)錯(cuò)注入eXp
---(select database()+from+information_schema.tables+limit+0,1)是查詢(xún)當(dāng)前數(shù)據(jù)庫(kù)名稱(chēng)
---concat(當(dāng)前數(shù)據(jù)庫(kù)名稱(chēng),foor(rand()*2)) as x將數(shù)據(jù)庫(kù)名稱(chēng)和一個(gè)隨機(jī)整數(shù)(0 或 1)連接起來(lái),如當(dāng)前數(shù)據(jù)庫(kù)concat(security,1)是:security1,命名為x
----select count(*),security1 as x from+information_schema.tables+group+by+x(查詢(xún)information_schema.tables的行數(shù)和數(shù)據(jù)庫(kù)名稱(chēng)與隨機(jī)數(shù)結(jié)合,并且按照x排序
---select 1 from x排序的數(shù)據(jù) as a x排序的數(shù)據(jù)的第一列
---原理分析(count與group by的虛擬表)
---以看出1班的記錄有4人 2班3人 3班6人 5班1人,與count(*)的結(jié)果相符合
---那么mysql在遇到select count(*) from student?group by classid;這語(yǔ)句的時(shí)候到底做了哪些操作呢?
---猜測(cè)mysql遇到該group by和count(*)時(shí)會(huì)建立一個(gè)虛擬表(實(shí)際上就是會(huì)建立虛擬表),那整個(gè)工作流程就會(huì)如下圖所示:先建立虛擬表,如下圖(其中key是主鍵,不可重復(fù)):
開(kāi)始查詢(xún)數(shù)據(jù),取數(shù)據(jù)庫(kù)數(shù)據(jù)第一條記錄,然后查看虛擬表存在classid為1的諸葛亮嗎?不存在則插入新記錄并計(jì)數(shù)為1,如果存在則count(*)字段直接加1
然后循環(huán)這樣的操作,直到查詢(xún)到最后一條記錄

---那為啥用group by floor(rand(0)*2)就會(huì)報(bào)錯(cuò)呢?
---因?yàn)閒loor(rand(0)*2)計(jì)算是有固定規(guī)律的0110110011......的順序
當(dāng)使用報(bào)錯(cuò)語(yǔ)句執(zhí)行查詢(xún)表的第一條數(shù)據(jù)時(shí),依據(jù)select floor(rand(0)*2) from student;得出的規(guī)律01101可以知道這次的值為0(這是第一次計(jì)算floor(rand(0)2)),查詢(xún)虛擬表,發(fā)現(xiàn)虛擬表沒(méi)有數(shù)據(jù),于是將0插入虛擬表key字段,
在插入key字段時(shí)floor(rand(0)*2)會(huì)再執(zhí)行一次(這是第二次計(jì)算,依據(jù)規(guī)律值為1),于是乎在key的第一個(gè)值插入的其實(shí)是1 count (*)計(jì)數(shù)為1
查詢(xún)第二條數(shù)據(jù)時(shí),依據(jù)01101規(guī)律floor(rand(0)*2)結(jié)果為1(這是第三次計(jì)算),查詢(xún)虛擬表,發(fā)現(xiàn)虛擬表已經(jīng)有了key為1的記錄,于是直接在count處進(jìn)行加1
查詢(xún)第三條數(shù)據(jù)時(shí),依據(jù)01101規(guī)律floor(rand(0)2)的結(jié)果為0(這是第四次計(jì)算),查詢(xún)虛擬表,發(fā)現(xiàn)虛擬表沒(méi)有key為0的值,在插入key字段時(shí)floor(rand(0)2)會(huì)再執(zhí)行一次(這時(shí)floor(rand(0)*2)的值成為1,這是第五次執(zhí)行),因?yàn)樵谔摂M表中已經(jīng)存在key為1的值,所以會(huì)出現(xiàn)虛擬表主鍵沖突,所以報(bào)錯(cuò)。
通過(guò)上面分析我們可以看出如果數(shù)據(jù)庫(kù)的記錄小于三條那floor報(bào)錯(cuò)也是無(wú)法利用的
所以我們查詢(xún)的表可以是information_schema.tables 這里面應(yīng)該是有大于等于三條記錄的

---總結(jié):基于 count(*)? concat() x from information_schema.tables group by x報(bào)錯(cuò)注入
---emmm有一些似懂非懂
---常見(jiàn)的報(bào)錯(cuò)注入
##基于布爾的盲注
---布爾盲注,即在頁(yè)面沒(méi)有錯(cuò)誤回顯時(shí)完成的注入攻擊。此時(shí)我們輸入的語(yǔ)句讓頁(yè)面呈現(xiàn)出兩種狀態(tài),相當(dāng)于true和false,根據(jù)這兩種狀態(tài)可以判斷我們輸入的語(yǔ)句是否查詢(xún)成功
---源碼如下
--- 我們輸入正確的id,顯示You are in .....

---輸入錯(cuò)誤的語(yǔ)句如id=1' ,或者id=-1時(shí),就什么都不顯示。這就是布爾盲注,屏幕上能得到信息不多,就是兩種狀態(tài)

---構(gòu)造判斷語(yǔ)句,根據(jù)頁(yè)面是否回顯證實(shí)猜想
---一般用到的函數(shù)ascii() 、substr() 、length(),exists()、concat()等
---ASCII()
函數(shù)返回給定字符的 ASCII 值(字符編碼)
---SUBSTR()
函數(shù)(也稱(chēng)為 SUBSTRING()
函數(shù))用于提取字符串的子串,根據(jù)起始位置和長(zhǎng)度(注意是長(zhǎng)度)來(lái)截取字符串的一部分(從1開(kāi)始)
---LENGTH()
函數(shù)用于計(jì)算字符串的長(zhǎng)度,即字符的數(shù)量。對(duì)于多字節(jié)字符集,它返回字節(jié)數(shù),而不是字符數(shù)
---EXISTS()
函數(shù)用于檢查一個(gè)子查詢(xún)是否返回任何結(jié)果。如果子查詢(xún)返回至少一行數(shù)據(jù),則 EXISTS()
返回 TRUE
,否則返回 FALSE
---CONCAT()
函數(shù)用于連接字符串,它將多個(gè)字符串連接在一起以創(chuàng)建一個(gè)更大的字符串
---常見(jiàn)的布爾盲注

---布爾盲注的思路:根據(jù)長(zhǎng)度判斷名稱(chēng),根據(jù)ASCII判斷

#1. 判斷數(shù)據(jù)庫(kù)類(lèi)型
MySQL數(shù)據(jù)庫(kù)表? ? ? information_schema.tables
access? ? ? ? ? ? ? ? ? ? ?msysobjects?
SQLServer?? ? ? ? ? ? ? sysobjects
---用下的語(yǔ)句判斷數(shù)據(jù)庫(kù),哪個(gè)頁(yè)面正常顯示,就屬于哪個(gè)數(shù)據(jù)庫(kù)
---這里可以發(fā)現(xiàn)是MYSQL數(shù)據(jù)庫(kù)

#2. 判斷當(dāng)前數(shù)據(jù)庫(kù)名
---判斷當(dāng)前數(shù)據(jù)庫(kù)的長(zhǎng)度,利用二分法
---大于7正常顯示,大于8不顯示,說(shuō)明大于7而不大于8,所以可知當(dāng)前數(shù)據(jù)庫(kù)長(zhǎng)度為8個(gè)字符

---2:判斷當(dāng)前數(shù)據(jù)庫(kù)的字符,和上面的方法一樣,利用二分法依次判斷
---由此可以判斷出當(dāng)前數(shù)據(jù)庫(kù)為 security

#3. 判斷當(dāng)前庫(kù)的表名
---猜測(cè)當(dāng)前數(shù)據(jù)庫(kù)中是否存在admin表
---這里是不存在

---判斷當(dāng)前數(shù)據(jù)庫(kù)中表的個(gè)數(shù)
---這里是4個(gè)表

---判斷每個(gè)表的長(zhǎng)度:
---第一個(gè)表的長(zhǎng)度為6,第二個(gè)也為6

---判斷每個(gè)字段名字的ascii值(可判斷出存在表 emails、referers、uagents、users ,猜測(cè)users表中最有可能存在賬戶(hù)和密碼
)
---判斷users表的列字段
判斷字段個(gè)數(shù)
判斷每個(gè)字段的長(zhǎng)度
猜每個(gè)字段的字符
---?1:判斷表中字段的個(gè)數(shù)
---2:判斷每個(gè)字段的長(zhǎng)度
---3:判斷每個(gè)字段名字的ascii值
---由此可判斷出users表中存在 id、username、password 字段
#5. 爆字段中的數(shù)據(jù)
---users中有三個(gè)字段 id 、username 、password,我們現(xiàn)在爆出每個(gè)字段的數(shù)據(jù)
---1.判斷數(shù)據(jù)的長(zhǎng)度
---例如這里是id字段的

---2.判斷數(shù)據(jù)的ASCII
---一般布爾盲注,手工去注入過(guò)于繁瑣,不建議手工注入,可以借助于工具
##基于時(shí)間的盲注
---通過(guò)觀察頁(yè)面,既沒(méi)有回顯數(shù)據(jù)庫(kù)內(nèi)容,又沒(méi)有報(bào)錯(cuò)信息也沒(méi)有布爾類(lèi)型狀態(tài),那么我們可以考慮用“絕招”--延時(shí)注入
---延時(shí)注入就是將頁(yè)面的時(shí)間線(xiàn)作為判斷依據(jù),一點(diǎn)一點(diǎn)注入出數(shù)據(jù)庫(kù)的信息。我們以第9關(guān)為例,在id=1后面加單引號(hào)或者雙引號(hào),頁(yè)面不會(huì)發(fā)生任何改變,所以我們考慮絕招延時(shí)注入
---?id=1' and sleep(5) --+ ? ?

---如果expr1的值為true,則返回expr2的值,如果expr1的值為false,則返回expr3的值
#獲取數(shù)據(jù)庫(kù)名稱(chēng)
---如果數(shù)據(jù)庫(kù)名字的第一個(gè)字符的acsii值為115,則進(jìn)行延時(shí),否則返回0即什么都不返回
?id=1' and if(ascii(substr(database(),1,1))= 115,sleep(5),0) --+
---頁(yè)面顯示延時(shí)5 秒,說(shuō)明數(shù)據(jù)庫(kù)名字第一個(gè)字母的ASCII 值是115,也就是字母s
---與盲注類(lèi)似,后面就是猜數(shù),這就是延時(shí)注入

##不安全的HTTP方法
---除標(biāo)準(zhǔn)的GET和POST方法外,HTTP請(qǐng)求還支持使用其他各種方法

---檢測(cè)服務(wù)器支持的方法:利用burp抓包
---將請(qǐng)求包的請(qǐng)求方式修改為OPTIONS,即可看到服務(wù)器允許的http請(qǐng)求方法

---此外,我們可以根據(jù)允許請(qǐng)求的信息,進(jìn)行GET/POST方法之外的傳參

##寬字節(jié)注入
---?寬字節(jié)注入準(zhǔn)確來(lái)說(shuō)不是注入手法,而是另外一種比較特殊的情況
---為了說(shuō)明寬字節(jié)注入問(wèn)題,我們以SQLi-labs 32 關(guān)為例子
---使用?id=1' 進(jìn)行測(cè)試的時(shí)候,發(fā)現(xiàn)提交的單引號(hào)會(huì)被轉(zhuǎn)義[\']

---查看源代碼:發(fā)現(xiàn)除了對(duì)于【 \ ' " 】都進(jìn)行了添加\轉(zhuǎn)義
---然后對(duì)參數(shù)設(shè)置成GBK存儲(chǔ),那么一個(gè)字符需要占2位(2Byte)
某字符的大小為一個(gè)字節(jié)時(shí),稱(chēng)其字符為窄字節(jié).
當(dāng)某字符的大小為兩個(gè)字節(jié)時(shí),稱(chēng)其字符為寬字節(jié).
所有英文默認(rèn)占一個(gè)字節(jié),漢字占兩個(gè)字節(jié)
常見(jiàn)的寬字節(jié)編碼:GB2312,GBK,GB18030,BIG5,Shift_JIS等等
---查看mysql是否為GBK編碼,且是否使用preg_replace()把單引號(hào)轉(zhuǎn)換成\'或自帶函數(shù)addslashes()進(jìn)行轉(zhuǎn)義(白盒檢測(cè)寬字節(jié)注入)
---繞過(guò)這個(gè)轉(zhuǎn)義處理,使單引號(hào)發(fā)揮作用不再被轉(zhuǎn)義,有兩個(gè)思路:
讓斜杠(\)失去作用
讓斜杠(\)消失(寬字節(jié)注入)
---轉(zhuǎn)義函數(shù)會(huì)將轉(zhuǎn)義字符 ‘\’ 轉(zhuǎn)為 %5c ,于是我們?cè)谒懊孑斎胍粋€(gè)單字符編碼與它組成一個(gè)新的多字符編碼,使得原本的轉(zhuǎn)義字符沒(méi)有發(fā)生作用
----由于在數(shù)據(jù)庫(kù)查詢(xún)前使用了GBK多字節(jié)編碼,即在漢字編碼范圍內(nèi)使用兩個(gè)字節(jié)會(huì)被編碼為一個(gè)漢字(前一個(gè)ascii碼要大于128,才到漢字的范圍)
---前面加上 %df' ?,轉(zhuǎn)義函數(shù)會(huì)將%df’改成%df\’ , 而\ 就是%5c ,即最后變成了%df%5c'
---而%df%5c在GBK中這兩個(gè)字節(jié)對(duì)應(yīng)著一個(gè)漢字 “運(yùn)” ,就是說(shuō) \ 已經(jīng)失去了作用,%df ' ,被認(rèn)為運(yùn)' ,成功消除了轉(zhuǎn)義函數(shù)的影響
---輸入 ?id=1%df',按道理來(lái)說(shuō)將轉(zhuǎn)義符吃掉了,結(jié)果應(yīng)該是 id='?運(yùn)' ?' ,為什么這里轉(zhuǎn)變成了中文后后面還有一個(gè)反斜杠了?(%27的是'的URL編碼)
---這里需要使用:火狐瀏覽器 > 菜單 > 查看 > 修復(fù)文字編碼

---通過(guò)order by發(fā)現(xiàn)存在3列

---這里發(fā)現(xiàn)是有回顯的注入

---gb2312和gbk應(yīng)該都是寬字節(jié)家族的一員,把源碼中set names修改成gb2312

---結(jié)果就不能注入了,這歸結(jié)于gb2312編碼的取值范圍,它的高位范圍是0xA1~0xF7,低位范圍是0xA1~0xFE,而\是0x5c,是不在低位范圍中的
---所以,0x5c根本不是gb2312中的編碼,所以自然也是不會(huì)被吃掉的
---因此:只要低位的范圍中含有0x5c的編碼,就可以進(jìn)行寬字符注入

---黑盒檢測(cè)寬字節(jié)注入:在注入點(diǎn)后面加%df,然后按照正常的注入流程開(kāi)始注入即可
----使用sqlmap進(jìn)行檢測(cè)注入的話(huà)也需要在注入點(diǎn)后面加%df然后再用sqlmap跑
##堆疊注入
---堆疊查詢(xún)也叫堆疊注入,在SQL中,分號(hào)(;)是用來(lái)表示一條sql語(yǔ)句的結(jié)束
---而union injection(聯(lián)合注入)也是將兩條語(yǔ)句合并在一起,兩者之間有什么區(qū)別么?
---區(qū)別就在于union 或者union all執(zhí)行的語(yǔ)句類(lèi)型是有限的,可以用來(lái)執(zhí)行查詢(xún)語(yǔ)句
----而堆疊注入可以執(zhí)行的是任意的語(yǔ)句,以sqli-labs第38關(guān)為例

---執(zhí)行堆疊注入更改密碼:?id=1';update+users+set+password='123456'+where+id=1;--+

---源代碼分析:mysqli_multi_query($con1, $sql) 導(dǎo)致的堆疊注入
---之前的代碼都是使用該函數(shù)執(zhí)行SQL語(yǔ)句:$result=mysql_query($sql);
##二次注入
---二次注入就是由于將數(shù)據(jù)存儲(chǔ)進(jìn)數(shù)據(jù)庫(kù)中時(shí)未做好過(guò)濾,先提交構(gòu)造好的特殊字符請(qǐng)求存儲(chǔ)進(jìn)數(shù)據(jù)庫(kù),然后提交第二次請(qǐng)求時(shí)與第一次提交進(jìn)數(shù)據(jù)庫(kù)中的字符發(fā)生了作用,形成了一條新的sql語(yǔ)句導(dǎo)致被執(zhí)行。以sqli-labs第24關(guān)為例
---這里注冊(cè)用戶(hù)名為 admin'#

---查看數(shù)據(jù)庫(kù),但是admin'#

----登陸后,修改密碼為cccc

---發(fā)現(xiàn)反倒是admin的密碼被修改成了ccccc,而我們注冊(cè)的用戶(hù)admin'#的密碼并沒(méi)有被修改

---漏洞原因:
1. 在進(jìn)行用戶(hù)注冊(cè)的允許存在'和#這種特殊字符(沒(méi)有過(guò)濾)
2. 在修改密碼頁(yè)面的源碼中,發(fā)現(xiàn)這里很明顯存在注入漏洞
---當(dāng)我們登錄賬號(hào)admin'#并修改密碼時(shí),這條sql語(yǔ)句就變成了如下
---#把后面的代碼都注釋掉了,所以修改了用戶(hù)admin的密碼為ccccc
##SQL注入文件讀取
---利用條件
web目錄具有寫(xiě)權(quán)限,能夠使用單引號(hào)
知道網(wǎng)站絕對(duì)路徑(根目錄,或則是根目錄往下的目錄都行)
secure_file_priv沒(méi)有具體值(在mysql/my.ini中查看,在mysql 5.6.34版本以后 secure_file_priv 的值默認(rèn)為NULL)
---secure_file_priv的值為null ,表示限制mysqld 不允許導(dǎo)入|導(dǎo)出
---當(dāng)secure_file_priv的值為/tmp/ ,表示限制mysqld 的導(dǎo)入|導(dǎo)出只能發(fā)生在/tmp/目錄下
---當(dāng)secure_file_priv的值沒(méi)有具體值時(shí),表示不對(duì)mysqld 的導(dǎo)入|導(dǎo)出做限制
---方法1:load_file('絕對(duì)路徑')讀取敏感文件
---直接select load_file()讀取

---創(chuàng)建表,將文件內(nèi)容導(dǎo)入到表內(nèi)
--方法2:load data local infile
---如果指定local關(guān)鍵詞,則表明從客戶(hù)主機(jī)讀文件;否則則文件必須位于服務(wù)器上
---使用local需要設(shè)置local_infile開(kāi)啟,該變量默認(rèn)為ON

---讀取文件到表中
----如圖所示

---方法3:system命令執(zhí)行:
---在mysql版本為5.x時(shí),除了可以使用上方法外,還可以使用系統(tǒng)命令直接讀取文件

#secure_file_priv=NULL 的繞過(guò)
---使用system執(zhí)行系統(tǒng)命令和load data infile語(yǔ)句加local選項(xiàng)繞過(guò)限制
system執(zhí)行系統(tǒng)命令適用版本為5.x。
此方法只能在本地讀取,遠(yuǎn)程連接mysql時(shí)無(wú)法使用system。
無(wú)法越權(quán)讀取。
使用load data local infile語(yǔ)句從客戶(hù)主機(jī)讀取文件。
---1.查詢(xún)secure_file_priv值:

---2.system讀寫(xiě)文件:(強(qiáng)調(diào)一下system執(zhí)行系統(tǒng)命令!)

---3.load data local infile需要將讀取的文件存儲(chǔ)在數(shù)據(jù)表中(可以讀取敏感文件)

##SQL注入文件寫(xiě)入
---方法1:into outfile()? 方法2:into dumpfile()
INTO OUTFILE
子句用于將查詢(xún)結(jié)果寫(xiě)入文本文件。這個(gè)文件可以是純文本文件,也可以包含 CSV 格式或其他文本格式(上傳一句話(huà))INTO DUMPFILE
子句用于將查詢(xún)結(jié)果寫(xiě)入二進(jìn)制文件。這個(gè)文件是 MySQL 內(nèi)部格式的二進(jìn)制文件,不適合直接讀取或編輯(UDF提權(quán))
---一句話(huà)十六進(jìn)制轉(zhuǎn)碼 “ <?php eval($_REQUEST[1]);?> ”

---編碼后,然后在最前面加上 0x
----如下我們將一句話(huà)木馬進(jìn)行十六進(jìn)制編碼后寫(xiě)入了根目錄下的outfile.php文件中

---拓展:SQL的os-shell會(huì)在指定的目錄生成了兩個(gè)文件(文件名是隨機(jī)的):
tmpbeewq.php 用來(lái)執(zhí)行系統(tǒng)命令
tmpuqvgw.php 用來(lái)上傳文件
---tmpbeewq.php的文件內(nèi)容為:通過(guò)cmd進(jìn)行request傳參
---系統(tǒng)命令執(zhí)行

---使用tmpuqvgw.php上傳文件,訪(fǎng)問(wèn)發(fā)現(xiàn)是一個(gè)文件上傳點(diǎn)

##MYSQL的萬(wàn)能密碼
---構(gòu)造永真條件,得到查詢(xún)條目(一般和or結(jié)合一起使用)
---利用數(shù)據(jù)庫(kù)的變量類(lèi)型轉(zhuǎn)換
---通常和字典一起結(jié)合,進(jìn)行fuzz

##一種隱蔽的注入
---思路就是將查詢(xún)內(nèi)容轉(zhuǎn)成數(shù)字進(jìn)行運(yùn)算

##Mysql過(guò)長(zhǎng)截?cái)?/p>
---在MySQL沒(méi)有開(kāi)啟STRICT_ALL_TABLES選項(xiàng)時(shí)(MySQL sql_mode默認(rèn)
為defalut),MySQL對(duì)插入超長(zhǎng)的值只會(huì)提示'warning'并且插入成功,而不是
error,這樣會(huì)導(dǎo)致一些截?cái)?/strong>問(wèn)題
---插入的username變成了從 'admin???????? 1' 變成了 'admin'

##sql注入各種繞過(guò)
---1.注釋符號(hào)繞過(guò)
---2.大小寫(xiě)繞過(guò):waf的正則對(duì)大小寫(xiě)不敏感的情況
---3.內(nèi)聯(lián)注釋繞過(guò)
---把一些僅在MYSQL上的語(yǔ)句放在 /!../ 中,這樣這些語(yǔ)句如果在WAF中不會(huì)檢測(cè)
---4.雙寫(xiě)繞過(guò)
---在waf中,將關(guān)鍵字select等只使用replace()函數(shù)置換為空,這時(shí)候可以使用雙寫(xiě)關(guān)鍵字繞過(guò)
---例如select
變成seleselectct
,在經(jīng)過(guò)waf的處理之后又變成select,達(dá)到繞過(guò)的要求
---5.編碼繞過(guò):如URLEncode編碼,ASCII,HEX,十六進(jìn)制編碼繞過(guò)
---6.空格繞過(guò):一般繞過(guò)空格過(guò)濾的方法有以下幾種方法來(lái)取代空格
---7.對(duì)or and xor not 繞過(guò)
---8.對(duì)等號(hào)=繞過(guò)
---9.對(duì)單引號(hào)的繞過(guò)(另外一種是寬字節(jié)繞過(guò))
---會(huì)使用到引號(hào)的地方一般是在最后的where子句,如下面的一條sql語(yǔ)句
---users的十六進(jìn)制的字符串是7573657273。那么最后的sql語(yǔ)句就變?yōu)榱耍?/p>
---10.sql注入關(guān)鍵函數(shù)替換
---12.’".md5($pass,true)."’ 登錄繞過(guò)
---很多站點(diǎn)為了安全都會(huì)利用這樣的語(yǔ)句
---md5(string,true) 函數(shù)在指定了true的時(shí)候,是返回的原始 16 字符二進(jìn)制格式
---為什么 6\xc9]\x99\xe9!r,\xf9\xedb\x1c 的布爾值是true呢
---在mysql里面,在用作布爾型判斷時(shí),以1開(kāi)頭的字符串會(huì)被當(dāng)做整型數(shù)
---要注意的是必須要有單引號(hào)括起來(lái)的,比如 password=‘xxx’ or ‘1xxxxxxxxx’,那么就相當(dāng)于password=‘xxx’ or 1 ,也就相當(dāng)于 password=‘xxx’ or true,所以返回值就是true
----這里不只是1開(kāi)頭,只要是數(shù)字開(kāi)頭都是可以的,當(dāng)然如果只有數(shù)字的話(huà),就不需要單引號(hào)
---附錄 PHP中一些常見(jiàn)的過(guò)濾方法及繞過(guò)方式
##SQL繞過(guò)安全SafedogWAF
#構(gòu)造and 1=1
---方法1:暴力破解fuzzMysql的/**/
--直接1=1被攔截

----我們這里嘗試用/*/來(lái)充當(dāng)注釋符

---選擇暴力破解,字符集就選/!*
進(jìn)行測(cè)試即可

---這里5000以下的都可以繞過(guò)(但是有一些會(huì)報(bào)錯(cuò))

---篩選正常回顯的:/*%2f**/

---方法2:內(nèi)聯(lián)注釋符號(hào),,就是/*!00000*/
這種的
---如果沒(méi)有接版本號(hào)時(shí),會(huì)直接執(zhí)行里面內(nèi)容
---當(dāng)!后面接數(shù)據(jù)庫(kù)版本號(hào)時(shí),如果自身版本號(hào)(上述例子中的5.5.53)大于等于字符數(shù)(例如上述例子中的99999和00000),就會(huì)將注釋中的內(nèi)容執(zhí)行,否則就會(huì)當(dāng)做注釋來(lái)處理
---構(gòu)造如下/*!000001*/=/*!000001*/
語(yǔ)句嘗試進(jìn)行繞過(guò)

#order by繞過(guò)
---直接order by,猜測(cè)是 order by和在一起會(huì)被攔截

---直接拿之前fuzz的/*%2f**/測(cè)試,發(fā)現(xiàn)歐克

---也可以嘗試內(nèi)聯(lián)注釋爆破
---單獨(dú)的一個(gè)union和單獨(dú)的select都是可以的,此時(shí)我就想利用它內(nèi)聯(lián)注釋字符數(shù)大于版本號(hào)時(shí)將其內(nèi)語(yǔ)句作為注釋來(lái)進(jìn)行一個(gè)繞過(guò)

---選擇暴力破解,設(shè)置0-9依次進(jìn)行爆破

---而后得到結(jié)果

#information_schema過(guò)濾繞過(guò)
---正常的爆表,但是 from 和 information_schema都會(huì)檢測(cè)
---兩個(gè)進(jìn)行分別fuzz的話(huà)比較麻煩,而且將兩者進(jìn)行一起用時(shí)可能會(huì)出現(xiàn)仍然被過(guò)濾的情況
---方法1:傳統(tǒng)的內(nèi)聯(lián)注釋? /*!*/
---因此想到了內(nèi)聯(lián)注釋這種方法,可不可以用這種方法來(lái)進(jìn)行繞過(guò)呢,我們先嘗試一下
---結(jié)果如下

----方法2:中間加注釋符再加換行,也就是/*!%23%0a*/
---本地測(cè)試
---嘗試第一種,發(fā)現(xiàn)被攔截
---嘗試第二種,也被攔截
---此時(shí)想有沒(méi)有可能是過(guò)濾了%23(對(duì)應(yīng)#),我們將%23換成--+
構(gòu)造payload如下
---這里查詢(xún)到數(shù)據(jù)庫(kù)的表

#like["%23"]
---我們知道%23是注釋符(#)的含義,那么在這里的時(shí)候,它這個(gè)語(yǔ)句到底有什么作用呢
---在查詢(xún)語(yǔ)句添加:like ["%23"]???????? //這里是一個(gè)約束條件

---構(gòu)造語(yǔ)句,那它這條語(yǔ)句就相當(dāng)于select?*?from?users
---測(cè)試聯(lián)合查詢(xún),但是檢測(cè)
---此時(shí)我們將union后的空格用換行符替代(也可以使用+替換)
---總結(jié):空格可以使用:+或者%oa替換
---總結(jié)相關(guān)繞過(guò)
---1.通過(guò) !#/- 等來(lái)暴力破解 /* a */
---2.通過(guò)內(nèi)聯(lián)注釋?zhuān)?code>/*!000001*/=/*!000001*/ ? 和? 0123456789 來(lái) fuzz /*!?? */使其無(wú)意義,以及 database/*!()*/
這種,利用bp在括號(hào)前面加上五個(gè)數(shù)字,使其有意義
---3.內(nèi)聯(lián)注釋的利用方法就是中間加注釋符再加換行:
/*!%23%0ainformation_schema.tables*/ 和
/*!%23/*%0ainformation_schema.tables*/
以及
/*!--+/*%0ainformation_schema.tables*/
---4.
select*from users where id=1 like "[%23]" union select *from users;
---通過(guò)fuzz的注釋語(yǔ)句編寫(xiě)sqlMap的tamper腳本
---思路:就是通過(guò)replace函數(shù)對(duì)應(yīng)傳統(tǒng)的SQL單個(gè)函數(shù)進(jìn)行混淆
##知識(shí)點(diǎn)總結(jié):
---1.SQL注入的原因:構(gòu)建查詢(xún)語(yǔ)句直接傳參,沒(méi)有檢測(cè)和過(guò)濾(預(yù)編譯)
---2.SQL注入:有回顯注入 和 無(wú)回顯注入
---3.有回顯注入: union注入 和 報(bào)錯(cuò)注入(updatexml()、extractvalue() 、count()+rand()+group_by()導(dǎo)致主鍵重復(fù))以及 布爾盲注
---4.無(wú)回顯注入:基于時(shí)間的注入(根據(jù)length()判斷長(zhǎng)度,根據(jù)Aecii判斷值)
---5.不安全的http方法:在數(shù)據(jù)包方法options,可以看到WEb允許的傳參方法
---6.SQL注入的方法:post/get/request
---7.SQL注入的傳參點(diǎn):x-forwarded / refrerer / cookie
---8.session(存儲(chǔ)在服務(wù)端,占用資源大) 、 cookie (sessioniD,保存在客戶(hù)端易盜取,不能跨域,存在多個(gè)服務(wù)器時(shí)需要備份)、token(服務(wù)端通過(guò)密匙檢驗(yàn)token的簽名,需要查詢(xún)數(shù)據(jù)庫(kù)獲取用戶(hù)信息) 、 jwt(json格式,在Authrization設(shè)置,用戶(hù)狀態(tài)不再存儲(chǔ)在服務(wù)端,自身包含一下會(huì)話(huà)信息,存在簽名) 的聯(lián)系與區(qū)別(類(lèi)似:記錄用戶(hù)信息的酒店電腦的Session、房卡cookie、指紋驗(yàn)證token、集成用戶(hù)會(huì)話(huà)信息加密的微型手機(jī)jwt)
---9.SQL函數(shù)出錯(cuò):堆疊注入、寬字節(jié)注入
---10、SQL注入帽子戲法:編碼注入繞過(guò)'過(guò)濾、萬(wàn)能密碼登陸、Mysql過(guò)長(zhǎng)截?cái)?、二次注入、md5(string,true) 函數(shù)繞過(guò)
---11.MySQL的讀?。簂oad_file('絕對(duì)路徑') 、 load data local infile '/tmp/1.txt' into table? 、system cat\type ,以及l(fā)oad data local infile和system繞過(guò)secure_file_pri = null
---12.MYSQL的導(dǎo)出:導(dǎo)出普通文件(上傳webshell):into outfile()? 導(dǎo)出二進(jìn)制(UDF):into dumpfile()
---13.sqlmap的os-shell,上傳2個(gè)文件,一個(gè)系統(tǒng)命令執(zhí)行,一個(gè)用于文件上傳
---14.SQL注入繞過(guò):注釋符號(hào)繞過(guò)、內(nèi)聯(lián)注釋繞過(guò)、大小寫(xiě)繞過(guò)、雙寫(xiě)繞過(guò)、編碼繞過(guò)、=<>的替換函數(shù)繞過(guò)、空格替換繞過(guò)、and or xor not替換繞過(guò)、like和=替換繞過(guò)、sleep/ascii/group_concat/等函數(shù)替換繞過(guò)、
---15.fuzz繞過(guò)安全狗:思路通過(guò)0-9和!-/*等特殊符號(hào)fuzz的/**/ 和/*!*/,通過(guò)對(duì)應(yīng)內(nèi)聯(lián)注釋的變種/*!%23%0a*/?
/*!%23/*%0a*/?
/*!--+/*%0a*/? 以及
like "[%23]"
構(gòu)造假條件
---16.sqlmap編寫(xiě)tamper腳本:通過(guò)replace函數(shù)替換傳統(tǒng)的SQL語(yǔ)句進(jìn)行混淆
參考文章:
【1】floor報(bào)錯(cuò)注入原理解析:http://t.csdn.cn/LcveO
【2】sql注入詳解(推薦):http://t.csdn.cn/tfJSW
【3】賽寧網(wǎng)安:《Web安全基礎(chǔ)1》
【4】傻傻分不清之 Cookie、Session、Token、JWT:https://juejin.cn/post/6844904034181070861
【5】授權(quán)認(rèn)證登錄之 Cookie、Session、Token、JWT 詳解:http://t.csdn.cn/vFIiH
【6】token和session與cookie詳解以及應(yīng)用原理:http://t.csdn.cn/01xNm
【7】HttpOnly 標(biāo)志——保護(hù) Cookie 免受 XSS:http://t.csdn.cn/t7ttc
【8】X-Forwarded-For sql注入:http://t.csdn.cn/5kahK
【9】MYSQL文件讀寫(xiě):http://t.csdn.cn/GVKBt
【10】sql注入各種繞過(guò):http://t.csdn.cn/xYBqS
【11】從SQL注入繞過(guò)最新安全狗WAF中學(xué)習(xí)fuzz:https://mp.weixin.qq.com/s/ChwHSHGMd3GJDpYN4z1c9Q