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

Статья Анализ патча для CVE-2020-17087

varwar

El Diff
Забанен
Регистрация
12.11.2020
Сообщения
1 383
Решения
5
Реакции
1 537
Пожалуйста, обратите внимание, что пользователь заблокирован
Введение

В этой статье я хотел бы описать процесс анализа патча для CVE-2020-17087. Несмотря на то, что уязвимость не самая свежая, читателям форума может быть интересно то, как Майкрософт исправляет свои ошибки с целью более быстрого анализа последующих исправлений. Может быть даже кто-то захочет создать модель уязвимости, написать ее на IDAPython и найти новые зеродеи. Я же в свою очередь постарался описать детали достаточно подробно, сопроводив листинги комментариями.

Сведения об уязвимости

Уязвимость CVE-2020-17087 находится в драйвере cng.sys, который экспортирует функции, отвечающие за криптографию. Подробнее.
Согласно отчету P0 ошибка содержится в функции CfgAdtpFormatPropertyBlock при обработке 16-битовых чисел и является целочисленным переполнением, которое приводит к переполнению пула ядра. Т.е. атакующий может контролировать размер буфера из пользовательского режима, обратившись к драйверу по IOCTL 0x390400.

Анализ патча

Для анализа патчей я использую BinDiff. На мой взгляд, это самый быстрый и удобный инструмент для своих нужд, несмотря на его "прожорливость" по отношению к оперативной памяти. В соседней ветке я описал некоторые проблемы, с которыми могут столкнуться новые пользователи. Настоятельно рекомендую ее прочитать.

Патч для уязвимости был добавлен в версии Windows 20H2 19042.630, поэтому для сравнения я взял файлы cng.sys версий 19042.572 и 19042.630 соответственно. На скриншоте ниже мы можем видеть, что изменена была одна функция CfgAdtpFormatPropertyBlock.
1.png


Также была добавлена функция RtlUSortMult(), которая осуществляет проверку 16-битовых чисел перед вызовом функции BCryptAlloc() и предотвращает переполнение, возвращая в этом случае ошибку с кодом STATUS_INTEGER_OVERFLOW.

2.png


На листинге декомпилятора ниже мы можем видеть исправленную версию функции CfgAdtpFormatPropertyBlock.
Функция RtlUShortMult возвращает указатель на результат произведения двух чисел, сохраненного в переменной p_mult_result.

10.png


Теперь вернемся к дизассемблерному листингу уязвимой версии драйвера.
В начале функция проверяет регистры rcx, bp, r8 на равенство нулю и возвращает STATUS_INVALID_PARAMETER, если утвереждение верно (распространенная проверка). Т.е. можем предположить, что аргументы функции хранятся именно в этих регистрах. Нас интересует регистр bp, который имеет размер 16 бит. Его мы и можем контролировать. После подсчета размера входящего буфера происходит выделение неисполняемого невыгружаемого пула ядра с помощью функции BCryptAlloc равного количеству байт, записанных в ecx из di.

5.png

BCryptAlloc является лишь оберткой для функции ExAllocatePoolWithTag. В передаваемых аргументах мы можем видеть тег Cngb или 0x62676E43, с которым происходит выделение чанков.

6.png


По указателю rax мы можем видеть выделенный чанк размером 0x10 байт после вызова BCryptAlloc(0x2aac). Судя по всему, Pool Manager не может выделить меньше 16 байт под чанк, т.к. в нашем случае должно было выделиться 8 байт из-за целочисленного переполнения.
(0x2aac * 0x6) - (0xffff + 0x1) = 0x8.

Код:
2: kd> ? @rax
Evaluate expression: -140676885522480 = ffff800e`1c35d7d0
2: kd> dt nt!_pool_header (@rax - 0x10)
   +0x000 PreviousSize     : 0y00000000 (0)
   +0x000 PoolIndex        : 0y00000000 (0)
   +0x002 BlockSize        : 0y00000010 (0x2)
   +0x002 PoolType         : 0y00000010 (0x2)
   +0x000 Ulong1           : 0x2020000
   +0x004 PoolTag          : 0x62676e43
   +0x008 ProcessBilled    : 0x2ecfaa1a`b58a06e6 _EPROCESS
   +0x008 AllocatorBackTraceIndex : 0x6e6
   +0x00a PoolTagHash      : 0xb58a

Код:
2: kd> u
cng!CfgAdtpFormatPropertyBlock+0x6b:
fffff801`59be24c7 663bdd          cmp     bx,bp
fffff801`59be24ca 7343            jae     cng!CfgAdtpFormatPropertyBlock+0xb3 (fffff801`59be250f)
fffff801`59be24cc 4c8bc5          mov     r8,rbp
fffff801`59be24cf 4c8d159a590300  lea     r10,[cng!`string'+0x18 (fffff801`59c17e70)]
fffff801`59be24d6 410fb606        movzx   eax,byte ptr [r14]
fffff801`59be24da 48c1e804        shr     rax,4
fffff801`59be24de 420fb60410      movzx   eax,byte ptr [rax+r10]
fffff801`59be24e3 668901          mov     word ptr [rcx],ax
2: kd> ? @bx
Evaluate expression: 0 = 00000000`00000000
2: kd> ? @bp
Evaluate expression: 10924 = 00000000`00002aac

После проверки размера буфера в r8 сохраняется длина, а в r10 передается указатель на массив.

Код:
2: kd> db @r10
fffff801`59c17e70  30 31 32 33 34 35 36 37-38 39 61 62 63 64 65 66  0123456789abcdef
fffff801`59c17e80  54 00 4c 00 53 00 5f 00-45 00 43 00 44 00 48 00  T.L.S._.E.C.D.H.
fffff801`59c17e90  45 00 5f 00 45 00 43 00-44 00 53 00 41 00 5f 00  E._.E.C.D.S.A._.
fffff801`59c17ea0  57 00 49 00 54 00 48 00-5f 00 41 00 45 00 53 00  W.I.T.H._.A.E.S.
fffff801`59c17eb0  5f 00 31 00 32 00 38 00-5f 00 43 00 42 00 43 00  _.1.2.8._.C.B.C.
fffff801`59c17ec0  5f 00 53 00 48 00 41 00-32 00 35 00 36 00 5f 00  _.S.H.A.2.5.6._.
fffff801`59c17ed0  50 00 32 00 35 00 36 00-00 00 00 00 00 00 00 00  P.2.5.6.........
fffff801`59c17ee0  54 00 4c 00 53 00 5f 00-45 00 43 00 44 00 48 00  T.L.S._.E.C.D.H.
На этом этапе мы переходим в цикл по смещению loc_1C00624D6.

7.png


В r14 находится указатель на большой NonPagedPool размером 0x3ab0 байт.

Код:
2: kd> db @r14
ffff800e`1c708000  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffff800e`1c708010  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffff800e`1c708020  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffff800e`1c708030  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffff800e`1c708040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffff800e`1c708050  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffff800e`1c708060  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffff800e`1c708070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
2: kd> !pool @r14
Pool page ffff800e1c708000 region is Nonpaged pool
ffff800e1c707000 doesn't look like a valid small pool allocation, checking to see
if the entire page is actually part of a large page allocation...
*ffff800e1c707000 : large page allocation, tag is Cngb, size is 0x3ab0 bytes
        Pooltag Cngb : CNG kmode crypto pool tag, Binary : ksecdd.sys

После первой итерации цикла мы можем видеть, что в чанк пула происходит копирование из массива шестнадцатиричных значений в формате hex_byte + 0x00 + hex_byte + 0x00 + 0x20 + 0x00. Далее проиходит декремент размера, который мы передавали в полезную нагрузку и цикл повторяется до тех пор, пока все 0x2aac * 0x6 байт не будет записано в чанк.

Код:
2: kd> db @rcx -0x4
ffff800e`1c35d7d0  30 00 30 00 20 00 00 00-00 00 00 00 00 00 00 00  0.0. ...........
ffff800e`1c35d7e0  00 00 02 02 49 6f 43 63-c6 a6 54 b0 1a aa cf 2e  ....IoCc..T.....
ffff800e`1c35d7f0  00 5e 0a 1c 0e 80 ff ff-a0 37 49 08 ff 7f 00 00  .^.......7I.....

Мы добрались до третьей итерации, на которой перезаписываем поля структуры _POOL_HEADER следующего чанка, в моем случае, с тегом IoCc, что должно вызвать BSOD c ошибкой BAD_POOL_HEADER или PAGE_FAULT_IN_NONPAGED_AREA при записи в невалидную память. Стоит отметить, что PoC работает нестабильно и может вызывать ошибки в других драйверах. Также, я сталкивался с тем, что при использовании Driver Verifier, PoC вообще не срабатывал. Возможно, это как-то связано с особенностями специального пула.

Код:
2: kd> db @rcx - 0x12
ffff800e`1c35d7d0  30 00 30 00 20 00 30 00-30 00 20 00 30 00 30 00  0.0. .0.0. .0.0.
ffff800e`1c35d7e0  20 00 02 02 49 6f 43 63-c6 a6 54 b0 1a aa cf 2e   ...IoCc..T.....
ffff800e`1c35d7f0  00 5e 0a 1c 0e 80 ff ff-a0 37 49 08 ff 7f 00 00  .^.......7I.....
ffff800e`1c35d800  00 00 02 02 49 6f 43 63-00 00 00 00 00 00 00 00  ....IoCc........
ffff800e`1c35d810  00 5e 0a 1c 0e 80 ff ff-a0 37 49 08 ff 7f 00 00  .^.......7I.....
ffff800e`1c35d820  00 00 02 02 56 69 30 33-00 00 00 00 00 00 00 00  ....Vi03........
ffff800e`1c35d830  01 00 00 00 ff ff ff ff-00 00 00 00 00 00 00 00  ................
ffff800e`1c35d840  00 00 02 02 44 78 67 4b-00 00 00 00 00 00 00 00  ....DxgK........

9.png


Анализ крэш-дампа

После загрузки полного ядерного дампа в отладчик и, выполнив analyze -v, мы можем видеть полезуню информацию для анализа.
Код:
*******************************************************************************
*                                                                             *
*                        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: ffffa50ab81f8000, memory referenced.
Arg2: 0000000000000002, value 0 = read operation, 1 = write operation.
Arg3: fffff80330cb2503, If non-zero, the instruction address which referenced the bad memory
    address.
Arg4: 0000000000000002, (reserved)

В Arg1 содержится указатель на память, куда происходит копирование данных. Как уже сказано в описании ошибки, скорее всего это освободившаяся страница памяти, куда мы пытаемся обратиться. Предыдущую валидную страницу, начинающуюся с 0xffffa50ab81f7000 мы практически полностью переписали.

Код:
3: kd> db ffffa50ab81f7000 L100
ffffa50a`b81f7000  98 5d c0 ae 0a a5 ff ff-00 d0 6f b6 0a a5 ff ff  .]........o.....
ffffa50a`b81f7010  80 5d c0 ae 0a a5 ff ff-00 00 00 00 00 00 00 00  .]..............
ffffa50a`b81f7020  3a 00 7d 00 11 00 00 00-7a 1d f1 56 0c 01 50 00  :.}.....z..V..P.
ffffa50a`b81f7030  54 55 50 55 51 55 55 14-15 54 55 55 15 55 01 00  TUPUQUU..TUU.U..
ffffa50a`b81f7040  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 fc  ................
ffffa50a`b81f7050  01 00 a5 ee 83 d0 ff ff-01 f0 a5 ee 83 d0 ff ff  ................
ffffa50a`b81f7060  00 00 02 02 4d 6d 52 6c-00 00 00 00 00 00 00 00  ....MmRl........
ffffa50a`b81f7070  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
ffffa50a`b81f7080  00 00 02 02 43 6e 67 62-00 00 00 00 00 00 00 00  ....Cngb........
ffffa50a`b81f7090  30 00 30 00 20 00 30 00-30 00 20 00 30 00 30 00  0.0. .0.0. .0.0.
ffffa50a`b81f70a0  20 00 30 00 30 00 20 00-30 00 30 00 20 00 30 00   .0.0. .0.0. .0.
ffffa50a`b81f70b0  30 00 20 00 30 00 30 00-20 00 30 00 30 00 20 00  0. .0.0. .0.0. .
ffffa50a`b81f70c0  30 00 30 00 20 00 30 00-30 00 20 00 30 00 30 00  0.0. .0.0. .0.0.
ffffa50a`b81f70d0  20 00 30 00 30 00 20 00-30 00 30 00 20 00 30 00   .0.0. .0.0. .0.

Несмотря на то, что есть очевидный примитив на запись, я пока не могу сообразить как его можно использовать т.к. содержимое чанка мы не контролируем. Возможно, это будет в следующей части наряду с разбором PoC и части драйвера. :)
 
Последнее редактирование:


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