付出宝客户端宁静消费客户端危险发掘_怪人怪事

付出宝客户端宁静消费客户端危险发掘

怪人怪事 2023-05-04 14:30www.bnfh.cn怪人怪事

支付宝客户端安全生产客户端风险挖掘

背景

客户端有着错综复杂的体系架构,多种技术栈组合及千人千面的场景,如何保障客户端高可用成了我们最大的命题。除了线上告警的应急快反之外,我们迫切需要提前挖掘风险的能力,构建一套体系化的挖掘能力,持续不断的挖掘端上漏洞是非常必要的,才能真正从源头上解决问题。

挖掘思路

实现端上全方位立体的扫雷工作,要先了解客户端的整体架构、开放接口、数据模块和依赖关系,从而能更全面的挖掘漏洞。

如上图是支付宝客户端mPaaS框架图,基于此框架的客户端App,都是由一个个积木搭建组成,这些积木我们称之为Bundle。由下往上library提供客户的基础服务,例如Rpc、Config、Push等基础功能。Application Frameork 提供应用的上下文、AOP切面能力、安全等功能。Common Services通用的服务模块接口,提供对外的能力的封装。Native Application是业务实现层,每个业务基于基础服务、通用能力,最终构建对应的业务能力。

通过对客户端框架的整体结构认识及历史故障分析,我们将扫雷方向瞄准在基础服务框架并梳理了5大类风险点,如下图是客户端风险扫雷整体挖掘思路

基础框架Rpc、Config 、Sync、Jsapi 、mTop、Lottie动画、缓存等公共SDK;

系统服务Broadcast、Scheme、ContextProvider、Http(s)、Cookie、SQlite等;

业务不可用按钮点击无响应、花屏检测、入口丢失、Loading异常、弹窗异常;

安全风险Jsapi越权检测、流量海关拦截、隐私明文检测、厂商隐私照明弹;

故障泛化多媒体视频流变异、限流、超时、模板变异、多线程;

能力介绍

通过客户端架构框架了解及风险点梳理,我们知道客户端风险集中在基础框架的业务变更上,其中有开关JSON配置异常{} -> [] 引起客户端闪退、ffmpeg直播拉流解码异常、卡片模板变更引发历史版本闪退,打包构建导致图标丢失界面异常,有必要建设一套全自动化风险挖掘体系,通过优质的种子变异能力,快速生成大量异常数据,持续不断挖掘客户端风险,从而提升客户端稳定性。

如上图是扫雷的三个核心能力,分别是风险挖掘(种子变异能力)、闪退溯源及攻防演练,通过云真机能力串联构建整个风险挖掘体系,实现全自动化挖掘,源源不断挖掘端上风险,提供攻防演练素材。以下是核心能力介绍

1.风险挖掘两步走

种子变异能力

  • AFL Fuzz针对二进制文件进行Bit变异,并以代码覆盖率作为牵引,实现高效模糊测试。(适合多媒体视频、动画模板文件)
  • 结构化变异传统Fuzz是无结构线性二进制数据,结构化变异通过理解结构体内容含义,语义化进行变异。(适合JSON/Java对象/OC/结构体)

异常检测能力

  • 端智能花屏检测引擎,基于AI图形训练挖掘端上渲染异常
  • 除了稳定闪退外,端上业务不可用治理面临严峻挑战,我们梳理线上不可用问题特征,基于图像相似度对比、OCR方案实现了按钮点击无响应、业务入口丢失、弹窗异常等业务异常

2.闪退溯源

  • 记录闪退运行时环境页面数据,提供复现路径信息、图片数据
  • 通过Call Graph函数调用图追溯闪退根因,将闪退与变更源绑定

3.攻防演练

  • 通过挖掘复现客户端闪退,持续不断发现漏洞,并以开关配置等方式进行真实的攻防演练,为演练提供源源不断的子弹
  • 一键提交缺陷,实时跟进缺陷修复情况

整体架构

本方案架构主要分为四部分,底层依赖、基础能力、风险类型和自动化实现。通过对底层能力依赖,我们构建了种子变异、异常检测及闪退溯源的基础能力。在此基建上我们实现五类风险类型的挖掘,抽象化挖掘通道,实现挖掘能力可拓展性,当前共实现28种风险类型的覆盖,打通从端到平台的整体链路。

底层依赖模块

在做扫雷时很大程度上需要依赖端上的Hook能力,如拦截Rpc、Http(s) response响应数据,替换注入脏数据挖掘端上稳定性风险。Android上主要依赖DexAOP切面能力,其原理主要通过函数invoke指令替换的方式,在构建时对主dex中的函数调用执行代码插桩切面。IOS端通过Method Sizzing,Method Sizzing是发生在运行时的,主要用于在运行时将两个Method进行交换,我们可以将Method Sizzling代码写到任何地方,只有在这段Method Silzzling代码执行完毕之后互换才起作用。

Android拿PackageManager#getPackageInfo(String, int)切点来说,切之前、之后的代码变化如下

基建能力

想要做好风险挖掘少不了打磨一套完备的种子变异能力,不同文件类型、数据结构有不同的变异策略。其中有针对多媒体Ffmpeg视频流变异AFL Fuzz,以覆盖率为导向,通过Bitflp变异策略挖掘文件风险。除去文件变异,端上大部分都是结构化数据如:Json、Java对象、OC、结构体等,我们基于递归思想构建了结构化数据变异的引擎,解决了结构化数据Fuzz。

异常检测能力线上问题除了稳定性问题外还有大部分非闪退问题,造成端上业务不可用。我们针对不可用问题梳理了对应的特征,实现了端上按钮点击无响应、入口丢失等检测能力,并接入了端智能花屏检测引擎,基于AI图像训练挖掘端上渲染异常。不可用问题千变万化,针对不可用问题检测挖掘,我们只是解决了部分问题,未来还需持续的探索。

闪退溯源能力当我们扫雷挖掘到客户端闪退,通过该能力我们能快速追溯到闪退根因,如configKey:h5_js_ebvie value设置[] ->{} 发生crash,通过CG函数调用图与闪退堆栈关键信息匹配快速溯源定位到闪退开关h5_js_ebvie,协助开发快速修复问题,也能提供攻防演练大量真实演练案例,解决了常态化演练弹药不足问题。

详细方案

以下是端上风险挖掘整体流程以Config为例

端上挖掘流程主要分为以下几部分

HOOK

挖掘的前提需要了解各个风险点的数据流特点,并通过反射代理、HOOK等方式拦截注入脏数据,完成异常注入的第一步。

Android使用DexAOP对各数据通道进行拦截,原理是通过函数invoke指令替换的方式,对主dex中的函数调用执行切面

iOS端Hook通过Method Sizzing,Method Sizzing是发生在运行时的,主要用于在运行时将两个Method进行交换

异常捕获

针对所有类型Config、Rpc、Jsapi注册切面后,我们需要初始化异常捕获,感知客户端闪退。

Android Crash捕获Thread定义的接口 UncaughtExceptionHandler用于处理未捕获的异常,当发生闪退时会调用UncaughtExceptionHandler.uncaughtException获取对应异常信息,可以通过Thread.setDefaultUncaughtExceptionHandler设置线程默认的异常处理器。native的崩溃发生在机器指令运行的层面,当客户端做了内核不可接受的事情,如除数为0让cpu无法识别指令、段错误等等,内核就会向app对应的线程发送信号(signal),默认处理方式是杀掉整个进程,可通过注册信号处理函数来捕获native crash(SIGSEGV, SIGBUS等)。

#include  
int sigaction(int signum,const struct sigaction act,struct sigaction oldact));

iOS Crash捕获主要分为三种

  • Mach exception捕获crash代码逻辑在SLCrashSignalExceptionHandler类的signalForMachException方法中,可捕获的异常如下
    • EXC_ARITHMETIC
    • EXC_BAD_ACCESS
    • EXC_BAD_INSTRUCTION
    • EXC_BREAKPOINT
    • EXC_EMULATION
    • EXC_SOFTWARE
  • signal捕获crash的代码逻辑在SLCrashSignalExceptionHandler类的signalRegister方法中,可捕获的异常如下
    • SIGABRT
    • SIGBUS
    • SIGFPE
    • SIGILL
    • SIGPIPE
    • SIGSEGV
    • SIGSYS
    • SIGTRAP
  • Exception捕获crash的代码逻辑在SLExceptionMonitor类的my_NSSetUncaughtExceptionHandler方法中,主要捕获OC层代码的数组越界、调用未知函数、参数不可用等异常。

捕获到闪退后,根据堆栈进行后续的匹配操作,具体逻辑在SLExceptionMonitor类的callWhenCrash方法中。

种子变异

模糊测试关键其中一块是种子变异能力,优秀的数据流变异策略能生产更加符合场景的测试数据,大大提高挖掘的深度,覆盖更多的业务分支。本方案主要介绍下基于AFL多媒体文件变异与结构化数据变异两种。

多媒体AFL Fuzz

随着客户端对直播短视频业务扩展及多媒体中台统一建设,如何高效治理解决多媒体业务带来的稳定性风险势在必行。针对多媒体文件变异,我们来了解下端上多媒体架构。

如上图,多媒体统一架构至下而上分为MediaFlo、UI层、互动层、对外接口及业务层。其中MediaFlo是多媒体核心基建,包含H264/AAC等音视频编解码能力、美颜视频算法、网络推流rtmp等,并实现多媒体原子能力接口统一输出。

针对多媒体模糊测试,我们瞄准MediaFlo的音视频编解码、音视频算法两部分,将端上MediaFlo代码裁剪实现linux平台移植,并构建多媒体文件输入Input入口,编译插桩执行AFL模糊测试。完成多媒体模糊测试主要分为两部分,MediaFlo裁剪移植与AFL模糊测试两部分。

1.MediaFlo 部分核心组件的裁剪分离化,重新组装了数据流转模式

当下线上运行的 MediaFlo 的运行逻辑如下图所示

数据封装成为 MFPacket后通过 Pipe在原子能力组件间进行流转,每一个原子能力组件又会被封装到一个 Runloop 中进行使用,这种运行模式并不适合进行直接的模糊测试,因为整个数据流转过程会产生一定的 Delay,拖慢整体的模糊测试流程的运行速度

目前通过代码逻辑上的调整,我们基于 MediaFlo 的预设组件来对 MediaFlo 中的原子能力进行了二次组装,得到了一个对模糊测试友好的测试实现方案,该测试方案在测试速度上相较于原始的技术方案有较大提升

  • 脱离 Runloop,原子能力直接进行组合
  • 不使用 Pipe,原子能力间数据直接进行对接

2.AFL执行模糊测试

什么是AFL?AFL是一款测试工具,可以高效地对二进制程序进行Fuzzing,通过对代码插桩afl-g,以覆盖率数据调整样本,提高漏洞挖掘概率,充分挖掘可能存在的风险漏洞,特别适合多媒体文件风险挖掘。如下图是AFL变异原理流程


1.源码编译时进行afl-g代码插桩,以记录代码覆盖率;

2.选择一些输入文件,作为初始测试集加入输入队列;

3.将队列中的文件按一定的策略变异,如Bitflip按位进行翻转,0变1,1变0;

4.如果经过变异,如果覆盖率有变动代表走到新的代码路径,则将其保留添加到队列中,作为优质种子继续变异;

5.整个Fuzz过程不断循环,期间触发了Crash的文件会被记录下来;

AFL Fuzz运行图

结构化数据变异

传统Fuzz是无结构线性二进制数据适用于通过AFL进行突变,结构化变异通过理解结构体内容含义,语义进行数据突变,适合JSON/java对象/OC/结构体。

结构化数据模糊测试两步走

1、递归遍历方式进行数据变异结构化数据无法像二进制流数据方式Fuzz,容易导致还未进入程序内部,就因数据结构异常被丢弃了,针对不同数据类型可以采取不同策略进行修改数据。如JSON可以通过递归方式获取每个子节点数据进行变异,针对安卓Intent/Bundle数据可以通过反射方式修改Field。

2、随机值+语义化变异Fuzz通常是随机性的,不关心数据类型字段含义进行变异,才能挖掘到一些出其不意的风险,这也是模糊测试的特点。有时候我们需要理解某些字段的含义,才能变异出更加符合真实场景下异常数据,走到更深的链路里,比如针对Http请求返回"suess":"false" ,如果返回了False可能导致页面打不开,子页面覆盖不到;发现字段类型是UI文案,则注入长字符或空字符可能导致控件重叠丢失;String可能是JSON/XML针对不同类型执行不同变异策略,在随机值变异的基础上,结合语义化变异能够组合变异出更多异常值,挖掘到更深链路。

如上图,种子变异分为通用变异和语义化变异。通用变异是基于种子数据特征梳理的变异策略,如针对文件类,则使用Bit变异,0变1/1变0等策略改变文件数据;边界值变异则是针对数字、字符长度、图片大小等空间边界进行枚举变异;语义化变异则是针对业务属性、数据类型采用符合业务场景的变异策略,如Lottie动画文件则采取Bit位变异,客户端开关则使用边界值变异,Version版本号则采用版本号变异规则等。

闪退溯源

闪退溯源是什么?

客户端运行时存在同一时刻多种网络数据并发请求,无法知道当前出现的Crash由于哪个Config、Rpc配置错误导致,所以想要定位问题根因,将闪退与变更配置绑定是必不可少的,将某个闪退与某个变更做绑定叫闪退溯源。

为什么风险挖掘需要闪退溯源这个能力?

有了闪退溯源的能力,可以解决以下几个问题

  • 定位问题能定位闪退是由某个开关配置或JSON异常导致,帮助开发快速定位问题修复缺陷;
  • 攻防演练找到闪退根因,复现客户端闪退,进行攻防演练;
  • 提高挖掘深度在客户端Fuzz过程中,进入某个场景Fuzz碰到开关配置异常导致Crash,进入发生多次Crash,导致Fuzz中断,通过闪退溯源我们能够绕过该异常开关配置,进入到其他子页面进行模糊测试。

闪退溯源原理(该方案以Config为例)

要定位客户端闪退是由于哪个Config引起,需要在端上Hook收集Config CallGraph调用栈数据,存储到本地Database中。在客户端出现闪退时获取Crash Trace,并过滤掉系统和三方调用,再和数据库中的Config调用栈数据进行相似度匹配,分析定位出闪退根因,以下是溯源简易流程

闪退溯源方案,主要分为以下几部分

开关切点调用栈收集

Hook开关并在invoke函数中获取当前线程的调用栈,即开关对应的函数调用链路数据。

Android:

IOS:

此时我们拿到的开关调用栈包含了系统和三方sdk的调用数据,我们需要通过关键字android.os、java.等剔除系统调用,拿到属于开关的调用栈数据。如下ta_extHub_api_trace

Android:

iOS:

通过Call Graph生成完整调用图

如上客户端Runtime收集的开关调用栈数据是不完整的,只能收集到开关调用前的链路,开关调用后的数据才是变更溯源的关键信息,我们通过静态代码分析Call Graph能力,获取到开关调用后的下游链路堆栈。

上图上半部是通过运行时hook获取到堆栈调用路径,下半部是需要通过CG求解的路径

有了如上的分析后,我们能够收集到完整的开关调用图,并落到本地数据库。简单介绍下Call Graph。CG是一种流程控制图,表示着目标程序里各个子过程的相互调用关系,上图箭头代表着函数的调用方向,详细CG能力建设有Soot、astparser等静态分析工具,在这里不继续展开。

堆栈相似度匹配

常规的相似度算法如Edit Distance算法是通过比较两个字符串之间,由一个转成另一个所需要的最怪异网小编辑操作次数,一般编辑距离越小,两个字符串的相似度越大。如下图算法,按照levenshtein计算方式abc变换到abe只需要1个替换步骤。



a

b

c


0

1

2

3

a

1

0

1

2

b

2

1

0

1

e

3

2

1

1

综上字符串相似度算法,由于闪退堆栈与开关调用栈数据长度大小不统一相差过大,导致我们无法使用类似的相似度计算公式来匹配,针对堆栈匹配需要另辟蹊径。

本方案主要通过对闪退堆栈进行按行拆分,提取关键词模糊匹配方式获取高疑闪退开关,并按照一定权重排序。如下是开关ebvie_apid配置异常导致客户端闪退的堆栈,结合排查经验,可知道栈顶MyWebVSitch.MyWebVie是导致客户端闪退的根因,搜索数据库开关调用栈,如有包含Class/Method则认为该开关可能是引起闪退的根因,添加到队列中。

大部分情况下,我们拿闪退堆栈栈顶Class/Method去数据库里搜如果有命中开关是比较理想结果,往往数据库里可能找不到数据匹配开关,那么我们需要把闪退Trace从栈顶向栈底按行拆解,可以设置拆解的深度比如5行,并对每一行Class/Method关键字进行拆分多个关键字,按照排列组合方式与数据库里收集的开关堆栈进行模糊匹配,输出匹配结果。下图是闪退堆栈按行拆解,组合关键字匹配流程

当我们根据闪退堆栈拆解出关键字并分析出开关集合后,可根据如下公式计算计算权重分将开关列表进行排序。

排序权重计算公式如上图(N=闪退堆栈大小,dep堆栈层级,ruleIndex为规则下标,factor因子越大权重值阶梯越明显),规则概要如下

  • 层级间,栈顶权重值最大,如 Top>Bottom;
  • 同层级间,规则1 (匹配类名/方法/行号)权重最大,如 rule1>rule2...>ruleN;

通过以上方法我们可以计算出开关调用堆栈与闪退堆栈的权重分数即相似度,并把开关按相似度进行排序,分数越大,命中率越大,通过该方式我们能在线下复现挖掘的风险。

自动化与数据上报

风险挖掘少不了自动化脚本,本方案使用slm自动化UI框架monkey+深度UI遍历,将挖掘的风险上报到服务端蓝军平台,并建根据闪退数据一键“创建缺陷”+“新增演练案例”。

通过线上问题及历史P级故障梳理沉淀,落地了多种风险类型挖掘,提升了挖掘广度,并持续打磨种子突变能力和闪退溯源等原子能力,提升了挖掘深度和代码覆盖率,在端上稳定性挖掘中有不错的收益,做到贴合业务场景发现真实问题。现在阶段挖掘能力以稳定性闪退为主,非闪退问题千变万化,针对不可用问题检测挖掘,我们只是解决了少部分问题,未来还需持续的探索,做好事前防御,把更多问题拦在线下。

作者:凡月

来源:微信公众号:蚂蚁质量AnTest

出处:http://mp.eixin.qq./s/WPjaMmQm9yFM7eh6E4Hg

Copyright © 2016-2025 www.bnfh.cn 怪异网 版权所有 Power by