Janus签名机制漏洞
检测App程序是否存在Janus签名机制漏洞
描述: Google披露了一个名为“Janus”的安卓漏洞(漏洞编号:CVE-2017-13156),该漏洞可以让攻击者绕过安卓系统的Signature scheme V1签名机制,用篡改过的APK覆盖原有的应用,并可访问原应用所有的数据,直接对App进行篡改。由于安卓系统的其他安全机制也是建立在签名和校验基础上的,所以可以说该漏洞相当于绕过了安卓系统的整个安全机制。该漏洞的影响范围:安卓5.0-8.0的各个版本系统;使用安卓Signature scheme V1签名的App APK文件。该漏洞的危害:对存储在原手机上的数据进行读取; 对用户的输入做各种监听、拦截、欺诈,引导用户输入密码,转账;更新Android的系统APP,从获得更高的系统权限,甚至root/越狱,为其他攻击做准备
解决方案 :
-1.及时校验App APK文件的开始字节,以确保App未被篡改;
-2.对应用同时使用Signature scheme V1和Signature scheme V2签名机制
应用签名未校验风险
描述:签名证书是对App开发者身份的唯一标识,开发者可利用签名证书有效降低App的盗版率。未进行签名证书的App,可能被反编译后进行二次打包。重新打包签名的应用,可能导致App被仿冒盗版,影响其合法收入,甚至可能被添加钓鱼代码、病毒代码、恶意代码,导致用户敏感信息泄露或者恶意攻击
解决方案 :
开发者自查:增加签名证书的校验代码,降低App被二次打包的几率
代码未混淆风险
描述: 代码混淆是一种用来隐藏代码结构及流程的技术,可以增加代码阅读的难度。代码混淆通过将Java代码中的方法名,变量名,类名,包名等这些元素名称改成毫无关联且无意义的名字(如单个字母或者无意义的组合),或者对简单的逻辑分支进行混淆,使攻击者难以找到函数调用的内容,无法掌控app内部实现逻辑,从而增加逆向工程和破解的难度。应用代码如果不经过混淆处理,一旦被反编译,源代码将直接暴露给攻击者,造成程序业务逻辑泄露、加解密算法失效、通信加密失效,攻击者可以利用这些信息窃取客户端的敏感数据,包括账号、密码;绕过业务安全认证流程,直接篡改用户账号信息;对服务器接口发起攻击等。虽然代码混淆并不能真正阻止逆向工程 ,但可以增大反编译代码被解读的难度
解决方案:
开发者打包时 应使用混淆模式
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard.cfg'
signingConfig signingConfigs.release
}
}
proguard-android.txt 为sdk目录下自带的混淆规则
proguard.cfg 为满足项目本身或三方引用自定义的混淆规则
使用调试证书发布应用风险
描述:签名证书是验证应用开发者身份的关键标识,可用于判断App是否是由合法开发者发布的正版应用,并且App常使用签名校验作为防止App被二次打包的措施。使用调试证书发布应用,可能导致App无法在应用市场上架,并且debug证书的有效期仅有一年使用;使用debug证书发布的应用可能会出现各个版本的签名证书不一致的情况,这样会导致App应用无法成功升级;同时,证书的不一致性可能造成App使用的签名校验措施频繁改动或者被迫取消,最终导致应用被二次打包
解决方案:开发者自查:使用开发者的release证书统一发布应用,并防止证书泄露
调试日志函数调用风险
描述:调试日志函数可能输出重要的日志文件,其中包含的信息可能导致客户端用户信息泄露,暴露客户端代码逻辑等,为发起攻击提供便利,例如:Activity的组件名,是Activity劫持需要的信息;通信交互的日志,会成为发动服务器攻击的依据;跟踪的变量值,可能泄露一些敏感数据,输入的账号、密码等
解决方案:
开发者自查:关闭调试日志函数调用,或者确保日志的输出使用了正确的级别,涉及敏感数据的日志信息在发布版本中被关闭
应用数据任意备份风险
描述:Android 2.1以上的系统可为App提供应用程序数据的备份和恢复功能,该由AndroidMainfest.xml文件中的allowBackup属性值控制,其默认值为true。当该属性没有显式设置为false时,攻击者可通过adb backup和adb restore对App的应用数据进行备份和恢复,从而可能获取明文存储的用户敏感信息,如用户的密码、证件号、手机号、交易密码、身份令牌、服务器通信记录等。利用此类信息攻击者可伪造用户身份,盗取用户账户资产,或者直接对服务器发起攻击
解决方案:
开发者自查:将allowBackup的属性显式设置为false,以关闭应用数据备份功能;或者使用具有本地数据加密功能的第三方专业加固方案,避免本地数据泄露
敏感函数调用风险
描述:敏感行为包括发送、拦截短信,读取、修改通讯录、通话记录,拨打电话,发送地理位置,使用摄像头,访问浏览器历史记录等。函数调用这些敏感行为,可能导致用户隐私数据泄露,钓鱼扣费等风险
解决方案:
开发者自查:审核包含敏感行为的函数调用,确保其使用是必要且限制于授权用户的
SharedPreferences数据全局可读写漏洞
描述:SharedPreferences作为Android系统的本地数据存储方式之一,可将应用数据以键值对(key-value)的存储形式永久保存于App应用中。当使用SharedPreferences方式在创建本地存储文件时,如果使用了MODE_WORLD_READABLE模式,或者使用了MODE_WORLD_WRITEABLE模式并且配置了“android:sharedUserId”属性值时,可能导致储存于SharedPreferences文件中的敏感信息被其他程序读写,导致应用内明文存储的个人身份信息、密码以及token等重要敏感信息泄露,或者存储的用户信息、历史数据被篡改,诱导用户误操作等。更为严重的是具备root权限的程序或用户可对所有应用程序通过任意模式(包括MODE_PRIVATE)创建的的Shared Preferences文件进行读写操作
解决方案:
开发者自查:避免使用Shared Preferences的存储方式来保存用户名、密码等敏感数据信息,当Android设备被root之后,该安全机制将失效而导致信息泄露;当必需使用Shared Preferences的存储方式来实现功能时,需要将创建模式设置为"MODE_PRIVATE"。避免在进程间通信时使用具有全局可读写模式的Shared Preferences文件来进行数据共享,建议使用更加正式的方式,包括ContentProvider和BroadcastReceiver
SharedUserId属性设置漏洞
描述:SharedPreferences作为Android系统的本地数据存储方式之一,可将应用数据以键值对(key-value)的存储形式永久保存于App应用中。当使用SharedPreferences方式在创建本地存储文件时,如果使用了MODE_WORLD_READABLE模式,或者使用了MODE_WORLD_WRITEABLE模式并且配置了“android:sharedUserId”属性值时,可能导致储存于SharedPreferences文件中的敏感信息被其他程序读写,导致应用内明文存储的个人身份信息、密码以及token等重要敏感信息泄露,或者存储的用户信息、历史数据被篡改,诱导用户误操作等。更为严重的是具备root权限的程序或用户可对所有应用程序通过任意模式(包括MODE_PRIVATE)创建的的Shared Preferences文件进行读写操作
解决方案:
开发者自查:避免使用Shared Preferences的存储方式来保存用户名、密码等敏感数据信息,当Android设备被root之后,该安全机制将失效而导致信息泄露;当必需使用Shared Preferences的存储方式来实现功能时,不要设置android:sharedUserId,并且需要将创建模式设置为"MODE_PRIVATE"
Java层动态调试风险
描述:客户端软件AndroidManifest.xml中的调试标记如果开启,可被Java调试工具例如jdb进行调试,获取和篡改用户敏感信息,甚至分析并且修改代码实现的业务逻辑,例如窃取用户密码,绕过验证码防护等
解决方案:
开发者自查:以发布方式生成apk包,并且确保AndroidManifest.xml中的调试标记关闭
android:debuggable=true
release包中 默认为false
剪切板敏感信息泄露漏洞
描述:Android剪切板在后台可以暂存数据,如果把隐私数据,如密码,存放在剪切板中是不安全的,因为任何的应用程序都可以访问剪切板中的数据,且可以进行任意修改。如果用户复制的是明文敏感数据,那么其它应用程序通过访问剪贴板就可以获取到该明文敏感数据了,甚至可以通过监听剪切板中的内容来获取信息
解决方案:
开发者自查:敏感信息 edittext 尽量屏蔽剪切板。若必须要使用剪切板,确保敏感信息不会被存入
比如 : 登录界面的密码输入框
屏蔽剪切板
HTTP传输数据风险
描述:无线传输的数据能被第三方轻易截获,由于客户端与服务器之间的传输数据遵循通信协议指定的格式和内容类型,如果未使用加密措施,传输数据可被还原成网络层的数据包并进行解包分析,直接暴露用户的各种关键数据,例如用户名,密码等。加入了SSL(Secure Socket Layer)子层实现的HTTPS协议可确保数据在网络上加密传输,即使传输的数据被截获,也无法解密和还原
解决方案:
开发者自查:使用HTTPS协议对传输数据进行加密保护
界面劫持风险
描述:
界面劫持是指当客户端程序调用一个应用界面时,被恶意的第三方程序探知,如果该界面组件是恶意程序预设的攻击对象,恶意程序立即启动自己的仿冒界面并覆盖在客户端程序界面之上。此时用户可能在无察觉的情况下将自己的账号、密码信息输入到仿冒的信息输入界面中,恶意程序再把这些数据返回到服务器中,完成钓鱼攻击。目前主要的界面劫持攻击通常发生在Android5.0以下的设备中。界面劫持风险将导致用户关键信息,例如账号、密码、银行卡等关键信息被窃取等风险
输入监听风险
描述:应用程序中的敏感信息通常主要来源于使用者的直接输入,如果用户的输入数据被监听或者按键位置被记录,很可能导致用户的输入数据被获取,其中的账号、密码等隐私信息泄露。而Android系统的默认输入键盘中通常都面临数据监听的风险
解决方案:
使用安全自绘随机键盘,防止输入内容被监听
自定义随机键盘
截屏攻击风险
描述:截屏攻击是指对App应用运行中的界面进行截图或者录制。截图攻击的主要对象是Android应用中的身份认证、登录界面和资金操作界面。而在Android5.0中新增了屏幕录制接口,无需特殊权限,使用系统API (MediaProjection)即可实现屏幕录制,并且攻击程序可以通过自定义的字符覆盖掉系统的录屏提示,诱导用户在不知情的情况下启动屏幕录制功能。当恶意程序获取到应用截图或者屏幕录像后,将其发送给攻击者,攻击者便能直接查看或者还原手机界面的操作情况,从而轻而易举获取用户QQ、微信等应用的用户名及密码,甚至银行客户端中输入的银行账号及支付密码,导致用户的资金损失
解决方案:
开发者自查:开发者审查应用中显示或者输入关键信息的界面,在此类Activity创建时设置WindowManager.LayoutParams.FLAG_SECURE属性,该属性能防止屏幕被截图和录制。以下为修复代码示例
public class DemoActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(LayoutParams.FLAG_SECURE, LayoutParams.FLAG_SECURE);
setContentView(R.layout.main);
}
}
动态注册Receiver风险
描述:BroadcastReceiver组件的注册方式可分为两种,一种是静态注册,即提前在AndroidManifest.xml文件中声明组件;另外一种是动态注册,即在代码中使用registerReceiver()方法注册BroadcastReceiver,只有当registerReceiver()的代码执行到了才进行注册,取消时则调用unregisterReceiver()方法。而容易被忽略的是registerReceiver()方法注册的是全局BroadcastReceiver,在其生命周期里是默认可导出的,如果没有指定权限访问控制,可以被任意外部应用访问,向其传递Intent来执行特定的功能。因此,动态注册的BroadcastReceive可能导致拒绝服务攻击、应用数据泄漏或是越权调用等风险
解决方案:
开发者自查:
1.开发者可使用LocalBroadcastManager来实现BroadcastReceiver动态注册,保证数据传递仅限于应用内。
以下为修复代码示例:
public static void sendLocalBroadcast(Intent intent) {
LocalBroadcastManager.getInstance(getInstance()).sendBroadcastSync(intent);
}
public static void registerLocalReceiver(BroadcastReceiver receiver, IntentFilter filter) {
LocalBroadcastManager.getInstance(getInstance()).registerReceiver(receiver, filter);
}
public static void unregisterLocalReceiver(BroadcastReceiver receiver) {
LocalBroadcastManager.getInstance(getInstance()).unregisterReceiver(receiver);
}
2如果仅在应用内部通信,可以在AndroidManifest.xml文件中静态注册BroadcastReceiver,同时设置exported="false"。
以下为修复代码示例
<receiver android:name=".DemoReceiver" android:exported="false" />
3.应用在动态注册BroadcastReceiver时,使用registerReceiver(BroadcastReceiver, IntentFilter, broadcastPermission,android.os.Handle)替代registerReceiver(BroadcastReceiver, IntentFilter),指定BroadcastReceiver接收时校验permission
Service组件导出风险
描述:Service作为组成Apk的四个组件之一,一般作为后台运行的服务进程,如果设置了导出权限,可能被系统或者第三方的App直接调出并使用。Service导出可能导致拒绝服务攻击,程序功能被第三方恶意调用等风险
解决方案:
开发者自查:关闭AndroidManifest.xml中的Service组件导出权限,对于必须导出的组件必须限制于授权用户或者应用组件
Intent组件隐式调用风险
描述:Intent通常用于Activity、Service、Broadcast Receiver等组件之间进行信息传递,包括发送端和接收端。当使用隐式的Intent调用时,并未对intent消息接收端进行限制,因此可能存在该消息被未知的第三方应用劫持的风险。Intent消息被劫持,可能导致用户的敏感数据泄露,或者恶意程序执行等风险
解决方案:
开发者自查:采用显式方式调用Intent组件,使用指定Intent消息接收方
反射调用风险
描述:在Java程序中,反射调用可用于强行访问正常途径没有访问权限的代码,在知道目标类的类名和方法名的情况下动态的去调用一些protected甚至是private的方法或类。反射调用在为Java程序对自身进行检查,访问程序的内部属性以及私有方法时提供了便利,但绕过了Java的代码访问权限,也容易留下安全漏洞。使用反射调用机制,可能绕过系统安全设置,访问程序的私有数据,造成用户敏感数据泄露
解决方案:
开发者自查:尽量不使用反射调用机制方法,如果一定要用,则对反射所访问的类以及方法进行审计,杜绝隐私数据及关键方法调用
Root设备运行风险
描述:为了获取更大的手机自主使用功能,如卸载应用、禁用自启动程序等,不少用户会将手机进行Root处理以获取Root权限。Root权限包括:超越任何用户和用户组来对文件或目录进行读取、修改或删除;对可执行程序的执行、终止;对硬件设备的添加、创建和移除等;也可以对文件和目录进行属主和权限进行修改,以适合系统管理的需要。获取Android的Root权限通常是通过系统漏洞,替换或添加可绕过用户验证的可执行SU程序。在给手机用户赋予Root权限的同时,也给了其他应用获取Root权限的可能性。恶意程序可能在用户不知情的情况下申请Root权限,读取到其他应用的文件或者进程中的敏感信息,例如支付宝、手机银行等应用的账号和密码等;或者任意读取手机中的短信记录和联系人信息;也可能获取到系统权限的重启功能,恶意重启或关闭设备
解决方案:
开发者自查:开发者应在应用启动时增加对应用运行环境的检测,当发现运行设备为Root设备时,应禁止应用启动。以下为修复代码示例
1.通过检测指定目录下是否存在su程序来检测运行环境是否为Root设备
public static boolean CheckRootPathSU()
{
File f=null;
final String kSuSearchPaths[] = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/","/vendor/bin/"};
try{
for(int i=0;i<kSuSearchPaths.length;i++)
{
f=new File(kSuSearchPaths[i]+"su");
if(f!=null&&f.exists())
{
return true;
}
}catch(Exception e)
{
e.printStackTrace();
}
return false;
}
当CheckRootPathSU返回值为true时,禁止应用启动
2.通过which命令检测系统PATH变量指定的路径下是否存在su程序来检测运行环境是否为Root设备
public static boolean checkRootWhichSU() {
String[] strCmd = new String[] {"/system/xbin/which","su"};
ArrayList<String> execResult = executeCommand(strCmd);
if (execResult != null){
return true;
}else{
return false;
}
}
public static ArrayList<String> executeCommand(String[] shellCmd){
String line = null;
ArrayList<String> fullResponse = new ArrayList<String>();
Process localProcess = null;
try {
localProcess = Runtime.getRuntime().exec(shellCmd);
} catch (Exception e) {
return null;
}
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(localProcess.getOutputStream()));
BufferedReader in = new BufferedReader(new InputStreamReader(localProcess.getInputStream()));
try {
while ((line = in.readLine()) != null) {
fullResponse.add(line);
}
} catch (Exception e) {
e.printStackTrace();
}
return fullResponse;
}
当CheckRootWhichSU返回值为true时,禁止应用启动
以下问题可以通过加固解决 第三方支持:使用主流的加固厂商提供的专业加固方案
1. 选择专业的加固方案
针对Android平台应用所面临的反编译和二次打包问题,对应用进行加固是目前最有效的解决方案。由于通用加固方案的加固强度较低、加固方式较为普遍,无法有效防止反编译工具的破解,或者容易被脱壳并且反编译,因此,建议采用企业级定制化加固方案,有效地保护源代码安全和防止篡改及二次打包风险
2.源代码反编译风险
Apk如果未采取有效的保护措施,可能面临被反编译的风险。反编译是将二进制程序转换成人们易读的一种描述语言的形式。反编译的结果是应用程序的代码,这样就暴露了客户端的所有逻辑,比如与服务端的通讯方式,加解密算法、密钥,转账业务流程、软键盘技术实现等等。攻击者可以利用这些信息窃取客户端的敏感数据,包括手机号、密码;截获与服务器之间的通信数据;绕过业务安全认证流程,直接篡改用户账号信息;对服务器接口发起攻击等
3.so文件破解风险
So文件为Apk中包含的动态链接库文件,Android利用NDK技术将C/C++语言实现的核心代码编译为so库供Java层调用。So被破解可能导致核心功能的汇编代码甚至源代码泄露,不仅损害开发者的知识产权,并且可能暴露了客户端的核心功能逻辑,攻击者可以利用这些信息窃取客户端的敏感数据,包括手机号、密码;截获与服务器之间的通信数据;绕过业务安全认证流程,直接篡改用户账号信息;对服务器接口发起攻击等
4.篡改和二次打包风险
Apk篡改后二次打包不仅已经严重危害开发者版权和经济利益,而且也使app用户遭受到不法应用的恶意侵害。对客户端程序添加或修改代码,修改客户端资源图片,配置信息、图标,添加广告,推广自己的产品,再生成新的客户端程序,可导致大量盗版应用的出现分食开发者的收入;恶意的二次打包还能实现应用钓鱼、添加病毒代码、添加恶意代码,从而窃取登录账号密码、支付密码,拦截验证码短信,修改转账目标账号、金额等等
5. 动态调试攻击风险
如果App存在C层代码动态调试的风险,攻击者可以利用GDB、IDA、Ptrace等调试器跟踪运行的目标程序,查看、修改内存中的代码和数据,甚至分析篡改程序的业务逻辑,对客户关键数据或者服务器进行恶意攻击,例如修改客户端业务操作的数据,比如转账账号、金额等,导致用户的损失
6.动态注入攻击风险
动态注入是指通过OS特定机制,利用系统API将代码写入到目标进程并让其执行。通过动态注入,攻击者可以将一段恶意代码写到目标进程,这段代码可以加载其它可执行程序,进而实施hook,监控程序运行、获取敏感信息等。常见的动态注入,可以实现窃取输入的登录账号、密码、支付密码,修改转账的目标账号、金额,窃取通讯数据等