# Web Security
[TOC]
payload 可以參考這個網站
[Web CTF Cheatsheet](https://github.com/w181496/Web-CTF-Cheatsheet)
## What is a website?
一般我們看到的網頁是由 HTML、JavaScript 以及 CSS 組成的
當你到一個網站時,實際上是向一個伺服器發起 HTTP 請求,並以此獲得資源
而瀏覽器則會 render 得到的 HTML 及 CSS
並執行裡面的 JavaScript
### Frontend
在網頁開發中,我們會將使用者可以看到並且操作的部分稱為前端,這部分是由 HTML/CSS/JavaScript 組成的
透過瀏覽器的開發者工具(F12),我們可以看到原始碼
* 主控台(console),可以執行 JavaScript
* 來源(source),此頁面所用到的所有 HTML、CSS、JavaScript 或是圖片等
* 網路(network),紀錄此頁面所發起的所有連線
* 應用程式(application),存放此頁面的 Cookie 以及 LocalStorage
### HTML
HTML(超文本標記語言) 是用來表示網頁架構的語言,本身雖然不具有任何執行的能力,但是可以在裡面擺放 `<script>` 標籤來讓瀏覽器執行 JavaScript
~~但加上 CSS 可以達成圖靈完備~~
同時一些如 `<img>` 或是 `<iframe>` 等標籤會讓瀏覽器向伺服器發起 HTTP 請求來取得圖片或是其他資源
瀏覽器會以 [DOM tree](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction) 的形式來對應 HTML 的元素,使得 CSS 可以透過標籤、class 或是 id 來對元素附加 style(樣式)
同時也讓 JavaScript 可以來操控 HTML 的元素
```xml
<!DOCTYPE html>
<html>
<head>
<title>My Link</title>
<link rel=stylesheet type="text/css" href="index.css">
</head>
<body>
<img class="bg-img" src="https://myimage.com">
<div class="content">
<div class="box">
hihi
</div>
</div>
</body>
<script>
console.log(123)
</script>
<style>
.content {
width: 300px;
justify-content: center;
display: flex;
align-items: center;
}
</style>
</html>
```
每一個標籤都可以含有多個屬性,而某些標籤的某些屬性可以註冊事件,使得在觸發某些事件時執行 JavaScript(按下按鈕、送出表單)
```xml
<button class="btn" onclick='alert(1)'>
<img id="my-img" src='https://imgur.com/123' onload='console.log(1)'>
```
### JavaScript

JavaScript 是一個~~自由的語言~~被設計用來操縱網頁的語言,可以透過瀏覽器提供的 API 來操縱網頁的 DOM 或是發起 HTTP 請求
他也是一個以 prototype (原型鍊)來實作物件導向的弱型別語言
而以 [Google v8](https://v8.dev/) 來實作的 [Node.js](https://nodejs.org/en) 也使得它可以脫離瀏覽器的環境來運作
[MDN JavaScript](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript)
```javascript
// 選取一個 id 為 my-element 的元素,並將他的文字設為 hello
const a = document.querySelector('#my-element')
a.innerText = 'hello'
```
#### fetch
https://developer.mozilla.org/zh-TW/docs/Web/API/Fetch_API
最早的時候,瀏覽器要發送請求只能透過網址或是發送表單,所以每次更動頁面都必須重新載入
後來 Google 開始使用 [AJAX](https://developer.mozilla.org/zh-TW/docs/Web/Guide/AJAX),使得網頁可以透過 JavaScript 來發送請求,並以此來動態的改變網頁
fetch 為現今瀏覽器提供的 API,比起以往的 `XMLHttpRequest` 介面好用許多
```javascript
fetch('https://example.com',{
method:'POST',
header:{
'Content-Type':'application/json'
},
body: JSON.stringify(myobject)
})
```
#### cookie
cookie 常被用來存放一些資訊,例如
* Session
* Token
* 狀態
* 追蹤資訊
而前端可以使用 JavaScript 來設置 cookie,或是使用 devtool 的 application 頁面來設置
```javascript
document.cookie = "username=Asuna";
//or
//Chrome api
cookieStore.set('admin',true)
```
後端要設置 cookie 則是透過 `Set-Cookie` 標頭
```
HTTP/1.1 200 OK
...
Set-Cookie: session=deadbeef;
...
```
而瀏覽器發起請求時,便會將這些 cookie 帶入 `Cookie` 標頭裡
```
GET / HTTP/1.1
...
Cookie: session=deadbeef;
...
```
cookie 同時也有不同的屬性,來決定瀏覽器要怎麼處理 cookie
* HttpOnly 前端的 JavaScript 不能操作這個 cookie
* Domain cookie 存放的網域
* Expire cookie 什麼時候過期
* Secure 此 cookie 只能在 https 下存放
[MDN Cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
### Backend
負責處理請求以及請求的資料,可以由幾乎所有程式語言來撰寫
現代常用的 HTTP Server 或是框架都有針對不同路徑執行不同函式的功能
* Nginx 現代主流的 Web Server
* Apache 老牌的 Web Server
* Flask Python 的後端框架
* Express Node.js 的後端框架
* PHP ~~世界上最好的語言~~
## HTTP
HTTP 請求是前後端溝通的手段
一個 HTTP 請求會分成三個部分
* request line 這會在請求的第一行,說明此請求的方法(method)、路徑(path)以及版本
* request header 請求的標頭,可包含許多訊息,包括 Cookie 或是使用者使用的客戶端
* request body 請求攜帶的資料,不一定需要
一個 HTTP 請求的範例長這樣
```
GET / HTTP/1.1
Host: google.com
User-Agent: curl/7.68.0
Accept: */*
```
request line 以及 header 的每一行會用 `\r\n` 來隔開
### Request Line
```
GET / HTTP/1.1
^ ^ ^
method path version
```
一般來說請求方法只會用到 `GET` 跟 `POST`
分別代表獲取以及上傳的意思
其他還有較不常用的 `PUT`、`OPTION`、`HEAD` 等
這部分其實完全可以自定義,只要後端有對應的 route 即可
有些開發者會依照 REST 的設計方法讓每個 API 的 method 都是更為精確的 `PUT` 或是 `OPTION` 之類的
可參考 : https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
請求路徑則是 URL 的一部分
但是還可以使用 `?` 來提供參數
`/foo/bar?dead=beef&id=123`
若是在請求路徑裡含有特殊字元(如 ascii 0x00~0x19 或是中文)
我們可以使用 url encoding,也就是以 hex 來表達該字元
`/%41%42%43`
### Request Header
```
Host: google.com
User-Agent: curl/7.68.0
Accept: */*
```
HTTP 標頭會攜帶封包的資訊,像是我們常聽說的 Cookie 之類的
而因為 HTTP 是無狀態的 (Stateless),所以 header 所攜帶的訊息非常重要
### HTTP Response
```
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Fri, 15 Sep 2023 02:34:04 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 22511
```
response 跟 request 的格式差不多相同
不同的點在於第一行會改而顯示 HTTP 狀態碼
像是 200 就是 OK,而我們常見的 404 就是無此頁面的意思
同時 request 跟 response 所使用的 header 也不盡相同
[MDN HTTP Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)
[MDN HTTP Status](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status)
### Lab
使用 Curl 或是 BurpSuite 來觀察 HTTP 封包
`curl https://www.google.com -v`
## Rookie Vulnerable
### SQLI(SQL Injection)
SQL 是非常常使用的資料庫語言,可說是現代資料庫的基石
他可以用來操作關聯式資料庫(RDBMS),也就是用表格型式來儲存資料的資料庫
這些表格被稱為 table
每一個欄位被稱為 column
而每一項資料被稱為 row
|id|user |password|
|--|------|-------|
|1 |Heathcliff|047463c04d2c12909efbbc71fb170efd
|2 |Kirito|8699314d319802ef792b7babac9da58a|
```sql
select * from users where user = 'Kirito' and password = '8699314d319802ef792b7babac9da58a'
-- ^ ^ ^
-- columns table condition
```
在 condition 的部分我們通常會讓使用者來提供輸入
但若沒檢查用戶輸入會發生非常嚴重的後果
假如使用者在 user 欄位輸入 `Healthcliff' or '1'='1';--`
則查詢的語句就會變成
```sql
select * from users where user = 'Healthcliff' or '1'='1';--' and password = 'something'
```
#### Union base
但是上述語句只有在登入的情況才能使用
我們能否透過其他查詢語句來將整個資料庫 dump 下來?
這時候就可以用到 union 關鍵字
假如我們有下列的 table
| id | book | author | rating |
| --- | ----------------- |:---------------- | ------ |
| 1 | Lord of the Rings | J. R. R. Tolkien | 5 |
| 2 | Sword Art Online | Kawahara Reki | 3 |
而有一個 query 長這樣
```sql
select * from books where book = 'Lord of the Rings'
```
union 便可以用來查詢另一個 table,並且跟原本的結果合併
**要注意的是**透過 union 查詢的另一個 select 語句的 column 數必須與原本的一樣
若不足的可以補 null、字串或是數字
有些 DBMS 除了 column 數要相同外,還會要求資料型態相同,可以使用 null 就好
```sql
select * from books where book = 'Lord of the Rings' union select *,null from users;
```
#### Boolean base
更常的一些情況是 query 查詢的結果不會直接顯示在頁面上
但是仍然能透過其他資訊來判斷是否查詢成功,像是登入成功與否之類的
而這時我們便可以使用 SQLI 裡的 if else 並搭配二分搜來將要搜尋的東西一個字一個字的爆出來
```sql
-- i = 1
-- mid = (l+r)/2
-- 用{}包起來不是合法 sql 語句,這樣弄只是避免看起來太長
... and if((select ascii(substring(password,{i},{i}))<={mid} from users where user='admin'),true,false);--
```
#### Metadata table
每種 SQL 資料庫都會有專門的 table 去存放所有 table 以及 column 的名稱
若你不知道你要撈的目標表名,則需要先從這些 table 將目標表明撈出來
* sqlite_master SQLite
* information_schema MySQL
* pg_database pg_tables PostgreSQL
#### How to prevent?
最佳解法就是使用 prepare statement 或是使用 ORM,來避免直接串接字串
自己去過濾關鍵字,可能會有漏網之魚,還不如用別人提供的
```javascript
db.get('SELECT * FROM Users WHERE username = ?', username)
```
### XSS(Cross-Site Scripting)
前端使用 HTML、CSS 以及 JavaScript 來 render 頁面
但若直接把用戶的輸入不加以過濾便插入至頁面上,就會造成 XSS(Cross-Site Scripting)
雖然 XSS 的形式有分很多種,但最常見的成因只有兩個
* 前端的 JS 使用到 innerHTML 來植入用戶提供的內容
* 後端在 render 模板時沒有 escape 用戶提供的內容
而受到 XSS 攻擊的網站會使得攻擊者能獲得其他用戶的 Cookie,造成能獲得其他使用者的敏感資訊,或是成為管理員
而若受到 XSS 攻擊的是使用 Electron 的應用程式,則也有機會能造成 RCE
一些常見的 payload
* `<img src=# onerror="alert(1)">`
* `<svg/onload="alert(1)">`
* `<button onclick="alert(1)">`
#### CORS(Cross-Origin Resource Sharing)
瀏覽器的同源政策 (Same-Origin Policy) 會限制網頁只能去存取同個網頁的資源,除了路徑以外,協定(protocol)、網域(domain)以及埠口(port)都必須相同
而如果要去獲取別的網站的資源(api、CSS、JavaScript),瀏覽器提供一個叫做 CORS 的機制
本質上就是只要該網站的回應包含 CORS 標頭就允許存取
`Access-Control-Allow-Origin: *`
[MDN CORS](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/CORS)
#### CSP
#### How to prevent?
通常會將 HTML 轉譯成 [HTML Entity](https://developer.mozilla.org/en-US/docs/Glossary/Entity) 來將 HTML 轉成純字串顯示
不要隨意使用 innerHTML
若是要有多功能的文字編輯(如用戶發文可以附圖片、超連結等)
一定要使用如 [Dompurify](https://github.com/cure53/DOMPurify) 等過濾函式庫來處理用戶的輸入
若是有其他需求,如使用到 iframe 的 srcdoc 或是讓用戶可以上傳並檢視 SVG 等,也需要去使用 [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) 做好防護
### Miss Configuration
#### Information Leak
有些檔案可能會帶有敏感資訊,同時網站開發者沒限制存取
* robots.txt
* .git
* .htaccess
* .well-known
* package.json
## Other Vulnerable
### Arbitrary Read/Write
有些網站可能會將使用者的輸入拼接到路徑之上,並且讀取該路徑的檔案
這時候便可以讀取諸如原始碼或是環境變數等檔案
```py
open(f'/tmp/userdir/{file}')
# file = ../../etc/passwd
open('/tmp/userdir/../../etc/passwd')
```
* /etc/passwd
* /proc/self/cmdline
* /proc/self/fd/[0-9]
* /proc/self/environ
* /etc/nginx/nginx.conf
* /etc/apache2/httpd.conf
* ...
#### Local File Inclusion
在一些特殊的語言如 PHP,可以在執行時去動態 include 外部的腳本
這時若能改變所 include 的檔案,則就有辦法達成 RCE
* [include](https://www.php.net/manual/en/function.include.php)
* [require](https://www.php.net/manual/en/function.require.php)
### Template Injection
### SSRF
### Prototype Pollution
### Unsafe Deserialization
# Docker
在打 Web 題的時候,出題者通常會提供 Dockerfile 來讓參賽者可以在自己的電腦把環境架起來
Docker 其實就是一種 KVM 的應用,可以透過設定檔來定義虛擬容器的內容
容器化的技術可以讓服務在更精簡的環境運行,同時擴展性以及開關速度都比以往的虛擬機器還要好

Docker 的架構主要有三個
* Dockerfile 決定 Image 如何去構建
* Image 決定 Container 開啟時會長怎樣,可以直接從官方倉庫抓下來別人構建好的
* Container 運作時的實體,每個 container 都互相獨立
## Install Docker
Ubuntu 可以直接輸入 `sudo apt-get install docker`
Windows 及 MacOS 則是下載 [Docker Desktop](https://www.docker.com/products/docker-desktop)
MacOS 還可以使用 [OrbStack](https://orbstack.dev/) 作為替代方案
## Dockerfile
Docker 可以使用 Dockerfile 來做好一個虛擬容器的配置
包括安裝套件或是複製原始碼
同時他也是一層一層的結構,也就是說每個 Dockerfile 都會是基於另一個 Dockerfile 去做配置
```dockerfile
# 基於 node 20 alpine 的環境
FROM node:20-alpine
# 將工作目錄設成容器裡的 /app
WORKDIR /app
# 將檔案及目錄複製過去
COPY package.json ./
COPY package-lock.json ./
COPY views ./views
COPY public ./public
COPY app.js ./
# 安裝套件
RUN npm install
# 容器啟動時要使用的指令
CMD ["node", "app.js"]
```
當寫好 Dockerfile 時,我們可以使用 `docker build -t .` 來將對應的 image 建構出來
而 Dockerfile 的內容則會在 build 時執行完畢
接著便能使用 `docker run` 來啟動容器
```sh
$ docker run -d --name web -p 8080:80 image
# -d 背景執行
# -p port 轉發
# image 你的 image 的名稱
```
## Docker compose
如果每次重新啟動容器都要重打參數會很麻煩,雖然可以自行寫 shell script 來自動化
但是官方提供了 docker compose 來使用 yaml 設定檔一次管理多個容器
docker-compose.yml
```yaml
# 版本
version: "3.7"
services:
minecraft:
# 要使用的 image
image: itzg/minecraft-server
# 容器名稱
container_name: minecraft
# 容器裡的環境變數
environment:
EULA: "TRUE"
TYPE: "FABRIC"
ENABLE_COMMAND_BLOCK: "true"
ONLINE_MODE: "false"
DIFFICULTY: "hard"
VERSION: "1.20"
VIEW_DISTANCE: 14
TZ: "Asia/Taipei"
# 使容器裡的目錄或檔案映射到 host 的目錄或是檔案,讓容器關閉後資料仍然能留存
volumes:
- ./minecraft:/data
# port 轉發
port:
- "25565:25565"
```
要啟動便在同一個目錄底下輸入
```sh
$ sudo docker compose up -d
```
如果是舊版則是
```sh
$ sudo docker-compose up -d
```