Juice Shop Write-up
===
---
###### tags: `JuiceShop` `CTF` `security` `Web`
解題解的莫名其妙的心路歷程,流水帳
按照題目類型整理過後的md筆記在 https://hackmd.io/@LJP/HJJHMuNlH
本篇將持續更新直到題目全部解完
(解題數, 題目數) = (28, 86)
從 `Security Policy` 開始,未加解題數,未更新整理版 Write-up
---
[TOC]
# Write-up
## Score Board
在一開始 Load 網頁時就監控 Network

發現有一個特別牛逼

看看內容是什麼

後來閒閒沒事又把其他咚咚看了

看得了關鍵的 URL: /#/score-board
連進去看看 就解開這題了
## Error Handling
不知道怎樣 莫名其妙解了 好像是隨便造成 server 的 error 都可解這題
我是有亂踩 SQL Injection 測試
## Confidential Document
進去 About Us 後,看到一個
Check out our boring terms blablabla....

進去後是長這樣

看看目錄有沒有設定讀取保護

耶 看來是沒有 ㄎㄎ

可是有保護說副檔名要是 .md 或 .pdf
就先踩踩這個 md ㄅ

就又解一題了 (????
## Easter Egg Tier 1
繼承上面
結果不知道為啥 incident-support.kdbx 可以下載!???!?

注意下方 我把它 download 下來了
用 notepad++ 開

well, 我還是用 binwalk 看看他是啥好了

結果啥都不是@@
好不重要,重要的是,我知道還是有辦法下載下來的
但後來餵狗後 發現我思考方式好像錯了
原本我想說 找找 GET .kdbx 與 GET 其他檔案 打的封包有什麼不同
後來發現更牛逼的方式

用 Burpsuit 攔截封包

把第一行的 GET 改成
```
GET /ftp/eastere.gg%00.md HTTP/1.1
```
看看
欸豆 不行

後來再查了一下,發現連 % 都要用URL Encoding
所以 % 要改成 %25

耶~~ 下載下來ㄌ
內容長醬子

那個一臉 Base64 樣的字串
```
L2d1ci9xcmlmL25lci9mYi9zaGFhbC9ndXJsL3V2cS9uYS9ybmZncmUvcnR0L2p2Z3V2YS9ndXIvcm5mZ3JlL3J0dA==
```
解碼後變成
```
/gur/qrif/ner/fb/shaal/gurl/uvq/na/rnfgre/rtt/jvguva/gur/rnfgre/rtt
```
欸先等等 已經解一題了 (不得不說 解題解的莫名其妙的
先記一下感覺,這解碼後的東東,可能可以試試看凱薩
## Forgotten Developer Backup
ㄜ 我想故技重施 把整個 FTP 看到的都載下來 就也解這題ㄌ
## Misplaced Signature File
同上
## Forgotten Sales Backup
同上
## Login Admin
再回來看看登入介面 嘗試看看 SQL Injection

回傳了...

所以若我知道 admin 的信箱是啥
而且輸入的部分沒有檔 '--' 或 '#'
就可以直接 injection 登進去
就先用看看
`' or 1=1 --`
就登入進去了
### 線索
後來登進後 隨便結個帳

得知 admin 信箱為 admin@juice-sh.op
這個 Injection 就可以改成
`admin@juice-sh.op' --`
而且結帳的動作會在 ftp 中創一個 pdf
現在有幾個新思路:
1. **如果 pdf 內容可控,可能能搞事**
2. **bonus 能改嗎**
## Forged Feedback
主要是這個頁面

用 Network 看了一下執行流程
1. 先 GET http://192.168.124.137:3000/rest/captcha/
取得一筆 json
內容為 answer, captcha, captchaId

2. POST 到 http://192.168.124.137:3000/api/Feedbacks/
POST 的欄位分別為 captcha, captchaId, comment, rating

寫了個 python 壓力測試
```python=
import requests
import json
host = "192.168.124.137:3000"
captcha_url = "http://" + host + "/rest/captcha/"
feedback_url = "http://" + host + "/api/Feedbacks/"
for i in range(50):
resp = requests.get(captcha_url)
data = resp.json()
postdata = {'UserId': 1,
'captcha': str(data['answer']),
'captchaId': data['captchaId'],
'comment': "YEEEEE",
'rating': 0
}
r = requests.post(feedback_url, data = postdata)
```
就解ㄌ
## CAPTCHA Bypass Tier 1
上面的解法 可以一起把這題解掉
## Zero Stars
硬把 Submit button 的 disable 取消
就可以發送 0 星評價ㄌ
## 線索1
不過 奇怪的是
ftp/quarantine 裡面的東西就不能用這招下載了
(可惡 他的檔名害我最想看看他長怎樣QQ)
而且其實一開始為何 .kdbx 可以下載也是個謎
## 線索2
在 main.js 挖到寶

看來是各種路徑,進看看 administration

也進進看 privacy-security

## Admin Section
用 `admin@juice-sh.op' -- ` 登入後
再進 administration
就解這題了

## Five-Star Feedback
進入 administration 後
砍掉五星評價
over
## Login Jim
用 `jim@juice-sh.op' -- ` 登入就解了 over
## Login Bender
用 `bender@juice-sh.op' -- ` 登入就解了 over
## Privacy Policy Tier 1
privacy-security 頁面進去就解這題了

## Basket Access Tier 1
觀察按下去 Your Basket 後會發送什麼封包


看到 Request URL: `http://192.168.124.141:3000/rest/basket/3`
3 是我現在登入的帳號的編號
很好,直接用 burpsuite 改改看那個數字

改成 4 看看
就拿到帳號編號為 4 的 basket 了
以下這張為沒有竄改封包 本人的basket

以下這張為帳號編號為 4 的 basket

也解這題了
## 線索3
看看按 like 能不能也寫腳本自動化
看 reviews 時 會打 GET 到
`http://192.168.124.137:3000/rest/product/產品編號/reviews`

回傳一個 json

觀察按下去後 會打 POST 到 `http://192.168.124.137:3000/rest/product/reviews`

欄位是 id, 就是要填看產品時回傳的 json 中 comment 的 _id
我猜是根據 Cookie 與 Authorization 做使用者判斷
保險起見,Host, Origin, Referer 在寫腳本時一併也要模擬
## Forged Review
既然 basket 都能亂存取到其他帳號的 basket
那麼其他功能是否也能亂存取到其他帳號的呢 e.g. 留言 按讚 結帳
留言的更改 是打以下 request:

Method 為 PATCH
API 在 /rest/product/reviews
id 推測為該留言的 id, message 為更改的內容
馬上來試改其他留言看看
先來查看隨便其中一個 product 的 review

得到id,來改看看封包,先編輯自己的留言,並用burpsuite攔截下來

把id改掉

成功爆改並解題

cool
## Basket Access Tier 2
我在想能不能隨便幫其他 user 亂加東西到購物車
首先,先觀察加進購物車的流程


看來關鍵是這個API:
POST `http://192.168.124.141:3000/api/BasketItems/`
BasketId 為要加進的 Basket
ProductId 為產品
quantity 為數量
好像很簡單 馬上攔截下來爆改看看


欸欸欸 無效 BasketId ?
那我改成登入 admin,然後硬給 BasketId 3 加咚咚試試看

結果還是一樣
那還是我刻意忽視掉的 API:

GET `http://192.168.124.141:3000/rest/basket/1`
是有作用的?
就是其實他有先打 GET rest/basket/{BasketId} (或有可能是 UserId)
再打 POST api/BasketItems
這次爆改 要連第一個GET都不放過試試看
登入admin,亂加購物車,攔截下來改:

結果一樣不行
那會不會是,真的有判斷登入者是誰?
不過經過一番研究後
發現 如果我的 basket 中已經有一個商品
再按一次加入購物車 會呼叫這個 API

這個商品在我的購物車中 原先已經有了10個
感覺是JS運算給他 +1 並打這個 Request 過去修改數量
我先測試看看 這個數量是否是能修改的


OK 看來後端沒檢查值一定要是剛好 +1 過後的
這個API是這樣
PUT `http://192.168.124.141:3000/api/BasketItems/8`
quantity 是數量
網址列中 最後的數字 8 應該是指 BasketItemId
已知 id = 8 的 BasketItem 是屬於 admin 的
現在就來登入其他帳號,並且試著透過這個API來修改這 id = 8 的 BasketItem 的 quantity
原先封包

改成

好像修改成功了


BananaJuice 變 50 個啦
但我終極目的是要幫其他 user 亂加東西進購物車餒
我在想 打 POST api/BasketItems 這API
我竄改後,他Response是401
真的要登入的話 就要幹 cookie 或是 session
~~好麻煩~~
最後放棄 Google 了 Write-up
看到結果後 還好我放棄了

原來有這種東西 長知識了!
馬上來玩玩看

Response:

成功把它加進其他user的購物車!!!
太神啦!!!
成功解掉這題
cool
## Repetitive Registration
這題有小小看了一下 Write-up,才知道方向是啥

砍掉 disable

來送一個 `password` 跟 `passwordRepeat` 欄位不一樣的 Request 試試看

可以註冊,表示重複密碼這件事情只有在前端有檢查,後端卻沒擋掉。
## ?
研究看看登入系統的 `Remember me` 這個功能
先辦了一個測試帳號
ljp@te.st
ljptest
登入一下 並且點選 Remember me
Session、Local Storage、Cookies 如下



登出後 Session、Local Storage、Cookies 如下



發現 Cookies 少了 token
Session 則少了 token 跟 bid
再登一次,但這次不要點選 Remember me



發現跟有按 Remember me 的差別在於 Local Storage 少了 email
## Password Strength
研究一下 logout
API:

Return:

其中密碼是32個16進制組成的
讓人聯想到,是否原本密碼會以某種 128bits(32 * 4) hash/encrypt 加工並且回傳
若以未登入之姿進入這個API

嗯,沒搞頭。
在其他題時就已經能登入 admin 了
看看 admin 登出後,那個看似 hash/encrypt 過的密碼長怎樣

```
email admin@juice-sh.op
password 0192023a7bbd73250516f069df18b500
```
128bits hash algorithms 有 MD5
測試看看線上破解網站

well 一個弱密碼
`admin123`
用原本的帳密登入 就解這題了
## ?
再來研究一下 forget password

發現每打一個字 都會打 Request
剛好我辦 ljp@te.st 帳號時,用了強制 enable 註冊按鈕,不用輸入安全提問。
拿另外一隻帳號有安全提問的
ljp2@te.st
ljptest

一搜尋到有此帳號,就會跳出他的安全提問。
所以可以用這個API來暴力尋找所有 email...囉?
測試看看有沒有 SQL Injection
狂踹猛踹 感覺不行
改帳密API如下

成功改密碼畫面如下


在想安全提問的字串比較會不會是以 PHP `==` 而非 `===`
如此,就可以用 PHP Type Juggling 特性來繞過檢查
要我解釋 PHP Type Juggling 是啥東,我用一張圖帶你入門

可以參考: https://php.net/manual/en/types.comparisons.php
改看看

結果導致了 500 Server 錯誤

那不要TRUE,改成 0 看看

雖然也是 500,但是 message 不一樣
回頭看改成 TRUE 後的 message,裡面提到了是爆炸在 JSON.parse()這個function,餵狗後:

那我就試試看用原本的UI送true看看
結果果然不行。
不過突然想到,所以字串比較這件事情,是在 `/juice-shop/server.js` 完成的囉? 所以不應該用 PHP Type Juggling,而是 JS
參見: https://dorey.github.io/JavaScript-Equality-Table/
不過 JS Type Comparison 的字串部分就沒什麼搞頭了。
## ?
打 Web CTF 蠻常先直接 Access 看看 robots.txt 的

well 這樣也能找到 /ftp
## Easter Egg Tier 2
繼承 Easter Egg Tier 1
當時是猜用凱薩密碼加密 ~~沒根據的亂猜~~
用網站 brute-force 了一下

```
/gur/qrif/ner/fb/shaal/gurl/uvq/na/rnfgre/rtt/jvguva/gur/rnfgre/rtt
/THE/DEVS/ARE/SO/FUNNY/THEY/HID/AN/EASTER/EGG/WITHIN/THE/EASTER/EGG
```
該不會這是一個路徑吧

**WOW靠!! 是怎樣 這顏色不正確的地球還會動**
造訪了這顆星球後,這題就解了
## Login MC SafeSearch
這題看了一下提示,聽了一下
先登入了 admin 看了一下 email 是 `mc.safesearch@juice-sh.op`
再來登看看就登入ㄌ
## ?
解了 Easter Egg Tier 2 後
的確是使用凱薩密碼
那麼 About us 頁面那些亂碼
是不是也是凱薩加密的???
解過後,看來不是,也有可能這些亂碼什麼都不是
## ?
之前有載到 package.json.bak
google search 看看裡面的每個 dependency 有沒有 vuln
我用這個搜尋格式 `node.js vuln <dependency-name>`
## Weird Crypto
這題題目寫說,透過 Contact Us 告訴 Server 一個被誤用的 Algorithm 或 Library
我是回傳 md5,不過理由是 logout 那邊會用 md5 hash 密碼並傳回來,而這件事情不應該發生
官方 write-up 對於 md5 是個答案的原因這樣寫:
> Passwords in the Users table are hashed with unsalted MD5
>
我的理解是,密碼以 unsalted MD5 hash 過再存回資料庫,但照理說應該要加一些料,讓他不是單純的 MD5
不過以黑箱的角度,能觀察到這件事情嗎?
除非我能 SQL Injection 直接 dump 整個 database 出來看,才能知道這件事吧
但其實不用,在登入頁面時,觸發 SQL 錯誤,會回傳整個 SQL Statement

那個 password 可以自行驗證一下,的確只是 md5 hash 過而已
得證資料庫中,Table Users 中的 password 為 unsalted md5 hash
## XSS Tier 0
```
<iframe src="javascript:alert(`xss`)">
```
以上打在 Track Orders 中

## XSS Tier 1
```
<iframe src="javascript:alert(`xss`)">
```
以上打在 Search 中

## XSS Tier 1.5
Set Username 中有套某些 RegEx,導致某種長相的字串會被取代
中間不小心試到一個 整個爆掉 = =

中間嘗試了很多 pattern
以下列著比較有意義的嘗試
| Before | After
| -------- | -------- |
| `<script>alert(`\``xss`\``)</script>` | `lert(xss)</script>` |
| `<</abcd>/abcd>` | `</abcd>` |
| `<<abcd>efgh>` | `<fgh>` |
| `<<a>efgh>` | `<fgh>` |
| `<<a>ascript>` | 頁面炸裂,`<script>` |
| `<<a>ascript></script>` | `<script></script>` |
| `<<a>ascript>alert(`\``xss`\``)</script>` | `<script>alert(`\``xss`\``)</script>` |
用最後一筆 pattern 就成功囉~
## XSS Tier 2
> Perform a persisted XSS attack with \<iframe src="javascript:alert(\`xss\`)"\> bypassing a client-side security mechanism.
persisted 的欄位,一開始直覺就是商品底下的 comment,但是踹 comment 沒有成功
回頭想想 persisted 的欄位、其他 User 看得到的,
J個嘛......
腦筋動到 User 本身上
- ~~User 名稱~~ (只能在自己的帳號管理頁面看到)
- User Email (admin 在主控台看得到,在 comment 中也會顯示)
User 名稱踹踹看,繼承 XSS Tier 1.5 嘗試中有些 RegEx 要閃
```
<<a>aiframe src="javascript:alert(`xss`)">
```
WoW

(噢對了 我頭像有亂改過)
然後玩到這裡才突然想到,comment 中顯示的不是 Username 而是 email
果斷放棄改 Username
改踹 Email,可是 Email 在註冊時就決定了,所以只能踩看看註冊API了
不過這次硬把 Register Button Enable 起來,也送不出 Request
好吧,只好攔截爆改了
把 Email 改成
```
<iframe src='javascript:alert(`xss`)'>
```


唉呦 可以ㄛ
試試看登入,發現糗了

登不進啦乾
but 還可以從 admin 頁面看到這個 email

一進來就 xss,表示真的成功了

阿可是這題沒解開,該不會是一定要打以下吧?
```
<iframe src="javascript:alert(`xss`)">
```
沒關係就來試試看
因為參數結構是
```
"email":"<blablabla...>"
```
要閃雙引號,所以改成如下

欸,一送出去,連驗證 xss 有沒有成功都不用就解這題了

## Security Policy
造訪一下 `http://192.168.124.144:3000/.well-known/security.txt` 就解了
此文件跟 robots.txt 作用類似
## Redirects Tier 1

觀察了一下結帳畫面下方 Merchandise
發現路徑是 `/redirect?to=一個連結`
按下去就會到那個連結
所以後端有個 redirect 是會接收 to 這個參數,並且連過去
看了一下這題的敘述
> Let us redirect you to one of our crypto currency addresses which are not promoted any longer
一個沒繼續 promoted 的加密貨幣連結(應該是斗內連結那種吧)
要從何找起哩? `main.js`?

有喔,搜到了。
連過去
`http://192.168.124.144:3000/redirect?to=https://blockchain.info/address/1AbKfgvw9psQ41NbLi8kufDQTezwG8DRZm`

就解這題了
## Deprecated Interface
登入後,在 complain 頁面
上傳檔案那邊,預設只能上傳 `*.pdf` 或 `*.zip`
硬是上傳其他副檔名的東西(我是拿 png 測) 會寫說不能上傳
雖然說到底能不能上傳又是另外一回事
但我還是先在 `main.js` 搜尋 `zip` 找到了一個區塊

發現其實能上傳 `text/xml` `application/xml`
隨便上傳一個 xml 就解這題了
## ??
