针对一次性验证码短信的安全性研究(下)

robots

 

0x01 Introduction

在本文中,对恶意的本地App可以在现代移动OS中未经授权访问SMS OTP消息的特定方式进行了首次深入、系统的研究。 研究确定了一组新的攻击渠道,这些渠道主要是由新引入的机制引起的。虽然开发了这些机制以允许使用更多和更安全的基于SMS的身份验证,但实际上,它们引入了新的攻击机会。为了更好地了解这些安全问题对现实世界的影响,对140,586个App进行了用户研究和大规模测量研究。 评估发现36款容易受到识别出的攻击的App(共享了数亿次安装),包括流行的消息传递App程序Telegram和KakaoTalk。此外,提供了有关如何减轻对App开发人员以及OS供应商的威胁的建议。

 

0x02 Exploiting Modern SMS APIs

在前面的部分中,已经描述了需要某种形式的用户交互的攻击,或以用户复制粘贴OTP的形式,或为App授予特定权限。取而代之的是,在本节中,将展示如何在现代Android版本中滥用最新的API,以在各种情况下执行自动,隐秘和无用户交互的攻击。

假设:对于这种类型的攻击,假定受害者App使用系统提供的API之一进行身份验证。但是不假定恶意App具有除联网(用于发送SMS OTP码)以外的任何权限,并且不假定任何用户交互。这些攻击的无许可和无交互性质使它们比以前的攻击更加令人担忧。

初步观察:有两个基本观察为针对这些现代SMS API的攻击奠定了基础。第一个观察结果是,在现代Android版本中,如果恶意App可以控制SMS OTP消息的部分内容,则恶意App可以读取整个消息,而无需任何许可或用户交互。这种令人惊讶的行为是由于存在前文所述的三个API,从而允许访问SMS消息而无需任何许可,这取决于它们是否包含特定的字符串(即Token或Hash码)。例如,考虑下图,并假设攻击者能够诱使(步骤1)受害者App的后端服务器发送SMS OTP消息(步骤2),该消息的内容包括与恶意App相关的Hash码:在这种情况下,操作系统会自动将携带OTP的SMS重定向到恶意App,而无需请求任何许可。

第二个观察结果是,由于这些现代SMS API的工作方式,如果受害App(及其关联的后端)使用SMS Retriever,SMS Token或SMS Token+,则攻击者可能有机会吸引该App的后端来提供部分攻击者控制的携带OTP的SMS消息,从而使恶意App有机会对其进行拦截。本节的其余部分讨论有关恶意App如何执行完整的端到端攻击的技术细节,这取决于使用的是三种现代API中的哪一种以及后端服务器的逻辑是如何实现的。

A.使用SMS Retriever攻击App

SMS Retriever API要求App的后端服务器保存App的Hash码,并将其包含在SMS OTP消息中。如果实施得当,攻击者将无法控制所传递消息的内容。在这种情况下,Hash码的存在将以密码方式确保只有合法App才能在不需要任何许可的情况下读取SMS OTP消息。在所使用的Hash算法具有弱抗碰撞性假设的前提下,此属性为真。

但是,如果App开发人员以不同且易受攻击的方式实施后端逻辑,则可能会不安全地使用此API。具体来说,可以实施后端,使其从App本身接收App的Hash码,然后将Hash码插入到生成的SMS OTP消息中。此时,合法App的后端将生成一条SMS OTP消息(包含恶意App的Hash码),该消息将由操作系统传递给恶意App,而不是合法的App。

研究发现这个问题出奇的普遍。推测发生这种情况是因为计算App的Hash码令人惊讶地是不直观的。官方文档了一个相当复杂的七步过程来获取该值,其中包括从Google Play商店下载签名密钥。尽管可以实现,但在给定APK文件(用于分发App的文件格式)的情况下,找不到任何可公开获得的代码,以返回其Hash码。相反,官方文档提供了一些代码,这些代码允许App本身获取其自己的Hash码。尽管相同的文档还警告不要在App中包括提供的代码,但发现许多开发人员都将其包括在内并用于计算Hash码。结果,本地生成的Hash码被发送到后端服务器,并用于SMS OTP消息生成。

还注意到开发人员不安全地使用此API的另一种常见方式。他们没有在App中动态计算Hash码并将其发送到后端服务器,而是在App中对Hash码进行了硬编码,并将硬编码的字符串发送到服务器,然后服务器使用它来生成SMS OTP消息。尽管Google文档特别指出:“请勿在验证消息中使用在客户端上动态计算的Hash字符串”,但并未明确声明不信任App后端代码中的硬编码和App提供的值。

B.使用SMS Token攻击App

SMS Token API本质上是不安全的,无论开发人员如何使用它。要使用SMS Token API,App程序A必须调用createAppSpecificSmsToken API来获取随机Token T。然后,该App将该Token发送到其后端服务器,后者将通过发送包含然后,操作系统将检测到接收到的SMS包含Token T,然后它将消息分发给App程序A。

但是,安装在受害者设备上的恶意App可以使用相同的机制来诱骗受害者App的后端。如上图所示,实际上,恶意App可以首先调用SMS Token API以获得 Token T2。然后,恶意App将此Token发送给攻击者(步骤0)。攻击者可以使用受害者App的后端服务器启动身份验证,将受害者的电话号码指定为电话号码,并将T2指定为Token(步骤1)。由于此API返回的Token是随机的,并且总是在变化,因此App的后端无法判断所提供的Token是来自合法用户还是攻击者,因此它需要信任身份验证请求中收到的任何内容。结果,后端将向受害人的手机发送包含O2的SMS OTP(步骤2),该SMS消息将由OS分发到恶意App(步骤3)。虽然有可能(尽管容易出错)正确使用SMS Retriever API,但在SMS Token API的情况下,无法识别所提供Token的合法性使此API本质上不安全地用于身份验证。

为了进一步证实本文的主张,使用ProVerif来演示SMS Token API的基本设计缺陷。 ProVerif是用于自动推理和验证给定密码或通信协议的安全属性的软件。在本例中根据上图所示,使用SMS Token API对App与相应的后端服务器之间的交互进行了建模。ProVerif能够说明攻击者如何从服务器获取OTP。证明的详细信息如下图。

不幸的是,此API的官方文档并未警告其使用的危险性。因此,发现使用广泛的App(例如Telegram)的开发人员将其用作基于SMS的身份验证的一部分,从而使他们的App容易受到上述攻击。

C.使用SMS Token+攻击App

SMS Token+ API的官方文档建议以与SMS Token API相同的方式使用此API。在这种情况下,使用此API的App容易受到与SMS Token中相同的攻击,因为唯一描述的差异是SMS OTP消息中存在潜在的自定义前缀,从安全角度来看这是无关紧要的。

但是,可以以更安全的方式使用它,但是此机制在官方文档中没有解释。换句话说,此API中的漏洞是由于文档不正确而不是其实现(如SMS Token中)所致。通过逆向工程该API的实现,发现其内部行为与SMS Retriever非常相似。具体来说,返回的Token始终等于调用App的Hash码,而不是随机的。同样,收到的包含Hash码A的消息仅传递给Hash码为A的App。

因此,安全使用此API的正确方法是忽略其返回值,而是将App的Hash码放入由App后端服务器生成的SMS OTP消息中。不幸的是,此关键信息从未在文档中提及,因此App开发人员仍将以与SMS Token相同的易受攻击的方式来实现此API。相反,官方文档指出:“Token仅在合理的时间内可用于一种用途”暗示了Token是随机生成的,或者至少可能会更改的事实。

D.责任披露和开发者回应

研究者向Android安全团队报告了有关SMS Token和SMS Token+ API的发现。与几位Google工程师举行了会议,他们承认了这些问题,并讨论了可能的缓解措施。后来谷歌告知研究者,在即将发布的Android季度更新中,他们计划修复或弃用这些API。

 

0x03 Additional Design Weaknesses Of the Modern APIs

除了前面各节中描述的内容之外,还发现了影响SMS Retriever,SMS Token和SMS Token+ API的其他设计漏洞。

A.现代API收件箱管理

SMS Retriever,SMS Token和SMS Token+ API旨在仅将SMS OTP的内容传递给特定的App。因此,使用这些API收到的SMS不应存储在SMS收件箱中。否则,能够获得Android读取SMS权限的恶意App可以读取它们并获取其中包含的OTP。

不幸的是,事实并非如此。具体来说,SMS Retriever API始终将接收到的SMS消息(即包含App的Hash码的消息)存储在SMS收件箱中。有趣的是,对于其他两个API(即SMS Token和SMS Token+),收到的消息在SMS收件箱中无法正常运行,但是攻击者可以强迫这种情况发生。实际上,只有当以下两个条件都成立时,这些API才会避免将收到的消息存储在收件箱中:

•受害者设备上的App先前已调用这些API并获得了令牌T;

•传入的SMS消息包含T。

攻击者可以通过使用这些API向App的后端服务器请求一个SMS OTP来轻松利用此行为,该SMS OTP指定了一个随机Token,而不是由这些API返回的Token。收到后,包含随机Token的消息将存储在SMS收件箱中,因此允许读取文本消息的App可以读取该消息。

B.加密漏洞

从加密的角度来看,SMS Retriever API没有遵循正确的准则。回想一下Hash码是通过将SHA256 Hash码转换为base64字符串并将其截断为11个字符来计算的。有效地,这将Hash算法的强度降低到66位(因为base64字符具有6位熵)。尽管截断Hash本身不是一个安全问题,但NIST准则要求不要将SHA256散列截断到少于224位。实际上,攻击者可以制作具有特制程序包名称的恶意App,使其具有与受害者App相同的Hash码。这种攻击需要找到一个66位Hash的弱抗碰撞性,尽管这很困难,但对于确定的攻击者来说在计算上是可行的。

为了测试在发生Hash码冲突的情况下Android和Google Play商店的行为,创建了两个具有相同Hash码的App。注意到由于生日攻击,创建两个冲突的App仅需要几个小时的CPU时间。有趣的是,能够在Google Play商店上载并获得批准的两个App。因此得出的结论是,市场运营商并未验证发布的App之间是否存在Hash码冲突。

将具有相同Hash码的两个App安装在同一设备上后,注意到两个App都停止接收通过SMS Retriever API传递的任何消息。但是,如果在未安装冲突的合法App的设备上安装了恶意App(具有冲突的Hash码),则该恶意App可以接收合法App后端传递的任何SMS OTP消息。

 

0x04 Large-scale App Measurement

为了更好地了解App如何使用现代API进行SMS OTP身份验证,对Google Play商店中的Android应用程序进行了大规模的测量分析。结果表明,由于使用了这些API,许多受欢迎的App被确认为易受攻击。

数据集:为了构建数据集,使用AndroidZoo获取Google Play商店中所有可用App的程序包名称。从此列表开始,根据Google Play商店中显示的信息下载了所有这些App,下载次数均超过50,000。为了加快App收集过程,基于其唯一的软件包名称从Google Play和第三方网站(例如,Google Play)下载了这些App的APK文件。最终数据集包括2019年12月至2020年2月之间总共下载的140,586个App。

A.脆弱的App标识

使用静态和动态分析机制的组合来查找由于使用现代SMS身份验证API而容易受到攻击的App。本文工具使用FlowDroid以及一组启发式方法来查找那些极有可能受到攻击的App。

为了检测SMS Token和SMS Token+的使用情况,本文分析首先检查方法签名(即createAppSpecificToken( )和create…WithPackageInfo( ))以及FlowDroid构造的调用图。调用图可帮助消除App实际上未调用的无效代码。如果根据它们的文档使用这两个API,则本质上是不安全的。结果,它们的使用表明可能存在易受攻击的身份验证方案。

对于使用SMS Retriever机制的App,静态分析会尝试检测(1)该App是否包含其自己的Hash码或动态计算该Hash码,以及(2)将其发送到服务器。这些功能强烈表明,后端服务器可以使用从App中获取的Hash码来创建SMS OTP消息。

为了检测硬编码Hash码的存在,研究者自己计算了App的Hash码,然后使用字符串匹配在App的代码中找到它的存在。此外,为了检测App是否动态计算自己的Hash码,检查该App是否调用了获取自己的签名证书所需的特定API(如上图所示)以及这些API的结果如何链接在一起。最后,使用FlowDroid提供的数据流分析来检测Hash码是否确实通过网络API发送出去。

动态验证:此外,使用动态分析和手动逆向工程来确认通过静态分析检测到的候选App是否确实易受攻击。具体来说,对App进行逆向工程,以确认检测到的API的使用作为其身份验证方案的一部分。将经过逆向工程分析的App归类为“可疑”。然后,为确认某个App易受攻击(即,恶意App可以在不需要任何许可或用户交互的情况下窃取其OTP),验证了可以以攻击者控制其内容的方式引诱App的后端生成OTP消息。

为了动态验证此属性,对App进行了检测(使用重新打包和Xposed检测工具),以修改Hash码(对于SMS Retriever)或Token。然后,手动与该App进行交互,触发其验证过程。最后,如果App的后端向研究者发送了包含修改后的Hash码或Token的SMS OTP,则将其分类为“Confirmed”。还要注意,在某些App中,在通知开发人员影响其身份验证方案的漏洞后,后端服务器代码逻辑已更新。将这些App分类为“Fixed”。动态验证表明,静态分析报告的大多数误报是由那些使用Hash码进行App完整性检查(例如重新包装检测)而不是实施SMS Retriever API的App引起的。

测量结果:上表总结了本文的发现。在进行动态分析时,发现有20个App被确认为容易受到攻击(上表中的第3列),这些App在Google Play商店中的安装总数超过1.33亿。同时,发现了16个App(表中的第4列),这些App在报告发现后已被确认为容易受到攻击,并获得了服务器端修复。总之,通过同时考虑“Confirmed” 和 “Fixed”App,本文研究了36个易受攻击的App,共超过2.3亿次安装。

请注意,由于多种原因,某些App无法触发身份验证过程。例如,某些App的后端服务器仅将SMS消息发送到无法获取的国际电话号码。因此,即使逆向工程表明他们的身份验证方案易受攻击,仍将其标记为可疑。发现有0个App使用SMS Token+机制。研究者认为这是因为该API最近才在Android 10中引入。

B.案例研究

(1)KakaoTalk:KakaoTalk是一种流行的即时消息App,在韩国93%的智能手机用户中都使用过。该App在其他亚洲国家也非常受欢迎。研究发现,KakaoTalk的后端使用带有App提供的Hash码的SMS Retriever。具体来说,App的代码包含硬编码的Hash码,该Hash码发送到App的后端,后端使用该Hash码生成SMS OTP消息。因此,它的实现很容易受到攻击。因此,攻击者可以创建一个与他们不拥有的电话号码关联的帐户,并冒充合法用户。

研究者已经录制了一个演示视频(https://pursec.cs.purdue.edu/projects/sms_mobile.html ),以说明针对KakaoTalk的端到端攻击。攻击通过以下步骤进行:

1)在受害者的设备上,已安装的恶意App(BadAppForVictim)会调用SMS Retriever;

2)在攻击者的设备上,攻击者在KakaoTalk应用中启动注册,并指定受害者的电话号码;

3)在攻击者的设备上,攻击者更改了KakaoTalk应用程序的行为(例如,通过Xposed框架),并将BadAppForVictim的Hash码发送到KakaoTalk的后端服务器;

4)KakaoTalk的后端服务器将验证文本消息发送到受害者的设备,并插入BadAppForVictim的Hash码。因此,BadAppForVictim可以读取此文本消息;

5)BadAppForVictim在受害者的设备上通过Internet将收到的SMS OTP消息发送回攻击者的设备;

6)BadAppForHacker在攻击者的设备上欺骗了包含被盗SMS OTP消息的传入文本消息。因此,在攻击者的设备上,KakaoTalk应用程序将作为受害者登录。

通过上述步骤,攻击者已成功使用受害者的电话号码进行了注册,现在可以充当受害者来接收和响应传入的消息。将某人添加到其KakaoTalk联系人列表中时,受害者的电话号码最终将与攻击者的设备(而不是受害者的设备)进行通信。

(2)Telegram:Telegram是移动平台上最流行的即时消息App之一。截至2020年1月,它在Google Play中的下载量已超过1亿。于2019年6月进行的先前实验中,该App被确定为容易受到攻击。在本文的研究中,发现Telegram中的SMS OTP身份验证过程同时使用了SMS Retriever和SMS Token(基于Android版本)。

还注意到,在Google Play商店中,存在许多Telegram非官方客户。这些App允许用户与其他Telegram用户聊天并连接到Telegram后端服务器。发现其中许多App的更新代码都没有像Telegram官方客户端那样快。这方面说明了在表中归类为“Fixed”的App,因为这些App仍包含未修补的Telegram代码,但由于它们使用已打补丁的Telegram后端服务器,因此无法被利用。

(3)Sinch库:Sinch库是一个Android身份验证库,针对Android应用程序的开发人员。发现Sinch库提供了SMS身份验证功能,该功能不仅使用易受攻击的SMS Token机制,而且还以易受攻击的方式使用SMS Retriever机制。

此外,发现另一个App很容易受到攻击,因为它使用了Sinch Library的函数,该函数在内部错误地使用了SMS Retriever API。具体来说,发现Sinch库的文档明确指示开发人员将其硬编码的Hash码插入作为用于启动库提供的SMS身份验证功能的函数的参数。然后,库将Hash码发送到库提供的后端服务器。

C.责任披露和开发者回应

对于在研究中被确定为易受攻击的所有App,已经联系了他们的开发人员。 Telegram,KakaoTalk和Sinch库的开发人员都承认了本文发现。 KakaoTalk和Telegram的开发人员都向研究者提供了漏洞赏金。

截至漏洞提交时,Sinch Library开发人员尚未发布任何更新来解决发现的问题。对于KakaoTalk,开发人员已更新其服务器端实现,以不再信任从客户端移动App收到的Hash码。对于Telegram,在通知开发人员后,后端的代码已快速更新,没有在SMS中包含SMS Token API使用的Token。后来,App的代码进行了更新,同时删除了SMS Token API和SMS Retriever API的用法。

 

0x05 Mitigation Strategies

在本文中,讨论了有关实现与SMS相关的身份验证功能的安全机制和API的许多不同建议。但是,这些建议中的每一个都有一些安全问题,并在设计空间中探索了不同的折衷方案。

研究者建议:现在,提出了现有API的一种更安全的变体,可以由App用来接收携带OTP的SMS消息并满足理想属性。本文提出的API基于Android操作系统可以与第三方App建立安全通信渠道的假设。另外,假设系统服务可以可靠地识别与其通信的App(及其签名)。系统服务可以通过使用Binder.getCallingUid( )API来实现此目标。这些假设符合本文的威胁模型。

建议的API与SMS Retriever API的工作方式类似,但具有以下修改:

1)使用此API的SMS OTP消息必须以精确的前缀开头(例如“ <OTP>”);

2)任何情况下,以特定前缀开头的消息都不会传递到SMS收件箱;

3)用户可以使用专用的系统App查看以特定前缀开头的消息;

4)消息传递到其Hash码包含在消息本身中的App;

5)Hash码的计算方式与当前的SMS Retriever API中相同,但其长度被截断为38个base64字符,而不是11个字符(根据NIST指南,确保228位熵)。

现在解释这些修改中的每一个如何满足上面列出的理想特性。条件1和条件2强制对SMS OTP消息进行明确标记,并且永远不要将其传递到SMS收件箱。这样,即使能够获得阅读短信的权限,恶意App也无法访问它们。条件3避免了可以利用设备中此功能的存在来以静默方式向电话号码发送文本消息(这可能会导致财务损失)。实际上,即使普通的App无法访问这些消息,也始终会通知用户它们的到来并能够看到它们。条件4和条件5使该API可以像当前SMS检索API一样传递消息。但是,较长的Hash码可确保恶意App无法获得与合法App相同的Hash码。反过来,由于SHA256具有弱抗碰撞性,此属性可确保App的后端服务器可以确保SMS仅会传递给App本身。考虑到所需的前缀,OTP的典型长度,Hash码的长度以及SMS消息可以长达160个字符(而不会产生任何额外费用)的事实,SMS OTP消息仍然具有约100个字符开发人员可以免费使用。

使用ProVerif实现了上述系统,并且验证了即使能够读取普通收件箱内容的恶意App也无法获得对OTP的未经授权的访问,下图中提供了ProVerif实现和证明的详细信息。与之前提出的解决方案(包括Google在SMS Retriever API中的实现)相比,本文解决方案能够实现上述所有理想属性。另外,据研究者所知,这是正式验证API属性以访问SMS OTP的第一项工作。

其他建议:正如在前文中所解释的那样,开发人员在计算所需的Hash码时遇到了困难,并且这方面导致他们错误地实现了后端服务器,这些后端服务器没有对正确的Hash码进行硬编码,而是从App中获取了该Hash码。因此,除了实施建议的API外,还建议:

•当前文档已更新,以明确声明后端服务器不应从App获取Hash码值。另外,如果需要支持多个合法的客户端App,则服务器必须验证该App发送的Hash码与合法App的Hash码之一匹配。

•应该为开发人员提供一种工具,可以轻松计算给定App的Hash码(从其APK文件开始)。Hash码也应显示在标准开发工具中,例如Android Studio和Google Play商店上的开发者控制台。

请注意,以上建议不是本文提议的防御机制的一部分,但是它们的目的是防止滥用当前的API。

 

0x06 Conclusion

这项研究是结合了逆向工程,形式化验证,用户研究和大规模自动化分析而进行的。工作不仅揭示了第三方App中的漏洞,而且还发现了由移动操作系统本身实现的核心API中的一些新设计和缺陷。例如,发现两个官方的Android API在设计上容易受到攻击,即使根据其文档使用它们,也不可避免地导致实现不安全的身份验证方案。此外,发现其他API容易被App的开发人员不安全地使用。

(完)