###HPP参数污染的定义
HTTP Parameter Pollution简称HPP,所以有的人也称之为“HPP参数污染”,HPP是一种注入型的漏洞,攻击者通过在HTTP请求中插入特定的参数来发起攻击,如果Web应用中存在这样的漏洞,可以被攻击者利用来进行客户端或者服务器端的攻击。
###HPP参数污染的原理
首先讲下HTTP的参数处理,在跟服务器进行交互的过程中,客户端往往会在GET/POST请求里面带上参数:
POST /foo HTTP/1.1
User-Agent: Mozilla/5.0
Host: Host
Accept: */*
Content-Length: 19
POST /foo HTTP/1.1
User-Agent: Mozilla/5.0
Host: Host
Accept: */*
Content-Length: 19
如上面的例子所示,这些参数会以名称-值对的形势出现,通常在一个请求中,同样名称的参数只会出现一次。但是在HTTP协议中是允许同样名称的参数出现多次的。大家可以在下面给出的W3School链接上试试看:
http://www.w3schools.com/html/tryit.asp?filename=tryhtml_form_checkbox
但是针对同样名称的参数出现多次的情况,不同的服务器的处理方式会不一样,比如看下面的3个例子:
http://www.google.com/search?q=italy&q=china(谷歌同时处理前后2个相同参数)
http://search.yahoo.com/search?p=italy&p=china(雅虎处理后面的参数)
https://www.baidu.com /search?p=italy&p=china(百度处理第一个参数)
如果同时提供2个搜索的关键字参数给Google,那么Google会对2个参数都进行查询;百度会处理第一参数的值,但是Yahoo则不一样,它只会处理后面一个参数。下面这个表简单列举了一些常见的Web服务器对同样名称的参数出现多次的处理方式:
Web服务器 | 参数获取函数 | 获取到的参数 |
php/Apache | $_GET(“par”) | last |
JSP/Tomcat | Request.getParameter(“par”) | First |
Perl(CGI)/Apache | Param(“par”) | First |
Python/Apache | getvalue(“par”) | All (List) |
ASP/IIS | Request.QueryString(“par”) | All (comma-delimited string) |
假设这个URL:http://www.xxxx.com/search.php?id=110&id=911
百度会理解成让百度搜索:110 #选择了第一个参数,放弃了第二个参数。
雅虎会理解成让雅虎搜索:911 #选择了第二个参数,放弃了第一个参数。
谷歌会理解成让谷歌搜索:110 911 #两个参数同时选择。
主要的就是这三种情况了。这主要是源于,不同的网站对处理参数的处理方式不同。
###HPP参数污染攻击方式
HTTP 参数污染,或者 HPP,在网站接受用户输入,将其用于生成发往其它系统的 HTTP 请求,并且不校验用户输出的时候发生。它以两种方式产生,通过服务器(后端)或者通过客户端
1.对客户端的攻击
比如有这样一个网站,用来给其他人在2个候选人之间投票,这个网站的URL和代码是这样的:
Url : http://host/election.jsp?poll_id=4568
Link1: <a href="vote.jsp?poll_id=4568&candidate=zhang">为张三投票</a>
Link2: <a href="vote.jsp?poll_id=4568&candidate=li">为李四投票</a>
因为种种原因,这个页面里面用于投票的链接实现的方式如下:如果这时候恶意攻击者生成了如下的一个URL发给投票人:
http_://host/election.jsp?poll_id=4568&candidate=zhang
那么最终在页面的内容会是:
Url : http://host/election.jsp?poll_id=4568&candidate=zhang
Link1: <a href="vote.jsp?poll_id=4568&candidate=zhang&candidate=zhang">为张三投票</a>
Link2: <a href="vote.jsp?poll_id=4568&candidate=zhang&candidate=li">为李四投票</a>
前面我们有知道对于JSP来说在有2个相同的名称的参数的时候,会取第一个值,所以不管投票人选择的是谁,始终都是张三得票。
一般来说,对客户端的攻击一般会是如下流程,导致用户选择不期望的选项:
2.对服务器端的攻击
比如某网站的实现如下:
void private executeBackendRequest(HTTPRequest request){
String action=request.getParameter("action");
String user=request.getParameter("userid");
String target=request.getParameter("target");
HttpRequest("http://centralauthencationserver/checkpriviledge.jsp", "POST","action="+action+"&user="+user+"&target="+target);}
/* get feedback of whether this user has privilege to perform specified action. If no such privilege, return error, otherwise continue perform the action*/
HttpRequest("http://businessserver/performaction.php", "POST","action="+action+"&user="+user+"&target="+target);}
它有个独立的集中认证服务器用来做用户权限方面的认证,另外的业务服务器专门用来处理业务,对外的门户实际上紧紧只是用来做请求的转发。那么看看一个本来紧紧只是具有只读权限的用户,如果发送如下请求给服务器:
http://www.backlion.org/page?action=view&userid=zhangsan&target=bizreport&action=edit
那么根据我们知道的Web服务器参数处理的方式,这个用户可以通过认证做一些本来没有权限做的事情。
Web服务器 | 参数获取函数 | 获取到的参数 |
PHP/Apache | $_GET(“par”) | Last |
JSP/Tomcat | Request.getParameter(“par”) | First |
###HPP参数污染TIPS
HPP还可以被攻击者用来绕过一些Web应用防火墙(WAF, WebApp Firewall),HTTP参数污染注入源于网站对于提交的相同的参数的不同处理方式导致。
例如:
www.backlion.org/a?key=ab&key=3
如果服务端返回输入key的值,可能会有
一: ab
二:3
三:ab3
这三种不同的方式。
具体服务端处理方式如下:
Web服务器 | 参数获取函数 | 获取到的参数 |
PHP/Apache | $_GET(“par”) | Last |
JSP/Tomcat | Request.getParameter(“par”) | First |
Perl(CGI)/Apache | Param(“par”) | First |
Python/Apache | getvalue(“par”) | All (List) |
ASP/IIS | Request.QueryString(“par”) | All (comma-delimited string) |
假设输入www.backlion.org/a?key=select&key=1,2,3,4 from table
服务端有可能会将key处理为select 1,2,3,4 from table,从而导致SQL注入
再比如对某页面的SQL注入攻击如下:
show_user.aspx?id=5;select+1,2,3+from+users+where+id=1--
这个攻击因为在参数id里面存在明显的SQL注入的模板:select…from…而会被WAF成功拦截。但是如果换成HPP的方式:
show_user.aspx?id=5;select+1&id=2&id=3+from+users+where+id=1--
这时候没有任何参数具备select…from…的特征,可能就可以绕过WAF的拦截了。
在PHP可用于以下绕过WAF:
http://www.xishaonian.com/hello.php?id=select 1&id=2,3,4 from admin
该种情况还可用于Bypass WAF,当然还可以与XSS结合。
###HPP参数污染案例
案例一:
2009年,ModSecurity过滤器会将类似于select1,2,3 from table这类的语句归类为黑名单。当web服务器遇到类似/index.aspx?page=select 1,2,3 from table这样的语句时,会阻断请求。但是这个web服务器在遇到为同一个参数赋值不同数值时,会将它们连接起来,攻击者可以通过这个方法来绕过黑名单。例如提交如下的URL:
/index.aspx?page=select 1&page=2,3from table
首先,这不是黑名单中的模式,不会触发黑名单的拦截功能。其次,由于web程序会采取连接操作,即将&符号前后的内容连接,因此SQL注入行为能够被执行。
案例二:
这个案例是关于Apple Cups的,它是被许多UNIX系统利用的打印系统。利用如下的方式触发一个XSS攻击:
http://127.0.0.1:631/admin/?kerberos=onmouseover=alert(1)&kerberos
利用这个方法可以绕过系统的验证机制,原因是这个验证系统只采纳了第二个kerberos的值,这个值为空,因此不会触发。而第一个kerberos直到被用于构建动态HTML内容前都没有被验证。最终在web站点的上下文中javascript语句被执行
案列三:
很多的付款链接,有可能存在漏洞;一方面,付款链接一般会有重要的参数构建的签名;这些签名是由某些重要的字段组成的,加了同名的字段以后;有可能在签名的时候验证了第一个付款金额参数;但是在实际付款的时候用了后面的一个付款金额参数,导致签名被绕了过去。
假设我们拥有以下站点:https://www.example.com/transferMoney.php,它可以通过 POST 方法访问,带有以下参数:
amount=1000&fromAccount=12345
当应用处理请求时,它生成自己的发往其它后端系统的 POST 请求,这实际上会使用固定的toAccount参数来处理事务。
分离后端 URL:https://backend.example/doTransfer.php
分离后端参数:toAccount=9876&amount=1000&fromAccount=12345
现在,如果在提供了重复的参数时,后端仅仅接受最后一个参数,并且假设攻击者修改了发往网站的 POST 请求来提交toAccount参数,像这样:
amount=1000&fromAccount=12345&toAccount=99999
存在 HPP 漏洞的站点就会将请求转发给另一个后端系统,像这样:
toAccount=9876&amount=1000&fromAccount=12345&toAccount=99999(php处理最后1个参数值,即99999)
这里,由恶意用户提交的第二个toAccount参数,会覆盖后端请求,并将钱转账给恶意用户调教得账户(99999)而不是由系统设置的预期账户(9876)。
如果攻击者打算修改它们自己的请求,并且由漏洞系统处理,这非常实用。但是如果攻击者可以从另一个攒点生产链接,并且诱使用户无意中提交恶意请求,并带有由攻击者附加的额外参数,它也可以对攻击者更加实用一些。
案列四:
另一方面,HPP 客户端涉及到向链接和其它src属性注入额外的参数。在 OWASP 的一个例子中,假设我们拥有下列代码:
<? $val=htmlspecialchars($_GET[‘par‘],ENT_QUOTES); ?> <a href="/page.php?action=view&par=‘.<?=$val?>.‘">View Me!</a>
它从 URL 接受par的值,确保它是安全的,并从中创建链接。现在,如果攻击者提交了:
http://host/page.php?par=123&action=edit
产生的链接可能为:
<a href="/page.php?action=view&par=123&action=edit">View Me!</a>
这会导致应用接受编辑操作而不是查看操作
###HPP参数污染实列
1. HackerOne 社交分享按钮
报告连接:
https://hackerone.com/blog/introducing-signal-and-impact
漏洞描述:
HackerOne 包含的链接用于在知名社交媒体站点上分享内容,例如 Twitter,Fackbook,以及其他。这些社交媒体的链接包含用于社交媒体链接的特定参数。
攻击者可以将另一个 URL 参数追加到链接中,并诱惑受害者点击。HackerOne 将其包含在发向社交媒体站点的 POST 请求中的参数更改,从而导致漏洞的产生。漏洞报告中所用的示例是将 URL:
https://hackerone.com/blog/introducing-signal
修改为:
https://hackerone.com/blog/introducing-signal?&u=https://vk.com/durov
要注意额外的参数u,如果更改后面U参数的值诱惑受害者点击,尝试通过社交媒体链接分享内容,恶意链接就会变为:
这里最后的参数u就会拥有比第一个参数更高的优先级,之后会用于 Fackbook 的发布。在 Twitter 上发布时,建议的默认文本也会改变:
2. Twitter 取消订阅
报告连接:
https://blog.mert.ninja/twitter-hpp-vulnerability/
漏洞描述:
2015 年 8 月,黑客 Mert Tasci 在取消接收 Twitter 的小心时,注意到一个有趣的 URL:
https://twitter.com/i/u?t=1&cn=bWV&sig=657&iid=F6542&uid=1134885524&nid=22+26
注意到参数 UID 可能是 Twitter 账户UID。现在要注意他做了认为多数黑客都会做的事情,尝试 UID 修改为其它用户, Twitter 返回了错误。考虑到其他方式可能已经放弃了,Mert 添加了第二个 UID 参数,所以 URL 看起来是这样:
https://twitter.com/i/u?iid=F6542&uid=2321301342&uid=1134885524&nid=22+26然后就成功了。他设法取消订阅了其它用户的邮件提醒。这就说明,Twitter 存在 HPP 取消订阅的漏洞。
3. Twitter Web Intents
报告链接:
https://ericrafaloff.com/parameter-tampering-attack-on-twitter-web-intents
漏洞描述:
根据档Twitter Web Intents提供了弹出优化的数据流用于处理 Tweets & Twitter 用户:发推、回复、转发、和关注。它使用户能够在你的站点上下文中和 Twitter 的内容进行交互,而不需要离开页面或者授权新的应用来交互。这里是它的一个示例:
再充分测试之后,黑客 Eric Rafaloff 发现全部四个 Intent 类型:关注用户、喜欢推文、转发和发推都存在 HPP漏洞。
如果 创建带有两个screen_name参数的 URL:
https://twitter.com/intent/follow?screen_name=twitter&scnreen_name=erictest3
Twitter 会处理让第二个screen_name,它比第一个优先来处理这个请求。根据Web 表单类似这样:
<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,但是点击按钮后,它们会关注erictest3。
与之类似,当展现 intent 用于喜欢时,Eric 发现它能够包含screen_name参数,虽然它和喜欢这个推文毫无关系,例如:
https://twitter.com/intent/like?tweet_id=6616252302978211845&screen_name=erictest3
喜欢这个推文会向受害者展示正确的用户资料,但是点击“关注”之后,它仍然会关注erictest3。
###漏洞防御
1.HPP是一种新的注入型漏洞。要防止这种漏洞,除了要做好对输入参数的格式验证外,另外还需要意识到HTTP协议是允许同名的参数的,在整个应用的处理过程中要意识到这一点从而根据业务的特征对这样的情况作正确的处理。
2.让WAF或其他网关设备(比如IPS)在检查URL时,对同一个参数被多次赋值的情况进行特殊处理。由于HTTP协议允许相同参数在URL中多次出现,因此这种特殊处理需要注意避免误杀的情况
3.在代码层面,编写WEB程序时,要通过合理的$_GET方法获取URL中的参数值,而尝试获取web服务器返回给程序的其他值时要慎重处理。