...

среда, 23 октября 2019 г.

ZeroNights Hackquest 2019. Results & Writeups

Дано: файл jD74nd8_task2.iso, загрузочный ISO образ. По файлам внутри образа можно предположить, что это Linux: присутствует ядро boot/kernel.xz, начальный рамдиск boot/rootfs.xz и загрузчик boot/syslinux/.

Пробуем распаковать ядро и рамдиск. Рамдиск здесь — обычный cpio архив, сжатый xz. Ядро распаковываем, используя скрипт https://github.com/torvalds/linux/blob/master/scripts/extract-vmlinux. Также можно обратить внимание на информацию о ядре:

> file kernel.xz                   
kernel.xz: Linux kernel x86 boot executable bzImage, version 5.0.11 (billy@micosoft.com) #1 SMP Sat Aug 25 13:37:00 CEST 2019, RO-rootFS, swap_dev 0x2, Normal VGA

Попутно находим в iso образе основную задачу minimal/rootfs/bin/activator: все сводится к записи введенных данных электронной почты и ключа активации в устройство /dev/activate в формате $email|$key. В случае удачной проверки ключа, чтение из /dev/activate будет выдавать строку ACTIVATED, и активатор в данном случае запустит игру 2048.

Настало время глянуть на задачу в динамике. Для этого запускаем эмулятор в KVM:

> qemu-system-x86_64 -enable-kvm -drive format=raw,media=cdrom,readonly,file=jD74nd8_task2.iso

Linux стартует и сразу запускает /bin/activator из overlay. Это прописано в /etc/inittab. Чтоб долго не копаться в бинаре ядра, хотелось получить шелл и посмотреть, как минимум, на /proc и /sys. Самым простым для меня способом оказалось просто подпачить iso файл в месте, где расположен сам скрипт активатора. Вместо sleep 1 поставил /bin/sh, т.е. получал шелл после каждой попытки ввода серийника.

Итак шелл есть: смотрим, что /proc/kallsyms отсутствует, т.е. отсутствуют символы ядра. С ними, конечно же, было б гораздо быстрее, но ничего страшного. Ищем информацию об устройстве /dev/activator:

/ # ls -la /dev/activate
crw-------    1 0        0         252,   0 Oct 15 08:57 /dev/activate
/ # cat /proc/devices
Character devices:
...
252 activate
...

Block devices:
...

Из информации в /proc/devices видно, что это символьное (char) устройство, у которого major версия 252 и minor — 0.

Настало время найти в бинаре ядра функцию регистрации этого устройства, чтоб найти обработчик его операции write. Для этого нужно найти перекрестные ссылки на строку activate. Но такой строки в ядре нет, вероятно её как-то прячут.

В следующей попытке пробуем найти функции, отвечающие за регистрацию символьных устройств: cdev_add и register_chrdev. Это можно сделать по перекрестным ссылкам на /dev/console или на любое другое символьное устройство и взяв исходный код ядра (я брал версию 5.0.11, но не уверен, что версия указана верно). Посмотрев список устройств, которые регистрируются, не находим там устройство с major версией 252. Вероятно регистрация происходит не этими двумя функциями.

Попробуем поискать еще какие-то зацепки в динамике:

/ # ls -la /sys/dev/char/252:0
lrwxrwxrwx    1 0        0                0 Oct 15 09:00 /sys/dev/char/252:0 -> ../../devices/virtual/EEy????I/activate

Вот и зацепка — класс устройства EEy????I. Пробуем найти данную строку в бинаре и она там есть!

Хоть и перекрестных ссылок на неё не найдено, но рядом видны данные, похожие на строки. Если посмотреть код, который их использует, то видно, что это те искомые обработчики чтения и записи устройства activate, которые зашифрованы простым XOR.

Функция обработки операции чтения:

Функция обработки операции записи, она же проверка лицензии:

Беглый осмотр кода проверки активации показал, что легче всего просто поставить точку останова на адресе 0xFFFFFFFF811F094B и там забрать код активации, не особо вникая, что же там происходит. Для этого запускаем qemu с флагом -s. В этом случае qemu запускает gdb stub, который позволяет использовать любой gdb клиент. Проще и быстрее всего это делать в IDA Pro, если есть лицензия. Но никто не запрещает все сделать в консольном gdb.

Проделываем все, как описано в официальном туториале. Теперь нужно найти функцию обработки внутри уже запущенного ядра.

Так как ядро собрано с поддержкой KASLR, то адреса запущенного ядра сдвинуты на рандомное смещение, которое генерируется каждый запуск ядра. Вычисляем это смещение (берем адрес уникальной последовательности байт в коде отлаживаемого ядра и отнимаем от него адрес этой последовательности в бинаре) и, добавляя к адресу функции активации, находим её в памяти. Всё, теперь дело за малым. Поставить точку останова и забрать код.

Let's block ads! (Why?)

Комментариев нет:

Отправить комментарий