Android逆向学习笔记3
on Blogs
课程来源:bilibli,视频链接:Android逆向学习(自学专用版)
第三阶段:业务APP安全漏洞分析
Android客户端常见安全漏洞:
- 组件/控件安全
- Android组件安全
- Activity界面劫持
- Backup备份安全
- Java调试开启安全
- 本地拒绝服务安全
- WebView远程代码执行安全
- 数据安全
- WebView密码明文保存漏洞
- 本地数据全局读写漏洞
- 界面敏感数据显示泄露
- 调试日志泄露
- 内置账号/IP泄露
- 业务安全
- HTTP/HTTPS中间人攻击
- 数据封包弱加密
- 手机验证码机制安全
- 服务器权限绕过
- 用户敏感信息泄露
- 手势密码绕过
- 会话保持机制安全
- 服务器请求重放攻击
组件/控件安全
Android有四大组件(都可以在AndroidManifest.xml文件中查看):
Activity界面
对于程序中的每一个界面都是一个Activity,每个界面都会有不同的功能,同时界面的切换需要满足一定条件(如果能够绕过条件进行切换,则会有安全隐患)
Service服务
Service服务伴随程序启动,一直在后台运行,主要是起到检测作用的执行代码。服务一般用于时刻检测客户端的更新状态、是否异地登录、上传用户的操作信息。
ContentProvider数据
用于保护和获取数据,并使其对所有应用程序可见,这是不同应用程序之间数据传输的唯一方式
Broadcast广播
当前程序检测到外界的某种运行环境发生变化时执行的逻辑代码,比如程序的自启、网络变化、实时消息(打车软件)
四大组件的访问权限控制主要是由android:exported
属性进行控制,true表示可以对外进行访问或者是用;false表示不可。
- 当Activity的exported设置为true时,可以在免Root环境下直接打开,如指令
adb shell am start -n 包名/Activity名
。
Backup备份安全
Android API Level 8 及以上的安卓系统提供了为应用程序数据的备份和恢复功能。此功能的开关位于应用程序中AndroidManifest.xml文件中的allowBackup属性值,其属性值默认是true。当其为true时,用户可以通过adb backup
和adb restore
来执行对应用数据的备份和恢复,这可能会带来一定风险。
备份指令:
adb backup -nosystem -noshared -apk -f com.xx.xxxx F:\Restore
还原指令:
adb restore -f F:\Restore -apk com.xx.xxxx
Java调试开启风险
Java调试的开启是指在客户端开发时,主配置文件AndroidManifest.xml中设置了Debuggable的属性为true,会产生以下风险:
- jdb调试:获取和篡改用户敏感信息,甚至分析并修改代码实现的业务逻辑,例如窃取用户密码,绕过验证码防护等。
- Release和Debug的调试:这两个模式下,程序运行着两种不同的逻辑流程,如
- Debug走程序中的内测流程,使用内网IP,Log调试日志全开;
- Release下则走线上发布的版本。
Android的本地拒绝服务漏洞
拒绝服务:用户使用不了APP,APP产生崩溃、假死等。
通常情况下程序A和程序B是平行运行的,互不干扰,但在某些情况下会产生影响,如程序A有漏洞,程序B可以攻击:
- 程序A读取公共区域的数据,程序B把公共数据修改了, 程序A在读取时出现解析错误导致崩溃;
- 程序A的Android组件有暴露的情况,可以被程序B任意的调用,程序B在恶意调用A时传递恶意参数,从而导致A崩溃。
但是有一种情况是不能称为漏洞的:当程序B拥有Root权限时,可以直接杀死进程A,此时不能视为A有漏洞。
Android提供了Intent机制来协助应用间的交互和通讯,Intent负责对一次操作的动作、动作涉及的数据进行描述,系统根据Intent描述来调用相应的Activity、Service和BroadCast等组件,完成组件调用。如果程序没有对Intent.getXXXExtra()
获取的异常或者畸形数据处理时没有进行异常捕获,就会导致攻击者可通过向受害者应用发送此类空数据、异常或者畸形数据来使该应用崩溃,简单的说就是通过intent发送空数据、异常或畸形数据给应用,会导致其崩溃。
程序崩溃有以下四种情况(通过查看日志了解是哪种报错):
有一款工具可以查看程序对外暴露了哪些组件,并测试是否存在有崩溃的可能:超级拒绝服务漏洞检测工具。
WebView常见安全漏洞点
WebView是APP的内置浏览器,其可能存在的问题:
当用户登录后,用户的登录状态怎么从原生控件转到WebView能识别到的Cookie
通常情况下,原生的用户状态是token,但是WebView使用的是Cookie,二者需要进行转换,会存在风险,如:
- 转换时使用的是HTTP;
- 转换的时候忽略了token,只认id,就容易出现越权漏洞。
WebView有很多设置点,如果设置不准确就会出现问题,容易出现问题的有:
- setSavePassword
- addJavaScript
WebView加载的页面的源代码容易暴露:这些代码都是在前端完成的, 而前端的代码只能混淆不能加密,因此存在暴露的风险
WebView密码明文存储漏洞
(mWebView.getSettings().setSavePassword(false))
在使用WebView过程中忽略了WebView的setSavePassword,当用户选择保存在WebView中输入的用户名和密码,则会被明文形式保存到应用数据目录的databases/webview.db中。
WebView忽略SSL证书验证错误漏洞
(handler.proceed())
WebView组件加载网页发生证书认证错误时,该方法调用了handler.proceed()来忽略认证错误的问题
WebView远程代码执行漏洞
漏洞成因: Android系统为了方便应用中Java代码和网页里的Javascript代码进行交互,在WebView控件中实现了addJavascriptInterface接口,网页中的Javascript代码可以利用该接口定义的名字调用Java层的代码。而Java对象继承关系会导致很多Public的函数(其中包括getClass)都可以在JS中访问,所以JS可以通过该接口利用Java的反射机制获得系统类的函数,绕开Webview所在的代码上下文。 该漏洞曾在乌云上风靡一时!然而,该漏洞已一去不返,目前只存在Android4.4.2 (API:17)及以下。
本地保存数据安全分析
- 检测APP客户端的隐私数据的工具:MT管理器、RE管理器,但是手机需要root
- APP本地保存数据的隐私路径在
/data/data/packagename
下 - 敏感数据的保存的关键文件夹:
- shared_prefs:内部保存的都是xml文件,有可能保存用户信息、账号信息、设备信息等
- databases:SQL数据,一般保存联系人信息、聊天记录、多账号登录信息等
- files:保存开发者自定义开发的一些数据
- app_webview:保存的是WebView控件产生的数据,可能会有cookie
APP在创建数据库时,将数据库设置了全局的可读权限,攻击者恶意读取数据库内容,获取敏感信息;
在设置数据库属性时如果设置全局可写,攻击者可能会篡改、伪造内容,可以能会进行诈骗行为,造成用户财产损失。
以下为一种实现方式的漏洞代码样例:
try {
SQLiteDatabase rdb = openOrCreateDatabase("all_r_db", Context.MODE_WORLD_READABLE, null);
SQLiteDatabase wdb = openOrCreateDatabase("all_r_db", Context.MODE_WORLD_WRITEABLE, null, null);
} catch(SQLiteException e) {
e.printStackTrace();
}
正确做法:应该是使用MODE_PRIVATE
模式创建数据库 **App本地数据保存需要关注的点**
- App客户端本地需要保存哪些数据,不能保存哪些数据
- 密码的相关数据 (x)
- 会话保持相关的数据。
cookies/token/session(需要加密)
- 个人隐私数据
- 敏感的个人信息
- App客户端本地保存的数据应该存档的路径、文件的格式
- SharePerefence格式/Sqldb数据库格式
- 私有目录下/SD卡路径下(不能使用SD卡保存数据)
- App客户端本地保存数据的形式
- 明文/密文
- 对称算法/非对称算法
Fiddler
一个抓包的工具:Fiddler,该工具的好处:
- 直接截获HTTP/HTTPS支持各种查看格式,比较友好;
- 手机端发出的HTTP/HTTPS,从而调试程序;
- 截获HTTP/HTTPS后,可以任意修改Request或者Responce
使用该工具必要的条件:安装Fiddler的机器,需要跟安卓手机在同一个网络里,否则手机不能把HTTP发送到fiddler的机器上来
有一种组合可以抓取95%以上的APP网络请求数据方式(均为手机APP):Xposed + JustMePlush + Postern
- Xposed: Patch系统函数的框架,可以在这个框架上自定义各种上帝模式的插件,可以修改系统、干涉其他进程(hook等);
- JustMePlush: 是一个Xposed插件,可以对系统验证SSL证书的函数、okHttps框架证书校验的函数进行Patch;
- Postern: 是设置系统中全局代理的工具,类似于VPN。
如果在抓包的时候发现没抓到(或者为灰色),在JustMePlash多点击几次,然后重启模拟器,重新抓包。
剩下的5%:
- 不支持模拟器运行不支持Root .不支持Xposed等等 换真机,然后在真机上配置Xposed+JustMeplush+Post.ern… 不支持Root: 隐藏Root 不支持Xposed: 隐藏Xposed
- 双向校验: 在APP包体内找证书文件:
.p12
.pfx
后缀证书文件; 找证书的安装密码
网络协议数据包弱加密检测
数据加密方式:
- 网络请求数据整体加密Qm get/post的数据是一个单独的太长串,这就整体加密 即:加密函数(username=admin&paswword=123456) —> 一个大长串。
参数的分别进行加密 username=加密函数( admin)&password=加密函数(123456),然后传到服务器
- sign的加密 MD5(usecname=加密函数( admin)&password=加密函数(123456)) ==== sign的值 为了防止黑客单独修改内部某个参数的值。
对于APP客户端来说,包括应用程序(Android/IOS/Html5/小程序)最常见的算法:
- 对称算法: AES DES 3DES PEB等,ECB/BCB模式; 在做算法逆向的时候,最重要的是找到key/IV
- 非对称算法:(公钥加密,私钥解密)RSA等。一般公钥在APP内
- 哈希算法: MD5 SHA-1 SHA-256等, 一般做验签、验证使用。
- 国密算法: SM3, SM4等
对于APP的算法逆向,重心是找key
有一个逆向工具:RECalcTool,该工具可以计算哈希值、编码解码、解压、加密算法解密等等
所谓的弱加密是指的其加密过程为国际通用算法,且调用的函数为系统函数。APP网络协议封包普遍存在弱加密易解密的主要原因是:Key/IV/CER等密钥的硬编码。 客户端App弱加密算法通用检测方案
➢Xposed框架: inspeckage Inspeckage是-一个为Android应 用程序提供动态分析的工具。通过应用hook到Android API的功能,Inspeckage将帮助您了解Android应用程序在运行时做什么。
- 本地数据
- 网络传输数据
- 通用算法hook
- 自定义方法hook
- ……
➢注入框架: Frida Frida是一款基于python + java的hook框架可运行在Android/iOs/Windows/等各平台,主要使用动态二进制插桩技术。
验证码机制
手机短信验证码手机验证码通常作为个敏感操作过程的二次校验因子。
- 双重校验的登录:验证密码+验证手机验证码
- 大额转账的时候:支付密码+手机验证码
手机验证码的机制的安全性,可以从以下几个方面来说:
无限请求。对向服务器请求下发验证码的时候,是否可以无限制次数的重复请求。如短信炸弹!
防范机制:在60秒之内只能向同一个手机号发送一条短信;
验证码重复使用。
验证码是否是真正随机。
看是否存在测试的验证码,比如说测试用的验证码时1497。
假如验证码的生成与时间戳有关系,我向服务器发送请求验证码—->服务器返回:时间戳+手机验证码—–>用户输入验证码,发送请求:时间戳+手机验证码。用户只需要将时间戳和验证码对应就行了
关于这一条,其实时间戳+手机号+手机验证码可能可以解决(个人的想法,将这三者结合计算一个哈希值之类的作为消息传送)
手机验证码返回本地。服务器将验证码明文或者验证码的MD5返回APP。
验证码的错误次数是否有限制。(防止爆破)4位验证码不超过5分钟爆破。
手机验证码的其他绕过手段:如修改false/true,APP的界面强制跳转。
- 图形验证码必须要是服务器下发。本地生成图形验证码是可以通过网络抓包绕过。
- 图形验证码的刷新问题。图形验证码要在用户操作失败的时候及时更新,比如任何情况下的登录失败,都必须要更新图形验证码。
- 图形验证码的识别性。
Xposed框架hook插件编写
编写准备(直接在AndroidStudio编写就行)
需要将XposedBridgeAPI放到工程中(libs文件夹下)
在加入的位置右键,选择Add As Library
修改build.gradle
- 删除
implementation files('lebs\\XposedBridgeApi.jar')
- 将
implementation fileTree
改成provided fileTree
- 删除
在
AndroidManifest.xml
中修改配置,使得Xposed知道这个是一个框架在application中添加meta-data片段
<meta-data android:name="xposedmodule" android:value="true"/> <!-- 表示编写的是Xposed插件 --> <meta-data android:name="xposeddescription" android:value="sample"/> <!-- Xposed插件内容描述 --> <meta-data android:name="xposedminversion" android:value="54"/> <!-- Xposed插件支持的最低版本 -->
编写hook,以下是一个样例(部分)
public class xposeds implements IXposedHookLoadPackage{
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
String TAG = "Xposedhook";
String packname = loadPackageParam.packageName; // 获取当前加载的包名
Log.e(TAG, "启动的APP:"+packname);
if(packname.equals("com.example.vaoqian.mytext")){ // 当加载的是我们要hook的包时进行操作
Log.e(TAG, "hook successfully! start");
XposedHelpers.findAndHookMethod("com.example.vaoqian.mytext.MainActivity", // 要hook的Activity
loadPackageParam.classLoader,
"isOK", // 要hook的函数名
String.class, // 函数的参数
String.class, // 函数的参数
new XC_MethodHook() {
// hook操作函数
// 示例中是要获取用户名和密码,而这两个作为参数传递到isOK函数中,因此在before中操作
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// 在目标函数调用之前执行的操作
super.beforeHookedMethod(param);
String account = (String) param.args[0];
String password = (String) param.args[1];
Log.e(TAG, "用户输入的用户名:"+account);
Log.e(TAG, "用户输入的密码:"+password);
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// 在目标函数调用之后执行的操作
super.afterHookedMethod(param);
// 模板获取正确的用户密码示例
Class clazz = para.thisObject.getClass(); // 获取当前运行的类
Field username = clazz.getDeclaredField("ACCOUNT"); // 获取ACCOUNT对象
Field password = clazz.getDeclaredField("PASSWORD");
username.setAccessable(true); // 设置对象可以访问
passworc.setAccessable(true);
String usernameS = (String) username.get(param.thisObject); // 类型转换
String passwordS = (String) password.get(param.thisObject);
Log.e(TAG, "username:" + usernameS + "password:" + passwordS);
// 也可以直接设置函数的返回值
param.setResult(true);
}
});
}
}
}
- 告诉Xposed框架自己编写的hook类的位置:
src -> main -> assets
下写一个初始化,xposed_init
文件,在里面写上自己刚刚写的类的路径,如:com.xposeds