Windows Kernel APC Data-Free Local Privilege Escalation Vulnerability

Credit: Derek Soeder
Risk: Medium
Local: No
Remote: Yes
CWE: CWE-Other

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

Windows Kernel APC Data-Free Local Privilege Escalation Vulnerability Release Date: December 13, 2005 Date Reported: May 23, 2005 External Refferences: eEye ID# EEYEB-20050523 OSVDB ID# 18823 CVE # CAN-2005-2827 Microsoft # MS05-055 Severity: Medium (Local Privilege Escalation to Kernel) Systems Affected: Windows NT 4.0 Windows 2000 Overview: eEye Digital Security has discovered a local privilege escalation vulnerability in the Windows kernel that could allow any code executing on a Windows NT 4.0 or Windows 2000 system to elevate itself to the highest possible local privilege level (kernel). For example, a malicious user, network worm, or e-mail virus could take advantage of this vulnerability in order to completely compromise the vulnerable system on which the exploit code is executing, regardless of that code's original privilege level. The vulnerability exists in the thread termination routine contained within NTOSKRNL.EXE. Through a specific series of steps, a local attacker can cause the code responsible for discarding queued Asynchronous Procedure Call (APC) entries to erroneously attempt to free a region of kernel data, producing a "data free" vulnerability that may be exploited in order to alter arbitrary kernel memory, or even divert the flow of execution directly. Technical Details: The basis of this vulnerability is in PspExitThread's APC freeing loop and in the behavior of KiMoveApcState, invoked from KiAttachProcess and KeUnstackDetachProcess. We'll give a description of the problem below, followed by a "call flow" illustration to outline the specific sequence of events. When a thread is exiting, PspExitThread will detach the thread's APC queues from ETHREAD.ApcState.ApcListHead[0] and ApcListHead[1], so that each queue is now a circular, doubly-linked list in which the first and last nodes do not point back to the list head (LIST_ENTRY structure). However, since the list heads' pointers are not modified, the purpose is presumably just to allow the APC freeing loop within PspExitThread to walk each list and free its nodes, without navigating back to the list head and erroneously attempting to free memory within the ETHREAD structure. Of course, the vulnerability is that this can be made to happen, and the result is a "data free" condition that eventually causes ExFreePoolWithTag to operate on user memory. APCs queued by an external process count against that process's pool quota, and therefore the quota block of the pool block containing the APC structure has a reference to the queuing process. If the exiting thread contains an APC queued by a now-terminated external process in its lists, and if that APC node represents the last reference to the process's Process object, then freeing that node will cause the Process object to be destroyed from within ExFreePoolWithTag. Part of this sequence involves executing PspProcessDelete, which switches to the ending process's address space using KeStackAttachProcess, calls PspExitProcess, and then reverses the switch with KeUnstackDetachProcess. Both the "attach" and "detach" functions call KiMoveApcState, which is intended to temporarily strip the thread of its APCs so that none are dispatched in an address space for which they were not intended, then re-link the list of APCs after the thread's native address space is reinstated. During attach, the ETHREAD.ApcState structure is duplicated, and the pointers of the lists' first and last nodes are adjusted to refer to the copy. Upon detach, the first and last nodes' pointers are adjusted to re-link the lists to the original ETHREAD.ApcState -- even though they were supposed to remain disconnected, since the APC free loop is still in progress. The end result is that the free loop will continue and attempt to free a portion of the ETHREAD structure as though it were a pool block header, culminating in the kernel operating on attacker-supplied pointers from user-land memory, because the accessed portion of ETHREAD contains predictable and mostly zeroed values. The following depicts the sequence of function calls and parameters involved in producing the vulnerable condition: . PspExitThread . . KeFlushQueueApc . . (detaches APC queues from ETHREAD.ApcState.ApcListHead) . . (APC free loop begins) . . ExFreePool(1st_APC -- queued by exited_process) . . . ExFreePoolWithTag(1st_APC) . . . . ObfDereferenceObject(exited_process) . . . . . ObpRemoveObjectRoutine . . . . . . PspProcessDelete . . . . . . . KeStackAttachProcess(exited_process) . . . . . . . . KiAttachProcess . . . . . . . . . KiMoveApcState(ETHREAD.ApcState --> duplicate) . . . . . . . . . KiSwapProcess . . . . . . . PspExitProcess(0) . . . . . . . KeUnstackDetachProcess . . . . . . . . KiMoveApcState(duplicate --> ETHREAD.ApcState) . . . . . . . . KiSwapProcess . . ExFreePool(2nd_APC) . . ExFreePool(ETHREAD + 30h) . . (APC free loop ends) The ETHREAD data upon which ExFreePool is called is mostly predictable, KernelStack at offset +28h being the single true variable; however, methods for leaking a thread's kernel ESP permit complete control over the path execution will take through ExFreePoolWithTag. With enough crafting, an arbitrary function pointer can be supplied as an object type method, allowing execution to be hijacked directly. Beginning with Windows XP, KeFlushQueueApc contains a code fix that resolves this vulnerability. Protection: Retina Network Security Scanner has been updated to identify this vulnerability. Vendor Status: Microsoft has released a patch for this vulnerability. The patch is available at: Credit: Derek Soeder Greetings: Dedicated to R. W. S., Sr. 1928 - 2005 From my father to his: "He was a good man; liked by all, loved by many. He was always upbeat, outgoing and loved to kid around. He was always willing to help others in their time of need and gave a lot of himself. He was very creative, handy with tools, and could fix about anything. He was the one everyone turned to for advice and direction. He was my father, and I miss him dearly." Copyright (c) 1998-2005 eEye Digital Security Permission is hereby granted for the redistribution of this alert electronically. It is not to be edited in any way without express consent of eEye. If you wish to reprint the whole or any part of this alert in any other medium excluding electronic medium, please email alert (at) eEye (dot) com [email concealed] for permission. Disclaimer The information within this paper may change without notice. Use of this information constitutes acceptance for use in an AS IS condition. There are no warranties, implied or express, with regard to this information. In no event shall the author be liable for any direct or indirect damages whatsoever arising out of or in connection with the use or spread of this information. Any use of this information is at the user's own risk.

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 2020,


Back to Top