绕过 Android SSL 验证和证书锁定的四种方法

移动应用程序完全忽略所有SSL错误并允许您随意拦截和修改其流量的日子已经一去不复返了。取而代之的是,大多数现代应用程序至少检查提供的证书是否已链接到有效的受信任的证书颁发机构(CA)。作为测试者,我们希望说服该应用程序,我们的证书是有效且受信任的,以便我们可以对它进行MITM攻击并修改其流量。在此博客中,我将介绍4种可用于绕过Android上的SSL证书检查的技术:

  • 将自定义CA添加到受信任的证书存储中
  • 用自定义CA证书覆盖打包的CA证书
  • 使用Frida挂钩和绕过SSL证书检查
  • 逆转自定义证书代码

这些范围从非常简单到非常高级的执行范围——该博客将尽力涵盖每个方面,不会因为具体情况而使你陷入困境。

SSL MITM ——为什么?

为什么我们需要特别注意移动应用程序的SSL MITM条件?为了查看移动应用程序的Web服务调用,我们需要使用诸如BurpSuite或ZAP之类的拦截代理。使用代理拦截SSL流量时,来自客户端的SSL连接在代理处终止——代理发送的用于标识自身的任何证书都由移动应用程序评估,就好像代理是Web服务终结点一样。默认情况下,由Burp等工具生成的自签名证书将没有有效的信任链,并且如果无法将该证书验证为受信任的证书,则大多数移动应用将终止连接,而不是通过可能不安全的通道进行连接。以下所有技术均具有说服移动应用程序信任我们拦截代理提供的证书的共同目标。

技术1 –将自定义CA添加到用户证书存储中

避免SSL错误的最简单方法是拥有有效的可信证书。如果您可以将新的受信任的CAs安装到设备上,则这相对容易——如果操作系统信任您的CA,它将信任由您的CA签名的证书。

Android具有两个内置的证书存储,用于跟踪操作系统信任哪些CA——系统存储(保存预安装的CAs)和用户存储(保存用户安装的CAs)。

默认情况下,来自所有应用程序的安全连接(使用TLS和HTTPS等协议)信任预安装的系统CAs,面向Android 6.0(API级别23)及更低版本的应用程序也信任用户添加的CA存储。应用程序可以使用base-config(用于应用程序范围的自定义)或domain-config(用于每个域的自定义)来自定义其自己的连接。

这对我们意味着什么?如果我们正在尝试的MITM应用程序的目标是安卓6.0或更低,我们可以简单地将我们的CA添加到用户添加的CA存储。当应用程序验证我们的自定义证书的信任链时,它将在信任存储中找到我们的自定义证书,并且我们的证书将被信任。然而,如果应用程序的目标是6.0以后的安卓版本,它将不会信任用户添加的CA存储。为了绕过这一点,我们可以编辑应用程序的清单,并强制它以安卓6.0为目标。目标应用编程接口级别在AndroidManifest.xml文件中“清单”元素的“platformBuildVersionCode”属性中指定。如下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.test.app"platformBuildVersionCode="25" platformBuildVersionName="7.1.1">

上面的清单元素目标是“platformBuildVersionCode=25”,我们需要将其更改为23。

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.test.app"platformBuildVersionCode="23" platformBuildVersionName="6.0">

当应用程序用这个更新的清单重新打包时,它将信任用户添加的CA存储。

或者,如果需要在特定平台版本上运行,我们可以在APK的“/RES/XML/network _ security _ config.XML”配置文件中定义特定的信任锚。例如,下面的文件定义了一个新的受信任的证书颁发机构,它需要存储在/res/raw/my_ca(来自http://developer.Android.com/training/arti...):

1. < ?xml version="1.0" encoding="utf-8"?>
2. < network-security-config >
3. < base-config >
4. < trust-anchors >
5. < certificates src="@raw/my_ca"/ >
6. < /trust-anchors >
7. < /base-config >
8. < /network-security-config >

如果应用程序只是验证所提供的证书是否有效,这种技术应该允许您建立一个成功的MITM条件。

技术2 –用自定义CA证书覆盖打包的CA证书

如果您成功将证书安装到用户添加的CA存储中,应用程序的目标是Android 6.0,并且当您尝试浏览其他受SSL保护的资源时证书显示为有效,但是应用程序仍然死于SSL错误,该怎么办?开发人员可能已采取其他步骤来限制应用程序信任的CAs集合。回想一下技术1,我们定义了一个自定义信任锚,并提供了CA证书的路径——这是预期的功能,开发人员可以使用这些功能来尝试保护其应用程序免受SSL拦截。

如果自定义证书链随应用程序一起分发,则提取APK并用我们的自定义CA覆盖提供的CA应该足以使我们的拦截证书受到信任。请注意,在某些情况下,可能会进行信任链的其他验证,因此此方法可能会产生不同的结果。

使用APK Studio等工具打开APK,可以明显看出与已部署的应用程序捆绑在一起的证书的存在。在上图中,证书位于“assets”目录下。用我们的自定义CA覆盖恰当地命名为“ UniversalRootCA”的证书应该可以欺骗应用程序接受我们的证书。

技术3 – Frida Hook

如果安装自己的CA不足以成功代理SSL流量,则该应用程序可能正在执行某种SSL固定或其他SSL验证。通常,要绕过这种类型的验证,我们需要挂钩应用程序的代码并干扰验证过程本身。这种类型的干扰仅限于植根/破解的手机,但是在Frida Gadget的帮助下,现在可以检测Android应用程序并获得对Frida整套功能的访问权限,而无需植根设备。

如果您之前曾进行过移动应用程序渗透测试,则可能对Frida框架很熟悉。全面介绍Frida的功能不在本博客的讨论范围之内,但从较高的角度来看,它是一个允许您在运行时篡改应用程序代码的框架。通常,Frida将作为独立程序在操作系统上运行——但这需要对设备进行生根。为避免这种情况,我们可以将Frida Gadget注入目标APK。Frida Gadget包含Frida的大部分功能,但封装在一个动态库中,该库由目标应用程序在运行时加载,从而使您能够检测和修改目标应用程序的代码。

要加载Frida Gadget,我们需要提取APK,插入动态库,编辑一些smali代码,以便我们的动态库是在应用程序启动时首先调用的东西,然后重新打包APK并安装它。John Kozyrakis记录了整个过程,因此至少值得手动进行一次,以了解所有组件如何协同工作。为了节省时间,还有另外一种工具可以使用——Objection。Objection会自动完成整个过程,并且只需要在命令行上提供目标APK。

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应该将其推送到我们连接的设备。在我们的目标设备上安装了Objection更改的APK之后,运行该应用程序应该会导致应用程序启动屏幕上的暂停。此时,我们可以连接到应该在设备上监听的Frida服务器。如果您更喜欢使用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锁定支路功能:

com.test.app on (motorola: 7.0) [usb] # android sslpinning disable
Job: 2f633f86-f252-4a57-958e-6b46ac8d69d1 - Starting
[6b46ac8d69d1] [android-ssl-pinning-bypass] Custom, Empty TrustManager ready
Job: 2f633f86-f252-4a57-958e-6b46ac8d69d1 – Started

技术4 –逆转自定义证书验证码

最后,开发人员可能会选择提供自己的SSL库,而不是依赖系统库来处理SSL证书验证。如果是这种情况,我们可能会希望提取APK并将smali转换回Java,以便我们可以查找负责处理证书验证的代码。

使用“ dex2jar”,语法如下:

C:\> d2j-dex2jar.bat “ C:\ test_app.apk”
dex2jar C:\ test_app.apk->。\ test_app-dex2jar.jar

生成的.jar文件应该可以在您喜欢的Java反向工具(例如JD-GUI)中打开。

一旦确定了负责证书验证的代码,您就可以选择完全修补它或使用Frida挂钩所需的功能。为避免重新构建整个应用程序,挂接负责证书验证的功能通常更为有效。使用技术3中的步骤,您将可以对应用程序进行检测——从那里,您应该能够使用Frida命令行工具或Objection界面来挂钩函数。

结论

上面提到的技术应允许您拦截Android SSL流量并绕过开发人员采用的一些较常见的防御措施。此外,此博客还简要介绍了Objection和Frida ——绕过SSL固定和其他防御措施的能力只是从这些工具提供的惊人功能的表面上了解了一点。对Android移动应用程序安全性测试期间,我希望该博客能够对使用的各种技术进行清晰的介绍,并说明采用多种方法绕过给定安全性控制的重要性。

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!
请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!