Задания на reverse engineering — обязательная часть любых CTF, и NeoQUEST в этом плане не исключение. В каждое задание мы добавляем «изюминку», которая, с одной стороны, несколько затрудняет участникам прохождение задания, а с другой — позволяет на практике разобраться с тем, с чем еще не приходилось работать.
Если говорить об «изюминках», то задание online-этапа NeoQUEST-2017 планеты Endian «Спасение экипажа» — практически кекс! Добро пожаловать под кат, в самые дебри реверса: поговорим об архитектуре PowerPC Big-Endian и немного — о QEMU!
А мы напоминаем, что 29 июня в Петербурге состоится «Очная ставка» NeoQUEST-2017: доклады, воркшопы, конкурсы, призы, отличное времяпровождение и свободный вход при регистрации на сайте — всё для тебя! Подробнее о том, что войдет в программу «Очной ставки», читай тут и на сайте!
Итак… Пока космические корабли бороздили просторы вселенной, участники NeoQUEST очутились на планете «Endian» и «обнаружили корабль прошлой экспедиции, той самой, которая считалась пропавшей без вести!». Уже неплохо! И приятно знать, что и в космосе тоже используют IP-адреса.
Читаем внимательно легенду и получаем следующую информацию:
Первым делом вбиваем данный IP-адрес в адресную строку браузера и погнали!
Название вкладки сразу же даёт первую подсказку. Каким-то образом здесь используется QEMU. Мысленно ставим галочку, чтобы не забыть, регистрируемся и грузим файл 1_8139.rom.
Что же происходит? Выводится скриншот, по-видимому, из виртуальной машины qemu, на скриншоте — просьба ввести пароль. Однако, как его ввести? Всё, что мы можем – подгружать странный файл, хотя… SeaBIOS и Booting from ROM… Те, кто работал с QEMU, уже, наверное, догадались, что это за файл мы загружаем. Но в целях чистоты эксперимента попробуем натравить на него команду file.
Таким образом, получается, что наш файл 8139 — это BIOS ROM Extension или PCI Expansion ROM.
Если попробовать загрузить произвольный файл, вот какой вывод у нас будет:
Похоже, что проверяются первые 3 байта. Ну, это не проблема — ставим первые три байта, как просят, иии… Ничего! То же самое. Как так? А дело в том, что если файл будет не в формате PCI Expansion ROM, то qemu его просто не запустит. А судя по заданию, там еще и подпись есть какая-то. Будем разбираться дальше.
Что это за зверь такой? PCI Expansion ROM – специальный кусочек кода инициализации логического PCI устройства, обычно хранимый либо в BIOS’е, либо на чипе PCI-устройства. Одно из самых распространенных вариантов использования – реализация механизма network boot (PXE). Если хотите увидеть, как это происходит, — запускаем qemu без параметров, и после того, как попытка загрузки с CD/HDD произойдет неудачно, qemu покажет вам PXE.
Мы определились с типом файла, давайте рассмотрим его структуру.
Под x86 у PCI ROM отводится первые 2 байта на сигнатуру 0x55aa, третий байт указывает на размер модуля (размер PCI ROM = значение 3 байта * 512), с четвертого байта идет уже исполняемый код. В большинстве PCI ROM, если не во всех, это обычный jump на код инициализации.
Ах да, чуть не забыл: так как это довольно старая технология, и использовалась она еще до времен UEFI, то весь код у нас 16-бит Real Mode.
Далее байты 0x18 указывают на смещение PCIR структуры, а 0x1A – структура PnP. Эти структуры носят служебный характер и несут основную информацию о модуле PCI ROM.
Внимательный читатель заметит, что вендор и девайс id как раз соответствуют сетевой карточке reltek 8139. Именно её модуль был взят за основу.
Из этих структур наибольший интерес представляет PnP. В ней находятся интересные поля, типа checksum и bootstrap entry.
Bootstrap entry указывает на код, на который передается управление при попытке загрузиться с этого девайса. В данном случае он имеет такой вид:
И, как видим, очень похоже на наш скриншот, значит, мы на верном пути! Теперь все, что нужно – заNOPить процедуру проверки пароля и попробовать скормить модифицированный файл вебке.
Не забываем, что у нас есть поле checksum в структуре PnP. Как считается чексумма? Все просто – изначально она ставится в 0x00 и считается сумма байтов файла. Затем полученная сумма вычитается из 0x100. Вот и получаем необходимое нам значение. Почему 0x100? Потому что чексумма однобайтовая. Дело в том, что сумма всех байтов при проверке должна показать 0x0. То есть, если сумма байтов нам показала 0x10, значением чексуммы будет 0xF0.
Понятно, что при загрузке модуля он запускается в qemu. Но ничего не мешает нам попробовать также использовать команду qemu -option-rom 1_8139.rom
Если все сделано правильно( патчим ROM и правим чексумму), то этот ROM просто будет возвращать управление BIOS’у. Пробуем в вебке… Успех! Появилось что-то новенькое:
Read CRC? Еще одна чексумма? Хорошо, хоть строка намекает на то, что у нас используется именно CRC (а значит, XOR) для проверки модуля. Найдем это значение в модуле.
Почти самый конец файла. Место мы знаем, теперь осталось понять алгоритм подписи CRC. Или хотя бы найти полином, а дальше можно и перебрать варианты. По заданию нам дают некий PowerPC Big-endian. Не надо пугать нас всякими PPC, мы и так пуганые. Берем IDA Pro, HEX-редактор, и вперед!
Размер файла довольно большой, поэтому решение «в лоб» вряд ли здесь уместно. Попробуем искать по зацепкам, которые у нас есть: алгоритм CRC и сообщение «CRC error». Для начала, найдем сообщения об ошибке, здесь нам поможет утилита strings.
Отлично! Все есть, но почему 2 раза? Разберемся! Смотрим в HEX-редакторе или в IDA.
Строку мы нашли, а заодно и остальные сообщения об ошибках. А еще — посмотрите внимательно! — перед каждым сообщением об ошибке у нас 2 байта, причем очень схожие. Ну а 0xFFFF (или -1 ) расставляет все на свои места. Это ассоциативный массив сообщений об ошибках. Создадим структуру в IDA Pro и получим вот такую красоту:
Теперь, следуя логике, весь этот код использует коды об ошибках. Однако у нас 2 попадания — код ошибки 0x1604 и 0xA04. Что ж, будем смотреть оба.
Ищем в IDA упоминания 0x1604:
Отлично, 2 попадания. Смотрим!
Этот код проверяет корректность CRC. Для удобства сразу обозначим вызов функции по адресу 0x42944 как crc_1604 и запомним, что функция возвращает значение через регистр R3. В регистре R29, скорее всего, хранится записанное значение CRC, а в регистре R0 – посчитанное значение CRC. Это становится понятно после просмотра кода функции crc_1604:
Небольшая функция, вот только она не похожа на CRC. Никакого полинома здесь нет, а используется обычный побайтовый XOR! Отсюда сразу же становится понятно, что код 0x1604 – для однобайтовой псевдо-CRC. А еще понятно, что посчитанное значение возвращается через R3 в вызывающую функцию, и, скорее всего, CRC надо сравнивать либо с записанным ранее, либо CRC должно быть нулевым. Вот два наиболее распространенных варианта проверки целостности.
Ну и отсюда следует, что нам нужен код 0xA04 – смотрим!
Аналогично ищем в IDA упоминания 0x1604:
О, всего одна функция, нам везёт!
Итак, что же видно? Код 0xA04 устанавливается, только если регистр r3 не равен нулю. А значит, функция по адресу 0x229c8 – наш клиент!
Сразу обращаем внимание на эти участки кода! И да, здесь тоже используется обычный XOR, только уже 4хбайтовый. Если подробно разбирать функцию, можно найти некоторые условия, которые накладываются на проверяемый файл. Но нам это не нужно, так как формат файла у нас есть, и нам нужен только механизм подписи. Мы это нашли! Обычный XOR по 4 байта.
Особенно неверующим можно показать примерную декомпиляцию данного участка (спасибо retdec.com).
Осталось только написать подсчет CRC и идти за ключом! Пишем заветные строки кода а-ля while(size--) {crc ^= *input++;} и заменяем значение в конце файла PCI ROM. Не забываем поменять чексумму, иии… Мы чемпионы!
Делая это задание, мы слегка ударились в ностальгию и вспомнили времена, когда макбуки были на PowerPC, ведь не одним Intel'ом прекрасен и разнообразен мир!
Участников финального соревнования hackquest 29 июня в Петербурге ждет множество разнообразных заданий, в которых, помимо реверса, потребуются также знания стеганографии, криптографии, умение проводить OSINT, работать с «рогатыми зелёными монстрами» (это мы, конечно же, про Android) и многое-многое другое!
Если говорить об «изюминках», то задание online-этапа NeoQUEST-2017 планеты Endian «Спасение экипажа» — практически кекс! Добро пожаловать под кат, в самые дебри реверса: поговорим об архитектуре PowerPC Big-Endian и немного — о QEMU!
А мы напоминаем, что 29 июня в Петербурге состоится «Очная ставка» NeoQUEST-2017: доклады, воркшопы, конкурсы, призы, отличное времяпровождение и свободный вход при регистрации на сайте — всё для тебя! Подробнее о том, что войдет в программу «Очной ставки», читай тут и на сайте!
С чего начать? С легенды!
Итак… Пока космические корабли бороздили просторы вселенной, участники NeoQUEST очутились на планете «Endian» и «обнаружили корабль прошлой экспедиции, той самой, которая считалась пропавшей без вести!». Уже неплохо! И приятно знать, что и в космосе тоже используют IP-адреса.
Читаем внимательно легенду и получаем следующую информацию:
- IP-адрес бортового компьютера 213.170.100.211
- «Специальный» файл 1_8139.rom
- Некий PowerPC Big-Endian бинарник 2_quest.cod_data
Первым делом вбиваем данный IP-адрес в адресную строку браузера и погнали!
Название вкладки сразу же даёт первую подсказку. Каким-то образом здесь используется QEMU. Мысленно ставим галочку, чтобы не забыть, регистрируемся и грузим файл 1_8139.rom.
Что же происходит? Выводится скриншот, по-видимому, из виртуальной машины qemu, на скриншоте — просьба ввести пароль. Однако, как его ввести? Всё, что мы можем – подгружать странный файл, хотя… SeaBIOS и Booting from ROM… Те, кто работал с QEMU, уже, наверное, догадались, что это за файл мы загружаем. Но в целях чистоты эксперимента попробуем натравить на него команду file.
Таким образом, получается, что наш файл 8139 — это BIOS ROM Extension или PCI Expansion ROM.
Если попробовать загрузить произвольный файл, вот какой вывод у нас будет:
Похоже, что проверяются первые 3 байта. Ну, это не проблема — ставим первые три байта, как просят, иии… Ничего! То же самое. Как так? А дело в том, что если файл будет не в формате PCI Expansion ROM, то qemu его просто не запустит. А судя по заданию, там еще и подпись есть какая-то. Будем разбираться дальше.
PCI Expansion ROM
Что это за зверь такой? PCI Expansion ROM – специальный кусочек кода инициализации логического PCI устройства, обычно хранимый либо в BIOS’е, либо на чипе PCI-устройства. Одно из самых распространенных вариантов использования – реализация механизма network boot (PXE). Если хотите увидеть, как это происходит, — запускаем qemu без параметров, и после того, как попытка загрузки с CD/HDD произойдет неудачно, qemu покажет вам PXE.
Мы определились с типом файла, давайте рассмотрим его структуру.
Под x86 у PCI ROM отводится первые 2 байта на сигнатуру 0x55aa, третий байт указывает на размер модуля (размер PCI ROM = значение 3 байта * 512), с четвертого байта идет уже исполняемый код. В большинстве PCI ROM, если не во всех, это обычный jump на код инициализации.
Ах да, чуть не забыл: так как это довольно старая технология, и использовалась она еще до времен UEFI, то весь код у нас 16-бит Real Mode.
Далее байты 0x18 указывают на смещение PCIR структуры, а 0x1A – структура PnP. Эти структуры носят служебный характер и несут основную информацию о модуле PCI ROM.
Внимательный читатель заметит, что вендор и девайс id как раз соответствуют сетевой карточке reltek 8139. Именно её модуль был взят за основу.
Из этих структур наибольший интерес представляет PnP. В ней находятся интересные поля, типа checksum и bootstrap entry.
Bootstrap entry указывает на код, на который передается управление при попытке загрузиться с этого девайса. В данном случае он имеет такой вид:
И, как видим, очень похоже на наш скриншот, значит, мы на верном пути! Теперь все, что нужно – заNOPить процедуру проверки пароля и попробовать скормить модифицированный файл вебке.
Не забываем, что у нас есть поле checksum в структуре PnP. Как считается чексумма? Все просто – изначально она ставится в 0x00 и считается сумма байтов файла. Затем полученная сумма вычитается из 0x100. Вот и получаем необходимое нам значение. Почему 0x100? Потому что чексумма однобайтовая. Дело в том, что сумма всех байтов при проверке должна показать 0x0. То есть, если сумма байтов нам показала 0x10, значением чексуммы будет 0xF0.
Понятно, что при загрузке модуля он запускается в qemu. Но ничего не мешает нам попробовать также использовать команду qemu -option-rom 1_8139.rom
Если все сделано правильно( патчим ROM и правим чексумму), то этот ROM просто будет возвращать управление BIOS’у. Пробуем в вебке… Успех! Появилось что-то новенькое:
Read CRC? Еще одна чексумма? Хорошо, хоть строка намекает на то, что у нас используется именно CRC (а значит, XOR) для проверки модуля. Найдем это значение в модуле.
Почти самый конец файла. Место мы знаем, теперь осталось понять алгоритм подписи CRC. Или хотя бы найти полином, а дальше можно и перебрать варианты. По заданию нам дают некий PowerPC Big-endian. Не надо пугать нас всякими PPC, мы и так пуганые. Берем IDA Pro, HEX-редактор, и вперед!
Размер файла довольно большой, поэтому решение «в лоб» вряд ли здесь уместно. Попробуем искать по зацепкам, которые у нас есть: алгоритм CRC и сообщение «CRC error». Для начала, найдем сообщения об ошибке, здесь нам поможет утилита strings.
Отлично! Все есть, но почему 2 раза? Разберемся! Смотрим в HEX-редакторе или в IDA.
Строку мы нашли, а заодно и остальные сообщения об ошибках. А еще — посмотрите внимательно! — перед каждым сообщением об ошибке у нас 2 байта, причем очень схожие. Ну а 0xFFFF (или -1 ) расставляет все на свои места. Это ассоциативный массив сообщений об ошибках. Создадим структуру в IDA Pro и получим вот такую красоту:
Теперь, следуя логике, весь этот код использует коды об ошибках. Однако у нас 2 попадания — код ошибки 0x1604 и 0xA04. Что ж, будем смотреть оба.
0x1604
Ищем в IDA упоминания 0x1604:
Отлично, 2 попадания. Смотрим!
Этот код проверяет корректность CRC. Для удобства сразу обозначим вызов функции по адресу 0x42944 как crc_1604 и запомним, что функция возвращает значение через регистр R3. В регистре R29, скорее всего, хранится записанное значение CRC, а в регистре R0 – посчитанное значение CRC. Это становится понятно после просмотра кода функции crc_1604:
Небольшая функция, вот только она не похожа на CRC. Никакого полинома здесь нет, а используется обычный побайтовый XOR! Отсюда сразу же становится понятно, что код 0x1604 – для однобайтовой псевдо-CRC. А еще понятно, что посчитанное значение возвращается через R3 в вызывающую функцию, и, скорее всего, CRC надо сравнивать либо с записанным ранее, либо CRC должно быть нулевым. Вот два наиболее распространенных варианта проверки целостности.
Ну и отсюда следует, что нам нужен код 0xA04 – смотрим!
0xA04
Аналогично ищем в IDA упоминания 0x1604:
О, всего одна функция, нам везёт!
Итак, что же видно? Код 0xA04 устанавливается, только если регистр r3 не равен нулю. А значит, функция по адресу 0x229c8 – наш клиент!
Сразу обращаем внимание на эти участки кода! И да, здесь тоже используется обычный XOR, только уже 4хбайтовый. Если подробно разбирать функцию, можно найти некоторые условия, которые накладываются на проверяемый файл. Но нам это не нужно, так как формат файла у нас есть, и нам нужен только механизм подписи. Мы это нашли! Обычный XOR по 4 байта.
Особенно неверующим можно показать примерную декомпиляцию данного участка (спасибо retdec.com).
Осталось только написать подсчет CRC и идти за ключом! Пишем заветные строки кода а-ля while(size--) {crc ^= *input++;} и заменяем значение в конце файла PCI ROM. Не забываем поменять чексумму, иии… Мы чемпионы!
На «Очной ставке» тоже будет интересно!
Делая это задание, мы слегка ударились в ностальгию и вспомнили времена, когда макбуки были на PowerPC, ведь не одним Intel'ом прекрасен и разнообразен мир!
Участников финального соревнования hackquest 29 июня в Петербурге ждет множество разнообразных заданий, в которых, помимо реверса, потребуются также знания стеганографии, криптографии, умение проводить OSINT, работать с «рогатыми зелёными монстрами» (это мы, конечно же, про Android) и многое-многое другое!