你的App正在裸奔!

作者:dfqin

地址:https://www.jianshu.com/p/fe0206f8be5b

前言

上周的12306用户信息泄露,本周关注度依然持续!近期又有消息爆出数据泄露事件属实。上周就有同学留言希望看关于安全的文章,今天就让我们一起了学习dfqin同学带来的移动安全的分析!

网络请求——裸奔的数据

无论是网页还是APP,都不可避免与后台服务进行访问,可能从服务器获取数据或者提交数据到服务器,这时就需要客户端发起网络请求。由于http协议简单、灵活和无状态等特点,目前大部分应用都是采用的http协议进行的网络请求。一个常见的网络请求如下图:

无处不在的安全隐患

因为http协议是明文传输的,所以上面的用户名和密码也是明文在网络中传输的,在传输过程中的任何一个环节,攻击者都可以直接看到明文数据。当然现在很多系统都不传输和保存明文账号密码,通常会采取MD5值传输和存储,之前比较出名的CSDN数据库泄漏,就是因为密码是明文保存,黑客可以方便的拿去撞库,加重了事件的严重性。

近几年MD5破解能力提高,例如黑客看到库中一个密码数量比较多,就可以尝试去爆破,加上也可以直接用密码的MD5值去撞库,所以现在通常生成MD5值时都需要加盐,例如MD5(name+pwd)作为密码存储,同样的密码生成的值是不一样的,在一定程度上提高了安全性。


攻击不一定需要用户名密码,或许可以直接打token和uid的主意。通过上图可以看到,通过上图可以看到,一旦用户登录后,服务器就通过token来标识身份。如果这个token被黑客劫持了,他就可以冒充你的身份进行攻击,如果token机制设计不合理,攻击者甚至可以直接暴力去撞token。大部分网站的机制也类似,服务器通过一个sessionId标识用户,攻击者一旦拿到这个sessionId,就可以去冒充一个合法用户。

当然,虽说这些数据明文传输,但是如果你不是网管、网络运营商,想拿到这些数据也不是那么容易,要不然也不会有那么人在公共场合开放WIFI去钓鱼了。即使抓不到别人的数据,也并不意味着什么也做不了,想想看我们能做什么?抓不到别人的数据,可以抓自己的数据,分析服务器接口,寻找设计漏洞做突破口。下面是一些应用场景:

  • 微信投票刷票。微信中充斥着各种投票活动,什么萌娃大赛、最美警察、舞蹈之星。基本都是一个链接甩过来,打开先提示获取你的公开信息(昵称、头像等),确认后通常是一个90年代风格粗糙的网页,找到对应编号的参赛者后,勾选提交。做这些操作前,设置好你的代理服务器,看看投票时发送的请求参数,多半情况下你会发现一些问题,用postman验证你的想法,然后差不多花半个小时,就能写一个刷票工具。这时你可以思考下,怎么设计才能让投票程序更健壮。

  • 微博xss事件。微博API本身是开放的,通过xss漏洞,注入调用关注、转发、私信API的js脚本。

  • 微信、点评、微博等机器人。在很多行业,大众点评网站中商户的好评差评,对商户的生意和口碑影响很大,于是出现了很多水军刷好评和恶意诋毁对手的事件,人肉养号和刷评论很显然成本比较高,加上活跃度越高的用户级别越高,级别越高的用户评论时对商户打分的权重也越高,所以当时有很多养机器人小号做不法勾当,直到后面公司投入很大资源成立专门的诚信团队这种现象才好转。微信公众号也有同样问题,一个名人公众号发篇文章才几百的阅读量,这样多没面子啊,花点钱找水军刷刷量吧,印象中微信针对这个事也从政策和技术上整顿过好几次了。微博就不多说了,我甚至怀疑官方的态度,水清则无鱼嘛。甚至连苹果APP Store都不能幸免,不过只能人肉和众包的形式。

使用https是否就万事大吉

https分单向认证和双向认证,大部分的应用场景是c/s模式,这时通常都是采用的单向认证的方式,也就是说可以保证客户端拿到的数据是后台发送的。这时想攻击确实很难了,除非你能忽悠别人安装并信任你的证书,实际上也并不是做不到,很多人蹭wifi时或者在网上下载一些资源,系统提示要信任什么东西,看都不看就点确定。

这些暂时不提,上面提到的那些攻击,都依赖于对后台接口的分析和调用,对于这类攻击者来说,你使用http和https是没有区别的。


使用抓包工具时,给目标设备安装并信任装包工具的自签名证书,这时候就可以分析https请求了。下面是正常抓https请求的包和配置过证书后的抓包

使用签名和加密数据

上面可以看到,https并不能阻挡攻击者分析请求接口并发起攻击,为了增加攻击者分析请求的难度,通常可以采用两种方式:

使用签名。即给你的请求参数添加一个签名,后台服务接收到请求时,先验证签名,签名不正确的话,则不予处理。签名规则五花八门,大致策略就是根据请求参数做一些运算最后生成一个唯一的字符串当做sign,微信支付的签名大家可以做一个参考:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3

数据加密。post到服务器和从服务器返回的数据都做加密,这样的话即使攻击者拿到你的数据,他不知道你的加密算法就无能为力了。

上面说的两种方式可以同时使用,但是大家还需要考虑一个问题:如何防止攻击者获取到你的签名生成规则和加密算法,例如你加密使用的AES算法,你的秘钥放在哪里呢?

反编译,让你的代码没有秘密

这里主要指Android项目。大家知道,很多应用尤其是单机版游戏类,都只有iOS版本,没有Android版本。一些联网游戏,有Android版本用户数据和iOS用户数据也是隔离的。这些现象的原因,一方面是Android应用分发的混乱,另一方面,就是比较低的反编译和二次打包发布的门槛。

项目结构

Android项目早期没有自己单独的IDE工具,是使用Eclipse加ADT插件进行开发,项目结构跟其他Java项目一致,即一个workspace表示一个工程,里面一个一个的project表示子项目。后面google推出了基于IntelliJ IDEA的Android Studio,项目结构也顺其自然的变成了Project+moduled形式。虽然目录层级有所变化,但是整体结构变化不大。


我们可以看下图,是一个典型Android项目,PermissionGrantor是项目名称,里面有两个子项目,分别是app和grantor,两个子项目图标看起来不一样,是因为他们一个是应用项目,即编译后可以生成一个可以在Android设备上运行的应用,另外一个是库项目,可以生成一个被其他项目引用的aar库。


我们看一下app项目即应用项目,红框标注的是几个比较重要的目录,libs是用来放项目依赖库的,java目录是项目的代码,res则是存放资源,包括图片、布局、字符串等:

最下面的AndroidManifest.xml可以理解成项目的配置文件,操作系统加载一个应用,从哪里开始执行、每个页面调用那个类和都有哪些服务哪些权限,都需要在这里配置。从这里可以透露一个很重要的信息,就是应用的入口和各个模块甚至是服务的类,都可以从这里看到,而且是必须要配置到这里并且这个文件是要附带到发布APK里面的。

编译与反编译

2.1 混淆

项目编译正式包时,默认是开启混淆的,即类名方法名会被混淆成a,b,c,d的样子,增加破解着的难度。Android采用的Proguard的开源方案,只能对java类进行混淆。上面的分析中我们知道,程序的启动类(Application)和业务类(Activity)都需要注册到AndroidManifest.xml中,而混淆只能对java类进行混淆,并不支持xml这种配置文件,所以Android默认对于Application、Activity、Service、UI相关的类都是不混淆的,而一些通过字符串反射生成类的地方也不能混淆(网络数据转为本地java对象),而jni调用方法也不能混淆。下图是QQ阅读反编译后的一个Activity和数据对象。


通过上面分析,我们发现虽然Android引入了混淆技术,但是由于当前混淆方案的缺陷和Android系统框架中大量使用反射技术,使得很多重要的类不能混淆,这极大的降低了反编译后阅读源码的成本。前文中提到的使用签名和加密算法,这时你会发现,被破解也不是特别的难,为了增加破解难度,我们可以把算法放到native代码中,即用C/C++实现算法,通过jni调用。

2.2 编译

项目编译成apk文件,主要分为以下几部分:

  • xml 编译为二进制文件。xml中所有字符串都会被放到一个资源池中,原来使用字符串的地方会被替换成资源的地址。所以xml编译为二进制不仅可以减少占用的空间,而且可以提高加载速度。

  • 生成resource.arsc资源索引表。

  • Java生成dex文件。

2.3 签名

APP编译成apk包后,必须对包进行签名才能在手机上安装。我们分析签名后的包会发现里面多了MANIFEST.MF、CERT.SF和CERT.RSA三个文件。我们直接用文本编辑器打开MANIFEST.MF如下图:

可以看到这个文件里面存储了每一个资源的SHA1摘要的base64值,这里就可以解释你直接替换了apk包里面的某个资源为什么apk无法安装,因为程序在安装时验证资源的摘要跟MANIFEST.MF中的不一致,可以判断出这个包被人动了手脚。


我们继续用文本编辑器打开CERT.SF,如下图:

我们会发现除了最上面多了一个SHA1-Digest-Manifest值,下面的文件跟第一个文件没区别,我们可以测试得知,这个值就是第一个文件MANIFEST.MF的SHA1摘要的base64值。

所以上面提到的,替换APK资源文件后,如果你同时更新MANIFEST.MF中对应的值也不行,因为此时通不过CERT.SF的验证。那么我同时修改CERT.SF中的值呢?我们可以继续看第三个文件CERT.RSA,我们发现用文本编辑器打开,它是二进制文件,可以通过下面的命令打开此文件:

openssl pkcs7 -inform DER -in CERT.RSA -noout -print_certs -text

打开后的内容为:

网上有资料解析这个结构,我们不展开分析,只要知道,上面那一坨16进制编码是签名的公钥,下面那一坨是签名,即用你keystore文件中的私钥对上面的域和值进行加密得到的结果。程序安装时使用上面的公钥进行校验,如果你拿不到对方的私钥就没办法对签名进行伪造。

这就回答了上面抛出的问题,替换apk包中的资源后,同时修改MANIFEST.MF和CERT.SF也无法通过验证。

但是如果我同时把上面的公钥也替换了呢?答案是当然可以通过验证啊。因为你的这一系列动作等同于对APK重新签名了,不过使用的是你自己的私钥。

Android采用的是自签名的证书,所以如果有人拿到了你的APP,用自己的keystore重新签名然后发布,用户并不知道他是山寨的还是正版的。这就解释了为什么很多单机游戏都不做Android版本的,你辛苦做一个收费的游戏,攻击者很容易破解在里面加点广告,然后再二次打包发布。

一些不愿意花钱买游戏的用户只需要忍受下广告就可以免费玩游戏了。通过这里的分析,我们知道攻击者很难伪造我们的签名,所以我们可以在程序中插入签名检查的代码,提高攻击门槛。

反编译

上面已经把APK结构分析的差不多了,反编译要做的工作就是反汇编.dex和还原xml文件。我们可以使用apktool直接对一个apk进行反编译,可以得到还原后的xml文件和.smali文件。这里的smali文件是可以运行在android虚拟机上的汇编语言,读起来还是有些晦涩的,如果有读源码的需求,可以直接解压apk包,使用dex2jar工具直接反编译dex文件,可以得到反编译后的class文件,即我们能得到反编译后的java源码。

我们前文中提到的加密算法和秘钥,就可以通过分析反编译后的源码去获取。为了不让别人获取类似敏感信息,很多人选择这些信息继续向底层放到lib.so中,下面一节我们会介绍。

未完待续!

今日问题:

你了解多少移动端安全知识?

打卡格式:

打卡 X 天,答:xxx 。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值