Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=967

The TSP touchscreen controller driver exposes several sysfs entries through which the driver may be configured. One such entry, "cmd", allows the user to write commands to be executed by the driver.

Specifically, the "cmd" entry is writable, and is present under:

/sys/devices/virtual/sec/tsp/cmd

Writes to this sysfs entry are handled by the function "cmd_store", under drivers/input/touchscreen/sec_ts/sec_ts_fn.c

This function fails to validate the length of the supplied buffer, before copying data from it into two memory locations. First, the data is copied into a static structure:

    ...
    memset(ts->cmd, 0x00, sizeof(ts->cmd));
    memcpy(ts->cmd, buf, length);
    memset(ts->cmd_param, 0, sizeof(ts->cmd_param));
    memset(buffer, 0x00, sizeof(buffer));
    ...

The "buf" argument contains the user-supplied data, and the "length" argument is completely user-controlled. Since the length of ts->cmd is defined to be CMD_STR_LEN (256), this memcpy will overflow into adjacent fields in the "ts" structure, allowing the attack to replace these with attack-controlled data.

Second, the user-supplied data is copied into a local stack-allocated buffer, like so:

    ...
    char buffer[CMD_STR_LEN];
    ...
    pos = strchr(buf, (int)delim);
    if (pos)
        memcpy(buffer, buf, pos - buf);
    else
        memcpy(buffer, buf, length);
    ...


This means that the attacker can also overwrite the data on the stack, including the value of frame pointer and return address, simply by providing a buffer of length >CMD_STR_LEN. This allows the attacker to directly hijack the control flow when the function returns.

I've statically and dynamically verified this issue on an SM-G935F device. The open-source kernel package I analysed was "SM-G935F_MM_Opensource", the device's build is "XXS1APG3".

The sysfs entries mentioned above have UID "system" and GID "radio". The SELinux context for these entries is: "u:object_r:sysfs_sec:s0".

According to the default SELinux rules as present on the SM-G935F (version XXS1APG3), the following contexts may access these files:

   allow shell sysfs_sec : file { read open } ;
   allow system_app sysfs_sec : file { ioctl read write getattr lock append open } ;
   allow rild sysfs_sec : file { ioctl read write getattr lock append open } ;
   allow system_app sysfs_sec : dir { ioctl read write getattr add_name remove_name search open } ;
   allow diagexe sysfs_sec : file { ioctl read write getattr lock append open } ;
   allow at_distributor sysfs_sec : file { ioctl read write getattr setattr lock append open } ;


Proof of concept for the buffer overflow in the TSP driver.

Includes a short ROP chain which allows execution of any arbitrary function in the context of the linux kernel, with arbitrary arguments. This PoC also uses the KASLR bypass in "pm_qos" to adjust for the KASLR slide).

The high-level flow for executing a function in the kernel is the following:
  -Allocate a (user-space) buffer on the heap with a dummy "marker" value
  -Start a new thread (denote it "Thread B", denote the original thread "Thread A")
  -Thread A:
    -Perform a busy loop waiting for the dummy value to be updated
  -Thread B:
    -Create a ROP chain which does the following:
      -Prepares arguments for a function call
      -Calls the wanted function in the context of the kernel
      -Stores X0 in a sysfs entry in the kernel VAS (e.g., uevent_seqnum)
      -Change the dummy value shared from thread A to indicate completion
      -Enter idle loop
  -Thread A:
    -(Exit busy loop as the marker value has been modified)
    -Read the result of the execution by reading the sysfs entry


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/41130.zip