# SQL期中報告 [toc] --- java -jar webgoat-server-8.2.1.jar --server.port=8080 --server.address=localhost • http://localhost:8080/WebGoat --- {PAGE 1} ## 前言 今天我要介紹的主題是SQL Injection (中文翻譯是SQL注入) 那為何選擇這個主題呢? 身為資管系的學生,在大二大多都有修過資料庫和網頁程式設計。 也學過了SQL語言,它是管理和查詢資料庫的關鍵工具。我們了解了資料庫是如何運作的,以及如何儲存和擷取數據。 而SQL注入攻擊能讓駭客對我們的資料庫造成資料外洩和損害。 這讓我開始思考,我們在課堂上學習到的技能,要如何應對現實世界中的資安挑戰。 --- {PAGE 2} ## 什麼是SQL Injection? SQL Injection是指利用Web應用程式中的漏洞,將惡意SQL語句注入到資料庫中,進而竊取、修改或刪除敏感資料。這種攻擊方式已經成為網際網路上最常見的攻擊技巧之一。 --- {PAGE 3} SQL injection和我們的生活並沒有那麼有距離 舉例來說: 大家可能習慣使用社群軟體來分享生活,如果這些社交平台受到SQL注入攻擊, 駭客就可以**竊取你的個人資料和帳號**,並在網絡上以你的名義進行不當的活動。 > EX:像是盜用你的facebook帳號賣airpod來詐騙XD 那相信大家也經常網購吧~不管是在蝦皮、淘寶、momo,或是 「哇~ 這個韓國海苔看起來好好吃! 上百萬種,韓國美國,人氣選品 價格超划算~又免運!」 恩對,在網購平台上,有些人會綁定信用卡。那如果網購平台受到SQL注入攻擊呢? 我們可能會被竊取信用卡資訊,導致金融訊息和個資的洩漏。 --- {PAGE 4} ## SQL injection (intro) ### 特殊字符 - 註釋 ->把後半段需要閉合的引號  ``` SELECT * FROM users WHERE name = 'admin' -- AND pass = 'pass' 等同於 SELECT * FROM users WHERE name = 'admin' ``` --- {PAGE 5} - 分號 ->把前面的SQL語句中斷,後面可以連接新的語句  ``` SELECT * FROM users; DROP TABLE users; 等同於 SELECT * FROM users; DROP TABLE users; ``` --- {PAGE 6} ### 簡單介紹SQL注入方式 假設一個網頁的欄位,它讓使用者輸入名字就可以查看使用者資訊。 使用者的輸入會被傳送到伺服器並插入 SQL 查詢中,然後由 SQL interpreter進行處理並回傳資料。  **變數userName會儲存使用者的輸入,並將其「注入」到查詢中。** > 如果輸入是 Smith,則查詢名為 Smith 的使用者的所有資料 SQL注入的用途不止能讀取使用者的資料。 以下是其他範例: 1. 運用類似'1'='1'的true/false判斷式  將傳回使用者表中的所有項目 > 輸入被傳送到伺服器並插入 SQL 查詢中,然後由 SQL interpreter進行處理。 2. 用分號或注釋功能去新增新sql式在後方  連結多個 SQL 命令,可以 DROP users table並刪除audit_log table中的所有項目 --- ### 情境題 1. 假如今天有兩張Table: 分別是 **user_data** 和 **user_system_data**  2. 想從表格中select所有資料,以及拿到Dave的帳號密碼  有兩種做法,可以使用 UNION,也可以在原本的輸入後面加上新的 SQl 語句。 3. 在輸入框輸入: `abc'; SELECT * from user_system_data--` 就可以得到user_system_data的資料了  > 建議可以利用瀏覽器的開發者工具,把想好的sql句直接填入HTML裡邏輯會比較好懂。 ## 特殊語法 ### Union 「Union」可以把將兩個 SQL 語句的結果合併起來,但有一個限制是: **兩個 SQL 語句所產生的欄位需要是同樣的資料種類**  ### 情境題 1. 假如今天有兩張table,要我們想用union的方式爆出Dave的密碼  我們需要用到user_system_data的資料,這裡有兩點需要注意: 1. 使用UNION SELECT 需要**前後兩張table的總列數相符**。 2. user_data(第一個table)有7個欄位,所以接在union後的SQL式也必須要列出7個欄位。 3. 由於題目中已知user_system_data(第二個table)只有4個欄位,剩下的3個欄位要跟第一張表格(user_data)的最後三個欄位資料種類。 > 一開始寫的是`UNION SELECT`,結果可以看到報錯為: `duplicate column name in derived table`。 是因為兩張表中都有userid列,而第二張表的userid還是主鍵。 所以我們需要在*前指定表名。 2. 用單引號’把last_name給閉合,後面用union select來查詢: `d' and 1=2 UNION SELECT user_system_data.*,NULL,NULL,NULL from user_system_data --`  -> 取得table的內容和Dave的密碼,成功爆出資料 --- ## Blind (盲注) 前面提到的狀況中,網頁會返回完整的信息,包括**資料庫的報錯還有完整的查詢結果**。 但在現實的大多數情況下,**資料庫只會回傳查詢是成功還是失敗**,獲得的資訊減少會增加了SQL注入的難度。 接著來介紹一種叫做Blind SQL Injection(中文翻譯為盲注)的技術 之前介紹的方式是: 透過利用系統因為讀取到錯誤SQL式的報錯,來決定要寫什麼SQL語法 盲注則不一樣: **系統不會有明確的錯誤訊息,要靠觀察伺服器有沒有回應來判斷SQL式是不是有用。** ### 情境題 1. 有個網站的網址是「hxxps://my-shop.com?article=4」,可以先推測這個網址的含意是要顯示資料庫中第4篇文章。 2. 在資料庫進行查詢的SQL式可能會是: `SELECT * from articles where article_id = 4` 3. 要如何驗證呢? 透過把參數改為「4 AND 1 = 1」,看看網頁的反應。 這時資料庫中的SQL式可能就變成: `SELECT * from articles where article_id = 4 AND 1 = 1` 4. 如果發現顯示的頁面跟原本的一樣,就可以進行下一個驗證步驟。 如果顯示的頁面不一樣(ex:出現404 Page not found或其他的錯誤),就代表這個網頁沒辦法應用盲注的技術。 5. 把參數改為「4 AND 1 = 2」看網頁的反應 這時資料庫中的SQL式可能就變成如下: `SELECT * from articles where article_id = 4 AND 1 = 2` 如果發現頁面資料無法正常顯示,就可以更加肯定這裡是可以應用盲注的技術,因為上述的SQL語法的邏輯是錯誤的,所以就會導致頁面無法正常顯示。 ## Demo :::info 目標: 以 tom 帳戶登錄系統,目前我們並不知道 tom帳號名稱 以及 table 的結構。 有兩個頁面: 1.登入頁面(需要輸入使用者名稱和密碼) 2.註冊頁面(需要使用者名稱、密碼、信箱)   ::: ### 方法 1. 先看登入頁面能不能直接登入 回傳:No results matched. Try Again.  看了登入帳號頁面的sourse code,發現使用預編譯(prepared statement)功能,也使用了問號對sql語句進行了佔位(place holder),因此是這個登入介面比是較安全的。  查看註冊帳號頁面的source code,發現輸入的參數是直接嵌入到SQL語句中,比較不安全。 這裡有個式子判斷新用戶輸入的名字是否是tom,可以猜測SQL注入點就是這裡,因為**正常情況下是不會有參數+單引號的判斷句**  2. 用tom的名字註冊帳號 註冊後系統回傳"tom已經存在",說明題目中的tom帳號的使用者名稱就是tom。 3. 在輸入用戶名的欄位填入`tom' and 1=1 --` 系統表示"帳戶已經存在": `User {0} already exists please try to register with a different username.`  > 這個{0}看著相當可疑…感覺可能是個能夠利用的點。 4. 測試` tom' and 1=2 --` 系統表示"帳戶成功建立",說明這部分應該是一個注入點。 因為後端的判斷條件執行了 and 1=1和and 1=2,1=2的條件不成立,所以後端認為這個帳戶原本是不存在的。 5. 使用BurpSuite截取請求 配置burpsuite的代理後訪問webgoat的這道題目,然後在proxy的歷史裡面找到這個request並儲存到本機 6. 用 sqlmap 進行注入的測試。 sqlmap顯示了"基於true/false的盲注"和"time-base的盲注"。 > 雖然可以使用SQL Map這類的工具來幫助我們完成一些自動化的SQL注入操作,但對於SQL語句基本原理的理解可以最大化發揮工具的價值。 7. 透過true/false來猜密碼 我需要得知密碼的"欄位"和"長度" 密碼的欄位可以猜一下,從burp suite在註冊頁面捕捉到的內容,能看到密碼欄位是password_reg,猜測密碼是password  先註冊一個使用者叫zhouss的帳號,然後猜測密碼欄位名稱為password 在使用者名稱的輸入框填入: `username_reg=zhouss' or length(password)=1--` > 這裡用length來判斷密碼長度只是為了驗證password這個欄位是否存在,因為使用的是or來連接兩個條件,所以只要使用者名稱是tom,那麼最終後端的語句執行結果都會是true  系統回傳"帳號已經存在",說明 password這個欄位 是存在的。 如果不存在,伺服器會回傳沒找到這個欄位而報錯。 ### Final `' UPDATE sql_challenge_users SET password = '12345';--`   --- 8. 猜測tom帳號的密碼長度 填入了: `username_reg=tom' and length(password)=1--` 因為需要判斷tom的密碼長度,所以換成and來連接,也就是兩個條件都得滿足,伺服器才會回傳帳號已經存在。 先從長度為1開始嘗試輸入,伺服器回傳"成功創建帳號"。 也就是代表length(password)=1這個條件失敗,需要繼續增加password欄位長度,直到回傳"帳戶已經存在"。  試到密碼長度為23的時候終於顯示"帳號已經存在"  9. 爆破密碼 在知道了tom的密碼長度後,就可以透過substring函數來產生true/false判斷 傳回的結果如果顯示"帳戶已經存在",代表密碼對應位置的值判斷成功,反之判斷失敗。 先來簡單測試,在輸入框填入: `substring(password,1,1)='1' --` 對password的第一位開始的第一個字元進行判斷,考慮到後面爆破的時候可能會有字母或特殊字,所以判斷的值用引號,實測能匹配數字1。  > Burp Suite 是一款暴力破解密碼的軟體,運用不斷的嘗試來獲取密碼 這裡加入了兩個變量: 一個是密碼的位置從1-23,一個是密碼位置對應的值。 attack type選擇爆破模式,也就是每個變量的payload都會和其他payload進行逐一匹配  第一個payload配置數字1-23  第二個payload配置大小寫的英文字母和常見的特殊字符  為了查看結果方便,配置grep match過濾匹配,把response裡包含 "already exists"的結果標記 > 因為傳回的結果顯示"帳戶已經存在",代表密碼對應位置的值判斷成功。  11. 對結果進行排序 開始攻擊以後終於顯示結果了,對grep的結果進行排序  剩下的就是依序拿答案了,抄出來的結果是 thisisasecretfortomonly 測試登入: 用tom的帳號密碼登入成功!  --- #### 結語: 基本上,現在已經有很多好用的研究工具了,採用好的自動化工具對避免SQL注入攻擊很有效。 我認為深入學習SQL injection,並了解如何預防注入攻擊,有助於我們在寫程式的時候不容易產生相關漏洞,能夠保護我們的個人信息。 我的報告到這邊差不多結束了,期末我會在介紹有關防禦SQL注入的部分,希望大家有因此對SQL注入的原理更加了解。 --- #### 參考資料 https://ithelp.ithome.com.tw/articles/10207336 https://youtu.be/yusJWttsD5o?si=801gq8wxI0nU6T8P https://www.hacksplaining.com/exercises/sql-injection#/first-login-attempt https://blog.csdn.net/weixin_44023403/article/details/112550339 https://hackmd.io/@foxo-tw/slides/%2Fp%2FHkdGf-naE
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up