Maddie Stone

The Basics

Disclosure or Patch Date: March 01, 2021

Product: Samsung Android


Affected Versions: Samsung Exynos, kernel 4.14.113, pre SMR-Mar-2021

First Patched Version: SMR-Mar-2021

Issue/Bug Report: N/A

Patch CL: N/A

Bug-Introducing CL: N/A

Reporter(s): Anonymous

The Code


// To triger the WARN_ON
   hwcnt_fd = ioctl(dev_mali_fd, 0x40148008, &v4);
   ioctl(hwcnt_fd, 0x4004BEFE, 0);

// To start the bugreport which writes kmsg contents to /data/log/sec_log.log
   system("setprop dumpstate.options bugreportfull; setprop ctl.start bugreport");

Exploit sample: N/A

Access to the exploit sample? Yes

The Vulnerability

Bug class: Logic/design flaw

Vulnerability details:

Usually apps, including system apps, don't have access to /dev/kmsg. This vulnerability gave a system app access and thus leaked the task_struct and sys_call_table addresses.

WARN_ON macros are used in the Linux kernel for debugging kernel failures. When they're hit, a full back trace is logged to kmsg. This full backtrace included raw stack contents, including the task_struct and sys_call_table addresses. This vulnerabiity is first triggered by hitting a WARN_ON statement in the ARM Mali GPU driver. When an invalid ioctl command is passed to the HWCNT driver, the WARN_ON is called:

 * 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) {
                rcode = put_user(HWCNT_READER_API, (u32 __user *)arg);
                rcode = kbasep_vinstr_hwcnt_reader_ioctl_get_hwver(
                        cli, (u32 __user *)arg);
                rcode = put_user(
                        (u32 __user *)arg);

                WARN_ON(true);    /** Hit here when ioctl command is invalid **/
                rcode = -EINVAL;

        return rcode;

Samsung added a custom bugreport service to dumpstate.rc. They also added a custom kernel logging where kmsg contents are also copied to /proc/sec_log. The customized dumpstate will copy all of the contents (which includes the WARN_ON backtrace) to /data/log/sec_log.log. The access controls on the sec_log file are much more permissive than /dev/kmsg.

Patch analysis:

  • dumpstate no longer writes to sec_log.log
  • Removed the bugreport service from dumpstate.rc

Upstream changes made in early 2020 would prevent this vulnerability in the future:

  • In February 2020, ARM changed the WARN_ON to a pr_warn in version r21p0 of the Mali driver. Samsung updated to this version
  • In April 2020, Linux removed printing the raw stack contents in a backtrace

Thoughts on how this vuln might have been found (fuzzing, code auditing, variant analysis, etc.):

This vuln was likely found by investigating what the sec_log is and then looking for a reachable WARN_ON that included the task_struct pointer and an address that could be used to break KASLR (in this case it's the sys_call_table address).

(Historical/present/future) context of bug:

This bug was used as a part of a chain described in this blog post.

The Exploit

(The terms exploit primitive, exploit strategy, exploit technique, and exploit flow are defined here.)

Exploit strategy (or strategies):

The leaked sys_call_table address is used to break KASLR.

The leaked task_struct is used to get the addr_limit pointer, which is used to gain arbitrary kernel read and write.

Exploit flow: N/A

Known cases of the same exploit flow:

It's common/standard to use the task_struct and addr_limit pointer to gain arbitrary kernel r/w. I hadn't previously seen the technique that the exploit used to work around UAO/PAN before. This technique is described in detail in the blog post.

Part of an exploit chain?

Yes, used in a chain with a kernel info leak and a kernel priv esc. There would have also needed to be a step prior because this exploit would need to be running within some application on the device.

The Next Steps

Variant analysis

Areas/approach for variant analysis (and why):

Samsung has quite a few modifications to AOSP's dumpstate and logging. Further auditing of this and who has access is probably worthwhile.

The exploit called setprop ctl.start. In AOSP, no contexts have general access to ctl.start, but on the targeted Samsung phones it's much more permissive. system_app even had access. Audit what other services this gives apps access to.

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;

Found variants: N/A

Structural improvements

What are structural improvements such as ways to kill the bug class, prevent the introduction of this vulnerability, mitigate the exploit flow, make this type of vulnerability harder to exploit, etc.?

Ideas to kill the bug class:

  • Auditing why kmsg contents, and other sensitive logs need to be copied to additional, more permissive locations.
  • Syncing to upstream Linux kernel and upstream drivers more often. The changes made to the ARM Mali GPU driver (Feb 2020) and the Linux kernel (April 2020) would have prevented this bug. As of the November 2020 update, the Samsung A50 phone didn't have either of these versions.

Ideas to mitigate the exploit flow: N/A

Other potential improvements: N/A

0-day detection methods

What are potential detection methods for similar 0-days? Meaning are there any ideas of how this exploit or similar exploits could be detected as a 0-day?

  • Maybe detecting if apps are trying to open and read log contents

Other References