📄 ratproxy.c
字号:
if (!strcasecmp(BEST_MIME,"text/html") || rp_strcasestr(BEST_MIME,"script") || !strcasecmp(BEST_MIME,"application/json") || !strcasecmp(BEST_MIME,"text/css") || !strcasecmp(BEST_MIME,"application/xhtml+xml")) SHOW_MSG(2,"Potential mixed content",0,m); else SHOW_MSG(0,"Potential mixed content",0,m); } /* If instructed to do so, adjust XSRF "safety" rating based on packet replay now. */ if (try_attacks) try_replay_xsrf(req,res); /*********************** * HEADER BASED CHECKS * ***********************/ if (res->code < 200 || res->code >= 400) { switch (NOECHO(m = get_modifiers(req,res))) { /* No big deal, but warrants an investigation; more important if the content is user-specific. */ case 0: case MOD_PRED: SHOW_MSG(0,"HTTP errors",0,m); break; case MOD_AUTH: case MOD_PRED | MOD_AUTH: SHOW_MSG(1,"HTTP errors",0,m); break; } } /* Detect 302 with Location: that contains req->query or req->payload, and lacks XSRF token? */ if (res->location && (req->query || req->payload) && !req->xsrf_safe) { _u8* hname = strdup(res->location), *y; if (!hname) fatal("out of memory"); if (!strncasecmp(hname,"http://",7)) hname += 7; else if (!strncasecmp(hname,"https://",8)) hname += 8; y = hname; while (isalnum(*y) || *y == '-' || *y == '.') y++; *y = 0; if (hname[0] && ((req->query && rp_strcasestr(req->query,hname)) || (req->payload && rp_strcasestr(req->payload,hname)))) { SHOW_MSG(3,"HTTP redirector",0,1); } } /* If not a HTTP redirector, examine for HTML redirection anyway */ if (!res->location && (req->query || req->payload) && res->payload && !req->xsrf_safe) { _u8* mref=rp_strcasestr(res->payload,"HTTP-EQUIV=\"Refresh\""); _u8* hname = mref ? rp_strcasestr(mref + 20, ";URL=") : 0; if (hname) { _u8* mrefend = strchr(mref + 20,'>'), *y; if (mrefend && hname < mrefend) { hname = strdup(hname + 5); if (!hname) fatal("out of memory"); if (!strncasecmp(hname,"http://",7)) hname += 7; else if (!strncasecmp(hname,"https://",8)) hname += 8; y = hname; while (isalnum(*y) || *y == '-' || *y == '.') y++; *y = 0; if (hname[0] && ((req->query && rp_strcasestr(req->query,hname)) || (req->payload && rp_strcasestr(req->payload,hname)))) { SHOW_MSG(3,"HTML META redirector",0,1); } } } } if (req->multipart) { m = get_modifiers(req,res); SHOW_MSG(0,"File upload forms",0,m); } if (all_post && req->payload && strcasecmp(req->method,"GET")) { m = get_modifiers(req,res); SHOW_MSG(0,"All POST requests",0,m); } if (unique_cookies(&req->cookies,&res->cookies) && !req->xsrf_safe && (req->payload || req->query)) { m = get_modifiers(req,res); SHOW_MSG(2,"Cookie issuer with no XSRF protection",0,m); /* TODO: Maybe check if query data copied over to cookies. */ } if (all_cookie && unique_cookies(&req->cookies,&res->cookies)) { m = get_modifiers(req,res); SHOW_MSG(0,"All cookie setting URLs",0,m); } /* If there's a request that requires authentication and accept parameters, it should probably employ anti-XSRF protection of some sort. */ if (!req->xsrf_safe && (req->payload || req->query)) { m = get_modifiers(req,res); if (m & MOD_AUTH) { if (!strcasecmp(req->method,"GET")) { if (get_xsrf) SHOW_MSG(0,"GET query with no XSRF protection",0,m); } else SHOW_MSG(3,"POST query with no XSRF protection",0,m); } else { /* POST requests that do not require authentication are interesting, though not necessarily very troubling. */ if (strcasecmp(req->method,"GET")) SHOW_MSG(1,"POST query with no XSRF protection",0,m); } } if (res->has_multiple) { /* Duplicate Content-Type or Content-Disposition headers are a sure way to get into trouble. */ switch (NOECHO(m = get_modifiers(req,res))) { case 0: SHOW_MSG(1,"Ambiguous HTTP content headers",0,m); break; case MOD_PRED: case MOD_AUTH: SHOW_MSG(2,"Ambiguous HTTP content headers",0,m); break; case MOD_PRED | MOD_AUTH: SHOW_MSG(3,"Ambiguous HTTP content headers",0,m); break; } } /* Unusual, but hey, let's report it because we can. */ if (res->has_badclen) SHOW_MSG(3,"Misstated Content-Length",0,0); /* POST requests that pass auth tokens between domains. If coming from an excluded domain, this is more important. */ if (req->ref_host && strcmp(req->method,"GET") && strcasecmp(req->host,req->ref_host)) { if (!host_ok(req->ref_host)) SHOW_REF_MSG(2,"Cross-domain POST requests",0); else SHOW_REF_MSG(1,"Cross-domain POST requests",0); } /* Report caching headers issues (but only once!) */ if (!req->from_ssl && unique_cookies(&req->cookies,&res->cookies) && is_public(req,res)) { switch (NOECHO(m = get_modifiers(req,res))) { case 0: case MOD_AUTH: SHOW_MSG(1,"Bad caching headers","cacheable SetCookie",m); break; case MOD_PRED: case MOD_AUTH | MOD_PRED: SHOW_MSG(3,"Bad caching headers","cacheable SetCookie",m); break; } } else if (!req->from_ssl && is_public(req,res) == 2 && res->payload_len && res->code < 300) { m = get_modifiers(req,res); if (NOECHO(m) == (MOD_AUTH | MOD_PRED)) SHOW_MSG(3,"Bad caching headers","Expires/Date/Cache-Control mismatch",m); else if (NOECHO(m) == MOD_AUTH) SHOW_MSG(2,"Bad caching headers","Expires/Date/Cache-Control mismatch",m); } /************************ * PAYLOAD BASED CHECKS * ************************/ /* If the document is empty, bail out (everything below relies on non-NULL res->payload). */ if (!res->payload_len) goto skip_tests; if (res->is_text && (!res->charset || res->bad_cset)) { /* Missing charsets and typos lead to UTF-7 cross-site scripting. */ if (strcasecmp(BEST_MIME,"text/css")) { /* Cases where content is echoed back are higher risk, but we care about stored attacks too. */ switch (NOECHO(m = get_modifiers(req,res))) { case 0: SHOW_MSG(ECHO(m) ? 3 : 1,"Bad or no charset declared for renderable file",0,m); break; case MOD_PRED: case MOD_AUTH: SHOW_MSG(ECHO(m) ? 3 : 1,"Bad or no charset declared for renderable file",0,m); break; case MOD_PRED | MOD_AUTH: SHOW_MSG(ECHO(m) ? 3 : 2,"Bad or no charset declared for renderable file",0,m); break; } } } if (res->mime_type && !strcasecmp(res->mime_type,"text/plain")) { /* Modern interactive websites have very few reasons to serve text/plain documents, and if these documents are user-controlled, content sniffing can lead to XSS. */ /* Let's just ignore text/css; the next check will catch it anyway, and it's nearly guaranteed to be harmless. */ if (strcasecmp(BEST_MIME,"text/css")) switch (NOECHO(m = get_modifiers(req,res))) { case 0: SHOW_MSG(1,"MIME type set to text/plain",0,m); break; case MOD_AUTH: case MOD_PRED: SHOW_MSG(ECHO(m) ? 2 : 1,"MIME type set to text/plain",0,m); break; case MOD_PRED | MOD_AUTH: SHOW_MSG(ECHO(m) ? 3 : 2,"MIME type set to text/plain",0,m); break; } } if (!res->mime_type) { /* Having no MIME type almost always warrants scrutiny, as content sniffing runs rampant and may have a browser-specific outcome. */ switch (NOECHO(m = get_modifiers(req,res))) { case 0: SHOW_MSG(1,"MIME type missing",0,m); break; case MOD_PRED: case MOD_AUTH: SHOW_MSG(ECHO(m) ? 2 : 1,"MIME type missing",0,m); break; case MOD_PRED | MOD_AUTH: SHOW_MSG(ECHO(m) ? 3 : 2,"MIME type missing",0,m); break; } } /* Let's be annoying here for initial betas, why not?. */ if (res->payload_len > 10 && res->mime_type && !res->sniffed_mime) debug(">>> Failed to detect MIME type '%s' (%s:%u/%s?%s), tell lcamtuf@google.com <<<\n", S(res->mime_type,0), S(req->host,0), req->port, S(req->path,0), req->query ? S(req->query,0) : (_u8*)""); if (res->sniffed_mime && res->mime_type && strcasecmp(res->mime_type, res->sniffed_mime)) { if (res->is_text) { /* MIME mismatch on text formats that are rendered by the browser is usually a major problem and may lead to XSS. */ /* Do not be too picky about HTML - XHTML mismatches, though... */ if (res->mime_type && res->sniffed_mime && !strcasecmp(res->mime_type,"text/html") && !strcasecmp(res->sniffed_mime,"application/xhtml+xml")) goto ignore_mime_mismatch; if (strcasecmp(BEST_MIME,"text/css")) switch (NOECHO(m = get_modifiers(req,res))) { case 0: SHOW_MSG(1,"MIME type mismatch on renderable file",0,m); break; case MOD_AUTH: case MOD_PRED: SHOW_MSG(ECHO(m) ? 2 : 1,"MIME type mismatch on renderable file",0,m); break; case MOD_PRED | MOD_AUTH: SHOW_MSG(ECHO(m) ? 3 : 2,"MIME type mismatch on renderable file",0,m); break; } } else if (!strncasecmp(BEST_MIME,"image/",6)) { /* Subtle mismatches with images may have disastrous effects as content sniffing inevitably kicks in and may lead to HTML parsing in EXIF or comment data.*/ switch (NOECHO(m = get_modifiers(req,res))) { case 0: SHOW_MSG(1,"MIME type mismatch on image file",0,m); break; case MOD_AUTH: case MOD_PRED: SHOW_MSG(2,"MIME type mismatch on image file",0,m); break; case MOD_PRED | MOD_AUTH: SHOW_MSG(3,"MIME type mismatch on image file",0,m); break; } } else { if (!strcasecmp(res->mime_type,"application/octet-stream")) { /* Defaulting to application/octet-stream may trigger content sniffing. */ switch (NOECHO(m = get_modifiers(req,res))) { case 0: SHOW_MSG(1,"Generic MIME type used",0,m); break; case MOD_AUTH: case MOD_PRED: SHOW_MSG(ECHO(m) ? 2 : 1,"Generic MIME type used",0,m); break; case MOD_PRED | MOD_AUTH: SHOW_MSG(ECHO(m) ? 3 : 2,"Generic MIME type used",0,m); break; } } else { /* Other MIME type mismatches still warrant attention, as this might be a result of a typo or the like. */ switch (NOECHO(m = get_modifiers(req,res))) { case 0: case MOD_AUTH: case MOD_PRED: SHOW_MSG(1,"MIME type mismatch on binary file",0,m); break; case MOD_PRED | MOD_AUTH: SHOW_MSG(2,"MIME type mismatch on binary file",0,m); break; } } } }ignore_mime_mismatch: if ((rp_strcasestr(BEST_MIME,"script") || !strcasecmp(BEST_MIME,"application/json"))) { /* JSON is almost always worth inspecting - doubly so if not secured against XSRF. */ switch (NOECHO(m = get_modifiers(req,res))) { case 0: case MOD_PRED: break; case MOD_AUTH: SHOW_MSG(standalone_script(res->payload) ? 0 : 1, "Dynamic Javascript for direct inclusion",0,m); break; case MOD_PRED | MOD_AUTH: /* TODO: Move this to a proper Javascript analyzer instead. */ if (standalone_script(res->payload)) { SHOW_MSG(0,"Dynamic Javascript for direct inclusion",0,m); } else if (is_json_safe(res->payload)) { SHOW_MSG(ECHO(m) ? 1 : 0,"Dynamic Javascript for direct inclusion",0,m); } else { SHOW_MSG(ECHO(m) ? 3 : 2,"Dynamic Javascript for direct inclusion",0,m); } break; } } if (!strcasecmp(BEST_MIME,"image/png") && !res->is_attach) { switch (NOECHO(m = get_modifiers(req,res))) { case 0: case MOD_PRED: if (check_png) SHOW_MSG(2,"Inline PNG image",0,m); break; case MOD_AUTH: SHOW_MSG(2,"Inline PNG image",0,m); break; case MOD_PRED | MOD_AUTH: SHOW_MSG(3,"Inline PNG image",0,m); break; } } /* Echoed markup in a query is bad. */ for (i=0;i<req->p.c;i++) if (!req->p.fn[i][0] && strchr(req->p.v2[i],'<') && strstr(res->payload,req->p.v2[i])) { switch (NOECHO(m = get_modifiers(req,res))) { case 0: case MOD_AUTH: SHOW_MSG(2,"Direct markup echoed back",req->p.v2[i],m); break; case MOD_PRED: case MOD_PRED | MOD_AUTH: SHOW_MSG(3,"Direct markup echoed back",req->p.v2[i],m); break; } break; } /* Non-echoed paths in query are often bad, though there are some common patterns of false psoitives. */ for (i=0;i<req->p.c;i++) if (!req->p.fn[i][0] && strlen(req->p.v2[i]) < MAX_FPATH && strcmp(req->p.v1[i],"utmp") /* Analytics-specific. */ ) { _u8* x = strchr(req->p.v2[i],'/'); _u8* y = strchr(req->p.v2[i],'.'); if (!x) continue; /* No slash - no problem */ if (y && y <= x) continue; /* "www.foo.com/bar/baz.jpg" */ if (x[1] == '/') continue; /* "http://www.foo.com/" */ if (isdigit(x[1]) && isdigit(x[2]) && x[3] == '/') continue; /* 01/02/2007 */ if (isdigit(x[1]) && isdigit(x[3]) && x[2] == '/') continue; /* 01/2/2007 */ do { x++; } while (isalnum(*x) || *x == '_');
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -