[WAF攻防]從WAF攻防角度重看sql注入

從WAF攻防角度重看sql注入

攻防都是在對抗中逐步提升的,所以如果想,且攻得明白,就必須對有深刻的了解

sql注入的大體流程

  1. Fuzz測試找到注入點
  2. 對注入點進行過濾檢測,及WAF繞過
  3. 構建payload,對資料庫進行脫褲,甚至getshell

sql注入流程分析

  • sql注入,最初沒有防護的時候,就只要能成功拼接sql語句,就可以進行sql注入
    image
  • 先對注入點進行判斷,是字元型還是整型。

對於整型一般是直接拼接sql,而對於字元型的一般是先要閉合掉單引號,或者雙引號,還有一種存在利用邏輯漏洞閉合sql語句的,等下一起分析了

所以這裡先通過加 ” ‘ 進行判斷,得出是字元型注入。

引號閉合方式:1. 利用注釋符 mysql中一般用的是–+和#,不過如果是GET傳參,需要將#先進行URL編碼,不然會被當作url中的hash結構,–+中的+是隨意的,只要–後跟一個字元就可以,如果利用的是GET傳參,也需要將空格進行url編碼,不然會被瀏覽器自動剔除最末 尾空白符的操作給疑惑到,一般用– -更方便,反正最後一個-也會被當作sql注釋後的內容
2. 利用邏輯,例如 id=’ or {payload} or ‘1’=’1 這樣的話最後拼接到資料庫中就是select * from user where id=’$id’>select * from user where id=” or {payload} or ‘1’=’1′;從而完成引號閉合 3 其他:利用邏輯將’轉義 select * from user where id=’$username’ and pass=’$password’;>select * from user where id=’asdasd’ and pass=’ or 1=1#’;在這裡,由於第二個引號被轉義,所以第一個引號和第三個引號和中間字元組成一個字元串,也就是id最終是{ asdasd’ and pass= },最終得以繞過

防護1:1. 對於整形數據,php中可以用intval()將輸入流先進行轉換 2.對於字元型參數,可以用合理利用addslashes()對其中的單引號和雙引號進行轉義,合理是要求對任何進入程式的輸入流都進行先轉義,包括資料庫,防止二次注入 3.合理利用編碼,防止編碼注入

  • 確認注入點後,確定欄位數
    image

確定欄位數為3

  • 開始構造payload
    • id=0′ union select 1,2,3–+
      確認回顯位置為2,3參數的位置
    • id=0′ union select 1,database(),3–+
      獲取資料庫名
    • id=0′ union select 1,database(),(select group_concat(column_name) from information_schema.columns where table_name=’users’ and table_schema=database())–+
      利用group_concat一下獲取該資料庫中的所有的表,當然也可以利用limit一個個來,這裡最好限定下庫名,防止其他庫中也有同名的表
    • id=0′ union select 1,(select group_concat(username) from users),(select group_concat(password) from users)–+
      最終成功獲取同表中的所有數據
      image

如果想獲取其他表或者其他庫中的文件,修改下參數即可,其他庫中的表 {庫名.表名},而用戶都放在mysql資料庫中,如果有許可權可以任意篡改,甚至還可以寫shell,操作多多。

防護2:過濾關鍵字,如select|information|table|column|from|union等,但黑名單總是存在被繞過的可能

猜想:可不可以利用mysql支援hex編碼,先將sql寫入到文件,再執行?

sql注入防護-採用預編譯

從上面的流程可以看出,最基本的防護就是採用黑名單模式過濾關鍵字元或字元串,但黑名單往往隨著sql語法的支援增加,黑名單的效果會下降。

預編譯其實是先通過了編譯,建立了AST語法樹,所以後續的操作只是像空格中填充東西而不影響句子的執行結構。

用了預編譯就一定安全了嘛?你真的會用預編譯嘛?

  1. 錯誤順序導致無效的預編譯
$query = "select balabala from table1 where 1=$_GET[『id』]";
$row = $db->prepare($query);
$row->execute();

可以看到在這裡,先把變數拼接到sql語句,再進行預編譯是無效的,這樣對應的惡意語法樹還是解析了

  1. 模擬預編譯導致的寬位元組注入
    image
    PDO中有一個PDO::ATTR_EMULATE_PREPARES配置,用來控制是模擬預編譯,其實就是對sql的特殊字元進行了轉義
    如果該數據的編碼是GBK的話,就有可能造成sql的寬位元組注入

其實這裡我也沒明白為什麼會搞一個模擬預編譯,為了性能?

  1. 支援多行程式碼執行的預編譯導致的sql注入(沒有過濾分號
Set @x=0x31
Prepare a from 「select balabala from table1 where 1=?」
Execute a using @x

由於sql中默認支援hex編碼,所以將目標程式碼進行hex編碼繞過檢測,然後就可以在預編譯時期造成sql執行。

對於動態程式碼,或支援動態程式碼的操作或者函數一定要小心。動態程式碼字元串就可以利用奇奇怪怪的編碼造成千人千面的效果,在終點再轉換為爭取的程式碼執行,以此來繞過防護

sql注入繞WAF

其實上面說的那麼多,很多就是軟體WAF的工作原理。
所以這裡繞過WAF,也就相當於是對上面的總結。

  • WAF檢測關鍵字元串和關鍵字元
    對於關鍵字元串,一般採用替代的方法,sql中有很多不為人知的用法,例如limit 1,1 如果對逗號進行了繞過還可以採用 limit 1 offset 1,如果對limit進行了限制,還可以用from to來,前提是對sql語法足夠熟悉。而對於空格,一般直接用Fuzz測試即可

  • 過濾了關鍵的庫名
    例如上面用的information庫,其實sys中的某些表或視圖也保存了相關的表名和欄位名,也就可以用來做替換

  • 利用WAF檢測點來繞過
    例如某WAF只對GET請求的參數進行了sql檢測,而同時後端用的是$_REQUEST來獲取參數,這個時候就可以用POST進行繞過,應該$_REQUEST可以也可以獲取POST參數,但WAF不會對POST參數進行檢測

  • 利用WAF的多重解碼,進行編碼注入
    有些WAF為了防止編碼注入,會先檢測一部分,然後對輸入流進行解碼,再檢測另一部分,利用解碼,可以繞過第一部分的惡意字元。除非是對解碼前後都進行了相同的完全的過濾。但這樣處理編碼,無異於對性能是個巨大的浪費。

  • 利用WAF只檢測部分數據
    有些WAF為了性能,不可能對全部的數據進行檢測,例如上傳一個大的文件,不可能對文件進行全面檢測,所以往往只檢測前幾十個位元組,而我們可以把惡意數據放在檢測區間的後面,來繞過檢測。

  • HTTP協議兼容性:HTTP Body多樣性

  • HPP參數污染
    HPP是HTTP Parameter Pollution的縮寫,意為HTTP參數污染。

在ASPX中,有一個比較特殊的HPP特性,當GET/POST/COOKIE同時提交的參數id,服務端接收參數id的順序GET,POST,COOKIE,中間通過逗號鏈接,於是就有了這個idea。

這些都是應對於軟體WAF的,還有雲WAF和硬體WAF,回頭再說