《黑客:利用艺术》一书。 第二版。”

图片 每个程序员本质上都是黑客。 毕竟,最初将黑客称为寻求一种熟练且非显而易见的解决方案。 了解编程可以帮助您发现漏洞,而漏洞检测技能可以帮助您创建程序,因此许多黑客会同时做这两项。 在编写精美程序的技术和发现弱点的技术中,都有有趣的非标准动作。

从哪里开始? 要使用缓冲区溢出来覆盖内存,访问远程服务器并拦截连接,您必须使用C和汇编器进行编程,使用Shell代码和处理器寄存器,熟悉网络交互作用和加密等等。

无论我们多么想相信奇迹,我们日常生活所依赖的软件和计算机网络都存在漏洞。

没有黑客的世界就是没有好奇心和创新解决方案的世界。 (约翰·埃里克森)

应对措施


有一只青蛙-一只可怕的李斯特拉兹(Phyllobates terribilis)。 她的皮肤腺体中含有最强的毒药。 只需触摸它即可导致致命的中毒。 这些青蛙以某些对毒具有免疫力的蛇为食,这说明了这种有力的补救措施。 逐渐地,青蛙的毒素变得越来越强。 由于这样的共同发展,可怕的攀爬者再也没有其他天敌了。 黑客正在发生类似的事情。 他们发明的技术早已众所周知,因此对策的出现是很自然的。 作为回应,黑客正在寻找规避和破坏防御机制的方法,这导致了新的防御技术的产生。

这种寻找措施和对策的周期非常有用。 病毒和蠕虫成为众多麻烦的原因,给企业带来巨大损失,但同时,它们迫使开发人员采取报复性措施来解决已出现的问题。 蠕虫使用低质量的软件漏洞进行自我复制。 这些年来,错误常常不为人所注意,并且相对无害的蠕虫(如CodeRed或Sasser)迫使开发人员对其进行修复。 这个过程可以与水痘相提并论,水痘可能会导致灾难性的后果,它更适合儿童时期而不是成年时期生病。 如果Internet蠕虫没有引起人们对安全漏洞的普遍关注,那么这些漏洞将继续受到攻击,其恶意目的远比简单的自我复制更为严重。 这样,蠕虫和病毒有助于长期的安全性。 但是,还有更活跃的方法:试图使攻击结果无效或使其完全不可能的技术。 “对策”的概念相当模糊,这些词可能意味着安全性的技术手段,一组规则,一个程序,或者只是一个细心的系统管理员。 有条件的对策分为两组:尝试检测攻击和尝试保护漏洞。

0x610攻击检测工具


从第一组的对策归结为试图及时注意到入侵并以某种​​方式做出反应。 识别过程可以以任何方式实现-从管理员读取系统日志到分析网络流量的程序。 有时,对入侵的反应归结为自动断开连接或关闭进程,有时,管理员必须仔细研究控制台的全部内容。

利用漏洞的已知方法对系统管理员而言,并不像他不知道的那样危险。 越早检测到入侵,就越早开始工作,并且越有可能对其进行本地化。 几个月来一直被忽略的入侵行为令人严重关切。

要识别入侵,您需要预见攻击者将要做什么。 这提供了有关确切需要监视的信息。 检测器会在日志,网络数据包甚至程序存储器中寻找熟悉的攻击模式。 当检测到入侵时,您可以剥夺黑客对计算机的访问权限,使用备份还原损坏的文件系统,并确定并修复安全漏洞。 利用当今可用的备份和恢复功能,减少攻击检测的对策非常有效。

对于攻击者而言,检测意味着对付他所做的一切。 当然,并非总是可能立即注意到攻击,因此在许多“抢走”情况下,检测的事实并不重要,但即使在这些情况下,最好也不要留下痕迹。 保密是黑客最有价值的素质之一。 通过利用漏洞来获得具有管理员权限的命令外壳的访问权限,可以在系统上执行任何操作,并且如果您设法避免检测,则没人会知道您的存在。 宽容与隐形的结合使黑客变得危险。 他们可以轻松地拦截网络上的密码和数据,为程序添加书签以进行后续的未授权访问,并攻击其他网络节点。 为了躲在阴影下,黑客应了解在特定情况下使用哪种识别方法。 如果知道他们到底在寻找什么,则可以避免某些利用漏洞的方式,也可以将您的操作伪装成有效的方法。 尚未引起人们注意的检测工具和技术的联合发展循环的驱动因素。

0x620系统守护程序


但是,使用实际示例可以最好地讨论针对黑客的对策及其变通办法。 现在,我们将考虑对接受传入连接的服务器程序的攻击。 在类似UNIX的操作系统上,系统守护程序适用于这些条件。 守护程序是在后台运行并以某种方式与控制终端分离的程序。 这个术语是麻省理工学院的黑客在1960年代创造的。 原型是一个神话生物,从物理学家詹姆斯·麦克斯韦(James Maxwell)的一项心理实验中分选分子。 麦克斯韦恶魔具有超自然的能力,可以轻松地执行复杂的任务,这违反了热力学第二定律。 同样,Linux系统守护程序不知疲倦地执行任务,例如授予对SSH的访问权限和维护系统日志。 恶魔名称通常以d结尾,这强调了其性质:例如sshd或syslogd。

只需进行一些编辑,即可将tinyweb.c从0x427部分变成与系统守护程序的逼真的相似。 该代码的新版本包含daemon()函数,该函数生成一个新的后台进程。 许多Linux系统守护进程都使用它。 这是目录中专门针对她的页面:

DAEMON(3)Linux程序员参考DAEMON(3)

姓名
守护程序-在后台运行
句法

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

说明

daemon()函数从控制终端断开程序并启动
她在后台作为系统守护程序。
使用非零参数nochdir,daemon()函数将更改当前工作目录。
在根(“ /”)上。
使用nullose时,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("  %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) {} //   } 

执行编译后的程序时,将注册信号处理程序,然后程序进入无限循环。 但是,尽管如此,传入的信号仍会中断其执行,并转向注册的处理程序。 以下是可以从控制终端激活的信号应用示例。 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), '\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); //       } 

该程序创建一个重复的进程,该进程在后台运行,与时间戳一起写入日志文件,并在接收到相应的信号后正确终止。 接受连接的日志文件描述符和套接字被声明为全局变量,以便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程序像原始的tinyweb一样处理HTTP内容,但是由于它没有控制终端并且输出到日志文件,因此其行为类似于系统守护程序。 这两个程序都容易受到同一缓冲区溢出的影响-但这仅仅是此漏洞的开始。 既然我们选择了tinywebd作为攻击的目标,我将向您展示如何在进入另一台计算机后如何避免被检测到。

»这本书的更多信息可以在出版商的网站上找到
» 目录
» 摘录

Habrozhitel的优惠券20%的折扣-Hacking

Source: https://habr.com/ru/post/zh-CN413989/


All Articles