论文笔记《On The insecurity of SMS One-Time Password Message against Local Attackers in Modern Mobile Devices》


概述

  本文发表在NDSS22,原文链接ndss2021_3B-4_24212_paper.pdf

  本文围绕短信验证码单因素认证的安全性问题,基于Local Attack的攻击模型,针对从短信OTP在被发送到受害者手机中,到最终输入到目标app中的这一过程,提出了多种有趣的攻击方式来窃取短信OTP。

Introduction

SMS 1FA OTP

  SMS 1FA OTP全称(SMS One-Factor One-Time Password),即短信单因素一次性密码。通俗的讲就是仅使用短信验证码作为验证的一种认证方式,在现代智能手机普及的今天,这种认证方式并不罕见,在各大移动应用中非常普及。这种认证方式极大的为用户带来了便利性,但是同时也存在一些潜在的安全隐患。

  以往的研究中,针对短信验证码的攻击五花八门:

  • SIM-swapping attack
  • exploitation of the SS7 network
  • state-level attackers
  • Insecurely implement of SMS OTP

  在安卓系统版本的迭代过程中,从安卓8开始出现了多个不同版本的短信验证码相关的API,旨在提高用户的使用体验,同时确保安全性。对于这些新的API能够对目前已有的短信验证码的安全体系产生多大的影响,目前尚未有人进行研究,本文旨在弥补这一Gap。

Contribution

  本文的Contribution如下:

  1. 对目前智能手机存在的所有获取短信验证码的机制进行分析,系统化的描述各自可能会遭受怎样的攻击
  2. 进行大规模的实验以及User Study,揭示上述攻击的严重性,危害程度大
  3. 提出短信验证码API的改良方案,减少收到攻击的可能

Threat Model

Attacker model and capabilities

  • Local Attack: 由攻击者开发的恶意应用,发布在应用商店后被受害者下载安装,并在运行后表现出恶意行为。
  • 本文对恶意应用的权限要求只有Internet Permission
  • 最后假设攻击者能够知道受害者的手机号,受限于这个假设,这类攻击只能对一些高价值目标展开,难以做到批量化大规模

Assumptions on OS integrity

  本文对于系统完整性的要求也很低,即使攻击者在一个具有正常完整性的系统中即可,这意味着app之间的隔离性良好,进程间通信(IPC)的信道也无法被劫持。此外,本文也不会利用以往已经发现的,基于UI劫持的攻击方式。

SMS 1FA OTP Schemes

  短信验证码的工作流程如下图所示:

SMSOTP_Schemes

  这套流程是否安全有一定的条件:

  • 短信验证码需要具有足够的随机性
  • 服务端需要对接收到的短信验证码进行校验
  • 服务端需要限制客户端的错误重试次数,防止暴力枚举

  本文假设上述条件均满足,而本文的攻击集中于上图中的Step 3,通过Local Attack窃取短信验证码。

Methods to access SMS OTP Messages

  合法应用获取短信验证码的方式分为三大类:

  • Access with User Interactions:通过用户交互完成
  • Access by Requesting SMS:通过请求短信验证码完成
  • Modern SMS APIs:通过使用现代系统中提供的新型短信API完成

Access with User Interactions

  通过用户交互获取短信验证码的方式也分为三类:

  • manually copy:手动复制验证码
  • ask-to-copy: 收到短信验证码后有个点击复制的按钮,点击后直接复制到剪贴板,然后手动粘贴到目标app中
  • One-Tap SMS verification:收到短信验证码后,会弹框提示app正在申请读取短信验证码,点击同意后自动将验证码填充到app中

asktocopy
onetap

Access by Requesting SMS

  通过申请短信读取权限来读取短信,具体由两类短信权限:

  • READ_SMS: 能读取信箱中的所有短信
  • RECEIVE_SMS: 仅能读取当前接收到的新短信

  这类权限被认为是高危权限,因此有着来自系统和应用商店的双重限制

OS-Level Restriction

  这类权限在app运行时需要动态的请求权限,会弹框询问用户是否同意授权
askpermission

Market-Level Restriction

  申请这类权限的app在发布到Google Play前,需要先接受人工审核,只有满足以下条件才能通过审核:

  • 被审核的app必须要是一个专门用来代替系统原有的短信app来完成短信收发工作的app
  • 这个app必须在使用时请求用户将其设置为默认的短信app

Modern SMS APIs

  在安卓高版本(安卓8+)中,提出了几个新的API专门用来处理短信验证码,它们的核心思想是:通过服务器在短信中附加标识性字符串来指定仅将该短信转发给特定的app。通过使用这些API,app可以无需申请其他任何的短信相关API而自动完成短信验证码的填充过程。具体有以下三类短信验证码API:

modernAPIs

SMS Retriever

SMSRetriever

  SMSRetriever工作原理如上图所示,这个api的标识性字符串被成为hashcode,其计算方式如下:

hashcode

SMS Token & SMS Token+

smstoken

  SMS Token 和 SMS Token+的工作流程均如上图所示,两者的区别如图modernAPIs所示,后者的参数多了一个prefixs可以对短信内容的前缀做筛选。此外在文档上无任何区别,但是下文发现事情没那么简单。

Maliciously Obtain SMS OTPs

  针对每一种获取短信验证码的方式,作者均提出了特定的攻击方式。

Deception attack

  针对通过用户交互来获取验证码的方式,作者提出了Deception attack,具体步骤如下:

  1. 受害者在Malicious App中输入手机号,并点击“获取验证码”
  2. Malicious App将手机号发送给攻击者,然后攻击者将该手机号输入一个合法app中,并点击“请求验证码”
  3. 该合法app服务端收到手机号后,会将验证码发送到受害者的手机上
  4. 受害者收到短信后直接将验证码输入到Malicious App中
  5. Malicious App将验证码发送给攻击者,攻击者输入该验证码到合法app中,完成攻击

  上述攻击无需申请任何短信相关权限,攻击成功的原因是受害者在点击“请求验证码”后通常情况下会收到,且仅会收到一条短信验证码,而此时正好合法App的服务端给他发了一条,那么就符合他的预期,导致他粗心大意没有判断这条短信验证码的来源是否和将要填入的app相匹配。

  对于这种攻击能产生多大的效果,作者进行了User Study,通过设计一个虚拟环境来模拟app注册和登录过程供志愿者进行操作,收集其交互结果并分析,其虚拟环境以及结果如下图:

userstudy1

userstudy1result

  不论是手动输入还是copy验证码,其结果的占比均相差不大,而且比例较高,受骗人占比在45%~71%之间。尽管在弹框中会明确提示哪个app即将读取短信,且同时显示了短信内容,但是还是有这么多人上当,更别说作者还指出,想要篡改弹框中显示的app名称,其实也并非难事。

Weakness 1:缺少可靠的方法来帮助用户鉴别到底是哪个app想要读取短信

Bypassing SMS Permission Restrictions

  作者对如何绕过申请读取短信权限时受到的限制进行了研究

Bypassing OS-level Restrictions

  系统层面的限制和上面的Weakness 1差不多,都是弹框,因此作者又做了一个User Study,设计了问卷来询问志愿者:在弹框后点击“Allow”会发生什么?问卷及其结果如下:

userstudy2

userstudy2result

  结果显示,很多用户虽然知道点击“Allow”以后App会读取短信,但是不知道它的潜在危害是能够窃取用户账号。

Weakness 2:系统提示的信息没有向用户解释清楚潜在威胁

Bypassing Market-level Restrictions

  对于Google Play有人工审核这一限制,能够自然而然的想到:首先提交一个合法的版本能够申请读取短信权限且通过人工审核,然后再发布一个更新版本,能够申请读取短信权限但是不满足通过人工审核的要求。结果显示,更新的版本也能成功发布在Google Play中,说明人工审核不会对更新的版本进行审核。

Weakness 3:在版本更新时,缺少人工审核

Requesting alternative permission

  除了申请读取短信权限以外,还有一种侧信道的方式能够读取短信内容。接收到短信时,通常都会显示在通知栏,因此,申请读取通知栏权限可以起到同样的效果,以此可以绕过申请读取短信权限时的诸多限制。但是由于读取通知权限在申请时,会要求用户手动的去设置中将该app的权限打开,操作较为复杂,因此该权限在系统层面较难获取,但是却无需受到来自应用市场的人工审核。

Weakness 4:来自应用市场和系统的限制没有对齐

Exploiting modern SMS APIs

  作者还对每个Modern API提出了可行的攻击方式

Attacking Apps using SMS Retriever

  从上文对SMS Retriever的介绍可知,这个API在理论上是比较安全的,但是实际上,由于该API的说明文档不够清晰,造成了许多开发者对这一API产生了误用。具体来说就是:他们没有将Hashcode硬编码在服务端并每次由服务端发送OTP时附加该Hashcode,相反的,他们在客户端计算或者硬编码Hashcode,然后发送给服务端,在由服务端将接收到的Hashcode附加在OTP中。这一情况导致了如下图所示的攻击:

attackSMSRetriever

Weakness 5:SMS Retriever API容易被误用

Attacking Apps using SMS Token

  这个API在根本上就是不安全的,原因是它的标识性字符串(Token)由客户端生成,且每次都随机生成,因此服务端无法区分该Token是否来自合法的App。因此导致了如下图所示的攻击:

attackSMSToken

Weakness 6:SMS Token在设计上是有缺陷的

Attacking Apps using SMS Token+

  理论上,由于文档说明中SMS Token与SMS Token+的唯一区别是后者多了个前缀filter,因此两者的安全性应该完全一致。但是实际上,SMS Token+的文档有误,或者说,没有说明两者最重要的区别:SMS Token+的Token并不是每次随机生成的,而是与SMS Retriever相似,基于包名和签名生成的。因此其正确的使用方式应该与SMS Retriever相同,然而如果开发者按照文档说明来使用这个API,结果就是和SMS Token一样会受到攻击。

Weakness 7:SMS Token+的文档推荐与SMS Token相同的使用方式

Additional Design Weakness

  作者还列举了两个比较重要的安全问题

Modern APIs’ Inbox Management

  短信验证码一旦进入到信箱中,就有可能被第三方app读取到,因此尽量不要让短信验证码进入到信箱中。根据图modernAPIs可知,SMS Retriever始终会将短信存入信箱,而对于SMS Token和SMS Token+,一条短信只有在满足两个条件的同时才不会被存入信箱:

  • 在接收到这条短信前曾经调用过SMS Token或者SMS Token+ API并取得的Token T
  • 这条短信的内容中包含有Token T

  即便如此,由于很多app的服务端会接收客户端的Token并返回,因此可以通过随意设置一个无用Token发给服务端,使得不满足上述的第二个条件,来使短信存入信箱中。

Weakness 8:短信验证码不应该存入信箱中

Cryptographic Weaknesses

  回顾以下SMS Retriever的Hashcode计算方式

hashcode

  在第二步中,将生成的SHA256转化为base64字符串后仅截取了结果的11位字符串作为Hashcode,因此将这11位Hashcode解码后,有效的hash位数为66bits。在NIST guidelines中要求一个可靠的hash位数应该不小于224 bits,否则容易产生hash碰撞。作者通过实验证明,两个hash碰撞的app能够顺利的发布在Google Play中。

Weakness 9:Hashcode的有效位数不符合NIST guidelines

Large-scale Measurement

  最后作者做了一个大规模的实验

Dataset

  • 来源:Google Play & AndroidZoo
  • 时间:2019.12~2020.02
  • 下载量:> 50000
  • 最终爬取app总数:140586

Static analysis

  第一步是通过静态分析来筛选出可能遭受攻击的app,分为以下几个步骤:

  1. 寻找app中是否存在硬编码或者动态计算的Hashcode。对于硬编码的情况,作者自己计算Hashcode并进行字符串匹配;对于动态计算的情况,作者在使用Flowdroid构建CFG排除掉死代码以后,查找是否有特定的用于计算Hashcode的API存在,具体如下:

specificAPIs

  1. 使用Flowdroid对Hashcode进行数据量分析,判断是否最终进入了一个网络请求API,即被发送到了服务端。

Dynamic confirmation

  通过静态分析的app会进入动态确认的环节:

  • 逆向人员会对这些app进行人工确认静态分析中检测到的API是否用于身份验证机制,而非用于App完整性检查(签名校验)。通过确认的app被记为“Suspicious”
  • 对于“Suspicious”的app,逆向人员使用Xposed对其Token或者Hashcode进行篡改,然后发送到服务端,观察服务端是否返回被篡改的值。如果是,记为“Confirmed”
  • 最后通知所有“Confirmed”的开发者,如果他们修复了后端的逻辑,则记为“Fixed”

Result

  最终结果如下:

result

  • 20个“Confirm”里共有1.33亿的下载量
  • 36个“Confirm”+“Fixed”里共有2.3亿的下载量
  • “Suspicious”里面有一部分可以被标记为“Confirm”但是由于没有国际手机号所以没法100%确定
  • SMS Token+是一个安卓10+的API所以目前暂时没人使用

Case Study

KakaoTalk

  这个app在韩国很火,93%的智能手机用户都在使用。这个app用的是SMS Retriever这个API但是后端实现逻辑有上文中提到的问题。具体攻击流程如下:

  1. 受害者手机中的Malicious App调用SMS Retriever API等待接收短信验证码
  2. 攻击者在自己手机中的KakaoTalk中输入受害者的手机号,并请求验证码
  3. 攻击者通过Hook的方式,在请求验证码时,将生成的Hashcode替换为Malicious App的Hashcode并随手机号一同上传至服务器
  4. 服务端收到手机号后,生成验证码并附加由攻击者篡改过的Hashcode,并将其发往受害者的手机号
  5. 受害者的手机接收到短信后,由于系统判断Hashcode与Malicious App一致且Malicious App调用了SMS Retriever API,因此将短信转发给Malicious App
  6. Malicious App收到短信后,将其通过网络发送给攻击者,攻击者提取验证码并输入KakaoTalk,攻击成功

  该App开发者在被作者告知漏洞后已修复这个问题。

Telegram

  这个软件大家都熟,Google Play中有1亿下载量,可惜用了SMS Token这个API,该App开发者在被作者告知漏洞后已修复这个问题。

Sinch Library

  这个是一个专门给开发用于集成短信验证码功能的SDK,其内部错误的使用了SMS Retriever API还明确的教开发者要硬编码在客户端作为参数传递,不仅如此还使用了SMS Token这个API,此外,开发者虽然在被作者告知漏洞后承认了这一问题,但是至今未修复。

Mitigation Strategies

  最后,作者对现有的Modern API提出改进。作者认为,一个套理想的短信验证码机制一个满足以下条件:

  1. 短信验证码应该直接自动转发给目标APP,而不是手动输入或者复制(解决Weakness 1)
  2. 应该使用类似SMS Retriever类似的机制而不是SMS Token那样的机制(解决Weakness 6和7)
  3. 文档中要清楚的描述正确、安全使用这一API的方式(解决Weakness 5)
  4. 从密码学角度要足够安全(解决Weakness 9)
  5. 短信验证码不要发到信箱中(解决Weakness 2~4和8)
  6. 用户应该有办法能够看到接收到的短信验证码
  7. 具有足够的易用性和兼容性能在现有的被设备投入使用

  根据以上几点作者提出了SMS Retriever API的修改版(解决Weakness 6和7):

  1. 短信验证码要以某个固定的前缀作为开头,只要满足这个固定前缀,那么无论如何这条短信都不会被存入信箱(解决Weakness 2~4和8)
  2. 不存入信箱的短信应该有一个专门的系统app用来查看(解决上面的条件6)
  3. 短信使用SMS Retriever的机制自动被转发到目标app而不是手动输入(解决Weakness 1)
  4. 计算的Hashcode最后截取38个base64字符串,以满足NIST guidelines

PPT

  最后附上我演讲时用的ppt:SMS OTP


文章作者: 大A
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 大A !
评论
  目录