Android 配置网络安全策略

在 Android 7.0 以上的系统中,Google 引入了一种名为网络安全配置(Network Security Configuration)的功能。据官方文档所说,这个功能可以让开发者在一个安全的声明性 XML 配置文件中自定义应用的网络安全设置,而无需修改应用代码。也可以针对特定域和特定应用配置这些设置。
可以参考官方文档原文
https://developer.android.google.cn/training/articles/security-config.html

当然这篇文章并不是介绍 Network Security Configuration 的具体用法的,本篇文章主要讲如何绕过这种在 Android 7.0+ 的默认行为。

如果了解过相关的知识,对于这个新增的功能,最直观的感觉可能就是,在运行着 Android 7.0 的手机上无法使用 Fiddler 或类似工具抓到 https 连接的包了。只有一些 https 的握手请求,无法查看到实际的数据,根本原因就是应用不再信任用户导入的 Fiddler 证书了。

想要研究如何绕过这项功能,就必须先了解如何正常使用。据官方文档描述,需要在 res/xml 下创建一个 XML 文件来自定义网络配置:
下面给出几个样例,可参照注释

配置该应用的所有 HTTPS 链接

1
2
3
4
5
6
7
8
9
<?xml version="1.0"encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>  <!-- 信任锚点集合 -->
            <certificates src="system"/>  <!-- 信任系统自带的证书 -->
            <certificates src="user"/>  <!-- 信任用户导入的证书 -->
        </trust-anchors>
    </base-config>
</network-security-config>

配置该应用的自定义 CA

1
2
3
4
5
6
7
8
<?xml version="1.0"encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="@raw/my_custom_ca"/>  <!-- 放在 res/raw 下的自定义 CA 文件 -->
        </trust-anchors>
    </base-config>
</network-security-config>

根据域名配置 HTTPS 可信域

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>  <!-- 与上文的 base-config 不同 -->
        <domain includeSubdomains="true">example1.iacn.me</domain>  <!-- 过滤域名,可配置多个 -->
        <domain includeSubdomains="true">example2.iacn.me</domain>  <!-- 一般会将 CDN 配置在此 -->
        <trust-anchors>
            <certificates src="system"/>
        </trust-anchors>
    </domain-config>
</network-security-config>

开发阶段的配置

仅在 android:debuggable=”true” 时生效

1
2
3
4
5
6
7
8
<?xml version="1.0"encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="system"/>
        </trust-anchors>
    </debug-overrides>
</network-security-config>

除此之外,还需要在 AndroidManifest.xml 中引用自定义的网络安全配置

1
2
3
4
5
6
7
<?xml version="1.0"encoding="utf-8"?>
<manifest>
    <application
        android:networkSecurityConfig="@xml/network_security_config"
        ...>
    </application>
</manifest>

上面是 Network Security Configuration 的使用简介,那么如何绕过该功能呢?

重编译 APK 文件

如上文介绍,需要将目标 APK 文件反编译,然后修改 XML 配置文件,在 trust-anchors 中信任用户导入的证书,之后重新打包即可。

运行时 Hook

在某些情况下,第一种方法也许是不可行的。比如说,需要保留目标应用原始的签名文件。这是无法做到的,因为你不可能拿到开发者的原始证书去给重编译后的应用签名。这里就需要用到 Hook 技术,可以在不修改应用代码的前提下修改应用的行为。

查看 Android 7.1.2_r36 源码,android.security.net.config.ManifestConfigSource 类 用于加载和处理网络安全配置 XML 文件的相关信息
getConfigSource() 方法下,如果应用未配置网络信息,它将会加载默认配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public final class NetworkSecurityConfig {

    ...

    private ConfigSource getConfigSource() {
            ...

            ConfigSource source;
            if (mConfigResourceId != 0) {
                ...
                source = new XmlConfigSource(mContext, mConfigResourceId, debugBuild,
                    mTargetSdkVersion, mTargetSandboxVesrsion);
            } else {
                ...
                source = new DefaultConfigSource(usesCleartextTraffic, mTargetSdkVersion,
                    mTargetSandboxVesrsion);
            }
            mConfigSource = source;

            return mConfigSource;
    }
}

DefaultConfigSource 类是 ManifestConfigSource 类中一个内部类,即上文代码中返回的默认网络配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public final class NetworkSecurityConfig {

    ...

    private static final class DefaultConfigSource implements ConfigSource {

        ...

        public DefaultConfigSource(boolean usesCleartextTraffic, int targetSdkVersion,
                int targetSandboxVesrsion) {
            mDefaultConfig = NetworkSecurityConfig.getDefaultBuilder(targetSdkVersion,
                    targetSandboxVesrsion)
                    .setCleartextTrafficPermitted(usesCleartextTraffic)
                    .build();
        }
    }
}

在其构造方法中,调用了 android.security.net.config.NetworkSecurityConfig 类中的 getDefaultBuilder() 去构造一个默认配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final class NetworkSecurityConfig {

    ...

    public static final Builder getDefaultBuilder(int targetSdkVersion, int targetSandboxVesrsion) {
        Builder builder = new Builder()
                .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
                .addCertificatesEntryRef(
                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false));
        
        ...

        if (targetSdkVersion <= Build.VERSION_CODES.M) {
            builder.addCertificatesEntryRef(
                    new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
        }

        return builder;
    }
}

可以看到,在应用的 targetSdkVersion <= M 时(Android 6.0 及以下),有一个 addCertificatesEntryRef(UserCertificateSource),系统将默认信任用户导入的证书。

那么以 Xposed 为例,Hook getDefaultBuilder(),在调用前将第一个参数 targetSdkVersion 改为 Android N 以下就可以了。这里给出核心代码:

1
2
3
4
5
6
7
8
9
10
11
public void initZygote(StartupParam startupParam) throws Throwable {
    Class<?> targetClass = findClass("android.security.net.config.NetworkSecurityConfig", null);
    if (targetClass != null) {
        XposedBridge.hookAllMethods(targetClass, "getDefaultBuilder", new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                param.args[0] = Build.VERSION_CODES.M;
            }
        });
    }
}

添加系统 CA 证书

在上文中可以发现,应用是会默认信任系统证书的。那么我们也可以将自己的 CA 文件添加至系统。
从 Charles 里导出二进制格式证书,但要使 Android 能够识别,还需要做一些转换,我们可以很方便的使用 openssl 工具来做到。

这里以导出的 cert.cer 证书文件为例。

1
$ openssl x509 -inform DER -subject_hash_old -in cert.cer

执行该命令,并复制输出结果的第一行哈希字符串,后面会用。如下图所示。
snipaste_20190814_191102.png

1
$ openssl x509 -inform DER -text -in cert.cer -out 7ef3ba8a.0

以刚才复制的哈希字符串为文件名,.0 结尾,如 7ef3ba8a.0,当做输出的文件名。
执行后就会输出 7ef3ba8a.0 这个文件。

复制输出的文件到 Android 上 /system/etc/security/cacerts/ 这个目录下,644 权限。
然后重启手机,你的证书就被添加到系统里了。

转载自https://iacn.me/2018/03/14/network-security-configuration-newer-than-android-n/

发布了6 篇原创文章 · 获赞 15 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/pkorochi/article/details/102567450