Гремлины и ELFийская магия: а что, если ELF-файл — это контейнер?

Гремлины и ELFийская магия: а что, если ELF-файл — это контейнер?

Мы, дети 90-х, любим добавить в задания NeoQUEST что-нибудь олдскульное. В этом году нам вспомнились гремлины, и мы добавили их в легенду одного из заданий соревнования «Очной ставки» NeoQUEST-2017.

Однако, под внешне забавной легендой скрывается вполне себе реальная практическая задача: а что, если привычные ELF-файлы — не просто исполняемые файлы, а контейнеры, открыть которые нам предстоит? Для этого придется испытать довольно-таки обширные возможности objcopy и освежить в памяти организацию ELF-файла.

Чтобы вычислить подозрительные секции, необходимо представлять секционный и сегментный состав типичного ELF. Помимо этого, конечно, пригодится и опыт — например, общение с firmware embedded-систем вполне может подсказать подходящие идеи!

Думаете, готовы на 100%? Уверены, что гремлинам удастся вас удивить спрятанными архивами, попорченными таблицами символов, а также аудиофайлами, которые зазвучат только в руках умелого мастера! Под катом — исходники к заданию и прохождение, чтобы каждый читатель Хабра мог собственноручно попробовать пройти задание!

Исходные данные к заданию

Легенда к заданию выглядела так:

Прибыв на место, мы обнаружили, что на базе полнейший хаос и разруха. Все это устроила парочка местных аборигенов, очень похожих на гремлинов из известного земного фильма. Один из них явно был главным в этой небольшой банде, более крупный, наглый и разговорчивый: — Hehehehehehe, we had ate your message! Had ate this stupid gremlin! And will eat you! Второй, мельче и скромнее, держался позади своего товарища и периодически выкрикивал: — The Gremlins Are Coming!

Похоже, эти паршивцы не только устроили хаос и разруху, повредили оставленное нам сообщение, но еще и товарища своего слопали…

Исходные данные — три файла: greik, straip и message.

Исходя из легенды, понимаем, что самый важный файл — message, и нужно его прослушать. Но, само собой, он не работает (иначе в чем смысл задания?). Если запустить его, получим следующую информацию:

Sorry, but i'm lost my prepare section.

Чтобы починить наше сообщение, надо «распотрошить» двух гремлинов, которые всё «погрызли», в том числе, и своего товарища. Запустим каждый файл. После ./greik увидим:

The Gremlins Are Coming! (оригинальная цитата из фильма, между прочим!)

Hehehehehehe, we had ate your message! Had ate this stupid gremlin! And will eat you! То есть, всё то, что описано в легенде. Раз эти гремлины съели всё необходимое для прохождения задания, нужно посмотреть, что у них внутри! Применим утилиту readelf на оба файла. Нас будут интересовать только секции файла и информация о них, поэтому необходимо использовать флаги -SW. В результате получим для straip:

Обратим внимание на секцию 28, stomach (желудок!). Сдампим ее. Для этого можно воспользоваться либо утилитой dd, высчитав все необходимые смещения, либо утилитой objcopy следующим образом:

Аналогичные действия произведем для файла greik. Выдача readelf -SW:

Дампим секции 28 и 29 (.first_stuff и .second_stuff):

Посмотрим, что же мы тут наизвлекали. Для этого используем утилиту file для каждого из извлеченных файлов. В результате получим:

stomach: Zip archive data, at least v2.0 to extract first_stuff: data second_stuff: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped

Разархивируем stomach и, используя утилиту file, посмотрим, что за файлы мы получили:

sec1: data piece2: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped key1: ASCII text

Ура, получили промежуточный ключ key1 («ISCJcPhB33cAvq9F9YkaZyU91SfwSObn»)! Остальные файлы необходимы для дальнейшего решения задачи. Исходный файл message сообщал, что у него потеряны секции, и из архива мы извлекли файл с именем sec1.

Применим утилиту readelf на message:

Видим, что имеется секция .extract (16) размером 0x50 (80). Также у нас есть один из извлеченных из гремлинов файлов размером 0x50 — first_stuff. Восстановим эту секцию:

Снова запустим message:

Preparing data for extract… Preparing data successful Sorry, but i'm lost my extract section.

Ему не хватает еще одной секции. По аналогии с предыдущей секцией восстанавливаем и эту:

Снова запускаем message:

Preparing data for extract… Preparing data successful Extracting key data… Extracting complete successful

Все выполнилось. В результате получили аудио-файл message.mp3. Однако, если попытаться его прослушать, мы услышим лишь тишину, потому что данные нулевые. Вспомним, что straip говорил нам, что они сожрали еще одного гремлина, и у нас осталось два неиспользованных файла в формате ELF.

Запуск их не даст ничего, кроме ошибки:

Failed to execute process './second_stuff'. Reason: exec: Exec format error The file './second_stuff' is marked as an executable but could not be run by the operating system.

Это все потому, что эти объектники необходимо слинковать друг с другом:

В результате получим:

piece2: In function `main': gizmo.c:(.text+0x5b): warning: the `gets' function is dangerous and should not be used. gizmo.c:(.text+0x86): undefined reference to `get_back_function' collect2: error: ld returned 1 exit status

И снова все не так, как должно быть! Используем утилиту readelf на наши «куски» гремлина, но на этот раз посмотрим таблицу символов (ключ -s). Для second_stuff:

Видим, что в одном из них есть наша функция get_back_function, на которую ругался компилятор, а во втором есть некая функция с именем «a4f922hjd9843jd». Переименуем ее в get_back_function:

Попробуем повторить сборку файла:

piece2: In function `main': gizmo.c:(.text+0x5b): warning: the `gets' function is dangerous and should not be used.

Как видим, теперь все хорошо. Запускаем gremlin:

Hello… I tried saved your message, but forgot my name… Remember my name, plese and I can get back part of your >>

Парниша вроде неплохой: пытался спасти наше сообщение, за что и пострадал. Готов все нам вернуть, да вот только своё имя забыл. Имя его Gizmo (оригинальное имя хорошего гремлина из фильма). Это можно увидеть как еще в процессе сборки, так и в любом HEX-редакторе в самом начале как исходных объектников, так и самого файла gremlin.

Введем его имя (с большой буквы, конечно!), ииии…

Hello… I tried saved your message, but forgot my name… Remember my name, please and I can get back part of your >> Gizmo Yes, yes! It's my name! Get back your part of file!

В результате получим еще один файл data_section. Легко догадаться, что и его мы запихиваем в исходный файл message:

Эту секцию .key можно было заметить еще когда мы исследовали утилитой readelf файл message. Во-первых, имя у нее говорящее, во-вторых, размер совпадает с тем, что вернул нам Gizmo.

Запустив файл message еще раз, мы уже получим нормальное сообщение, которое можно прослушать. Там сказано, что нам необходимо взять md5-хэш от этого файла и это будет ключом. Берём md5 и получаем ключ: 65935b0ae91b566c14c8b584ee8cf85d.

Happy End!

Оказывается, ELF-файл может быть далеко не просто исполняемым файлом, а контейнером для чего-либо. Это задание можно было пройти, выполнив реверс бинарников, но не реверсом единым сильны специалисты по инфобезу!

В данном варианте прохождения достаточно нескольких нехитрых манипуляций — и ELF-ский пазл успешно сложен, секции и таблицы символов восстановлены, архивы распакованы, а нашим гремлинам не остается ничего, кроме как сменить внушительное «The Gremlins Are Coming!» на белый флаг и выдать долгожданные ключи!

📎📎📎📎📎📎📎📎📎📎