Слышали уже предупреждение?

Remote exploitation of an input validation error in the handling of
AutoFilter records in Excel BIFF8 format spreadsheet files by Microsoft Corp.’s
Excel 2003 could allow an attacker to execute arbitrary code in the context of
the current user. The AutoFilter feature of Excel allows data not matching a specified
criteria to be filtered out. By creating a document containing a specially
crafted filter record, an attacker is able to cause an invalid memory access
leading to arbitrary code execution.

Попробуем разобраться с не желающими устанавливать обновления
(как и не имеющими такой возможности), с тем чем
чреваты безобидные действия вида чтения документов из сомнительных источников. 

Для начала изучим чужой опыт в виде описания формата Biff.

Файл представляется цепочкой записей переменной длины имеющих единый шаблон структуры:

Назначение поля Размер в байтах
Тип 2
Размер данных 2
Данные переменный

Документ начинается записью типа Bof, завершается записью
Eof. Увы — в приведённом документе структура записи AutoFilter не описана, даже
не указан её код. Придется в очередной раз спросить гугл, в итоге он приводит к
весьма любопытному исходнику.

Пришло время набросать
генератор файла. Для начала создадим только заголовок и маркер конца потока,
дабы убедиться в правильности выводов:

Запускаем генератор после компиляции, отдаем
результат на съедение экселю — тот показывает пустую страницу, что и ожидалось.

Теперь пришло время подготовки зловредной записи.
Теоретически запись может иметь 64K данных, однако поделюсь
опытом — обычно записи с блоком данных, размер которого более 0x2020 байт попросту игнорируются.
Дописываем:

На созданный файл эксель реагирует не столь благодушно:

Сообщать об ошибке совершенно не входит в наши планы, поэтому
предложим ему отдаться отладчику. На этот раз всплывает следующее окно:

Из текста следует, что решил он записать нечто по адресу 0x000e4658, а
туда нельзя. Теперь нужно выяснить, что его побудило обратиться именно к этому
адресу, если он зависит от входных данных — то им можно управлять, а если еще он
и записывает туда что-то из входных данных — возникает некоторая вероятность радикального влияния на исполняемый процесс,
вплоть до перехвата управления. Посмотрим на происходящее отладчиком:

Знакомые всё цифры! В EBX размер блока данных записи, ESI указывает на бред в виде
‘AAAAAAA….’, стало быть эксель занят обработкой той самой записи.
Смотрим сбойную команду:

301999C7 mov         dword ptr [edi+14h],ecx

Попробуем разобраться, как регистры обрели текущие значения.
Единичка в ECX получается из наших данных:

301999B2 mov         cl,byte ptr [esi+2] ; cl=0x41
...
301999C4 and         ecx,3 ; ecx=1

В EDI параметр текущей функции, погружаемый в стек последним:

301999BE mov         edi,dword ptr [ebp+8]

Теперь посмотрим на код вызывающей сбой
функции с целью прояснить
происхождение значения в EDI:

30199A9A movzx       eax,word ptr [esi]			;	eax=0x4141	'AA'
30199A9D mov         ecx,dword ptr ds:[3085C4C0h]	;	ecx=??
30199AA3 imul        eax,eax,38h			;	eax=0x000E4638
30199AA6 mov         ecx,dword ptr [ecx+21Ch]		;	ecx=??
30199AAC lea         edi,[ecx+eax+0Ch]			;	edi=0xe4644 - по факту
30199AB0 push        esi  
30199AB1 push        edi  
30199AB2 call        301999A7

Как видим, EDI вычисляется по входным данным, стало быть возможна
контролируемая запись в память! При расчете адреса несколько раз
модифицируется ECX, поэтому (дабы не ошибиться)
установим точку останова на
0x30199A9A и запустив эксель откроем файл заново.

Дополним значения регистров:

30199A9A movzx       eax,word ptr [esi]			;	eax=0x4141	'AA'
30199A9D mov         ecx,dword ptr ds:[3085C4C0h]	;	ecx=0x00DA2178
30199AA3 imul        eax,eax,38h			;	eax=0x000E4638
30199AA6 mov         ecx,dword ptr [ecx+21Ch]		;	ecx=0
30199AAC lea         edi,[ecx+eax+0Ch]			;	edi=0x0E4638+0xC=0xe4644
30199AB0 push        esi  
30199AB1 push        edi  
30199AB2 call        301999A7

Ну вот, зависимость оказалась достаточно простой. Согласно ей можно
изменять значения двойных слов в диапазоне 0xc…0x37FFD4 c шагом 0x38
значениями 0…3. Немного, но:

  • хоть что-то;
  • адресное пространство модифицируемого двойного слова
    должно принадлежать сегменту доступному для записи;
  • нужно определить какая часть записи влияет на адрес, а
    какая на значение.

Пришло время выяснять структуру записи. Для начала изменим код
генерации заполнителя для придания ему разнообразия:

Точка останова взведена, рестартуем процесс, видим вот что:

Есть основания полагать, что на адрес по которому выполняется запись
влияют первые два байта, пусть это будет Dummy0. Попробуем изменить данные в верхушке стека,
поскольку этот сегмент памяти заведомо имеет разрешение на запись. Адрес
вычисляется как Dummy0 * 0x38 + 0xC + 0x14, где 0xC и 0x14 константы команд:

30199AAC   LEA EDI,DWORD PTR DS:[ECX+EAX+C]
....
301999C7   MOV DWORD PTR DS:[EDI+14],ECX

Тогда при желании изменить память вблизи адреса 0x127E2C Dummy0=(0x127E2C — 0xC — 0x14)/0x38=0x5489,
обратный пересчет дает 0x5489 * 0x38 + 0xC + 0x14 = 0x127E18. Проверяем:

Запускаем эксель клацая по созданному файлу — открывается без сбоев. Смотрим на это дело отладчиком:

Ну что, фокус удался! Понаблюдаем за происходящим дальше, и вот тут возникает такое чудо:

Сравните содержимое стека по адресу 0x00127E20 с предыдущим дампом… Адрес возврата изменён!
А что будет с случае если по командам

30199A0B   MOV AL,BYTE PTR DS:[ESI+E]
30199A0E   MOV BYTE PTR DS:[EDI+1C],AL

выполнялась бы пересылка не байта а двойного слова? Адрес возврата был бы перезаписан полностью,
а это означает перехват потока команд, поскольку адрес возврата в этом случае брался бы из входных данных!

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

Прежде всего как видно — переписан EBP. Кроме того, в стеке минимум 4 двойных слова, похожих на наши данные.
Стоп! EBP переписан, и дальнейшее исполнение должно привести к сбою, проверим, однако?

Сбоит, даже раньше чем ожидалось. Но почему с командной строки файл открывается вполне успешно? Ответ пока
напрашивается один — при запуске с командной строки стек отличается от того что мы видим в отладчике.
Пустим эксель, открываем файл через меню — сбой! Возникает проблема — как выяснить конфигурацию стека при старте
из командной строки? Можно позвать на помощь SoftIce, или приаттачиться к процессу, когда сбой уже произошел.
Попутно попытаемся определить начало чувствительной для выполнения области стека при его перезаписи.
Посему смодифицируем генератор записи с целью потыкаться наобум, эдакий fuzz:

Ничего необычного не происходит, видимо эта область слишком высоко.
Изменим исследуемый диапазон на 0x5410…0x5420. Началось!

Сбой произошел на команде

30199AB7 mov         dword ptr [edi],eax

ESP = 0x001263DC. Ставим точку останова в отладчике, пускаем эксель, открываем файл.
На этой команде ESP = 0x00127E2C, следовательно при чтении файла из меню указатель стека сдвинут на 0x1A50
байт в сторону старших адресов.

Теперь посчитаем. Адрес возврата хранимый при загрузке файла из меню в стеке по адресу
0x00127E20 при загрузке с командной строки должен располагаться по адресу 0x00127E20-0x1A50=0x001263D0.
Можно ли его переписать? Dummy0=(0x001263D0 — 0xC — 0x14)/0x38=0x5410, обратный пересчет дает 0x1263A0.
Но 0x5410 только что проверяли, бродим где-то рядом. Поправим диапазон на 0x5411…0x5420, и…

Это означает, что наши данные стали частью кода. Что дальше? А теперь пора выявить поля отвечающие за
адрес передачи управления:

Смотрим реакцию:

Вот теперь можно сделать вывод о смещении в записи блока отвечающего за адрес, на который будет передано
управление:

но вот куда можно его передать? Поищем наш бред в памяти. К примеру вот, в стеке.

Пусть это будет адрес 0x001274BC. А что туда поместить?
То, что называется полезной нагрузкой — pyload.
Небольшой код, выполняющий некоторые действия. Методика разработки полезной нагрузки — отдельная тема,
описывать её здесь — надолго оттянуть интересный
(на мой
взгляд)  финал. Воспользуемся готовым кодом,
тогда запись будет формироваться так:

Ну, посмотрели полученный документ? А ведь это мог быть не калькулятор…

Обратите внимание — для выявления данного эффекта не пришлось долго возиться с отладчиком, отслеживая
растекание по памяти входных данных. Конечно, данная методика не вскроет всех уязвимостей, но в сочетании
с изучением алгоритма обработки данных файлового буфера отладчиком поможет
в обнаружении далеко не одного сюрприза.

Оставить мнение

Check Also

В гостях у чертёнка. FreeBSD глазами линуксоида

Порог вхождения новичка в мир Linux за последние десять-пятнадцать лет ощутимо снизился. О…