安卓逆向入门-安全基线 安卓OWASP10 组件安全 Android有Activity、Service、ContentProvider、Broadcast Receiver、Indent五大组件,不需要进行跨应用运行的组件需要在AndroidManifest.xml中将属性android\:exported设为false,否则该组件可被任意应用启动,被恶意调用。实在需要设为true的组件应用android\:permission指定自定义权限,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <permission android:name ="example.permission.USESERVICE" android:protectionLevel ="normal" /> <service android:name ="com.example.haohai.helloWorldService" android:exported ="true" android:label ="@string/app_name" android:permission ="example.permission.USESERVICE" </service > <uses-permission android:name ="example.permission.USESERVICE" />
ContentProvider为外部提供统一数据存储和读取接口,赢对其设置的腹泻访问控制权限:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <provider android:name ="com.example.haohai.HelloWorldProvider" android:authorities ="com.example.haohai.HelloWorldProvider" > <path-permission android:pathPattern ="/Apk/.*" android:readPermission ="com.example.haohai.permission.READ" android:protectionLevel ="normal" /> </provider > <provider android:name ="com.example.haohai.HelloWorldProvider" android:authorities ="com.example.haohai.HelloWorldProvider" android:readPermission ="com.example.haohai.permission.READ" > </provider >
Intent进行组件间跳转有两种方法,一种显式调用,即通过指定组件名,用setComponent
、setClassName
、setClass
、new Intent(A.class,b.class)
等。另一种是隐式调用,配置文件中设置Intent Filter,系统根据action、category、数据等隐式意图进行组件跳转,可能存贷判断失误而导致数据泄露。显式调用方法:
1 2 Intent intent=new Intent (HelloWolrdActivity.this ,TargetActivity.class); startActivity(intent)
数据存储安全 SharedPreference内部用键值对方式存储,XML结构存储在/data/data/packageName/shared_prefs下。MODE_PRIVAT4E表示当前文件私有化存储模式,只能被应用本身访问,写入时覆盖原文件。反之MODE_APPEND模式将追加内容,应设置为MODE_PRIVATE以防被恶意修改或泄露:
1 SharedPreferences mySharedPreferences=getSharedPreferences("HelloWorld" ,Activity.MODE_PRIVATE);
认证安全 WebView自带记住密码功能,密码以明文保存在/data/data/com.package.name/databases/webview.db中,Root后可读,关闭自动保存功能为:
1 myWebView.getSettings().setSavePassword(false );
数据加密 敏感信息建议用SHA-256或SHA-3加密,不建议MD5、SHA-1、PIPEMD。
授权安全 绑定设备时,客户端检测用户是否有权限访问数据或执行操作时,可能出现信息伪造、数据替换等风险。为每个用户分配一个唯一ID,可对所有访问关键数据和敏感操作进行追溯,且该唯一ID应当不可伪造。IMEI国际移动设备识别码在移动电话网络中唯一标识了每台独立的移动通信设备,但模拟器可能会模拟或伪造。为避免该风险,建议用DEVICE_ID、MAC ADDRESS、Sim Serial Number、IMEI进行组装生成哈希后使用。
1 2 3 4 5 6 7 8 9 10 11 TelephonyManager tm=(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); String DEVICE_ID=tm.getDevice(); WifiManager wifi=(WifiManager)getSystemService(Context.WIFI_SERVICE); WifiInfo info=wifi.getConnectionInfo(); String macAddress=info.getMacAddress(); TelephonyManager tm=(TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE); String SimSerialNumber=tm.getSimSerialNumber(); String IMEI=((TelephonyManager)getSystemService(TELEPHONY_SERVICE)).getDeviceId();
其他 SSL通信服务/客户端检测信任任意整数:开发者重写了checkServerTrusted
但没做任何校验,使得可用中间人攻击获取加密内容。
WebView忽略SSL证书错误:WebView调用onReceivedSslError
时直接执行handler.proceed()
来忽略证书错误,可引起中间人攻击。
HTTPS关闭主机名验证:构造HttpClient时设置HostnameVerifier使用ALLOW_ALL_HOSTNAME_VERIFIER或空,可导致中间人攻击。修复应用STRICT_HOSTNAME_VERIFIER并自定义实现。
Intent Scheme URL攻击:AndroidManifest.xml设置Scheme协议后可用浏览器打开对应Activity,攻击者可构造Intent唤起应用相应组件,引起拒绝服务或提权漏洞。修复应配置category filter以添加android.intent.category.BROWSABLE。
AES弱加密:使用AES/ECB/NoPadding或AES/ECB/PKCS5padding模式。修复应指定CBC或CFB模式,填充PKCS5padding填充,密钥至少128位,推荐256位。
Provider文件目录便利:Provider被导出且复写openFile
时没有对Content Query Uri进行判断或过滤,导致文件遍历。
WebView启用访问文件数据:WebView使用默认的setAllowFileAccess(true)
时,则在File域下可执行任意JS代码,绕过同源策略对私有文件目录访问,造成隐私泄露。
Unzip解压缩:getName
获取文件名后未对名称进行校验,被解压文件可进行目录跳转而被解压到其他目录,覆盖相应文件造成任意代码执行。
未使用编译器堆栈保护技术:Stack Canaries、PIE,修复在Android.mk中添加:
1 2 LOCAL_CFLAGS:=-Wall-O2-U_FORTIFY_SOURCE-fstack-protector-all LOCAL_CFLAGS:=-fpie -pie
随机数不安全使用:用SecureRandom类setSeed
时生成随机数有确定性,应用/dev/urandom或/dev/random来初始化伪随机数生成器
AES/DES硬编码密钥。
权限安全 PackageManagerService中有ApplicationInfo.FLAG_SYSTEM标记,或位于特定目录如/vendor/overlay、/system/framework、/system/priv-app、/system/app、/vendor/app、/oem/app等,为系统应用。
PackageManagerService中有ApplicationInfo.PRIVATE_FLAG_PRIVILEGED标记的系统应用为特权应用,可使用protectionLevel为signatureOrSystem或signature|privileged权限。
权限分为正常权限、危险权限、signature、signatureOrSystem。危险权限如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 CALENDAR: READ_CALENDAR WRITE_CALENDAR CAMERA: CAMERA CONTACTS: READ_CONTACTS WRITE_CONTACTS GET_ACCOUNTS LOCATION: ACCESS_FINE_LOCATION ACCESE_COARSE_LOCATION MICROPHONE: RECORD_AUDIO PHONE: READ_PHONE_STATE CALL_PHONE READ_CALL_LOG WRITE_CALL_LOG ADD_VOICEMAIL USE_SIP PROCESS_OUTGOING_CALLS SENSORS: BODY_SENSORS SMS: SEND_SMS RECEIVE_SMS READ_SMS RECEIVE_WAP_PUSH RECEIVE_MMS STORAGE: READ_EXTERNAL_STORAGE WRITE_EXTERNAL_SOTRAGE
signature权限只有当请求该权限应用具有与声明权限的应用相同签名时,不通知用户并授予权限。signatureOrSystem权限时请求和声明权限的应用签名相同或请求权限的为系统应用。
MobSF 安装:
1 2 docker pull opensecurity/mobile-security-framework-mobsf:latest docker run -it --rm -p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest
默认登陆账密mobsf。
IoT入门 IoT安卓应用常见高风险权限:
1 2 3 4 5 6 7 8 9 10 11 ACCESS_COARSE_LOCATION CellID或WiFi 接收到基站服务信号便可获得位置信息 ACCESS_FINE_LOCATION 精良位置GPS CAMERA GET_TASKS MOUNT_UNMOUNT_FILESYSTEMS SD卡内创建删除文件 READ_EXTERNAL_STORAGE READ_PHONE_STATE 获取手机号码、IMEI、IMSI等 REQUEST_INSTALL_PACKAGES 安装未知来源应用 SYSTEM_ALERT_WINDOW 全局弹出系统弹出框 WRITE_EXTERNAL_STORAGE 向SD卡写入 WRITE_SETTINGS 修改系统设置数据
Android数组存储位置有:
1 2 3 4 5 6 /data/data/包名/ /data/data/包名/databases /data/data/包名/shared_prefs /data/data/包名/files/*.realm /data/data/包名/app_webview /sdcard/Android/data/包名
常见攻击方法 重签名绕过 例如签名校验:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 package com.demo.repackage;import android.app.Activity;import android.os.Bundle;import android.util.Log;import com.demo.tools.ToolUtils;public class MainActivity extends Activity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); ToolUtils.getSignature2(getApplicationContext()); if (ToolUtils.checkSignature(getApplicationContext())){ Log.i("checkSignature" , "True" ); }else { Log.i("checkSignature" , "False" ); } } } package com.demo.tools;import android.content.Context;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.content.pm.Signature;import android.util.Log;import java.security.MessageDigest;public class ToolUtils { static String sign = "8b34e97425e0e682e3a73bd55830fc28ce34a4e8" ; public static boolean checkSignature (Context context) { if (sign.equals(getAppSignature(context))){ return true ; }else { return false ; } } public static String getAppSignature (Context context) { try { return sha1(getSignature(context)); } catch (Exception e) {} return null ; } public static String sha1 (byte [] bytes) { try { MessageDigest md = MessageDigest.getInstance("SHA1" ); md.update(bytes); byte [] b = md.digest(); int i; StringBuilder sb = new StringBuilder (); for (byte value : b) { i = value; if (i < 0 ) i += 256 ; if (i < 16 ) sb.append("0" ); sb.append(Integer.toHexString(i)); } return sb.toString(); } catch (Exception e) { e.printStackTrace(); } return "" ; } public static byte [] getSignature(Context context) { try { PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES); Signature[] signatures = packageInfo.signatures; if (signatures != null ){ return signatures[0 ].toByteArray(); } } catch (Exception e) {} return null ; } }
这里通过PacakgeManagerService(PMS)服务获取应用签名信息,通过反射结合动态代理方式控制PMS返回值,达到替换签名的目的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 package com.verify.signature;import android.content.Context;import android.content.pm.PackageManager;import com.verify.signature.ProxyHookPMS;import java.lang.reflect.Field;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class HookServiceWraper { public static void startHookPMS (Context context, String signData, String packageName) { try { Class<?> activityThreadClass = Class.forName("android.app.ActivityThread" ); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread" ); Object currentActivityThread = currentActivityThreadMethod.invoke(null ); Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager" ); sPackageManagerField.setAccessible(true ); Object sPackageManager = sPackageManagerField.get(currentActivityThread); Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager" ); Object proxy = Proxy.newProxyInstance(iPackageManagerInterface.getClassLoader(),new Class <?>[] { iPackageManagerInterface },new ProxyHookPMS (sPackageManager, signData, packageName, 0 )); sPackageManagerField.set(currentActivityThread, proxy); PackageManager pm = context.getPackageManager(); Field mPmField = pm.getClass().getDeclaredField("mPM" ); mPmField.setAccessible(true ); mPmField.set(pm, proxy); }catch (Exception e){ e.printStackTrace(); } } } package com.verify.signature;import android.content.pm.PackageInfo;import android.content.pm.PackageManager;import android.content.pm.Signature;import android.util.Log;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;public class ProxyHookPMS implements InvocationHandler { private Object base; private String SIGN; private String appPkgName = "" ; public ProxyHookPMS (Object base, String sign, String appPkgName, int hashCode) { try { this .base = base; this .SIGN = sign; this .appPkgName = appPkgName; } catch (Exception e) { e.printStackTrace(); } } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { if ("getPackageInfo" .equals(method.getName())){ String pkgName = (String)args[0 ]; Integer flag = (Integer)args[1 ]; if (flag == PackageManager.GET_SIGNATURES && appPkgName.equals(pkgName)){ Signature sign = new Signature (SIGN); PackageInfo info = (PackageInfo) method.invoke(base, args); info.signatures[0 ] = sign; return info; } } return method.invoke(base, args); } } package com.verify.signature;import android.app.Application;import android.content.Context;import com.verify.signature.HookServiceWraper;public class MyApplication extends Application { @Override protected void attachBaseContext (Context baseContext) { String sign = "3082038f30820277a00302010202045ad85776300d06092a864886f70d01010b05003078311230100603550406130952657061636b616765311230100603550408130952657061636b616765311230100603550407130952657061636b61676531123010060355040a130952657061636b61676531123010060355040b130952657061636b616765311230100603550403130952657061636b616765301e170d3232303431323031353032315a170d3437303430363031353032315a3078311230100603550406130952657061636b616765311230100603550408130952657061636b616765311230100603550407130952657061636b61676531123010060355040a130952657061636b61676531123010060355040b130952657061636b616765311230100603550403130952657061636b61676530820122300d06092a864886f70d01010105000382010f003082010a0282010100a207868100beed73e1f04f36435af22054e4ef1eed443c87e73844ece9c1269f40d1ec652528f7c0375cb81ad2edc9acc57136ac2e43252b158d5e8a1118d2d0772aa3b9f0ef6f34906e185eedee4e5dacc45c78b57e1d5b42a031261e3f05cce8055033cfdf4ace0ecdb2ff4c704b0c7d05ef45590040d3921393e15d38b782d1349156155a69461745b83325fed5c4dc8bb2f6c0a74969c1f35e19a5f7ba568e17245360de5c26718454e9a165b857fa236840635b9048bcccf620ea5083cec6b875ee6a541db17d3a8da6a328b237dba73f2d66191593fe76aa9a133cd5dcdafa33d697052b2599c7440689b74434702b6df163152631628985c7eb7120fb0203010001a321301f301d0603551d0e0416041407eaf98b249478511f997b2bb8eea40c236000c4300d06092a864886f70d01010b050003820101004708ad73716e350908dd2bf4fde35667885aa8c0bd3ac5805d9fcee40e1fd70d2cb8db75cb80d9db2bb0e9876f5e7c4bf057816d26592e2608f24ff3b5d9fbe8080ff6f8bfa7ae5bfb48d9a497474005ef51fc41af1d805e9f2bf33786c42b95c488db6c69a201bee659023509ea6182a435330e94f0c1c5ac51a37d3b4785189b66354e9403d6bd72af99b0dd30ae66bfc3a9fc07e8a3c84121d3f6d2dc3941b710b84f46df1604a13c42598dfdc1a6da6fde94a47abf16e042657fd361e8e5cce789b6078b604b0df20abe0d11734fa671fe41eb5c563e27e6568de367fc0c786c90f298cb1783a6881891b04005b0c84079103283b404cdfe7c91d319bf20" ; String packageNeme = "com.demo.repackage" ; HookServiceWraper.startHookPMS(baseContext, sign, packageNeme); super .attachBaseContext(baseContext); } @Override public void onCreate () { super .onCreate(); } }
使用方法是创建一个和目标应用包名相同的新应用,将上述Hook代码编译为APK再解包,获取Smali代码文件。将目标应用也解包,将Hook用的Smali塞到目标应用的Smali文件下,并重打包。