Читаем Защита от хакеров корпоративных сетей полностью

Дамп стека после переполнения

Главное предназначение этой программы заключается в анализе уязвимостей переполнения буфера, поэтому на рис. 8.19 показан дамп стека после выполнения функции fread. Для примера был создан файл «badfile» с двадцатью символами «Л». После выполнения функции fread область стека изменена так же, как и в предыдущей программе, но дополнительно появилась возможность управлять записью данных в буфер с помощью файла «badfile». Запомним, что в функции определена дополнительная переменная стека – указатель дескриптора файла (дескриптор файла – уникальный идентификатор, присваиваемый системой Windows файлу в момент его открытия или создания и существующий до момента его закрытия), которая размещена в старших адресах памяти стека сразу за областью буфера.

Рис. 8.19. Дамп стека после выполнения функции fread

Программа переполнения буфера

После ознакомления с примером программы, уязвимой к переполнению буфера при чтении файла «badfile», пришло время познакомиться с программой, извлекающей из этого пользу, – программой переполнения буфера. Программа переполнения буфера написана на ANSI C, поэтому она может быть откомпилирована любым компилятором ANSI C. Для приведенных в книге примеров использованы компиляторы Visual C++ for Windows NT и GCC for Linux.

Вначале рассмотрен более простой случай – программа переполнения буфера для Linux, а затем для Windows NT и отмечены различия используемых способов переполнения буфера для других платформ.

Основные принципы построения программ переполнения буфера

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

Контролируя содержимое регистра EIP, можно выполнить нужный программный код. Обычно это достигается путем прямого или косвенного указания на специально написанный для этих целей программный код – программный код полезной добавки, или payload-код. Программный код полезной добавки описываемой программы переполнения буфера очень прост. Он только демонстрирует возможность осуществления задуманного. Более изощренные примеры программного кода полезной нагрузки будут рассмотрены позднее.

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

Создание программ переполнения буфера подразумевает несколько этапов. Во-первых, нужно получить доступ к буферу данных, то есть найти способ размещения в нем данных. Во-вторых, для того чтобы выполнить нужный код, следует найти способ контроля содержимого регистра EIP. Таких способов несколько. И наконец, в-третьих, нужен программный код полезной нагрузки, выполняющий возложенные на него функции.

Структура программы переполнения буфера

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

Загрузчики и программный код полезной нагрузки. В военном деле широко используются два связанных понятия: средства доставки и полезный груз. Аналогичные понятия применимы и для переполнения буфера. Говоря о переполнении буфера, подразумевают наличие средства доставки – загрузчика (injection vector) и полезного груза – программного кода полезной нагрузки. Загрузчик – выполнимый программный код, который позволяет управлять указателем на текущую команду удаленной машины. Это код целиком определяется компьютером, на котором он будет выполняться, и преследуемыми целями. Главная задача загрузчика заключается в том, что он должен заставить выполниться программный код полезной нагрузки. Программный код полезной нагрузки подобен вирусу: он должен работать везде, в любое время и независимо от того, как он попал на удаленную машину. Если программный код полезной нагрузки не удовлетворяет перечисленным требованиям, то он неработоспособен. Рассмотрим условия его создания.

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

Все программы вводят данные пользователя и хранят их где-нибудь. Любой буфер в программе является кандидатом на хранение программного кода полезной нагрузки. Вопрос только в том, как заставить процессор выполнить его.

Чаще всего для размещения программного кода полезной нагрузки используются:

• файлы на диске, которые загружаются в память;

• доступные локальному пользователю переменные окружения;

• передаваемые через Web-запрос общие переменные окружения;

• доступные пользователю поля сетевого протокола.

После размещения программного кода полезной нагрузки в памяти компьютера остается правильно загрузить в регистр EIP-адрес кода. При размещении программного кода полезной нагрузки не в области стека появляется ряд преимуществ, и ранее невозможное становится возможным. Например, сразу исчезает ограничение на размер кода. До сих пор для установления контроля над компьютером используется ошибка занижения или завышения на единицу числа подсчитываемых объектов (off-by-one error).

Способы передачи управления программному коду полезной нагрузки

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

Явный переход. Если программе переполнения буфера известен адрес программного кода полезной нагрузки, то можно передать ему управление при помощи явного перехода (direct jump). Программный код полезной нагрузки может быть загружен в область стека. Несмотря на то что адрес стека определить нетрудно, при явном переходе возможны ошибки. Во-первых, адрес стека может оказаться нулевым, поэтому может потребоваться размещение в памяти программного кода полезной нагрузки до загрузчика, сокращая доступную коду память. Во-вторых, адрес размещения в памяти программного кода полезной нагрузки может меняться, поэтому нужно учитывать возможные изменения адреса перехода. Способ явного перехода прост для использования. К тому же существуют хитроумные способы, позволяющие упростить определение адреса (см. секцию «Последовательность команд NOP»). В большинстве случаев в операционной системе UNIX адрес стека ненулевой, поэтому для нее описываемый способ подходит. И наконец, если программный код полезной нагрузки размещен вне стека, то способ явного перехода вне конкуренции.

Неявный переход. Регистр ESP указывает на текущее положение в стеке. Способ неявного перехода(blind return) основан натом, что любая команда ret, выполнив так называемое выталкивание данных из стека, загрузит в регистр EIP значение из области, на которую указывает регистр ESP. Существенно то, что команда ret загрузит в регистр EIP значение с вершины стека, которое будет проинтерпретировано как адрес программного кода. Если атакующий сможет подменить сохраненное в стеке содержимое регистра EIP на адрес своей программы, то по команде ret ей будет передано управление.

В ряде способов передачи управления программному коду полезной нагрузки используются регистры процессора для указания на область данных в стеке. Регистр EIP программно недоступен, поэтому нельзя подмененный указатель текущей команды непосредственно загрузить в регистр EIP и воспользоваться им для передачи управления в программе, как это показано на рис. 8.20. Для загрузки указателя текущей команды в регистр EIP указатель текущей команды должен указывать на реальную команду, как это показано на рис. 8.21.

Рис. 8.20. В регистр нельзя загрузить указатель на команду

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже