前前言
本人的個人博客網(wǎng)址:www.QmSharing.space澎怒,所有的文章都可以在里面找到爆土,歡迎各位大佬前來參觀并留下寶貴的建議哮幢,大家一起學(xué)習(xí)一起成長 :-)
本題目標(biāo)
本題好像是 v1.10 版本新加入的, 因此網(wǎng)上的分析文章并不是很多.
CSP(Content Security Policy) 是一種用來防止 XSS 攻擊的手段, 通過在頭部添加 Content-Security-Policy 的相關(guān)參數(shù), 來限制未知(不信任)來源的 javascript 的執(zhí)行從而防止 XSS 攻擊. 本題并不是要介紹這種技術(shù)有什么漏洞, 而且本題所導(dǎo)致的 XSS 攻擊都是因?yàn)殚_發(fā)者的不正當(dāng)配置所致. 所以本題僅僅讓你能成功執(zhí)行一個彈框的 XSS 注入即可.
Low
因?yàn)?CSP 主要是客戶端進(jìn)行防御, 所以服務(wù)端的核心代碼應(yīng)該是沒有變的(除了 CSP 頭):
# 主要就是設(shè)置 HTTP 頭部信息, 后面就不粘代碼了, 直接分析頭部即可
$headerCSP = "Content-Security-Policy: script-src 'self' https://pastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;"; // allows js from self, pastebin.com, jquery and google analytics.
header($headerCSP);
這個頭部信息的具體含義可以參考這個 參考文檔, 這里直接理解就是能從 https://pastebin.com 等網(wǎng)站加載 javascript, 你可以試試從自己的搭建的服務(wù)器中加載 javascript, 結(jié)果如下:
可以明顯的看到, 這個 javascript 的加載被 blocked 了(估計 Firefox 的插件 No-script 也是基于這個的). 既然不能從未指定的源加載 javascript, 那我可以從它信任的源, 比如 https://pastebin.com 中來加載. 可能有人不知道這個網(wǎng)站是什么, 我開始也不知道的, 但你打開就會發(fā)現(xiàn)這就是個共享粘貼板, 允許你粘貼任何文本內(nèi)容.
那我們就可以在這個粘貼板網(wǎng)站中寫入一段惡意 javascript, 比如 alert("hacked")
, 然后, 通過該網(wǎng)站的 raw 形式(原生)來顯示, 我當(dāng)時生成的網(wǎng)址是這個 惡意 javascript, 然后你就可以在 URL 欄中輸入這個網(wǎng)址, 讓瀏覽器將這個遠(yuǎn)程加載文本當(dāng)作 javascript 來執(zhí)行. Boom! 成功注入, 結(jié)果你們自己去試哈.
Medium
通過瀏覽器抓包可以看到:
script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';
這次使用了兩個新的參數(shù)(self 不算哈). 其中 'unsafe-inline' 代表可以執(zhí)行諸如 onclick 等事件或 script 標(biāo)簽內(nèi)的內(nèi)容這類 javascript, 而后者就是指如果你要使用 script 標(biāo)簽加載 javascript, 你需要指明其 nonce 值, 比如 <script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert('hacked')</script>
這個就能正常加載, 從而造成 XSS 注入.
但需要注意的是, 如果你測試 <img src=hacked onerror=alert('hacked')>
這類在標(biāo)簽內(nèi)執(zhí)行的 javascript, 會得到如下結(jié)果:
錯誤提示告訴我們, 因?yàn)轭^部指定了 nonce 值, 所以自動忽略了 'unsafe-inline' 這個參數(shù). 因此可以判斷這兩個參數(shù)是不能共存的, 而且如果共存, 后者是會覆蓋前者的.
這里還有個有意思的地方, 你看那個 nonce 的值很明顯是個 base64 編碼, 我就無聊去解碼了一下, 嗯... 原文"TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA="轉(zhuǎn)換后是 "Never going to give you up", 還是給出題者 666. 不過呢, 正確的防御方式下的 nonce 值不應(yīng)該是個固定值, 而是應(yīng)該是個隨機(jī)生成的值, 這樣才能真正達(dá)到防止 XSS 的目的.
High
這個級別已經(jīng)沒有輸入框了, 不過題目已經(jīng)給了足夠多的提示. 首先先看一下 CSP 頭, 發(fā)現(xiàn)只有 script-src 'self';
, 看來只允許本界面加載的 javascript 執(zhí)行. 然后研究了一下這個點(diǎn)擊顯示答案的邏輯(邏輯在 source/high.js
里), 大致如下: 點(diǎn)擊按鈕 -> js 生成一個 script 標(biāo)簽(src 指向 source/jsonp.php?callback=solveNum), 并把它加入到 DOM 中 -> js 中定義了一個 solveNum 的函數(shù) -> 因此 script 標(biāo)簽會把遠(yuǎn)程加載的 solveSum({"answer":"15"})
當(dāng)作 js 代碼執(zhí)行, 而這個形式正好就是調(diào)用了 solveSum 函數(shù), 然后這個函數(shù)就會在界面適當(dāng)?shù)奈恢脤懭氪鸢?
本來嘛, 應(yīng)該是沒辦法修改在服務(wù)器的 jsonp.php 文件的(除非結(jié)合別的漏洞, 拿 shell 后修改). 然而, 我后來在查看服務(wù)端源碼的時候發(fā)現(xiàn)了這個:
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
" . $_POST['include'] . "
";
}
# 剩余的顯示代碼
666, 竟然還偷偷接收 include 參數(shù)(不清楚是不是作者復(fù)用了之前 Medium 的代碼). 總之, 這肯定能作為一個注入點(diǎn), 我開始打算用簡單粗暴的 <script>alert('hacked')</script>
來搞定的, 誰知道, 這種是屬于 'unsafe-inline' 形式的, 所以被限制執(zhí)行了. 嗯... 既然如此的話, 那我就利用 src 吧.
if (array_key_exists ("callback", $_GET)) {
$callback = $_GET['callback'];
} else {
return "";
}
$outp = array ("answer" => "15");
# 這個 callback 可以被控制的
echo $callback . "(".json_encode($outp).")";
這個即使你不看源碼, 你做幾個測試也會發(fā)現(xiàn), 那個 callback 參數(shù)可以被操控以生成任何你想要得到的結(jié)果, 比如 alert, 因此可以構(gòu)造 Payload: <script src="source/jsonp.php?callback=alert('hacked');"></script>
, 并把這個當(dāng)做 include 參數(shù)傳給界面就 Boom! 注入成功!
Impossible
該級別主要還是修復(fù)了 callback 參數(shù)可被控制問題(畢竟這是問題根源):
$outp = array ("answer" => "15");
# 寫死的話, 就沒辦法被控制了
echo "solveSum (".json_encode($outp).")";