|
我们来看看apache出错原因。
下面是源代码 src\main\http_request.c: 的处理请求的代码:
static void process_request_internal(request_rec *r) { int access_status;
/* Ignore embedded %2F's in path for proxy requests */ if (r->proxyreq == NOT_PROXY && r->parsed_uri.path) { /* 如果不是代理 */ access_status = ap_unescape_url(r->parsed_uri.path); /* 解码%,rfc要求 */ if (access_status) { ap_die(access_status, r); return; } }
ap_getparents(r->uri); /* OK --- shrinking transformations... */ /* 处理/../,出错,没考虑windows下面的\..\ */
if ((access_status = location_walk(r))) { ap_die(access_status, r); return; }
if ((access_status = ap_translate_name(r))) { decl_die(access_status, "translate", r); return; }
if (r->proxyreq == NOT_PROXY) { /* * We don't want TRACE to run through the normal handler set, we * handle it specially. */ if (r->method_number == M_TRACE) { if ((access_status = ap_send_http_trace(r))) ap_die(access_status, r); else ap_finalize_request_protocol(r); return; } }
if (r->proto_num > HTTP_VERSION(1,0) && ap_table_get(r->subprocess_env, "downgrade-1.0")) { r->proto_num = HTTP_VERSION(1,0); }
/* * NB: directory_walk() clears the per_dir_config, so we don't inherit * from location_walk() above */
if ((access_status = directory_walk(r))) { ap_die(access_status, r); return; }
if ((access_status = file_walk(r))) { ap_die(access_status, r); return; }
if ((access_status = location_walk(r))) { ap_die(access_status, r); return; }
if ((access_status = ap_header_parse(r))) { ap_die(access_status, r); return; }
switch (ap_satisfies(r)) { case SATISFY_ALL: case SATISFY_NOSPEC: if ((access_status = ap_check_access(r)) != 0) { decl_die(access_status, "check access", r); return; } if (ap_some_auth_required(r)) { if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) { decl_die(access_status, ap_auth_type(r) ? "check user. No user file?" : "perform authentication. AuthType not set!", r); return; } if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) { decl_die(access_status, ap_auth_type(r) ? "check access. No groups file?" : "perform authentication. AuthType not set!", r); return; } } break; case SATISFY_ANY: if (((access_status = ap_check_access(r)) != 0) || !ap_auth_type(r)) { if (!ap_some_auth_required(r)) { decl_die(access_status ? access_status : HTTP_INTERNAL_SERVER_ERROR, ap_auth_type(r) ? "check access" : "perform authentication. AuthType not set!", r); return; } if (((access_status = ap_check_user_id(r)) != 0) || !ap_auth_type(r)) { decl_die(access_status, ap_auth_type(r) ? "check user. No user file?" : "perform authentication. AuthType not set!", r); return; } if (((access_status = ap_check_auth(r)) != 0) || !ap_auth_type(r)) { decl_die(access_status, ap_auth_type(r) ? "check access. No groups file?" : "perform authentication. AuthType not set!", r); return; } } break; }
if (! (r->proxyreq != NOT_PROXY && r->parsed_uri.scheme != NULL && strcmp(r->parsed_uri.scheme, "http") == 0) ) { if ((access_status = ap_find_types(r)) != 0) { decl_die(access_status, "find types", r); return; } }
if ((access_status = ap_run_fixups(r)) != 0) { ap_die(access_status, r); return; }
if ((access_status = ap_invoke_handler(r)) != 0) { ap_die(access_status, r); return; }
/* Take care of little things that need to happen when we're done */ ap_finalize_request_protocol(r); }
下面是源代码 src\main\util.c 处理/../的代码:
API_EXPORT(void) ap_getparents(char *name) { int l, w;
/* Four paseses, as per RFC 1808 */ /* a) remove ./ path segments */
for (l = 0, w = 0; name[l] != '\0';) { if (name[l] == '.' && name[l + 1] == '/' && (l == 0 || name[l - 1] == '/')) l += 2; else name[w++] = name[l++]; }
/* b) remove trailing . path, segment */ if (w == 1 && name[0] == '.') w--; else if (w > 1 && name[w - 1] == '.' && name[w - 2] == '/') w--; name[w] = '\0';
/* c) remove all xx/../ segments. (including leading ../ and /../) */ l = 0;
while (name[l] != '\0') { if (name[l] == '.' && name[l + 1] == '.' && name[l + 2] == '/' && (l == 0 || name[l - 1] == '/')) { register int m = l + 3, n;
l = l - 2; if (l >= 0) { while (l >= 0 && name[l] != '/') l--; l++; } else l = 0; n = l; while ((name[n] = name[m])) (++n, ++m); } else ++l; }
/* d) remove trailing xx/.. segment. */ if (l == 2 && name[0] == '.' && name[1] == '.') name[0] = '\0'; else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.' && name[l - 3] == '/') { l = l - 4; if (l >= 0) { while (l >= 0 && name[l] != '/') l--; l++; } else l = 0; name[l] = '\0'; } }
大家看到了,显然没有考虑windows下面的“\..\”,看来编写这个函数的人不熟悉WINDOWS的特性,是个长期在*UNIX下面编程的人员。其实没考虑WINDOWS下面的“\..\”已经出过很多问题的了。那为什么直接telnet 用“\..\”不行呢?看来在解码之前把所有“\”替换成了“/”。显然这个转换是该在解码之后,估计上面没有检测“\..\”的原因就是认为先已经转换了,这个就与IIS的%c1%1c漏洞一样。照这样就一定得注意如果有什么编码、解码的,一定得重新考虑前面的一些检测,因为弄不好就能重新编码出不符合前面检测的东西来。
看这是源代码 src\main\http_protocol.c 中将URI中的“\”转化成“/”的处理模块:
/* parse_uri: break apart the uri * Side Effects: * - sets r->args to rest after '?' (or NULL if no '?') * - sets r->uri to request uri (without r->args part) * - sets r->hostname (if not set already) from request (scheme://host:port) */ CORE_EXPORT(void) ap_parse_uri(request_rec *r, const char *uri) { int status = HTTP_OK;
r->unparsed_uri = ap_pstrdup(r->pool, uri);
if (r->method_number == M_CONNECT) { status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri); } else { /* Simple syntax Errors in URLs are trapped by parse_uri_components(). */ status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri); }
if (ap_is_HTTP_SUCCESS(status)) { /* if it has a scheme we may need to do absoluteURI vhost stuff */ if (r->parsed_uri.scheme && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) { r->hostname = r->parsed_uri.hostname; } else if (r->method_number == M_CONNECT) { r->hostname = r->parsed_uri.hostname; } r->args = r->parsed_uri.query; r->uri = r->parsed_uri.path ? r->parsed_uri.path : ap_pstrdup(r->pool, "/"); #if defined(OS2) || defined(WIN32) /* Handle path translations for OS/2 and plug security hole. * This will prevent "http://www.wherever.com/..\..\/" from * returning a directory for the root drive. */ { char *x;
for (x = r->uri; (x = strchr(x, '\\')) != NULL; ) *x = '/'; /* \转换成/ */
} #endif /* OS2 || WIN32 */ } else { r->args = NULL; r->hostname = NULL; r->status = status; /* set error status */ r->uri = ap_pstrdup(r->pool, uri); } } |