Содержание статьи
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Подготовка
Играть будем с английской лицензионной версией 1.1 с накаченным поверх NoCD. Русская версия ничем принципиально не отличается, за исключением поддержки кириллических шрифтов. Я протестировал эксплоит на обеих версиях, и они оказались совместимы.
MD5
gta-vc.exe: 16094566bdaac10c7f9cc10beeac7ae8
gta-vc.exe: 32f157ea394c23ff0a91096226eccbf7
В качестве отладчика я буду использовать Immunity Debugger. Закинь к нему в папку PyCommands
скрипт mona.py, он добавляет новые команды во встроенный интерпретатор Python. Они потребуются для поиска ROP-гаджетов на этапе создания эксплоита.
По умолчанию игра открывается в полноэкранном режиме. Если запустить ее в отладчике и поймать точку останова, мы застрянем на зависшей программе, которая не дает себя нормально свернуть. Насколько я понимаю, игра не поддерживает два экрана, поэтому единственным решением может быть запуск в режиме окна. Нет никакой возможности сделать это через настройку конфигов или ключами запуска. Благо есть утилита D3DWindower 1.88, которая перехватывает функцию Direct3DCreate9
, подменяя взаимодействия с интерфейсом IDirect3D9
, чтобы включить оконный режим и выставить требуемый размер окна. Советую поставить ее или любой аналог.
Как устроен BMP
Давай посмотрим, как устроен формат изображений. Сначала идут два заголовка. За ними следует массив линий, каждая из которых состоит из информации о конкретных пикселях.
struct BMPheader{ uint16 magic; uint32 size; uint16 reserved[2]; uint32 offset;};struct DIBheader{ uint32 headerSize; int32 width; int32 height; int16 numPlanes; int16 depth; uint32 compression; uint32 imgSize; int32 hres; int32 vres; int32 paletteLen; int32 numImportant;};struct Pixel{ int8 red; int8 green; int8 blue;};BMPheader head_bmp;DIBheader head_dib;Pixel[256][256] lines;
Возьмем скин Batman.
как первоначальный образец для фаззера.
BMPheader
magic BM
size 196662
reserved 0
offset 54
DIBheader
headerSize 40
width 256
height 256
numPlanes 1
depth 24
compression 0
imgSize 196608
hres 0
vres 0
paletteLen 0
numImportant 0
Lines
Line
Pixel
R 57
G 52
B 33
Сломать можно только заголовки, любое изменение линий приведет исключительно к смене цветов на картинке.
Пишем фаззер BMP
Значимых полей не очень много. Заголовки занимают всего 54 байта. Мы легко можем побить их все, уничтожая по одному биту за раз, и получить на выходе 432 файла. Самый простой, но эффективный метод фаззинга — bit flipping, или переворачивание битов. Там, где был ноль, станет единица, там, где была единица, станет ноль.
Мне нравится проверять предположения по очереди, так что за один раз будем бить конкретный бит в каждом байте. Если бить нулевой, то значение увеличится или уменьшится на единицу, если бить седьмой, то на 128. Это крайние значения, проверку стоит начать с них.
import pwndef reverse_one_bit(data, bit_index): data_int = int.from_bytes(data, 'big', signed=False) max_bits = len(data) * 8 - 1 bit_mask = pow(2, bit_index) if bit_mask & data_int: data_int -= bit_mask else: data_int += bit_mask return data_int.to_bytes(len(data), 'big', signed=False)def mutate_file(input_path, headers_end, bit_num): input_file = pwn.read(input_path) file_head = input_file[:headers_end] body_len = len(input_file) - headers_end file_body = pwn.cyclic(body_len) for i in range(headers_end): mutated = reverse_one_bit(file_head[:], i*8 + bit_num) out_path = f'output/mutated_{i}_{bit_num}.bmp' pwn.write(out_path, mutated + file_body, create_dir=True)mutate_file('Batman.bmp', 54, 7)
Разберемся, что делает код. Сначала подключаем библиотеку pwntools. Затем с помощью ее функций читаем образец файла. В заголовке каждого файла повреждается один бит. Тело файла заменяется специальной последовательностью, которую выдает pwn.
. Этот метод генерирует строку типа aaaa
. Каждые 4 байта повторяются в ней лишь однажды. Это гарантирует, что мы всегда сможем визуально определить тело файла в памяти процесса.
Ловим падения
На прошлом шаге фаззер подарил нам 54 файла. Скопируем их в C:\
и через D3DWindower запустим gta-vc.
. Теперь присоединим к нему Immunity Debugger. По какой‑то причине игра подвисает, если повторно подключить отладчик к тому же процессу. Так что перезапускай Immunity, если у тебя такая же проблема.
Продолжение доступно только участникам
Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».
Присоединяйся к сообществу «Xakep.ru»!
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее