标注的博客| 安全研究| 渗透测试| APT

首页

lastpass 2fa实现中的设计缺陷

作者 krahenbuhl 时间 2020-04-13
all

作为红队参与的一部分,我发现自己正在寻找一种方法来绕过Lastpass中的双因素身份验证(2FA)。不幸的是,这发生在塔维斯·奥曼迪在上一次传球中报告多个0天之前。会节省我们这么多时间的!无论如何,2FA是一个额外的安全层,可以保护用户帐户免受已经泄露密码的攻击者的攻击。我提到这一点是因为这是理解这篇文章目的的关键。

当您使用用户名和密码登录到服务时,在授予访问权限之前,您将收到一个额外的挑战。通常是每30秒更改一次的6位临时代码。Google authenticator、Authy和Toopher只是基于RFC6238和RFC4226的2FA解决方案Lastpass支持的几个。还有其他类型的2FA,但这些是最常见的。

临时代码是基于几个变量生成的,包括一个秘密种子和一个时间戳。这个秘密使它安全而独特,时间使它以通常30秒的间隔变化。服务器需要与客户端共享机密种子,以便能够设置和同步代码生成过程。这通常是通过在二维码中编码种子来完成的,二维码是你用2FA应用程序扫描的。

LastPass 2FA实现

在寻找绕过2FA的方法时,我首先检查的是二维码是如何存储的。最后,这就是生成有效临时代码的秘密所在。如果我能以某种方式窃取它,我就能登录(因为我已经有了密码)。

我在测试帐户中启用了Google authenticator,当我被提示扫描二维码时,我检查了源代码。

如您所见,二维码是一个img标记,源属性指向:

/谷歌认证码.php?迭代次数=1&passwordhash=TOKEN

/google_auth_qr_code.php?iterations=1&passwordhash=TOKEN

我以前研究过Lastpass,并在Blackhat EU做过一个演讲。当我看到参数“passwordhash”时,我立刻知道它是什么。passwordhash是LastPass用于身份验证的“登录哈希”。它由一个SHA256-PBKDF2密码和由“迭代次数”参数+1指示的轮数组成。基本上,Lastpass使用PBKDF2导出保护保险库不受主密码影响的加密密钥。因为Lastpass不想知道您的加密密钥,所以它们会应用另一轮PBKDF2并将其用作登录散列(passwordhash参数)。

问题

这都是什么意思?Lastpass将2FA secret seed存储在一个可以从您的密码派生的URL下。这实际上超过了2FA的全部用途,正如上面提到的,2FA是一个安全层,用于防止已经拥有密码的攻击者登录。从长远来看,想象一下你家里有一个保险箱,里面放着你最值钱的东西。你认为门和保险箱用同一把锁是个好主意吗?门钥匙也应该打开保险箱吗?

二维码检索需要验证

虽然我们知道如何获取秘密URL来获取QR码,但这是一个经过身份验证的请求。这意味着我们需要通过身份验证才能检索二维码。因为我们还没有通过认证(我们需要二维码),所以我们无法检索它。鸡和蛋的问题…还是不。

请求已通过身份验证,但我们可以强制受害者为我们提出请求。这称为跨站点请求伪造(CSRF)漏洞。通常,目标是“状态更改请求”(更新、创建或删除资源的操作),因为同源策略(SOP)不允许攻击者看到响应。

不幸的是,Lastpass将二维码用作纯图像文件。攻击者可以使用指向2FA URL的“src”属性在其域上设置“img”标记。映像将在攻击者的域上下文下加载,而不受任何SOP限制(请阅读底部的更新)。

值得注意的是,攻击者没有必要潜伏受害者访问其恶意网站。受害者信任的网站上的任何xs,如Facebook或Gmail,都可以被攻击者用来添加有效载荷来窃取QR码并将其发送回服务器。

其他问题

Lastpass 2FA设计还有其他后果。如上所述,通过将登录散列作为URL参数传递来检索QR码。这几乎相当于在URL中传递密码。如上所述,登录哈希与解密保险库的主密钥不同。具体来说,1轮SHA256-PBKDF2。

在URL中传递秘密是一种不好的安全做法。网址由网络服务器记录。Lastpass可以保护存储所有用户数据的数据库,但它们是否对日志应用相同的安全标准?攻击者通常以查找机密的日志为目标。

URL也会在缓存、代理、引用头甚至浏览器历史记录中泄漏。

用经典CSRF禁用2FA

我想我应该写一篇关于我的发现的文章是因为我想强调保持密码和2FA作为“独立实体”的重要性。事实上,我发现了另一种更直接的方法来禁用2FA,但技术上不那么有趣。

Lastpass让我们重新生成2FA秘密种子(以防它被破坏)。此功能的实现有三个问题:

所有这些问题的结合使得攻击者能够禁用受害者的2FA保护。

禁用2FA的请求如下:

GET/google_auth_regent_key.php HTTP/1.1Host:lastpass.comCookie:PHPSESSID=REDACTED

GET /google_auth_regenerate_key.php HTTP/1.1
Host: lastpass.com
Cookie: PHPSESSID=REDACTED

典型的CSRF。获取用于无CSRF令牌的状态更改请求的请求。访问受攻击者控制的站点的受害者可能会被迫发出此请求并禁用2FA。

LastPass修复

Lastpass从第一天就推出了一些初始修复,目前正在识别所有CSRF易受攻击的请求(还有更多)。通过CSRF漏洞禁用2FA已通过添加CSRF令牌修复。

就不安全的2FA设计而言,他们推送了一个初始补丁来检查原始头。这将确保获取二维码的请求只能来自lastpass.com。这是一个很好的即时修复,但不适用于不支持原始文件头的旧浏览器。

他们还停止使用登录散列来检索二维码,而是使用“uid hash”。uidhash是“一个SHA256散列的uid加上服务器机密和随机的每用户盐(我们根据用户的uid查找)。我们还将值的散列存储在数据库中,以便在X分钟后强制值过期,并确保用户实际请求发布信息”。

时间线

更新日期:04/21/2017

3个不同的人(一个同事,一个reddit用户和一个在下面评论的匿名用户)注意到我在本文中犯的一个错误,这是关键。我提到“映像将在攻击者的域上下文下加载,而不受任何SOP限制”。这不是真的。CORS将阻止从攻击者的域访问图像数据。

早在二月份,当我就这个问题联系Lastpass时,我就写了一个PoC来测试我的发现。当我看到这些评论时,我意识到我的错误,然后回去寻找我写的代码。当我试图让一个QR解码器JS库工作时,我正在测试指向一个本地图像文件,我从未更新PoC以指向Lastpass域,并且基于PoC工作的事实做出了错误的假设。

虽然2FA实现仍然很糟糕,但它是不可利用的。当然,正如我在文章中所解释的,您仍然可以通过标准CSRF禁用2FA。我再次联系Lastpass,让他们知道我报告的两个问题中的一个是不可利用的。

多亏了那三个人。今天学到的教训是,在做出实际假设之前,要仔细检查理论。