HPP是HTTP Parameter Pollution的缩写。这个漏洞由S. di Paola 与L. Caret Toni在2009年的OWASP上首次公布。这也是一种注入型的漏洞,攻击者通过在HTTP请求中插入特定的参数来发起攻击。如果Web应用中存在这样的漏洞,可以被攻击者利用来进行客户端或者服务器端的攻击。下面对这个漏洞的原理做一下详细解释。
首先讲下HTTP的参数处理
在跟服务器进行交互的过程中,客户端往往会在GET/POST请求里面带上参数:
GET /foo?par1=val1&par2=val2 HTTP/1.1
User-Agent: Mozilla/5.0
Host: Host
Accept: */*
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
但是针对同样名称的参数出现多次的情况,不同的服务器的处理方式会不一样,比如看下面的2个例子:
http://www.google.com/search?q=italy&q=china
http://search.yahoo.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) |
那么这会有什么问题吗?实际上这本身并没有什么问题,但是前提是Web应用程序的开发者知道这个事情并且有正确的进行处理。否则的话那么难免会对攻击者造成可乘之机。如果对同样名称的参数出现多次的情况没有进行正确处理的话,那么可能会导致漏洞使得攻击者能够利用来发起对服务器端或客户端的攻击。下面举一些例子来详细说明。
对客户端的攻击
比如有这样一个网站,用来给其他人在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>
因为种种原因,这个页面里面用于投票的链接实现的方式如下:
ID = Request.getParameter("pool_id")
href_link = "vote.jsp?poll_id=" + ID + "&candidate=xyz"
如果这时候恶意攻击者生成了如下的一个URL发给投票人:
http_://host/election.jsp?poll_id=4568%26candidate%3Dzhang
那么最终在页面的内容会是:
Url : http://host/election.jsp?poll_id=4568%26candidate%3Dzhang
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个相同的名称的参数的时候,会取第一个值:
Web服务器 | 参数获取函数 | 获取到的参数 |
JSP/Tomcat | Request.getParameter(“par”) | First |
所以不管投票人选择的是谁,始终都是张三得票。
一般来说,对客户端的攻击一般会是如下流程,导致用户选择不期望的选项:
对服务器端的攻击
比如某网站的实现如下:
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);}
它有个独立的集中认证服务器用来做用户权限方面的认证,另外的业务服务器专门用来处理业务,对外的门户实际上紧紧只是用来做请求的转发。这里不会有SQL注入之类的漏洞,因为不管是集中认证服务器还是业务处理服务器都会对传入的参数的格式做检查,确保不会存在SQL注入。那么哪儿有问题?因为集中认证服务器和业务处理服务器分别由2个团队开发,使用了不同的脚本语言,又没有考虑到HPP的情况。那么看看一个本来紧紧只是具有只读权限的用户,如果发送如下请求给服务器:
http_://frontHost/page?action=view&userid=zhangsan&target=bizreport%26action%3dedit
那么根据我们知道的Web服务器参数处理的方式,这个用户可以通过认证做一些本来没有权限做的事情。
Web服务器 | 参数获取函数 | 获取到的参数 |
PHP/Apache | $_GET(“par”) | Last |
JSP/Tomcat | Request.getParameter(“par”) | First |
除此以外,HPP还可以被攻击者用来绕过一些Web应用防火墙(WAF, WebApp Firewall),比如对某页面的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的拦截了。
总的来说,HPP是一种新的注入型漏洞。要防止这种漏洞,除了要做好对输入参数的格式验证外,另外还需要意识到HTTP协议是允许同名的参数的,在整个应用的处理过程中要意识到这一点从而根据业务的特征对这样的情况作正确的处理。