Мы, дети 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! (оригинальная цитата из фильма, между прочим!)
После ./straip:
Hehehehehehe, we had ate your message! Had ate this stupid gremlin! And will eat you!
То есть, всё то, что описано в легенде. Раз эти гремлины съели всё необходимое для прохождения задания, нужно посмотреть, что у них внутри! Применим утилиту readelf на оба файла. Нас будут интересовать только секции файла и информация о них, поэтому
необходимо использовать флаги -SW. В результате получим для straip:
Обратим внимание на секцию 28, stomach (желудок!). Сдампим ее. Для этого можно
воспользоваться либо утилитой dd, высчитав все необходимые смещения, либо утилитой objcopy следующим образом:
objcopy -O binary --only-section=.stomach --set-section-flags .stomach=alloc straip stomach
Аналогичные действия произведем для файла greik. Выдача readelf -SW:
Дампим секции 28 и 29 (.first_stuff и .second_stuff):
objcopy -O binary --only-section=.first_stuff --set-section-flags .first_stuff=alloc greik first_stuff
objcopy -O binary --only-section=.second_stuff --set-section-flags .second_stuff=alloc greik 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. Восстановим эту секцию:
objcopy --update-section .prepare=first_stuff message
Снова запустим message:
Preparing data for extract…
Preparing data successful
Sorry, but i'm lost my extract section...
Ему не хватает еще одной секции. По аналогии с предыдущей секцией восстанавливаем и эту:
objcopy --update-section .extract=sec1 message
Снова запускаем 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.
Это все потому, что эти объектники необходимо слинковать друг с другом:
gcc second_stuff piece2 -o gremlin
В результате получим:
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:
Для piece2:
Видим, что в одном из них есть наша функция get_back_function, на которую ругался компилятор, а во втором есть некая функция с именем «a4f922hjd9843jd». Переименуем ее в get_back_function:
objcopy --redefine-sym a4f922hjd9843jd=get_back_function second_stuff
Попробуем повторить сборку файла:
gcc second_stuff piece2 -o gremlin
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:
objcopy --update-section .key=data_section message
Эту секцию .key можно было заметить еще когда мы исследовали утилитой readelf файл message. Во-первых, имя у нее говорящее, во-вторых, размер совпадает с тем, что вернул нам Gizmo.
Запустив файл message еще раз, мы уже получим нормальное сообщение, которое можно
прослушать. Там сказано, что нам необходимо взять md5-хэш от этого файла и это будет ключом. Берём md5 и получаем ключ: 65935b0ae91b566c14c8b584ee8cf85d.
Happy End!
Оказывается, ELF-файл может быть далеко не просто исполняемым файлом, а контейнером для чего-либо. Это задание можно было пройти, выполнив реверс бинарников, но не реверсом единым сильны специалисты по инфобезу!
В данном варианте прохождения достаточно нескольких нехитрых манипуляций — и ELF-ский пазл успешно сложен, секции и таблицы символов восстановлены, архивы распакованы, а нашим гремлинам не остается ничего, кроме как сменить внушительное «The Gremlins Are Coming!» на белый флаг и выдать долгожданные ключи!
Автор: NWOcs