1 Fragment注入漏洞CVE-2013-6271检测 # 12001
系统版本小于Android 4.4(API level 19)存在该漏洞。
在API level < 19(即Android 4.4)的app,所有继承了PreferenceActivity类的activity并将该类置为exported的应用都受到Fragment注入漏洞的威胁。因为PreferenceActivity.isValidFragment函数的默认返回值为true,开发者不对该方法进行重写时不会报错。
Google在 Android 4.4 KitKat 里面修正了该问题,PreferenceActivity.isValidFragment函数的默认返回值为false,且要求用户重写该函数验证Fragment来源正确性,若不重写则会报错。
风险等级:中危
检测方式:静态检测
问题示例:
检测API Level小于19的app是否有导出activity继承自PreferenceActivity,如果存在则有注入漏洞。
我们查看一下isValidFragment
的方法体,我们可以看到在Android 4.4版本前该方法的返回值是true
反编译成Smali代码
即
建议:
- 当Android api >=19时,要重写每一个PreferenceActivity类下的isValidFragment方法以避免异常抛出;
- 当Android api < 19时,如果在PreferenceActivity内没有引用任何fragment,建议覆盖isValidFragment并返回false
查阅更多:
- http://developer.android.google.cn/reference/android/os/Build.VERSION_CODES.html#KITKAT
- http://developer.android.google.cn/reference/android/preference/PreferenceActivity.html#isValidFragment(java.lang.String)
2 SQLite数据库日志泄露漏洞(CVE-2011-3901)检测 # 12002
Android2.3.7版本存在该漏洞,其他版本可能也受到影响,4.0.1不受影响。
Android SQLite数据库journal文件可被所有应用程序读取,所有目录对应程序数据库目录拥有执行权限,意味着应用程序数据目录全局访问,/data/data/<app package>/databases
目录以[rwxrwx--x]
权限创建,可导致全局读写。数据库目录下创建的journal文件以[-rw-r--r--]
权限创建,可被所有app读取。
风险等级:低危
检测方式:静态检测
通过检测app的AndroidManifest.xml文件中声明的min SDK版本进行判断。如果min sdk小于4.0.1(Android API Level 14),则判定有风险,否则安全。
建议:
升级到Android4.0.1以上版本或者使用SQLCipher或其他库加密数据库和日志信息。
3 随机数生成漏洞 # 12003
SecureRandom的使用不当会导致生成的随机数可被预测,该漏洞存在于Android系统随机生成数字串安全密钥的环节中。该漏洞的生成原因是对SecureRandom类的不正确使用方式导致生成的随机数不随机。
该漏洞存在于Android 4.2之前(即API < 17),在Android API 17以后SecureRandom的默认实现方式从Cipher.RSA换到了OpenSSL。SecureRandom新的实现方式不能将自己的seed替换掉系统的seed。
风险等级:高危
检测方式:静态检测
问题示例:
检测是否调用了SecureRandom(byte[]seed)或者setSeed(long seed)和setSeed(byte[]seed)方法。1
2
3Ljava/security/SecureRandom;-><init>([B)V
Ljava/security/SecureRandom;->setSeed([B)V
Ljava/security/SecureRandom;->setSeed(J)V
例如:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SecureRandom secureRandom = new SecureRandom();
byte[] b = new byte[] { (byte) 1 };
secureRandom.setSeed(b);
// 对于Android 4.2版本Test1和Test2会返回相同的值
Log.v("wgc","-------------------------------");
Log.v("wgc","Test1:" + secureRandom.nextInt());
SecureRandom secureRandom2 = new SecureRandom(new byte[] { (byte) 1 });
Log.v("wgc","Test2:" + secureRandom2.nextInt());
SecureRandom secureRandom3 = new SecureRandom();
secureRandom3.setSeed(10L);
Log.v("wgc","Test3:" + secureRandom3.nextInt());
SecureRandom secureRandom4 = new SecureRandom();
secureRandom4.nextBytes(b);
secureRandom4.setSeed(10L);
Log.v("wgc","Test4:" + secureRandom4.nextInt());
SecureRandom secureRandom5 = new SecureRandom();
Log.v("wgc","Test5:" + secureRandom5.nextInt());
打印的日志信息见下图:
可见,Test1和Test2使用了相同的自定义种子,替换掉了系统默认的种子,导致随机数的产生结果相同,而Test3使用了自定义、固定的种子,则会产生固定的结果。只有方法4和方法5才真正做到了随机值,Test4在调用setSeed()方法前先调用了一次nextBytes()方法,而Test5则使用默认的参数进行随机数的生成。
建议:
- 不要使用自定义随机源代替系统默认随机源(推荐)除非有特殊需求,在使用SecureRandom类时,不要调用以下函数:SecureRandom类下SecureRandom(byte[]seed)、setSeed(long seed)和setSeed(byte[]seed)方法。
- 在调用setSeed方法前先调用任意nextXXX方法。具体做法是调用setSeed方法前先调用一次SecureRandom的nextBytes(byte[]bytes)方法,可以避免默认随机源被替代,详细见参考资料。
查阅更多: