Издательский дом ООО "Гейм Лэнд"СПЕЦВЫПУСК ЖУРНАЛА ХАКЕР #45, АВГУСТ 2004 г.

Ломаем структуры

Мысла Владислав

Спецвыпуск Xakep, номер #045, стр. 045-036-2


Переполнение классов

Все сказанное касается также и классов. Возьмем предыдущий пример, но на этот раз все данные будут храниться не в структуре, а в классе. Более того, пусть теперь можно будет указать данные не для одного, а для двух пользователей.

Реализация аутентификации с помощью классов

Давай опять проверим работу получившегося приложения, которое также чувствительно к длине передаваемых ему строк. Приложение работает корректно, если длина строки не превышает 16 символов; если нарушить это правило, то перед вами откроются другие возможности, совсем не предусмотренные программистом :-). Так, например, при передаче в качестве пароля для первого пользователя строку из 30 байт приложение критически завершит свою работу.

Причина происшедшего в том, что для хранения класса используется динамическая память. Переполняя буфер, ты можешь изменить значения статических переменных, это верно, но ошибка не из-за этого. Посмотри на отладочную информацию. Видно, что после того как первый экземпляр класса получил логин и пароль, у второго изменилось значение VTable. А VTable – это адрес таблицы методов класса. В ней находятся все методы, помеченные как виртуальные, а все остальные находятся в сегменте кода. Адрес этой таблицы хранится по указателю объекта, а поскольку они создаются в стеке, то адрес таблицы лежит там же. Но на самом деле все просто: функции хранятся в области кода, переменные фиксированной длины лежат в той же области памяти, где расположен сам объект. Чтобы перезаписать область кода, необходимо копировать буфер очень большей длины, и поэтому, даже если есть возможность записи в сегмент кода, ей не всегда можно воспользоваться из-за расстояния от него до стека или кучи. Однако для большей гибкости и реализации наследования программисты используют виртуальные функции. Они ничем не отличаются от обычных за исключением метода доступа к ним. Если точнее, адрес функции не жестко прописан в коде, а находится в таблице, которая может меняться в ходе выполнения программы. Адрес таблицы хранится в первых 4 байтах объекта, а поэтому, если есть переполнение, то его можно перезаписать. Именно это и произошло в рассмотренном примере. Когда ты передал строку «name AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA name pass», пароль, хранящийся в первом экземпляре, перезаписал виртуальную таблицу методов второго. Чтобы в этой ситуации получить контроль над приложением, необходимо найти такой адрес в памяти, который не содержит в себе нулей и указывает на адрес, по которому находится твой shell-код. Надеюсь, теперь тебе понятно, в чем дело. Существует еще множество методов переполнения объектов в памяти, но эта тема заслуживает отдельного рассмотрения, а пока что мы остановимся на переполнении структурированных данных.

Назад на стр. 045-036-1  Содержание  Вперед на стр. 045-036-3