重新认识被人遗忘的HTTP头注入
前言
注入类漏洞经久不衰,多年保持在owasp Top 10的首位。今天就聊聊那些被人遗忘的http头注入。用简单的实际代码进行演示,让每个人更深刻的去认识该漏洞。
HOST****注入
在以往http1.0中并没有host字段,但是在http1.1中增加了host字段,并且http协议在本质也是要建立tcp连接,而建立连接的同时必须知道对方的ip和端口,然后才能发送数据。既然已经建立了连接,那host字段到底起着什么样的的作用?
Host头域指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的是比如**www.test.com和mail.test.com**两个域名IP相同,由同一台服务器支持,服务器可以根据host域,分别提供不同的服务,在客户端看来是两个完全不同的站点。
Host头实验总结:在http 1.1中不能缺失host字段,如果缺失, 服务器返回400 bad request,http1.1中不能缺失host字段,但host字段可以是空值。
0×01 密码重置的中毒
相信大家忘记密码时,使用邮箱找回密码并不陌生。看上去貌似能可靠只能发送到你绑的邮箱去重置密码。并且使用你自己独特的密钥令牌才能重置成功。但是好多cms和实际场景为了获取网站的域名拼接秘钥令牌的方式将连接发送到你的邮箱。但是往往获取正确的域名并不是十分的不容易, 然而用一个固定的URI来作为域名会有各种麻烦。所以一般程序员会采用(java)request.getHeader(“Host”); (php)$_SERVER['HTTP_HOST']的方式来获取域名。
上面所述的两种获取域名的方法并不可靠。都可以人为的去控制。导致当域名和令牌拼接时,攻击者通过篡改hsot值生成钓鱼连接,当受害者点击时,受害者的秘钥令牌将会直接发送到攻击者的服务器上。使其受害者的密码被攻击者篡改。
文字看上去比较绕的话,我在本地用代码将此处的业务场景进行了复现。
public class FindPass extends HttpServlet{
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
//此处真实场景的话,是根据输入的账号去查对应的邮箱。就不繁琐的构造了。
String email =request.getParameter("email");
//此处获取了host头中的host字段,此值可以被攻击者篡改
String Host =request.getHeader("Host");
int max=200000000;
int min=100000000;
Random random = new Random();
//生成随机的秘钥令牌
int randomNubmer = random.nextInt(max-min) + min;
String content= "用户您好:<br>现在给您发送邮件的XX银行系统,您的账户"+email+"申请找回密码!<br>" +
"只需在提交请求后的三天之内,通过点击下面的链接重置您的密码:<br>"+
"http://"+Host+"?id="+randomNubmer;
//此处用Host拼接了秘钥名牌
try {
//调用发信接口
SendEmail.send(email, content);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(email+Host);
}
}
提交找回密码表单,篡改其中的host头值。
当用户收到密码重置邮件后,发现是钓鱼者的网址,且秘钥令牌也已参数id的形式传给给攻击者,此时攻击者可以直接使用受害者的令牌进行密码重置。
0×02 XSS****:
有些网站会根据HTTP_HOST字段来进行加载css样式表。如果要为固定的话,当域名发生改变时,那么站内所有的css即将失效。而且修改的时候也将的十分头疼,因为每个页面都会引用大量的公共资源。如下是我重构的场景重现:
<?php
echo<<<EOT
测试host攻击<br>
cccccccc
<head>
<link rel="stylesheet" type="text/css" href="http://{$_SERVER['HTTP_HOST']}?id=111111" />
</head>
EOT;
echo $_SERVER["HTTP_HOST"];
?>
0×03 web****缓存中毒
简单描述:
Web缓存服务器的应用模式主要是正向代理和反向代理。正向代理(Proxy)模式是代理网络用户访问internet,客户端将本来要直接发送到internet上源服务器的连接请求发送给代理服务器处理。正向代理的目的是加速用户在使用浏览器访问Internet时的请求响应时间,并提高广域网线路的利用率。正向代理浏览器无需和该站点建立联系,只访问到Web缓存即可。通过正向代理,大大提高了后续用户的访问速度,使他们无需再穿越Internet,只要从本地Web缓存就可以获取所需要的信息,避免了带宽问题,同时可以大量减少重复请求在网络上的传输,从而降低网络流量,节省资费。
思路:
Apache接收到一个带有非法host header的请求,它会将此请求转发给在 httpd.conf 里定义的第一个虚拟主机。因此,Apache很有可能将带有任意host header的请求转发给应用。
场景:
一般的缓存服务器都会识别hsot,所以一般直接替换host字段会被拦截。。Varnish辨别第一个host Apache识别所有请求的host, Nginx则只是看最后一个请求。此时就可以通以下方式
GET / HTTP/1.1
Host: test.com
Host: exp.com
来绕过缓存服务器的检查。
0x04WordPress与PHPMailer****结合导致命令执行
漏洞描述:
独立研究人员Dawid Golunski发现该漏洞—远程攻击者利用该漏洞,可实现远程任意代码在web服务器上执行,并使web应用陷入威胁中。攻击者主要在常见的web表单如意见反悔表单、注册表单中 ,邮件密码重置表单等使用发送的组建时利用此漏洞。
POC展示:
主要是利用host注入恶意命令导致远任意代码执行
Host : target(any -froot@localhost -be ${run{${substr{0}{1}{$spool_directory}}usr${substr{0}{1}{$spool_directory}}bin${substr{0}{1}{$spool_directory}}touch${substr{10}{1}{$tod_log}}${substr{0}{1}{$spool_directory}}tmp${substr{0}{1}{$spool_directory}}manning.test}} null)
下面针对这个做个简要分析,网上很多文章已经对此作了详细分析,我在此就不做详细分析了。
if ( !isset( $from_email ) ) {
//此处会获取SERVER_NAME字段拼接到$from_email变量上且SERVER_NAME可被攻击者控制
$sitename = strtolower( $_SERVER['SERVER_NAME'] );
if ( substr( $sitename, 0, 4 ) == 'www.' ) {
$sitename = substr( $sitename, 4 );
}
$from_email = 'wordpress@' . $sitename;
}
private function mailPassthru($to, $subject, $body, $header, $params)
{
//Check overloading of mail function to avoid double-encoding
if (ini_get('mbstring.func_overload') & 1) {
$subject = $this->secureHeader($subject);
} else {
$subject = $this->encodeHeader($this->secureHeader($subject));
}
if (ini_get('safe_mode') || !($this->UseSendmailOptions)) {
$result = @mail($to, $subject, $body, $header);
} else {
//最终$params参数被@mail调用后会触发CVE-2016-10033 phpmailer命令执行漏洞执行远程命令。
$result = @mail($to, $subject, $body, $header, $params);
}
return $result;
}
X-Forwarded-For****注入:
大家有没有见过这样一种场景,当你对用户网站进行的爆破或者sql注入的时候,为了防止你影响服务器的正常工作,会限制你访问,当你再次访问时,会提示你的由于你的访问频过快或者您的请求有攻击行为,限制访问几个小时内不能登陆,并且重定向到一个错误友好提示页面。
由此可以发起联想?http是无状态连接,而且自己也清空了COOKIE信息,服务器是怎么还是自己的?
首先当你有攻击行为的时候,服务器一般会把恶意用户的ip存入数据库。当每次用户请求的时候(以java语言为例),服务器通过request.getRemoteAddr()这个方法来获取请求者的ip。于是想到这个ip我们自己到底能不能伪造?答案是否定的。因为经过测试request.getRemoteAddr并不会回从数据包的请求头去获取ip字段的Value。所以推测ip地址是ip包中的soure来的,当我们发送http请求时是否可以更新soure ip来绕过限制呢,这个可以有!,但是你将不会收到对方的响应,因为在正常的TCP/IP通信中,伪造数据包来源 IP会让发送出去的数据包返回到伪造的IP上,无法实现正常的通信。这样我们也就失去的绕过的意义。
request.getRemoteAddr方法我们没办法伪造,是不是我们就无法利用这个点了呢?
结合实际一般程序员是不会通过request.getRemoteAddr方法来获取ip的,这个方法虽然获取的ip比较准确,暂无办法绕过。但实际场景中往往服务器前面会有一个代理服务器和均衡负载服务器。当使用request.getRemoteAddr方法时,获取的只是代理服务器的ip并不能获取请求者的真实ip。这时候一些程序员为了实现获取真实ip会采取如下方法。
public static String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Real-IP");
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
if (ip != null) {
if (ip.indexOf(',') > 0) {
ip = ip.split(",")[0];
}
} else {
ip = "0.0.0.0";
}
return ip;
}
X-Forwarded-For通过这个字段我们可以控制绕过ip的限制,但是当我们去绕过ip限制的时候ip会带入数据库去查询,这点我们是不是可以尝试一下sql注入。
将数据包写入文件,通过 sqlmap.py -r “filePath”
通过sqlmap抛出的信息,可以看到存在sql注入的点是X-Forwarded-For字段,并且爆出了mysql的版本信息和脚本语言的信息。
User-Agent****注入
User-Agent、Content-Type等只要是http包中存在的都可以进行篡改。在这再说下User-Agent的头注入和Content-Type的头注入,其他的就不详细赘述。基本原理大同小异。最后会进行总结。
一般使用user-Agent,有两种场景一是记录访问者的信息,如什么浏览器、操作系统版本等。二是获取客户的user-Agent,根据你提供的信息来给你推送不同的网页。如果你手机手机访问那么返回你的页面将是小型的web界面,还有各种浏览器的特性,推送相兼容的页面。此时已经进行的数据库的入库和查询操作,如果没对此做过滤那么漏洞就产生了。
Content-Type****注入又一个远程命令执行
Content-Type看见这个字段相信大家都不陌生,瞬间就会想起一个很可怕的漏洞。S2-045的远程命令执行。当Jkarta解析文件上传请求包不当,当攻击者使用恶意的Content-Type,会导致上传发生异常。从而导致Str2框架对Content-Type的内容进行ongl的解析导致命令执行。这个漏洞虽然要涉及Jkarta插件,而arta插件ommons-fileupload和commons-io包,但是这是Str2的默认插件。也就是说基本所有开发使用到上传都会导入这两个依赖包。最重要的是这个漏洞不需要真正的上传只需进行模拟上传即可,甚至可以是GET请求。具体的详细代码就不跟大家分析了,要不估计还比我这篇文章还长。有兴趣的可以到我的博客或者网上搜索那些大佬的分析。
总结
归根结底,http头攻击漏洞的产生就是因为服务器使用了不受信任的http头字段对某个业务进行的操作。而http头都是我们可以恶意篡改的。具体产生什么漏洞,就看你把这个不受信任的字段,拿去做什么样的业务操作。在平常渗透的时候,可以思考程序员们会拿着这些参数去干什么,从而更有方向的去渗透。