Boa Web Server 缺陷报告及其修正方法
综述
Boa 作为一种轻巧实用的 WEB 服务器广泛应用于嵌入式设备上,
但 Boa 对实现动态网页的 CGI 的支持上仍存在一些缺陷,
本文描述了 Boa 对 CGI 的 Status/Location 的支持的缺陷及其修正方法.
版本: 所有版本 (0.94.13)
缺陷: BOA 解析 CGI 应答头时不能完整处理 Status 和 Location
缺陷描述:
CGI/1.1 标准规定, CGI 脚本可以通过 Status 设置 HTTP 应答状态(如, Status: 500 Internal Error) 和
Location 进行地址重定向 (如, Location: www.xxx.com), 而不管它们在应答头中的位置.
Boa 支持 Stauts 和 Location 两种应答头, 但它的实现仅能正确处理 Stauts 和 Location 在应答第一行的
CGI 应答. 这将给 CGI 程序的移植带来很多不便, 进而影响 Boa 作为Web Server 的功能的发挥.
影响功能:
ASP/PHP/JSP/Perl/... 等的 header, redirect, ... 等都会应用到 Stauts/Location 进行设置应答状态和
地址重定向. Boa 的该实现将影响 CGI 脚本正常功能的使用.
缺陷功能对比(对Status/Location的支持程序):
Apache 1.3.x/2.x IIS 4.x/5.x/6.X Boa 0.9x thttpd mini-httpd
完全支持 完全支持 * 部分支持 完全支持 完全支持
缺陷分析
| 缺陷分析 CGI 应用程序进行应答时, 可以 HTTP 头进行有限的控制. 如,设置客户端不缓存页面可用下面的 C 脚本, HTTP/1.0: printf("Pragma: no-cache\n"); 或 HTTP/1.1: printf("Cache-Control: no-cache; no-store\n"); 如果, 同时还需要告诉浏览器进行设置 Cookie 和控制相应状态(200 OK) 或地址重定向, 那么就必须输出多行 http 头控制语句, CGI 支持两个解析头 "Status: " 和 "Loction: ", 即协议规定, Web 服务器支持解析头时能使用 "Status: " 进行应答状态控制, 使用 "Location: " 进行地址重定向, 并为应答添加状态头 "HTTP/1.0 302 Moved Temporarily\n" 或 "HTTP/1.1 302 Found\n". 而不管它们在 CGI 应答头的什么位置. 分析 Boa Source Code: cgi_header.c Line 82-136 容易发现, Boa 只解析 CGI 应答的第一行, 是否为 "Status: ", "Location: ", 如下所示 |
- 23
- 24 int process_cgi_header(request * req)
- 25 {
- 26 char *buf;
- 27 char *c;
- 28
- 29 if (req->cgi_status != CGI_DONE)
- 30 req->cgi_status = CGI_BUFFER;
- 31
- 32 buf = req->header_line;
- 33
- 34 c = strstr(buf, "\n\r\n");
- 35 if (c == NULL) {
- 36 c = strstr(buf, "\n\n");
- 37 if (c == NULL) {
- 38 log_error_time();
- 39 fputs("cgi_header: unable to find LFLF\n", stderr);
- 40 #ifdef FASCIST_LOGGING
- 41 log_error_time();
- 42 fprintf(stderr, "\"%s\"\n", buf);
- 43 #endif
- 44 send_r_bad_gateway(req);
- 45 return 0;
- 46 }
- 47 }
- 48 if (req->simple) {
- 49 if (*(c + 1) == '\r')
- 50 req->header_line = c + 2;
- 51 else
- 52 req->header_line = c + 1;
- 53 return 1;
- 54 }
- 55 if (!strncasecmp(buf, "Status: ", 8)) {
- 56 req->header_line--;
- 57 memcpy(req->header_line, "HTTP/1.0 ", 9);
- 58 } else if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
- 59 #ifdef FASCIST_LOGGING
- 60
- 61 log_error_time();
- 62 fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
- 63 __FILE__, __LINE__, buf + 10);
- 64 #endif
- 65
- 66
- 67 if (buf[10] == '/') { /* virtual path */
- 68 log_error_time();
- 69 fprintf(stderr,
- 70 "server does not support internal redirection: " \
- 71 "\"%s\"\n", buf + 10);
- 72 send_r_bad_request(req);
- 73
- 74 /*
- 75 * We (I, Jon) have declined to support absolute-path parsing
- 76 * because I see it as a major security hole.
- 77 * Location: /etc/passwd or Location: /etc/shadow is not funny.
- 78 *
- 79 * Also, the below code is borked.
- 80 * request_uri could contain /cgi-bin/bob/extra_path
- 81 */
- 82
- 83 /*
- 84 strcpy(req->request_uri, buf + 10);
- 85 return internal_redirect(req);
- 86 */
- 87 } else { /* URL */
- 88 char *c2;
- 89 c2 = strchr(buf + 10, '\n');
- 90 /* c2 cannot ever equal NULL here because we already have found one */
- 91
- 92 --c2;
- 93 while (*c2 == '\r')
- 94 --c2;
- 95 ++c2;
- 96 /* c2 now points to a '\r' or the '\n' */
- 97 *c2++ = '\0'; /* end header */
- 98
- 99 /* first next header, or is at req->header_end */
- 100 while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
- 101 ++c2;
- 102 if (c2 == req->header_end)
- 103 send_r_moved_temp(req, buf + 10, "");
- 104 else
- 105 send_r_moved_temp(req, buf + 10, c2);
- 106 }
- 107 req->status = DONE;
- 108 return 1;
- 109 } else { /* not location and not status */
- 110 char *dest;
- 111 int howmuch;
- 112 send_r_request_ok(req); /* does not terminate */
- 113 /* got to do special things because
- 114 a) we have a single buffer divided into 2 pieces
- 115 b) we need to merge those pieces
- 116 Easiest way is to memmove the cgi data backward until
- 117 it touches the buffered data, then reset the cgi data pointers
- 118 */
- 119 dest = req->buffer + req->buffer_end;
- 120 if (req->method == M_HEAD) {
- 121 if (*(c + 1) == '\r')
- 122 req->header_end = c + 2;
- 123 else
- 124 req->header_end = c + 1;
- 125 req->cgi_status = CGI_DONE;
- 126 }
- 127 howmuch = req->header_end - req->header_line;
- 128
- 129 if (dest + howmuch > req->buffer + BUFFER_SIZE) {
- 130 /* big problem */
- 131 log_error_time();
- 130 fprintf(stderr, "Too much data to move! Aborting! %s %d\n",
- 131 __FILE__, __LINE__);
- 132 /* reset buffer pointers because we already called
- 133 send_r_request_ok... */
- 134 req->buffer_start = req->buffer_end = 0;
- 135 send_r_error(req);
- 136 return 0;
- 137 }
- 138 memmove(dest, req->header_line, howmuch);
- 139 req->buffer_end += howmuch;
- 140 req->header_line = req->buffer + req->buffer_end;
- 141 req->header_end = req->header_line;
- 142 req_flush(req);
- 143 if (req->method == M_HEAD)
- 144 return 0;
- 145 }
- 146 return 1;
- 147 }
- 148
- 149
修正方法
CGI 应答头包括多行, 我们必须对其进行逐行分析, 并作出正确的应答.
下面是修改好的源程序, 即将原来的 82-136 (即相当下文#else, #endif内部分) 替换成如下代码:
- #if 1
- while(1) {
- int len;
- char * pnext = NULL;
- char * ptmp = NULL;
- /* not find HTTP header tailer */
- if (NULL == (pnext=strchr(buf, '\n'))) /* has no '\n' */
- break;
- /* the length of this line,
- * include '\n'
- */
- len = pnext - buf + 1;
- if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
- /* not the first one
- * exchange this line to the first line
- */
- if (buf != req->header_line)
- {
- if (NULL == (ptmp=(char *)malloc(len)))
- {
- log_error_time();
- perror("malloc");
- send_r_error(req);
- return 0;
- }
- /* move Status: to line header */
- memcpy(ptmp, buf, len);
- memmove(req->header_line+len, req->header_line, buf-req->header_line);
- memcpy(req->header_line, ptmp, len);
- free(ptmp);
- }
- /* force pointer header */
- buf = req->header_line;
- #ifdef FASCIST_LOGGING
- log_error_time();
- fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
- __FILE__, __LINE__, buf + 10);
- #endif
- if (buf[10] == '/') { /* virtual path */
- log_error_time();
- fprintf(stderr,
- "server does not support internal redirection: " \
- "\"%s\"\n", buf + 10);
- send_r_bad_request(req);
- /*
- * We (I, Jon) have declined to support absolute-path parsing
- * because I see it as a major security hole.
- * Location: /etc/passwd or Location: /etc/shadow is not funny.
- *
- * Also, the below code is borked.
- * request_uri could contain /cgi-bin/bob/extra_path
- */
- /*
- strcpy(req->request_uri, buf + 10);
- return internal_redirect(req);
- */
- } else { /* URL */
- char *c2;
- c2 = strchr(buf + 10, '\n');
- /* c2 cannot ever equal NULL here because we already have found one */
- --c2;
- while (*c2 == '\r')
- --c2;
- ++c2;
- /* c2 now points to a '\r' or the '\n' */
- *c2++ = '\0'; /* end header */
- /* first next header, or is at req->header_end */
- while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
- ++c2;
- if (c2 == req->header_end)
- send_r_moved_temp(req, buf + 10, "");
- else
- send_r_moved_temp(req, buf + 10, c2);
- }
- req->status = DONE;
- return 1;
- } else if (!strncasecmp(buf, "Status: ", 8)) {
- /* not the first one
- * exchange this line to the first line
- */
- if (buf != req->header_line)
- {
- if (NULL == (ptmp=(char *)malloc(len)))
- {
- log_error_time();
- perror("malloc");
- send_r_error(req);
- return 0;
- }
- /* move Status: to line header */
- memcpy(ptmp, buf, len);
- memmove(req->header_line+len, req->header_line, buf-req->header_line);
- memcpy(req->header_line, ptmp, len);
- free(ptmp);
- }
- req->header_line--;
- memcpy(req->header_line, "HTTP/1.0 ", 9);
- return 1;
- }
- /* pointer to next line */
- buf = pnext + 1;
- /* reach the end of HTTP header */
- if ('\0' == buf[0] || '\n' == buf[0] || '\r' == buf[0])
- break;
- }
- if (1) { /* always done */
- #else
- if (!strncasecmp(buf, "Status: ", 8)) {
- req->header_line--;
- memcpy(req->header_line, "HTTP/1.0 ", 9);
- } else if (!strncasecmp(buf, "Location: ", 10)) { /* got a location header */
- #ifdef FASCIST_LOGGING
- log_error_time();
- fprintf(stderr, "%s:%d - found Location header \"%s\"\n",
- __FILE__, __LINE__, buf + 10);
- #endif
- if (buf[10] == '/') { /* virtual path */
- log_error_time();
- fprintf(stderr,
- "server does not support internal redirection: " \
- "\"%s\"\n", buf + 10);
- send_r_bad_request(req);
- /*
- * We (I, Jon) have declined to support absolute-path parsing
- * because I see it as a major security hole.
- * Location: /etc/passwd or Location: /etc/shadow is not funny.
- *
- * Also, the below code is borked.
- * request_uri could contain /cgi-bin/bob/extra_path
- */
- /*
- strcpy(req->request_uri, buf + 10);
- return internal_redirect(req);
- */
- } else { /* URL */
- char *c2;
- c2 = strchr(buf + 10, '\n');
- /* c2 cannot ever equal NULL here because we already have found one */
- --c2;
- while (*c2 == '\r')
- --c2;
- ++c2;
- /* c2 now points to a '\r' or the '\n' */
- *c2++ = '\0'; /* end header */
- /* first next header, or is at req->header_end */
- while ((*c2 == '\n' || *c2 == '\r') && c2 < req->header_end)
- ++c2;
- if (c2 == req->header_end)
- send_r_moved_temp(req, buf + 10, "");
- else
- send_r_moved_temp(req, buf + 10, c2);
- }
- req->status = DONE;
- return 1;
- } else { /* not location and not status */
- #endif
转载连接:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=824840
Boa Web Server 缺陷报告及其修正方法的更多相关文章
- 简易web server之python实现
网络编程一项基本功是socket编程,包括TCP socket,UDP socket的客户端.服务器端编程. 应用层的各路协议如http,smtp,telnet,ftp等都依赖于传输层的TCP或者UD ...
- Tomcat建立多个应用(Web Server),多个主机,多个站点的方法
https://blog.csdn.net/chungle2011/article/details/52317433 http://piperzero.iteye.com/blog/1475773 转 ...
- 咏南中间件当作WEB SERVER使用方法
咏南中间件当作WEB SERVER使用方法 1)开启咏南中间件 2)浏览器打开http://localhost:5566/web?page=echo.html
- [Windows Server 2008] IIS配置伪静态方法(Web.config模式的IIS rewrite)
★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频.★ 本节我们将带领大家:安装伪静态(w ...
- VisualSVN Server的配置和使用方法(转)
1.为什么要用VisualSVN Server,而不用Subversion? 回答: 因为如果直接使用Subversion,那么在Windows 系统上,要想让它随系统启动,就要封装SVN Serve ...
- Web Server PROPFIND Method internal IP Discosure
Title:Web Server PROPFIND Method internal IP Discosure --2012-11-09 09:47 Nessus扫描出来一个安全缺陷,Web Serv ...
- The Web server is configured to not list the contents of this directory.
部署一个ASP.NET MVC网站至一个全新的服务器Windows Server 2008 R2, 数据为MS SQL Server 2014 64bit Expression版本. 运行时,它第一次 ...
- 【转】推荐介绍几款小巧的Web Server程序
原博地址:http://blog.csdn.net/heiyeshuwu/article/details/1753900 偶然看到几个小巧有趣的Web Server程序,觉得有必要拿来分享一下,让大家 ...
- VisualSVN Server的配置和使用方法 图文
转载 http://www.jb51.net/article/17365.htm VisualSVN Server是免费的,而VisualSVN是收费的.VisualSVN是SVN的客户端,和Visu ...
随机推荐
- JavaScript深入理解-PWA渐进式应用
WPA-渐进式 web 应用 PWA 是什么 渐进式 Web 应用,提升 web app 浏览体验. manifest 应用程序清单 基本介绍: web app manifest是 PWA 技术集合中 ...
- 【CTF】XCTF Misc 心仪的公司 & 就在其中 writeup
前言 这两题都是Misc中数据包的题目,一直觉得对数据包比较陌生,不知道怎么处理. 这里放两道题的wp,第一题strings命令秒杀觉得非常优秀,另外一题有涉及RSA加密与解密(本文不具体讨论RSA非 ...
- FastAPI项目实战:"异步"接口测试"平台"
apiAutoTestWeb 是什么? apiAutoTest接口自动化测试工具的可视化版本,将原本对用例的操作转移到Web页面之上 用什么实现? 接口自动化测试:大体上测试逻辑将采用apiAutoT ...
- 【运维】Shell -- 快速上手Shell脚本
1.Shell概述 shell脚本是利用shell的功能所写的一个[程序(program)].这个程序是使用纯文本文件,将一些shell的语法与命令(含外部命令)写在里面,搭配正则表达式.管道命令与数 ...
- Mybatis3源码笔记(六)SqlSession执行过程
前几篇大致分析了初始化的过程,今天打算走一个SqlSession具体执行过程. @Test void shouldSelectAllAuthors() { try (SqlSession sessio ...
- The Blocks Problem UVA - 101
Many areas of Computer Science use simple, abstract domains for both analytical and empirical stud ...
- docker搭建简单mysql主从
关于MySQL主从模式,如果我们直接在本机上搭建的话,是没法搭建的,只能借助于虚拟机,但有的时候我们又需要搭建一个主从集群,以便于进行一些功能性的测试.这个时候我们就可以尝试使用docker,借助于d ...
- json 标准库
1. 序列化的简单概念 2. json 标准库 2.1 json.dumps() 2.2 json.loads() 2.3 json.dump() 2.4 json.load() 1. 序列化的简单概 ...
- win 远程桌面 ubuntu
开始 起因 因为工作需求经常要远程 局域网内的 Ubuntu设备 之前用的一直是 Teamviver 但是最近不知怎么了,Teamviver发病 打不开了,也懒得折腾了! 简单的记录一下 能用的几种连 ...
- 从苏宁电器到卡巴斯基第15篇:我在苏宁电器当营业员 VII
我们苹果的倒班制度 当年我在苏宁的时候,实行的是单休制度,而且只能选择在周一到周五其中的某一天,因为周六周日顾客比较多,是不允许休息的.尽管是单休,但并不表示我们在上班的时候每天都要完完整整地上八小时 ...