CVE-2022-22252: Huawei HWLog Vmalloc Use-After-Free

IoT 2年前 (2022) admin
689 0 0

In this advisory we are disclosing a vulnerability in the Huawei log device that allows any unprivileged process to disclose sensitive information from the kernel.

Huawei kernels are shipped with custom log devices (/dev/hwlog_dubai/dev/hwlog_exception and /dev/hwlog_jank) that facilitate better system diagnostics through a series of ioctl calls. One of these diagnostics module is referred to as zrhung, and it provides information and configuration options to monitor hung processes.

The implementation of the config set ioctl contains a race condition vulnerability that allows an attacker to free the underlying buffer that holds the configuration data. Consecutive config get ioctl calls use the dangling pointers to read and disclose, potentially sensitive, dynamically allocated kernel data. An attacker can use this vulnerability to disclose cryptographic materials, such as kernel stack cookies, or potentially user identifying personal data. Due to an access control configuration error, this interface is exposed to untrusted and isolated application contexts, as a result any unprivileged process can exploit this vulnerability.

Vulnerability Details

The zrhung config can be set through the LOGGER_SET_HCFG ioctl call and can be read through the LOGGER_GET_HCFG ioctl. Furthermore, the LOGGER_SET_FEATURE ioctl can be used to set the value of the global ctx.cfg_feature variable.

The vulnerability is in the hcfgk_set_cfg() function, that implements the config set feature. The call chain to this function and its annotated code is presented below.

  • logger_ioctl()
  • zrhung_ioctl()
  • hcfgk_set_cfg()
int hcfgk_set_cfg(struct file *file, const void __user *arg)
{
  [...]
  ret = copy_from_user(&len, arg, sizeof(len));
  if (ret) {
    pr_err("copy hung config table from user failed\n");
    return ret;
  }
  if (len > HCFG_VAL_SIZE_MAX)
    return -EINVAL;

  // 1.
  spin_lock(&lock);

  // 2.
  if (ctx.cfg_feature != HCFG_FEATURE_VERSION) {
    spin_unlock(&lock);
    pr_err("cfg_feature is invalid\n");
    return -EINVAL;
  }
  table_len = sizeof(struct hcfg_table_version);
  entry_num = ZRHUNG_CFG_ENTRY_NUM;

  // 3.
  spin_unlock(&lock);

  mem_size = PAGE_ALIGN(table_len + len);

  user_table = vmalloc(mem_size);
  if (!user_table)
    return -ENOMEM;

  memset(user_table, 0, mem_size);

  ret = copy_from_user(user_table, arg, table_len + len);
  if (ret) {
    pr_err("copy hung config table from user failed\n");
    vfree(user_table);
    return ret;
  }

  // 4.
  spin_lock(&lock);

  // 5.
  tmp = ctx.user_table;
  ctx.user_table = user_table;
  user_table = tmp;
  ctx.mem_size = mem_size;
  ctx.entry_num = entry_num;

  // 6.
  if (ctx.cfg_feature != HCFG_FEATURE_VERSION) {
    spin_unlock(&lock);
    pr_err("cfg_feature is invalid\n");
    vfree(user_table);
    return -EINVAL;
  }

  // 7.
  t = ctx.user_table;
  ctx.table.entry = t->entry;
  ctx.table.data = t->data;

  /* make sure last byte in data is 0 terminated */
  ctx.table.len = len;
  ctx.table.data[len - 1] = '\0';

  spin_unlock(&lock);

  if (user_table != NULL)
    vfree(user_table);

  return ret;
}

The set config function first acquires the global spinlock, called lock (1), then checks if the global ctx.cfg_feature variable is set to the expected value (2). If not the function aborts. After this check, the global lock (3) is released, and a new config buffer is allocated with the user provided size. Then the function proceeds to acquire the global lock again (4) and swaps the old ctx.user_table pointer with the address of the freshly allocated buffer (5). Note, that ctx.user_table is not used directly to access the config data, instead the ctx.table.entry and ctx.table.data pointers are maintained for that purpose.

At (6) the feature variable is checked again, and if it is not set to the correct value, the old config buffer is released and the function exits. If the feature value is correct, the function updates the ctx.table.entry and ctx.table.data pointers to point into the new config buffer (7). These are the pointers that are used to access and read the configuration by the get config function. The get config function simply returns data to the user space from the ctx.table.data location.

The root cause of the vulnerability is the additional feature variable check at (6). If this check fails, the old config buffer is released and the function terminates. Thus the ctx.table.data pointer is not updated (7) with the address of the new config buffer. The global config is not invalidated in any ways. Future calls to read the config would use the dangling pointers, that now point into the freed vmalloc buffer, to read back the config data. While the feature variable is checked before (2), the global lock is released between (3) and (4), thus a competing thread can change its value before it is checked again at (6).

The vulnerability can be exploited by a malicious process to disclose arbitrary information from the kernel’s vmalloc address space. Once the vulnerability is triggered the attacker can spray the victim data (e.g. kernel stacks, see Brandon Azad’s article for details), that would overtake the freed vmalloc buffer’s location. The ctx.table.data still points to the original location, where now the victim data resides, and the next get config ioctl would return its content.

If the attacker fails to win the race, the function simply returns at (2) without any side effect. The attacker can attempt the race as many times as needed, they can win it very reliably. The size of the config buffer is also controlled by the attacker in the 4808-312608 bytes range, allowing a large variety of potentially overlapping vmalloc allocations.

Access Control

The Linux DAC permission allows any process to open the hwlog devices for writing, which is sufficient for issuing ioctl commands. Each device has a different Selinux label (see the listing below), however their ioctl interface is identical. The domain type attribute, which includes a series of unprivileged contexts such as isolated and untrusted app, is allowed to open, write and issue ioctls on these devices.

ls -lZ /dev/hwlog_*
crw-rw--w- 1 root   system u:object_r:dubai_log_device:s0 /dev/hwlog_dubai
crw-rw--w- 1 root   log    u:object_r:exception_device:s0 /dev/hwlog_exception
crw-rw--w- 1 root   system u:object_r:jank_device:s0      /dev/hwlog_jank

Selinux extended permissions are used to create a whitelist of ioctls on the /dev/hwlog_exception and /dev/hwlog_jank devices, that only allow a small set of ioctls to be called by the domain type attribute. However, these extended permissions are not applied for the dubai_log_device context, consequently, through the /dev/hwlog_dubai device every hwlog ioctl command is exposed to all the unprivileged processes.

Affected Devices (Verified)

  • Kirin 990
    • Huawei Mate 30 Pro (LIO)
    • Huawei P40 Pro (ELS)
    • Huawei P40 (ANA)
  • Kirin 9000/9000E
    • Huawei Mate40 Pro (NOH) EMUI
    • Huawei MatePad Pro 12 (WGH) HarmonyOS

Fix

Huawei OTA images, released after April 2022, contain the fix for the vulnerability.

Timeline

  • 2022.01.21. Bug reported to Huawei PSIRT via Harmony Bug Bounty Program
  • 2022.03.23. Huawei confirms vulnerability, assigns CVE and Critical severity
  • 2022.04.01. Huawei releases security bulletin

原文始发于Gyorgy Miru:CVE-2022-22252: Huawei HWLog Vmalloc Use-After-Free

版权声明:admin 发表于 2022年8月13日 上午11:23。
转载请注明:CVE-2022-22252: Huawei HWLog Vmalloc Use-After-Free | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...