《白帽子講Web安全》7-注入攻擊

第7章 注入攻擊

  • 注入攻擊是Web安全領域中一種最爲常見的攻擊方式。
  • 注入攻擊的本質,是把用戶輸入的數據當做代碼執行。
  • 有兩個關鍵條件:
    • 用戶能夠控制輸入
    • 原本程序要執行的代碼,拼接了用戶的數據

7.1 SQL注入

  • SQL注入的典型例子
    • “拼接”的過程很重要,正是這個拼接的過程,導致了代碼的注入。
  • Web服務器開啓了錯誤回顯,會爲攻擊者提供極大的便利。
    • 錯誤回顯披露了敏感信息,對於攻擊者來說構造SQL注入的語句就可以更加得心應手。

7.1.1 盲注(Blind Injection)

  • 盲注,就是在服務器沒有錯誤回顯時完成的注入攻擊。
  • 最常見的盲注驗證方法是,構造簡單的條件語句,根據返回頁面是否變化,來判斷SQL語句是否得到執行。
    • 爲了進一步確認注入是否存在,攻擊者還必須再次驗證則個過程。
      • 因爲一些處理邏輯或安全功能,在攻擊者構造異常請求時,也可能會導致頁面返回不正常。
    • 服務器雖然關閉了錯誤回顯,但是攻擊者通過簡單的條件判斷,再對比頁面返回結果的差異,就可以判斷出SQL注入漏洞是否存在。
    • 以上就是盲注的工作原理
  • 舉例:
    • and 1=1返回正常界面
    • and 1=2無法返回查詢到的數據
    • 由此可判斷漏洞存在

7.1.2 Timing Attack

  • MySQL一例;
    • 利用BENCHMARK()函數,可以讓同一個函數執行若干次,使得結果返回的時間比平時要長;通過時間長短的變化,可以判斷出注入語句是否執行成功。
  • 在不同的數據庫中,都有着類似於BENCHMARK()的函數,可以被Timing Attack所利用。

7.2 數據庫攻擊技巧

SQL注入是基於數據庫的一種攻擊。不同的數據庫有着不同的功能、不同的語法和函數。因此針對不同的數據庫,SQL注入的技巧也有所不同

7.2.1 常見的攻擊技巧

  1. 猜解出數據庫的對應版本
  2. 利用union select來分別確認表名admin是否存在,列名passwd是否存在
  3. 想要猜解出username和passwd具體的值,可以通過判斷字符的範圍,一步步讀出來。
  4. 在注入攻擊的過程中,常常會用到一些讀寫文件的技巧。
    • 寫入文件的技巧,經常被用於導出一個Webshell,爲攻擊者的進一步攻擊做鋪墊。

7.2.2 命令執行

  1. 在MySQL中,除了可以通過導出Webshell間接地執行命令外,還可以利用“用戶自定義函數”的技巧,即UDF(User-Defined Function)來執行命令。
  2. 例子
    • 例1:在MySQL 4的服務器上,通過UDF執行系統命令。
    • 例2:通過lib_mysqludf_sys提供的幾個函數執行系統命令,其中最主要的函數是sys_eval()和sys_exec()。
      • 自動化注入工具sqlmap已經集成了此功能。
    • 例3:在MS SQL Server中,可以直接使用存儲過程“xp_cmdshelll”執行系統命令
    • 例4:在Oracle數據庫中,如果服務器同時還有Java環境,當SQL注入後可以執行多語句的情況下,可以在Oracle中創建Java的存儲過程執行系統命令。
  3. 一般來說,在數據庫中執行系統命令,要求具有較高的權限。在數據庫加固時,可以參考官方文檔給出的安全指導文檔。
  4. 在建立數據庫賬戶時應該遵循“最小權限則”,應該避免給Web應用使用數據庫的管理員權限。

7.2.3 攻擊存儲過程

  1. 存儲過程與UDF很像。但存儲過程必須使用CALL或者EXECUTE來執行。在MS SQL Server和Oracle數據庫中,都有大量的存儲過程。在注入攻擊中,存儲過程將爲攻擊者提供很大的便利。
  2. 舉例:MS SQL Server中的存儲過程“xp_cmdshell”
    • 其他可以操作註冊表的存儲過程
    • 其他可以攻擊的存儲過程
  3. 除了利用存儲過程直接攻擊外,存儲過程本身也可能會存在注入漏洞。

7.2.4 編碼問題

  1. 基於字符集的注入攻擊技巧
    • 注入攻擊中常常會用到單引號、雙引號等特殊字符。在應用中,開發者爲了安全,經常使用轉義字符“\”來轉義這些特殊字符。但當數據庫使用了“寬字符集”時,可能會產生一些意想不到的漏洞。
    • 要解決這種問題,需要統一數據庫、操作系統、Web應用所使用的字符集,以避免各層對字符的理解存在差異。統一設置爲UTF-8是一個很好的方法。
    • 基於字符集的攻擊不侷限於SQL注入,凡是會解析數據的地方都可能存在此問題。
      • XSS攻擊時,由於瀏覽器與服務器返回的字符編碼不同看,也可能會存在字符集攻擊。
      • 解決方法:在HTML界面的<meta>標籤中指定當前頁面的charset。
    • 如果因爲種種原因無法統一字符編碼,則需要單獨實現一個用於過濾或轉義的安全函數,在其中需要考慮到字符的可能範圍。

7.2.5 SQL Column Truncation

  • MySQL的配置選項中,有一個sql_mode選項。
  • 當其設置爲default時,即沒有開啓STRICT_ALL_TABLES選項時,MySQL對於用戶插入的超長值只會題是warning,而不是error(error則插入不成功),這可能會導致發生一些“截斷”問題。
  • 舉例:
    • 插入一個名稱爲admin[55 space chars]x的用戶到數據庫中,由於截斷,就會有第二個admin用戶存在於數據庫中。

7.3 正確地防禦SQL注入

  1. 從防禦的角度來看,要做的事有兩件:
    • 找到所有的SQL注入漏洞
    • 修補這些漏洞。
  2. 基於黑名單來過濾字符的方法,都或多或少存在一些問題。

7.3.1 使用預編譯語句

  1. 一般來說,防禦SQL注入的最佳方式,就是使用預編譯語句,綁定變量。
    • 使用預編譯的SQL語句,SQL語句的語義不會發生改變。
    • PHP中可以綁定變量
    • 在不同的語言中,都有着使用預編譯語句的方法。

7.3.2 使用存儲過程

  1. 使用存儲過程的效果和使用預編譯語句類似,其區別就是存儲過程需要先將SQL語句定義在數據庫中。
  2. 注意,存儲過程中也可能會存在注入問題,因此應該儘量避免在存儲過程中使用動態的SQL語句。
    • 如果無法避免,則應該使用嚴格的輸入過濾或者是編碼函數來處理用戶的輸入數據。
  3. 有時候,無法使用預編譯語句或存儲過程,該怎麼辦?
    • 只能在此回到過濾和編碼等方法上來。

7.3.3 檢查數據類型

  • 檢查輸入數據的數據類型,在很大程度上可以對抗SQL注入。
  • 舉例
    • 限制輸入數據的類型只能爲integer
    • 輸入郵箱時,必須嚴格按照郵箱的格式
    • 輸入時間、日期,必須嚴格按照時間、日期的格式
  • 數據類型檢查並非萬能
    • 比如,需要用戶提交字符串,如一段短文,則需要其他方法防範SQL注入

7.3.4 使用安全函數

  1. 我們需要一個足夠安全的編碼函數。數據廠商往往都對此做出了“指導”。
  2. 例如:
    • MySQL中,按照書中舉例編碼字符
    • 也可以參考OWASP ESAPI中的實現。
  3. 從數據庫自身的角度來說,應該使用最小權限原則,避免Web應用直接使用root、dbowner等高權限賬戶直接連接數據庫。
    • 有多個不同的應用在使用同一個數據庫,應該爲每個應用分配不同的賬戶。
    • Web應用使用的數據庫行乎,不應該有創建自定義函數、操作本地文件的權限。

7.4 其他注入攻擊

這些注入攻擊都有相同的特點,就是應用違背了“數據與代碼分離”的原則。

7.4.1 XML注入

  1. XML是一種常用的標記語言,通過標籤對數據進行結構化表示。
  2. XML與HTML都是SGML(Standard Generalized Markup Language,標準通用標記語言)。
  3. XML注入,也需要滿足注入攻擊的兩大條件:
    • 用戶能控制數據的輸入
    • 程序拼湊了數據
  4. 修補方案
    • 與HTML注入類似
    • 對用戶輸入數據中包含的“語言本身的保留字符”進行轉義即可

7.4.2 代碼注入

  1. 代碼注入與命令注入往往都是由一些不安全的函數或者方法引起的,其中的典型代表就是eval()。
  2. 存在代碼注入漏洞的地方,與“後門”沒有區別。
  3. 在Java中也可以實施代碼注入,比如利用Java的腳本引擎ScriptEngineManager
  4. JSP的動態include也能導致代碼注入。
    • 嚴格來說,PHPO、JSP的動態include(文件包含漏洞)導致的代碼執行,都可以算是一種代碼注入。
  5. 代碼注入多見於腳本語言,有時候代碼注入可以造成命令注入(Command Injection)。
    • 例如,PHP,C語言
  6. 對抗代碼注入、命令注入時
    • 需要禁用eval()、system()等可以執行命令的函數。
    • 如果一定要使用這些函數,則需要對用戶的輸入數據進行處理。
    • 在PHP/JSP中避免動態include遠程文件,或者安全地處理它。
  7. 代碼注入往往是由於不安全的編程習慣所造成的,危險函數應該儘量避免在開發中使用,可以在開發規範中明確指出哪些函數是禁止使用的。

7.4.3 CRLF注入

  1. CRLF
    • CR,Carriage Return(ASCII 13, \r)
    • LF,Line Feed(ASCIi 10, \n)
    • \r\n用於表示換行,十六進制編碼如下:
      • \r 0x0d
      • \n 0x0a
  2. log注入
    • 在日誌文件中僞造admin用戶的登錄失敗記錄
  3. 注入HTTP頭
    • 又稱“HTTP Response Splitting”
    • HTTP協議中,HTTP頭是通過“\r\n”來分隔的。
    • 如果服務器端沒有過濾“\r\n”,而又把用戶輸入的數據放在HTPP頭中,則有可能導致安全隱患。
      • 通過兩次CRLF注入到HTTP Body,完成XSS攻擊
      • 注入一個HTTP頭,比如注入一個Link頭,關閉IE8的XSS Filter功能。
  4. 可以說它的危害甚至比XSS還要大,因爲它破壞了HTTP協議的完整性。
  5. 對抗CRLF的方法非常簡單,只需要處理好“\r”、“\n”這兩個保留字符即可,尤其是那些使用“換行符”作爲分隔符的完整性。

7.5 小結

  1. 注入攻擊是應用違背了“數據與代碼分離原則”導致的結果。
  2. 注入攻擊的兩個條件:
    • 用戶能夠控制數據的輸入
    • 代碼拼湊了用戶輸入的數據,把數據當作代碼執行了
  3. 對抗注入攻擊
    • 牢記“數據與代碼分離原則”
    • 在“拼湊”發生的地方進行安全檢查,就能避免此類問題
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章