Книга «Хакинг: искусство эксплойта. 2-е изд.»

в 12:25, , рубрики: Блог компании Издательский дом «Питер», книга, Профессиональная литература

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

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

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

Мир без хакеров — это мир без любопытства и новаторских решений. (Джон Эриксон)

Меры противодействия

Есть такая лягушка — ужасный листолаз (Phyllobates terribilis). Ее кожные железы содержат сильнейший яд. Достаточно просто прикоснуться к ней, чтобы получить смертельное отравление. Столь мощное средство защиты объясняется тем, что этими лягушками питаются определенные виды змей, выработавшие у себя невосприимчивость к яду. Постепенно яд лягушек становился все сильнее и сильнее. В результате такой совместной эволюции у ужасных листолазов не осталось других естественных врагов. Нечто подобное происходит и с хакерами. Придуманные ими техники давно известны, так что появление мер противодействия вполне естественно. В ответ хакеры ищут способы обойти и разрушить защитные механизмы, что приводит к созданию новых оборонительных техник.

Этот цикл поиска мер и контрмер весьма полезен. Вирусы и черви становятся причиной многочисленных неприятностей и приносят бизнесу большие убытки, но в то же время они заставляют разработчиков принимать ответные меры для решения возникших проблем. Черви самовоспроизводятся, используя уязвимости некачественного программного обеспечения. Зачастую ошибки остаются незамеченными на протяжении лет, а относительно безвредные черви, такие как CodeRed или Sasser, заставляют разработчиков их исправить. Этот процесс можно сравнить с ветрянкой, которой лучше переболеть в детстве, а не во взрослом возрасте, когда она способна привести к катастрофическим последствиям. Если бы интернет-черви не привлекли всеобщее внимание к дырам в безопасности, эти дыры оставались бы открытыми для атаки с куда более злонамеренными целями, чем простое самовоспроизведение. Таким образом, черви и вирусы содействуют укреплению безопасности в долгосрочной перспективе. Но есть и более активные способы: техники, пытающиеся свести к нулю результаты атаки или сделать ее и вовсе неосуществимой. Понятие «меры противодействия» довольно расплывчатое, под этими словами могут подразумеваться технические средства обеспечения безопасности, набор правил, программа или просто внимательный системный администратор. Меры противодействия условно делятся на две группы: пытающиеся обнаружить атаку и пытающиеся защитить уязвимость.

0x610 Средства обнаружения атак

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

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

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

Для атакующего обнаружение означает противодействие всему, что он делает. Разумеется, мгновенно заметить атаку удается далеко не всегда, поэтому существует ряд сценариев типа «схватил и убежал», в которых факт обнаружения не важен, но даже в этих случаях лучше не оставлять следов. Скрытность — одно из самых ценных качеств хакеров. Получение доступа к командной оболочке с правами администратора путем эксплуатации уязвимостей дает возможность делать в системе что угодно, а если удастся избежать обнаружения, никто не узнает о вашем присутствии. Именно сочетание вседозволенности с невидимостью делает хакеров опасными. Они могут спокойно перехватывать в сети пароли и данные, добавлять в программы закладки для последующего несанкционированного доступа и атаковать другие узлы сети. Чтобы оставаться в тени, хакеру следует понимать, какие методы распознавания используются в том или ином конкретном случае. Если знать, что именно они ищут, можно избежать определенных шаблонов эксплуатации уязвимости или замаскировать свои действия под допустимые. Движущим фактором для цикла совместной эволюции средств обнаружения и приемов, позволяющих оставаться незамеченным, служат идеи, которые пока не пришли в голову другой стороне.

0x620 Системные демоны

Впрочем, меры противодействия хакерам и их обходные маневры лучше обсуждать на практическом примере. Сейчас мы рассмотрим атаку на серверную программу, принимающую входящие соединения. В UNIX-подобных операционных системах под эти критерии подходят системные демоны. Демоном называется программа, работающая в фоновом режиме и определенным способом отделенная от управляющего терминала. Термин был придуман в 1960-е хакерами из Массачусетского технологического института. Прообразом стало мифическое существо, сортирующее молекулы, из мысленного эксперимента физика Джеймса Максвелла. Демон Максвелла обладал сверхъестественной способностью легко выполнять сложные задачи, нарушая при этом второй закон термодинамики. Аналогичным образом системные демоны в Linux без устали выполняют такие задачи, как предоставление доступа к SSH и ведение системных журналов. Имена демонов, как правило, заканчиваются на d, что подчеркивает их природу: например, sshd или syslogd.

Небольшое редактирование превратит программу tinyweb.c из раздела 0x427 в реалистичное подобие системного демона. Новая версия кода содержит функцию daemon(), порождающую новый фоновый процесс. Она используется многими процессами системных демонов в Linux. Вот посвященная ей страница из справочника:

DAEMON(3) Справочник программиста Linux DAEMON(3)

ИМЯ
daemon – запуск в фоновом режиме
СИНТАКСИС

#include <unistd.h>
int daemon(int nochdir, int noclose);

ОПИСАНИЕ

Функция daemon() отключает программу от управляющего терминала и запускает
ее в фоновом режиме как системного демона.
При ненулевом аргументе nochdir функция daemon() меняет текущий рабочий каталог
на корневой ("/").
При ненулевом аргументе noclose функция daemon() перенаправляет потоки
стандартного ввода, стандартного вывода и ошибок в /dev/null.
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

(Эта функция порождает копию процесса, и при успешном выполнении функции fork()
родитель выполняет _exit(0), чтобы дальнейшие ошибки были видимы только
дочернему процессу.) В случае успеха возвращается ноль. При ошибке функция
daemon() возвращает -1 и присваивает глобальной переменной errno номер ошибки
из библиотеки функций fork(2) и setsid(2).

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

0x621 Обзор сигналов

Сигналы обеспечивают взаимодействие между процессами в UNIX. После получения сигнала процессом операционная система прерывает его выполнение, чтобы вызвать обработчик сигнала. Каждому сигналу соответствует свой номер и свой обработчик. Например, при нажатии комбинации клавиш Ctrl+C посылается сигнал прерывания, обработчик которого завершает открытую на управляющем терминале программу, даже если та вошла в бесконечный цикл.

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

signal_example.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
/* Сигналы из файла signal.h
      * #define SIGHUP 1 Отбой
      * #define SIGINT 2 Прерывание (Ctrl+C)
      * #define SIGQUIT 3 Выход (Ctrl+)
      * #define SIGILL 4 Недопустимая инструкция
      * #define SIGTRAP 5 Перехват события
      * #define SIGABRT 6 Самоостановка
      * #define SIGBUS 7 Ошибка шины
      * #define SIGFPE 8 Исключение с плавающей точкой
      * #define SIGKILL 9 Сигнал завершения
      * #define SIGUSR1 10 Пользовательский сигнал 1
      * #define SIGSEGV 11 Нарушение сегментации
      * #define SIGUSR2 12 Пользовательский сигнал 2
      * #define SIGPIPE 13 Есть запись в канал, но нет чтения
      * #define SIGALRM 14 Обратный отсчет таймера, заданного alarm()
      * #define SIGTERM 15 Завершение (посланное командой kill)
      * #define SIGCHLD 17 Сигнал дочернего процесса
      * #define SIGCONT 18 Продолжить, если была остановка
      * #define SIGSTOP 19 Остановка (пауза в выполнении)
      * #define SIGTSTP 20 Остановка терминала [ожидание] (Ctrl+Z)
      * #define SIGTTIN 21 Фоновый процесс пытается читать стандартный ввод
      * #define SIGTTOU 22 Фоновый процесс пытается читать стандартный вывод
*/
/* Обработчик сигнала */
void signal_handler(int signal) {
      printf("Перехвачен сигнал %dt", signal);
      if (signal == SIGTSTP)
          printf("SIGTSTP (Ctrl-Z)");
      else if (signal == SIGQUIT)
          printf("SIGQUIT (Ctrl-\)");
      else if (signal == SIGUSR1)
            printf("SIGUSR1");
      else if (signal == SIGUSR2)
            printf("SIGUSR2");
            printf("n");
}

void sigint_handler(int x) {
      printf("Перехвачено Ctrl-C (SIGINT) в отдельном обработчикеnExiting.n");
      exit(0);
}

int main() {
      /* Registering signal handlers */
      signal(SIGQUIT, signal_handler); // Сделать signal_handler()
      signal(SIGTSTP, signal_handler); // обработчиком этих сигналов
      signal(SIGUSR1, signal_handler);
      signal(SIGUSR2, signal_handler);

      signal(SIGINT, sigint_handler); // Установить sigint_handler() для SIGINT

      while(1) {} // Бесконечный цикл
}

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

reader@hacking:~/booksrc $ gcc -o signal_example signal_example.c
reader@hacking:~/booksrc $ ./signal_example
Перехвачен сигнал 20 SIGTSTP (Ctrl-Z)
Перехвачен сигнал 3 SIGQUIT (Ctrl-)
Перехвачено Ctrl-C (SIGINT) в отдельном обработчике
Exiting.
reader@hacking:~/booksrc $

Команда kill позволяет посылать процессу целый набор сигналов. По умолчанию она посылает сигнал завершения (SIGTERM). Добавление к ней параметра -l выводит список всех возможных сигналов. Посмотрим, как программе signal_example, выполняемой на другом терминале, посылаются сигналы SIGUSR1 и SIGUSR2.

reader@hacking:~/booksrc $ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGBUS       8) SIGFPE
 9) SIGKILL     10) SIGUSR1     11) SIGSEGV     12) SIGUSR2
13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGSTKFLT
17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU
25) SIGXFSZ     26) SIGVTALRM   27) SIGPROF     28) SIGWINCH
29) SIGIO       30) SIGPWR      31) SIGSYS      34) SIGRTMIN
35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3  38) SIGRTMIN+4
39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12
47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14
51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10
55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7  58) SIGRTMAX-6
59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2
63) SIGRTMAX-1  64) SIGRTMAX
reader@hacking:~/booksrc $ ps a | grep signal_example
24491 pts/3    R+     0:17 ./signal_example
24512 pts/1    S+     0:00 grep signal_example
reader@hacking:~/booksrc $ kill -10 24491
reader@hacking:~/booksrc $ kill -12 24491
reader@hacking:~/booksrc $ kill -9 24491
reader@hacking:~/booksrc $

В конце командой kill -9 посылается сигнал SIGKILL. Обработчик этого сигнала поменять нельзя, поэтому команда kill -9 всегда используется для уничтожения процессов. Запущенная на другом терминале программа signal_example показывает, что сигналы были перехвачены, а процесс — уничтожен.

reader@hacking:~/booksrc $ ./signal_example
Перехвачен сигнал 10       SIGUSR1
Перехвачен сигнал 12       SIGUSR2
Killed
reader@hacking:~/booksrc $

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

0x622 Демон tinyweb

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

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

tinywebd.c
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#include "hacking.h"
#include "hacking-network.h"
			 
#define PORT 80 // Порт, к которому подсоединяются пользователи
#define WEBROOT "./webroot" // Корневой каталог веб-сервера
#define LOGFILE "/var/log/tinywebd.log" // Имя файла журнала
			 
int logfd, sockfd; // Глобальные дескрипторы файлов журнала и сокета
void handle_connection(int, struct sockaddr_in *, int);
int get_file_size(int); // Возвращаем размер файла открытого с указанным 
			       // дескриптором
void timestamp(int); // Записываем временную метку в дескриптор открытого файла
			 
// Эта функция вызывается при уничтожении процесса
void handle_shutdown(int signal) {
      timestamp(logfd);
      write(logfd, "Завершение работы.n", 16);
      close(logfd);
      close(sockfd);
      exit(0);
}
			 
int main(void) {

     int new_sockfd, yes=1;
     struct sockaddr_in host_addr, client_addr; // Мои адресные данные
     socklen_t sin_size;
			 
     logfd = open(LOGFILE, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
     if(logfd == -1)
	 fatal("открытие файла журнала");
			 
     if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
         fatal("в сокете");
			 
     if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
	 fatal("при задании параметра SO_REUSEADDR");
			 
     printf("Запуск демона tiny web.n");
     if(daemon(1, 0) == -1) // Создание демонизированного процесса
	 fatal("при создании демонизированного процесса");
			 
     signal(SIGTERM, handle_shutdown);   // При завершении вызываем handle_shutdown
     signal(SIGINT, handle_shutdown);   // При прерывании вызываем handle_shutdown
			 
     timestamp(logfd);
     write(logfd, "Запуск.n", 15);
     host_addr.sin_family = AF_INET;       // Локальный порядок байтов
     host_addr.sin_port = htons(PORT);     // Короткое целое, сетевой порядок байтов
     host_addr.sin_addr.s_addr = INADDR_ANY; // Автоматически заполняется моим IP
     memset(&(host_addr.sin_zero), '', 8); // Обнуляем остаток структуры
			 
     if (bind(sockfd, (struct sockaddr *)&host_addr, sizeof(struct sockaddr)) == -1)
	 fatal("связь с сокетом");
			 
     if (listen(sockfd, 20) == -1)
	 fatal("слушание со стороны сокета");
			 
     while(1) { // Цикл функции accept
	   sin_size = sizeof(struct sockaddr_in);
	   new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);
	   if(new_sockfd == -1)
		fatal("прием соединения");
			 
	    handle_connection(new_sockfd, &client_addr, logfd);
       }
       return 0;
}
			 
/* Функция обрабатывает соединение на переданном сокете и с
* переданного адреса клиента и пишет журнал в переданный FD.
* Соединение обрабатывается как веб-запрос, а функция отвечает
* через сокет соединения. После ее завершения сокет закрывается.
*/
void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr, int logfd) {
      unsigned char *ptr, request[500], resource[500], log_buffer[500];
      int fd, length;
			 
      length = recv_line(sockfd, request);
			 
      sprintf(log_buffer, "От %s:%d "%s"t", inet_ntoa(client_addr_ptr->sin_addr),
ntohs(client_addr_ptr->sin_port), request);
			 
       ptr = strstr(request, " HTTP/"); // Поиск корректного запроса
       if(ptr == NULL) { // в этом случае HTTP некорректный
	    strcat(log_buffer, " НЕ HTTP!n");
       } else {
	      *ptr = 0; // Завершаем буфер в конце адреса URL
	      ptr = NULL; // Устанавливаем ptr на NULL (используется как флаг для 
			        // некорректного запроса)
	      if(strncmp(request, "GET ", 4) == 0) // Запрос GET
	           ptr = request+4; // ptr - это URL
	      if(strncmp(request, "HEAD ", 5) == 0) // Запрос HEAD
	           ptr = request+5; // ptr - это URL
	      if(ptr == NULL) { // тогда запрос не распознан
	           strcat(log_buffer, " НЕИЗВЕСТНЫЙ ЗАПРОС!n");
	      } else { // Корректный запрос с ptr, указывающим на имя ресурса
		   if (ptr[strlen(ptr) - 1] == '/')   // Для ресурсов, заканчивающихся 
			                                        // на '/',
		        strcat(ptr, "index.html");   // добавляем в конец 'index.html'
		   strcpy(resource, WEBROOT);   // Начать resource с пути к корневому 
			                                        // каталогу
		   strcat(resource, ptr);   // объединить с путем к ресурсу
		   fd = open(resource, O_RDONLY, 0);  // Пытаемся открыть файл
		   if(fd == -1) { // Если файл не обнаружен
                       strcat(log_buffer, " 404 Not Foundn");
	               send_string(sockfd, "HTTP/1.0 404 NOT FOUNDrn");
                       send_string(sockfd, "Server: Tiny webserverrnrn");
                       send_string(sockfd, "<html><head><title>404 Not Found</title>
			                                         </head>");
                       send_string(sockfd, "<body><h1>URL not found</h1></body></html>
			                                          rn");
		   } else {       // В противном случае передаем файл
		        strcat(log_buffer, " 200 OKn");
	                send_string(sockfd, "HTTP/1.0 200 OKrn");
	                send_string(sockfd, "Server: Tiny webserverrnrn");
	                if(ptr == request + 4) { // тогда это запрос GET
			     if( (length = get_file_size(fd)) == -1)
			          fatal("при получении размера файла ресурса ");
			     if( (ptr = (unsigned char *) malloc(length)) == NULL)
			          fatal("при выделении памяти под чтение ресурса ");
			          read(fd, ptr, length);  // Читаем файл в память
			          send(sockfd, ptr, length, 0);  // Посылаем его на сокет
			          free(ptr);  // Освобождаем память от файла
	             }
	             close(fd); // Закрываем файл
		     } // Конец блока if для обнаружения/не обнаружения файла
	        } // Конец блока if для определения корректности запроса
	   } // Конец блока if для определения корректности HTTP
	   timestamp(logfd);
	   length = strlen(log_buffer);
	   write(logfd, log_buffer, length); // Записываем в журнал
			 
	   shutdown(sockfd, SHUT_RDWR); // Корректно закрываем сокет
}
			 
/* Функция принимает дескриптор открытого файла и возвращает размер
 * ассоциированного с ним файла. При неудаче возвращает -1.
*/
int get_file_size(int fd) {
     struct stat stat_struct;
			 
      if(fstat(fd, &stat_struct) == -1)
	  return -1;
      return (int) stat_struct.st_size;
}
/* Функция записывает временную метку строки в переданный в нее
* дескриптор открытого файла.
*/
void timestamp(fd) {
      time_t now;
      struct tm *time_struct;
      int length;
      char time_buffer[40];

      time(&now); // Получаем число секунд с начала эры
      time_struct = localtime((const time_t *)&now); // Преобразуем в структуру tm
      length = strftime(time_buffer, 40, "%m/%d/%Y %H:%M:%S> ", time_struct);
      write(fd, time_buffer, length); // Записываем строку временной метки в журнал
}

Эта программа создает дубликат процесса, работающий в фоновом режиме, делает записи в файл журнала вместе с временными метками и корректно завершает работу после получения соответствующего сигнала. Дескриптор файла журнала и принимающий соединение сокет объявлены как глобальные переменные, чтобы функция handle_shutdown() могла корректно завершить их работу. Она задана как обработчик обратного вызова для сигналов завершения и прерывания, что обеспечивает аккуратное закрытие программы командой kill.

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

reader@hacking:~/booksrc $ gcc -o tinywebd tinywebd.c
reader@hacking:~/booksrc $ sudo chown root ./tinywebd
reader@hacking:~/booksrc $ sudo chmod u+s ./tinywebd
reader@hacking:~/booksrc $ ./tinywebd
Запуск демона tiny web.

reader@hacking:~/booksrc $ ./webserver_id 127.0.0.1
The web server for 127.0.0.1 is Tiny webserver
reader@hacking:~/booksrc $ ps ax | grep tinywebd
25058 ? Ss 0:00 ./tinywebd
25075 pts/3 R+ 0:00 grep tinywebd
reader@hacking:~/booksrc $ kill 25058
reader@hacking:~/booksrc $ ps ax | grep tinywebd
25121 pts/3 R+ 0:00 grep tinywebd
reader@hacking:~/booksrc $ cat /var/log/tinywebd.log
cat: /var/log/tinywebd.log: Permission denied
reader@hacking:~/booksrc $ sudo cat /var/log/tinywebd.log
07/22/2007 17:55:45> Запуск.
07/22/2007 17:57:00> От 127.0.0.1:38127 "HEAD / HTTP/1.0" 200 OK
07/22/2007 17:57:21> Завершение работы.
reader@hacking:~/booksrc $

Новая программа tinywebd обрабатывает HTTP-контент, как и исходная tinyweb, но ведет себя как системный демон, потому что у нее отсутствует управляющий терминал, а вывод происходит в файл журнала. Обе программы уязвимы к одному и тому же переполнению буфера — но с эксплуатации этой уязвимости все только начинается. Теперь, когда мы выбрали в качестве объекта атаки демона tinywebd, я покажу вам, как избежать обнаружения после того, как вы проникли на чужую машину.

» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок

Для Хаброжителей скидка 20% по купону — Hacking

Автор: ph_piter

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js