免责声明
本文仅作漏洞披露与防御研究,不含可直接复现的攻击脚本/PoC,不含真实域名、IP、令牌与信息。本文不提供可直接复现的攻击脚本、PoC、令牌、真实域名/IP,也不涉及绕过平板电脑管控的具体操作。
一、背景
前段时间在测试环境中,对学校租用并实际控制的一批教育平板电脑进行安全评估。设备均安装了博喻云管控 Android 客户端,用于统一管控。
测试过程中发现,该客户端在 HTTPS 通信层面存在一个比较基础但非常逆天的问题:TLS 证书与主机名校验被全局禁用,客户端会信任任意证书。
这个问题本身并不复杂,但因为它出现在管控类客户端中,一旦被中间人利用,可能导致通信数据被读取或篡改,进而带来设备身份凭证冒用、管控策略绕过等风险。因此整理成文,供移动安全方向的朋友参考。
二、样本信息
| 项目 |
值 |
| 产品名称 |
博喻云管控 Android 客户端 |
| 包名 |
com.boeyu.honor.launcher |
| versionName |
3.168.0 |
| versionCode |
455 |
| build 类型 |
release(BuildConfig.DEBUG=false) |
| 分析方法 |
APK 解包 + jadx 静态反编译(未做生产环境抓包) |
| 漏洞类型 |
异常处理不当 / CWE-295 / CWE-297 |
| 风险等级 |
高(CVSS 3.1:7.4) |
CVSS 向量:
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
三、问题定位
静态审计时,在客户端网络层发现一个自定义 TLS 工具类:
com/boeyu/launcher/net/SSLSocketClient.java
相关位置大致在 SSLSocketClient.java:15,26,43,60。该类中存在典型的"信任所有证书"实现——X509TrustManager 的 checkServerTrusted()、checkClientTrusted() 均为空实现,HostnameVerifier.verify() 恒返回 true,并且这套逻辑被无条件注入到全局网络客户端。
简化后的关键代码如下(伪代码,仅用于说明漏洞点):
public class SSLSocketClient {
// :15 OkHttp 使用的 SSLSocketFactory
public static SSLSocketFactory getSSLSocketFactory() {
SSLContext ctx = SSLContext.getInstance("SSL");
ctx.init(null, getTrustManager(), new SecureRandom()); // 注入下面的空 TrustManager
return ctx.getSocketFactory();
}
// :26 返回空实现的 TrustManager[]
private static TrustManager[] getTrustManager() {
return new TrustManager[]{ new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] c, String s) { } // 空实现
public void checkServerTrusted(X509Certificate[] c, String s) { } // 空实现:接受任意证书
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}};
}
// :43 单独暴露的 X509TrustManager
public static X509TrustManager getX509TrustManager() {
return new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] c, String s) { }
public void checkServerTrusted(X509Certificate[] c, String s) { } // 空实现
public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
};
}
// :60 恒真的 HostnameVerifier
public static HostnameVerifier getHostnameVerifier() {
return new HostnameVerifier() {
public boolean verify(String host, SSLSession session) { return true; } // 恒为 true
};
}
}
注入点(无条件调用):
// com/boeyu/launcher/application/MainApp.java:347-358 —— initOkGo()
SSLSocketFactory f = SSLSocketClient.getSSLSocketFactory();
if (f != null) {
builder.sslSocketFactory(f, SSLSocketClient.getX509TrustManager());
}
builder.hostnameVerifier(SSLSocketClient.getHostnameVerifier());
// com/boeyu/launcher/net/WebSocketManager.java:88
new OkHttpClient.Builder()
.sslSocketFactory(
SSLSocketClient.getSSLSocketFactory(),
SSLSocketClient.getX509TrustManager())
.hostnameVerifier(SSLSocketClient.getHostnameVerifier())
.build();
这意味着,应用发起的 HTTPS 连接不会验证证书是否由可信 CA 签发、是否过期、是否被吊销,也不会验证证书中的域名是否与目标主机匹配。自签名证书、伪造证书、域名不匹配证书,都可能被直接接受。
四、进一步排查
为了确认不是单一请求问题,继续检查了全局网络配置,发现:
- 工程中未使用
okhttp3.CertificatePinner;
- 未发现证书加载或白名单校验代码(无
.bks / .cer / .pem);
- AndroidManifest 中未配置
android:networkSecurityConfig;
android:usesCleartextTraffic="true",允许明文流量;
- TLS 校验缺失逻辑被注入到全局 OkHttp 与 WebSocket 客户端,而非某个单独接口。
对应 Manifest 片段(示意):
<!-- 未引用 android:networkSecurityConfig,且允许明文流量 -->
<application
android:usesCleartextTraffic="true"
... >
...
</application>
也就是说,问题不是"某一个请求忘了校验",而是全局网络层默认不校验证书和主机名。从代码注入点看,主要涉及:
MainApp.initOkGo()(行号约 347-358)
WebSocketManager 初始化(行号约 88)
五、风险分析
在正常网络环境下,这个问题可能不容易被感知。但攻击者如果处于同一网络路径(例如同一 Wi-Fi、网关、ARP 欺骗场景),或取得链路中间人位置,就可能利用该缺陷:
- 构造自签名或伪造证书的 HTTPS 服务;
- 冒充服务端域名;
- 客户端因不校验证书与主机名而接受连接;
- 攻击者读取或篡改请求与响应;
- 获取
Authorization 令牌、加密参数、设备标识等敏感数据;
- 进一步可能导致设备身份凭证被冒用,以及教育平板电脑管控模式被绕过的风险。
需要强调的是,本文不展开具体利用链和 PoC。对于管控类设备,公开可复现的攻击细节可能带来现实滥用风险。因此,验证记录、录屏和日志仅以受限附件形式提交给厂商和漏洞平台。
六、验证说明
本次验证在本人所在学校合法租用并实际控制的内部平板设备上进行,共 20 台,均安装管控 App。测试遵循最小化验证、不扩大影响、完整留痕原则,未对外收取任何费用,验证细节暂未公开。
具体 PoC 数据与复现记录另行以附件形式提交,不在公开帖中展开。
补充:应用自身通道为"完全信任";而 WebView 通道使用系统 TrustManager(该包 targetSdk 28,默认不信任用户安装 CA),属于平台行为,与本文所述"应用主动关闭校验"性质不同。
七、修复建议
1. 删除空校验实现
移除 SSLSocketClient 中的:
- 空
TrustManager
- 恒真
HostnameVerifier
不要为了"方便抓包"或"兼容自签名证书"而在 release 版本关闭 TLS 校验。
2. 恢复平台默认校验
改用系统默认 X509TrustManager,让系统按标准流程校验证书链和主机名:
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init((KeyStore) null); // 使用系统默认信任库
X509TrustManager xtm = (X509TrustManager) tmf.getTrustManagers()[0];
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(null, new TrustManager[]{ xtm }, new SecureRandom());
SSLSocketFactory factory = ctx.getSocketFactory();
// OkHttp 注入默认校验
builder.sslSocketFactory(factory, xtm);
// 不再设置自定义 hostnameVerifier,使用 OkHttp 默认主机名校验
3. 使用 CertificatePinner 做证书固定
如果业务需要绑定服务端证书,建议使用 okhttp3.CertificatePinner,固定证书或公钥的 SHA-256,并配置 backup pin。不要用"关闭校验"替代证书固定。
CertificatePinner pinner = new CertificatePinner.Builder()
.add("你的服务端域名", "sha256/AAAA...(证书或公钥 SHA-256,主 pin)")
.add("你的服务端域名", "sha256/BBBB...(备用 backup pin)")
.build();
builder.certificatePinner(pinner);
提示:pin 值请从你自有服务端证书/公钥计算,务必配置 backup pin,避免证书轮换导致客户端全部断连。
4. 增加网络安全配置
新增 res/xml/network_security_config.xml(示意),并在 AndroidManifest 中引用。release 构建禁止信任用户 CA;如调试需要,可仅在 debug 构建中通过 debug-overrides 放行。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- 默认:仅信任系统 CA,禁止明文 -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system"/>
</trust-anchors>
</base-config>
<!-- 调试构建才允许用户 CA(示意,release 不包含) -->
<debug-overrides>
<trust-anchors>
<certificates src="user"/>
</trust-anchors>
</debug-overrides>
</network-security-config>
<application
android:networkSecurityConfig="@xml/network_security_config"
... >
</application>
5. 强制 HTTPS
关闭 android:usesCleartextTraffic="true",全站强制 HTTPS,避免明文流量。
<!-- 修改前 -->
<application android:usesCleartextTraffic="true" ... />
<!-- 修改后:移除该属性(或显式设为 false),配合上面的 network_security_config -->
<application android:usesCleartextTraffic="false" ... />
6. 应用层防护
对 Authorization、令牌类数据增加应用层签名与时效校验,降低 TLS 被破坏后的重放、篡改影响。例如对请求体拼接时间戳后做应用层签名,并在服务端校验签名与时效。
八、参考
- CWE-295: Improper Certificate Validation
- CWE-297: Improper Validation of Certificate with Host Mismatch
- OWASP Mobile Top 10 — M3: Insecure Communication
- 博喻云管控平板漏洞披露报告(CNNVD 格式)
- PoC 验证记录、复现录屏等以附件形式另行提交
九、总结
这次分析的核心其实并不复杂:Android 客户端在全局网络层关闭了 TLS 证书与主机名校验。 该客户端其自定义 TLS 工具类 SSLSocketClient 中,X509TrustManager 的 checkServerTrusted() / checkClientTrusted() 为空实现,HostnameVerifier.verify() 恒返回 true,并被无条件注入全局 OkHttp 客户端与 WebSocket 客户端。应用所有 HTTPS 连接不校验证书有效性,可接受任意自签名、过期或不匹配的证书。工程未正确配置 CertificatePinner 。攻击者若取得链路中间人位置,可读取、篡改请求与响应,进而可能导致设备凭证报错,并绕过教育平板管控模式,夺取内部网络设备控制权,甚至散播不当内容。
对内部开发者来说,抓包调试可以理解,但 release 版本必须恢复标准校验啊。对安全研究者来说,涉及管控类设备的漏洞,公开时更要注意脱敏和负责任披露,避免 PoC 被滥用。
本文不提供可直接复现的攻击细节,也不鼓励任何未授权测试。希望厂商尽快修复,也欢迎移动安全方向的朋友交流防御思路,,,,