背景介绍

近日,京东探索研究院信息安全实验室的研究团队发现一项高危Android 11系统漏洞链,利用该漏洞链,黑客可能在用户毫无感知的情况下,获取用户手机中所有APP的隐私数据和权限,如获取任一社交软件的聊天记录,任意劫持邮箱、公司内部沟通软件、支付软件等等。

听起来影响很大,但是往往最具威力的漏洞都有着很朴素的技术原理,魔形女系列漏洞正是如此。本文将在法律允许的范围内对魔形女系列漏洞进行技术解析。

故事的开始——CVE-2021-25485 of Samsung

这是一个三星手机的漏洞,下面是三星官方的披露信息。

Path traversal vulnerability in FactoryAirCommnadManger prior to SMR Oct-2021 Release 1 allows attackers to write file as system UID via BT remote socket.

根据这个信息,在Galaxy S21的相关组件中我们可以找到下面的代码,极有可能和该漏洞相关(其中省略了部分代码)

public void startSocket(Context context2) {

byte[] buffer = new byte[8192];

byte[] newBuffer = new byte[8192];

int maxLength = 0;

ACUtil.log_i(CLASS_NAME, "startSocket");

this.readSocket = new BufferedReader(new InputStreamReader(this.is, Charset.forName("UTF-8")));

ACUtil.log_i(CLASS_NAME, "readSocket", "readSocket: " + this.readSocket);

this.isRunning = true;

while (true) {

try {

int read = this.is.read(buffer); // Read data from bluetooth socket

//...

ACUtil.log_i(CLASS_NAME, "Socket", "Receive " + read + " bytes: " + Support.Functions.byteArrayToHexString(buffer, read));

if (this.isFileMode) {

//...

} else if (this.isMetaMode) {

//...

} else {

System.arraycopy(buffer, 0, newBuffer, maxLength, read);

maxLength += read;

if (newBuffer[maxLength - 1] == 126) {

//...

} else if (maxLength > 1 && newBuffer[maxLength - 2] == 13 && newBuffer[maxLength - 1] == 10) {

ACUtil.log_i(CLASS_NAME, "Socket", "string");

byte[] fullBuffer2 = new byte[maxLength];

System.arraycopy(newBuffer, 0, fullBuffer2, 0, maxLength);

String str3 = new String(fullBuffer2, 0, maxLength, "UTF-8");

if (str3.toUpperCase().contains("AT+FACMFILE")) {

this.isFileMode = true;

String subStr = str3.substring(str3.indexOf("=") + 1, str3.length() - 2);

this.fileName = subStr.split(",")[0];

this.fileLength = Integer.parseInt(subStr.split(",")[1]);

ACUtil.log_i(CLASS_NAME, "Socket", "name: " + this.fileName + " length: " + this.fileLength);

File file = new File(savePath);

if (!file.exists()) {

file.mkdirs(); // Create directory

}

File file2 = new File(savePath + this.fileName);

ACUtil.log_d(CLASS_NAME, "Socket", "Save: " + file2.getPath());

if (this.fileLength != 0) {

this.fos = new FileOutputStream(file2); // Write file

this.mHandler.sendEmptyMessage(0);

}

maxLength = 0;

Arrays.fill(newBuffer, (byte) 0);

} else {

//...

}

}

}

} catch (Exception e) {

e.printStackTrace();

BluetoothService.mHandler.sendEmptyMessageDelayed(1000, 3000);

}

}

//...

}

可以看到这里实际上是一个蓝牙的Socket接口,接收了相关的参数并进行的文件写入操作,这里面存在两个问题:

该Socket接口并没有任何的权限校验

在写入文件的过程中存在路径穿越问题,会导致任意文件写入

同时更严重的问题是,这个应用使用的是system uid运行,同时SELinux标签是system_app,这是一个特权应用。

Double Kill——CVE-2021-25450 of Samsung

在三星的相同组件中还存在第二个漏洞,下面是三星官方的披露信息。

Path traversal vulnerability in FactoryAirCommnadManger prior to SMR Sep-2021 Release 1 allows attackers to write file as system uid via remote socket.

同样地我们找到了下面的代码,极有可能和该漏洞相关(其中省略了部分代码)

public Void doInBackground(Void... voids) {

File checkFolder;

int n;

try {

this.dis = new DataInputStream(this.socket.getInputStream());

} catch (Exception e) {

e.printStackTrace();

}

while (true) {

ACUtil.log_d(CLASS_NAME, "doInBackground", "Wait for data...");

try {

//...

while (true) {

if (i >= count || isCancelled()) {

break;

}

String fileName = this.dis.readUTF(); // Read file name

String filePath = this.dis.readUTF(); // Read file path

long transferSize = this.dis.readLong(); // Read file size

ACUtil.log_d(CLASS_NAME, "doInBackground", "transferSize: " + transferSize);

String filePath2 = savePath + this.uniqueNumber + "/" + folderCount + "/" + filePath;

File file = new File(filePath2);

ACUtil.log_d(CLASS_NAME, "doInBackground", "filePath: " + filePath2);

ACUtil.log_d(CLASS_NAME, "doInBackground", "getCanonicalPath: " + file.getCanonicalPath());

if (!(file.getCanonicalPath() + '/').equals(filePath2)) {

cancel(true);

break;

}

if (!file.exists()) {

file.mkdirs(); // Create directory

}

File file2 = new File(filePath2 + fileName);

if (!file2.getCanonicalFile().toString().equals(filePath2 + fileName)) {

cancel(true);

break;

}

ACUtil.log_d(CLASS_NAME, "doInBackground", "Save: " + file2.getCanonicalFile());

this.fos = new FileOutputStream(file2);

byte[] b = new byte[1024];

long fileSize = transferSize;

long totalSize = 0;

while (fileSize > 0 && (n = this.dis.read(b, 0, (int) Math.min((long) b.length, fileSize))) != -1) {

this.fos.write(b, 0, n); // Write file

fileSize -= (long) n;

totalSize += (long) n;

}

ACUtil.log_d(CLASS_NAME, "doInBackground", "totalSize: " + totalSize);

ACUtil.log_d(CLASS_NAME, "doInBackground", "Save: " + (i + 1) + "/" + count);

this.context.sendBroadcast(new Intent(WifiCommandService.PROGRESS_CHANGE).putExtra("value", i + 1));

this.fos.close();

i++;

}

this.context.sendBroadcast(new Intent(WifiCommandService.PROGRESS_FINISH).putExtra("path", checkFolder.getPath()));

} catch (RuntimeException e2) {

throw e2;

} catch (Exception e3) {

e3.printStackTrace();

}

}

ACUtil.log_d(CLASS_NAME, "doInBackground", "Data Input Stream is null");

try {

if (this.fos != null) {

this.fos.close();

ACUtil.log_d(CLASS_NAME, "doInBackground", "fos close");

}

if (this.dis != null) {

this.dis.close();

ACUtil.log_d(CLASS_NAME, "doInBackground", "dis close");

}

} catch (Exception e4) {

e4.printStackTrace();

}

this.context.sendBroadcast(new Intent(WifiCommandService.PROGRESS_FINISH));

if (isCancelled()) {

return null;

}

this.context.sendBroadcast(new Intent(WifiCommandService.START_FILE_RECEIVE));

return null;

}

和上面的漏洞非常类似,这里是一个网络Socket接口,和漏洞披露的信息也是对的上的,也同样是接收了相关的参数并进行的文件写入操作,这里面也存在两个问题:

该Socket接口并没有任何的权限校验

在写入文件的过程中存在路径穿越问题,会导致任意文件写入

当然,该代码依旧是特权应用的一部分,不再赘述。

Triple kill——CVE-2021-23243 of OPPO

其实魔形女系列漏洞还包括一个OPPO的漏洞,但是由于我并没有OPPO的设备,并且对OPPO也不是很感兴趣,所以不深入分析,我估计原理和上两个漏洞应该也是非常类似的,都是system_app的路径穿越导致的任意文件写入。

如何将文件写入转化为代码执行

说了这么多我们只是做到了system_app标签权限的任意文件写入,即使是通过远程进行攻击,我们也无法窃取设备上的任何数据,那么如何将这类漏洞转化为RCE(远程代码执行)呢?先看一个前人的案例

该案例是Google Project Zero早在2015年的时候就发现的,当时是为了写另一个CVE-2015-7889漏洞的利用,也是三星的漏洞。这个方法是通过写/data/dalvik-cache下面的dex文件实现的,这样可以把文件写转化为代码执行

flame:/data/dalvik-cache # ls -lZ

total 51

drwx--x--x 2 root root u:object_r:dalvikcache_data_file:s0 3488 2021-11-04 10:22 arm

drwx--x--x 2 root root u:object_r:dalvikcache_data_file:s0 53248 2021-11-04 10:22 arm64

当然了现在已经不能用了,因为现在已经限制了不允许app标签的进程写dalvikcache_data_file

其实在2021年初的时候我手里也有其他Android厂商的几个类似的路径穿越漏洞(具体暂时无法进行披露),正在思考如何利用,因为Android里面文件系统的限制,只有/data分区是可写分区,其他的都需要有root权限并且进行remount才能操作,所以我当时的关注点也是/data分区下的文件。

我们先确定一般的思路,就像是上面的利用一样,我们要企图去写入覆盖一个dex或者so文件,甚至是odex和vdex文件也行,因为这些文件是Android平台上的可执行文件,只要覆盖了如果其存在自动执行的场景,就能转化为代码执行。但是说起来容易做起来难,Android有严格的SELinux限制,我当时检查了所有可能包含这类文件的路径,都没有发现可以允许system_app标签写入的目录。直到…

Google的迷之操作——CVE-2021-0691

这个漏洞表面上看起来还是很简单的:

# Settings need to access app name and icon from asec

allow system_app asec_apk_file:file r_file_perms;

-# Allow system_app (adb data loader) to write data to /data/incremental

-allow system_app apk_data_file:file write;

-

-# Allow system app (adb data loader) to read logs

-allow system_app incremental_control_file:file r_file_perms;

-

# Allow system apps (like Settings) to interact with statsd

binder_call(system_app, statsd)

apk_data_file正是各种应用的apk文件的标签,Google错误地允许system_app对apk_data_file执行文件写入操作。这个问题只在Android 11中存在,我当时攻击的目标,是基于Android 10的ROM,而这段代码是Android 11升级的时候才加入的。

-rw-r--r-- 1 system system u:object_r:apk_data_file:s0 114976454 2021-11-12 16:01 base.apk

drwxr-xr-x 3 system system u:object_r:apk_data_file:s0 3488 2021-11-12 16:01 lib

后面的事情就顺理成章了,我们只需要覆盖任意一个会自启动的apk文件即可,后面就可以为所欲为了,就像开头说的一样。

后记

以上漏洞均在Google 2021 #9公告,三星的2021 #9和#10公告中得到了修复,并且该套利用只影响Android 11版本,虽然对于Google来说这个问题非常严重,因为是最新的版本。对于最终用户而言,Android 11的手机占比约为24.3%,大概1/4的Android客户受影响(经评论区提示,已经根据Android 开发者信息分发中心的新数据进行更新)。