导语:在安全界打拼多年的行家一定对以前的安卓移动应用程序的安全性印象深刻,比如你可以毫不顾忌所有的SSL错误,并随意拦截和修改SSL的通信。不过这种好日子已经到头了,目前大多数的应用程序都会检查证书是否是由有效的可信证书颁发机构(CA
在安全界打拼多年的行家一定对以前的安卓移动应用程序的安全性印象深刻,比如你可以毫不顾忌所有的SSL错误,并随意拦截和修改SSL的通信。不过这种好日子已经到头了,目前大多数的应用程序都会检查证书是否是由有效的可信证书颁发机构(CA)颁发的。作为测试人员,我的重要任务之一就是要验证证书是否总是有效和可信的,为此我选用了中间人攻击(MITM)的方式来尝试修改其通信。本文,我将介绍4种可以绕过Android SSL证书检查的技术:
1.将自定义CA添加到受信任的证书存储区; 2.使用自定义CA证书覆盖已封装的CA证书; 3.使用Frida hook并绕过SSL证书检查; 4.逆向自定义证书代码。
这四个技术的执行复杂度是从简单到复杂依次排列的,不过请注意,本文只会介绍大致的操作框架,并不会过多的强调技术细节。
为什么会选择SSL MITM进行测试
为什么我会选择对移动应用程序进行SSL MITM,因为为了查看和模糊移动应用程序的网络服务调用,我需要使用拦截代理(如BurpSuite或ZAP)。当使用代理拦截SSL通信时,来自客户端的SSL连接会在代理处被终止。默认情况下,由Burp等工具生成的自签名证书将不具有有效的信任链,并且如果证书无法验证为可信,则大多数移动应用程序将终止连接,而不会通过不安全的渠道进行连接。所有这4中技术的共同目标,就是试图让移动应用程序信任拦截代理提供的证书。
将自定义CA添加到受信任的证书存储区
避免SSL错误的最简单方法是拥有一个有效的可信证书,如果你可以将新的、受信任的CA安装到设备上,则使用这种技术就相对容易一些。如果操作系统信任你的CA,则它将信任你的CA签名的证书。
Android有两个内置的证书存储,分别跟踪系统存储操作系统(包含预安装的CA)和用户存储操作系统(存储用户安装的CA))信任的CA。
默认情况下,所有应用的安全连接(使用TLS和HTTPS协议)均信任预安装的系统CA,而针对Android 6.0(API级别23)及更低级别的应用程序也会默认信任用户添加的CA存储。应用程序可以使用base-config(针对应用程序范围的自定义)或domain-config(针对每个域的自定义)自定义自己的连接。
这意味着,如果我尝试将MITM应用于Android 6.0或更低版本的应用程序,就可以简单地将我的CA添加到用户添加的CA存储中。当应用程序验证我自定义证书的信任链时,它会在信任库中找到我的自定义CA,并信任我的证书。但是,如果应用程序的目标版本是6.0以上的Android版本,则不会信任用户添加的CA存储。为了解决这个问题,我可以编辑应用程序的manifest,强制它实现以Android 6.0版本的环境实现。目标API级别会在AndroidManifest.xml文件的“manifest”元素的“platformBuildVersionCode”属性中指定。
上面的manifest元素的目标是“platformBuildVersionCode = 25”,我需要将其更改为23。
当应用程序使用此更新的manifest重新封装时,它将信任用户添加的CA存储。再或者,如果需要在特定版本的平台上运行,则可以在APK的“/res/xml/network_security_config.xml”配置文件中定义特定的信任锚(Trust Anchor)。例如,你可以用以下文件定义需要存储在/res /raw /my_ca的新的可信CA:
如果应用程序只是验证所提交的证书是否有效,那么该技术就可以实现SSL MITM。
用自定义CA证书覆盖已封装的CA证书
如果你已成功将证书安装到用户添加的CA存储中,那么应用程序的运行版本就是Android 6.0,并且当你尝试浏览其他受SSL保护的资源时,证书也显示为有效。不过你也许会奇怪,难道应用程序不会因SSL错误而崩溃吗?由于开发者可能已经采取了额外的措施来限制应用程序所信任的CA,所以不会崩溃。回想一下技术1,我定义了一个自定义的信任锚,并提供了一个CA证书的路径,这也是开发人员保护应用程序免受SSL拦截的方法。
如果自定义的证书链正在与应用程序一起使用,则提取APK并用我的自定义CA应该足以使我的拦截证书可信。请注意,在某些情况下,可能需要信任链的附加验证,所以此方法可能会产生一些其它后果。
使用诸如APK Studio之类的工具打开APK,会使与已部署的应用程序捆绑在一起的证书显而易见。在上图中,证书位于‘assets’目录下。我的自定义CA中覆盖一个名为“UniversalRootCA”的证书,这会让应用程序接受我的自定义证书。
使用Frida hook并绕过SSL证书检查
如果安装自己的CA还不足以成功代理SSL通信,那么应用程序就可能正在执行某种SSL Pinning或额外的SSL验证。通常,为了绕过这种类型的验证,我需要hook应用程序的代码,并干扰验证过程本身。这种类型的干扰仅限于root/jailbreak的手机,但在共享库frida-gadget的帮助下,现在可以使用Android应用程序,并在不支持设备的情况下访问Frida功能的完整套件。
如果你之前执行过移动应用程序渗透测试,那么你可能熟悉Frida框架。至于如何完全覆盖Frida的功能,则不在本文的讲解范围。简而言之,它是一个允许你在运行时篡改应用程序代码的框架。通常,Frida将作为一个独立的程序在操作系统上运行,不过前提是要有一个获得Root权限的设备。为了避免这种情况,我可以将Frida Gadget插入到目标APK中, Frida Gadget包含了Frida的大部分功能,但封装在一个动态库中,在运行时由目标应用程序加载,允许你测试和修改目标应用程序的代码。
要加载Frida Gadget,就需要提取APK,插入动态库,编辑一些smali代码,这样我的动态库就会在应用程序启动时被调用,然后重新封装APK并安装它。详细过程你可以点此查看,不过请注意需要手工完成一次,以便了解协同工作的详细过程。但为了节省时间,我还可以使用另一种工具——objection ,objection 是一个轻量级的依赖注入框架。我只需要在命令行上提供目标APK ,Objection会自动完成整个过程。
C:\ >objection patchapk -s test_app.apk No architecture specified. Determining it using `adb`... Detected target device architecture as: armeabi-v7a Github FridaGadget is v10.6.28, local is v10.6.13. Updating... Downloading armeabi-v7a library to C:\.objection\android\armeabi-v7a\libfrida-gadget.so.xz... Unpacking C:\.objection\android\armeabi-v7a\libfrida-gadget.so.xz... Cleaning up downloaded archives... Using Gadget version: 10.6.28 Unpacking test_app.apk App already has android.permission.INTERNET Reading smali from: C:\Temp\tmp8dxqks1u.apktemp\smali\com/test/app/TestMainActivity.smali Injecting loadLibrary call at line: 10 Writing patched smali back to: C:\Temp\tmp8dxqks1u.apktemp\smali\com/test/app/TestMainActivity.smali Creating library path: C:\Temp\tmp8dxqks1u.apktemp\lib\armeabi-v7a Copying Frida gadget to libs path... Rebuilding the APK with the frida-gadget loaded... Built new APK with injected loadLibrary and frida-gadget Signing new APK. jar signed. Signed the new APK Performing zipalign Zipaling completed Copying final apk from C:\Users\cwass\AppData\Local\Temp\tmp8dxqks1u.apktemp.aligned.objection.apk to current directory... Cleaning up temp files...
这样,我就会在我的工作目录中有一个名为“test_app.objection.apk”的文件——objection。默认情况下,将“.objection”附加到原始APK的名称中。我可以像安装任何其他APK一样安装此APK ,这样它就会被adb install test_app.objection.apk推送到我的连接设备上。在目标设备上安装了objectionAPK后,运行应用程序应该会在应用程序启动屏幕上暂停。此时,我可以连接到Frida服务器。
C:\>frida-ps -U PID Name ---- ------ 6383 Gadget C:\>frida -U gadget ____ / _ | Frida 10.3.14 - A world-class dynamic instrumentation framework | (_| | > _ | Commands: /_/ |_| help -> Displays the help system . . . . object? -> Display information about 'object' . . . . exit/quit -> Exit . . . . . . . . More info at http://www.frida.re/docs/home/ [Motorola Moto G (5) Plus::gadget]-> Java.available true Alternatively, Objection supports interaction with the listening Frida server by using the ‘explore’ command: C:\>objection explore ___| |_ |_|___ ___| |_|_|___ ___ | . | . | | | -_| _| _| | . | | |___|___|_| |___|___|_| |_|___|_|_| |___|(object)inject(ion) v1.2.2 Runtime Mobile Exploration by: @leonjza from @sensepost [tab] for command suggestions com.test.app on (motorola: 7.0) [usb] # android hooking search classes TrustManager android.security.net.config.RootTrustManager android.app.trust.ITrustManager$Stub$Proxy android.app.trust.ITrustManager android.security.net.config.NetworkSecurityTrustManager android.security.net.config.RootTrustManagerFactorySpi android.app.trust.TrustManager android.app.trust.ITrustManager$Stub com.android.org.conscrypt.TrustManagerImpl com.android.org.conscrypt.TrustManagerImpl$ExtendedKeyUsagePKIXCertPathChecker com.android.org.conscrypt.TrustManagerImpl$TrustAnchorComparator com.android.org.conscrypt.TrustManagerFactoryImpl javax.net.ssl.TrustManagerFactory$1 javax.net.ssl.TrustManager javax.net.ssl.TrustManagerFactory javax.net.ssl.X509TrustManager javax.net.ssl.TrustManagerFactorySpi javax.net.ssl.X509ExtendedTrustManager [Ljavax.net.ssl.TrustManager;
此时,你应该能够从内置的SSL Pinning绕过函数中获取以下内容:
逆向自定义证书验证码
最后,开发人员可能会选择提供自己的SSL库,而不是依靠系统库来处理SSL证书验证。如果是这种情况,我可能还需要提取APK并将smali逆向回Java,以便我可以查找负责处理证书验证的代码。
使用'dex2jar',语法如下:
生成的.jar文件应该可以在你最喜欢的Java逆向工具(如JD-GUI)中打开。
一旦确定了负责证书验证的代码,你就可以将其完全修复或使用Frida hook所需的功能。为了避免重新构建整个应用程序,通常更有效的方法就是是将负责证书验证的函数hook。
接着使用第3种技术就可以完成对应用程序的测试。这样,你应该就能够使用Frida命令行工具或Objection接口来hook函数。
总结
上面提到的这四种技术应该能满足你在不同的运行环境中,拦截Android SSL通信,并绕过常见的防御措施。另外,通过对objection和Frida的简要介绍,你大概知道了它们具有绕过SSL Pinning和其他防御措施的能力。