The book "Hacking: the art of exploit. 2nd ed. "

image Each programmer is essentially a hacker. After all, originally hacking was called the search for a skillful and non-obvious solution. Understanding programming principles helps find vulnerabilities, and vulnerability detection skills help create programs, so many hackers do both. Interesting non-standard moves are in the techniques of writing elegant programs, and in techniques for finding weaknesses.

Where to begin? To overwrite memory using buffer overflow, access the remote server and intercept connections, you will need to program in C and assembler, use the shell code and processor registers, become familiar with network interactions and encryption, and much more.

No matter how much we want to believe in miracles, software and computer networks on which our daily lives depend, have vulnerabilities.

A world without hackers is a world without curiosity and innovative solutions. (John Erickson)

Countermeasures


There is such a frog - terrible listolaz (Phyllobates terribilis). Its skin glands contain the strongest poison. Simply touch it to get fatal poisoning. Such a powerful means of protection is explained by the fact that certain frogs feed on certain species of snakes that have developed resistance to poison. Gradually, the frog venom was becoming stronger and stronger. As a result of such a joint evolution, terrible leaflets did not have other natural enemies. Something similar happens with hackers. The techniques they invented have long been known, so the appearance of countermeasures is quite natural. In response, hackers are looking for ways to circumvent and destroy defense mechanisms, which leads to the creation of new defensive techniques.

This cycle of searching for measures and countermeasures is very useful. Viruses and worms cause many troubles and bring big losses to business, but at the same time they force developers to take retaliatory measures to solve the problems that have arisen. Worms reproduce themselves using vulnerabilities in low-quality software. Errors often go unnoticed over the years, and relatively harmless worms, such as CodeRed or Sasser, force developers to fix them. This process can be compared with chickenpox, which is better to get sick in childhood, and not in adulthood, when it can lead to disastrous consequences. If Internet worms didn’t draw everyone’s attention to security holes, these holes would remain open to attack with much more malicious targets than simple self-replication. Thus, worms and viruses contribute to enhancing security in the long term. But there are more active ways: technology, trying to nullify the results of the attack or make it completely impracticable. The concept of "countermeasures" is rather vague, by these words it can be understood as technical means of ensuring security, a set of rules, a program, or just an attentive system administrator. Countermeasures are conditionally divided into two groups: those trying to detect an attack and trying to protect a vulnerability.

0x610 Attack Detection Tools


Countermeasures from the first group are reduced to attempts to notice the invasion in time and somehow respond to it. The recognition process can be implemented in any way - from the administrator reading the system logs to programs that analyze network traffic. Sometimes the reaction to the invasion is reduced to an automatic connection failure or closing process, and sometimes the administrator has to carefully examine the entire contents of the console.

Known ways to exploit vulnerabilities are not as dangerous for the system administrator as they are not aware of. The sooner an intrusion can be detected, the sooner work with it will begin and the more likely it will be localized. Invasions that have been ignored for months are a major cause for concern.

To recognize the invasion, you need to anticipate what the attacker is going to do. This gives information about exactly what to follow. Detection tools look for familiar attack patterns in logs, network packets, and even program memory. When an intrusion is detected, it is possible to deprive the hacker of access to the machine, restore the damaged file system using a backup copy and identify and fix a security hole. With the backup and recovery capabilities that exist today, countermeasures that are limited to detecting attacks are quite effective.

For an attacker, detection means countering everything that he does. Of course, it is not always possible to immediately notice the attack, so there are a number of “grabbed and fled” scenarios in which the fact of detection is not important, but even in these cases it is better not to leave traces. Stealth - one of the most valuable qualities of hackers. Getting access to the shell with administrator rights by exploiting vulnerabilities makes it possible to do anything on the system, and if you can avoid detection, no one will know about your presence. It is the combination of permissiveness with invisibility that makes hackers dangerous. They can easily intercept passwords and data in the network, add bookmarks to the programs for subsequent unauthorized access and attack other network nodes. To stay in the shadows, the hacker should understand what recognition methods are used in a particular case. If you know exactly what they are looking for, you can avoid certain patterns of exploiting the vulnerability or disguise your actions as permissible. The driving factors for the cycle of the joint evolution of detection tools and techniques that allow you to remain unnoticed are ideas that have not yet occurred to the other side.

0x620 system daemons


However, countermeasures to hackers and their workarounds are best discussed with a practical example. Now we will consider an attack on a server program that accepts incoming connections. In UNIX-like operating systems, system daemons are suitable for these criteria. A daemon is a program that runs in the background and is separated from the controlling terminal in a certain way. The term was coined in the 1960s by hackers from the Massachusetts Institute of Technology. The prototype was the mythical creature, sorting molecules from the mental experiment of physicist James Maxwell. Maxwell's demon had a supernatural ability to easily perform complex tasks, while violating the second law of thermodynamics. Similarly, system daemons in Linux perform tasks such as granting access to SSH and keeping system logs tirelessly. Daemon names usually end in d, which underlines their nature: for example, sshd or syslogd.

Minor editing will turn tinyweb.c from section 0x427 into a realistic similarity to the system daemon. The new version of the code contains the daemon () function, which generates a new background process. It is used by many Linux system daemon processes. Here is a page dedicated to her from the directory:

DAEMON (3) Linux Programmer's Reference DAEMON (3)

NAME
daemon - run in the background
SYNTAX

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

DESCRIPTION

The daemon () function disconnects the program from the controlling terminal and starts
its in the background as a system daemon.
If the nochdir argument is non-zero, the daemon () function changes the current working directory
on root ("/").
If the noclose argument is non-zero, the daemon () function redirects the threads
standard input, standard output, and errors in / dev / null.
RETURN VALUE

(This function spawns a copy of the process, and if successful, the fork () function
parent executes _exit (0) so that further errors are visible only
child process.) If successful, returns zero. On error, function
daemon () returns -1 and assigns an error number to global errno
from the fork (2) and setsid (2) function library.

System daemons do not have a controlling terminal, so the tinywebd code of the new daemon will output to the logbook. Daemons are usually controlled by signals. The new version of the tinyweb program should be able to receive a completion signal in order to complete the work correctly.

0x621 Signal Overview


Signals provide inter-process communication in UNIX. After the process receives a signal, the operating system interrupts its execution to call the signal handler. Each signal has its own number and its handler. For example, when you press the Ctrl + C key combination, an interrupt signal is sent, the handler of which completes the program open on the control terminal, even if it has entered an infinite loop.

You can create your own signal handlers and register them using the signal () function. Let's look at the code in which several handlers are registered for some signals, and there is an infinite loop in the main part.

 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("  %d\t", 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) {} //   } 

When a compiled program is executed, signal handlers are registered, and then the program enters an infinite loop. But despite this, incoming signals will interrupt its execution and contact registered handlers. Below are examples of the use of signals that can be activated from the controlling terminal. After the signal_handler () function completes, control returns to the interrupted loop, while the sigint_handler () function terminates the program.

 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 $ 

The kill command allows the process to send a whole set of signals. By default, it sends a complete signal (SIGTERM). Adding the -l option to it displays a list of all possible signals. Let's see how the signal_example program, running on another terminal, sends SIGUSR1 and SIGUSR2 signals.

 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 $ 

At the end, the kill -9 command sends a SIGKILL signal. The signal handler cannot be changed, so the kill -9 command is always used to kill processes. The signal_example program launched on another terminal shows that the signals were intercepted and the process was destroyed.

 reader@hacking:~/booksrc $ ./signal_example   10       SIGUSR1   12       SIGUSR2 Killed reader@hacking:~/booksrc $ 

The signals themselves are very simple, but the interaction between the processes can quickly turn into a complex web of dependencies. Fortunately, in our tinyweb daemon, signals are used only for proper shutdown, and everything is implemented very simply.

0x622 demon tinyweb


The new version of the tinywebd program is a system daemon running in the background without a controlling terminal. The output data is recorded in the logbook with time stamps, and the program itself waits for a SIGTERM signal in order to complete the work correctly.

The changes made to the original are not very significant, but they allow a more realistic study of the process of exploiting the vulnerability. New code snippets in bold.

 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), '\0', 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 Found\n");           send_string(sockfd, "HTTP/1.0 404 NOT FOUND\r\n");                send_string(sockfd, "Server: Tiny webserver\r\n\r\n");                send_string(sockfd, "<html><head><title>404 Not Found</title>                                     </head>");            send_string(sockfd, "<body><h1>URL not found</h1></body></html>                                     \r\n"); } else {       //      strcat(log_buffer, " 200 OK\n");             send_string(sockfd, "HTTP/1.0 200 OK\r\n");           send_string(sockfd, "Server: Tiny webserver\r\n\r\n");            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); //       } 

This program creates a duplicate process that runs in the background, makes entries in the log file along with timestamps, and correctly exits after receiving the appropriate signal. The log file handle and the socket accepting connection are declared as global variables so that the handle_shutdown () function can correctly terminate their work. It is defined as a callback handler for termination and interrupt signals, which ensures that the program is carefully closed using the kill command.

Here is the result of compiling, executing and terminating the program. Notice the timestamps in the log and the shutdown message that appeared after the program received the corresponding signal and called the handle_shutdown () function.

 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 $ 

The new tinywebd program handles HTTP content, like the original tinyweb, but behaves like a system daemon, because it has no control terminal, and the output is in the log file. Both programs are vulnerable to the same buffer overflow - but with the exploitation of this vulnerability, everything is just beginning. Now that we’ve chosen the tinywebd daemon as the target, I’ll show you how to avoid detection after you have entered a foreign machine.

»More information about the book can be found on the publisher site.
» Table of Contents
» Excerpt

For Habrozhiteley 20% discount coupon - Hacking

Source: https://habr.com/ru/post/413989/


All Articles