HTTP 參數污染(HTTP Parameter Pollution,HPP)是「操弄網站對請求參數的處理方式」的攻擊。攻擊者注入額外參數,網站若信任這些值,就可能產生非預期行為。HPP 漏洞發生在伺服器端或客戶端,後者在瀏覽器中即可看到效果,前者則必須靠輸入與輸出反推伺服器邏輯——因此 HPP 比起其他漏洞,更需要實驗與耐心。
伺服器端 HPP(Server-Side HPP)#
概念#
伺服器端 HPP 的目標是送出意料之外的參數組合,讓伺服器內部程式回傳意外結果。攻擊者看不到伺服器程式碼,只能從輸入與輸出反推。
範例:銀行轉帳的相同參數重複#
假設銀行用 URL 參數驅動轉帳:
https://www.bank.com/transfer?from=12345&to=67890&amount=5000from、to、amount 分別代表來源、目的、金額。如果攻擊者送出兩個 from:
https://www.bank.com/transfer?from=12345&to=67890&amount=5000&from=ABCDEF伺服器處理多個同名參數的策略因平台而異——一旦驗證階段用第一個 from、執行階段用第二個 from,攻擊者就能轉走別人帳戶的錢。
各伺服器處理重複參數的方式不同:
- PHP / Apache:取最後一個
- Apache Tomcat:取第一個
- ASP / IIS:全部接收
詳細差異可參考 Luca Carettoni 與 Stefano di Paolo 在 AppSec EU 09 的整理。
範例:陣列順序錯位#
如果伺服器用陣列接收參數,順序就成了關鍵。考慮一段 Ruby 後端:
user.account = 12345
def prepare_transfer(params)
params << user.account
transfer_money(params)
end
def transfer_money(params)
to = params[0]
amount = params[1]
from = params[2]
transfer(to, amount, from)
end正常 URL:
https://www.bank.com/transfer?to=67890&amount=5000進入 prepare_transfer 時 params = [67890, 5000],再被附加 user.account 後變成 [67890, 5000, 12345],最後正確轉帳。
但攻擊者多塞一個 from:
https://www.bank.com/transfer?to=67890&amount=5000&from=ABCDEFparams 會先是 [67890, 5000, "ABCDEF"],再加上 user.account 變成 [67890, 5000, "ABCDEF", 12345]。transfer_money 取 params[2] 當作 from——拿到的是 ABCDEF,不是受害者的帳戶。
同樣的命名參數,如果後端程式以陣列順序而非鍵名取值,就有機會被錯位污染。
客戶端 HPP(Client-Side HPP)#
客戶端 HPP 注入額外參數,目標是影響瀏覽器端最終生成的 URL 或內容。
範例:URL 編碼夾帶額外參數#
考慮以下伺服器程式碼:
<? $val=htmlspecialchars($_GET['par'],ENT_QUOTES); ?>
<a href="/page.php?action=view&par='.<?=$val?>.'">View Me!</a>
攻擊者帶入:
http://host/page.php?par=123%26action=edit關鍵:%26 是 & 的 URL 編碼。
- 第一次解析時
%26仍是字串,par的整個值是"123%26action=edit",action=edit不會被切成獨立參數 htmlspecialchars把%26轉成 HTML 實體&- 最終輸出:
<a href="/page.php?action=view&par=123&action=edit">
於是 href 多了一個 action=edit,當使用者點擊時,這個被偷渡進來的參數就可能改變應用行為。
三個案例分別在 HackerOne 與 Twitter,雖然都涉及 URL 參數,但沒有兩個是用同樣方法找到或同樣根因——再次證明 HPP 需要全方位測試。
案例 1:HackerOne Social Sharing Buttons#
- 難度:低
- URL:
https://hackerone.com/blog/introducing-signal-and-impact/- 回報來源:https://hackerone.com/reports/105953/ ↗
- 回報日期:2015-12-18
- 獎金:$500
漏洞描述#
HackerOne 部落格底部提供「分享到 Twitter / Facebook」按鈕,會把當前文章 URL 帶入分享連結。研究者發現可在文章 URL 後追加額外參數:
https://hackerone.com/blog/introducing-signal&u=https://vk.com/durov被 HackerOne 拼接後,產生的 Facebook 分享連結變成:
https://www.facebook.com/sharer.php?u=https://hackerone.com/blog/introducing-signal?&u=https://vk.com/durovFacebook 對重複的 u 參數取最後一個,分享出去的連結最終指向 vk.com/durov。
同樣手法也能竄改 Twitter 預設文字:
https://hackerone.com/blog/introducing-signal?&u=https://vk.com/durov&text=another_site:https://vk.com/durovTakeaways#
凡是「網站接收使用者內容、轉接到第三方服務、並用當前 URL 動態產生輸出」的場景,都值得測試 HPP。
案例 2:Twitter Unsubscribe Notifications#
- 難度:低
- URL:
https://www.twitter.com/- 回報來源:https://blog.mert.ninja/twitter-hpp-vulnerability/ ↗
- 回報日期:2015-08-23
- 獎金:$700
漏洞描述#
研究者 Mert Tasci 取消電子報訂閱時注意到一條像這樣的 URL(簡化過):
https://twitter.com/i/u?iid=F6542&uid=1134885524&nid=22+26&sig=647192e86e28fb6691db2502c5ef6cf3xxxuid 是當前登入帳戶的 ID。直接把 uid 換成另一名使用者的 ID 並無效——Twitter 用 sig 驗證 URL 沒被竄改。
但 Tasci 並未放棄。他塞進第二個 uid:
https://twitter.com/i/u?iid=F6542&uid=2321301342&uid=1134885524&nid=22+26&sig=647192e86e28fb6691db2502c5ef6cf3xxx成功——他取消了另一名使用者的電子報訂閱。
漏洞精彩之處在於:Twitter 用第一個
uid對sig做簽章驗證(驗證通過),卻用第二個uid執行取消訂閱動作。
Takeaways#
- 耐心:第一次嘗試(換
uid)失敗時若放棄,就拿不到 $700 - 盯住自動遞增整數欄位:例如
UID,這類值常與權限相關(第 16 章 IDOR 會延伸討論)
案例 3:Twitter Web Intents#
- 難度:低
- URL:
https://twitter.com/- 回報來源:https://ericrafaloff.com/parameter-tampering-attack-on-twitter-web-intents/ ↗
- 回報日期:2015 年 11 月
- 獎金:未公開
背景#
Twitter Web Intents 提供彈出視窗,讓非 Twitter 網站上的使用者快速完成 follow、like、retweet、tweet 等操作。透過 GET 請求觸發:
https://twitter.com/intent/intentType?parameter_name=parameterValue
Figure 3-1: 早期版本的 Twitter Web Intents——使用者可在不離開頁面的情況下與 Twitter 內容互動
漏洞描述#
研究者 Eric Rafaloff 發現四種 intent 全部受 HPP 影響。以 follow 為例,傳入兩個 screen_name:
https://twitter.com/intent/follow?screen_name=twitter&screen_name=ericrtest3伺服器產生的 HTML 形如:
<form
class="follow"
id="follow_btn_form"
action="/intent/follow?screen_name=ericrtest3"
method="post"
>
<input type="hidden" name="authenticity_token" value="..." />
<input type="hidden" name="screen_name" value="twitter" />
<input type="hidden" name="profile_id" value="783214" />
<button class="button" type="submit"><b></b><strong>Follow</strong></button>
</form>關鍵不一致:
- 顯示用第一個
screen_name(twitter,畫面看起來在追蹤官方帳號) - 送出時
form action用第二個screen_name(ericrtest3)
於是使用者「以為自己 follow 了 Twitter 官方」,實際上 follow 了攻擊者帳號。
同樣手法可在 like intent 注入無關的 screen_name:
https://twitter.com/intent/like?tweet_id=6616252302978211845&screen_name=ericrtest3點 Like 後,旁邊的 Follow 按鈕指向的卻是 ericrtest3。
Takeaways#
一個地方有 HPP,常常代表系統性問題。發現一處後值得在整個平台尋找類似行為。
章末總結#
- HPP 的風險取決於後端如何處理多個同名參數,以及污染後的參數被用在何處
- 因為看不到伺服器程式碼,找這類漏洞比其他漏洞更需要試錯
- 建議的測試切入點:
- 內容會被分享到第三方服務(社群網站)的「分享連結」
- 帶有 ID、UID 等遞增整數的 URL
- 同時包含「驗證簽章」與「實際動作」的 URL,可嘗試插入第二份參數讓兩端各自取值
- 找到一處 HPP,就把整個平台再掃一遍——往往不是孤例