diff -Nru skipfish-2.02b/ChangeLog skipfish-2.10b/ChangeLog --- skipfish-2.02b/ChangeLog 2011-07-06 19:59:07.000000000 +0000 +++ skipfish-2.10b/ChangeLog 2012-12-04 13:27:54.000000000 +0000 @@ -1,3 +1,208 @@ +Version 2.10b: + - Updated HTML tags and attributes that are checked for URL XSS + injections to also include a few HTML5 specific ones + + - Updated test and description for semi-colon injection in HTML meta + refresh tags (this is IE6 specific) + + - Relaxed HTML parsing a bit to allow spaces between HTML tag attributes + and their values (e.g. "foo =bar"). + + - Major update of LFI tests by adding more dynamic tests (double + encoding, dynamic amount of ../'s for web.xml). The total amount of + tests for this vulnerability is now 40 per injection point. + + - The RFI test is now a separate test and no longer requires special + compile options. The default RFI URL and it's payload check are + still defined in src/config.h. + + - Using the --flush-to-disk flag will cause requests and responses + to be flushed to disk which reduces the memory footprint. (especially + noticable in large scans) + + - Fixed a bug where in some conditions (e.g. a page looks similar to + another) links were not scraped from responses which lead to links + to be missed (thanks to Anurag Chaurasia for reporting) + + - Added configuration file support with the --config flag. In + config/example.conf you can find flags and examples. + + - Several signature keyword enhancements have been made. Most + significant are the "header" keyword, which allows header matching + and the "depend" keyword which allows signature chaining. + + - Fixed basic authentication which was broken per 2.08b. Cheers to + Michael Stevens for reporting. + + - Fixed -k scheduling where 1:0:0 would count as a second in stead of + an hour (also visa versa). Cheers to Claudio Criscione for reporting. + + - Small fix to compile time warnings + +Version 2.09b: + + - Fixed a crash that could be triggered during 404 fingerprint failures + + - Signature IDs for detected issues are now stored in the report + JSON files. + + - Added mod_status, mod_info, MySQL dump, phpMyAdmin SQL dump and + robots.txt signatures. + + - Improved the Flash and Silverlight crossdomain policy signatures to + only warn about them when they use wildcards. + +Version 2.08b: + + - Added Host header XSS testing. + + - Added HTML encoding XSS tests to detect scenarios where our + injection string ends up in an attributes that execute HTML encoded + Javascript. For example: onclick. + + - Bruteforcing is now disabled for URLs that gave a directory listing. + + - Added subject alternate name checking for SSL certificates (cheers + to Matt Caroll for his feedback) + + - Added signature matching (see doc/signatures.txt) which means a lot + of the content based issues are no longer hardcoded. + + - Added active XSSI test. The passive XSSI stays (for now) but this + active check is more acurate and will remove issues detected by the + passive one if they cannot be confirmed. This reduces false positives + + - Added HTML tag XSS test which triggers when our payload is used + as a tag attribute value but without quotes (courtesy of wavsep). + + - Added javascript: scheme XSS testing (courtesy of wavsep). + + - Added form based authentication. During these authenticated + scans, skipfish will check if the session has ended and re-authenticates + if necessary. + + - Fixed a bug where in slow scans the console output could mess up + due to the high(er) refresh rate. + + - Fixed a bug where a missed response during the injection tests could + result in a crash. (courtesy of Sebastian Roschke) + + - Restructure the source package a bit by adding a src/, doc/ and + tools/ directory. + +Version 2.07b: +-------------- + + - A bugfix to fprint_response() will help reduce false positives that + could occur for differential tests (i.e. the query and shell injection + tests) + + - We now suppress implicit cache warnings when dealing with 302, 303 and 307 + redirects. + + - Added --no-checks which allows a scan to be run without any injection + tests. This still allows bruteforcing and combines well with the + new ability to load URLs from previous scan results. + + - We can now parse the pivots.txt, which can be found in the output + directory of older scans. All URLs will be loaded which seriously + speeds up recurring scans. + + - Directory bruteforcing now includes a content negotiation trick where + a using a fake mime in the Accept: header will cause some servers to + propose us files via a 406 response. + + - A horrible bug fix which caused instable pages not be marked + as such. The result: false positives. + + +Version 2.06b: +-------------- + + - Crawler update which gives more control over the injection test + scheduling. This comes with the --checks and --checks-toggle + flags to display and enable/disable checks. + + - Pages where the response varies are no longer completely + discarded. Instead now we only disable tests that require stability + which increases scan coverage. + + - Split the traversal and disclosure test to increase coverage: + traversal checks require stable pages, the disclosure checks can be + performed on all. + + - Updated dictionaries and converted them to use the dictionary + optimisations we introduced in 2.03b + + - Fixed offline report viewing (thanks to Sebastian Roschke) + + - Added NULL byte file disclosure tests + + - Added JSP inclusion error check to analyse.c + + - Added XSS injection tests for cookies + + - Directory listings are now reported as individual (info-type) issues + + - Added warning in case the negotiated SSL cipher turns out to be a + weak one (leaving the cipher enumeration to network scanners) + + - Added experimental -v flag which can be used to enable (limited) + runtime reporting. This output is written to stderr and should be + redirected to a file, unless you use the -u flag. + + - The man page has been rewritten and now includes detailed descriptions + and examples. + + - A whole bunch of small bug fixes + + +Version 2.05b: +-------------- + + - Fixed a NULL pointer crash when adding "callback" tests to JavaScript + URLs that have a parameter with no value. + + - Bug fix in the redirect callback which expected 2 responses but + since 2.04b actually should process 4. + +Version 2.04b: +-------------- + + - Option -V eliminated in favor of -W / -S. + + - Option -l added to limit the maximum requests per second (contributed by Sebastian Roschke) + + - Option -k added to limit the maximum duration of a scan (contributed by Sebastian Roschke) + + - Support for #ro, -W-; related documentation changes. + + - HTTPS -> HTTP form detection. + + - Added more diverse traversal and file disclosure tests (including file:// scheme tests) + + - Improved injection detection in "); - if (next) *next = 0; - check_js_xss(req, res, tmp); - if (next) *next = '<'; - /* Don't skip right away, as there might be some nested HTML inside. */ - } - - /* CHECK 3.7: ...and so are stylesheets. */ - - if (!strcasecmp((char*)tag_name, "style")) { - - u8* next = inl_strcasestr(tmp, (u8*)""); - if (next) *next = 0; - check_js_xss(req, res, tmp); - if (next) *next = '<'; - - } - - ck_free(tag_name); - - } else tmp = (u8*)strchr((char*)tmp, '<'); - - } while (tmp && *tmp); - - /* CHECK 4: Known exceptions / error pages, etc. */ - - detect_mime(req, res); - res->sniffed_mime = (u8*)mime_map[res->sniff_mime_id][0]; - - check_for_stuff(req, res); - -binary_checks: - - detect_mime(req, res); - res->sniffed_mime = (u8*)mime_map[res->sniff_mime_id][0]; - - /* No MIME checks on Content-Disposition: attachment responses. */ - - if ((tmp = GET_HDR((u8*)"Content-Disposition", &res->hdr)) && - inl_strcasestr(tmp, (u8*)"attachment")) return; - -// if (!relaxed_mime) { -// -// /* CHECK 5A: Renderable documents that are not CSS or static JS are of -// particular interest when it comes to MIME / charset mistakes. */ -// -// if (is_mostly_ascii(res) && !is_css(res) && (!is_javascript(res) || -// (!strstr((char*)res->payload, "function ") && -// !strstr((char*)res->payload, "function(")))) high_risk = 1; -// -// } else - - { - - /* CHECK 5B: Documents with skipfish signature strings echoed back - are of particular interest when it comes to MIME / charset mistakes. */ - - u8* tmp = (u8*)strstr((char*)res->payload, "sfi"); - - if ((tmp && isdigit(tmp[3]) && tmp[9] == 'v') || - strstr((char*)res->payload, "sfish") || - strstr((char*)res->payload, "skipfish")) high_risk = 1; - - } - - /* CHECK 6: MIME mismatch? Ignore cases where the response had a valid - MIME type declared in headers, but we failed to map it to a known - value... and also failed to sniff. - - Mismatch between MIME_ASC_HTML and MIME_XML_XHTML is not worth - complaining about; the same about JS or CSS responses being - sniffed as "unknown ASCII". */ - - if (res->sniff_mime_id != res->decl_mime_id && - !((res->decl_mime_id == MIME_ASC_JAVASCRIPT || - res->decl_mime_id == MIME_ASC_CSS) && - res->sniff_mime_id == MIME_ASC_GENERIC) && - !(res->decl_mime_id == MIME_ASC_HTML && - res->sniff_mime_id == MIME_XML_XHTML) && - !(res->decl_mime_id == MIME_XML_XHTML && - res->sniff_mime_id == MIME_ASC_HTML) && - !(res->header_mime && !res->decl_mime_id && - (res->sniff_mime_id == MIME_ASC_GENERIC || - res->sniff_mime_id == MIME_BIN_GENERIC))) - problem(high_risk ? PROB_BAD_MIME_DYN : PROB_BAD_MIME_STAT, - req, res, res->sniffed_mime, req->pivot, 0); - - /* CHECK 7: application/octet-stream or text/plain; both have - unintended consequences (but complain only if 3 didn't fire). */ - - else if (res->header_mime && (!strcasecmp((char*)res->header_mime, - "application/octet-stream") || !strcasecmp((char*)res->header_mime, - "text/plain"))) - problem(high_risk ? PROB_GEN_MIME_DYN : PROB_GEN_MIME_STAT, - req, res, res->sniffed_mime, req->pivot, 0); - - /* CHECK 8: Missing charset? */ - - if (is_mostly_ascii(res) && !res->meta_charset && !res->header_charset) - problem(high_risk ? PROB_BAD_CSET_DYN : PROB_BAD_CSET_STAT, - req, res, 0, req->pivot, 0); - - /* CHECK 9: Duplicate, inconsistent C-T or charset? */ - - if (is_mostly_ascii(res) && (res->warn & WARN_CFL_HDR || - (res->meta_charset && res->header_charset && - strcasecmp((char*)res->meta_charset, (char*)res->header_charset)))) - problem(high_risk ? PROB_CFL_HDRS_DYN : PROB_CFL_HDRS_STAT, - req, res, 0, req->pivot, 0); - - /* CHECK 10: Made up charset? */ - - if (res->header_charset || res->meta_charset) { - u32 i = 0; - - while (valid_charsets[i]) { - if (!strcasecmp((char*)valid_charsets[i], (char*)(res->header_charset ? - res->header_charset : res->meta_charset))) break; - i++; - } - - if (!valid_charsets[i]) - problem(high_risk ? PROB_BAD_CSET_DYN : PROB_BAD_CSET_STAT, - req, res, res->header_charset ? - res->header_charset : res->meta_charset, req->pivot, 0); - - } - -} - - -/* Does MIME detection on a message. Most of this logic is reused from - ratproxy, with some improvements and additions. */ - -static void detect_mime(struct http_request* req, struct http_response* res) { - u8 sniffbuf[SNIFF_LEN]; - - if (res->sniff_mime_id) return; - - /* First, classify declared response MIME, if any. */ - - if (res->header_mime) { - u32 i; - - for (i=0;iheader_mime, - strlen((char*)mime_map[i][j] + 1))) break; - } else { - if (!strcasecmp((char*)mime_map[i][j], (char*)res->header_mime)) - break; - } - j++; - } - - if (mime_map[i][j]) break; - - } - - if (i != MIME_COUNT) res->decl_mime_id = i; - - } - - /* Next, work out the actual MIME that should be set. Mostly - self-explanatory. */ - - memcpy(sniffbuf, res->payload, - (res->pay_len > SNIFF_LEN - 1) ? (SNIFF_LEN - 1) : res->pay_len); - - sniffbuf[(res->pay_len > SNIFF_LEN - 1) ? (SNIFF_LEN - 1) : res->pay_len] = 0; - - if (is_mostly_ascii(res)) { - - /* ASCII checks. */ - - if (is_javascript(res)) { - res->sniff_mime_id = MIME_ASC_JAVASCRIPT; - return; - } - - if (is_css(res)) { - res->sniff_mime_id = MIME_ASC_CSS; - return; - } - - - if (!prefix(sniffbuf, "%!PS")) { - res->sniff_mime_id = MIME_ASC_POSTSCRIPT; - return; - } - - if (!prefix(sniffbuf, "{\\rtf")) { - res->sniff_mime_id = MIME_ASC_RTF; - return; - } - - /* Adobe PDF (may be mostly ASCII in some cases). */ - - if (!prefix(sniffbuf, "%PDF")) { - res->sniff_mime_id = MIME_EXT_PDF; - return; - } - - /* Several types of XML documents, taking into account that - they might be missing their xmlns=, etc: */ - - if (strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_OPENSEARCH; - return; - } - - if (strstr((char*)sniffbuf, "") || - strstr((char*)sniffbuf, "") || - strstr((char*)sniffbuf, "") || - strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_RSS; - return; - } - - if (strstr((char*)sniffbuf, "")) { - res->sniff_mime_id = MIME_XML_ATOM; - return; - } - - if (strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_WML; - return; - } - - if (strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_SVG; - return; - } - - if (strstr((char*)sniffbuf, "")) { - res->sniff_mime_id = MIME_XML_CROSSDOMAIN; - return; - } - - if (strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_XHTML; - else - res->sniff_mime_id = MIME_XML_GENERIC; - - return; - - } - - /* Do an unconvincing check for HTML once we ruled out - known XML cases. */ - - if (inl_strcasestr(sniffbuf, (u8*)"") || - inl_strcasestr(sniffbuf, (u8*)"href=")) { - res->sniff_mime_id = MIME_ASC_HTML; - return; - } - - /* OK, we're out of ideas. Let's do a last-resort check for XML again, - now that HTML is also off the table. */ - - if (strstr((char*)sniffbuf, "")) { - res->sniff_mime_id = MIME_XML_GENERIC; - return; - } - - res->sniff_mime_id = MIME_ASC_GENERIC; - - } else { - - /* Binary checks. Start with simple images (JPG, GIF, PNG, TIFF, BMP). */ - - if (sniffbuf[0] == 0xFF && sniffbuf[1] == 0xD8 && sniffbuf[2] == 0xFF) { - res->sniff_mime_id = MIME_IMG_JPEG; - return; - } - - if (!prefix(sniffbuf, "GIF8")) { - res->sniff_mime_id = MIME_IMG_GIF; - return; - } - - if (sniffbuf[0] == 0x89 && !prefix(sniffbuf + 1, "PNG")) { - res->sniff_mime_id = MIME_IMG_PNG; - return; - } - - if (!prefix(sniffbuf, "BM")) { - res->sniff_mime_id = MIME_IMG_BMP; - return; - } - - if (!prefix(sniffbuf, "II") && sniffbuf[2] == 42 /* dec */) { - res->sniff_mime_id = MIME_IMG_TIFF; - return; - } - - /* Next: RIFF containers (AVI, ANI, WAV). */ - - if (!prefix(sniffbuf, "RIFF")) { - - if (sniffbuf[8] == 'A') { - if (sniffbuf[9] == 'C') - res->sniff_mime_id = MIME_IMG_ANI; - else - res->sniff_mime_id = MIME_AV_AVI; - } else res->sniff_mime_id = MIME_AV_WAV; - - return; - - } - - /* Cursor / ICO drama (we roll it back into BMP, because few sites - make the distinction anyway, and cursors are unlikely to be - attacker-supplied)... */ - - if (res->pay_len > 3 && !sniffbuf[0] && !sniffbuf[1] && - sniffbuf[2] && !sniffbuf[3]) { - res->sniff_mime_id = MIME_IMG_BMP; - return; - } - - /* Windows Media container (WMV, WMA, ASF). */ - - if (sniffbuf[0] == 0x30 && sniffbuf[1] == 0x26 && sniffbuf[2] == 0xB2) { - res->sniff_mime_id = MIME_AV_WMEDIA; - return; - } - - /* MPEG formats, Ogg Vorbis, QuickTime, RealAudio, RealVideo. */ - - if (sniffbuf[0] == 0xFF && sniffbuf[1] == 0xFB) { - res->sniff_mime_id = MIME_AV_MP3; - return; - } - - if (sniffbuf[0] == 0x00 && sniffbuf[1] == 0x00 && sniffbuf[2] == 0x01 && - (sniffbuf[3] >> 4) == 0x0B) { - res->sniff_mime_id = MIME_AV_MPEG; - return; - } - - if (!prefix(sniffbuf, "OggS")) { - res->sniff_mime_id = MIME_AV_OGG; - return; - } - - if (sniffbuf[0] == 0x28 && !prefix(sniffbuf + 1, "RMF")) { - res->sniff_mime_id = MIME_AV_RA; - return; - } - - if (sniffbuf[0] == 0x2E && !prefix(sniffbuf + 1, "RMF")) { - res->sniff_mime_id = MIME_AV_RV; - return; - } - - if (!prefix(sniffbuf + 4, "free") || - !prefix(sniffbuf + 4, "mdat") || - !prefix(sniffbuf + 4, "wide") || - !prefix(sniffbuf + 4, "pnot") || - !prefix(sniffbuf + 4, "skip") || - !prefix(sniffbuf + 4, "moov")) { - /* Oookay, that was weird... */ - res->sniff_mime_id = MIME_AV_QT; - return; - } - - /* Flash and FLV. */ - - if (!prefix(sniffbuf, "FLV")) { - res->sniff_mime_id = MIME_AV_FLV; - return; - } - - if (!prefix(sniffbuf, "FCWS") || - !prefix(sniffbuf, "CWS")) { - res->sniff_mime_id = MIME_EXT_FLASH; - return; - } - - /* Adobe PDF. */ - - if (!prefix(sniffbuf, "%PDF")) { - res->sniff_mime_id = MIME_EXT_PDF; - return; - } - - /* JAR versus ZIP. A bit tricky, because, well, they are both just - ZIP archives. */ - - if (!prefix(sniffbuf, "PK") && - sniffbuf[2] < 6 && sniffbuf[3] < 7) { - - if (inl_memmem(res->payload, res->pay_len, "META-INF/", 9)) - res->sniff_mime_id = MIME_EXT_JAR; - else - res->sniff_mime_id = MIME_BIN_ZIP; - return; - - } - - /* Java class files. */ - - if (sniffbuf[0] == 0xCA && sniffbuf[1] == 0xFE && sniffbuf[2] == 0xBA && - sniffbuf[3] == 0xBE) { - res->sniff_mime_id = MIME_EXT_CLASS; - return; - } - - /* The joy of Microsoft Office containers. */ - - if (res->pay_len > 512 && sniffbuf[0] == 0xD0 && sniffbuf[1] == 0xCF && - sniffbuf[2] == 0x11 && sniffbuf[3] == 0xE0) { - - switch (sniffbuf[512]) { - case 0xEC: res->sniff_mime_id = MIME_EXT_WORD; return; - case 0xFD: - case 0x09: res->sniff_mime_id = MIME_EXT_EXCEL; return; - case 0x00: - case 0x0F: - case 0xA0: res->sniff_mime_id = MIME_EXT_PPNT; return; - } - - } - - /* GZIP. Unfortunately, tar has no discernible header to speak of, - so we just let it slide - few sites are serving tars on purpose - anyway. */ - - if (sniffbuf[0] == 0x1F && sniffbuf[1] == 0x8B && sniffbuf[2] == 0x08) { - res->sniff_mime_id = MIME_BIN_GZIP; - return; - } - - /* CAB. */ - - if (sniffbuf[0] == 'M' && sniffbuf[1] == 'S' && sniffbuf[2] == 'C' && - sniffbuf[3] == 'F' && !sniffbuf[4]) { - res->sniff_mime_id = MIME_BIN_CAB; - return; - } - - res->sniff_mime_id = MIME_BIN_GENERIC; - - } - - /* No more ideas? */ - -} - - -/* "Stuff" means various error messages and cool, unusual content. For large - files, this function may be sort-of expensive, but we need to look through - the entire response, as not all messages are full-page errors, etc. */ - -static void check_for_stuff(struct http_request* req, - struct http_response* res) { - - u8 sniffbuf[SNIFF_LEN]; - u8* tmp; - - if (!res->pay_len || !is_mostly_ascii(res) || res->stuff_checked) return; - - /* We will use sniffbuf for checks that do not need to look through the - entire file. */ - - memcpy(sniffbuf, res->payload, - (res->pay_len > SNIFF_LEN - 1) ? (SNIFF_LEN - 1) : res->pay_len); - - sniffbuf[(res->pay_len > SNIFF_LEN - 1) ? (SNIFF_LEN - 1) : res->pay_len] = 0; - - res->stuff_checked = 1; - - /* Assorted interesting error messages. */ - - if (strstr((char*)res->payload, "error '")) { - problem(PROB_ERROR_POI, req, res, (u8*)"Microsoft runtime error", - req->pivot, 0); - return; - } - - if (strstr((char*)res->payload, "

Server Error in '")) { - problem(PROB_ERROR_POI, req, res, (u8*) - "ASP.NET Yellow Screen of Death", req->pivot, 0); - return; - } - - if (strstr((char*)sniffbuf, "JRun Servlet Error")) { - problem(PROB_ERROR_POI, req, res, (u8*)"JRun servlet error", req->pivot, 0); - return; - } - - if (strstr((char*)res->payload, "Exception in thread \"") || - strstr((char*)res->payload, "at java.lang.") || - (strstr((char*)res->payload, "\tat ") && - (strstr((char*)res->payload, ".java:")))) { - problem(PROB_ERROR_POI, req, res, (u8*)"Java exception trace", req->pivot, 0); - return; - } - - if ((tmp = (u8*)strstr((char*)res->payload, " on line "))) { - u32 off = 512; - - while (tmp - 1 > res->payload && !strchr("\r\n", tmp[-1]) - && off--) tmp--; - - if (off && (!prefix(tmp, "Warning: ") || !prefix(tmp, "Notice: ") || - !prefix(tmp, "Fatal error: ") || !prefix(tmp, "Parse error: ") || - !prefix(tmp, "Deprecated: ") || - !prefix(tmp, "Strict Standards: ") || - !prefix(tmp, "Catchable fatal error: "))) { - problem(PROB_ERROR_POI, req, res, (u8*)"PHP error (text)", req->pivot, 0); - return; - } - - if (off && !prefix(tmp, "") && (!prefix(tmp + 3, "Warning: ") || - !prefix(tmp + 3, "Notice: ") || - !prefix(tmp + 3, "Fatal error: ") || - !prefix(tmp + 3, "Parse error: ") || - !prefix(tmp + 3, "Deprecated: ") || - !prefix(tmp + 3, "Strict Standards: ") || - !prefix(tmp + 3, "Catchable fatal error: "))) { - problem(PROB_ERROR_POI, req, res, (u8*)"PHP error (HTML)", req->pivot, 0); - return; - } - - } - - if (strstr((char*)res->payload, "Warning: MySQL: ") || - strstr((char*)res->payload, "java.sql.SQLException") || - strstr((char*)res->payload, "You have an error in your SQL syntax; ")) { - problem(PROB_ERROR_POI, req, res, (u8*)"SQL server error", req->pivot, 0); - return; - } - - if ((tmp = (u8*)strstr((char*)res->payload, "ORA-")) && - isdigit(tmp[4]) && tmp[9] == ':') { - problem(PROB_ERROR_POI, req, res, (u8*)"Oracle server error", req->pivot, 0); - return; - } - - if (strstr((char*)res->payload, "[an error occurred while processing")) { - problem(PROB_ERROR_POI, req, res, (u8*)"SHTML error", req->pivot, 0); - return; - } - - if (strstr((char*)res->payload, "Traceback (most recent call last):")) { - problem(PROB_ERROR_POI, req, res, (u8*)"Python error", req->pivot, 0); - return; - } - - /* Interesting files. */ - - if (strstr((char*)res->payload, "ADDRESS=(PROTOCOL=")) { - problem(PROB_FILE_POI, req, res, (u8*)"SQL configuration or logs", req->pivot, 0); - return; - } - - if (inl_strcasestr(res->payload, (u8*)";database=") && - inl_strcasestr(res->payload, (u8*)";pwd=")) { - problem(PROB_FILE_POI, req, res, (u8*)"ODBC connect string", req->pivot, 0); - return; - } - - if (strstr((char*)sniffbuf, "")) { - problem(PROB_FILE_POI, req, res, (u8*) - "Flash cross-domain policy", req->pivot, 0); - - /* - if (strstr((char*)res->payload, "domain=\"*\"")) - problem(PROB_CROSS_WILD, req, res, (u8*) - "Cross-domain policy with wildcard rules", req->pivot, 0); - */ - - return; - } - - if (strstr((char*)sniffbuf, "")) { - problem(PROB_FILE_POI, req, res, (u8*)"Silverlight cross-domain policy", - req->pivot, 0); - - /* - if (strstr((char*)res->payload, "uri=\"*\"")) - problem(PROB_CROSS_WILD, req, res, (u8*) - "Cross-domain policy with wildcard rules", req->pivot, 0); - */ - - return; - } - - if (inl_strcasestr(sniffbuf, (u8*)"\nAuthType ") || - (inl_strcasestr(sniffbuf, (u8*)"\nOptions ") && ( - inl_strcasestr(sniffbuf, (u8*)"\nOptions +") || - inl_strcasestr(sniffbuf, (u8*)"\nOptions -") || - inl_strcasestr(sniffbuf, (u8*)"\nOptions All") || - inl_strcasestr(sniffbuf, (u8*)"\nOptions Exec") || - inl_strcasestr(sniffbuf, (u8*)"\nOptions Follow") || - inl_strcasestr(sniffbuf, (u8*)"\nOptions In") || - inl_strcasestr(sniffbuf, (u8*)"\nOptions Mult") || - inl_strcasestr(sniffbuf, (u8*)"\nOptions Sym")) - ) || - inl_strcasestr(sniffbuf, (u8*)"\npivot, 0); - return; - } - - if (res->sniff_mime_id == MIME_ASC_GENERIC) { - u8* x = sniffbuf; - - /* Generic something:something[:...] password syntax. */ - - while (*x && (isalnum(*x) || strchr("._-+$", *x)) && - (x - sniffbuf) < 64) x++; - - if (x != sniffbuf && *x == ':' && x[1] != '/' && x[1] != '.') { - u8* start_x = ++x; - - while (*x && (isalnum(*x) || strchr("./*!+=$", *x)) && - (x - sniffbuf) < 128) x++; - - if (*x == ':' || ((start_x != x) && (!*x || *x == '\r' || *x == '\n'))) - problem(PROB_FILE_POI, req, res, (u8*) - "Possible password file", req->pivot, 0); - - } - } - - if (inl_strcasestr(sniffbuf, (u8*)"\nDisallow:") || - inl_strcasestr(sniffbuf, (u8*)"\rDisallow:")) { - problem(PROB_FILE_POI, req, res, (u8*)"robots.txt ruleset", req->pivot, 0); - return; - } - - if (strstr((char*)sniffbuf, "pivot, 0); - return; - } - - /* Add more directory signatures here... */ - - if (strstr((char*)sniffbuf, "") || - strstr((char*)sniffbuf, "") || - strstr((char*)sniffbuf, "

Index of /") || - strstr((char*)sniffbuf, ">[To Parent Directory]<")) { - problem(PROB_FILE_POI, req, res, (u8*)"Directory listing", req->pivot, 0); - return; - } - - if (res->sniff_mime_id == MIME_ASC_GENERIC) { - u8* x = sniffbuf; - u32 slashes = 0; - - /* Five slashes in the first line in a plaintext file should be a - reasonably good check for CVS. */ - - while (*x && *x != '\n' && - (x - sniffbuf) < 256) { - if (*x == '/') slashes++; - x++; - } - - if (slashes == 5) { - problem(PROB_FILE_POI, req, res, (u8*)"CVS RCS data", req->pivot, 0); - return; - } - - } - - if (strstr((char*)sniffbuf, "pivot, 0); - return; - } - - /* This should also cover most cases of Perl, Python, etc. */ - - if (!prefix(sniffbuf, "#!/")) { - problem(PROB_FILE_POI, req, res, (u8*)"shell script", req->pivot, 0); - return; - } - - if (strstr((char*)res->payload, "payload, "?>") && - !strstr((char*)sniffbuf, "payload, "# ?>") && - !strstr((char*)res->payload, "pivot, 0); - return; - } - - if (strstr((char*)res->payload, "<%@") && strstr((char*)res->payload, "%>")) { - problem(PROB_FILE_POI, req, res, (u8*)"JSP source", req->pivot, 0); - return; - } - - if (strstr((char*)res->payload, "<%") && strstr((char*)res->payload, "%>")) { - problem(PROB_FILE_POI, req, res, (u8*)"ASP source", req->pivot, 0); - return; - } - - if (strstr((char*)sniffbuf, "\nimport java.")) { - problem(PROB_FILE_POI, req, res, (u8*)"Java source", req->pivot, 0); - return; - } - - if (strstr((char*)sniffbuf, "\n#include")) { - problem(PROB_FILE_POI, req, res, (u8*)"C/C++ source", req->pivot, 0); - return; - } - - if (strstr((char*)res->payload, "End Sub\n") || - strstr((char*)res->payload, "End Sub\r")) { - problem(PROB_FILE_POI, req, res, (u8*)"Visual Basic source", req->pivot, 0); - return; - } - - if (strstr((char*)sniffbuf, "0] \"GET /")) { - problem(PROB_FILE_POI, req, res, (u8*)"Apache server logs", req->pivot, 0); - return; - } - - if (strstr((char*)sniffbuf, "0, GET, /")) { - problem(PROB_FILE_POI, req, res, (u8*)"IIS server logs", req->pivot, 0); - return; - } - - /* Plain text, and every line contains ;, comma, or |? */ - - if (res->sniff_mime_id == MIME_ASC_GENERIC) { - u8* cur = res->payload; - u8 all_delim = 0; - u8* eol; - - do { - u32 del = strcspn((char*)cur, ",|;\n"); - - eol = (u8*)strchr((char*)cur, '\n'); - - if (!cur[del] || cur[del] == '\n' || (cur[del] == ',' && - cur[del+1] == ' ')) { - all_delim = 0; - break; - } - - all_delim = 1; - cur = eol + 1; - - } while (eol && cur && *cur); - - if (all_delim) { - problem(PROB_FILE_POI, req, res, - (u8*)"Delimited database dump", req->pivot, 0); - return; - } - - } - - /* Excel is almost always interesting on its own. */ - - if (res->sniff_mime_id == MIME_EXT_EXCEL) { - problem(PROB_FILE_POI, req, res, (u8*)"Excel spreadsheet", req->pivot, 0); - return; - } - - /* This is a bit dodgy, but the most prominent sign of non-browser JS on - Windows is the instantiation of obscure ActiveX objects to access local - filesystem, create documents, etc. Unfortunately, some sites may also be - creating obscure ActiveX objects; these would likely need to be just - blacklisted here. */ - - if (is_javascript(res) && strstr((char*)res->payload, "new ActiveXObject(") && - !strstr((char*)res->payload, "XMLHTTP") && - !strstr((char*)res->payload, "ShockwaveFlash")) { - problem(PROB_FILE_POI, req, res, (u8*)"server-side JavaScript source", - req->pivot, 0); - return; - } - - /* Three very lame rules follow; help improve. */ - - if (inl_strcasestr(res->payload, (u8*)"\nCREATE TABLE") || - inl_strcasestr(res->payload, (u8*)"\nSELECT * FROM") || - inl_strcasestr(res->payload, (u8*)"\nDROP TABLE")) { - problem(PROB_FILE_POI, req, res, (u8*)"SQL script", req->pivot, 0); - return; - } - - if (inl_strcasestr(sniffbuf, (u8*)"@echo ")) { - problem(PROB_FILE_POI, req, res, (u8*)"DOS batch script", req->pivot, 0); - return; - } - - if (inl_strcasestr(res->payload, (u8*)"(\"Wscript.")) { - problem(PROB_FILE_POI, req, res, (u8*)"Windows shell script", req->pivot, 0); - return; - } - -} - - -/* Deletes payload of binary responses if requested. This is called when pivot - enters PSTATE_DONE. */ - -void maybe_delete_payload(struct pivot_desc* pv) { - u8 tmp[64]; - u32 i; - - if (pv->res && pv->res->pay_len > 256 && !is_mostly_ascii(pv->res)) { - ck_free(pv->res->payload); - sprintf((char*)tmp, "[Deleted binary payload (%u bytes)]", pv->res->pay_len); - pv->res->payload = ck_strdup(tmp); - pv->res->pay_len = strlen((char*)tmp); - } - - for (i=0;iissue_cnt;i++) { - - if (pv->issue[i].res && pv->issue[i].res->pay_len > 256 && - !is_mostly_ascii(pv->issue[i].res)) { - ck_free(pv->issue[i].res->payload); - sprintf((char*)tmp, "[Deleted binary payload (%u bytes)]", - pv->issue[i].res->pay_len); - pv->issue[i].res->payload = ck_strdup(tmp); - pv->issue[i].res->pay_len = strlen((char*)tmp); - } - - } - -} - diff -Nru skipfish-2.02b/analysis.h skipfish-2.10b/analysis.h --- skipfish-2.02b/analysis.h 2011-07-02 06:35:05.000000000 +0000 +++ skipfish-2.10b/analysis.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,207 +0,0 @@ -/* - skipfish - content analysis - --------------------------- - - Author: Michal Zalewski - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#ifndef _HAVE_ANALYSIS_C - -#include "types.h" -#include "http_client.h" -#include "database.h" -#include "crawler.h" - -extern u8 no_parse, /* Disable HTML link detection */ - warn_mixed, /* Warn on mixed content */ - log_ext_urls, /* Log all external URLs */ - no_forms, /* Do not submit forms */ - pedantic_cache; /* Match HTTP/1.0 and HTTP/1.1 */ - -/* Helper macros to group various useful checks: */ - -#define PIVOT_CHECKS(_req, _res) do { \ - pivot_header_checks(_req, _res); \ - content_checks(_req, _res); \ - scrape_response(_req, _res); \ - } while (0) - - -#define RESP_CHECKS(_req, _res) do { \ - content_checks(_req, _res); \ - scrape_response(_req, _res); \ - } while (0) - -/* Form autofill hints: */ - -extern u8** addl_form_name; -extern u8** addl_form_value; -extern u32 addl_form_cnt; - - -/* Runs some rudimentary checks on top-level pivot HTTP responses. */ - -void pivot_header_checks(struct http_request* req, - struct http_response* res); - -/* Adds a new item to the form hint system. */ - -void add_form_hint(u8* name, u8* value); - -/* Analyzes response headers (Location, etc), body to extract new links, - keyword guesses, examine forms, mixed content issues, etc. */ - -void scrape_response(struct http_request* req, struct http_response* res); - -/* Analyzes response headers and body to detect stored XSS, redirection, - 401, 500 codes, exception messages, source code, offensive comments, etc. */ - -void content_checks(struct http_request* req, struct http_response* res); - -/* Deletes payload of binary responses if requested. */ - -void maybe_delete_payload(struct pivot_desc* pv); - -/* MIME detector output codes: */ - -#define MIME_NONE 0 /* Checks missing or failed */ - -#define MIME_ASC_GENERIC 1 /* Unknown, but mostly 7bit */ -#define MIME_ASC_HTML 2 /* Plain, non-XML HTML */ -#define MIME_ASC_JAVASCRIPT 3 /* JavaScript or JSON */ -#define MIME_ASC_CSS 4 /* Cascading Style Sheets */ -#define MIME_ASC_POSTSCRIPT 5 /* PostScript */ -#define MIME_ASC_RTF 6 /* Rich Text Format */ - -#define MIME_XML_GENERIC 7 /* XML not recognized otherwise */ -#define MIME_XML_OPENSEARCH 8 /* OpenSearch specification */ -#define MIME_XML_RSS 9 /* Real Simple Syndication */ -#define MIME_XML_ATOM 10 /* Atom feeds */ -#define MIME_XML_WML 11 /* WAP WML */ -#define MIME_XML_CROSSDOMAIN 12 /* crossdomain.xml (Flash) */ -#define MIME_XML_SVG 13 /* Scalable Vector Graphics */ -#define MIME_XML_XHTML 14 /* XML-based XHTML */ - -#define MIME_IMG_JPEG 15 /* JPEG */ -#define MIME_IMG_GIF 16 /* GIF */ -#define MIME_IMG_PNG 17 /* PNG */ -#define MIME_IMG_BMP 18 /* Windows BMP (including ICO) */ -#define MIME_IMG_TIFF 19 /* TIFF */ -#define MIME_IMG_ANI 20 /* RIFF: ANI animated cursor */ - -#define MIME_AV_WAV 21 /* RIFF: WAV sound file */ -#define MIME_AV_MP3 22 /* MPEG audio (commonly MP3) */ -#define MIME_AV_OGG 23 /* Ogg Vorbis */ -#define MIME_AV_RA 24 /* Real audio */ - -#define MIME_AV_AVI 25 /* RIFF: AVI container */ -#define MIME_AV_MPEG 26 /* MPEG video */ -#define MIME_AV_QT 27 /* QuickTime */ -#define MIME_AV_FLV 28 /* Flash video */ -#define MIME_AV_RV 29 /* Real video */ - -#define MIME_AV_WMEDIA 30 /* Windows Media audio */ - -#define MIME_EXT_FLASH 31 /* Adobe Flash */ -#define MIME_EXT_PDF 32 /* Adobe PDF */ -#define MIME_EXT_JAR 33 /* Sun Java archive */ -#define MIME_EXT_CLASS 34 /* Sun Java class */ -#define MIME_EXT_WORD 35 /* Microsoft Word */ -#define MIME_EXT_EXCEL 36 /* Microsoft Excel */ -#define MIME_EXT_PPNT 37 /* Microsoft Powerpoint */ - -#define MIME_BIN_ZIP 38 /* ZIP not recognized otherwise */ -#define MIME_BIN_GZIP 39 /* GZIP */ -#define MIME_BIN_CAB 40 /* CAB */ - -#define MIME_BIN_GENERIC 41 /* Binary, unknown type */ - -#define MIME_COUNT (MIME_BIN_GENERIC + 1) - -/* NULL-terminated MIME mapping sets. Canonical name should go first; do not - put misspelled or made up entries here. This is used to match server intent - with the outcome of MIME sniffing. */ - -#ifdef _VIA_ANALYSIS_C - -static char* mime_map[MIME_COUNT][8] = { - -/* MIME_NONE */ { 0 }, - -/* MIME_ASC_GENERIC */ { "text/plain", "?text/x-", "?text/vnd.", - "?application/x-httpd-", "text/csv", 0 }, -/* MIME_ASC_HTML */ { "text/html", 0 }, -/* MIME_ASC_JAVASCRIPT */ { "application/javascript", - "application/x-javascript", - "application/json", "text/javascript", 0 }, -/* MIME_ASC_CSS */ { "text/css", 0 }, -/* MIME_ASC_POSTSCRIPT */ { "application/postscript", 0 }, -/* MIME_ASC_RTF */ { "text/rtf", "application/rtf", 0 }, - -/* MIME_XML_GENERIC */ { "text/xml", "application/xml", 0 }, -/* MIME_XML_OPENSEARCH */ { "application/opensearchdescription+xml", 0 }, -/* MIME_XML_RSS */ { "application/rss+xml", 0 }, -/* MIME_XML_ATOM */ { "application/atom+xml", 0 }, -/* MIME_XML_WML */ { "text/vnd.wap.wml", 0 }, -/* MIME_XML_CROSSDOMAIN */ { "text/x-cross-domain-policy", 0 }, -/* MIME_XML_SVG */ { "image/svg+xml", 0 }, -/* MIME_XML_XHTML */ { "application/xhtml+xml", 0 }, - -/* MIME_IMG_JPEG */ { "image/jpeg", 0 }, -/* MIME_IMG_GIF */ { "image/gif", 0 }, -/* MIME_IMG_PNG */ { "image/png", 0 }, -/* MIME_IMG_BMP */ { "image/x-ms-bmp", "image/bmp", "image/x-icon", 0 }, -/* MIME_IMG_TIFF */ { "image/tiff", 0 }, -/* MIME_IMG_ANI */ { "application/x-navi-animation", 0 }, - -/* MIME_AV_WAV */ { "audio/x-wav", "audio/wav", 0 }, -/* MIME_AV_MP3 */ { "audio/mpeg", 0 }, -/* MIME_AV_OGG */ { "application/ogg", 0 }, -/* MIME_AV_RA */ { "audio/vnd.rn-realaudio", - "audio/x-pn-realaudio", "audio/x-realaudio", 0 }, - -/* MIME_AV_AVI */ { "video/avi", 0 }, -/* MIME_AV_MPEG */ { "video/mpeg", "video/mp4", 0 }, -/* MIME_AV_QT */ { "video/quicktime", 0 }, -/* MIME_AV_FLV */ { "video/flv", "video/x-flv", 0 }, -/* MIME_AV_RV */ { "video/vnd.rn-realvideo", 0 }, - -/* MIME_AV_WMEDIA */ { "video/x-ms-wmv", "audio/x-ms-wma", - "video/x-ms-asf", 0 }, - -/* MIME_EXT_FLASH */ { "application/x-shockwave-flash", 0 }, -/* MIME_EXT_PDF */ { "application/pdf", 0 }, -/* MIME_EXT_JAR */ { "application/java-archive", 0 }, -/* MIME_EXT_CLASS */ { "application/java-vm", 0 }, -/* MIME_EXT_WORD */ { "application/msword", 0 }, -/* MIME_EXT_EXCEL */ { "application/vnd.ms-excel", 0 }, -/* MIME_EXT_PPNT */ { "application/vnd.ms-powerpoint", 0 }, - -/* MIME_BIN_ZIP */ { "application/zip", "application/x-zip-compressed", 0 }, -/* MIME_BIN_GZIP */ { "application/x-gzip", "application/x-gunzip", - "application/x-tar-gz", 0 }, -/* MIME_BIN_CAB */ { "application/vnd.ms-cab-compressed", 0 }, - -/* MIME_BIN_GENERIC */ { "application/binary", "application/octet-stream", - 0 } - -}; - -#endif /* _VIA_ANALYSIS_C */ - -#endif /* !_HAVE_ANALYSIS_H */ diff -Nru skipfish-2.02b/assets/index.html skipfish-2.10b/assets/index.html --- skipfish-2.02b/assets/index.html 2011-07-03 07:25:04.000000000 +0000 +++ skipfish-2.10b/assets/index.html 2012-12-04 13:27:53.000000000 +0000 @@ -256,9 +256,13 @@ "10203": "New 'Via' header value seen", "10204": "New 'X-*' header value seen", "10205": "New 404 signature seen", + "10401": "Resource not directly accessible", "10402": "HTTP authentication required", "10403": "Server error triggered", + "10404": "Directory listing enabled", + "10405": "Hidden files / directories", + "10501": "All external links", "10502": "External URL redirector", "10503": "All e-mail addresses", @@ -274,6 +278,7 @@ "10804": "Conflicting MIME / charset info (low risk)", "10901": "Numerical filename - consider enumerating", "10902": "OGNL-like parameter behavior", + "10909": "Signature match (informational)", "20101": "Resource fetch failed", "20102": "Limits exceeded, fetch suppressed", @@ -289,20 +294,27 @@ "30202": "Self-signed SSL certificate", "30203": "SSL certificate host name mismatch", "30204": "No SSL certificate data found", + "30205": "Weak SSL cipher negotiated", + "30206": "Host name length mismatch (name string has null byte)", "30301": "Directory listing restrictions bypassed", "30401": "Redirection to attacker-supplied URLs", "30402": "Attacker-supplied URLs in embedded content (lower risk)", "30501": "External content embedded on a page (lower risk)", "30502": "Mixed content embedded on a page (lower risk)", + "30503": "HTTPS form submitting to a HTTP URL", "30601": "HTML form with no apparent XSRF protection", "30602": "JSON response with no apparent XSSI protection", + "30603": "Auth form leaks credentials via HTTP GET", "30701": "Incorrect caching directives (lower risk)", "30801": "User-controlled response prefix (BOM / plugin attacks)", + "30901": "HTTP header injection vector", + "30909": "Signature match detected", "40101": "XSS vector in document body", "40102": "XSS vector via arbitrary URLs", "40103": "HTTP response header splitting", "40104": "Attacker-supplied URLs in embedded content (higher risk)", + "40105": "XSS vector via injected HTML tag attribute", "40201": "External content embedded on a page (higher risk)", "40202": "Mixed content embedded on a page (higher risk)", "40301": "Incorrect or missing MIME type (higher risk)", @@ -314,14 +326,18 @@ "40501": "Directory traversal / file inclusion possible", "40601": "Incorrect caching directives (higher risk)", "40701": "Password form submits from or to non-HTTPS page", + "40909": "Signature match detected (higher risk)", "50101": "Server-side XML injection vector", "50102": "Shell injection vector", "50103": "Query injection vector", "50104": "Format string vector", "50105": "Integer overflow vector", + "50106": "File inclusion", + "50107": "Remote file inclusion", "50201": "SQL query or similar syntax in parameters", - "50301": "PUT request accepted" + "50301": "PUT request accepted", + "50909": "Signature match detected (high risk)" }; @@ -396,20 +412,47 @@ /* Displays request or response dump in a faux window. */ +var g_path = ''; +var g_ignore = 0; function show_dat(path, ignore) { + + g_path = path; + g_ignore = ignore; + + /* workaround for cases where there is no response */ + if (typeof req !== 'undefined') + if (req !== null) req = null; + if (typeof res !== 'undefined') + if (res !== null) res = null; + + prepare_view(); + + load_script(path + '/request.js', render_dat); + load_script(path + '/response.js', render_dat_res); + +} + +var req_text = ''; +var res_text = ''; +var finalize = 0; +var pX = 0; +var pY = 0; + +function prepare_view() { + var out = document.getElementById('req_txtarea'), - cov = document.getElementById('cover'); + cov = document.getElementById('cover'); document.body.style.overflow = 'hidden'; out.value = ''; - var x = new XMLHttpRequest(); - var content; + var path = g_path; + var ignore = g_ignore; - var pX = window.scrollX ? window.scrollX : document.body.scrollLeft; - var pY = window.scrollY ? window.scrollY : document.body.scrollTop; + pX = window.scrollX ? window.scrollX : document.body.scrollLeft; + pY = window.scrollY ? window.scrollY : document.body.scrollTop; out.parentNode.style.left = pX; out.parentNode.style.top = pY; @@ -419,56 +462,139 @@ out.parentNode.style.display = 'block'; cov.style.display = 'block'; - x.open('GET', path + '/request.dat', false); - x.send(null); + req_text = ''; + res_text = ''; + finalize = 0; + + out.focus(); + window.scrollTo(pX, pY); + + if (ignore) ignore_click = true; + return false; +} + +function render_dat() { - content = '=== REQUEST ===\n\n' + x.responseText; + if (typeof req !== 'undefined') + if (req != null) req_text = req.data; - x.open('GET', path + '/response.dat', false); - x.send(null); + if (req_text != null && finalize) finalize_view(); + + finalize = 1; +} - if (x.responseText.substr(0,5) == 'HTTP/') - content += '\n=== RESPONSE ===\n\n' + x.responseText + '\n=== END OF DATA ===\n'; +function render_dat_res() { + + if (typeof res !== 'undefined') + if (res != null) res_text = res.data; + + if (res_text != null && finalize) finalize_view(); + + finalize = 1; + +} + +function finalize_view() { + + var out = document.getElementById('req_txtarea'); + + var content = '=== REQUEST ===\n\n' + req_text; + + if (res_text.substr(0,5) == 'HTTP/') + content += '\n=== RESPONSE ===\n\n' + res_text + '\n=== END OF DATA ===\n'; else content += '\n=== RESPONSE NOT AVAILABLE ===\n\n=== END OF DATA ===\n'; out.value = content; - delete x; out.focus(); + window.scrollTo(pX,pY); - if (ignore) ignore_click = true; - return false; } - /* Displays request or response dump in a proper window. */ +var wind = null; + function show_win(path, ignore) { - var out = window.open('','_blank','scroll=yes,addressbar=no'); - var x = new XMLHttpRequest(); - var content; - x.open('GET', path + '/request.dat', false); - x.send(null); + g_path = path; + g_ignore = g_ignore; + wind = null; + + /* workaround when there is no response */ + if (typeof req !== 'undefined') + if (req !== null) req = null; + if (typeof res !== 'undefined') + if (res !== null) res = null; - content = '=== REQUEST ===\n\n' + x.responseText; + prepare_win(); - x.open('GET', path + '/response.dat', false); - x.send(null); + load_script(path + '/request.js', render_win); + load_script(path + '/response.js', render_win_res); + +} + +function prepare_win() { + + wind = window.open('','_blank','scroll=yes,addressbar=no'); + var out = wind; + var content = ''; + + var path = g_path; + var ignore = g_ignore; + + req_text = ''; + res_text = ''; + finalize = 0; + +} - if (x.responseText.substr(0,5) == 'HTTP/') - content += '\n=== RESPONSE ===\n\n' + x.responseText + '\n=== END OF DATA ===\n'; +/* Callback to render request or response dump */ + +function render_win() { + + + req_text = ''; + if (typeof req !== 'undefined') req_text = req.data; + + if (req_text != null && finalize) finalize_win(); + + finalize = 1; +} + +function render_win_res() { + + res_text = ''; + if (typeof res !== 'undefined') + if (res != null) res_text = res.data; + + if (res_text != null && finalize) finalize_win(); + + finalize = 1; +} + +function finalize_win() { + + if (typeof wind == 'undefined') return; + if (wind == null) return; + + var out = wind; + var content = ''; + + content = '=== REQUEST ===\n\n' + req_text; + + if (res_text.substr(0,5) == 'HTTP/') + content += '\n=== RESPONSE ===\n\n' + res_text + '\n=== END OF DATA ===\n'; else content += '\n=== RESPONSE NOT AVAILABLE ===\n\n=== END OF DATA ===\n'; out.document.body.innerHTML = '
';
 
   out.document.body.firstChild.appendChild(out.document.createTextNode(content));
-  delete x;
 
   if (ignore) ignore_click = true;
-  return false;
-}
+    return false;
 
+}
 
 /* Hides request view. */
 
@@ -485,31 +611,61 @@
   document.getElementById('cover').style.display = 'none'
 }
 
+/* Dynamically load JavaScript files */
+
+function load_script(sname, callback) {
+
+  /* Remove previously loaded scripts */
+  var old_script = document.getElementsByName('tmp_script');
+
+  for (var i = 0; i < old_script.length; i++) {
+    document.removeChild(old_script[i]);
+  }
+
+  var head = document.getElementsByTagName('head')[0];
+  var script = document.createElement('script');
+  script.type = 'text/javascript';
+  script.id = sname;
+  script.name = 'tmp_script';
+  script.src = sname;
+  script.onload = callback;
+  script.onerror = callback;
+  head.appendChild(script);
+
+}
 
 /* Loads issues, children for a node, renders HTML. */
 
+var g_add_html = '';
+var g_tid = 0;
+var g_dir = '';
+
 function load_node(dir, tid) {
-  var x = new XMLHttpRequest();
+  g_dir = dir;
+  g_tid = tid;
   var t = document.getElementById('c_' + tid);
-  var add_html = '';
 
-  x.open('GET', dir + 'child_index.js', false);
-  x.send(null);
-  eval(x.responseText);
-
-  x.open('GET', dir + 'issue_index.js', false);
-  x.send(null);
-  eval(x.responseText);
+  load_script(dir + 'child_index.js', function () {});
 
   if (diff_mode) {
-    x.open('GET', dir + 'diff_data.js', false);
-    x.send(null);
-    eval(x.responseText);
+    load_script(dir + 'issue_index.js', function () {});
+    load_script(dir + 'diff_data.js', load_issues);
+  } else {
+    load_script(dir + 'issue_index.js', load_issues);
   }
 
-  delete x;
+}
+
+/* Function callback to render the node data */
+
+function load_issues() {
 
-  next_opacity('c_' + tid, 0);
+  var t = document.getElementById('c_' + g_tid);
+
+  next_opacity('c_' + g_tid, 0);
+
+  var add_html = '';
+  var dir = g_dir;
 
   if (issue.length > 0)
     add_html += '
\n'; @@ -552,8 +708,14 @@ add_html += '
  • ' + 'Fetch result: ' + i2.error + '
    '; } - - if (i2.extra.length > 0) add_html += '
    Memo: ' + H(i2.extra) + '
    \n'; + + if (i2.extra.length > 0) { + add_html += '
    Memo: ' + H(i2.extra); + if (i2.sid.length > 0 && i2.sid > 0) { + add_html += ' (sig: ' + i2.sid + ')'; + } + add_html += '
    \n'; + } } @@ -768,9 +930,14 @@ '[
    show trace ' + '+ ]\n'; - if (i.samples[sno].extra && i.samples[sno].extra.length > 0) - add_html += '
    Memo: ' + H(i.samples[sno].extra) + '
    \n'; + if (i.samples[sno].extra && i.samples[sno].extra.length > 0) { + add_html += '
    Memo: ' + H(i.samples[sno].extra); + if (i.samples[sno].sid && i.samples[sno].sid > 0) { + add_html += ' (sig: ' + i.samples[sno].sid + ')'; + } + add_html += '
    \n'; + } } add_html += '\n'; diff -Nru skipfish-2.02b/config/example.conf skipfish-2.10b/config/example.conf --- skipfish-2.02b/config/example.conf 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/config/example.conf 2012-12-04 13:27:54.000000000 +0000 @@ -0,0 +1,188 @@ + +###################################### +## Reporting options +################################## + +# Output to this directory +output = CHANGEME + +# Toggle mixed content reporting +log-mixed-content = false + +# Toggle logging of all external URLs +log-external-urls = false + +# Enable extra cache related logging +log-cache-mismatches = false + +# Turn off console statistics reporting +#quiet = false + +# Increase verbosity of runtime reporting +#verbose = false + +###################################### +## Crawler user agent options +################################## + +# Pretend that 'domain' resolves to 'IP' +#host = domain=IP + +# Specify header values that will be send with every request +#header = headername=value +#header = X-Scanner=skipfish + +# Specify which one of the pre-defined user agents to use (i|p|f). +user-agent = i + +# Set cookie value and send it with every request +#cookie = name1=value1 +#cookie = name2=value3 + +# Reject any new cookies +reject-cookies = false + +###################################### +## Authentication options +################################## + +# Specify the location of the login form +#auth-form = http://example.org/login.php + +# Specify the username and password that you want to authenticate +# with. It's advised to use throw away (test) accounts. +#auth-user = myuser +#auth-pass = mypass + +# Specify the credential field names when not detected by skipfish. +#auth-user-field = user-field-name +#auth-pass-field = pass-field-name + +# The URL to test is the scan is authenticated. +#auth-verify-url = http://example.org/show-profile.php + +# In some cases, you might have to specify the location to which the +# form data has to be submitted. +#auth-form-target + +# Specify credentials for basic HTTP authentication +#auth = user:pass + + +###################################### +## Crawler scope / depth options +################################## + +# Maximum crawl tree depth +max-crawl-depth = 16 + +# Maximum children to index per node +max-crawl-child = 512 + +# Maximum descendants to index per branch +max-crawl-descendants = 8192 + +# Max total number of requests to send +max-request-total = 100000000 + +# Max requests per second +#max-request-rate = 200 + +# Node and link crawl probability +crawl-probability = 100 + +# Repeat probabilistic scan with given seed +#seed = 0xXXXXXX + +# Only follow URLs matching 'string' +#include-string = /want/ + +# Exclude URLs matching 'string' +#exclude-string = /want-not/ + +# Crawl cross-site links to another domain +#include-domain = scan.also.example.org + +# Trust, but do not crawl, another domain +#trust-domain = .google-analytics.com + +# Do not parse HTML, etc, to find new links +#no-html-parsing = false + +# Do not descend into 5xx locations +skip-error-pages = false + +# Add new form auto-fill rule +#form-value = field=value + +###################################### +## Dictionary management +################################## + +# The read-only wordlist that is used for bruteforcing +wordlist = dictionaries/medium.wl + +# The read-write wordlist and where learned keywords will be written +# for future scans. +#rw-wordlist = my-wordlist.wl + +# Disable extension fuzzing +no-extension-brute = false + +# Disable keyword learning +no-keyword-learning = false + +###################################### +## Performance options +################################## + +# Max simultaneous TCP connections, global +max-connections = 40 + +# Max simultaneous connections, per target IP +max-host-connections = 10 + +# Max number of consecutive HTTP errors +max-failed-requests = 100 + +# Total request response timeout +request-timeout = 20 + +# Individual network I/O timeout +network-timeout = 10 + +# Timeout on idle HTTP connections +idle-timeout = 10 + +# Response size limit in bytes +response-size = 400000 + +# Do not keep binary responses for reporting +discard-binary = true + +# Flush request / response data immediately to disk +flush-to-disk = false + +# Stop scanning after the given duration h:m:s +#scan-timeout = h:m:s + +###################################### +## Detection / inject options +################################## + +# Specify the signatures file location. To disable signatures, specify /dev/null. +signatures = signatures/signatures.conf + +# Enable or disable specific injection tests +#checks-toggle + +# Disable all injection tests which means the scan will focus on crawling, +# bruteforcing and passively detect security issues via signatures. +no-injection-tests = false + +# Ignore this parameter in the scan +#skip-parameter = search + +# Do not submit forms +no-form-submits = false + diff -Nru skipfish-2.02b/config.h skipfish-2.10b/config.h --- skipfish-2.02b/config.h 2011-07-03 08:53:34.000000000 +0000 +++ skipfish-2.10b/config.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,263 +0,0 @@ -/* - skipfish - configurable settings - -------------------------------- - - Author: Michal Zalewski - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#ifndef _HAVE_CONFIG_H -#define _HAVE_CONFIG_H - -#define USE_COLOR 1 /* Use terminal colors */ - -#define SHOW_SPLASH 1 /* Annoy user with a splash screen */ - -/* Define this to enable experimental HTTP proxy support, through the -J - option in the command line. This mode will not work as expected for - HTTPS requests at this time - sorry. */ - -// #define PROXY_SUPPORT 1 - -/* Default paths to runtime files: */ - -#define ASSETS_DIR "assets" -#define DEF_WORDLIST "skipfish.wl" - -/* Various default settings for HTTP client (cmdline override): */ - -#define MAX_CONNECTIONS 40 /* Simultaneous connection cap */ -#define MAX_CONN_HOST 10 /* Per-host connction cap */ -#define MAX_REQUESTS 1e8 /* Total request count cap */ -#define MAX_FAIL 100 /* Max consecutive failed requests */ -#define RW_TMOUT 10 /* Individual network R/W timeout */ -#define RESP_TMOUT 20 /* Total request time limit */ -#define IDLE_TMOUT 10 /* Connection tear down threshold */ -#define SIZE_LIMIT 200000 /* Response size cap */ -#define MAX_GUESSES 256 /* Guess-based wordlist size limit */ - -/* HTTP client constants: */ - -#define MAX_URL_LEN 1024 /* Maximum length of an URL */ -#define MAX_DNS_LEN 255 /* Maximum length of a host name */ -#define READ_CHUNK 4096 /* Read buffer size */ - -/* Define this to use FILO, rather than FIFO, scheduling for new requests. - FILO ensures a more uniform distribution of requests when fuzzing multiple - directories at once, but may reduce the odds of spotting some stored - XSSes, and increase memory usage a bit. */ - -// #define QUEUE_FILO 1 - -/* Dummy file to upload to the server where possible. */ - -#define DUMMY_EXT "gif" -#define DUMMY_FILE "GIF89a,\x01" -#define DUMMY_MIME "image/gif" - -/* Allocator settings: */ - -#define MAX_ALLOC 0x50000000 /* Refuse larger allocations. */ - -/* Configurable settings for crawl database (cmdline override): */ - -#define MAX_DEPTH 16 /* Maximum crawl tree depth */ -#define MAX_CHILDREN 512 /* Maximum children per tree node */ -#define MAX_DESCENDANTS 8192 /* Maximum descendants per branch */ -#define MAX_SAMENAME 3 /* Identically named path nodes */ - -/* Crawl / analysis constants: */ - -#define MAX_WORD 64 /* Maximum wordlist item length */ -#define GUESS_PROB 50 /* Guess word addition probability */ -#define WORD_HASH 256 /* Hash table for wordlists */ -#define SNIFF_LEN 1024 /* MIME sniffing buffer size */ -#define MAX_SAMPLES 1024 /* Max issue / MIME samples */ -#define MAX_JS_WHITE 16 /* Maximum JS wspaces before id */ - -/* Page fingerprinting constants: */ - -#define FP_SIZE 10 /* Page fingerprint size */ -#define FP_MAX_LEN 15 /* Maximum word length to count */ -#define FP_T_REL 5 /* Relative matching tolerance (%) */ -#define FP_T_ABS 6 /* Absolute matching tolerance */ -#define FP_B_FAIL 3 /* Max number of failed buckets */ - -#define BH_CHECKS 15 /* Page verification check count */ - -/* Crawler / probe constants: */ - -#define BOGUS_FILE "sfi9876" /* Name that should not exist */ -#define BOGUS_EXT "sfish" /* Nonsensical file extension */ -#define BOGUS_PARAM "9876sfi" /* Meaningless parameter */ -#define MAX_404 4 /* Maximum number of 404 sigs */ -#define PAR_MAX_DIGITS 6 /* Max digits in a fuzzable int */ -#define PAR_INT_FUZZ 100 /* Fuzz by + / - this much */ - -#ifdef QUEUE_FILO -#define DICT_BATCH 100 /* Brute-force queue block */ -#else -#define DICT_BATCH 300 /* Brute-force queue block */ -#endif /* ^QUEUE_FILO */ - -/* Single query for IPS detection - Evil Query of Doom (tm). */ - -#define IPS_TEST \ - "?_test1=c:\\windows\\system32\\cmd.exe" \ - "&_test2=/etc/passwd" \ - "&_test3=|/bin/sh" \ - "&_test4=(SELECT * FROM nonexistent) --" \ - "&_test5=>/no/such/file" \ - "&_test6=" \ - "&_test7=javascript:alert(1)" - -/* A benign query with a similar character set to compare with EQoD. */ - -#define IPS_SAFE \ - "?_test1=ccddeeeimmnossstwwxy.:\\\\\\" \ - "&_test2=acdepsstw//" \ - "&_test3=bhins//" \ - "&_test4=CEEFLMORSTeeinnnosttx-*" \ - "&_test5=cefhilnosu///" \ - "&_test6=acceiilpprrrssttt1)(" \ - "&_test7=aaaceijlprrsttv1):(" - -/* XSRF token detector settings: */ - -#define XSRF_B16_MIN 8 /* Minimum base10/16 token length */ -#define XSRF_B16_MAX 45 /* Maximum base10/16 token length */ -#define XSRF_B16_NUM 2 /* ...minimum digit count */ -#define XSRF_B64_MIN 6 /* Minimum base32/64 token length */ -#define XSRF_B64_MAX 32 /* Maximum base32/64 token length */ -#define XSRF_B64_NUM 1 /* ...minimum digit count && */ -#define XSRF_B64_CASE 2 /* ...minimum uppercase count */ -#define XSRF_B64_NUM2 3 /* ...digit count override */ -#define XSRF_B64_SLASH 2 /* ...maximum slash count */ - -#ifdef _VIA_DATABASE_C - -/* Domains we always trust (identical to -B options). These entries do not - generate cross-domain content inclusion warnings. NULL-terminated. */ - -static const char* always_trust_domains[] = { - ".google-analytics.com", - ".googleapis.com", - ".googleadservices.com", - ".googlesyndication.com", - "www.w3.org", - 0 -}; - -#endif /* _VIA_DATABASE_C */ - -#ifdef _VIA_ANALYSIS_C - -/* NULL-terminated list of JSON-like response prefixes we consider to - be sufficiently safe against cross-site script inclusion (courtesy - ratproxy). */ - -static const char* json_safe[] = { - "while(1);", /* Parser looping */ - "while (1);", /* ... */ - "while(true);", /* ... */ - "while (true);", /* ... */ - "&&&", /* Parser breaking */ - "//OK[", /* Line commenting */ - "{\"", /* Serialized object */ - "{{\"", /* Serialized object */ - "throw 1; <", /* Magical combo */ - ")]}'", /* Recommended magic */ - 0 -}; - -/* NULL-terminated list of known valid charsets. Charsets not on the list are - considered dangerous (as they may trigger charset sniffing). - - Note that many common misspellings, such as "utf8", are not valid and NOT - RECOGNIZED by browsers, leading to content sniffing. Do not add them here. - - Also note that SF does not support encoding not compatible with US ASCII - transport (e.g., UTF-16, UTF-32). Lastly, variable-length encodings - other than utf-8 may have character consumption issues that are not - tested for at this point. */ - -static const char* valid_charsets[] = { - "utf-8", /* Valid 8-bit safe Unicode */ - "iso8859-1", /* Western Europe */ - "iso8859-2", /* Central Europe */ - "iso8859-15", /* New flavor of ISO8859-1 */ - "iso8859-16", /* New flavor of ISO8859-2 */ - "iso-8859-1", /* Browser-supported misspellings */ - "iso-8859-2", /* - */ - "iso-8859-15", /* - */ - "iso-8859-16", /* - */ - "windows-1252", /* Microsoft's Western Europe */ - "windows-1250", /* Microsoft's Central Europe */ - "us-ascii", /* Old school but generally safe */ - "koi8-r", /* 8-bit and US ASCII compatible */ - 0 -}; - - -/* Default form auto-fill rules - used to pair up form fields with fun - values! Do not attempt security attacks here, though - this is to maximize - crawl coverage, not to exploit anything. The last item must have a name - of NULL, and the value will be used as a default option when no other - matches found. */ - -static const char* form_suggestion[][2] = { - - { "phone" , "6505550100" }, /* Reserved */ - { "zip" , "94043" }, - { "first" , "John" }, - { "last" , "Smith" }, - { "name" , "Smith" }, - { "mail" , "skipfish@example.com" }, - { "street" , "1600 Amphitheatre Pkwy" }, - { "city" , "Mountain View" }, - { "state" , "CA" }, - { "country" , "US" }, - { "language" , "en" }, - { "company" , "ACME" }, - { "search" , "skipfish" }, - { "login" , "skipfish" }, - { "user" , "skipfish" }, - { "nick" , "skipfish" }, - { "pass" , "skipfish" }, - { "pwd" , "skipfish" }, - { "year" , "2010" }, - { "card" , "4111111111111111" }, /* Reserved */ - { "code" , "000" }, - { "cvv" , "000" }, - { "expir" , "1212" }, - { "ssn" , "987654320" }, /* Reserved */ - { "url" , "http://example.com/?sfish_form_test" }, - { "site" , "http://example.com/?sfish_form_test" }, - { "domain" , "example.com" }, - { "search" , "a" }, - { "comment" , "skipfish" }, - { "desc" , "skipfish" }, - { "title" , "skipfish" }, - { "subject" , "skipfish" }, - { "message" , "skipfish" }, - { NULL , "1" } - -}; - -#endif /* _VIA_ANALYSIS_C */ - -#endif /* ! _HAVE_CONFIG_H */ diff -Nru skipfish-2.02b/crawler.c skipfish-2.10b/crawler.c --- skipfish-2.02b/crawler.c 2011-07-06 19:59:51.000000000 +0000 +++ skipfish-2.10b/crawler.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,3398 +0,0 @@ -/* - skipfish - crawler state machine - -------------------------------- - - Includes dictionary and security injection logic. - - Author: Michal Zalewski - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#define _VIA_CRAWLER_C - -#include "debug.h" -#include "config.h" -#include "types.h" -#include "http_client.h" -#include "database.h" -#include "crawler.h" -#include "analysis.h" - -u32 crawl_prob = 100; /* Crawl probability (1-100%) */ -u8 no_fuzz_ext; /* Don't fuzz extensions for dirs */ -u8 no_500_dir; /* Don't crawl 500 directories */ -u8 delete_bin; /* Don't keep binary responses */ - -/* - - ************************* - **** GENERAL HELPERS **** - ************************* - - Assorted functions used by all the crawl callbacks for manipulating - requests, parsing responses, etc. - - */ - - -/* Classifies a response, with a special handling of "unavailable" and - "gateway timeout" codes. */ - -#define FETCH_FAIL(_res) ((_res)->state != STATE_OK || (_res)->code == 503 || \ - (_res)->code == 504) - - -/* Dumps request, response (for debugging only). */ - -u8 show_response(struct http_request* req, struct http_response* res) { - - dump_http_request(req); - - if (FETCH_FAIL(res)) { - SAY("^^^ REQUEST SHOWN ABOVE CAUSED ERROR: %d ^^^\n", res->state); - return 0; - } - - dump_http_response(res); - - return 0; /* Do not keep req/res */ - -} - - -/* Strips trailing / from a directory request, optionally replaces it with - a new value. */ - -static void replace_slash(struct http_request* req, u8* new_val) { - u32 i; - - for (i=0;ipar.c;i++) - if (req->par.t[i] == PARAM_PATH && !req->par.n[i] && !req->par.v[i][0]) { - if (new_val) { - ck_free(req->par.v[i]); - req->par.v[i] = ck_strdup(new_val); - } else req->par.t[i] = PARAM_NONE; - return; - } - - /* Could not find a slash segment - create a new segment instead. */ - - set_value(PARAM_PATH, 0, new_val, -1, &req->par); - -} - - -/* Releases children for crawling (called once parent node had 404, IPS - probes done, etc). Note that non-directories might have locked - children too. */ - -static void unlock_children(struct pivot_desc* pv) { - u32 i; - - DEBUG_HELPER(pv); - - for (i=0;ichild_cnt;i++) - if (pv->child[i]->state == PSTATE_PENDING) { - - DEBUG_PIVOT("Unlocking", pv->child[i]); - - pv->child[i]->state = PSTATE_FETCH; - - if (!pv->child[i]->res) async_request(pv->child[i]->req); - else switch (pv->child[i]->type) { - - case PIVOT_DIR: dir_retrieve_check(pv->req, pv->res); break; - case PIVOT_PARAM: - case PIVOT_FILE: file_retrieve_check(pv->req, pv->res); break; - case PIVOT_UNKNOWN: unknown_retrieve_check(pv->req, pv->res); break; - default: FATAL("Unknown pivot type '%u'", pv->type); - - } - - } - -} - - -/* Handles response error for callbacks in a generalized manner. If 'stop' is - 1, marks the entire pivot as busted, unlocks children. */ - -static void handle_error(struct http_request* req, struct http_response* res, - u8* desc, u8 stop) { - - DEBUG_CALLBACK(req, res); - - if (res->state == STATE_SUPPRESS) { - problem(PROB_LIMITS, req, res, (u8*)"Too many previous fetch failures", - req->pivot, 0); - } else { - problem(PROB_FETCH_FAIL, req, res, desc, req->pivot, 0); - } - - if (stop) { - req->pivot->state = PSTATE_DONE; - unlock_children(req->pivot); - } - -} - - -/* Finds nearest "real" directory parent, so that we can consult it for 404 - signatures, etc. Return NULL also if dir found, but signature-less. */ - -static struct pivot_desc* dir_parent(struct pivot_desc* pv) { - struct pivot_desc* ret; - - ret = pv->parent; - - while (ret && ret->type != PIVOT_DIR && ret->type != PIVOT_SERV) - ret = ret->parent; - - if (ret && !ret->r404_cnt) return NULL; - return ret; -} - - -/* Deletes any cached requests and responses stored by injection probes. */ - -static void destroy_misc_data(struct pivot_desc* pv, - struct http_request* self) { - u32 i; - - for (i=0;imisc_req[i] != self) { - - if (pv->misc_req[i]) - destroy_request(pv->misc_req[i]); - - if (pv->misc_res[i]) - destroy_response(pv->misc_res[i]); - - } - - pv->misc_req[i] = NULL; - pv->misc_res[i] = NULL; - - } - - pv->misc_cnt = 0; - -} - - - -/* - - *************************************** - **** ASSORTED FORWARD DECLARATIONS **** - *************************************** - - */ - -static u8 dir_404_check(struct http_request*, struct http_response*); -static u8 dir_up_behavior_check(struct http_request*, struct http_response*); -static u8 dir_ips_check(struct http_request*, struct http_response*); -static void inject_start(struct pivot_desc*); -static void inject_start2(struct pivot_desc*); -static void dir_dict_start(struct pivot_desc*); -static u8 dir_dict_check(struct http_request*, struct http_response*); -static u8 dir_dict_bogus_check(struct http_request*, struct http_response*); -static u8 put_upload_check(struct http_request*, struct http_response*); -static u8 inject_behavior_check(struct http_request*, struct http_response*); -static u8 inject_dir_listing_check(struct http_request*, struct http_response*); -static u8 inject_xml_check(struct http_request*, struct http_response*); -static u8 inject_shell_check(struct http_request*, struct http_response*); -static u8 inject_xss_check(struct http_request*, struct http_response*); -static u8 inject_prologue_check(struct http_request*, struct http_response*); -static u8 inject_redir_check(struct http_request*, struct http_response*); -static u8 inject_split_check(struct http_request*, struct http_response*); -static u8 inject_sql_check(struct http_request*, struct http_response*); -static u8 inject_format_check(struct http_request*, struct http_response*); -static u8 inject_integer_check(struct http_request*, struct http_response*); -static void param_numerical_start(struct pivot_desc*); -static u8 param_behavior_check(struct http_request*, struct http_response*); -static u8 unknown_retrieve_check2(struct http_request*, struct http_response*); -static u8 param_numerical_check(struct http_request*, struct http_response*); -static u8 param_dict_check(struct http_request*, struct http_response*); -static u8 param_trylist_check(struct http_request*, struct http_response*); -static void param_dict_start(struct pivot_desc*); -static void param_start(struct pivot_desc*); -static void inject_done(struct pivot_desc*); -static u8 param_ognl_check(struct http_request*, struct http_response*); -static u8 dir_case_check(struct http_request* req, struct http_response* res); - - -/* - - ****************************** - **** ACTUAL STATE MACHINE **** - ****************************** - - The following is a rough sketch of what's going on here. - - == Pivot creation states == - - Path elements: - - root - PSTATE_DONE, no callback - - server - PSTATE_FETCH, dir_retrieve_check - - dir - PSTATE_FETCH, dir_retrieve_check - PSTATE_PENDING if parent state <= PSTATE_IPS_CHECK - - last seg - PSTATE_FETCH, unknown_retrieve_check - PSTATE_PENDING if parent state <= PSTATE_IPS_CHECK - - file - PSTATE_FETCH, file_retrieve_check - PSTATE_PENDING if parent state <= PSTATE_IPS_CHECK - - If element in name=value format, also add value to pivot's trylist. - Call param_trylist_start if pivot already in PSTATE_DONE. - - Query elements: - - PSTATE_FETCH, file_retrieve_check - PSTATE_PENDING if parent dir state <= PSTATE_IPS_CHECK - - Add value to pivot's trylist. Call param_trylist_start if pivot already - in PSTATE_DONE. - - == Initial fetch actions == - - unknown_retrieve_check: - - Initial retrieval of an unknown path element. - - File not found: unlock_children, -> param_start - Otherwise: -> file_retrieve_check or -> unknown_retrieve_check2 - - unknown_retrieve_check2: - - Secondary check to detect dir-like behavior (for unknown_retrieve_check). - - -> dir_retrieve_check or -> file_retrieve_check - - file_retrieve_check: - - Initial retrieval of a file, query parameter, or so. - - -> secondary_ext_start (async) - -> dir_case_start (async) - unlock_children - - Query value pivot: -> param_start - Other pivots: PSTATE_CHILD_INJECT, -> inject_start - - dir_retrieve_check: - - Initial retrival of a directory or PATHINFO resource. - - -> secondary_ext_start (async) - PSTATE_404_CHECK, -> dir_404_check - - == Basic directory checks == - - dir_404_check: - - Performs basic 404 signature detection. Calls itself in a loop. - - -> dir_case_start (async) - PSTATE_PARENT_CHECK, -> dir_up_behavior_check - - dir_up_behavior_check: - - Checks if path hierarchy is honored by the server. - - PSTATE_IPS_CHECK, -> dir_ips_check - - dir_ips_check: - - Checks for IPS-like behavior. - - unlock_children - PSTATE_CHILD_INJECT, -> inject_start - - dir_case_start: - - Asynchronous handler to check directory case-sensitivity. - - -> dir_case_check - - dir_case_check: - - Case sensitivity callback. No further branching. - - == Parameter behavior (name=val pivots only) == - - param_start: - - Initial parametric testing entry point. - - Non-fuzzable parameter: PSTATE_DONE - Otherwise: PSTATE_PAR_CHECK, -> param_behavior_check - - param_behavior_check: - - Parameter behavior check callback. - - -> param_ognl_check (async) - PSTATE_PAR_INJECT, -> inject_start - - param_ognl_check: - - Asynchronous OGNL behavior check. No further branching. - - == Injection attacks == - - inject_start: - - Injection testing entry point. - - Directory: -> put_upload_check - Other cases: -> inject_start2 - - put_upload_check: - - Check for PUT upload vulnerabilities (dir only). - - -> inject_start2 - - inject_start2: - - Injection testing entry point for non-dir nodes. - - -> inject_behavior_check - - inject_behavior_check: - - Parameter behavior consistency test. - - Bad pivot: -> inject_done - OK pivot: -> inject_dir_listing_check - - inject_dir_listing_check: - - Directory listing probe. - - -> inject_xml_check - - inject_xml_check: - - Server-side XML injection probe. - - -> inject_shell_check - - inject_shell_check: - - Shell injection probe. - - -> inject_xss_check - - inject_xss_check: - - Reflected XSS probe. - - -> inject_prologue_check - - inject_prologue_check: - - Attacker-controlled response check. - - -> inject_redir_check - - inject_redir_check: - - Probe for redirection vulnerabilities. - - -> inject_split_check - - inject_split_check: - - Header splitting probe. - - -> inject_sql_check - - inject_sql_check: - - SQL injection probe. - - -> inject_format_check - - inject_format_check: - - Format string vulnerability probe. - - -> inject_integer_check - - inject_integer_check: - - Integer overflow probe. - - -> inject_done - - inject_done: - - Injection testing wrap-up. - - Path element: PSTATE_CHILD_DICT, -> dir_dict_start if fuzzable dir - -> param_start if not dir or no 404 sigs - PSTATE_DONE if not allowed or varies randomly - - Other parametric: -> param_numerical_start - PSTATE_DONE if varies randomly - - == Parameter brute-force (name=val only) == - - param_numerical_start: - - Begin numerical brute-force if applicable. - - Numerical: PSTATE_PAR_NUMBER, -> param_numerical_check - Otherwise: PSTATE_PAR_DICT, -> param_dict_start - - param_numerical_check: - - Numerical brute-force callback. May store results as PIVOT_VALUE / - PSTATE_DONE nodes. - - -> secondary_ext_start (async) - PSTATE_PAR_DICT, -> param_dict_start - - param_dict_start: - - Dictionary brute-force init / resume. - - Out of keywords: -> param_trylist_start - Otherwise: -> param_dict_check - - param_dict_check: - - Dictionary brute-force callback. May store results as PIVOT_VALUE / - PSTATE_DONE nodes. - - -> secondary_ext_start (async) - Loops to -> param_trylist_start if not called via secondary_ext_check - - param_trylist_start: - - Begins trylist fuzzing, or resumes from offset. - - Bad pivot or no more keywords: PSTATE_DONE - Otherwise: PSTATE_PAR_TRYLIST, -> param_trylist_check - - param_trylist_check: - - Trylist dictionary callback. May store results as PIVOT_VALUE / PSTATE_DONE - nodes. - - -> secondary_ext_start (async) - PSTATE_DONE - - == Directory brute-force == - - dir_dict_start: - - Dictionary brute-force init / resume. - - Bad pivot or no more keywords: -> param_start - Otherwise: -> dir_dict_bogus_check - - dir_dict_bogus_check: - - Check for good keyword candidates, proceed with extension fuzzing. - -> dir_dict_check - - Loops over to -> dir_dict_start - - dir_dict_check: - - Dictionary brute-force callback. - - Loops over to -> dir_dict_start if not called via secondary_ext_start. - - == Secondary extension brute-force == - - secondary_ext_start: - - Asynchronous secondary extension check - - Query: -> param_dict_check - Path: -> dir_dict_check - - */ - - -static void dir_case_start(struct pivot_desc* pv) { - u32 i, len; - s32 last = -1; - struct http_request* n; - - if (pv->parent->c_checked) return; - - DEBUG_HELPER(pv); - - for (i=0;ireq->par.c;i++) - if (PATH_SUBTYPE(pv->req->par.t[i]) && pv->req->par.v[i][0]) last = i; - - if (last < 0) return; - - len = strlen((char*)pv->req->par.v[last]); - - for (i=0;ireq->par.v[last][i])) break; - - if (i == len) return; - - pv->parent->c_checked = 1; - - n = req_copy(pv->req, pv, 1); - n->callback = dir_case_check; - - /* Change case. */ - - n->par.v[last][i] = islower(n->par.v[last][i]) ? toupper(n->par.v[last][i]) : - tolower(n->par.v[last][i]); - - DEBUG("* candidate parameter: %s -> %s\n", pv->req->par.v[last], - n->par.v[last]); - - async_request(n); - -} - - -static u8 dir_case_check(struct http_request* req, - struct http_response* res) { - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - RPAR(req)->c_checked = 0; - return 0; - } - - if (!same_page(&res->sig, &RPRES(req)->sig)) - RPAR(req)->csens = 1; - - return 0; - -} - - -static void secondary_ext_start(struct pivot_desc* pv, struct http_request* req, - struct http_response* res, u8 is_param) { - - u8 *base_name, *fpos, *lpos, *ex; - s32 tpar = -1, i = 0, spar = -1; - - DEBUG_HELPER(req->pivot); - DEBUG_HELPER(pv); - - if (is_param) { - - tpar = pv->fuzz_par; - - } else { - - /* Find last path segment other than NULL-''. */ - for (i=0;ipar.c;i++) - if (PATH_SUBTYPE(req->par.t[i])) { - if ((req->par.t[i] == PARAM_PATH && - !req->par.n[i] && !req->par.v[i][0])) spar = i; else tpar = i; - } - - } - - if (tpar < 0) return; - - base_name = req->par.v[tpar]; - - /* Reject parameters with no '.' (unless in no_fuzz_ext mode), - with too many '.'s, or '.' in an odd location. */ - - fpos = (u8*)strchr((char*)base_name, '.'); - - if (!no_fuzz_ext || fpos) - if (!fpos || fpos == base_name || !fpos[1]) return; - - lpos = (u8*)strrchr((char*)base_name, '.'); - - if (fpos != lpos) return; - - i = 0; - - while ((ex = wordlist_get_extension(i, 0))) { - u8* tmp = ck_alloc(strlen((char*)base_name) + strlen((char*)ex) + 2); - u32 c; - - /* Avoid foo.bar.bar. */ - - if (lpos && !strcasecmp((char*)lpos + 1, (char*)ex)) { - i++; - ck_free(tmp); - continue; - } - - sprintf((char*)tmp, "%s.%s", base_name, ex); - - /* Matching child? If yes, don't bother. */ - - for (c=0;cchild_cnt;c++) - if (!((is_c_sens(pv) ? strcmp : strcasecmp)((char*)tmp, - (char*)pv->child[c]->name))) break; - - /* Matching current node? */ - - if (pv->fuzz_par != -1 && - !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)tmp, - (char*)pv->req->par.v[pv->fuzz_par]))) c = ~pv->child_cnt; - - if (c == pv->child_cnt) { - struct http_request* n = req_copy(req, pv, 1); - - /* Remove trailing slash if present. */ - if (spar >= 0) n->par.t[spar] = PARAM_NONE; - - ck_free(n->par.v[tpar]); - n->par.v[tpar] = tmp; - - n->user_val = 1; - - memcpy(&n->same_sig, &res->sig, sizeof(struct http_sig)); - - n->callback = is_param ? param_dict_check : dir_dict_check; - /* Both handlers recognize user_val == 1 as a special indicator. */ - async_request(n); - - } else ck_free(tmp); - - i++; - } - -} - - -/* Internal helper macros: */ - -#define TPAR(_req) ((_req)->par.v[(_req)->pivot->fuzz_par]) - -#define SET_VECTOR(_state, _req, _str) do { \ - if (_state == PSTATE_CHILD_INJECT) { \ - replace_slash((_req), (u8*)_str); \ - } else { \ - ck_free(TPAR(_req)); \ - TPAR(_req) = ck_strdup((u8*)_str); \ - } \ - } while (0) - -#define APPEND_VECTOR(_state, _req, _str) do { \ - if (_state == PSTATE_CHILD_INJECT) { \ - replace_slash((_req), (u8*)_str); \ - } else { \ - u8* _n = ck_alloc(strlen((char*)TPAR(_req)) + strlen((char*)_str) + 1); \ - sprintf((char*)_n, "%s%s", TPAR(_req), _str); \ - ck_free(TPAR(_req)); \ - TPAR(_req) = _n; \ - } \ - } while (0) - - -static void inject_start(struct pivot_desc* pv) { - - DEBUG_HELPER(pv); - - /* Do a PUT probe, but only on directories proper. */ - - if (pv->type == PIVOT_DIR || pv->type == PIVOT_SERV) { - struct http_request* n; - n = req_copy(pv->req, pv, 1); - if (n->method) ck_free(n->method); - n->method = ck_strdup((u8*)"PUT"); - n->callback = put_upload_check; - replace_slash(n, (u8*)("PUT-" BOGUS_FILE)); - async_request(n); - } else { - inject_start2(pv); - } - -} - - -static u8 put_upload_check(struct http_request* req, - struct http_response* res) { - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during PUT checks", 0); - } else { - if (res->code >= 200 && res->code < 300 && - !same_page(&RPRES(req)->sig, &res->sig)) { - problem(PROB_PUT_DIR, req, res, 0, req->pivot, 0); - } - } - - inject_start2(req->pivot); - return 0; - -} - - -static void inject_start2(struct pivot_desc* pv) { - struct http_request* n; - u32 i; - - DEBUG_HELPER(pv); - - pv->misc_cnt = BH_CHECKS; - - for (i=0;ireq, pv, 1); - n->callback = inject_behavior_check; - n->user_val = i; - async_request(n); - } -} - - -static u8 inject_behavior_check(struct http_request* req, - struct http_response* res) { - struct http_request* n; - u32 orig_state = req->pivot->state; - u8* tmp = NULL; - - /* pv->state may change after async_request() calls in - insta-fail mode, so we should cache accordingly. */ - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during page variability checks", 0); - } else { - if (!same_page(&RPRES(req)->sig, &res->sig)) { - req->pivot->res_varies = 1; - problem(PROB_VARIES, req, res, 0, req->pivot, 0); - } - } - - if ((--req->pivot->misc_cnt)) return 0; - - /* If response fluctuates, do not perform any injection checks at all. */ - - if (req->pivot->res_varies) { - inject_done(req->pivot); - return 0; - } - - /* Directory listing - 4 requests. The logic here is a bit - different for parametric targets (which are easy to examine with - a ./ trick) and directories (which require a more complex - comparison). */ - - req->pivot->misc_cnt = 0; - - n = req_copy(req->pivot->req, req->pivot, 1); - - if (orig_state == PSTATE_CHILD_INJECT) { - replace_slash(n, (u8*)"."); - set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->par); - } else { - tmp = ck_alloc(strlen((char*)TPAR(n)) + 5); - sprintf((char*)tmp, ".../%s", TPAR(n)); - ck_free(TPAR(n)); - TPAR(n) = ck_strdup(tmp); - req->pivot->i_skip_add = 6; - } - - n->callback = inject_dir_listing_check; - n->user_val = 0; - async_request(n); - - n = req_copy(req->pivot->req, req->pivot, 1); - - if (orig_state == PSTATE_CHILD_INJECT) { - replace_slash(n, (u8*)".sf"); - set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->par); - } else { - ck_free(TPAR(n)); - TPAR(n) = ck_strdup(tmp + 2); - } - - n->callback = inject_dir_listing_check; - n->user_val = 1; - async_request(n); - - n = req_copy(req->pivot->req, req->pivot, 1); - - if (orig_state == PSTATE_CHILD_INJECT) { - replace_slash(n, (u8*)"\\.\\"); - } else { - tmp[3] = '\\'; - ck_free(TPAR(n)); - TPAR(n) = ck_strdup(tmp); - } - - n->callback = inject_dir_listing_check; - n->user_val = 2; - async_request(n); - - n = req_copy(req->pivot->req, req->pivot, 1); - - if (orig_state == PSTATE_CHILD_INJECT) { - replace_slash(n, (u8*)"\\.sf\\"); - } else { - ck_free(TPAR(n)); - TPAR(n) = ck_strdup(tmp + 2); - ck_free(tmp); - } - - n->callback = inject_dir_listing_check; - n->user_val = 3; - async_request(n); - - if (orig_state != PSTATE_CHILD_INJECT) { - - n = req_copy(req->pivot->req, req->pivot, 1); - - ck_free(TPAR(n)); - TPAR(n) = ck_strdup((u8*)"../../../../../../../../etc/hosts"); - - n->callback = inject_dir_listing_check; - n->user_val = 4; - async_request(n); - - n = req_copy(req->pivot->req, req->pivot, 1); - - ck_free(TPAR(n)); - TPAR(n) = ck_strdup((u8*)"..\\..\\..\\..\\..\\..\\..\\..\\boot.ini"); - - n->callback = inject_dir_listing_check; - n->user_val = 5; - async_request(n); - - } - - - return 0; - -} - - -static u8 inject_dir_listing_check(struct http_request* req, - struct http_response* res) { - struct http_request* n; - u32 orig_state = req->pivot->state; - - DEBUG_CALLBACK(req, res); - - if (req->pivot->i_skip[0 + req->pivot->i_skip_add]) return 0; - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during directory listing / traversal attacks", 0); - req->pivot->i_skip[0 + req->pivot->i_skip_add] = 1; - goto schedule_next; - } - - req->pivot->misc_req[req->user_val] = req; - req->pivot->misc_res[req->user_val] = res; - - if (req->pivot->i_skip_add) { - if ((++req->pivot->misc_cnt) != 6) return 1; - } else { - if ((++req->pivot->misc_cnt) != 4) return 1; - } - - /* Got all responses. For directories, this is: - - pivot = / - misc[0] = /./ - misc[1] = /.sf/ - misc[2] = \.\ - misc[3] = \.sf\ - - Here, if pivot != misc[0], and misc[0] != misc[1], we probably - managed to list a hidden dir. The same test is carried out for - misc[2] and misc[3]. - - For parameters, this is: - - misc[0] = .../known_val - misc[1] = ./known_val - misc[2] = ...\known_val - misc[3] = .\known_val - misc[4] = ../../../../../../../../etc/hosts - misc[5] = ..\..\..\..\..\..\..\..\boot.ini - - Here, the test is simpler: if misc[1] != misc[0], or misc[3] != - misc[2], we probably have a bug. The same if misc[4] or misc[5] - contain magic strings, but misc[0] doesn't. - - */ - - if (orig_state == PSTATE_CHILD_INJECT) { - - if (MRES(0)->code < 300 && - !same_page(&MRES(0)->sig, &RPRES(req)->sig) && - !same_page(&MRES(0)->sig, &MRES(1)->sig)) { - problem(PROB_DIR_LIST, MREQ(0), MRES(0), - (u8*)"unique response for /./", - req->pivot, 0); - - /* Use pivot's request, rather than MREQ(0), for link scraping; - MREQ(0) contains an "illegal" manually constructed path. */ - - RESP_CHECKS(RPREQ(req), MRES(0)); - } - - if (MRES(2)->code < 300 && - !same_page(&MRES(2)->sig, &RPRES(req)->sig) && - !same_page(&MRES(2)->sig, &MRES(3)->sig)) { - problem(PROB_DIR_LIST, MREQ(2), MRES(2), - (u8*)"unique response for \\.\\", - req->pivot, 0); - RESP_CHECKS(MREQ(2), MRES(2)); - } - - } else { - - if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) { - problem(PROB_DIR_TRAVERSAL, MREQ(1), MRES(1), - (u8*)"responses for ./val and .../val look different", - req->pivot, 0); - RESP_CHECKS(MREQ(0), MRES(0)); - } - - if (!same_page(&MRES(2)->sig, &MRES(3)->sig)) { - problem(PROB_DIR_TRAVERSAL, MREQ(3), MRES(3), - (u8*)"responses for .\\val and ...\\val look different", - req->pivot, 0); - RESP_CHECKS(MREQ(2), MRES(2)); - } - - if (inl_findstr(MRES(4)->payload, (u8*)"127.0.0.1", 512) && - !inl_findstr(MRES(0)->payload, (u8*)"127.0.0.1", 512)) { - problem(PROB_DIR_TRAVERSAL, MREQ(4), MRES(4), - (u8*)"response resembles /etc/hosts", req->pivot, 0); - } - - if (inl_findstr(MRES(5)->payload, (u8*)"[boot loader]", 512) && - !inl_findstr(MRES(0)->payload, (u8*)"[boot loader]", 512)) { - problem(PROB_DIR_TRAVERSAL, MREQ(5), MRES(5), - (u8*)"response resembles c:\\boot.ini", req->pivot, 0); - } - - } - -schedule_next: - - destroy_misc_data(req->pivot, req); - - /* Backend XML injection - 2 requests. */ - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "sfish>'>\">"); - n->callback = inject_xml_check; - n->user_val = 0; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "sfish>'>\">"); - n->callback = inject_xml_check; - n->user_val = 1; - async_request(n); - - return 0; - -} - - -static u8 inject_xml_check(struct http_request* req, - struct http_response* res) { - struct http_request* n; - u32 orig_state = req->pivot->state; - - DEBUG_CALLBACK(req, res); - - if (req->pivot->i_skip[1 + req->pivot->i_skip_add]) return 0; - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during backend XML injection attacks", 0); - req->pivot->i_skip[1 + req->pivot->i_skip_add] = 1; - goto schedule_next; - } - - req->pivot->misc_req[req->user_val] = req; - req->pivot->misc_res[req->user_val] = res; - if ((++req->pivot->misc_cnt) != 2) return 1; - - /* Got all responses: - - misc[0] = valid XML - misc[1] = bad XML - - If misc[0] != misc[1], we probably have XML injection on backend side. */ - - if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) { - problem(PROB_XML_INJECT, MREQ(0), MRES(0), - (u8*)"responses for and look different", - req->pivot, 0); - RESP_CHECKS(MREQ(1), MRES(1)); - } - -schedule_next: - - destroy_misc_data(req->pivot, req); - - /* Shell command injection - 9 requests. */ - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "`true`"); - n->callback = inject_shell_check; - n->user_val = 0; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "`false`"); - n->callback = inject_shell_check; - n->user_val = 1; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "`uname`"); - n->callback = inject_shell_check; - n->user_val = 2; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "\"`true`\""); - n->callback = inject_shell_check; - n->user_val = 3; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "\"`false`\""); - n->callback = inject_shell_check; - n->user_val = 4; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "\"`uname`\""); - n->callback = inject_shell_check; - n->user_val = 5; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "'`true`'"); - n->callback = inject_shell_check; - n->user_val = 6; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "'`false`'"); - n->callback = inject_shell_check; - n->user_val = 7; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "'`uname`'"); - n->callback = inject_shell_check; - n->user_val = 8; - async_request(n); - - return 0; - -} - - -static u8 inject_shell_check(struct http_request* req, - struct http_response* res) { - struct http_request* n; - u32 orig_state = req->pivot->state; - - DEBUG_CALLBACK(req, res); - - if (req->pivot->i_skip[2 + req->pivot->i_skip_add]) return 0; - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during path-based shell injection attacks", 0); - req->pivot->i_skip[2 + req->pivot->i_skip_add] = 1; - goto schedule_next; - } - - req->pivot->misc_req[req->user_val] = req; - req->pivot->misc_res[req->user_val] = res; - if ((++req->pivot->misc_cnt) != 9) return 1; - - /* Got all responses: - - misc[0] = `true` - misc[1] = `false` - misc[2] = `uname` - misc[3] = "`true`" - misc[4] = "`false`" - misc[5] = "`uname`" - misc[6] = '`true`' - misc[7] = "`false`" - misc[8] = '`uname`' - - If misc[0] == misc[1], but misc[0] != misc[2], we probably have shell - injection. Ditto for the remaining triplets. We use the `false` case - to avoid errors on search fields, etc. */ - - if (same_page(&MRES(0)->sig, &MRES(1)->sig) && - !same_page(&MRES(0)->sig, &MRES(2)->sig)) { - problem(PROB_SH_INJECT, MREQ(0), MRES(0), - (u8*)"responses to `true` and `false` different than to `uname`", - req->pivot, 0); - RESP_CHECKS(MREQ(2), MRES(2)); - } - - if (same_page(&MRES(3)->sig, &MRES(4)->sig) && - !same_page(&MRES(3)->sig, &MRES(5)->sig)) { - problem(PROB_SH_INJECT, MREQ(3), MRES(3), - (u8*)"responses to `true` and `false` different than to `uname`", - req->pivot, 0); - RESP_CHECKS(MREQ(5), MRES(5)); - } - - if (same_page(&MRES(6)->sig, &MRES(7)->sig) && - !same_page(&MRES(6)->sig, &MRES(8)->sig)) { - problem(PROB_SH_INJECT, MREQ(6), MRES(6), - (u8*)"responses to `true` and `false` different than to `uname`", - req->pivot, 0); - RESP_CHECKS(MREQ(8), MRES(8)); - } - -schedule_next: - - destroy_misc_data(req->pivot, req); - - /* Cross-site scripting - two requests (also test common - "special" error pages). */ - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, new_xss_tag(NULL)); - set_value(PARAM_HEADER, (u8*)"Referer", new_xss_tag(NULL), 0, &n->par); - register_xss_tag(n); - n->callback = inject_xss_check; - n->user_val = 0; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, new_xss_tag((u8*)".htaccess.aspx")); - register_xss_tag(n); - n->callback = inject_xss_check; - n->user_val = 1; - async_request(n); - - return 0; - -} - - -static u8 inject_xss_check(struct http_request* req, - struct http_response* res) { - struct http_request* n; - u32 orig_state = req->pivot->state; - - DEBUG_CALLBACK(req, res); - - /* Note that this is not a differential check, so we can let - 503, 504 codes slide. */ - - if (res->state != STATE_OK) { - handle_error(req, res, (u8*)"during cross-site scripting attacks", 0); - goto schedule_next; - } - - /* Content checks do automatic HTML parsing and XSS detection. - scrape_page() is generally not advisable here. */ - - content_checks(req, res); - - /* Attacker-controlled response start - 1 request */ - -schedule_next: - - if (req->user_val) return 0; - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, (u8*)"SKIPFISH~STRING"); - n->callback = inject_prologue_check; - async_request(n); - - return 0; - -} - - -static u8 inject_prologue_check(struct http_request* req, - struct http_response* res) { - struct http_request* n; - u32 orig_state = req->pivot->state; - - DEBUG_CALLBACK(req, res); - - /* Likewise, 503 / 504 is OK here. */ - - if (res->state != STATE_OK) { - handle_error(req, res, (u8*)"during response prologue attacks", 0); - goto schedule_next; - } - - if (res->pay_len && !prefix(res->payload, (u8*)"SKIPFISH~STRING") && - !GET_HDR((u8*)"Content-Disposition", &res->hdr)) - problem(PROB_PROLOGUE, req, res, NULL, req->pivot, 0); - -schedule_next: - - /* XSS checks - 3 requests */ - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "http://skipfish.invalid/;?"); - n->callback = inject_redir_check; - n->user_val = 0; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "//skipfish.invalid/;?"); - n->callback = inject_redir_check; - n->user_val = 1; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "skipfish://invalid/;?"); - n->callback = inject_redir_check; - n->user_val = 2; - async_request(n); - - return 0; - -} - - -static u8 inject_redir_check(struct http_request* req, - struct http_response* res) { - struct http_request* n; - u8* val; - u32 orig_state = req->pivot->state; - - DEBUG_CALLBACK(req, res); - - /* Likewise, not a differential check. */ - - if (res->state != STATE_OK) { - handle_error(req, res, (u8*)"during URL injection attacks", 0); - goto schedule_next; - } - - /* Check Location, Refresh headers. */ - - val = GET_HDR((u8*)"Location", &res->hdr); - - if (val) { - - if (!case_prefix(val, "http://skipfish.invalid/") || - !case_prefix(val, "//skipfish.invalid/")) - problem(PROB_URL_REDIR, req, res, (u8*)"injected URL in 'Location' header", - req->pivot, 0); - - if (!case_prefix(val, "skipfish:")) - problem(PROB_URL_XSS, req, res, (u8*)"injected URL in 'Location' header", - req->pivot, 0); - - } - - val = GET_HDR((u8*)"Refresh", &res->hdr); - - if (val && (val = (u8*)strchr((char*)val, '=')) && val++) { - u8 semi_safe = 0; - - if (*val == '\'' || *val == '"') { val++; semi_safe++; } - - if (!case_prefix(val, "http://skipfish.invalid/") || - !case_prefix(val, "//skipfish.invalid/")) - problem(PROB_URL_REDIR, req, res, (u8*)"injected URL in 'Refresh' header", - req->pivot, 0); - - /* Unescaped semicolon in Refresh headers is unsafe with MSIE6. */ - - if (!case_prefix(val, "skipfish:") || - (!semi_safe && strchr((char*)val, ';'))) - problem(PROB_URL_XSS, req, res, (u8*)"injected URL in 'Refresh' header", - req->pivot, 0); - - } - - /* META tags and JS will be checked by content_checks(). We're not - calling scrape_page(), because we don't want to accumulate bogus, - injected links. */ - - content_checks(req, res); - -schedule_next: - - if (req->user_val != 2) return 0; - - /* Header splitting - 2 requests */ - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "bogus\nSkipfish-Inject:bogus"); - n->callback = inject_split_check; - n->user_val = 0; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "bogus\rSkipfish-Inject:bogus"); - n->callback = inject_split_check; - n->user_val = 1; - async_request(n); - - return 0; - -} - - -static u8 inject_split_check(struct http_request* req, - struct http_response* res) { - u8 is_num = 0; - struct http_request* n; - u32 orig_state = req->pivot->state; - - DEBUG_CALLBACK(req, res); - - /* Not differential. */ - - if (res->state != STATE_OK) { - handle_error(req, res, (u8*)"during header injection attacks", 0); - goto schedule_next; - } - - /* Check headers - that's all! */ - - if (GET_HDR((u8*)"Skipfish-Inject", &res->hdr)) - problem(PROB_HTTP_INJECT, req, res, - (u8*)"successfully injected 'Skipfish-Inject' header into response", - req->pivot, 0); - -schedule_next: - - if (req->user_val != 1) return 0; - - /* SQL injection - 10 requests */ - - if (orig_state != PSTATE_CHILD_INJECT) { - u8* pstr = TPAR(RPREQ(req)); - u32 c = strspn((char*)pstr, "01234567890.+-"); - if (pstr[0] && !pstr[c]) is_num = 1; - } - - n = req_copy(RPREQ(req), req->pivot, 1); - if (!is_num) SET_VECTOR(orig_state, n, "9-8"); - else APPEND_VECTOR(orig_state, n, "-0"); - n->callback = inject_sql_check; - n->user_val = 0; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - if (!is_num) SET_VECTOR(orig_state, n, "8-7"); - else APPEND_VECTOR(orig_state, n, "-0-0"); - n->callback = inject_sql_check; - n->user_val = 1; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - if (!is_num) SET_VECTOR(orig_state, n, "9-1"); - else APPEND_VECTOR(orig_state, n, "-0-9"); - n->callback = inject_sql_check; - n->user_val = 2; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "\\\'\\\""); - set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish\\\'\\\"", 0, &n->par); - set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish\\\'\\\"", 0, &n->par); - set_value(PARAM_HEADER, (u8*)"Accept-Language", (u8*)"sfish\\\'\\\",en", 0, - &n->par); - n->callback = inject_sql_check; - n->user_val = 3; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "\'\""); - set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish\'\"", 0, &n->par); - set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish\'\"", 0, &n->par); - set_value(PARAM_HEADER, (u8*)"Accept-Language", (u8*)"sfish\'\",en", 0, - &n->par); - n->callback = inject_sql_check; - n->user_val = 4; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "\\\\\'\\\\\""); - set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish\\\\\'\\\\\"", 0, &n->par); - set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish\\\\\'\\\\\"", 0, &n->par); - set_value(PARAM_HEADER, (u8*)"Accept-Language", (u8*)"sfish\\\\\'\\\\\",en", 0, - &n->par); - n->callback = inject_sql_check; - n->user_val = 5; - async_request(n); - - /* This is a special case to trigger fault on blind numerical injection. */ - - n = req_copy(RPREQ(req), req->pivot, 1); - if (!is_num) SET_VECTOR(orig_state, n, "9 - 1"); - else APPEND_VECTOR(orig_state, n, " - 0 - 0"); - n->callback = inject_sql_check; - n->user_val = 6; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - if (!is_num) SET_VECTOR(orig_state, n, "9 1 -"); - else APPEND_VECTOR(orig_state, n, " 0 0 - -"); - n->callback = inject_sql_check; - n->user_val = 7; - async_request(n); - - /* Another round of SQL injection checks for a different escaping style. */ - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "''''\"\"\"\""); - set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish''''\"\"\"\"", 0, - &n->par); - set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish''''\"\"\"\"", 0, &n->par); - set_value(PARAM_HEADER, (u8*)"Accept-Language", (u8*)"sfish''''\"\"\"\",en", - 0, &n->par); - n->callback = inject_sql_check; - n->user_val = 8; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - APPEND_VECTOR(orig_state, n, "'\"'\"'\"'\""); - set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish'\"'\"'\"'\"", 0, - &n->par); - set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish'\"'\"'\"'\"", 0, - &n->par); - set_value(PARAM_HEADER, (u8*)"Accept-Language", - (u8*)"sfish'\"'\"'\"'\",en", 0, &n->par); - n->callback = inject_sql_check; - n->user_val = 9; - async_request(n); - - /* TODO: We should probably also attempt cookie vectors here. */ - - return 0; - -} - - -static u8 inject_sql_check(struct http_request* req, - struct http_response* res) { - struct http_request* n; - u32 orig_state = req->pivot->state; - DEBUG_CALLBACK(req, res); - - if (req->pivot->i_skip[3 + req->pivot->i_skip_add]) return 0; - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during SQL injection attacks", 0); - req->pivot->i_skip[3 + req->pivot->i_skip_add] = 1; - goto schedule_next; - } - - req->pivot->misc_req[req->user_val] = req; - req->pivot->misc_res[req->user_val] = res; - if ((++req->pivot->misc_cnt) != 10) return 1; - - /* Got all data: - - misc[0] = 9-8 (or orig-0) - misc[1] = 8-7 (or orig-0-0) - misc[2] = 9-1 (or orig-0-9) - misc[3] = [orig]\'\" - misc[4] = [orig]'" - misc[5] = [orig]\\'\\" - misc[6] = 9 - 1 (or orig - 0 - 0) - misc[7] = 9 1 - (or orig 0 0 - -) - - misc[8] == [orig]''''"""" - misc[9] == [orig]'"'"'"'" - - If misc[0] == misc[1], but misc[0] != misc[2], probable (numeric) SQL - injection. Ditto for misc[1] == misc[6], but misc[6] != misc[7]. - - If misc[3] != misc[4] and misc[3] != misc[5], probable text SQL - injection. - - If misc[4] == misc[9], and misc[8] != misc[9], probable text SQL - injection. - - */ - - if (same_page(&MRES(0)->sig, &MRES(1)->sig) && - !same_page(&MRES(0)->sig, &MRES(2)->sig)) { - problem(PROB_SQL_INJECT, MREQ(0), MRES(0), - (u8*)"response suggests arithmetic evaluation on server side (type 1)", - req->pivot, 0); - RESP_CHECKS(MREQ(0), MRES(0)); - RESP_CHECKS(MREQ(2), MRES(2)); - } - - if (same_page(&MRES(1)->sig, &MRES(6)->sig) && - !same_page(&MRES(6)->sig, &MRES(7)->sig)) { - problem(PROB_SQL_INJECT, MREQ(7), MRES(7), - (u8*)"response suggests arithmetic evaluation on server side (type 2)", - req->pivot, 0); - RESP_CHECKS(MREQ(6), MRES(6)); - RESP_CHECKS(MREQ(7), MRES(7)); - } - - if (!same_page(&MRES(3)->sig, &MRES(4)->sig) && - !same_page(&MRES(3)->sig, &MRES(5)->sig)) { - problem(PROB_SQL_INJECT, MREQ(4), MRES(4), - (u8*)"response to '\" different than to \\'\\\"", req->pivot, 0); - RESP_CHECKS(MREQ(3), MRES(3)); - RESP_CHECKS(MREQ(4), MRES(4)); - } - - if (same_page(&MRES(4)->sig, &MRES(9)->sig) && - !same_page(&MRES(8)->sig, &MRES(9)->sig)) { - problem(PROB_SQL_INJECT, MREQ(4), MRES(4), - (u8*)"response to ''''\"\"\"\" different than to '\"'\"'\"'\"", req->pivot, 0); - RESP_CHECKS(MREQ(8), MRES(8)); - RESP_CHECKS(MREQ(9), MRES(9)); - } - -schedule_next: - - destroy_misc_data(req->pivot, req); - - /* Format string attacks - 2 requests. */ - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "sfish%dn%dn%dn%dn%dn%dn%dn%dn"); - n->callback = inject_format_check; - n->user_val = 0; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "sfish%nd%nd%nd%nd%nd%nd%nd%nd"); - n->callback = inject_format_check; - n->user_val = 1; - async_request(n); - - return 0; -} - - -static u8 inject_format_check(struct http_request* req, - struct http_response* res) { - struct http_request* n; - u32 orig_state = req->pivot->state; - DEBUG_CALLBACK(req, res); - - if (req->pivot->i_skip[4 + req->pivot->i_skip_add]) return 0; - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during format string attacks", 0); - req->pivot->i_skip[4 + req->pivot->i_skip_add] = 1; - goto schedule_next; - } - - req->pivot->misc_req[req->user_val] = req; - req->pivot->misc_res[req->user_val] = res; - if ((++req->pivot->misc_cnt) != 2) return 1; - - /* Got all data: - - misc[0] = %dn... (harmless) - misc[1] = %nd... (crashy) - - If misc[0] != misc[1], probable format string vuln. - - */ - - if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) { - problem(PROB_FMT_STRING, MREQ(1), MRES(1), - (u8*)"response to %dn%dn%dn... different than to %nd%nd%nd...", - req->pivot, 0); - RESP_CHECKS(MREQ(1), MRES(1)); - } - -schedule_next: - - destroy_misc_data(req->pivot, req); - - /* Integer overflow bugs - 9 requests. */ - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "-0000012345"); - n->callback = inject_integer_check; - n->user_val = 0; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "-2147483649"); - n->callback = inject_integer_check; - n->user_val = 1; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "-2147483648"); - n->callback = inject_integer_check; - n->user_val = 2; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "0000012345"); - n->callback = inject_integer_check; - n->user_val = 3; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "2147483647"); - n->callback = inject_integer_check; - n->user_val = 4; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "2147483648"); - n->callback = inject_integer_check; - n->user_val = 5; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "4294967295"); - n->callback = inject_integer_check; - n->user_val = 6; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "4294967296"); - n->callback = inject_integer_check; - n->user_val = 7; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - SET_VECTOR(orig_state, n, "0000023456"); - n->callback = inject_integer_check; - n->user_val = 8; - async_request(n); - - return 0; -} - - -static u8 inject_integer_check(struct http_request* req, - struct http_response* res) { - - DEBUG_CALLBACK(req, res); - - if (req->pivot->i_skip[5 + req->pivot->i_skip_add]) return 0; - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during integer overflow attacks", 0); - req->pivot->i_skip[5 + req->pivot->i_skip_add] = 1; - goto schedule_next; - } - - req->pivot->misc_req[req->user_val] = req; - req->pivot->misc_res[req->user_val] = res; - if ((++req->pivot->misc_cnt) != 9) return 1; - - /* Got all data: - - misc[0] = -12345 (baseline) - misc[1] = -(2^31-1) - misc[2] = -2^31 - misc[3] = 12345 (baseline) - misc[4] = 2^31-1 - misc[5] = 2^31 - misc[6] = 2^32-1 - misc[7] = 2^32 - misc[8] = 23456 (validation) - - If misc[3] != misc[8], skip tests - we're likely dealing with a - search field instead. - - If misc[0] != misc[1] or misc[2], probable integer overflow; - ditto for 3 vs 4, 5, 6, 7. - - */ - - if (!same_page(&MRES(3)->sig, &MRES(8)->sig)) - goto schedule_next; - - if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) { - problem(PROB_INT_OVER, MREQ(1), MRES(1), - (u8*)"response to -(2^31-1) different than to -12345", - req->pivot, 0); - RESP_CHECKS(MREQ(1), MRES(1)); - } - - if (!same_page(&MRES(0)->sig, &MRES(2)->sig)) { - problem(PROB_INT_OVER, MREQ(2), MRES(2), - (u8*)"response to -2^31 different than to -12345", - req->pivot, 0); - RESP_CHECKS(MREQ(2), MRES(2)); - } - - if (!same_page(&MRES(3)->sig, &MRES(4)->sig)) { - problem(PROB_INT_OVER, MREQ(4), MRES(4), - (u8*)"response to 2^31-1 different than to 12345", - req->pivot, 0); - RESP_CHECKS(MREQ(4), MRES(4)); - } - - if (!same_page(&MRES(3)->sig, &MRES(5)->sig)) { - problem(PROB_INT_OVER, MREQ(5), MRES(5), - (u8*)"response to 2^31 different than to 12345", - req->pivot, 0); - RESP_CHECKS(MREQ(5), MRES(5)); - } - - if (!same_page(&MRES(3)->sig, &MRES(6)->sig)) { - problem(PROB_INT_OVER, MREQ(6), MRES(6), - (u8*)"response to 2^32-1 different than to 12345", - req->pivot, 0); - RESP_CHECKS(MREQ(6), MRES(6)); - } - - if (!same_page(&MRES(3)->sig, &MRES(7)->sig)) { - problem(PROB_INT_OVER, MREQ(7), MRES(7), - (u8*)"response to 2^32 different than to 12345", - req->pivot, 0); - RESP_CHECKS(MREQ(7), MRES(7)); - } - -schedule_next: - - destroy_misc_data(req->pivot, req); - inject_done(req->pivot); - - return 0; - -} - - -static void inject_done(struct pivot_desc* pv) { - - if (pv->state == PSTATE_CHILD_INJECT) { - - if (url_allowed(pv->req) && !pv->res_varies) { - - if ((pv->type == PIVOT_DIR || pv->type == PIVOT_SERV) - && pv->r404_cnt && !pv->bad_parent) { - pv->state = PSTATE_CHILD_DICT; - pv->cur_key = 0; - dir_dict_start(pv); - } else { - param_start(pv); - } - - } else { - - pv->state = PSTATE_DONE; - if (delete_bin) maybe_delete_payload(pv); - return; - - } - - } else { - - if (pv->bogus_par || pv->res_varies) { - pv->state = PSTATE_DONE; - if (delete_bin) maybe_delete_payload(pv); - } else { - param_numerical_start(pv); - } - - } - -} - - -static void param_start(struct pivot_desc* pv) { - struct http_request* n; - u32 i; - - if (pv->fuzz_par < 0 || !url_allowed(pv->req) || !param_allowed(pv->name)) { - pv->state = PSTATE_DONE; - if (delete_bin) maybe_delete_payload(pv); - return; - } - - DEBUG_HELPER(pv); - - pv->state = PSTATE_PAR_CHECK; - - /* Parameter behavior. */ - - pv->ck_pending += BH_CHECKS; - - for (i=0;ireq, pv, 1); - ck_free(TPAR(n)); - TPAR(n) = ck_strdup((u8*)BOGUS_PARAM); - n->callback = param_behavior_check; - n->user_val = i; - async_request(n); - } - -} - - -static u8 param_behavior_check(struct http_request* req, - struct http_response* res) { - - struct http_request* n; - u8* tmp; - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during parameter behavior tests", 0); - goto schedule_next; - } - - if (same_page(&res->sig, &RPRES(req)->sig)) { - DEBUG("* Parameter seems to have no effect.\n"); - req->pivot->bogus_par = 1; - goto schedule_next; - } - - DEBUG("* Parameter seems to have some effect:\n"); - debug_same_page(&res->sig, &RPRES(req)->sig); - - if (req->pivot->bogus_par) { - DEBUG("* We already classified it as having no effect, whoops.\n"); - req->pivot->res_varies = 1; - problem(PROB_VARIES, req, res, 0, req->pivot, 0); - goto schedule_next; - } - - /* If we do not have a signature yet, record it. Otherwise, make sure - it did not change. */ - - if (!req->pivot->r404_cnt) { - - DEBUG("* New signature, recorded.\n"); - memcpy(&req->pivot->r404[0], &res->sig, sizeof(struct http_sig)); - req->pivot->r404_cnt = 1; - - } else { - - if (!same_page(&res->sig, &req->pivot->r404[0])) { - DEBUG("* Signature does not match previous responses, whoops.\n"); - req->pivot->res_varies = 1; - problem(PROB_VARIES, req, res, 0, req->pivot, 0); - goto schedule_next; - } - - } - -schedule_next: - - if ((--req->pivot->ck_pending)) return 0; - - /* All probes failed? Assume bogus parameter, what else to do... */ - - if (!req->pivot->r404_cnt) - req->pivot->bogus_par = 1; - - /* If the parameter has an effect, schedule OGNL checks. */ - - if (!req->pivot->bogus_par && !req->pivot->res_varies && - req->par.n[req->pivot->fuzz_par]) { - - n = req_copy(req->pivot->req, req->pivot, 1); - tmp = ck_alloc(strlen((char*)n->par.n[req->pivot->fuzz_par]) + 8); - sprintf((char*)tmp, "[0]['%s']", n->par.n[req->pivot->fuzz_par]); - ck_free(n->par.n[req->pivot->fuzz_par]); - n->par.n[req->pivot->fuzz_par] = tmp; - n->callback = param_ognl_check; - n->user_val = 0; - async_request(n); - - n = req_copy(req->pivot->req, req->pivot, 1); - ck_free(n->par.n[req->pivot->fuzz_par]); - n->par.n[req->pivot->fuzz_par] = ck_strdup((u8*)"[0]['sfish']"); - n->callback = param_ognl_check; - n->user_val = 1; - async_request(n); - - } - - /* Injection attacks should be carried out even if we think this - parameter has no visible effect; but injection checks will not proceed - to dictionary fuzzing if bogus_par or res_varies is set. */ - - req->pivot->state = PSTATE_PAR_INJECT; - inject_start(req->pivot); - - return 0; - -} - - -static u8 param_ognl_check(struct http_request* req, - struct http_response* res) { - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during OGNL tests", 0); - return 0; - } - - /* First response is meant to give the same result. Second - is meant to give a different one. */ - - if (req->user_val == 0) { - if (same_page(&req->pivot->res->sig, &res->sig)) - req->pivot->ognl_check++; - } else { - if (!same_page(&req->pivot->res->sig, &res->sig)) - req->pivot->ognl_check++; - } - - if (req->pivot->ognl_check == 2) - problem(PROB_OGNL, req, res, - (u8*)"response to [0]['name']=... identical to name=...", - req->pivot, 0); - - return 0; - -} - - -static void param_numerical_start(struct pivot_desc* pv) { - u8 *val = TPAR(pv->req), *out, fmt[16]; - u32 i, dig, tail; - s32 val_i, range_st, range_en; - u8 zero_padded = 0; - - DEBUG_HELPER(pv); - - if (!descendants_ok(pv)) goto schedule_next; - - /* Skip to the first digit, then to first non-digit. */ - - i = 0; - while (val[i] && !isdigit(val[i])) i++; - if (!val[i]) goto schedule_next; - - dig = i; - while (val[i] && isdigit(val[i])) i++; - tail = i; - - /* Too many digits is a no-go. */ - - if (tail - dig > PAR_MAX_DIGITS) goto schedule_next; - - if (val[dig] == '0' && tail - dig > 1) zero_padded = 1; - - val_i = atoi((char*)val + dig); - range_st = val_i - PAR_INT_FUZZ; - range_en = val_i + PAR_INT_FUZZ; - if (range_st < 0) range_st = 0; - - if (zero_padded) sprintf((char*)fmt, "%%.%us%%0%uu%%s", dig, tail - dig); - else sprintf((char*)fmt, "%%.%us%%%uu%%s", dig, tail - dig); - - out = ck_alloc(strlen((char*)val) + 16); - - /* Let's roll! */ - - pv->state = PSTATE_PAR_NUMBER; - - pv->num_pending = range_en - range_st + 1; - - for (i=range_st;i<=range_en;i++) { - struct http_request* n; - - if (i == val_i) { pv->num_pending--; continue; } - - sprintf((char*)out, (char*)fmt, val, i, val + tail); - - n = req_copy(pv->req, pv, 1); - ck_free(TPAR(n)); - TPAR(n) = ck_strdup((u8*)out); - n->callback = param_numerical_check; - async_request(n); - - } - - ck_free(out); - - if (!pv->num_pending) goto schedule_next; - return; - -schedule_next: - - pv->state = PSTATE_PAR_DICT; - param_dict_start(pv); - - /* Pew pew! */ - -} - - -static u8 param_numerical_check(struct http_request* req, - struct http_response* res) { - struct pivot_desc *par, *n = NULL, *orig_pv = req->pivot; - u32 i; - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during numerical brute-force tests", 0); - goto schedule_next; - } - - /* Looks like parent, or like its 404 signature? */ - - if (same_page(&res->sig, &req->pivot->r404[0]) || - same_page(&res->sig, &req->pivot->res->sig)) - goto schedule_next; - - par = dir_parent(req->pivot); - - /* Check with parent if sigs available, but if not - no biggie. */ - - if (par) - for (i=0;ir404_cnt;i++) - if (same_page(&res->sig, &par->r404[i])) goto schedule_next; - - /* Matching child? If yes, don't bother. */ - - for (i=0;ipivot->child_cnt;i++) - if (req->pivot->child[i]->type == PIVOT_VALUE && - !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)TPAR(req), - (char*)req->pivot->child[i]->name))) goto schedule_next; - - if (!descendants_ok(req->pivot)) goto schedule_next; - - /* Hmm, looks like we're onto something. Let's manually create a dummy - pivot and attach it to current node, without any activity planned. - Attach any response notes to that pivot. */ - - n = ck_alloc(sizeof(struct pivot_desc)); - - n->type = PIVOT_VALUE; - n->state = PSTATE_DONE; - n->name = ck_strdup(TPAR(req)); - n->req = req; - n->res = res; - n->fuzz_par = req->pivot->fuzz_par; - n->parent = req->pivot; - - DEBUG("--- New pivot (value): %s ---\n", n->name); - - req->pivot->child = ck_realloc(req->pivot->child, (req->pivot->child_cnt + 1) - * sizeof(struct pivot_desc*)); - - req->pivot->child[req->pivot->child_cnt++] = n; - - add_descendant(req->pivot); - - req->pivot = n; - - RESP_CHECKS(req, res); - - secondary_ext_start(orig_pv, req, res, 1); - - if (delete_bin) maybe_delete_payload(n); - -schedule_next: - - if (!(--(orig_pv->num_pending))) { - orig_pv->state = PSTATE_PAR_DICT; - param_dict_start(orig_pv); - } - - /* Copied over to pivot. */ - return n ? 1 : 0; - -} - - -static void param_dict_start(struct pivot_desc* pv) { - static u8 in_dict_init; - struct http_request* n; - u8 *kw, *ex; - u32 i, c; - u8 specific; - - /* Too many requests still pending, or already done? */ - - if (in_dict_init || pv->pdic_pending > DICT_BATCH || - pv->state != PSTATE_PAR_DICT) return; - - DEBUG_HELPER(pv); - -restart_dict: - - if (!descendants_ok(pv)) { - param_trylist_start(pv); - return; - } - - i = 0; - - kw = (pv->pdic_guess ? wordlist_get_guess : wordlist_get_word) - (pv->pdic_cur_key, &specific); - - if (!kw) { - - /* No more keywords. Move to guesswords if not there already, or - advance to try list otherwise. */ - - if (pv->pdic_guess) { param_trylist_start(pv); return; } - - pv->pdic_guess = 1; - pv->pdic_cur_key = 0; - goto restart_dict; - - } - - /* Use crawl_prob/100 dictionary entries. */ - - if (R(100) < crawl_prob) { - - /* Schedule extension-less probe, if the keyword is not - on the child list. */ - - for (c=0;cchild_cnt;c++) - if (pv->type == PIVOT_VALUE && - !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)kw, - (char*)pv->child[c]->name))) break; - - /* ...and does not match the node itself. */ - - if (pv->fuzz_par != -1 && - !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)kw, - (char*)pv->req->par.v[pv->fuzz_par]))) c = ~pv->child_cnt; - - if (c == pv->child_cnt) { - n = req_copy(pv->req, pv, 1); - ck_free(TPAR(n)); - TPAR(n) = ck_strdup(kw); - n->callback = param_dict_check; - n->user_val = 0; - pv->pdic_pending++; - in_dict_init = 1; - async_request(n); - in_dict_init = 0; - } - - /* Schedule probes for all extensions for the current word, but - only if the original parameter contained '.' somewhere, - and only if string is not on the try list. Special handling - for specific keywords with '.' inside. */ - - if (!no_fuzz_ext && strchr((char*)TPAR(pv->req), '.')) - while ((ex = wordlist_get_extension(i, specific))) { - - u8* tmp = ck_alloc(strlen((char*)kw) + strlen((char*)ex) + 2); - - sprintf((char*)tmp, "%s.%s", kw, ex); - - for (c=0;cchild_cnt;c++) - if (pv->type == PIVOT_VALUE && - !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)tmp, - (char*)pv->child[c]->name))) break; - - if (pv->fuzz_par != -1 && - !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)tmp, - (char*)pv->req->par.v[pv->fuzz_par]))) c = ~pv->child_cnt; - - if (c == pv->child_cnt) { - n = req_copy(pv->req, pv, 1); - ck_free(TPAR(n)); - TPAR(n) = tmp; - n->user_val = 0; - n->callback = param_dict_check; - pv->pdic_pending++; - in_dict_init = 1; - async_request(n); - in_dict_init = 0; - } else ck_free(tmp); - - i++; - } - - } - - pv->pdic_cur_key++; - - if (pv->pdic_pending < DICT_BATCH) goto restart_dict; - -} - - -static u8 param_dict_check(struct http_request* req, - struct http_response* res) { - struct pivot_desc *par, *n = NULL, *orig_pv = req->pivot; - u8 keep = 0; - u32 i; - - DEBUG_CALLBACK(req, res); - - if (!req->user_val) - req->pivot->pdic_pending--; - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during parameter brute-force tests", 0); - goto schedule_next; - } - - /* Same as parent or parent's 404? Don't bother. */ - - if (same_page(&res->sig, &req->pivot->r404[0]) || - same_page(&res->sig, &RPRES(req)->sig)) goto schedule_next; - - par = dir_parent(req->pivot); - - if (par) - for (i=0;ir404_cnt;i++) - if (same_page(&res->sig, &par->r404[i])) goto schedule_next; - - /* Matching child? If yes, don't bother. */ - - for (i=0;ipivot->child_cnt;i++) - if (req->pivot->child[i]->type == PIVOT_VALUE && - !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)TPAR(req), - (char*)req->pivot->child[i]->name))) goto schedule_next; - - if (!descendants_ok(req->pivot)) goto schedule_next; - - n = ck_alloc(sizeof(struct pivot_desc)); - - n->type = PIVOT_VALUE; - n->state = PSTATE_DONE; - n->name = ck_strdup(TPAR(req)); - n->req = req; - n->res = res; - n->fuzz_par = req->pivot->fuzz_par; - n->parent = req->pivot; - - DEBUG("--- New pivot (value): %s ---\n", n->name); - - req->pivot->child = ck_realloc(req->pivot->child, (req->pivot->child_cnt + 1) - * sizeof(struct pivot_desc*)); - - req->pivot->child[req->pivot->child_cnt++] = n; - - add_descendant(req->pivot); - - req->pivot = n; - - keep = 1; - - RESP_CHECKS(req, res); - - if (!req->user_val) - secondary_ext_start(orig_pv, req, res, 1); - - if (delete_bin) maybe_delete_payload(n); - -schedule_next: - - if (!req->user_val) - param_dict_start(orig_pv); - - return keep; - -} - - -void param_trylist_start(struct pivot_desc* pv) { - u32 i; - - /* If the parameter does not seem to be doing anything, there is - no point in going through the try list if restarted. */ - - if (pv->fuzz_par == -1 || pv->bogus_par || pv->res_varies - || !descendants_ok(pv)) { - - pv->state = PSTATE_DONE; - if (delete_bin) maybe_delete_payload(pv); - - return; - } else - pv->state = PSTATE_PAR_TRYLIST; - - DEBUG_HELPER(pv); - - for (i=pv->try_cur;itry_cnt;i++) { - u32 c; - - /* If we already have a child by this name, don't poke it again. */ - - for (c=0;cchild_cnt;c++) - if (!((is_c_sens(pv) ? strcmp : strcasecmp)((char*)pv->try_list[i], - (char*)pv->child[c]->name))) continue; - - /* Matching current node? Ditto. */ - - if (pv->fuzz_par != -1 && - !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)pv->try_list[i], - (char*)pv->req->par.v[pv->fuzz_par]))) continue; - - if (c == pv->child_cnt) { - - if (R(100) < crawl_prob) { - struct http_request* n; - - pv->try_pending++; - - n = req_copy(pv->req, pv, 1); - ck_free(TPAR(n)); - TPAR(n) = ck_strdup(pv->try_list[i]); - n->callback = param_trylist_check; - async_request(n); - } - - } else { - if (!pv->child[c]->linked) pv->child[c]->linked = 1; - } - - } - - pv->try_cur = i; - - if (!pv->try_pending) { - pv->state = PSTATE_DONE; - if (delete_bin) maybe_delete_payload(pv); - return; - } - -} - - -static u8 param_trylist_check(struct http_request* req, - struct http_response* res) { - struct pivot_desc *par, *n = NULL; - struct pivot_desc* orig_pv = req->pivot; - u32 i; - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during try list fetches", 0); - goto schedule_next; - } - - /* Same as parent or parent's 404? Don't bother. */ - - if (same_page(&res->sig, &req->pivot->r404[0]) || - same_page(&res->sig, &RPRES(req)->sig)) goto schedule_next; - - par = dir_parent(req->pivot); - - if (par) - for (i=0;ir404_cnt;i++) - if (same_page(&res->sig, &par->r404[i])) goto schedule_next; - - /* Name matching known child? If yes, don't bother. */ - - for (i=0;ipivot->child_cnt;i++) - if (req->pivot->child[i]->type == PIVOT_VALUE && - !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)TPAR(req), - (char*)req->pivot->child[i]->name))) goto schedule_next; - - if (!descendants_ok(req->pivot)) goto schedule_next; - - n = ck_alloc(sizeof(struct pivot_desc)); - - n->type = PIVOT_VALUE; - n->state = PSTATE_DONE; - n->name = ck_strdup(TPAR(req)); - n->req = req; - n->res = res; - n->fuzz_par = req->pivot->fuzz_par; - n->parent = req->pivot; - - DEBUG("--- New pivot (value): %s ---\n", n->name); - - req->pivot->child = ck_realloc(req->pivot->child, (req->pivot->child_cnt + 1) - * sizeof(struct pivot_desc*)); - - req->pivot->child[req->pivot->child_cnt++] = n; - - add_descendant(req->pivot); - - req->pivot = n; - - RESP_CHECKS(req, res); - - secondary_ext_start(orig_pv, req, res, 1); - - if (delete_bin) maybe_delete_payload(n); - -schedule_next: - - if (!(--(orig_pv->try_pending))) { - orig_pv->state = PSTATE_DONE; - if (delete_bin) maybe_delete_payload(orig_pv); - } - - /* Copied over to pivot. */ - return n ? 1 : 0; - -} - - -u8 file_retrieve_check(struct http_request* req, struct http_response* res) { - u32 i = 0; - struct pivot_desc* par; - - RPRES(req) = res; - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during initial file fetch", 1); - return 1; - } - - /* Matches parent's 404? */ - - par = dir_parent(req->pivot); - - if (par) - for (i=0;ir404_cnt;i++) - if (same_page(&res->sig, &par->r404[i])) break; - - /* If no signatures on parents, fall back to a basic 404 check, it's - the least we could do. */ - - if ((!par && res->code == 404) || (par && i != par->r404_cnt)) { - - req->pivot->missing = 1; - - } else { - - if (res->code > 400) - problem(PROB_NO_ACCESS, req, res, NULL, req->pivot, 0); - - /* Do not bother with checks on files or params if - content identical to parent. */ - - if (!RPAR(req)->res || !same_page(&res->sig, &RPAR(req)->res->sig)) { - RESP_CHECKS(req, res); - if (par && req->pivot->type != PIVOT_PARAM) - secondary_ext_start(par, req, res, 0); - } - - if (req->pivot->type == PIVOT_FILE) - dir_case_start(req->pivot); - - } - - /* On non-param nodes, we want to proceed with path-based injection - checks. On param nodes, we want to proceed straght to parametric - testng, instead. */ - - unlock_children(req->pivot); - - if (req->pivot->type == PIVOT_PARAM) { - param_start(req->pivot); - } else { - req->pivot->state = PSTATE_CHILD_INJECT; - inject_start(req->pivot); - } - - - /* This is the initial callback, keep the response. */ - return 1; - -} - - -u8 dir_retrieve_check(struct http_request* req, struct http_response* res) { - struct http_request* n; - struct pivot_desc* par; - RPRES(req) = res; - - DEBUG_CALLBACK(req, res); - - /* Error at this point means we should give up on other probes in this - directory. */ - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during initial directory fetch", 1); - return 1; - } - - if (req->pivot->type == PIVOT_SERV) - PIVOT_CHECKS(req, res); - - /* The next step is checking 404 responses for all extensions (starting - with an empty one), which would also determine if the directory exists - at all, etc. We make an exception for server pivot, though, which is - presumed to be a directory (so we do PIVOT_CHECKS right away). */ - - req->pivot->state = PSTATE_404_CHECK; - n = req_copy(req, req->pivot, 1); - replace_slash(n, (u8*)BOGUS_FILE); - - n->user_val = 0; - n->callback = dir_404_check; - - req->pivot->r404_pending++; - - async_request(n); - - par = dir_parent(req->pivot); - if (par) secondary_ext_start(par, req, res, 0); - - /* Header, response belong to pivot - keep. */ - return 1; -} - - -static u8 dir_404_check(struct http_request* req, - struct http_response* res) { - - struct http_request* n; - u32 i; - s32 ppval = -1, pval = -1, val = -1; - - DEBUG_CALLBACK(req, res); - - if (req->pivot->r404_skip) goto schedule_next; - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during 404 response checks", 0); - goto schedule_next; - } - - /* If the first 404 probe returned something that looks like the - "root" page for the currently tested directory, panic. But don't - do that check on server pivots, or if valid redirect detected - earlier. */ - - if (!req->user_val && !req->pivot->sure_dir && - req->pivot->type != PIVOT_SERV && RPRES(req) && - same_page(&res->sig, &RPRES(req)->sig)) { - DEBUG("* First 404 probe identical with parent!\n"); - goto schedule_next; - } else if (!req->user_val) { - DEBUG("* First 404 probe differs from parent (%d)\n", - RPRES(req) ? RPRES(req)->code : 0); - } - - /* Check if this is a new signature. */ - - for (i=0;ipivot->r404_cnt;i++) - if (same_page(&res->sig, &req->pivot->r404[i])) break; - - if (i == req->pivot->r404_cnt) { - struct pivot_desc* par; - - DEBUG("* New signature found (%u).\n", req->pivot->r404_cnt); - - /* Need to add a new one. Make sure we're not over the limit. */ - - if (req->pivot->r404_cnt >= MAX_404) { - - req->pivot->r404_skip = 1; - - problem(PROB_404_FAIL, RPREQ(req), RPRES(req), - (u8*)"too many 404 signatures found", req->pivot, 0); - - goto schedule_next; - - } - - memcpy(&req->pivot->r404[i], &res->sig, sizeof(struct http_sig)); - - req->pivot->r404_cnt++; - - /* Is this a new signature not seen on parent? Notify if so, - and check it thoroughly. */ - - par = dir_parent(req->pivot); - - if (par) { - - for (i=0;ir404_cnt;i++) - if (same_page(&res->sig, &par->r404[i])) break; - - } - - if (!par || i == par->r404_cnt) { - problem(PROB_NEW_404, req, res, NULL, req->pivot, 1); - RESP_CHECKS(req, res); - } - - } - -schedule_next: - - /* First probe OK? */ - - if (!req->user_val) { - u8* nk; - u32 cur_ext = 0; - - /* First probe should already yield a 404 signature. */ - - if (!req->pivot->r404_cnt) { - DEBUG("* First probe failed to yield a signature.\n"); - goto bad_404; - } - - DEBUG("* First probe yielded a valid signature.\n"); - - /* At this point, we can be reasonably sure the response is - meaningful. */ - - PIVOT_CHECKS(req->pivot->req, req->pivot->res); - dir_case_start(req->pivot); - - /* Aaand schedule all the remaining probes. Repeat BH_CHECKS - times to also catch random variations. */ - - while ((nk = wordlist_get_extension(cur_ext++, 0))) { - u8* tmp = ck_alloc(strlen(BOGUS_FILE) + strlen((char*)nk) + 2); - - sprintf((char*)tmp, "%s.%s", BOGUS_FILE, nk); - - for (i=0;ipivot, 1); - replace_slash(n, tmp); - n->callback = dir_404_check; - n->user_val = 1; - - /* r404_pending is at least 1 to begin with, so this is safe - even if async_request() has a synchronous effect. */ - - req->pivot->r404_pending++; - async_request(n); - - } - - ck_free(tmp); - - } - - /* Also issue 404 probe for "lpt9", as "con", "prn", "nul", "lpt#", - etc, are handled in a really annoying way by IIS. */ - - n = req_copy(RPREQ(req), req->pivot, 1); - replace_slash(n, (u8*)"lpt9"); - n->callback = dir_404_check; - n->user_val = 1; - req->pivot->r404_pending++; - async_request(n); - - /* ...and for ~user, since this sometimes has a custom response, too. */ - - n = req_copy(RPREQ(req), req->pivot, 1); - replace_slash(n, (u8*)"~" BOGUS_FILE); - n->callback = dir_404_check; - n->user_val = 1; - req->pivot->r404_pending++; - async_request(n); - - /* Lastly, make sure that directory 404 is on file. */ - - n = req_copy(RPREQ(req), req->pivot, 1); - replace_slash(n, (u8*)BOGUS_FILE); - set_value(PARAM_PATH, 0, (u8*)"", -1, &n->par); - n->callback = dir_404_check; - n->user_val = 1; - req->pivot->r404_pending++; - async_request(n); - - } - - if (--(req->pivot->r404_pending)) return 0; - - /* If we're here, all probes completed, and we had no major errors. - If no signatures gathered, try to offer useful advice. */ - -bad_404: - - if (!req->pivot->r404_cnt || req->pivot->r404_skip) { - - DEBUG("* 404 detection failed.\n"); - - if (RPRES(req)->code == 404) { - - req->pivot->missing = 1; - - } else if (RPRES(req)->code >= 400) { - - problem(PROB_NO_ACCESS, RPREQ(req), RPRES(req), NULL, req->pivot, 0); - - /* Additional check for 401, 500 codes, as we're not calling - content_checks() otherwise. */ - - if (RPRES(req)->code == 401) - problem(PROB_AUTH_REQ, RPREQ(req), RPRES(req), NULL, req->pivot, 0); - else if (RPRES(req)->code >= 500) - problem(PROB_SERV_ERR, RPREQ(req), RPRES(req), NULL, req->pivot, 0); - - } else { - - if (req->pivot->type != PIVOT_SERV) { - req->pivot->type = PIVOT_PATHINFO; - replace_slash(req->pivot->req, NULL); - } else - problem(PROB_404_FAIL, RPREQ(req), RPRES(req), - (u8*)"no distinctive 404 behavior detected", req->pivot, 0); - } - - req->pivot->r404_cnt = 0; - - /* We can still try parsing the response, if it differs from parent - and is not on parent's 404 list. */ - - if (!RPAR(req)->res) { - PIVOT_CHECKS(req->pivot->req, req->pivot->res); - } else { - - if (!same_page(&RPRES(req)->sig, &RPAR(req)->res->sig)) { - - struct pivot_desc* par; - par = dir_parent(req->pivot); - - if (par) { - - for (i=0;ir404_cnt;i++) - if (same_page(&res->sig, &par->r404[i])) break; - - } - - if (!par || i == par->r404_cnt) - PIVOT_CHECKS(req->pivot->req, req->pivot->res); - - } - - } - - } else DEBUG("* 404 detection successful: %u signatures.\n", req->pivot->r404_cnt); - - /* Note that per-extension 404 probes coupled with a limit on the number of - 404 signatures largely eliminates the need for BH_COUNT identical probes - to confirm sane behavior here. */ - - /* The next probe is checking if /foo/current_path/ returns the same - response as /bar/current_path/. If yes, then the directory probably - should not be fuzzed. */ - - req->pivot->state = PSTATE_PARENT_CHECK; - - n = req_copy(RPREQ(req), req->pivot, 1); - n->callback = dir_up_behavior_check; - n->user_val = 0; - - /* Last path element is /; previous path element is current dir name; - previous previous element is parent dir name. Find and replace it. */ - - for (i=0;ipar.c;i++) { - if (PATH_SUBTYPE(n->par.t[i])) { - ppval = pval; - pval = val; - val = i; - } - } - - if (ppval != -1 && req->pivot->r404_cnt) { - - ck_free(n->par.v[ppval]); - n->par.v[ppval] = ck_strdup((u8*)BOGUS_FILE); - async_request(n); - - } else { - - /* Top-level dir - nothing to replace. Do a dummy call to - dir_up_behavior_check() to proceed directly to IPS checks. */ - - n->user_val = 1; - dir_up_behavior_check(n, res); - destroy_request(n); - - } - - return 0; - -} - - -static u8 dir_up_behavior_check(struct http_request* req, - struct http_response* res) { - - struct http_request* n; - - DEBUG_CALLBACK(req, res); - - if (req->user_val || req->pivot->r404_skip) { - DEBUG("* Check not carried out (non-existent / bad parent).\n"); - goto schedule_next; - } - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during parent checks", 0); - goto schedule_next; - } - - if (same_page(&res->sig, &RPRES(req)->sig)) { - problem(PROB_PARENT_FAIL, req, res, 0, req->pivot, 0); - DEBUG("* Parent may be bogus, skipping.\n"); - req->pivot->bad_parent = 1; - } else { - DEBUG("* Parent behaves OK.\n"); - } - - /* Regardless of the outcome, let's schedule a final IPS check. Theoretically, - a single request would be fine; but some servers, such as gws, tend - to respond to /?foo very differently than to /. */ - -schedule_next: - - req->pivot->state = PSTATE_IPS_CHECK; - - n = req_copy(RPREQ(req), req->pivot, 1); - tokenize_path((u8*)IPS_TEST, n, 0); - n->callback = dir_ips_check; - n->user_val = 0; - async_request(n); - - n = req_copy(RPREQ(req), req->pivot, 1); - tokenize_path((u8*)IPS_SAFE, n, 0); - n->callback = dir_ips_check; - n->user_val = 1; - async_request(n); - - return 0; - -} - - -static u8 dir_ips_check(struct http_request* req, - struct http_response* res) { - struct pivot_desc* par; - - DEBUG_CALLBACK(req, res); - - if (req->pivot->i_skip[4]) return 0; - - if (req->user_val == 1 && FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during IPS tests", 0); - req->pivot->i_skip[4] = 1; - goto schedule_next; - } - - req->pivot->misc_req[req->user_val] = req; - req->pivot->misc_res[req->user_val] = res; - if ((++req->pivot->misc_cnt) != 2) return 1; - - par = dir_parent(req->pivot); - - if (!par || !par->uses_ips) { - - if (MRES(0)->state != STATE_OK) - problem(PROB_IPS_FILTER, MREQ(0), MRES(0), - (u8*)"request timed out (could also be a flaky server)", - req->pivot, 0); - else if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) - problem(PROB_IPS_FILTER, MREQ(0), MRES(0), NULL, req->pivot, 0); - - } else { - - if (MRES(0)->state == STATE_OK && same_page(&MRES(0)->sig, &MRES(1)->sig)) - problem(PROB_IPS_FILTER_OFF, MREQ(0), MRES(0), NULL, req->pivot, 0); - - } - -schedule_next: - - destroy_misc_data(req->pivot, req); - - /* Schedule injection attacks. */ - - unlock_children(req->pivot); - - req->pivot->state = PSTATE_CHILD_INJECT; - inject_start(req->pivot); - - return 0; -} - - -static void dir_dict_start(struct pivot_desc* pv) { - static u8 in_dict_init; - struct http_request* n; - u8 *kw; - u8 specific; - - /* Too many requests still pending, or already moved on to - parametric tests? */ - - if (in_dict_init || pv->pending > DICT_BATCH || pv->state != PSTATE_CHILD_DICT) - return; - - if (!descendants_ok(pv)) { - param_start(pv); - return; - } - - if (pv->no_fuzz) { - if (pv->no_fuzz == 1) - problem(PROB_LIMITS, pv->req, pv->res, - (u8*)"Recursion limit reached, not fuzzing", pv, 0); - else - problem(PROB_LIMITS, pv->req, pv->res, - (u8*)"Directory out of scope, not fuzzing", pv, 0); - param_start(pv); - return; - } - - DEBUG_HELPER(pv); - -restart_dict: - - kw = (pv->guess ? wordlist_get_guess : wordlist_get_word) - (pv->cur_key, &specific); - - if (!kw) { - - /* No more keywords. Move to guesswords if not there already, or - advance to parametric tests otherwise. */ - - if (pv->guess) { param_start(pv); return; } - - pv->guess = 1; - pv->cur_key = 0; - goto restart_dict; - - } - - /* Only schedule crawl_prob% dictionary entries. */ - - if (R(100) < crawl_prob) { - - /* First, schedule a request for /foo.bogus to see if extension - fuzzing is advisable. */ - - u8* tmp = ck_alloc(strlen((char*)kw) + strlen((char*)BOGUS_EXT) + 2); - - sprintf((char*)tmp, "%s.%s", kw, BOGUS_EXT); - - n = req_copy(pv->req, pv, 1); - replace_slash(n, tmp); - n->callback = dir_dict_bogus_check; - n->trying_key = kw; - n->trying_spec = specific; - pv->pending++; - in_dict_init = 1; - async_request(n); - in_dict_init = 0; - - ck_free(tmp); - - } - - pv->cur_key++; - - /* Grab more keywords until we have a reasonable number of parallel requests - scheduled. */ - - if (pv->pending < DICT_BATCH) goto restart_dict; - -} - - -static u8 dir_dict_bogus_check(struct http_request* req, - struct http_response* res) { - - struct http_request* n; - u32 i, c; - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - - handle_error(req, res, (u8*)"during path-based dictionary probes", 0); - i = ~req->pivot->r404_cnt; - - } else { - - if (!req->pivot->r404_cnt) - DEBUG("Bad pivot with no sigs! Pivot name = '%s'\n", - req->pivot->name); - - for (i=0;ipivot->r404_cnt;i++) - if (same_page(&res->sig, &req->pivot->r404[i])) break; - - } - - /* Do not schedule probes for .ht* files if default Apache config spotted. */ - - if (i == req->pivot->r404_cnt && res->code == 403 && - prefix(req->trying_key, ".ht")) goto schedule_next; - - /* New file? Add pivot for the extension. */ - - if (i == req->pivot->r404_cnt) maybe_add_pivot(req, res, 0); - - /* Schedule extension probes only if bogus extension resulted in known 404. */ - - if (i != req->pivot->r404_cnt && !no_fuzz_ext) { - u8* ex; - - i = 0; - - while ((ex = wordlist_get_extension(i, req->trying_spec))) { - - u8* tmp = ck_alloc(strlen((char*)req->trying_key) + - strlen((char*)ex) + 2); - - sprintf((char*)tmp, "%s.%s", req->trying_key, ex); - - /* See if that file is already known... */ - - for (c=0;cpivot->child_cnt;c++) - if (!((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)tmp, - (char*)req->pivot->child[c]->name))) break; - - /* When dealing with name=value pairs, also compare to - currently fuzzed value string. */ - - if (req->pivot->fuzz_par != -1 && - !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)tmp, - (char*)req->pivot->req->par.v[req->pivot->fuzz_par]))) - c = ~req->pivot->child_cnt; - - /* Not found - schedule a probe. */ - - if (c == req->pivot->child_cnt) { - n = req_copy(req->pivot->req, req->pivot, 1); - replace_slash(n, tmp); - n->callback = dir_dict_check; - n->user_val = 0; - req->pivot->pending++; - async_request(n); - } - - ck_free(tmp); - - i++; - } - - } - - /* Regardless of this, also schedule requests for /$name and /$name/. */ - - for (c=0;cpivot->child_cnt;c++) - if (!((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)req->trying_key, - (char*)req->pivot->child[c]->name))) break; - - if (req->pivot->fuzz_par != -1 && - !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)req->trying_key, - (char*)req->pivot->req->par.v[req->pivot->fuzz_par]))) - c = ~req->pivot->child_cnt; - - if (c == req->pivot->child_cnt) { - n = req_copy(req->pivot->req, req->pivot, 1); - replace_slash(n, req->trying_key); - n->callback = dir_dict_check; - n->user_val = 0; - req->pivot->pending++; - async_request(n); - - if (prefix(req->trying_key, (u8*)".ht")) { - - n = req_copy(req->pivot->req, req->pivot, 1); - replace_slash(n, req->trying_key); - set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->par); - n->user_val = 0; - n->callback = dir_dict_check; - req->pivot->pending++; - async_request(n); - - } - - } - -schedule_next: - - /* Calling dir_dict_start() ensures that, if no new requests were scheduled - earlier on and nothing else is pending, that we will still advance to - parametric checks. */ - - req->pivot->pending--; - dir_dict_start(req->pivot); - - return 0; - -} - - -static u8 dir_dict_check(struct http_request* req, - struct http_response* res) { - u32 i; - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during path-based dictionary probes", 0); - } else { - - /* Check if 404... */ - - for (i=0;ipivot->r404_cnt;i++) - if (same_page(&res->sig, &req->pivot->r404[i])) break; - - /* Special case for secondary extension fuzzing - skip secondary - extensions that seemingly return the same document. */ - - if (req->user_val && same_page(&res->sig, &req->same_sig)) - i = ~req->pivot->r404_cnt; - - /* If not 404, do response, and does not look like - parent's original file signature, add pivot. */ - - if (i == req->pivot->r404_cnt) maybe_add_pivot(req, res, 0); - - } - - /* Try replenishing the queue. */ - - if (!req->user_val) { - req->pivot->pending--; - dir_dict_start(req->pivot); - } - - return 0; - -} - - -u8 unknown_retrieve_check(struct http_request* req, struct http_response* res) { - u32 i = 0 /* bad gcc */; - struct pivot_desc *par; - struct http_request* n; - u8* name = NULL; - - RPRES(req) = res; - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during initial resource fetch", 1); - return 1; - } - - /* Matches parent's 404? */ - - par = dir_parent(req->pivot); - - if (par) - for (i=0;ir404_cnt;i++) - if (same_page(&res->sig, &par->r404[i])) break; - - /* Again, 404 is the least we could do. */ - - if ((!par && res->code == 404) || (par && i != par->r404_cnt)) { - - req->pivot->missing = 1; - unlock_children(req->pivot); - param_start(req->pivot); - return 1; - - } - - /* If the response looks like parent's original unknown_callback() - response, assume file. This is a workaround for some really - quirky architectures. */ - - if (par && res->pay_len && res->code == 200 && - same_page(&par->unk_sig, &res->sig)) { - - req->pivot->type = PIVOT_FILE; - return file_retrieve_check(req, res); - - } - - /* Another workaround for quirky frameworks: identical signature - as parent's both probes, and 3xx code. */ - - if (par && res->code >= 300 && res->code < 400 && - same_page(&par->unk_sig, &res->sig) && - same_page(&par->res->sig, &res->sig)) { - - req->pivot->type = PIVOT_FILE; - return file_retrieve_check(req, res); - - } - - /* Special handling for .ht* */ - - if (req->pivot->type < PIVOT_PARAM) { - u32 i; - - /* Find last path segment. */ - - for (i=0;ipar.c;i++) - if (PATH_SUBTYPE(req->par.t[i])) name = req->par.v[i]; - - if (name && !prefix(name, (u8*)".ht")) { - req->pivot->type = PIVOT_FILE; - return file_retrieve_check(req, res); - } - - } - - /* Schedule a request to settle the type of this pivot point. */ - - n = req_copy(req, req->pivot, 1); - set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->par); - n->callback = unknown_retrieve_check2; - n->user_val = 0; - - if (name) { - u8* ppos = (u8*) strrchr((char*)name, '.'); - if (!ppos || ppos == name) n->user_val = 1; - } - - async_request(n); - - /* This is the initial callback, keep the response. */ - - return 1; - -} - - -static u8 unknown_retrieve_check2(struct http_request* req, - struct http_response* res) { - u8 keep = 0; - - DEBUG_CALLBACK(req, res); - - if (FETCH_FAIL(res)) { - handle_error(req, res, (u8*)"during node type checks", 0); - goto schedule_next; - } - - /* If pivot == res, we are probably dealing with PATH_INFO-style - plot device, which is best approached as a directory anyway - (worst-case scenario, dir handlers will dismiss it as - misbehaving and demote it to PIVOT_PATHINFO after some extra - checks). - - If pivot != res, and res is not a 404 response, assume dir; - and if it is 404, assume file, except if pivot redirected to res. - - We also have a special case if the original request returned a - non-empty 2xx response, but the new one returned 3xx-5xx - this is - likely a file, too. */ - - if (same_page(&RPRES(req)->sig, &res->sig)) goto assume_dir; else { - u32 i = 0; - struct pivot_desc* par = dir_parent(req->pivot); - - if (res->code == 404 && RPRES(req)->code >= 300 && RPRES(req)->code < 400) { - u8 *loc = GET_HDR((u8*)"Location", &RPRES(req)->hdr); - - if (loc) { - u8* path = serialize_path(req, 1, 0); - - if (!strcasecmp((char*)path, (char*)loc)) { - ck_free(path); - req->pivot->sure_dir = 1; - goto assume_dir; - } - - ck_free(path); - - } - - } - - if (par) { - for (i=0;ir404_cnt;i++) - if (same_page(&res->sig, &par->r404[i])) break; - - /* Do not use extension-originating signatures for settling non-extension - cases. */ - - if (i && req->user_val) i = par->r404_cnt; - - } - - if ((!par && res->code == 404) || (par && i != par->r404_cnt) || - (RPRES(req)->code < 300 && res->code >= 300 && RPRES(req)->pay_len)) { - - req->pivot->type = PIVOT_FILE; - - } else { - -assume_dir: - - /* If any of the responses is 500, and the user asked for 500 to - be treated specially to work around quirky frameworks, - assume file right away. */ - - if (no_500_dir && (res->code >= 500 || RPRES(req)->code >= 500)) { - DEBUG("Feels like a directory, but assuming file pivot as per -Z flag.\n"); - req->pivot->type = PIVOT_FILE; - goto schedule_next; - } - - req->pivot->type = PIVOT_DIR; - - /* Perform content checks before discarding the old payload. */ - - if (!same_page(&RPRES(req)->sig, &res->sig)) - content_checks(RPREQ(req), RPRES(req)); - - /* Replace original request, response with new data. */ - - destroy_request(RPREQ(req)); - - if (RPRES(req)) { - memcpy(&req->pivot->unk_sig, &RPRES(req)->sig, sizeof(struct http_sig)); - destroy_response(RPRES(req)); - } - - RPREQ(req) = req; - RPRES(req) = res; - - keep = 1; - - } - - } - -schedule_next: - - /* Well, we need to do something. */ - - if (req->pivot->type == PIVOT_DIR || req->pivot->type == PIVOT_SERV) - dir_retrieve_check(RPREQ(req), RPRES(req)); - else file_retrieve_check(RPREQ(req), RPRES(req)); - - return keep; -} - diff -Nru skipfish-2.02b/crawler.h skipfish-2.10b/crawler.h --- skipfish-2.02b/crawler.h 2011-07-03 05:51:36.000000000 +0000 +++ skipfish-2.10b/crawler.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -/* - skipfish - crawler state machine - -------------------------------- - - Author: Michal Zalewski - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#ifndef _HAVE_CRAWLER_H - -#include "types.h" -#include "http_client.h" -#include "database.h" - -extern u32 crawl_prob; /* Crawl probability (1-100%) */ -extern u8 no_parse, /* Disable HTML link detection */ - warn_mixed, /* Warn on mixed content? */ - no_fuzz_ext, /* Don't fuzz ext in dirs? */ - no_500_dir, /* Don't assume dirs on 500 */ - delete_bin, /* Don't keep binary responses */ - log_ext_urls; /* Log external URLs? */ - -/* Provisional debugging callback. */ - -u8 show_response(struct http_request* req, struct http_response* res); - -/* Asynchronous request callback for the initial PSTATE_FETCH request of - PIVOT_UNKNOWN resources. */ - -u8 unknown_retrieve_check(struct http_request* req, struct http_response* res); - -/* Asynchronous request callback for the initial PSTATE_FETCH request of - PIVOT_FILE resources. */ - -u8 file_retrieve_check(struct http_request* req, struct http_response* res); - -/* Asynchronous request callback for the initial PSTATE_FETCH request of - PIVOT_DIR resources. */ - -u8 dir_retrieve_check(struct http_request* req, struct http_response* res); - -/* Initializes the crawl of try_list items for a pivot point (if any still - not crawled). */ - -void param_trylist_start(struct pivot_desc* pv); - -/* Adds new name=value to form hints list. */ - -void add_form_hint(u8* name, u8* value); - -/* Macros to access various useful pivot points: */ - -#define MREQ(_x) (req->pivot->misc_req[_x]) -#define MRES(_x) (req->pivot->misc_res[_x]) -#define RPAR(_req) ((_req)->pivot->parent) -#define RPREQ(_req) ((_req)->pivot->req) -#define RPRES(_req) ((_req)->pivot->res) - -/* Debugging instrumentation for callbacks and callback helpers: */ - -#ifdef LOG_STDERR - -#define DEBUG_CALLBACK(_req, _res) do { \ - u8* _url = serialize_path(_req, 1, 1); \ - DEBUG("* %s: URL %s (%u, len %u)\n", __FUNCTION__, _url, (_res) ? \ - (_res)->code : 0, (_res) ? (_res)->pay_len : 0); \ - ck_free(_url); \ - } while (0) - -#define DEBUG_PIVOT(_text, _pv) do { \ - u8* _url = serialize_path((_pv)->req, 1, 1); \ - DEBUG("* %s: %s\n", _text, _url); \ - ck_free(_url); \ - } while (0) - -#define DEBUG_HELPER(_pv) do { \ - u8* _url = serialize_path((_pv)->req, 1, 1); \ - DEBUG("* %s: URL %s (%u, len %u)\n", __FUNCTION__, _url, (_pv)->res ? \ - (_pv)->res->code : 0, (_pv)->res ? (_pv)->res->pay_len : 0); \ - ck_free(_url); \ - } while (0) - -#else - -#define DEBUG_CALLBACK(_req, _res) -#define DEBUG_HELPER(_pv) -#define DEBUG_PIVOT(_text, _pv) - -#endif /* ^LOG_STDERR */ - -#endif /* !_HAVE_CRAWLER_H */ diff -Nru skipfish-2.02b/database.c skipfish-2.10b/database.c --- skipfish-2.02b/database.c 2011-07-02 06:35:04.000000000 +0000 +++ skipfish-2.10b/database.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,1499 +0,0 @@ -/* - skipfish - database & crawl management - -------------------------------------- - - Author: Michal Zalewski - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#define _VIA_DATABASE_C - -#include -#include -#include -#include - -#include "debug.h" -#include "config.h" -#include "types.h" -#include "http_client.h" -#include "database.h" -#include "crawler.h" -#include "analysis.h" -#include "string-inl.h" - -struct pivot_desc root_pivot; - -u8 **deny_urls, /* List of banned URL substrings */ - **allow_urls, /* List of required URL substrings */ - **allow_domains, /* List of allowed vhosts */ - **trust_domains, /* List of trusted vhosts */ - **skip_params; /* List of parameters to ignore */ - -u32 num_deny_urls, - num_allow_urls, - num_allow_domains, - num_trust_domains, - num_skip_params; - -u32 max_depth = MAX_DEPTH, - max_children = MAX_CHILDREN, - max_descendants = MAX_DESCENDANTS, - max_guesses = MAX_GUESSES; - -u8 dont_add_words; /* No auto dictionary building */ - -#define KW_SPECIFIC 0 -#define KW_GENERIC 1 -#define KW_GEN_AUTO 2 - -struct kw_entry { - u8* word; /* Keyword itself */ - u32 hit_cnt; /* Number of confirmed sightings */ - u8 is_ext; /* Is an extension? */ - u8 hit_already; /* Had its hit count bumped up? */ - u8 read_only; /* Read-only dictionary? */ - u8 class; /* KW_* */ - u32 total_age; /* Total age (in scan cycles) */ - u32 last_age; /* Age since last hit */ -}; - -static struct kw_entry* - keyword[WORD_HASH]; /* Keyword collection (bucketed) */ - -static u32 keyword_cnt[WORD_HASH]; /* Per-bucket keyword counts */ - -struct ext_entry { - u32 bucket; - u32 index; -}; - -static struct ext_entry *extension, /* Extension list */ - *sp_extension; - -static u8 **guess; /* Keyword candidate list */ - -u32 guess_cnt, /* Number of keyword candidates */ - extension_cnt, /* Number of extensions */ - sp_extension_cnt, /* Number of specific extensions */ - keyword_total_cnt, /* Current keyword count */ - keyword_orig_cnt; /* At-boot keyword count */ - -static u32 cur_xss_id, scan_id; /* Stored XSS manager IDs */ -static struct http_request** xss_req; /* Stored XSS manager req cache */ - - -/* Checks descendant counts. */ - -u8 descendants_ok(struct pivot_desc* pv) { - - if (pv->child_cnt > max_children) return 0; - - while (pv) { - if (pv->desc_cnt > max_descendants) return 0; - pv = pv->parent; - } - - return 1; - -} - - -void add_descendant(struct pivot_desc* pv) { - - while (pv) { - pv->desc_cnt++; - pv = pv->parent; - } - -} - -/* Maps a parsed URL (in req) to the pivot tree, creating or modifying nodes - as necessary, and scheduling them for crawl. This should be called only - on requests that were *not* yet retrieved. */ - -void maybe_add_pivot(struct http_request* req, struct http_response* res, - u8 via_link) { - - struct pivot_desc *cur = NULL; - - u32 i, par_cnt = 0, path_cnt = 0, last_val_cnt = 0, pno; - u8 ends_with_slash = 0; - u8* last_val = 0; - -#ifdef LOG_STDERR - - u8* url = serialize_path(req, 1, 1); - DEBUG("--- New pivot requested: %s (%d) --\n", url, via_link); - ck_free(url); - -#endif /* LOG_STDERR */ - - if (!req) FATAL("Invalid request data."); - - /* Initialize root pivot if not done already. */ - - if (!root_pivot.type) { - root_pivot.type = PIVOT_ROOT; - root_pivot.state = PSTATE_DONE; - root_pivot.linked = 2; - root_pivot.fuzz_par = -1; - root_pivot.name = ck_strdup((u8*)"[root]"); - } - - if (!url_allowed(req)) { url_scope++; return; } - - /* Count the number of path and query parameters in the request. */ - - for (i=0;ipar.c;i++) { - - if (QUERY_SUBTYPE(req->par.t[i]) || POST_SUBTYPE(req->par.t[i])) par_cnt++; - - if (PATH_SUBTYPE(req->par.t[i])) { - - if (req->par.t[i] == PARAM_PATH && !req->par.n[i] && !req->par.v[i][0]) - ends_with_slash = 1; - else - ends_with_slash = 0; - - if (req->par.v[i][0]) last_val = req->par.v[i]; - - path_cnt++; - - } - - /* While we're at it, try to learn new keywords. */ - - if (PATH_SUBTYPE(req->par.t[i]) || QUERY_SUBTYPE(req->par.t[i])) { - if (req->par.n[i]) wordlist_confirm_word(req->par.n[i]); - wordlist_confirm_word(req->par.v[i]); - } - - } - - /* Try to find pivot point for the host. */ - - for (i=0;ireq->host, (char*)req->host) && - cur->req->port == req->port && - cur->req->proto == req->proto) break; - } - - if (i == root_pivot.child_cnt) { - - /* No server pivot found, we need to create one. */ - - cur = ck_alloc(sizeof(struct pivot_desc)); - - root_pivot.child = ck_realloc(root_pivot.child, - (root_pivot.child_cnt + 1) * sizeof(struct pivot_desc*)); - - root_pivot.child[root_pivot.child_cnt++] = cur; - - add_descendant(&root_pivot); - - cur->type = PIVOT_SERV; - cur->state = PSTATE_FETCH; - cur->linked = 2; - cur->fuzz_par = -1; - cur->parent = &root_pivot; - - /* Copy the original request, sans path. Create a dummy - root dir entry instead. Derive pivot name by serializing - the URL of the associated stub request. */ - - cur->req = req_copy(req, cur, 0); - set_value(PARAM_PATH, NULL, (u8*)"", -1, &cur->req->par); - cur->name = serialize_path(cur->req, 1, 0); - cur->req->callback = dir_retrieve_check; - - /* If matching response not provided, schedule request. */ - - if (res && !par_cnt && path_cnt == 1) { - cur->res = res_copy(res); - dir_retrieve_check(req, cur->res); - } else async_request(cur->req); - - wordlist_confirm_word(req->host); - - } - - /* One way or the other, 'cur' now points to server pivot. Let's - walk through all path elements, and follow or create sub-pivots - for them. */ - - pno = 0; - - for (i=0;ipar.t[pno])) pno++; - - /* Bail out on the trailing NULL-'' indicator, if present. It is - used to denote a directory, and will always be the last path - element. */ - - if (i == path_cnt - 1 && req->par.t[pno] == PARAM_PATH && - !req->par.n[pno] && !req->par.v[pno][0]) break; - - pname = req->par.n[pno] ? req->par.n[pno] : req->par.v[pno]; - - ccnt = cur->child_cnt; - - /* Try to find a matching node. */ - - for (c=0;cchild[c]->name)) { - cur = cur->child[c]; - if (cur->linked < via_link) cur->linked = via_link; - break; - } - - if (c == ccnt) { - - /* Node not found. We need to create one. */ - - struct pivot_desc* n; - - /* Enforce user limits. */ - - if ((i + 1) >= max_depth || !descendants_ok(cur)) { - problem(PROB_LIMITS, req, res, (u8*)"Child node limit exceeded", cur, - 0); - return; - } - - /* Enforce duplicate name limits as a last-ditch effort to prevent - endless recursion. */ - - if (last_val && !strcmp((char*)last_val, (char*)req->par.v[pno])) - last_val_cnt++; - - if (last_val_cnt > MAX_SAMENAME) { - problem(PROB_LIMITS, req, res, - (u8*)"Duplicate name recursion limit exceeded", cur, 0); - return; - } - - /* Create and link back to parent. */ - - n = ck_alloc(sizeof(struct pivot_desc)); - - cur->child = ck_realloc(cur->child, (cur->child_cnt + 1) * - sizeof(struct pivot_desc*)); - - cur->child[cur->child_cnt++] = n; - - add_descendant(cur); - - n->parent = cur; - n->linked = via_link; - n->name = ck_strdup(pname); - - /* Copy the original request, then copy over path up to the - current point. */ - - n->req = req_copy(req, n, 0); - - for (c=0;c<=pno;c++) - if (PATH_SUBTYPE(req->par.t[c])) - set_value(req->par.t[c], req->par.n[c], req->par.v[c], -1, - &n->req->par); - - /* If name is parametric, indicate which parameter to fuzz. */ - - if (req->par.n[pno]) n->fuzz_par = n->req->par.c - 1; - else n->fuzz_par = -1; - - /* Do not fuzz out-of-scope or limit exceeded dirs... */ - - if ((i + 1) == max_depth - 1) n->no_fuzz = 1; - - if (i != path_cnt - 1) { - - /* This is not the last path segment, so let's assume a "directory" - (hierarchy node, to be more accurate), and schedule directory - tests. */ - - set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->req->par); - n->type = PIVOT_DIR; - n->req->callback = dir_retrieve_check; - - if (!url_allowed(n->req)) n->no_fuzz = 2; - - /* Subdirectory tests require parent directory 404 testing to complete - first. If these are still pending, wait a bit. */ - - if (cur->state > PSTATE_IPS_CHECK) { - - n->state = PSTATE_FETCH; - - /* If this actually *is* the last parameter, taking into account the - early-out hack mentioned above, and we were offered a response - - make use of it and don't schedule a new request. */ - - if (i == path_cnt - 2 && ends_with_slash && res) { - - n->res = res_copy(res); - dir_retrieve_check(n->req, n->res); - - } else async_request(n->req); - - } else n->state = PSTATE_PENDING; - - } else { - - /* Last segment. If no parameters, copy response body, mark type as - "unknown", schedule extra checks. */ - - if (!url_allowed(n->req)) n->no_fuzz = 2; - - if (!par_cnt) { - - n->type = PIVOT_UNKNOWN; - n->res = res_copy(res); - n->req->callback = unknown_retrieve_check; - - if (cur->state > PSTATE_IPS_CHECK) { - - n->state = PSTATE_FETCH; - - /* If we already have a response, call the callback directly - (it will schedule further requests on its own). */ - - if (!res) { - n->state = PSTATE_FETCH; - async_request(n->req); - } else unknown_retrieve_check(n->req, n->res); - - } else n->state = PSTATE_PENDING; - - } else { - - /* Parameters found. Assume file, schedule a fetch. */ - - n->type = PIVOT_FILE; - n->req->callback = file_retrieve_check; - - if (cur->state > PSTATE_IPS_CHECK) { - n->state = PSTATE_FETCH; - async_request(n->req); - } else n->state = PSTATE_PENDING; - - } - - } - - cur = n; - - } - - /* At this point, 'cur' points to a newly created or existing node - for the path element. If this element is parametric, make sure - that its value is on the 'try' list. */ - - if (req->par.n[pno]) { - - for (c=0;ctry_cnt;c++) - if (cur->try_list[c] && !(is_c_sens(cur) ? strcmp : strcasecmp) - ((char*)req->par.v[pno], (char*)cur->try_list[c])) break; - - /* Not found on the list - try adding. */ - - if (c == cur->try_cnt) { - - cur->try_list = ck_realloc(cur->try_list, (cur->try_cnt + 1) * - sizeof(u8*)); - cur->try_list[cur->try_cnt++] = ck_strdup(req->par.v[pno]); - - if (cur->state == PSTATE_DONE) - param_trylist_start(cur); - - } - - } - - pno++; - - } - - /* Phew! At this point, 'cur' points to the final path element, and now, - we just need to take care of parameters. Each parameter has its own - pivot point, and a full copy of the request - unless on the - param_skip list. */ - - pno = 0; - - for (i=0;ipar.t[pno]) && !POST_SUBTYPE(req->par.t[pno])) - pno++; - - pname = req->par.n[pno] ? req->par.n[pno] : (u8*)"[blank]"; - ccnt = cur->child_cnt; - - /* Try to find a matching node. */ - - for (c=0;cchild[c]->name)) { - cur = cur->child[c]; - if (cur->linked < via_link) cur->linked = via_link; - break; - } - - if (c == ccnt) { - - /* Node not found. We need to create one. */ - - struct pivot_desc* n; - - /* Enforce user limits. */ - - if (!descendants_ok(cur)) { - problem(PROB_LIMITS, req, res, (u8*)"Child node limit exceeded", cur, 0); - return; - } - - /* Create and link back to parent. */ - - n = ck_alloc(sizeof(struct pivot_desc)); - - cur->child = ck_realloc(cur->child, (cur->child_cnt + 1) * - sizeof(struct pivot_desc*)); - - cur->child[cur->child_cnt++] = n; - - add_descendant(cur); - - n->parent = cur; - n->type = PIVOT_PARAM; - n->linked = via_link; - n->name = ck_strdup(pname); - - /* Copy the original request, in full. Remember not to fuzz - file inputs. */ - - n->req = req_copy(req, n, 1); - n->fuzz_par = req->par.t[pno] == PARAM_POST_F ? -1 : pno; - n->res = res_copy(res); - - /* File fetcher does everything we need. */ - - n->req->callback = file_retrieve_check; - - if (cur->state > PSTATE_IPS_CHECK) { - n->state = PSTATE_FETCH; - if (res) file_retrieve_check(n->req, n->res); - else async_request(n->req); - } else n->state = PSTATE_PENDING; - - cur = n; - - } - - /* Ok, again, 'cur' is at the appropriate node. Make sure the - current value is on the 'try' list. */ - - for (c=0;ctry_cnt;c++) - if (cur->try_list[c] && !(is_c_sens(cur) ? strcmp : strcasecmp) - ((char*)req->par.v[pno], (char*)cur->try_list[c])) break; - - /* Not found on the list - try adding. */ - - if (c == cur->try_cnt) { - - cur->try_list = ck_realloc(cur->try_list, (cur->try_cnt + 1) * - sizeof(u8*)); - cur->try_list[cur->try_cnt++] = ck_strdup(req->par.v[pno]); - - if (cur->state == PSTATE_DONE) - param_trylist_start(cur); - - } - - /* Parameters are not hierarchical, so go back to the parent node. */ - - cur = cur->parent; - pno++; - - } - - /* Done, at last! */ - -} - - -/* Finds the host-level pivot point for global issues. */ - -struct pivot_desc* host_pivot(struct pivot_desc* pv) { - while (pv->parent && pv->parent->parent) pv = pv->parent; - return pv; -} - - -/* Gets case sensitivity info from the nearest DIR / SERV node. */ - -u8 is_c_sens(struct pivot_desc* pv) { - while (pv->parent && (pv->type != PIVOT_DIR || pv->type != PIVOT_SERV)) - pv = pv->parent; - return pv->csens; -} - - -/* Registers a problem, if not duplicate (res, extra may be NULL): */ - -void problem(u32 type, struct http_request* req, struct http_response* res, - u8* extra, struct pivot_desc* pv, u8 allow_dup) { - - u32 i; - - if (pv->type == PIVOT_NONE) FATAL("Uninitialized pivot point"); - if (type == PROB_NONE || !req) FATAL("Invalid issue data"); - - DEBUG("--- NEW PROBLEM - type: %u, extra: '%s' ---\n", type, extra); - - /* Check for duplicates */ - - if (!allow_dup) - for (i=0;iissue_cnt;i++) - if (type == pv->issue[i].type && !strcmp(extra ? (char*)extra : "", - pv->issue[i].extra ? (char*)pv->issue[i].extra : "")) return; - - pv->issue = ck_realloc(pv->issue, (pv->issue_cnt + 1) * - sizeof(struct issue_desc)); - - pv->issue[pv->issue_cnt].type = type; - pv->issue[pv->issue_cnt].extra = extra ? ck_strdup(extra) : NULL; - pv->issue[pv->issue_cnt].req = req_copy(req, pv, 1); - pv->issue[pv->issue_cnt].res = res_copy(res); - - /* Mark copies of half-baked requests as done. */ - - if (res && res->state < STATE_OK) { - pv->issue[pv->issue_cnt].res->state = STATE_OK; - ck_free(pv->issue[pv->issue_cnt].res->payload); - pv->issue[pv->issue_cnt].res->payload = - ck_strdup((u8*)"[...truncated...]\n"); - pv->issue[pv->issue_cnt].res->pay_len = 18; - } - - pv->issue_cnt++; - - /* Propagate parent issue counts. */ - - do { pv->desc_issue_cnt++; } while ((pv = pv->parent)); - -} - - - -/* Three functions to check if the URL is permitted under current rules - (0 = no, 1 = yes): */ - -u8 url_allowed_host(struct http_request* req) { - u32 i; - - for (i=0;ihost, allow_domains[i]); - - if (pos && strlen((char*)req->host) == - strlen((char*)allow_domains[i]) + (pos - req->host)) - return 1; - - } else - if (!strcasecmp((char*)req->host, (char*)allow_domains[i])) - return 1; - - } - - return 0; -} - - -u8 url_trusted_host(struct http_request* req) { - u32 i; - - i = 0; - - while (always_trust_domains[i]) { - - if (always_trust_domains[i][0] == '.') { - - u8* pos = inl_strcasestr(req->host, (u8*)always_trust_domains[i]); - - if (pos && strlen((char*)req->host) == - strlen(always_trust_domains[i]) + (pos - req->host)) - return 1; - } else - if (!strcasecmp((char*)req->host, (char*)always_trust_domains[i])) - return 1; - - i++; - - } - - for (i=0;ihost, trust_domains[i]); - - if (pos && strlen((char*)req->host) == - strlen((char*)trust_domains[i]) + (pos - req->host)) - return 1; - - } - - return 0; -} - - -u8 url_allowed(struct http_request* req) { - u8* url = serialize_path(req, 1, 0); - u32 i; - - /* Check blacklist first */ - - for (i=0;icode != sig2->code) return 0; - - for (i=0;idata[i] - sig2->data[i]; - u32 scale = sig1->data[i] + sig2->data[i]; - - if (abs(diff) > 1 + (scale * FP_T_REL / 100) || - abs(diff) > FP_T_ABS) - if (++bucket_fail > FP_B_FAIL) return 0; - - total_diff += diff; - total_scale += scale; - - } - - if (abs(total_diff) > 1 + (total_scale * FP_T_REL / 100)) - return 0; - - return 1; - -} - - -/* Dumps signature data: */ - -void dump_signature(struct http_sig* sig) { - u32 i; - - DEBUG("SIG %03d: ", sig->code); - for (i=0;idata[i]); - DEBUG("\n"); - -} - - -/* Debugs signature comparison: */ - -void debug_same_page(struct http_sig* sig1, struct http_sig* sig2) { - -#ifdef LOG_STDERR - - u32 i; - s32 total_diff = 0; - u32 total_scale = 0; - - dump_signature(sig1); - dump_signature(sig2); - - DEBUG(" "); - - for (i=0;idata[i] - sig2->data[i]; - DEBUG("[%04d] ", diff); - } - - DEBUG("(diff)\n "); - - for (i=0;idata[i] - sig2->data[i]; - u32 scale = sig1->data[i] + sig2->data[i]; - - if (abs(diff) > 1 + (scale * FP_T_REL / 100) || - abs(diff) > FP_T_ABS) - DEBUG("[FAIL] "); else DEBUG("[pass] "); - - total_diff += diff; - total_scale += scale; - } - - DEBUG("\n "); - - for (i=0;idata[i] + sig2->data[i]; - - DEBUG("[%04d] ", (u32)( 1 + (scale * FP_T_REL / 100))); - } - - DEBUG("(allow)\n"); - - DEBUG("Total diff: %d, scale %d, allow %d\n", - total_diff, total_scale, 1 + (u32)(total_scale * FP_T_REL / 100)); - -#endif /* LOG_STDERR */ - -} - - - -/* Keyword management: */ - - -/* Word hashing helper. */ - -static inline u32 hash_word(u8* str) { - register u32 ret = 0; - register u8 cur; - - if (str) - while ((cur=*str)) { - ret = ~ret ^ (cur) ^ - (cur << 5) ^ (~cur >> 5) ^ - (cur << 10) ^ (~cur << 15) ^ - (cur << 20) ^ (~cur << 25) ^ - (cur << 30); - str++; - } - - return ret % WORD_HASH; -} - - -/* Adds a new keyword candidate to the global "guess" list. This - list is case-sensitive. */ - -void wordlist_add_guess(u8* text) { - u32 target, i, kh; - - if (dont_add_words) return; - - /* Check if this is a bad or known guess or keyword. */ - - if (!text || !text[0] || strlen((char*)text) > MAX_WORD) return; - - for (i=0;i= max_guesses) target = R(max_guesses); - else target = guess_cnt++; - - ck_free(guess[target]); - guess[target] = ck_strdup(text); - -} - - -/* Adds a single, sanitized keyword to the list, or increases its hit count. - Keyword list is case-sensitive. */ - -static void wordlist_confirm_single(u8* text, u8 is_ext, u8 class, u8 read_only, - u32 add_hits, u32 total_age, u32 last_age) { - u32 kh, i; - - if (!text || !text[0] || strlen((char*)text) > MAX_WORD) return; - - /* Check if this is a known keyword. */ - - kh = hash_word(text); - - for (i=0;i 4) return; - - if (ppos != -1) { - - /* Period only? Too long? */ - if (tlen == 1 || tlen - ppos > 12) return; - - if (ppos && ppos != tlen - 1 && !isdigit(text[ppos] + 1)) { - wordlist_confirm_single(text + ppos + 1, 1, KW_GEN_AUTO, 0, 1, 0, 0); - text[ppos] = 0; - wordlist_confirm_single(text, 0, KW_GEN_AUTO, 0, 1, 0, 0); - text[ppos] = '.'; - return; - } - - } - - wordlist_confirm_single(text, 0, KW_GEN_AUTO, 0, 1, 0, 0); -} - - -/* Returns wordlist item at a specified offset (NULL if no more available). */ - -u8* wordlist_get_word(u32 offset, u8* specific) { - u32 cur_off = 0, kh; - - for (kh=0;kh offset) break; - cur_off += keyword_cnt[kh]; - } - - if (kh == WORD_HASH) return NULL; - - *specific = (keyword[kh][offset - cur_off].is_ext == 0 && - keyword[kh][offset - cur_off].class == KW_SPECIFIC); - - return keyword[kh][offset - cur_off].word; -} - - -/* Returns keyword candidate at a specified offset (or NULL). */ - -u8* wordlist_get_guess(u32 offset, u8* specific) { - if (offset >= guess_cnt) return NULL; - *specific = 0; - return guess[offset]; -} - - -/* Returns extension at a specified offset (or NULL). */ - -u8* wordlist_get_extension(u32 offset, u8 specific) { - - if (!specific) { - if (offset >= extension_cnt) return NULL; - return keyword[extension[offset].bucket][extension[offset].index].word; - } - - if (offset >= sp_extension_cnt) return NULL; - return keyword[sp_extension[offset].bucket][sp_extension[offset].index].word; -} - - -/* Loads keywords from file. */ - -void load_keywords(u8* fname, u8 read_only, u32 purge_age) { - FILE* in; - u32 hits, total_age, last_age, lines = 0; - u8 type[3]; - s32 fields; - u8 kword[MAX_WORD + 1]; - char fmt[32]; - - kword[MAX_WORD] = 0; - - in = fopen((char*)fname, "r"); - - if (!in) { - PFATAL("Unable to open wordlist '%s'", fname); - return; - } - - sprintf(fmt, "%%2s %%u %%u %%u %%%u[^\x01-\x1f]", MAX_WORD); - - while ((fields = fscanf(in, fmt, type, &hits, &total_age, &last_age, kword)) - == 5) { - - u8 class = KW_GEN_AUTO; - - if (type[0] != 'e' && type[0] != 'w') - FATAL("Wordlist '%s': bad keyword type in line %u.\n", fname, lines + 1); - - if (type[1] == 's') class = KW_SPECIFIC; else - if (type[1] == 'g') class = KW_GENERIC; - - if (!purge_age || last_age < purge_age) - wordlist_confirm_single(kword, (type[0] == 'e'), class, read_only, hits, - total_age + 1, last_age + 1); - lines++; - fgetc(in); /* sink \n */ - } - - if (fields != -1 && fields != 5) - FATAL("Wordlist '%s': syntax error in line %u.\n", fname, lines); - - if (!lines) - WARN("Wordlist '%s' contained no valid entries.", fname); - - keyword_orig_cnt = keyword_total_cnt; - - fclose(in); - -} - - -/* qsort() callback for sorting keywords in save_keywords(). */ - -static int keyword_sorter(const void* word1, const void* word2) { - if (((struct kw_entry*)word1)->hit_cnt < ((struct kw_entry*)word2)->hit_cnt) - return 1; - else if (((struct kw_entry*)word1)->hit_cnt == - ((struct kw_entry*)word2)->hit_cnt) - return 0; - else return -1; -} - - -/* Saves all keywords to a file. */ - -void save_keywords(u8* fname) { - struct stat st; - FILE* out; - s32 fd; - u32 i, kh; - u8* old; - -#ifndef O_NOFOLLOW -#define O_NOFOLLOW 0 -#endif /* !O_NOFOLLOW */ - - if (stat((char*)fname, &st) || !S_ISREG(st.st_mode)) return; - - /* First, sort the list. */ - - for (kh=0;khtype) { - case PIVOT_SERV: pivot_serv++; /* Fall through */ - case PIVOT_DIR: pivot_dir++; break; - case PIVOT_FILE: pivot_file++; break; - case PIVOT_PATHINFO: pivot_pinfo++; break; - case PIVOT_UNKNOWN: pivot_unknown++; break; - case PIVOT_PARAM: pivot_param++; break; - case PIVOT_VALUE: pivot_value++; break; - } - - if (pv->missing) pivot_missing++; - - switch (pv->state) { - case PSTATE_PENDING: pivot_pending++; break; - case PSTATE_FETCH ... PSTATE_IPS_CHECK: pivot_init++; break; - case PSTATE_CHILD_INJECT: - case PSTATE_PAR_INJECT: pivot_attack++; break; - case PSTATE_DONE: pivot_done++; break; - default: pivot_bf++; - } - - for (i=0;iissue_cnt;i++) - issue_cnt[PSEV(pv->issue[i].type)]++; - - for (i=0;ichild_cnt;i++) - pv_stat_crawl(pv->child[i]); - -} - - -void database_stats() { - - pivot_pending = pivot_init = pivot_attack = pivot_bf = pivot_pinfo = - pivot_done = pivot_serv = pivot_dir = pivot_file = pivot_param = - pivot_value = pivot_missing = pivot_unknown = pivot_cnt = 0; - - memset(issue_cnt, 0, sizeof(issue_cnt)); - - pv_stat_crawl(&root_pivot); - - SAY(cLBL "Database statistics:\n\n" - cGRA " Pivots : " cNOR "%u total, %u done (%.02f%%) \n" - cGRA " In progress : " cNOR "%u pending, %u init, %u attacks, " - "%u dict \n" - cGRA " Missing nodes : " cNOR "%u spotted\n" - cGRA " Node types : " cNOR "%u serv, %u dir, %u file, %u pinfo, " - "%u unkn, %u par, %u val\n" - cGRA " Issues found : " cNOR "%u info, %u warn, %u low, %u medium, " - "%u high impact\n" - cGRA " Dict size : " cNOR "%u words (%u new), %u extensions, " - "%u candidates\n", - pivot_cnt, pivot_done, pivot_cnt ? ((100.0 * pivot_done) / (pivot_cnt)) - : 0, pivot_pending, pivot_init, pivot_attack, pivot_bf, pivot_missing, - pivot_serv, pivot_dir, pivot_file, pivot_pinfo, pivot_unknown, - pivot_param, pivot_value, issue_cnt[1], issue_cnt[2], issue_cnt[3], - issue_cnt[4], issue_cnt[5], keyword_total_cnt, keyword_total_cnt - - keyword_orig_cnt, extension_cnt, guess_cnt); - -} - - -/* Dumps pivot database, for debugging purposes. */ - -void dump_pivots(struct pivot_desc* cur, u8 nest) { - - u8* indent = ck_alloc(nest + 1); - u8* url; - u32 i; - - if (!cur) cur = &root_pivot; - - memset(indent, ' ', nest); - - SAY(cBRI "\n%s== Pivot " cLGN "%s" cBRI " [%d] ==\n", - indent, cur->name, cur->dupe); - SAY(cGRA "%sType : " cNOR, indent); - - switch (cur->type) { - case PIVOT_NONE: SAY(cLRD "PIVOT_NONE (bad!)\n" cNOR); break; - case PIVOT_ROOT: SAY("PIVOT_ROOT\n"); break; - case PIVOT_SERV: SAY("PIVOT_SERV\n"); break; - case PIVOT_DIR: SAY("PIVOT_DIR\n"); break; - case PIVOT_FILE: SAY("PIVOT_FILE\n"); break; - case PIVOT_PATHINFO: SAY("PIVOT_PATHINFO\n"); break; - case PIVOT_VALUE: SAY("PIVOT_VALUE\n"); break; - case PIVOT_UNKNOWN: SAY("PIVOT_UNKNOWN\n"); break; - case PIVOT_PARAM: SAY("PIVOT_PARAM\n"); break; - default: SAY(cLRD " (bad!)\n" cNOR, cur->type); - } - - SAY(cGRA "%sState : " cNOR, indent); - - switch (cur->state) { - case PSTATE_NONE: SAY(cLRD "PSTATE_NONE (bad!)\n" cNOR); break; - case PSTATE_PENDING: SAY("PSTATE_PENDING\n"); break; - case PSTATE_FETCH: SAY("PSTATE_FETCH\n"); break; - case PSTATE_TYPE_CHECK: SAY("PSTATE_TYPE_CHECK\n"); break; - case PSTATE_404_CHECK: SAY("PSTATE_404_CHECK\n"); break; - case PSTATE_PARENT_CHECK: SAY("PSTATE_PARENT_CHECK\n"); break; - case PSTATE_IPS_CHECK: SAY("PSTATE_IPS_CHECK\n"); break; - case PSTATE_CHILD_INJECT: SAY("PSTATE_CHILD_INJECT\n"); break; - case PSTATE_CHILD_DICT: SAY("PSTATE_CHILD_DICT\n"); break; - case PSTATE_PAR_CHECK: SAY("PSTATE_PAR_CHECK\n"); break; - case PSTATE_PAR_INJECT: SAY("PSTATE_PAR_INJECT\n"); break; - case PSTATE_PAR_NUMBER: SAY("PSTATE_PAR_NUMBER\n"); break; - case PSTATE_PAR_DICT: SAY("PSTATE_PAR_DICT\n"); break; - case PSTATE_PAR_TRYLIST: SAY("PSTATE_PAR_TRYLIST\n"); break; - case PSTATE_DONE: SAY("PSTATE_DONE\n"); break; - default: SAY(cLRD " (bad!)\n" cNOR, - cur->state); - } - - if (cur->missing) { - if (cur->linked == 2) - SAY(cGRA "%sMissing : " cMGN "YES\n" cNOR, indent); - else - SAY(cGRA "%sMissing : " cLBL "YES (followed a dodgy link)\n" cNOR, - indent); - } - - SAY(cGRA "%sFlags : " cNOR "linked %u, case %u/%u, fuzz_par %d, ips %u, " - "sigs %u, reqs %u, desc %u/%u\n", indent, cur->linked, cur->csens, cur->c_checked, - cur->fuzz_par, cur->uses_ips, cur->r404_cnt, cur->pending, cur->child_cnt, - cur->desc_cnt); - - if (cur->req) { - url = serialize_path(cur->req, 1, 0); - SAY(cGRA "%sTarget : " cNOR "%s (" cYEL "%d" cNOR ")\n", indent, url, - cur->res ? cur->res->code : 0); - ck_free(url); - - if (cur->res) - SAY(cGRA "%sMIME : " cNOR "%s -> %s [" - "%s:%s]\n", indent, cur->res->header_mime ? cur->res->header_mime : - (u8*)"-", cur->res->sniffed_mime ? cur->res->sniffed_mime : (u8*)"-", - cur->res->header_charset ? cur->res->header_charset : (u8*)"-", - cur->res->meta_charset ? cur->res->meta_charset : (u8*)"-"); - } - - if (cur->try_cnt) { - SAY(cGRA "%sTry : " cNOR, indent); - for (i=0;itry_cnt;i++) - SAY("%s%s", cur->try_list[i], (i == cur->try_cnt - 1) ? "" : ", "); - SAY("\n"); - } - - /* Dump issues. */ - - for (i=0;iissue_cnt;i++) { - if (cur->issue[i].req) url = serialize_path(cur->issue[i].req, 0, 0); - else url = ck_strdup((u8*)"[none]"); - SAY(cGRA "%s-> Issue : " cNOR "type %d, extra '%s', URL: " cLGN "%s" - cNOR " (" cYEL "%u" cNOR ")\n", indent, cur->issue[i].type, - cur->issue[i].extra, url, cur->issue[i].res ? cur->issue[i].res->code - : 0); - ck_free(url); - } - - ck_free(indent); - - for (i=0;ichild_cnt;i++) - dump_pivots(cur->child[i], nest + 1); - -} - - -/* Cleans up pivot structure for memory debugging. */ - -static void dealloc_pivots(struct pivot_desc* cur) { - u32 i; - - if (!cur) cur = &root_pivot; - - if (cur->req) destroy_request(cur->req); - if (cur->res) destroy_response(cur->res); - - ck_free(cur->name); - - if (cur->try_cnt) { - for (i=0;itry_cnt;i++) ck_free(cur->try_list[i]); - ck_free(cur->try_list); - } - - if (cur->issue) { - for (i=0;iissue_cnt;i++) { - ck_free(cur->issue[i].extra); - if (cur->issue[i].req) destroy_request(cur->issue[i].req); - if (cur->issue[i].res) destroy_response(cur->issue[i].res); - } - ck_free(cur->issue); - } - - for (i=0;ichild_cnt;i++) - dealloc_pivots(cur->child[i]); - - ck_free(cur->child); - - if (cur != &root_pivot) ck_free(cur); - -} - - - -/* Creates a new XSS location tag. */ - -u8* new_xss_tag(u8* prefix) { - static u8* ret; - - if (ret) __DFL_ck_free(ret); - ret = __DFL_ck_alloc((prefix ? strlen((char*)prefix) : 0) + 32); - - if (!scan_id) scan_id = R(999999) + 1; - - sprintf((char*)ret, "%s-->\">'>'\"", - prefix ? prefix : (u8*)"", cur_xss_id, scan_id); - - return ret; - -} - - -/* Registers last XSS tag along with a completed http_request */ - -void register_xss_tag(struct http_request* req) { - xss_req = ck_realloc(xss_req, (cur_xss_id + 1) * - (sizeof(struct http_request*))); - xss_req[cur_xss_id] = req_copy(req, 0, 1); - cur_xss_id++; -} - - -/* Gets the request that submitted the tag in the first place */ - -struct http_request* get_xss_request(u32 xid, u32 sid) { - if (sid != scan_id || xid >= cur_xss_id) return NULL; - return xss_req[xid]; -} - - -/* Cleans up other database entries, for memory profiling purposes. */ - -void destroy_database() { - u32 i, kh; - - dealloc_pivots(0); - - ck_free(deny_urls); - ck_free(allow_urls); - ck_free(allow_domains); - ck_free(trust_domains); - - ck_free(addl_form_name); - ck_free(addl_form_value); - ck_free(skip_params); - - for (kh=0;kh - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#ifndef _HAVE_DATABASE_H -#define _HAVE_DATABASE_H - -#include "debug.h" -#include "config.h" -#include "types.h" -#include "http_client.h" - -/* Testing pivot points - used to organize the scan: */ - -/* - Pivot types: */ - -#define PIVOT_NONE 0 /* Invalid */ -#define PIVOT_ROOT 1 /* Root pivot */ - -#define PIVOT_SERV 10 /* Top-level host pivot */ -#define PIVOT_DIR 11 /* Directory pivot */ -#define PIVOT_FILE 12 /* File pivot */ -#define PIVOT_PATHINFO 13 /* PATH_INFO script */ - -#define PIVOT_UNKNOWN 18 /* (Currently) unknown type */ - -#define PIVOT_PARAM 100 /* Parameter fuzzing pivot */ -#define PIVOT_VALUE 101 /* Parameter value pivot */ - -/* - Pivot states (initialized to PENDING or FETCH by database.c, then - advanced by crawler.c): */ - -#define PSTATE_NONE 0 /* Invalid */ -#define PSTATE_PENDING 1 /* Pending parent tests */ - -#define PSTATE_FETCH 10 /* Initial data fetch */ - -#define PSTATE_TYPE_CHECK 20 /* Type check (unknown only) */ -#define PSTATE_404_CHECK 22 /* 404 check (dir only) */ -#define PSTATE_PARENT_CHECK 24 /* Parent check (dir only) */ -#define PSTATE_IPS_CHECK 26 /* IPS filtering check */ - -/* For directories only (injecting children nodes): */ - -#define PSTATE_CHILD_INJECT 50 /* Common security attacks */ -#define PSTATE_CHILD_DICT 55 /* Dictionary brute-force */ - -/* For parametric nodes only (replacing parameter value): */ - -#define PSTATE_PAR_CHECK 60 /* Parameter works at all? */ -#define PSTATE_PAR_INJECT 65 /* Common security attacks */ -#define PSTATE_PAR_NUMBER 70 /* Numeric ID traversal */ -#define PSTATE_PAR_DICT 75 /* Dictionary brute-force */ -#define PSTATE_PAR_TRYLIST 99 /* 'Try list' fetches */ - -#define PSTATE_DONE 100 /* Analysis done */ - -/* - Descriptor of a pivot point: */ - -struct pivot_desc { - u8 type; /* PIVOT_* */ - u8 state; /* PSTATE_* */ - u8 linked; /* Linked to? (0/1/2) */ - u8 missing; /* Determined to be missing? */ - - u8 csens; /* Case sensitive names? */ - u8 c_checked; /* csens check done? */ - - u8* name; /* Directory / script name */ - - struct http_request* req; /* Prototype HTTP request */ - - s32 fuzz_par; /* Fuzz target parameter */ - u8** try_list; /* Values to try */ - u32 try_cnt; /* Number of values to try */ - u32 try_cur; /* Last tested try list offs */ - - struct pivot_desc* parent; /* Parent pivot, if any */ - struct pivot_desc** child; /* List of children */ - u32 child_cnt; /* Number of children */ - u32 desc_cnt; /* Number of descendants */ - - struct issue_desc* issue; /* List of issues found */ - u32 issue_cnt; /* Number of issues */ - u32 desc_issue_cnt; /* Number of child issues */ - - struct http_response* res; /* HTTP response seen */ - - u8 res_varies; /* Response varies? */ - u8 bad_parent; /* Parent is well-behaved? */ - - /* Fuzzer and probe state data: */ - - u8 no_fuzz; /* Do not attepmt fuzzing. */ - u8 sure_dir; /* Very sure it's a dir? */ - - u8 uses_ips; /* Uses IPS filtering? */ - - u32 cur_key; /* Current keyword */ - u32 pdic_cur_key; /* ...for param dict */ - - u8 guess; /* Guess list keywords? */ - u8 pdic_guess; /* ...for param dict */ - - u32 pending; /* Number of pending reqs */ - u32 pdic_pending; /* ...for param dict */ - u32 num_pending; /* ...for numerical enum */ - u32 try_pending; /* ...for try list */ - u32 r404_pending; /* ...for 404 probes */ - u32 ck_pending; /* ...for behavior checks */ - - struct http_sig r404[MAX_404]; /* 404 response signatures */ - u32 r404_cnt; /* Number of sigs collected */ - struct http_sig unk_sig; /* Original "unknown" sig. */ - - /* Injection attack logic scratchpad: */ - -#define MISC_ENTRIES 10 - - struct http_request* misc_req[MISC_ENTRIES]; /* Saved requests */ - struct http_response* misc_res[MISC_ENTRIES]; /* Saved responses */ - u8 misc_cnt; /* Request / response count */ - - u8 i_skip[15]; /* Injection step skip flags */ - u8 i_skip_add; - u8 r404_skip; - - u8 bogus_par; /* fuzz_par does nothing? */ - - u8 ognl_check; /* OGNL check flags */ - - /* Reporting information: */ - - u32 total_child_cnt; /* All children */ - u32 total_issues[6]; /* Issues by severity */ - u8 dupe; /* Looks like a duplicate? */ - u32 pv_sig; /* Simple pivot signature */ - -}; - -extern struct pivot_desc root_pivot; - -/* Checks child / descendant limits. */ - -u8 descendants_ok(struct pivot_desc* pv); - -/* Increases descendant count. */ - -void add_descendant(struct pivot_desc* pv); - -/* Maps a parsed URL (in req) to the pivot tree, creating or modifying nodes - as necessary, and scheduling them for crawl; via_link should be 1 if the - URL came from an explicit link or user input, 0 if brute-forced. - - Always makes a copy of req, res; they can be destroyed safely; via_link - set to 2 means we're sure it's a valid link; 1 means "probably". */ - -void maybe_add_pivot(struct http_request* req, struct http_response* res, - u8 via_link); - -/* Creates a working copy of a request for use in db and crawl functions. If all - is 0, does not copy path, query parameters, or POST data (but still - copies headers); and forces GET method. */ - -struct http_request* req_copy(struct http_request* req, - struct pivot_desc* pv, u8 all); - -/* Finds the host-level pivot point for global issues. */ - -struct pivot_desc* host_pivot(struct pivot_desc* pv); - -/* Case sensitivity helper. */ - -u8 is_c_sens(struct pivot_desc* pv); - -/* Recorded security issues: */ - -/* - Informational data (non-specific security-relevant notes): */ - -#define PROB_NONE 0 /* Invalid */ - -#define PROB_SSL_CERT 10101 /* SSL issuer data */ - -#define PROB_NEW_COOKIE 10201 /* New cookie added */ -#define PROB_SERVER_CHANGE 10202 /* New Server: value seen */ -#define PROB_VIA_CHANGE 10203 /* New Via: value seen */ -#define PROB_X_CHANGE 10204 /* New X-*: value seen */ -#define PROB_NEW_404 10205 /* New 404 signatures seen */ - -#define PROB_NO_ACCESS 10401 /* Resource not accessible */ -#define PROB_AUTH_REQ 10402 /* Authentication requires */ -#define PROB_SERV_ERR 10403 /* Server error */ - -#define PROB_EXT_LINK 10501 /* External link */ -#define PROB_EXT_REDIR 10502 /* External redirector */ -#define PROB_MAIL_ADDR 10503 /* E-mail address seen */ -#define PROB_UNKNOWN_PROTO 10504 /* Unknown protocol in URL */ -#define PROB_UNKNOWN_FIELD 10505 /* Unknown form field */ - -#define PROB_FORM 10601 /* XSRF-safe form */ -#define PROB_PASS_FORM 10602 /* Password form */ -#define PROB_FILE_FORM 10603 /* File upload form */ - -#define PROB_USER_LINK 10701 /* User-supplied A link */ - -#define PROB_BAD_MIME_STAT 10801 /* Bad MIME type, low risk */ -#define PROB_GEN_MIME_STAT 10802 /* Generic MIME, low risk */ -#define PROB_BAD_CSET_STAT 10803 /* Bad charset, low risk */ -#define PROB_CFL_HDRS_STAT 10804 /* Conflicting hdr, low risk */ - -#define PROB_FUZZ_DIGIT 10901 /* Try fuzzing file name */ -#define PROB_OGNL 10902 /* OGNL-like parameter */ - -/* - Internal warnings (scan failures, etc): */ - -#define PROB_FETCH_FAIL 20101 /* Fetch failed. */ -#define PROB_LIMITS 20102 /* Crawl limits exceeded. */ - -#define PROB_404_FAIL 20201 /* Behavior probe failed. */ -#define PROB_PARENT_FAIL 20202 /* Parent behavior problem */ -#define PROB_IPS_FILTER 20203 /* IPS behavior detected. */ -#define PROB_IPS_FILTER_OFF 20204 /* IPS no longer active. */ -#define PROB_VARIES 20205 /* Response varies. */ - -#define PROB_NOT_DIR 20301 /* Node should be a dir. */ - -/* - Low severity issues (limited impact or check specificity): */ - -#define PROB_URL_AUTH 30101 /* HTTP credentials in URL */ - -#define PROB_SSL_CERT_DATE 30201 /* SSL cert date invalid */ -#define PROB_SSL_SELF_CERT 30202 /* Self-signed SSL cert */ -#define PROB_SSL_BAD_HOST 30203 /* Certificate host mismatch */ -#define PROB_SSL_NO_CERT 30204 /* No certificate data? */ - -#define PROB_DIR_LIST 30301 /* Dir listing bypass */ - -#define PROB_URL_REDIR 30401 /* URL redirection */ -#define PROB_USER_URL 30402 /* URL content inclusion */ - -#define PROB_EXT_OBJ 30501 /* External obj standalone */ -#define PROB_MIXED_OBJ 30502 /* Mixed content standalone */ - -#define PROB_VULN_FORM 30601 /* Form w/o anti-XSRF token */ -#define PROB_JS_XSSI 30602 /* Script with no XSSI prot */ - -#define PROB_CACHE_LOW 30701 /* Cache nit-picking */ - -#define PROB_PROLOGUE 30801 /* User-supplied prologue */ - -/* - Moderate severity issues (data compromise): */ - -#define PROB_BODY_XSS 40101 /* Document body XSS */ -#define PROB_URL_XSS 40102 /* URL-based XSS */ -#define PROB_HTTP_INJECT 40103 /* Header splitting */ -#define PROB_USER_URL_ACT 40104 /* Active user content */ - -#define PROB_EXT_SUB 40201 /* External subresource */ -#define PROB_MIXED_SUB 40202 /* Mixed content subresource */ - -#define PROB_BAD_MIME_DYN 40301 /* Bad MIME type, hi risk */ -#define PROB_GEN_MIME_DYN 40302 /* Generic MIME, hi risk */ -#define PROB_BAD_CSET_DYN 40304 /* Bad charset, hi risk */ -#define PROB_CFL_HDRS_DYN 40305 /* Conflicting hdr, hi risk */ - -#define PROB_FILE_POI 40401 /* Interesting file */ -#define PROB_ERROR_POI 40402 /* Interesting error message */ - -#define PROB_DIR_TRAVERSAL 40501 /* Directory traversal */ - -#define PROB_CACHE_HI 40601 /* Serious caching issues */ - -#define PROB_PASS_NOSSL 40701 /* Password form, no HTTPS */ - -/* - High severity issues (system compromise): */ - -#define PROB_XML_INJECT 50101 /* Backend XML injection */ -#define PROB_SH_INJECT 50102 /* Shell cmd injection */ -#define PROB_SQL_INJECT 50103 /* SQL injection */ -#define PROB_FMT_STRING 50104 /* Format string attack */ -#define PROB_INT_OVER 50105 /* Integer overflow attack */ - -#define PROB_SQL_PARAM 50201 /* SQL-like parameter */ - -#define PROB_PUT_DIR 50301 /* HTTP PUT accepted */ - -/* - Severity macros: */ - -#define PSEV(_x) ((_x) / 10000) -#define PSEV_INFO 1 -#define PSEV_WARN 2 -#define PSEV_LOW 3 -#define PSEV_MED 4 -#define PSEV_HI 5 - -/* Issue descriptor: */ - -struct issue_desc { - u32 type; /* PROB_* */ - u8* extra; /* Problem-specific string */ - struct http_request* req; /* HTTP request sent */ - struct http_response* res; /* HTTP response seen */ -}; - -/* Register a problem, if not duplicate (res, extra may be NULL): */ - -void problem(u32 type, struct http_request* req, struct http_response* res, - u8* extra, struct pivot_desc* pv, u8 allow_dup); - -/* Compare the checksums for two responses: */ - -u8 same_page(struct http_sig* sig1, struct http_sig* sig2); - -/* URL filtering constraints (exported from database.c): */ - -#define APPEND_FILTER(_ptr, _cnt, _val) do { \ - (_ptr) = ck_realloc(_ptr, ((_cnt) + 1) * sizeof(u8*)); \ - (_ptr)[_cnt] = (u8*)(_val); \ - (_cnt)++; \ - } while (0) - -extern u8 **deny_urls, **allow_urls, **allow_domains, - **trust_domains, **skip_params; - -extern u32 num_deny_urls, - num_allow_urls, - num_allow_domains, - num_trust_domains, - num_skip_params; - -extern u32 max_depth, - max_children, - max_descendants, - max_trylist, - max_guesses; - -extern u32 guess_cnt, - extension_cnt, - keyword_total_cnt, - keyword_orig_cnt; - -/* Check if the URL is permitted under current rules (0 = no, 1 = yes): */ - -u8 url_allowed_host(struct http_request* req); -u8 url_trusted_host(struct http_request* req); -u8 url_allowed(struct http_request* req); -u8 param_allowed(u8* pname); - -/* Keyword management: */ - -extern u8 dont_add_words; - -/* Adds a new keyword candidate to the "guess" list. */ - -void wordlist_add_guess(u8* text); - -/* Adds non-sanitized keywords to the list. */ - -void wordlist_confirm_word(u8* text); - -/* Returns wordlist item at a specified offset (NULL if no more available). */ - -u8* wordlist_get_word(u32 offset, u8* specific); - -/* Returns keyword candidate at a specified offset (or NULL). */ - -u8* wordlist_get_guess(u32 offset, u8* specific); - -/* Returns extension at a specified offset (or NULL). */ - -u8* wordlist_get_extension(u32 offset, u8 specific); - -/* Loads keywords from file. */ - -void load_keywords(u8* fname, u8 read_only, u32 purge_age); - -/* Saves all keywords to a file. */ - -void save_keywords(u8* fname); - -/* Database maintenance: */ - -/* Dumps pivot database, for debugging purposes. */ - -void dump_pivots(struct pivot_desc* cur, u8 nest); - -/* Deallocates all data, for debugging purposes. */ - -void destroy_database(); - -/* Prints DB stats. */ - -void database_stats(); - -/* XSS manager: */ - -/* Creates a new stored XSS id (buffer valid only until next call). */ - -u8* new_xss_tag(u8* prefix); - -/* Registers last XSS tag along with a completed http_request. */ - -void register_xss_tag(struct http_request* req); - -/* Returns request associated with a stored XSS id. */ - -struct http_request* get_xss_request(u32 xid, u32 sid); - -/* Dumps signature data: */ - -void dump_signature(struct http_sig* sig); - -/* Displays debug information for same_page() checks. */ - -void debug_same_page(struct http_sig* sig1, struct http_sig* sig2); - -#endif /* _HAVE_DATABASE_H */ - diff -Nru skipfish-2.02b/debian/changelog skipfish-2.10b/debian/changelog --- skipfish-2.02b/debian/changelog 2011-12-03 04:21:54.000000000 +0000 +++ skipfish-2.10b/debian/changelog 2013-03-18 15:13:58.000000000 +0000 @@ -1,8 +1,31 @@ -skipfish (2.02b-1build1) precise; urgency=low +skipfish (2.10b-1backbox1) precise; urgency=high - * No-change rebuild to drop spurious libsfgcc1 dependency on armhf. + * Upload for BackBox Linux - -- Adam Conrad Fri, 02 Dec 2011 21:21:54 -0700 + -- Raffaele Forte Mon, 18 Mar 2013 16:10:00 +0100 + +skipfish (2.10b-1) unstable; urgency=low + + * The Akamai Technologies paid volunteer days release. + * New upstream version. + * Bumped Standards-Version (no changes needed). + * Various path fixes because of upstream changes. + * Added new libpcre3-dev build dependency. + * Totally rewritten copyright file to comply with new copyright standard. + + -- Bartosz Fenski Thu, 20 Dec 2012 14:59:36 +0100 + +skipfish (2.05b-1) unstable; urgency=low + + * New upstream version. + + -- Bartosz Fenski Thu, 15 Mar 2012 10:18:34 +0100 + +skipfish (2.03b-1) unstable; urgency=low + + * New upstream version. + + -- Bartosz Fenski Wed, 15 Feb 2012 14:23:24 +0100 skipfish (2.02b-1) unstable; urgency=low diff -Nru skipfish-2.02b/debian/control skipfish-2.10b/debian/control --- skipfish-2.02b/debian/control 2011-03-29 16:16:58.000000000 +0000 +++ skipfish-2.10b/debian/control 2012-12-20 17:19:59.000000000 +0000 @@ -2,8 +2,8 @@ Section: web Priority: extra Maintainer: Bartosz Fenski -Build-Depends: debhelper (>= 7.0.50~), libssl-dev, zlib1g-dev, libidn11-dev -Standards-Version: 3.9.1 +Build-Depends: debhelper (>= 7.0.50~), libssl-dev, zlib1g-dev, libidn11-dev, libpcre3-dev +Standards-Version: 3.9.3 Homepage: http://code.google.com/p/skipfish/ Package: skipfish diff -Nru skipfish-2.02b/debian/copyright skipfish-2.10b/debian/copyright --- skipfish-2.02b/debian/copyright 2011-03-29 16:18:48.000000000 +0000 +++ skipfish-2.10b/debian/copyright 2012-12-20 17:17:05.000000000 +0000 @@ -1,76 +1,66 @@ -This work was packaged for Debian by: +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: skipfish +Source: http://code.google.com/p/skipfish/ + +Files: * +Copyright: 2009-2012 Google Inc + Michal Zalewski + Niels Heinen + Sebastian Roschke +License: Apache-2.0 + On Debian systems the full text of the Apache License can be found + in `/usr/share/common-licenses/Apache-2.0'. + +File: src/string-inl.h +Copyright: 1990, 1993 The Regents of the University of California. + Chris Torek +License: BSD + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +Files: assets/* +Copyright: 2006-2007 Everaldo Coelho, Crystal Project +License: LGPL-2.1 + On Debian systems the full copy of the GNU LESSER GENERAL PUBLIC LICENSE + version 2.1 can be found in `/usr/share/common-licenses/LGPL-2.1'. + +Files: debian/* +Copyright: 2010-2012 Bartosz Fenski +License: GPL-2+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". - Bartosz Fenski on Mon, 22 Mar 2010 18:41:42 +0100 - -It was downloaded from: - - http://code.google.com/p/skipfish/ - -Upstream Author(s): - - Michal Zalewski - -Copyright: - - Copyright 2009-2011 Google Inc, rights reserved. - -License: - - Apache-2.0 - -On Debian systems the full text of the Apache License can be found -in `/usr/share/common-licenses/Apache-2.0'. - -The Debian packaging is: - - Copyright (C) 2010 Bartosz Fenski - -and is licensed under the Apache-2.0 license, -see "/usr/share/common-licenses/Apache-2.0". - -License (for string-inl.h): - - Copyright (c) 1990, 1993 - The Regents of the University of California. All rights reserved. - - This code is derived from software contributed to Berkeley by - Chris Torek. - -and is licensed under the following BSD license: - - Copyright (c) 1990, 1993 - The Regents of the University of California. All rights reserved. - - This code is derived from software contributed to Berkeley by - Chris Torek. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the University nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. - -License (for icons used in HTML reports): - - Copyright (c) 2006-2007 Everaldo Coelho, Crystal Project - -and are licensed under the terms and conditions of the GNU LESSER GENERAL -PUBLIC LICENSE version 2.1, see "/usr/share/common-licenses/LGPL-2.1". diff -Nru skipfish-2.02b/debian/docs skipfish-2.10b/debian/docs --- skipfish-2.02b/debian/docs 2010-03-22 17:41:45.000000000 +0000 +++ skipfish-2.10b/debian/docs 2012-12-20 16:26:36.000000000 +0000 @@ -1 +1,2 @@ README +doc/*.txt diff -Nru skipfish-2.02b/debian/examples skipfish-2.10b/debian/examples --- skipfish-2.02b/debian/examples 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/debian/examples 2012-12-20 16:28:11.000000000 +0000 @@ -0,0 +1 @@ +config/example.conf diff -Nru skipfish-2.02b/debian/install skipfish-2.10b/debian/install --- skipfish-2.02b/debian/install 2010-03-23 11:49:49.000000000 +0000 +++ skipfish-2.10b/debian/install 2012-12-20 16:37:09.000000000 +0000 @@ -1,3 +1,4 @@ skipfish /usr/bin dictionaries /usr/share/skipfish/ assets /usr/share/skipfish/ +signatures /usr/share/skipfish/ diff -Nru skipfish-2.02b/debian/manpages skipfish-2.10b/debian/manpages --- skipfish-2.02b/debian/manpages 2010-04-05 16:43:49.000000000 +0000 +++ skipfish-2.10b/debian/manpages 2012-12-20 14:30:15.000000000 +0000 @@ -1 +1 @@ -skipfish.1 +doc/skipfish.1 diff -Nru skipfish-2.02b/debian/patches/debian-paths skipfish-2.10b/debian/patches/debian-paths --- skipfish-2.02b/debian/patches/debian-paths 2010-03-23 13:31:41.000000000 +0000 +++ skipfish-2.10b/debian/patches/debian-paths 2012-12-20 16:46:43.000000000 +0000 @@ -1,19 +1,53 @@ -Description: Debian changes in paths to directories and assets - Putting directories and assets in /usr/share/skipfish/. +Description: fixes paths for Debian + Fixes paths for Debian Author: Bartosz Fenski Origin: Debian Forwarded: not-needed +Last-Update: 2012-12-20 ---- skipfish-1.13b.orig/config.h -+++ skipfish-1.13b/config.h -@@ -29,8 +29,8 @@ +--- skipfish-2.10b.orig/signatures/signatures.conf ++++ skipfish-2.10b/signatures/signatures.conf +@@ -6,23 +6,23 @@ + # The mime signatures warn about server responses that have an interesting + # mime. For example anything that is presented as php-source will likely + # be interesting +-include signatures/mime.sigs ++include /usr/share/skipfish/signatures/mime.sigs + + # The files signature will use the content to determine if a response + # is an interesting file. For example, a SVN file. +-include signatures/files.sigs ++include /usr/share/skipfish/signatures/files.sigs + + # The messages signatures look for interesting server messages. Most + # are based on errors, such as caused by incorrect SQL queries or PHP + # execution failures. +-include signatures/messages.sigs ++include /usr/share/skipfish/signatures/messages.sigs + + # The apps signatures will help to find pages and applications who's + # functionality is a security risk by default. For example, phpinfo() + # pages that leak information or CMS admin interfaces. +-include signatures/apps.sigs ++include /usr/share/skipfish/signatures/apps.sigs + + # Context signatures are linked to injection tests. They look for strings + # that are relevant to the current injection test and help to highlight + # potential vulnerabilities. +-include signatures/context.sigs ++include /usr/share/skipfish/signatures/context.sigs +--- skipfish-2.10b.orig/src/config.h ++++ skipfish-2.10b/src/config.h +@@ -29,10 +29,10 @@ /* Default paths to runtime files: */ -#define ASSETS_DIR "assets" --#define DEF_WORDLIST "skipfish.wl" +#define ASSETS_DIR "/usr/share/skipfish/assets" -+#define DEF_WORDLIST "/usr/share/skipfish/dictionaries/minimal.wl" + + /* Default signature file */ +-#define SIG_FILE "signatures/signatures.conf" ++#define SIG_FILE "/usr/share/skipfish/signatures/signatures.conf" /* Various default settings for HTTP client (cmdline override): */ diff -Nru skipfish-2.02b/debian/rules skipfish-2.10b/debian/rules --- skipfish-2.02b/debian/rules 2010-03-28 09:59:13.000000000 +0000 +++ skipfish-2.10b/debian/rules 2012-03-02 09:19:32.000000000 +0000 @@ -1,10 +1,4 @@ #!/usr/bin/make -f -# -*- makefile -*- -# Sample debian/rules that uses debhelper. -# This file was originally written by Joey Hess and Craig Small. -# As a special exception, when this file is copied by dh-make into a -# dh-make output file, you may use that output file without restriction. -# This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 diff -Nru skipfish-2.02b/debug.h skipfish-2.10b/debug.h --- skipfish-2.02b/debug.h 2011-07-02 06:35:05.000000000 +0000 +++ skipfish-2.10b/debug.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,106 +0,0 @@ -/* - - skipfish - debugging and messaging macros - ----------------------------------------- - - Author: Michal Zalewski - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -*/ - -#ifndef _HAVE_DEBUG_H -#define _HAVE_DEBUG_H - -#include -#include "config.h" - -#ifdef USE_COLOR -# define cBLK "\x1b[0;30m" -# define cRED "\x1b[0;31m" -# define cGRN "\x1b[0;32m" -# define cBRN "\x1b[0;33m" -# define cBLU "\x1b[0;34m" -# define cMGN "\x1b[0;35m" -# define cCYA "\x1b[0;36m" -# define cNOR "\x1b[0;37m" -# define cGRA "\x1b[1;30m" -# define cLRD "\x1b[1;31m" -# define cLGN "\x1b[1;32m" -# define cYEL "\x1b[1;33m" -# define cLBL "\x1b[1;34m" -# define cPIN "\x1b[1;35m" -# define cLCY "\x1b[1;36m" -# define cBRI "\x1b[1;37m" -# define cRST "\x1b[0m" -#else -# define cBLK "" -# define cRED "" -# define cGRN "" -# define cBRN "" -# define cBLU "" -# define cMGN "" -# define cCYA "" -# define cNOR "" -# define cGRA "" -# define cLRD "" -# define cLGN "" -# define cYEL "" -# define cLBL "" -# define cPIN "" -# define cLCY "" -# define cBRI "" -# define cRST "" -#endif /* ^USE_COLOR */ - -#ifdef LOG_STDERR -# define DEBUG(x...) fprintf(stderr,x) -#else -# define DEBUG(x...) -#endif /* ^LOG_STDERR */ - -#define F_DEBUG(x...) fprintf(stderr,x) -#define SAY(x...) printf(x) - -#define WARN(x...) do { \ - F_DEBUG(cYEL "[!] WARNING: " cBRI x); \ - F_DEBUG(cRST "\n"); \ - } while (0) - -#define FATAL(x...) do { \ - F_DEBUG(cLRD "[-] PROGRAM ABORT : " cBRI x); \ - F_DEBUG(cLRD "\n Stop location : " cNOR "%s(), %s:%u\n" cRST, \ - __FUNCTION__, __FILE__, __LINE__); \ - exit(1); \ - } while (0) - -#define ABORT(x...) do { \ - F_DEBUG(cLRD "[-] PROGRAM ABORT : " cBRI x); \ - F_DEBUG(cLRD "\n Stop location : " cNOR "%s(), %s:%u\n" cRST, \ - __FUNCTION__, __FILE__, __LINE__); \ - abort(); \ - } while (0) - -#define PFATAL(x...) do { \ - F_DEBUG(cLRD "[-] SYSTEM ERROR : " cBRI x); \ - F_DEBUG(cLRD "\n Stop location : " cNOR "%s(), %s:%u\n", \ - __FUNCTION__, __FILE__, __LINE__); \ - perror(cLRD " OS message " cNOR); \ - F_DEBUG(cRST); \ - exit(1); \ - } while (0) - - -#endif /* ! _HAVE_DEBUG_H */ diff -Nru skipfish-2.02b/dictionaries/README-FIRST skipfish-2.10b/dictionaries/README-FIRST --- skipfish-2.02b/dictionaries/README-FIRST 2011-06-23 08:35:33.000000000 +0000 +++ skipfish-2.10b/dictionaries/README-FIRST 1970-01-01 00:00:00.000000000 +0000 @@ -1,229 +0,0 @@ -This directory contains four alternative, hand-picked Skipfish dictionaries. - -PLEASE READ THIS FILE CAREFULLY BEFORE PICKING ONE. This is *critical* to -getting good results in your work. - ------------------------- -Key command-line options ------------------------- - -The dictionary to be used by the tool can be specified with the -W option, -and must conform to the format outlined at the end of this document. If you -omit -W in the command line, 'skipfish.wl' is assumed. This file does not -exist by default. That part is by design: THE SCANNER WILL MODIFY THE -SUPPLIED FILE UNLESS SPECIFICALLY INSTRUCTED NOT TO. - -That's because the scanner automatically learns new keywords and extensions -based on any links discovered during the scan, and on random sampling of -site contents. The information is consequently stored in the dictionary -for future reuse, along with other bookkeeping information useful for -determining which keywords perform well, and which ones don't. - -All this means that it is very important to maintain a separate dictionary -for every separate set of unrelated target sites. Otherwise, undesirable -interference will occur. - -With this out of the way, let's quickly review the options that may be used -to fine-tune various aspects of dictionary handling: - - -L - do not automatically learn new keywords based on site content. - - This option should not be normally used in most scanning - modes; if supplied, the scanner will not be able to discover - and leverage technology-specific terms and file extensions - unique to the architecture of the targeted site. - - -G num - change jar size for keyword candidates. - - Up to candidates are randomly selected from site - content, and periodically retried during brute-force checks; - when one of them results in a unique non-404 response, it is - promoted to the dictionary proper. Unsuccessful candidates are - gradually replaced with new picks, and then discarded at the - end of the scan. The default jar size is 256. - - -V - prevent the scanner from updating the dictionary file. - - Normally, the primary read-write dictionary specified with the - -W option is updated at the end of the scan to add any newly - discovered keywords, and to update keyword usage stats. Using - this option eliminates this step. - - -R num - purge all dictionary entries that had no non-404 hits for - the last scans. - - This option prevents dictionary creep in repeated assessments, - but needs to be used with care: it will permanently nuke a - part of the dictionary! - - -Y - inhibit full ${filename}.${extension} brute-force. - - In this mode, the scanner will only brute-force one component - at a time, trying all possible keywords without any extension, - and then trying to append extensions to any otherwise discovered - content. - - This greatly improves scan times, but reduces coverage. Scan modes - 2 and 3 shown in the next section make use of this flag. - --------------- -Scanning modes --------------- - -The basic dictionary-dependent modes you should be aware of (in order of the -associated request cost): - -1) Orderly crawl with no DirBuster-like brute-force at all. In this mode, the - scanner will not discover non-linked resources such as /admin, - /index.php.old, etc: - - ./skipfish -W /dev/null -LV [...other options...] - - This mode is very fast, but *NOT* recommended for general use because of - limited coverage. Use only where absolutely necessary. - -2) Orderly scan with minimal extension brute-force. In this mode, the scanner - will not discover resources such as /admin, but will discover cases such as - /index.php.old (once index.php itself is spotted during an orderly crawl): - - cp dictionaries/extensions-only.wl dictionary.wl - ./skipfish -W dictionary.wl -Y [...other options...] - - This method is only slightly more request-intensive than #1, and therefore, - is a marginally better alternative in cases where time is of essence. It's - still not recommended for most uses. The cost is about 100 requests per - fuzzed location. - -3) Directory OR extension brute-force only. In this mode, the scanner will only - try fuzzing the file name, or the extension, at any given time - but will - not try every possible ${filename}.${extension} pair from the dictionary. - - cp dictionaries/complete.wl dictionary.wl - ./skipfish -W dictionary.wl -Y [...other options...] - - This method has a cost of about 2,000 requests per fuzzed location, and is - recommended for rapid assessments, especially when working with slow - servers or very large services. - -4) Normal dictionary fuzzing. In this mode, every ${filename}.${extension} - pair will be attempted. This mode is significantly slower, but offers - superior coverage, and should be your starting point. - - cp dictionaries/XXX.wl dictionary.wl - ./skipfish -W dictionary.wl [...other options...] - - Replace XXX with: - - minimal - recommended starter dictionary, mostly focusing on backup - and source files, about 60,000 requests per fuzzed location. - - medium - more thorough dictionary, focusing on common frameworks, - about 140,000 requests. - - complete - all-inclusive dictionary, over 210,000 requests. - - Normal fuzzing mode is recommended when doing thorough assessments of - reasonably responsive servers; but it may be prohibitively expensive - when dealing with very large or very slow sites. - ----------------------------------- -Using separate master dictionaries ----------------------------------- - -A recently introduced feature allows you to load any number of read-only -supplementary dictionaries in addition to the "main" read-write one (-W -dictionary.wl). - -This is a convenient way to isolate (and be able to continually update) your -customized top-level wordlist, whilst still acquiring site-specific data in -a separate file. The following syntax may be used to accomplish this: - -./skipfish -W initially_empty_site_specific_dict.wl -W +supplementary_dict1.wl \ - -W +supplementary_dict2.wl [...other options...] - -Only the main dictionary will be modified as a result of the scan, and only -newly discovered site-specific keywords will be appended there. - ----------------------------- -More about dictionary design ----------------------------- - -Each dictionary may consist of a number of extensions, and a number of -"regular" keywords. Extensions are considered just a special subset of the -keyword list. - -You can create custom dictionaries, conforming to this format: - -type hits total_age last_age keyword - -...where 'type' is either 'e' or 'w' (extension or keyword), followed by a -qualifier (explained below); 'hits' is the total number of times this keyword -resulted in a non-404 hit in all previous scans; 'total_age' is the number of scan -cycles this word is in the dictionary; 'last_age' is the number of scan cycles -since the last 'hit'; and 'keyword' is the actual keyword. - -Qualifiers alter the meaning of an entry in the following way: - - wg - generic keyword that is not associated with any specific server-side - technology. Examples include 'backup', 'accounting', or 'logs'. These - will be indiscriminately combined with every known extension (e.g., - 'backup.php') during the fuzzing process. - - ws - technology-specific keyword that are unlikely to have a random - extension; for example, with 'cgi-bin', testing for 'cgi-bin.php' is - usually a waste of time. Keywords tagged this way will be combined only - with a small set of technology-agnostic extensions - e.g., 'cgi-bin.old'. - - NOTE: Technology-specific keywords that in the real world, are always - paired with a single, specific extension, should be combined with said - extension in the 'ws' entry itself, rather than trying to accommodate - them with 'wg' rules. For example, 'MANIFEST.MF' is OK. - - eg - generic extension that is not specific to any well-defined technology, - or may pop-up in administrator- or developer-created auxiliary content. - Examples include 'bak', 'old', 'txt', or 'log'. - - es - technology-specific extension, such as 'php', or 'cgi', that are - unlikely to spontaneously accompany random 'ws' keywords. - -Skipfish leverages this distinction by only trying the following brute-force -combinations: - - /some/path/wg_keyword ('index') - /some/path/ws_keyword ('cgi-bin') - /some/path/wg_extension ('old') - /some/path/ws_extension ('php') - - /some/path/wg_keyword.wg_extension ('index.old') - /some/path/wg_keyword.ws_extension ('index.php') - - /some/path/ws_keyword.ws_extension ('cgi-bin.old') - -To decide between 'wg' and 'ws', consider if you are likely to ever encounter -files such as ${this_word}.php or ${this_word}.class. If not, tag the keyword -as 'ws'. - -Similarly, to decide between 'eg' and 'es', think about the possibility of -encoutering cgi-bin.${this_ext} or formmail.${this_ext}. If it seems unlikely, -choose 'es'. - -For your convenience, all legacy keywords and extensions, as well as any entries -detected automatically, will be stored in the dictionary with a '?' qualifier. -This is equivalent to 'g', and is meant to assist the user in reviewing and -triaging any automatically acquired dictionary data. - -Other notes about dictionaries: - - - Do not duplicate extensions as keywords - if you already have 'html' as an - 'e' entry, there is no need to also create a 'w' one. - - - There must be no empty or malformed lines, or comments, in the wordlist - file. Extension keywords must have no leading dot (e.g., 'exe', not '.exe'), - and all keywords should be NOT url-encoded (e.g., 'Program Files', not - 'Program%20Files'). No keyword should exceed 64 characters. - - - Tread carefully; poor wordlists are one of the reasons why some web security - scanners perform worse than expected. You will almost always be better off - narrowing down or selectively extending the supplied set (and possibly - contributing back your changes upstream!), than importing a giant wordlist - scored elsewhere. diff -Nru skipfish-2.02b/dictionaries/complete.wl skipfish-2.10b/dictionaries/complete.wl --- skipfish-2.02b/dictionaries/complete.wl 2011-07-03 05:59:36.000000000 +0000 +++ skipfish-2.10b/dictionaries/complete.wl 2012-12-04 13:27:53.000000000 +0000 @@ -1,2171 +1,2221 @@ -e 1 1 1 7z -e 1 1 1 as -e 1 1 1 asmx -e 1 1 1 asp -e 1 1 1 aspx -e 1 1 1 bak -e 1 1 1 bat -e 1 1 1 bin -e 1 1 1 bz2 -e 1 1 1 c -e 1 1 1 cc -e 1 1 1 cfg -e 1 1 1 cfm -e 1 1 1 cgi -e 1 1 1 class -e 1 1 1 cnf -e 1 1 1 conf -e 1 1 1 config -e 1 1 1 core -e 1 1 1 cpp -e 1 1 1 cs -e 1 1 1 csproj -e 1 1 1 csv -e 1 1 1 dat -e 1 1 1 db -e 1 1 1 dll -e 1 1 1 do -e 1 1 1 doc -e 1 1 1 dump -e 1 1 1 ep -e 1 1 1 err -e 1 1 1 error -e 1 1 1 exe -e 1 1 1 fcgi -e 1 1 1 gif -e 1 1 1 gz -e 1 1 1 htm -e 1 1 1 html -e 1 1 1 inc -e 1 1 1 ini -e 1 1 1 jar -e 1 1 1 java -e 1 1 1 jhtml -e 1 1 1 jpg -e 1 1 1 js -e 1 1 1 jsf -e 1 1 1 jsp -e 1 1 1 key -e 1 1 1 lib -e 1 1 1 log -e 1 1 1 lst -e 1 1 1 manifest -e 1 1 1 mdb -e 1 1 1 meta -e 1 1 1 msg -e 1 1 1 nsf -e 1 1 1 o -e 1 1 1 old -e 1 1 1 ora -e 1 1 1 orig -e 1 1 1 out -e 1 1 1 part -e 1 1 1 pdf -e 1 1 1 pem -e 1 1 1 pfx -e 1 1 1 php -e 1 1 1 php3 -e 1 1 1 phtml -e 1 1 1 pl -e 1 1 1 pm -e 1 1 1 png -e 1 1 1 ppt -e 1 1 1 properties -e 1 1 1 py -e 1 1 1 rar -e 1 1 1 rb -e 1 1 1 rhtml -e 1 1 1 rss -e 1 1 1 rtf -e 1 1 1 save -e 1 1 1 sh -e 1 1 1 shtml -e 1 1 1 so -e 1 1 1 sql -e 1 1 1 stackdump -e 1 1 1 svn-base -e 1 1 1 swf -e 1 1 1 tar -e 1 1 1 tar.bz2 -e 1 1 1 tar.gz -e 1 1 1 temp -e 1 1 1 test -e 1 1 1 tgz -e 1 1 1 tmp -e 1 1 1 tpl -e 1 1 1 trace -e 1 1 1 txt -e 1 1 1 vb -e 1 1 1 vbs -e 1 1 1 war -e 1 1 1 ws -e 1 1 1 xls -e 1 1 1 xml -e 1 1 1 xsl -e 1 1 1 xslt -e 1 1 1 yml -e 1 1 1 zip -w 1 1 1 .bash_history -w 1 1 1 .bashrc -w 1 1 1 .cvsignore -w 1 1 1 .history -w 1 1 1 .htaccess -w 1 1 1 .htpasswd -w 1 1 1 .passwd -w 1 1 1 .perf -w 1 1 1 .ssh -w 1 1 1 .svn -w 1 1 1 .web -w 1 1 1 0 -w 1 1 1 00 -w 1 1 1 01 -w 1 1 1 02 -w 1 1 1 03 -w 1 1 1 04 -w 1 1 1 05 -w 1 1 1 06 -w 1 1 1 07 -w 1 1 1 08 -w 1 1 1 09 -w 1 1 1 1 -w 1 1 1 10 -w 1 1 1 100 -w 1 1 1 1000 -w 1 1 1 1001 -w 1 1 1 101 -w 1 1 1 11 -w 1 1 1 12 -w 1 1 1 13 -w 1 1 1 14 -w 1 1 1 15 -w 1 1 1 1990 -w 1 1 1 1991 -w 1 1 1 1992 -w 1 1 1 1993 -w 1 1 1 1994 -w 1 1 1 1995 -w 1 1 1 1996 -w 1 1 1 1997 -w 1 1 1 1998 -w 1 1 1 1999 -w 1 1 1 2 -w 1 1 1 20 -w 1 1 1 200 -w 1 1 1 2000 -w 1 1 1 2001 -w 1 1 1 2002 -w 1 1 1 2003 -w 1 1 1 2004 -w 1 1 1 2005 -w 1 1 1 2006 -w 1 1 1 2007 -w 1 1 1 2008 -w 1 1 1 2009 -w 1 1 1 2010 -w 1 1 1 2011 -w 1 1 1 2012 -w 1 1 1 2013 -w 1 1 1 2014 -w 1 1 1 21 -w 1 1 1 22 -w 1 1 1 23 -w 1 1 1 24 -w 1 1 1 25 -w 1 1 1 2g -w 1 1 1 3 -w 1 1 1 300 -w 1 1 1 3g -w 1 1 1 4 -w 1 1 1 42 -w 1 1 1 5 -w 1 1 1 50 -w 1 1 1 500 -w 1 1 1 51 -w 1 1 1 6 -w 1 1 1 7 -w 1 1 1 8 -w 1 1 1 9 -w 1 1 1 ADM -w 1 1 1 ADMIN -w 1 1 1 Admin -w 1 1 1 AdminService -w 1 1 1 AggreSpy -w 1 1 1 AppsLocalLogin -w 1 1 1 AppsLogin -w 1 1 1 BUILD -w 1 1 1 CMS -w 1 1 1 CVS -w 1 1 1 DB -w 1 1 1 DMSDump -w 1 1 1 Documents and Settings -w 1 1 1 Entries -w 1 1 1 FCKeditor -w 1 1 1 JMXSoapAdapter -w 1 1 1 LICENSE -w 1 1 1 MANIFEST.MF -w 1 1 1 META-INF -w 1 1 1 Makefile -w 1 1 1 OA -w 1 1 1 OAErrorDetailPage -w 1 1 1 OA_HTML -w 1 1 1 Program Files -w 1 1 1 README -w 1 1 1 Rakefile -w 1 1 1 Readme -w 1 1 1 Recycled -w 1 1 1 Root -w 1 1 1 SERVER-INF -w 1 1 1 SOAPMonitor -w 1 1 1 SQL -w 1 1 1 SUNWmc -w 1 1 1 SiteScope -w 1 1 1 SiteServer -w 1 1 1 Spy -w 1 1 1 TEMP -w 1 1 1 TMP -w 1 1 1 TODO -w 1 1 1 Thumbs.db -w 1 1 1 WEB-INF -w 1 1 1 WS_FTP -w 1 1 1 XXX -w 1 1 1 _ -w 1 1 1 _adm -w 1 1 1 _admin -w 1 1 1 _common -w 1 1 1 _conf -w 1 1 1 _files -w 1 1 1 _include -w 1 1 1 _js -w 1 1 1 _mem_bin -w 1 1 1 _old -w 1 1 1 _pages -w 1 1 1 _private -w 1 1 1 _res -w 1 1 1 _source -w 1 1 1 _src -w 1 1 1 _test -w 1 1 1 _vti_bin -w 1 1 1 _vti_cnf -w 1 1 1 _vti_pvt -w 1 1 1 _vti_txt -w 1 1 1 _www -w 1 1 1 a -w 1 1 1 aa -w 1 1 1 aaa -w 1 1 1 abc -w 1 1 1 abc123 -w 1 1 1 abcd -w 1 1 1 abcd1234 -w 1 1 1 about -w 1 1 1 access -w 1 1 1 access-log -w 1 1 1 access-log.1 -w 1 1 1 access.1 -w 1 1 1 access_log -w 1 1 1 access_log.1 -w 1 1 1 accessibility -w 1 1 1 account -w 1 1 1 accounting -w 1 1 1 accounts -w 1 1 1 action -w 1 1 1 actions -w 1 1 1 active -w 1 1 1 activex -w 1 1 1 ad -w 1 1 1 adclick -w 1 1 1 add -w 1 1 1 addpost -w 1 1 1 addressbook -w 1 1 1 adm -w 1 1 1 admin -w 1 1 1 admin-console -w 1 1 1 admin_ -w 1 1 1 admins -w 1 1 1 adobe -w 1 1 1 adodb -w 1 1 1 ads -w 1 1 1 adv -w 1 1 1 advanced -w 1 1 1 advertise -w 1 1 1 advertising -w 1 1 1 affiliate -w 1 1 1 affiliates -w 1 1 1 agenda -w 1 1 1 agent -w 1 1 1 agents -w 1 1 1 ajax -w 1 1 1 akamai -w 1 1 1 album -w 1 1 1 albums -w 1 1 1 alcatel -w 1 1 1 alert -w 1 1 1 alerts -w 1 1 1 alias -w 1 1 1 aliases -w 1 1 1 all -w 1 1 1 all-wcprops -w 1 1 1 alpha -w 1 1 1 alumni -w 1 1 1 amanda -w 1 1 1 amazon -w 1 1 1 analog -w 1 1 1 android -w 1 1 1 announcement -w 1 1 1 announcements -w 1 1 1 annual -w 1 1 1 anon -w 1 1 1 anonymous -w 1 1 1 ansi -w 1 1 1 apac -w 1 1 1 apache -w 1 1 1 apexec -w 1 1 1 api -w 1 1 1 apis -w 1 1 1 app -w 1 1 1 appeal -w 1 1 1 appeals -w 1 1 1 append -w 1 1 1 appl -w 1 1 1 apple -w 1 1 1 appliation -w 1 1 1 applications -w 1 1 1 apps -w 1 1 1 apr -w 1 1 1 arch -w 1 1 1 archive -w 1 1 1 archives -w 1 1 1 array -w 1 1 1 art -w 1 1 1 article -w 1 1 1 articles -w 1 1 1 artwork -w 1 1 1 ascii -w 1 1 1 asdf -w 1 1 1 ashley -w 1 1 1 asset -w 1 1 1 assets -w 1 1 1 atom -w 1 1 1 attach -w 1 1 1 attachment -w 1 1 1 attachments -w 1 1 1 attachs -w 1 1 1 attic -w 1 1 1 auction -w 1 1 1 audio -w 1 1 1 audit -w 1 1 1 audits -w 1 1 1 auth -w 1 1 1 author -w 1 1 1 authorized_keys -w 1 1 1 authors -w 1 1 1 auto -w 1 1 1 automatic -w 1 1 1 automation -w 1 1 1 avatar -w 1 1 1 avatars -w 1 1 1 award -w 1 1 1 awards -w 1 1 1 awl -w 1 1 1 awstats -w 1 1 1 axis -w 1 1 1 axis-admin -w 1 1 1 axis2 -w 1 1 1 axis2-admin -w 1 1 1 b -w 1 1 1 b2b -w 1 1 1 b2c -w 1 1 1 back -w 1 1 1 backdoor -w 1 1 1 backend -w 1 1 1 backup -w 1 1 1 backups -w 1 1 1 balance -w 1 1 1 balances -w 1 1 1 bandwidth -w 1 1 1 bank -w 1 1 1 banking -w 1 1 1 banks -w 1 1 1 banner -w 1 1 1 banners -w 1 1 1 bar -w 1 1 1 base -w 1 1 1 bash -w 1 1 1 basic -w 1 1 1 basket -w 1 1 1 baskets -w 1 1 1 batch -w 1 1 1 baz -w 1 1 1 bb -w 1 1 1 bb-hist -w 1 1 1 bb-histlog -w 1 1 1 bboard -w 1 1 1 bbs -w 1 1 1 bean -w 1 1 1 beans -w 1 1 1 beehive -w 1 1 1 benefits -w 1 1 1 beta -w 1 1 1 bfc -w 1 1 1 big -w 1 1 1 bigip -w 1 1 1 bill -w 1 1 1 billing -w 1 1 1 binaries -w 1 1 1 binary -w 1 1 1 bins -w 1 1 1 bio -w 1 1 1 bios -w 1 1 1 biz -w 1 1 1 bkup -w 1 1 1 blah -w 1 1 1 blank -w 1 1 1 blog -w 1 1 1 blogger -w 1 1 1 bloggers -w 1 1 1 blogs -w 1 1 1 blogspot -w 1 1 1 board -w 1 1 1 boards -w 1 1 1 bob -w 1 1 1 bofh -w 1 1 1 book -w 1 1 1 books -w 1 1 1 boot -w 1 1 1 bottom -w 1 1 1 broken -w 1 1 1 broker -w 1 1 1 browse -w 1 1 1 browser -w 1 1 1 bs -w 1 1 1 bsd -w 1 1 1 bug -w 1 1 1 bugs -w 1 1 1 build -w 1 1 1 builder -w 1 1 1 buildr -w 1 1 1 bulk -w 1 1 1 bullet -w 1 1 1 business -w 1 1 1 button -w 1 1 1 buttons -w 1 1 1 buy -w 1 1 1 buynow -w 1 1 1 bypass -w 1 1 1 ca -w 1 1 1 cache -w 1 1 1 cal -w 1 1 1 calendar -w 1 1 1 call -w 1 1 1 callback -w 1 1 1 callee -w 1 1 1 caller -w 1 1 1 callin -w 1 1 1 calling -w 1 1 1 callout -w 1 1 1 camel -w 1 1 1 car -w 1 1 1 card -w 1 1 1 cards -w 1 1 1 career -w 1 1 1 careers -w 1 1 1 cars -w 1 1 1 cart -w 1 1 1 carts -w 1 1 1 cat -w 1 1 1 catalog -w 1 1 1 catalogs -w 1 1 1 catalyst -w 1 1 1 categories -w 1 1 1 category -w 1 1 1 catinfo -w 1 1 1 cats -w 1 1 1 ccbill -w 1 1 1 cd -w 1 1 1 cert -w 1 1 1 certificate -w 1 1 1 certificates -w 1 1 1 certified -w 1 1 1 certs -w 1 1 1 cf -w 1 1 1 cfcache -w 1 1 1 cfdocs -w 1 1 1 cfide -w 1 1 1 cfusion -w 1 1 1 cgi-bin -w 1 1 1 cgi-bin2 -w 1 1 1 cgi-home -w 1 1 1 cgi-local -w 1 1 1 cgi-pub -w 1 1 1 cgi-script -w 1 1 1 cgi-shl -w 1 1 1 cgi-sys -w 1 1 1 cgi-web -w 1 1 1 cgi-win -w 1 1 1 cgibin -w 1 1 1 cgiwrap -w 1 1 1 cgm-web -w 1 1 1 change -w 1 1 1 changed -w 1 1 1 changes -w 1 1 1 charge -w 1 1 1 charges -w 1 1 1 chat -w 1 1 1 chats -w 1 1 1 check -w 1 1 1 checking -w 1 1 1 checkout -w 1 1 1 checkpoint -w 1 1 1 checks -w 1 1 1 child -w 1 1 1 children -w 1 1 1 chris -w 1 1 1 chrome -w 1 1 1 cisco -w 1 1 1 cisweb -w 1 1 1 citrix -w 1 1 1 cl -w 1 1 1 claim -w 1 1 1 claims -w 1 1 1 classes -w 1 1 1 classified -w 1 1 1 classifieds -w 1 1 1 clear -w 1 1 1 click -w 1 1 1 clicks -w 1 1 1 client -w 1 1 1 clientaccesspolicy -w 1 1 1 clients -w 1 1 1 clk -w 1 1 1 clock -w 1 1 1 close -w 1 1 1 closed -w 1 1 1 closing -w 1 1 1 club -w 1 1 1 cluster -w 1 1 1 clusters -w 1 1 1 cmd -w 1 1 1 cms -w 1 1 1 cnt -w 1 1 1 cocoon -w 1 1 1 code -w 1 1 1 codec -w 1 1 1 codecs -w 1 1 1 codes -w 1 1 1 cognos -w 1 1 1 coldfusion -w 1 1 1 columns -w 1 1 1 com -w 1 1 1 comment -w 1 1 1 comments -w 1 1 1 commerce -w 1 1 1 commercial -w 1 1 1 common -w 1 1 1 communicator -w 1 1 1 community -w 1 1 1 compact -w 1 1 1 company -w 1 1 1 compat -w 1 1 1 complaint -w 1 1 1 complaints -w 1 1 1 compliance -w 1 1 1 component -w 1 1 1 components -w 1 1 1 compress -w 1 1 1 compressed -w 1 1 1 computer -w 1 1 1 computers -w 1 1 1 computing -w 1 1 1 conference -w 1 1 1 conferences -w 1 1 1 configs -w 1 1 1 console -w 1 1 1 consumer -w 1 1 1 contact -w 1 1 1 contacts -w 1 1 1 content -w 1 1 1 contents -w 1 1 1 contest -w 1 1 1 contract -w 1 1 1 contracts -w 1 1 1 control -w 1 1 1 controller -w 1 1 1 controlpanel -w 1 1 1 cookie -w 1 1 1 cookies -w 1 1 1 copies -w 1 1 1 copy -w 1 1 1 copyright -w 1 1 1 corp -w 1 1 1 corpo -w 1 1 1 corporate -w 1 1 1 corrections -w 1 1 1 count -w 1 1 1 counter -w 1 1 1 counters -w 1 1 1 counts -w 1 1 1 course -w 1 1 1 courses -w 1 1 1 cover -w 1 1 1 cpadmin -w 1 1 1 cpanel -w 1 1 1 cr -w 1 1 1 crack -w 1 1 1 crash -w 1 1 1 crashes -w 1 1 1 create -w 1 1 1 creator -w 1 1 1 credit -w 1 1 1 credits -w 1 1 1 crm -w 1 1 1 cron -w 1 1 1 crons -w 1 1 1 crontab -w 1 1 1 crontabs -w 1 1 1 crossdomain -w 1 1 1 crypt -w 1 1 1 crypto -w 1 1 1 css -w 1 1 1 current -w 1 1 1 custom -w 1 1 1 custom-log -w 1 1 1 custom_log -w 1 1 1 customer -w 1 1 1 customers -w 1 1 1 cute -w 1 1 1 cv -w 1 1 1 cxf -w 1 1 1 czcmdcvt -w 1 1 1 d -w 1 1 1 daemon -w 1 1 1 daily -w 1 1 1 dan -w 1 1 1 dana-na -w 1 1 1 data -w 1 1 1 database -w 1 1 1 databases -w 1 1 1 date -w 1 1 1 day -w 1 1 1 db_connect -w 1 1 1 dba -w 1 1 1 dbase -w 1 1 1 dblclk -w 1 1 1 dbman -w 1 1 1 dbmodules -w 1 1 1 dbutil -w 1 1 1 dc -w 1 1 1 dcforum -w 1 1 1 dclk -w 1 1 1 de -w 1 1 1 dealer -w 1 1 1 debug -w 1 1 1 decl -w 1 1 1 declaration -w 1 1 1 declarations -w 1 1 1 decode -w 1 1 1 decoder -w 1 1 1 decrypt -w 1 1 1 decrypted -w 1 1 1 decryption -w 1 1 1 def -w 1 1 1 default -w 1 1 1 defaults -w 1 1 1 definition -w 1 1 1 definitions -w 1 1 1 del -w 1 1 1 delete -w 1 1 1 deleted -w 1 1 1 demo -w 1 1 1 demos -w 1 1 1 denied -w 1 1 1 deny -w 1 1 1 design -w 1 1 1 desktop -w 1 1 1 desktops -w 1 1 1 detail -w 1 1 1 details -w 1 1 1 dev -w 1 1 1 devel -w 1 1 1 developer -w 1 1 1 developers -w 1 1 1 development -w 1 1 1 device -w 1 1 1 devices -w 1 1 1 devs -w 1 1 1 df -w 1 1 1 dialog -w 1 1 1 dialogs -w 1 1 1 diff -w 1 1 1 diffs -w 1 1 1 digest -w 1 1 1 digg -w 1 1 1 dir -w 1 1 1 dir-prop-base -w 1 1 1 directories -w 1 1 1 directory -w 1 1 1 dirs -w 1 1 1 disabled -w 1 1 1 disclaimer -w 1 1 1 display -w 1 1 1 django -w 1 1 1 dl -w 1 1 1 dm -w 1 1 1 dm-config -w 1 1 1 dms -w 1 1 1 dms0 -w 1 1 1 dns -w 1 1 1 docebo -w 1 1 1 dock -w 1 1 1 docroot -w 1 1 1 docs -w 1 1 1 document -w 1 1 1 documentation -w 1 1 1 documents -w 1 1 1 domain -w 1 1 1 domains -w 1 1 1 donate -w 1 1 1 done -w 1 1 1 doubleclick -w 1 1 1 down -w 1 1 1 download -w 1 1 1 downloader -w 1 1 1 downloads -w 1 1 1 drop -w 1 1 1 dropped -w 1 1 1 drupal -w 1 1 1 dummy -w 1 1 1 dumps -w 1 1 1 dvd -w 1 1 1 dwr -w 1 1 1 dyn -w 1 1 1 dynamic -w 1 1 1 e -w 1 1 1 e2fs -w 1 1 1 ear -w 1 1 1 ecommerce -w 1 1 1 edge -w 1 1 1 edit -w 1 1 1 editor -w 1 1 1 edits -w 1 1 1 edp -w 1 1 1 edu -w 1 1 1 education -w 1 1 1 ee -w 1 1 1 effort -w 1 1 1 efforts -w 1 1 1 egress -w 1 1 1 ejb -w 1 1 1 element -w 1 1 1 elements -w 1 1 1 em -w 1 1 1 email -w 1 1 1 emails -w 1 1 1 embed -w 1 1 1 embedded -w 1 1 1 emea -w 1 1 1 employees -w 1 1 1 employment -w 1 1 1 empty -w 1 1 1 emu -w 1 1 1 emulator -w 1 1 1 en -w 1 1 1 en_US -w 1 1 1 enc -w 1 1 1 encode -w 1 1 1 encoder -w 1 1 1 encrypt -w 1 1 1 encrypted -w 1 1 1 encyption -w 1 1 1 eng -w 1 1 1 engine -w 1 1 1 english -w 1 1 1 enterprise -w 1 1 1 entertainment -w 1 1 1 entries -w 1 1 1 entry -w 1 1 1 env -w 1 1 1 environ -w 1 1 1 environment -w 1 1 1 eric -w 1 1 1 error-log -w 1 1 1 error_log -w 1 1 1 errors -w 1 1 1 es -w 1 1 1 esale -w 1 1 1 esales -w 1 1 1 etc -w 1 1 1 eu -w 1 1 1 europe -w 1 1 1 event -w 1 1 1 events -w 1 1 1 evil -w 1 1 1 evt -w 1 1 1 ews -w 1 1 1 ex -w 1 1 1 example -w 1 1 1 examples -w 1 1 1 excalibur -w 1 1 1 exchange -w 1 1 1 exec -w 1 1 1 explorer -w 1 1 1 export -w 1 1 1 ext -w 1 1 1 ext2 -w 1 1 1 extern -w 1 1 1 external -w 1 1 1 extras -w 1 1 1 ezshopper -w 1 1 1 f -w 1 1 1 fabric -w 1 1 1 face -w 1 1 1 facebook -w 1 1 1 faces -w 1 1 1 faculty -w 1 1 1 fail -w 1 1 1 failure -w 1 1 1 fake -w 1 1 1 family -w 1 1 1 faq -w 1 1 1 faqs -w 1 1 1 favorite -w 1 1 1 favorites -w 1 1 1 fb -w 1 1 1 fbook -w 1 1 1 fc -w 1 1 1 fcgi-bin -w 1 1 1 feature -w 1 1 1 features -w 1 1 1 feed -w 1 1 1 feedback -w 1 1 1 feeds -w 1 1 1 felix -w 1 1 1 fetch -w 1 1 1 field -w 1 1 1 fields -w 1 1 1 file -w 1 1 1 fileadmin -w 1 1 1 filelist -w 1 1 1 files -w 1 1 1 filez -w 1 1 1 finance -w 1 1 1 financial -w 1 1 1 find -w 1 1 1 finger -w 1 1 1 firefox -w 1 1 1 firewall -w 1 1 1 firmware -w 1 1 1 first -w 1 1 1 fixed -w 1 1 1 flags -w 1 1 1 flash -w 1 1 1 flow -w 1 1 1 flows -w 1 1 1 flv -w 1 1 1 fn -w 1 1 1 folder -w 1 1 1 folders -w 1 1 1 font -w 1 1 1 fonts -w 1 1 1 foo -w 1 1 1 footer -w 1 1 1 footers -w 1 1 1 form -w 1 1 1 format -w 1 1 1 formatting -w 1 1 1 formmail -w 1 1 1 forms -w 1 1 1 forrest -w 1 1 1 fortune -w 1 1 1 forum -w 1 1 1 forum1 -w 1 1 1 forum2 -w 1 1 1 forumdisplay -w 1 1 1 forums -w 1 1 1 forward -w 1 1 1 foto -w 1 1 1 foundation -w 1 1 1 fr -w 1 1 1 frame -w 1 1 1 frames -w 1 1 1 framework -w 1 1 1 free -w 1 1 1 freebsd -w 1 1 1 friend -w 1 1 1 friends -w 1 1 1 frob -w 1 1 1 frontend -w 1 1 1 fs -w 1 1 1 ftp -w 1 1 1 fuck -w 1 1 1 fuckoff -w 1 1 1 fuckyou -w 1 1 1 full -w 1 1 1 fun -w 1 1 1 func -w 1 1 1 funcs -w 1 1 1 function -w 1 1 1 functions -w 1 1 1 fund -w 1 1 1 funding -w 1 1 1 funds -w 1 1 1 fusion -w 1 1 1 fw -w 1 1 1 g -w 1 1 1 gadget -w 1 1 1 gadgets -w 1 1 1 galleries -w 1 1 1 gallery -w 1 1 1 game -w 1 1 1 games -w 1 1 1 ganglia -w 1 1 1 garbage -w 1 1 1 gateway -w 1 1 1 gb -w 1 1 1 geeklog -w 1 1 1 general -w 1 1 1 geronimo -w 1 1 1 get -w 1 1 1 getaccess -w 1 1 1 getjobid -w 1 1 1 gfx -w 1 1 1 gid -w 1 1 1 git -w 1 1 1 gitweb -w 1 1 1 glimpse -w 1 1 1 global -w 1 1 1 globals -w 1 1 1 glossary -w 1 1 1 go -w 1 1 1 goaway -w 1 1 1 google -w 1 1 1 government -w 1 1 1 gprs -w 1 1 1 grant -w 1 1 1 grants -w 1 1 1 graphics -w 1 1 1 group -w 1 1 1 groupcp -w 1 1 1 groups -w 1 1 1 gsm -w 1 1 1 guest -w 1 1 1 guestbook -w 1 1 1 guests -w 1 1 1 guide -w 1 1 1 guides -w 1 1 1 gump -w 1 1 1 gwt -w 1 1 1 h -w 1 1 1 hack -w 1 1 1 hacker -w 1 1 1 hacking -w 1 1 1 hackme -w 1 1 1 hadoop -w 1 1 1 hardcore -w 1 1 1 hardware -w 1 1 1 harmony -w 1 1 1 head -w 1 1 1 header -w 1 1 1 headers -w 1 1 1 health -w 1 1 1 hello -w 1 1 1 help -w 1 1 1 helper -w 1 1 1 helpers -w 1 1 1 hi -w 1 1 1 hidden -w 1 1 1 hide -w 1 1 1 high -w 1 1 1 hipaa -w 1 1 1 hire -w 1 1 1 history -w 1 1 1 hit -w 1 1 1 hits -w 1 1 1 hole -w 1 1 1 home -w 1 1 1 homepage -w 1 1 1 hop -w 1 1 1 horde -w 1 1 1 hosting -w 1 1 1 hosts -w 1 1 1 hour -w 1 1 1 hourly -w 1 1 1 howto -w 1 1 1 hp -w 1 1 1 hr -w 1 1 1 hta -w 1 1 1 htbin -w 1 1 1 htdoc -w 1 1 1 htdocs -w 1 1 1 htpasswd -w 1 1 1 http -w 1 1 1 httpd -w 1 1 1 https -w 1 1 1 httpuser -w 1 1 1 hu -w 1 1 1 hyper -w 1 1 1 i -w 1 1 1 ia -w 1 1 1 ibm -w 1 1 1 icat -w 1 1 1 ico -w 1 1 1 icon -w 1 1 1 icons -w 1 1 1 id -w 1 1 1 idea -w 1 1 1 ideas -w 1 1 1 ids -w 1 1 1 ie -w 1 1 1 iframe -w 1 1 1 ig -w 1 1 1 ignore -w 1 1 1 iisadmin -w 1 1 1 iisadmpwd -w 1 1 1 iissamples -w 1 1 1 image -w 1 1 1 imagefolio -w 1 1 1 images -w 1 1 1 img -w 1 1 1 imgs -w 1 1 1 imp -w 1 1 1 import -w 1 1 1 important -w 1 1 1 in -w 1 1 1 inbound -w 1 1 1 incl -w 1 1 1 include -w 1 1 1 includes -w 1 1 1 incoming -w 1 1 1 incubator -w 1 1 1 index -w 1 1 1 index1 -w 1 1 1 index2 -w 1 1 1 index_1 -w 1 1 1 index_2 -w 1 1 1 inetpub -w 1 1 1 inetsrv -w 1 1 1 inf -w 1 1 1 info -w 1 1 1 information -w 1 1 1 ingress -w 1 1 1 init -w 1 1 1 inline -w 1 1 1 input -w 1 1 1 inquire -w 1 1 1 inquiries -w 1 1 1 inquiry -w 1 1 1 insert -w 1 1 1 install -w 1 1 1 int -w 1 1 1 intel -w 1 1 1 intelligence -w 1 1 1 inter -w 1 1 1 interim -w 1 1 1 intermediate -w 1 1 1 internal -w 1 1 1 international -w 1 1 1 internet -w 1 1 1 intl -w 1 1 1 intra -w 1 1 1 intranet -w 1 1 1 intro -w 1 1 1 ip -w 1 1 1 ipc -w 1 1 1 iphone -w 1 1 1 ips -w 1 1 1 irc -w 1 1 1 is -w 1 1 1 isapi -w 1 1 1 iso -w 1 1 1 issues -w 1 1 1 it -w 1 1 1 item -w 1 1 1 items -w 1 1 1 j -w 1 1 1 j2ee -w 1 1 1 j2me -w 1 1 1 jacob -w 1 1 1 jakarta -w 1 1 1 java-plugin -w 1 1 1 javadoc -w 1 1 1 javascript -w 1 1 1 javax -w 1 1 1 jboss -w 1 1 1 jbossas -w 1 1 1 jbossws -w 1 1 1 jdbc -w 1 1 1 jennifer -w 1 1 1 jessica -w 1 1 1 jigsaw -w 1 1 1 jira -w 1 1 1 jj -w 1 1 1 jmx-console -w 1 1 1 job -w 1 1 1 jobs -w 1 1 1 joe -w 1 1 1 john -w 1 1 1 join -w 1 1 1 joomla -w 1 1 1 journal -w 1 1 1 jp -w 1 1 1 jpa -w 1 1 1 jre -w 1 1 1 jrun -w 1 1 1 json -w 1 1 1 jsso -w 1 1 1 jsx -w 1 1 1 juniper -w 1 1 1 junk -w 1 1 1 jvm -w 1 1 1 k -w 1 1 1 kboard -w 1 1 1 keep -w 1 1 1 kernel -w 1 1 1 keygen -w 1 1 1 keys -w 1 1 1 kids -w 1 1 1 kill -w 1 1 1 known_hosts -w 1 1 1 l -w 1 1 1 la -w 1 1 1 labs -w 1 1 1 lang -w 1 1 1 large -w 1 1 1 law -w 1 1 1 layout -w 1 1 1 layouts -w 1 1 1 ldap -w 1 1 1 leader -w 1 1 1 leaders -w 1 1 1 left -w 1 1 1 legacy -w 1 1 1 legal -w 1 1 1 lenya -w 1 1 1 letters -w 1 1 1 level -w 1 1 1 lg -w 1 1 1 library -w 1 1 1 libs -w 1 1 1 license -w 1 1 1 licenses -w 1 1 1 limit -w 1 1 1 line -w 1 1 1 link -w 1 1 1 links -w 1 1 1 linux -w 1 1 1 list -w 1 1 1 listinfo -w 1 1 1 lists -w 1 1 1 live -w 1 1 1 lo -w 1 1 1 loader -w 1 1 1 loading -w 1 1 1 loc -w 1 1 1 local -w 1 1 1 location -w 1 1 1 lock -w 1 1 1 locked -w 1 1 1 log4j -w 1 1 1 log4net -w 1 1 1 logfile -w 1 1 1 logger -w 1 1 1 logging -w 1 1 1 login -w 1 1 1 logins -w 1 1 1 logo -w 1 1 1 logoff -w 1 1 1 logon -w 1 1 1 logos -w 1 1 1 logout -w 1 1 1 logs -w 1 1 1 lost -w 1 1 1 lost+found -w 1 1 1 low -w 1 1 1 ls -w 1 1 1 lucene -w 1 1 1 m -w 1 1 1 mac -w 1 1 1 macromedia -w 1 1 1 maestro -w 1 1 1 mail -w 1 1 1 mailer -w 1 1 1 mailing -w 1 1 1 mailman -w 1 1 1 mails -w 1 1 1 main -w 1 1 1 mambo -w 1 1 1 manage -w 1 1 1 managed -w 1 1 1 management -w 1 1 1 manager -w 1 1 1 manual -w 1 1 1 manuals -w 1 1 1 map -w 1 1 1 maps -w 1 1 1 mark -w 1 1 1 marketing -w 1 1 1 master -w 1 1 1 master.passwd -w 1 1 1 match -w 1 1 1 matrix -w 1 1 1 matt -w 1 1 1 maven -w 1 1 1 mbox -w 1 1 1 me -w 1 1 1 media -w 1 1 1 medium -w 1 1 1 mem -w 1 1 1 member -w 1 1 1 members -w 1 1 1 membership -w 1 1 1 memory -w 1 1 1 menu -w 1 1 1 menus -w 1 1 1 message -w 1 1 1 messages -w 1 1 1 messaging -w 1 1 1 michael -w 1 1 1 microsoft -w 1 1 1 migrate -w 1 1 1 migrated -w 1 1 1 migration -w 1 1 1 mina -w 1 1 1 mini -w 1 1 1 minute -w 1 1 1 mirror -w 1 1 1 mirrors -w 1 1 1 misc -w 1 1 1 mission -w 1 1 1 mix -w 1 1 1 mlist -w 1 1 1 mms -w 1 1 1 mobi -w 1 1 1 mobile -w 1 1 1 mock -w 1 1 1 mod -w 1 1 1 modify -w 1 1 1 mods -w 1 1 1 module -w 1 1 1 modules -w 1 1 1 mojo -w 1 1 1 money -w 1 1 1 monitor -w 1 1 1 monitoring -w 1 1 1 monitors -w 1 1 1 month -w 1 1 1 monthly -w 1 1 1 more -w 1 1 1 motd -w 1 1 1 move -w 1 1 1 moved -w 1 1 1 movie -w 1 1 1 movies -w 1 1 1 mp -w 1 1 1 mp3 -w 1 1 1 mp3s -w 1 1 1 ms -w 1 1 1 ms-sql -w 1 1 1 msadc -w 1 1 1 msadm -w 1 1 1 msft -w 1 1 1 msie -w 1 1 1 msql -w 1 1 1 mssql -w 1 1 1 mta -w 1 1 1 multi -w 1 1 1 multimedia -w 1 1 1 music -w 1 1 1 mx -w 1 1 1 my -w 1 1 1 myadmin -w 1 1 1 myfaces -w 1 1 1 myphpnuke -w 1 1 1 mysql -w 1 1 1 mysqld -w 1 1 1 n -w 1 1 1 nav -w 1 1 1 navigation -w 1 1 1 nc -w 1 1 1 net -w 1 1 1 netbsd -w 1 1 1 netcat -w 1 1 1 nethome -w 1 1 1 nets -w 1 1 1 network -w 1 1 1 networking -w 1 1 1 new -w 1 1 1 news -w 1 1 1 newsletter -w 1 1 1 newsletters -w 1 1 1 newticket -w 1 1 1 next -w 1 1 1 nfs -w 1 1 1 nice -w 1 1 1 nl -w 1 1 1 nobody -w 1 1 1 node -w 1 1 1 none -w 1 1 1 note -w 1 1 1 notes -w 1 1 1 notification -w 1 1 1 notifications -w 1 1 1 notified -w 1 1 1 notifier -w 1 1 1 notify -w 1 1 1 novell -w 1 1 1 ns -w 1 1 1 nude -w 1 1 1 nuke -w 1 1 1 nul -w 1 1 1 null -w 1 1 1 oa_servlets -w 1 1 1 oauth -w 1 1 1 obdc -w 1 1 1 obsolete -w 1 1 1 obsoleted -w 1 1 1 odbc -w 1 1 1 ode -w 1 1 1 oem -w 1 1 1 ofbiz -w 1 1 1 office -w 1 1 1 offices -w 1 1 1 onbound -w 1 1 1 online -w 1 1 1 op -w 1 1 1 open -w 1 1 1 openbsd -w 1 1 1 opencart -w 1 1 1 opendir -w 1 1 1 openejb -w 1 1 1 openjpa -w 1 1 1 opera -w 1 1 1 operations -w 1 1 1 opinion -w 1 1 1 oprocmgr-status -w 1 1 1 opt -w 1 1 1 option -w 1 1 1 options -w 1 1 1 oracle -w 1 1 1 oracle.xml.xsql.XSQLServlet -w 1 1 1 order -w 1 1 1 ordered -w 1 1 1 orders -w 1 1 1 org -w 1 1 1 osc -w 1 1 1 oscommerce -w 1 1 1 other -w 1 1 1 outcome -w 1 1 1 outgoing -w 1 1 1 outline -w 1 1 1 output -w 1 1 1 outreach -w 1 1 1 overview -w 1 1 1 owa -w 1 1 1 owl -w 1 1 1 ows -w 1 1 1 ows-bin -w 1 1 1 p -w 1 1 1 p2p -w 1 1 1 pack -w 1 1 1 package -w 1 1 1 packaged -w 1 1 1 packages -w 1 1 1 packed -w 1 1 1 page -w 1 1 1 page1 -w 1 1 1 page2 -w 1 1 1 page_1 -w 1 1 1 page_2 -w 1 1 1 pages -w 1 1 1 paid -w 1 1 1 panel -w 1 1 1 paper -w 1 1 1 papers -w 1 1 1 parse -w 1 1 1 partner -w 1 1 1 partners -w 1 1 1 party -w 1 1 1 pass -w 1 1 1 passive -w 1 1 1 passwd -w 1 1 1 password -w 1 1 1 passwords -w 1 1 1 past -w 1 1 1 patch -w 1 1 1 patches -w 1 1 1 pay -w 1 1 1 payment -w 1 1 1 payments -w 1 1 1 paypal -w 1 1 1 pbo -w 1 1 1 pc -w 1 1 1 pci -w 1 1 1 pda -w 1 1 1 pdfs -w 1 1 1 pear -w 1 1 1 peek -w 1 1 1 pending -w 1 1 1 people -w 1 1 1 perf -w 1 1 1 performance -w 1 1 1 perl -w 1 1 1 personal -w 1 1 1 pg -w 1 1 1 phf -w 1 1 1 phone -w 1 1 1 phones -w 1 1 1 phorum -w 1 1 1 photo -w 1 1 1 photos -w 1 1 1 phpBB -w 1 1 1 phpBB2 -w 1 1 1 phpEventCalendar -w 1 1 1 phpMyAdmin -w 1 1 1 phpbb -w 1 1 1 phpmyadmin -w 1 1 1 phpnuke -w 1 1 1 phps -w 1 1 1 pic -w 1 1 1 pics -w 1 1 1 pictures -w 1 1 1 pii -w 1 1 1 ping -w 1 1 1 pipe -w 1 1 1 pipermail -w 1 1 1 piranha -w 1 1 1 pivot -w 1 1 1 pix -w 1 1 1 pixel -w 1 1 1 pkg -w 1 1 1 pkgs -w 1 1 1 plain -w 1 1 1 play -w 1 1 1 player -w 1 1 1 playing -w 1 1 1 playlist -w 1 1 1 pls -w 1 1 1 plugin -w 1 1 1 plugins -w 1 1 1 poc -w 1 1 1 poi -w 1 1 1 policies -w 1 1 1 policy -w 1 1 1 politics -w 1 1 1 poll -w 1 1 1 polls -w 1 1 1 pool -w 1 1 1 pop -w 1 1 1 pop3 -w 1 1 1 popup -w 1 1 1 porn -w 1 1 1 port -w 1 1 1 portal -w 1 1 1 portals -w 1 1 1 portfolio -w 1 1 1 pos -w 1 1 1 post -w 1 1 1 posted -w 1 1 1 postgres -w 1 1 1 postgresql -w 1 1 1 postnuke -w 1 1 1 postpaid -w 1 1 1 posts -w 1 1 1 pr -w 1 1 1 pr0n -w 1 1 1 premium -w 1 1 1 prepaid -w 1 1 1 presentation -w 1 1 1 presentations -w 1 1 1 preserve -w 1 1 1 press -w 1 1 1 preview -w 1 1 1 previews -w 1 1 1 previous -w 1 1 1 pricing -w 1 1 1 print -w 1 1 1 printenv -w 1 1 1 printer -w 1 1 1 printers -w 1 1 1 priv -w 1 1 1 privacy -w 1 1 1 private -w 1 1 1 pro -w 1 1 1 problems -w 1 1 1 proc -w 1 1 1 procedures -w 1 1 1 procure -w 1 1 1 procurement -w 1 1 1 prod -w 1 1 1 product -w 1 1 1 product_info -w 1 1 1 production -w 1 1 1 products -w 1 1 1 profile -w 1 1 1 profiles -w 1 1 1 profiling -w 1 1 1 program -w 1 1 1 programming -w 1 1 1 programs -w 1 1 1 progress -w 1 1 1 project -w 1 1 1 projects -w 1 1 1 promo -w 1 1 1 promoted -w 1 1 1 promotion -w 1 1 1 prop -w 1 1 1 prop-base -w 1 1 1 property -w 1 1 1 props -w 1 1 1 prot -w 1 1 1 protect -w 1 1 1 protected -w 1 1 1 protection -w 1 1 1 proto -w 1 1 1 proxies -w 1 1 1 proxy -w 1 1 1 prv -w 1 1 1 ps -w 1 1 1 psql -w 1 1 1 pt -w 1 1 1 pub -w 1 1 1 public -w 1 1 1 publication -w 1 1 1 publications -w 1 1 1 pubs -w 1 1 1 pull -w 1 1 1 purchase -w 1 1 1 purchases -w 1 1 1 purchasing -w 1 1 1 push -w 1 1 1 pw -w 1 1 1 pwd -w 1 1 1 python -w 1 1 1 q -w 1 1 1 q1 -w 1 1 1 q2 -w 1 1 1 q3 -w 1 1 1 q4 -w 1 1 1 qotd -w 1 1 1 qpid -w 1 1 1 quarterly -w 1 1 1 queries -w 1 1 1 query -w 1 1 1 queue -w 1 1 1 queues -w 1 1 1 quote -w 1 1 1 quotes -w 1 1 1 r -w 1 1 1 radio -w 1 1 1 random -w 1 1 1 rdf -w 1 1 1 read -w 1 1 1 readme -w 1 1 1 real -w 1 1 1 realestate -w 1 1 1 receive -w 1 1 1 received -w 1 1 1 recharge -w 1 1 1 record -w 1 1 1 recorded -w 1 1 1 recorder -w 1 1 1 records -w 1 1 1 recovery -w 1 1 1 recycle -w 1 1 1 recycled -w 1 1 1 redir -w 1 1 1 redirect -w 1 1 1 reference -w 1 1 1 reg -w 1 1 1 register -w 1 1 1 registered -w 1 1 1 registration -w 1 1 1 registrations -w 1 1 1 release -w 1 1 1 releases -w 1 1 1 remind -w 1 1 1 reminder -w 1 1 1 remote -w 1 1 1 removal -w 1 1 1 removals -w 1 1 1 remove -w 1 1 1 removed -w 1 1 1 render -w 1 1 1 rendered -w 1 1 1 rep -w 1 1 1 repl -w 1 1 1 replica -w 1 1 1 replicas -w 1 1 1 replicate -w 1 1 1 replicated -w 1 1 1 replication -w 1 1 1 replicator -w 1 1 1 reply -w 1 1 1 report -w 1 1 1 reporting -w 1 1 1 reports -w 1 1 1 reprints -w 1 1 1 req -w 1 1 1 reqs -w 1 1 1 request -w 1 1 1 requests -w 1 1 1 requisition -w 1 1 1 requisitions -w 1 1 1 res -w 1 1 1 research -w 1 1 1 resin -w 1 1 1 resize -w 1 1 1 resolution -w 1 1 1 resolve -w 1 1 1 resolved -w 1 1 1 resource -w 1 1 1 resources -w 1 1 1 rest -w 1 1 1 restore -w 1 1 1 restored -w 1 1 1 restricted -w 1 1 1 result -w 1 1 1 results -w 1 1 1 retail -w 1 1 1 reverse -w 1 1 1 reversed -w 1 1 1 revert -w 1 1 1 reverted -w 1 1 1 review -w 1 1 1 reviews -w 1 1 1 right -w 1 1 1 roam -w 1 1 1 roaming -w 1 1 1 robot -w 1 1 1 robots -w 1 1 1 roller -w 1 1 1 room -w 1 1 1 root -w 1 1 1 rpc -w 1 1 1 rsa -w 1 1 1 ru -w 1 1 1 ruby -w 1 1 1 rule -w 1 1 1 rules -w 1 1 1 run -w 1 1 1 rwservlet -w 1 1 1 s -w 1 1 1 sale -w 1 1 1 sales -w 1 1 1 salesforce -w 1 1 1 sam -w 1 1 1 samba -w 1 1 1 saml -w 1 1 1 sample -w 1 1 1 samples -w 1 1 1 san -w 1 1 1 sav -w 1 1 1 saved -w 1 1 1 saves -w 1 1 1 sbin -w 1 1 1 scan -w 1 1 1 scanned -w 1 1 1 scans -w 1 1 1 sched -w 1 1 1 schedule -w 1 1 1 scheduled -w 1 1 1 scheduling -w 1 1 1 schema -w 1 1 1 science -w 1 1 1 screen -w 1 1 1 screens -w 1 1 1 screenshot -w 1 1 1 screenshots -w 1 1 1 script -w 1 1 1 scriptlet -w 1 1 1 scriptlets -w 1 1 1 scripts -w 1 1 1 sdk -w 1 1 1 se -w 1 1 1 search -w 1 1 1 sec -w 1 1 1 second -w 1 1 1 secret -w 1 1 1 section -w 1 1 1 sections -w 1 1 1 secure -w 1 1 1 secured -w 1 1 1 security -w 1 1 1 seed -w 1 1 1 select -w 1 1 1 sell -w 1 1 1 send -w 1 1 1 sendmail -w 1 1 1 sendto -w 1 1 1 sent -w 1 1 1 serial -w 1 1 1 serv -w 1 1 1 serve -w 1 1 1 server -w 1 1 1 server-info -w 1 1 1 server-status -w 1 1 1 servers -w 1 1 1 service -w 1 1 1 services -w 1 1 1 servlet -w 1 1 1 servlets -w 1 1 1 session -w 1 1 1 sessions -w 1 1 1 setting -w 1 1 1 settings -w 1 1 1 setup -w 1 1 1 sex -w 1 1 1 shadow -w 1 1 1 share -w 1 1 1 shared -w 1 1 1 shares -w 1 1 1 shell -w 1 1 1 ship -w 1 1 1 shipped -w 1 1 1 shipping -w 1 1 1 shockwave -w 1 1 1 shop -w 1 1 1 shopper -w 1 1 1 shopping -w 1 1 1 shops -w 1 1 1 shoutbox -w 1 1 1 show -w 1 1 1 show_post -w 1 1 1 show_thread -w 1 1 1 showcat -w 1 1 1 showenv -w 1 1 1 showjobs -w 1 1 1 showmap -w 1 1 1 showmsg -w 1 1 1 showpost -w 1 1 1 showthread -w 1 1 1 sign -w 1 1 1 signed -w 1 1 1 signer -w 1 1 1 signin -w 1 1 1 signing -w 1 1 1 signoff -w 1 1 1 signon -w 1 1 1 signout -w 1 1 1 signup -w 1 1 1 simple -w 1 1 1 sink -w 1 1 1 site -w 1 1 1 site-map -w 1 1 1 site_map -w 1 1 1 sitemap -w 1 1 1 sites -w 1 1 1 skel -w 1 1 1 skin -w 1 1 1 skins -w 1 1 1 skip -w 1 1 1 sl -w 1 1 1 sling -w 1 1 1 sm -w 1 1 1 small -w 1 1 1 smile -w 1 1 1 smiles -w 1 1 1 sms -w 1 1 1 smtp -w 1 1 1 snoop -w 1 1 1 soap -w 1 1 1 soaprouter -w 1 1 1 soft -w 1 1 1 software -w 1 1 1 solaris -w 1 1 1 sold -w 1 1 1 solution -w 1 1 1 solutions -w 1 1 1 solve -w 1 1 1 solved -w 1 1 1 source -w 1 1 1 sources -w 1 1 1 sox -w 1 1 1 sp -w 1 1 1 space -w 1 1 1 spacer -w 1 1 1 spam -w 1 1 1 special -w 1 1 1 specials -w 1 1 1 sponsor -w 1 1 1 sponsors -w 1 1 1 spool -w 1 1 1 sport -w 1 1 1 sports -w 1 1 1 sqlnet -w 1 1 1 squirrel -w 1 1 1 squirrelmail -w 1 1 1 src -w 1 1 1 srv -w 1 1 1 ss -w 1 1 1 ssh -w 1 1 1 ssi -w 1 1 1 ssl -w 1 1 1 sslvpn -w 1 1 1 ssn -w 1 1 1 sso -w 1 1 1 staff -w 1 1 1 staging -w 1 1 1 standalone -w 1 1 1 standard -w 1 1 1 standards -w 1 1 1 star -w 1 1 1 start -w 1 1 1 stat -w 1 1 1 statement -w 1 1 1 statements -w 1 1 1 static -w 1 1 1 staticpages -w 1 1 1 statistic -w 1 1 1 statistics -w 1 1 1 stats -w 1 1 1 status -w 1 1 1 stock -w 1 1 1 storage -w 1 1 1 store -w 1 1 1 stored -w 1 1 1 stores -w 1 1 1 stories -w 1 1 1 story -w 1 1 1 strut -w 1 1 1 struts -w 1 1 1 student -w 1 1 1 students -w 1 1 1 stuff -w 1 1 1 style -w 1 1 1 styles -w 1 1 1 submissions -w 1 1 1 submit -w 1 1 1 subscribe -w 1 1 1 subscribed -w 1 1 1 subscriber -w 1 1 1 subscribers -w 1 1 1 subscription -w 1 1 1 subscriptions -w 1 1 1 success -w 1 1 1 suite -w 1 1 1 suites -w 1 1 1 sun -w 1 1 1 sunos -w 1 1 1 super -w 1 1 1 support -w 1 1 1 surf -w 1 1 1 survey -w 1 1 1 surveys -w 1 1 1 sws -w 1 1 1 synapse -w 1 1 1 sync -w 1 1 1 synced -w 1 1 1 sys -w 1 1 1 sysmanager -w 1 1 1 system -w 1 1 1 systems -w 1 1 1 sysuser -w 1 1 1 t -w 1 1 1 tag -w 1 1 1 tags -w 1 1 1 tail -w 1 1 1 tape -w 1 1 1 tapes -w 1 1 1 tapestry -w 1 1 1 tb -w 1 1 1 tcl -w 1 1 1 team -w 1 1 1 tech -w 1 1 1 technical -w 1 1 1 technology -w 1 1 1 tel -w 1 1 1 tele -w 1 1 1 templ -w 1 1 1 template -w 1 1 1 templates -w 1 1 1 terms -w 1 1 1 test-cgi -w 1 1 1 test-env -w 1 1 1 test1 -w 1 1 1 test123 -w 1 1 1 test1234 -w 1 1 1 test2 -w 1 1 1 test3 -w 1 1 1 testimonial -w 1 1 1 testimonials -w 1 1 1 testing -w 1 1 1 tests -w 1 1 1 texis -w 1 1 1 text -w 1 1 1 text-base -w 1 1 1 texts -w 1 1 1 theme -w 1 1 1 themes -w 1 1 1 thread -w 1 1 1 threads -w 1 1 1 thumb -w 1 1 1 thumbnail -w 1 1 1 thumbnails -w 1 1 1 thumbs -w 1 1 1 tickets -w 1 1 1 tiki -w 1 1 1 tiles -w 1 1 1 tip -w 1 1 1 tips -w 1 1 1 title -w 1 1 1 tls -w 1 1 1 tmpl -w 1 1 1 tmps -w 1 1 1 tn -w 1 1 1 toc -w 1 1 1 todo -w 1 1 1 toggle -w 1 1 1 tomcat -w 1 1 1 tool -w 1 1 1 toolbar -w 1 1 1 toolkit -w 1 1 1 tools -w 1 1 1 top -w 1 1 1 topic -w 1 1 1 topics -w 1 1 1 torrent -w 1 1 1 torrents -w 1 1 1 tos -w 1 1 1 tour -w 1 1 1 tpv -w 1 1 1 tr -w 1 1 1 traceroute -w 1 1 1 traces -w 1 1 1 track -w 1 1 1 trackback -w 1 1 1 tracker -w 1 1 1 trackers -w 1 1 1 tracking -w 1 1 1 tracks -w 1 1 1 traffic -w 1 1 1 trailer -w 1 1 1 trailers -w 1 1 1 training -w 1 1 1 trans -w 1 1 1 transaction -w 1 1 1 transactions -w 1 1 1 transparent -w 1 1 1 transport -w 1 1 1 trash -w 1 1 1 travel -w 1 1 1 treasury -w 1 1 1 tree -w 1 1 1 trees -w 1 1 1 trial -w 1 1 1 true -w 1 1 1 trunk -w 1 1 1 tsweb -w 1 1 1 tt -w 1 1 1 turbine -w 1 1 1 tuscany -w 1 1 1 tutorial -w 1 1 1 tutorials -w 1 1 1 tv -w 1 1 1 tweak -w 1 1 1 twitter -w 1 1 1 type -w 1 1 1 typo3 -w 1 1 1 typo3conf -w 1 1 1 u -w 1 1 1 ubb -w 1 1 1 uds -w 1 1 1 uk -w 1 1 1 umts -w 1 1 1 union -w 1 1 1 unix -w 1 1 1 unlock -w 1 1 1 unpaid -w 1 1 1 unreg -w 1 1 1 unregister -w 1 1 1 unsubscribe -w 1 1 1 up -w 1 1 1 upd -w 1 1 1 update -w 1 1 1 updated -w 1 1 1 updater -w 1 1 1 updates -w 1 1 1 upload -w 1 1 1 uploader -w 1 1 1 uploads -w 1 1 1 url -w 1 1 1 urls -w 1 1 1 us -w 1 1 1 usa -w 1 1 1 usage -w 1 1 1 user -w 1 1 1 userlog -w 1 1 1 users -w 1 1 1 usr -w 1 1 1 util -w 1 1 1 utilities -w 1 1 1 utility -w 1 1 1 utils -w 1 1 1 v -w 1 1 1 v1 -w 1 1 1 v2 -w 1 1 1 var -w 1 1 1 vault -w 1 1 1 vector -w 1 1 1 velocity -w 1 1 1 vendor -w 1 1 1 vendors -w 1 1 1 ver -w 1 1 1 ver1 -w 1 1 1 ver2 -w 1 1 1 version -w 1 1 1 vfs -w 1 1 1 video -w 1 1 1 videos -w 1 1 1 view -w 1 1 1 view-source -w 1 1 1 viewcvs -w 1 1 1 viewforum -w 1 1 1 viewonline -w 1 1 1 views -w 1 1 1 viewsource -w 1 1 1 viewsvn -w 1 1 1 viewtopic -w 1 1 1 viewvc -w 1 1 1 virtual -w 1 1 1 vm -w 1 1 1 voip -w 1 1 1 vol -w 1 1 1 vote -w 1 1 1 voter -w 1 1 1 votes -w 1 1 1 vpn -w 1 1 1 vuln -w 1 1 1 w -w 1 1 1 w3 -w 1 1 1 w3c -w 1 1 1 wa -w 1 1 1 wap -w 1 1 1 warez -w 1 1 1 way-board -w 1 1 1 wbboard -w 1 1 1 wc -w 1 1 1 weather -w 1 1 1 web -w 1 1 1 web-beans -w 1 1 1 web-console -w 1 1 1 webaccess -w 1 1 1 webadmin -w 1 1 1 webagent -w 1 1 1 webalizer -w 1 1 1 webapp -w 1 1 1 webb -w 1 1 1 webbbs -w 1 1 1 webboard -w 1 1 1 webcalendar -w 1 1 1 webcart -w 1 1 1 webcasts -w 1 1 1 webcgi -w 1 1 1 webchat -w 1 1 1 webdata -w 1 1 1 webdav -w 1 1 1 webdb -w 1 1 1 weblog -w 1 1 1 weblogic -w 1 1 1 weblogs -w 1 1 1 webmail -w 1 1 1 webplus -w 1 1 1 webshop -w 1 1 1 website -w 1 1 1 websphere -w 1 1 1 websql -w 1 1 1 webstats -w 1 1 1 websvn -w 1 1 1 webwork -w 1 1 1 week -w 1 1 1 weekly -w 1 1 1 welcome -w 1 1 1 whitepapers -w 1 1 1 whois -w 1 1 1 whosonline -w 1 1 1 wicket -w 1 1 1 wiki -w 1 1 1 win -w 1 1 1 win32 -w 1 1 1 windows -w 1 1 1 winnt -w 1 1 1 wireless -w 1 1 1 wml -w 1 1 1 word -w 1 1 1 wordpress -w 1 1 1 work -w 1 1 1 working -w 1 1 1 world -w 1 1 1 wow -w 1 1 1 wp -w 1 1 1 wp-content -w 1 1 1 wp-dbmanager -w 1 1 1 wp-includes -w 1 1 1 wp-login -w 1 1 1 wp-syntax -w 1 1 1 wrap -w 1 1 1 ws-client -w 1 1 1 ws_ftp -w 1 1 1 wsdl -w 1 1 1 wtai -w 1 1 1 www -w 1 1 1 www-sql -w 1 1 1 www1 -w 1 1 1 www2 -w 1 1 1 www3 -w 1 1 1 wwwboard -w 1 1 1 wwwroot -w 1 1 1 wwwstats -w 1 1 1 wwwthreads -w 1 1 1 wwwuser -w 1 1 1 wysiwyg -w 1 1 1 x -w 1 1 1 xalan -w 1 1 1 xerces -w 1 1 1 xhtml -w 1 1 1 xmlrpc -w 1 1 1 xsql -w 1 1 1 xxx -w 1 1 1 xyzzy -w 1 1 1 y -w 1 1 1 yahoo -w 1 1 1 year -w 1 1 1 yearly -w 1 1 1 youtube -w 1 1 1 yt -w 1 1 1 z -w 1 1 1 zboard -w 1 1 1 zencart -w 1 1 1 zend -w 1 1 1 zero -w 1 1 1 zimbra -w 1 1 1 zipfiles -w 1 1 1 zips -w 1 1 1 zoom -w 1 1 1 zope -w 1 1 1 zorum -w 1 1 1 ~admin -w 1 1 1 ~amanda -w 1 1 1 ~apache -w 1 1 1 ~ashley -w 1 1 1 ~bin -w 1 1 1 ~bob -w 1 1 1 ~chris -w 1 1 1 ~dan -w 1 1 1 ~eric -w 1 1 1 ~ftp -w 1 1 1 ~guest -w 1 1 1 ~http -w 1 1 1 ~httpd -w 1 1 1 ~jacob -w 1 1 1 ~jennifer -w 1 1 1 ~jessica -w 1 1 1 ~john -w 1 1 1 ~log -w 1 1 1 ~logs -w 1 1 1 ~lp -w 1 1 1 ~mark -w 1 1 1 ~matt -w 1 1 1 ~michael -w 1 1 1 ~nobody -w 1 1 1 ~root -w 1 1 1 ~test -w 1 1 1 ~tmp -w 1 1 1 ~www +#ro +eg 1 1 1 7z +es 1 1 1 as +es 1 1 1 asmx +es 1 1 1 asp +es 1 1 1 aspx +eg 1 1 1 bak +es 1 1 1 bat +eg 1 1 1 bin +eg 1 1 1 bz2 +es 1 1 1 c +es 1 1 1 cc +eg 1 1 1 cfg +es 1 1 1 cfm +es 1 1 1 cgi +es 1 1 1 class +eg 1 1 1 cnf +eg 1 1 1 conf +eg 1 1 1 config +eg 1 1 1 core +es 1 1 1 cpp +es 1 1 1 cs +es 1 1 1 csproj +eg 1 1 1 csv +eg 1 1 1 dat +eg 1 1 1 db +es 1 1 1 dll +eg 1 1 1 do +es 1 1 1 doc +eg 1 1 1 dump +es 1 1 1 ep +eg 1 1 1 err +eg 1 1 1 error +es 1 1 1 exe +es 1 1 1 fcgi +es 1 1 1 gif +eg 1 1 1 gz +es 1 1 1 htm +es 1 1 1 html +es 1 1 1 inc +es 1 1 1 ini +es 1 1 1 jar +es 1 1 1 java +es 1 1 1 jhtml +es 1 1 1 jpg +es 1 1 1 js +es 1 1 1 jsf +es 1 1 1 jsp +eg 1 1 1 key +eg 1 1 1 lib +eg 1 1 1 log +eg 1 1 1 lst +es 1 1 1 manifest +eg 1 1 1 mdb +eg 1 1 1 meta +eg 1 1 1 msg +es 1 1 1 nsf +eg 1 1 1 o +eg 1 1 1 old +eg 1 1 1 ora +eg 1 1 1 orig +eg 1 1 1 out +eg 1 1 1 part +es 1 1 1 pdf +es 1 1 1 pem +es 1 1 1 pfx +es 1 1 1 php +es 1 1 1 php3 +es 1 1 1 phps +es 1 1 1 phtml +es 1 1 1 pl +es 1 1 1 pm +es 1 1 1 png +es 1 1 1 ppt +eg 1 1 1 properties +es 1 1 1 py +eg 1 1 1 rar +es 1 1 1 rb +es 1 1 1 rhtml +es 1 1 1 rss +es 1 1 1 rtf +eg 1 1 1 save +es 1 1 1 sh +es 1 1 1 shtml +es 1 1 1 so +es 1 1 1 sql +eg 1 1 1 stackdump +es 1 1 1 svn-base +es 1 1 1 swf +eg 1 1 1 swp +eg 1 1 1 tar +eg 1 1 1 tar.bz2 +eg 1 1 1 tar.gz +eg 1 1 1 temp +eg 1 1 1 test +eg 1 1 1 tgz +eg 1 1 1 tmp +es 1 1 1 tpl +eg 1 1 1 trace +eg 1 1 1 txt +es 1 1 1 vb +es 1 1 1 vbs +es 1 1 1 war +es 1 1 1 ws +es 1 1 1 xls +eg 1 1 1 xml +es 1 1 1 xsl +es 1 1 1 xslt +es 1 1 1 yml +eg 1 1 1 zip +ws 1 1 1 .bash_history +ws 1 1 1 .bashrc +ws 1 1 1 .cvsignore +ws 1 1 1 .history +ws 1 1 1 .htaccess +ws 1 1 1 .htpasswd +ws 1 1 1 .passwd +ws 1 1 1 .perf +ws 1 1 1 .ssh +ws 1 1 1 .svn +ws 1 1 1 .web +wg 1 1 1 0 +wg 1 1 1 00 +wg 1 1 1 01 +wg 1 1 1 02 +wg 1 1 1 03 +wg 1 1 1 04 +wg 1 1 1 05 +wg 1 1 1 06 +wg 1 1 1 07 +wg 1 1 1 08 +wg 1 1 1 09 +wg 1 1 1 1 +wg 1 1 1 10 +wg 1 1 1 100 +wg 1 1 1 1000 +wg 1 1 1 1001 +wg 1 1 1 101 +wg 1 1 1 11 +wg 1 1 1 12 +wg 1 1 1 13 +wg 1 1 1 14 +wg 1 1 1 15 +wg 1 1 1 1990 +wg 1 1 1 1991 +wg 1 1 1 1992 +wg 1 1 1 1993 +wg 1 1 1 1994 +wg 1 1 1 1995 +wg 1 1 1 1996 +wg 1 1 1 1997 +wg 1 1 1 1998 +wg 1 1 1 1999 +wg 1 1 1 2 +wg 1 1 1 20 +wg 1 1 1 200 +wg 1 1 1 2000 +wg 1 1 1 2001 +wg 1 1 1 2002 +wg 1 1 1 2003 +wg 1 1 1 2004 +wg 1 1 1 2005 +wg 1 1 1 2006 +wg 1 1 1 2007 +wg 1 1 1 2008 +wg 1 1 1 2009 +wg 1 1 1 2010 +wg 1 1 1 2011 +wg 1 1 1 2012 +wg 1 1 1 2013 +wg 1 1 1 2014 +wg 1 1 1 21 +wg 1 1 1 22 +wg 1 1 1 23 +wg 1 1 1 24 +wg 1 1 1 25 +wg 1 1 1 2g +wg 1 1 1 3 +wg 1 1 1 300 +wg 1 1 1 3g +wg 1 1 1 4 +wg 1 1 1 42 +wg 1 1 1 5 +wg 1 1 1 50 +wg 1 1 1 500 +wg 1 1 1 51 +wg 1 1 1 6 +wg 1 1 1 7 +wg 1 1 1 8 +wg 1 1 1 9 +wg 1 1 1 ADM +wg 1 1 1 ADMIN +wg 1 1 1 Admin +wg 1 1 1 AdminService +ws 1 1 1 App_Data +wg 1 1 1 AggreSpy +wg 1 1 1 AppsLocalLogin +wg 1 1 1 AppsLogin +ws 1 1 1 CHANGELOG +ws 1 1 1 ChangeLog +ws 1 1 1 BUILD +wg 1 1 1 CMS +wg 1 1 1 CVS +wg 1 1 1 DB +wg 1 1 1 DMSDump +ws 1 1 1 Documents and Settings +ws 1 1 1 Entries +wg 1 1 1 FCKeditor +wg 1 1 1 INSTALL +wg 1 1 1 JMXSoapAdapter +wg 1 1 1 LICENSE +ws 1 1 1 MANIFEST.MF +ws 1 1 1 META-INF +ws 1 1 1 Makefile +wg 1 1 1 OA +wg 1 1 1 OAErrorDetailPage +ws 1 1 1 OA_HTML +ws 1 1 1 Program Files +wg 1 1 1 README +ws 1 1 1 Rakefile +wg 1 1 1 Readme +ws 1 1 1 Recycled +wg 1 1 1 Root +ws 1 1 1 SERVER-INF +wg 1 1 1 SOAPMonitor +wg 1 1 1 SQL +wg 1 1 1 SUNWmc +wg 1 1 1 SiteScope +wg 1 1 1 SiteServer +wg 1 1 1 Spy +wg 1 1 1 TEMP +wg 1 1 1 TMP +wg 1 1 1 TODO +ws 1 1 1 Thumbs.db +ws 1 1 1 UPGRADE +ws 1 1 1 WEB-INF +ws 1 1 1 WS_FTP +wg 1 1 1 XXX +ws 1 1 1 _ +ws 1 1 1 _adm +ws 1 1 1 _admin +ws 1 1 1 _app +ws 1 1 1 _common +ws 1 1 1 _conf +ws 1 1 1 _files +ws 1 1 1 _global +ws 1 1 1 _include +ws 1 1 1 _js +ws 1 1 1 _mem_bin +ws 1 1 1 _old +ws 1 1 1 _pages +ws 1 1 1 _pda +ws 1 1 1 _private +ws 1 1 1 _res +ws 1 1 1 _source +ws 1 1 1 _src +ws 1 1 1 _test +ws 1 1 1 _vti_bin +ws 1 1 1 _vti_cnf +ws 1 1 1 _vti_pvt +ws 1 1 1 _vti_txt +ws 1 1 1 _www +wg 1 1 1 a +wg 1 1 1 aa +wg 1 1 1 aaa +wg 1 1 1 abc +wg 1 1 1 abc123 +wg 1 1 1 abcd +wg 1 1 1 abcd1234 +wg 1 1 1 about +wg 1 1 1 accept +wg 1 1 1 access +ws 1 1 1 access-log +ws 1 1 1 access-log.1 +ws 1 1 1 access.1 +ws 1 1 1 access_log +ws 1 1 1 access_log.1 +wg 1 1 1 accessibility +wg 1 1 1 account +wg 1 1 1 accounting +wg 1 1 1 accounts +wg 1 1 1 action +wg 1 1 1 actions +wg 1 1 1 activate +wg 1 1 1 active +wg 1 1 1 activex +wg 1 1 1 ad +wg 1 1 1 adbuys +wg 1 1 1 adclick +wg 1 1 1 add +wg 1 1 1 addpost +wg 1 1 1 addressbook +wg 1 1 1 adm +wg 1 1 1 admin +wg 1 1 1 admins +wg 1 1 1 administrator +wg 1 1 1 admin-console +ws 1 1 1 admin_ +wg 1 1 1 admins +wg 1 1 1 adobe +wg 1 1 1 adodb +wg 1 1 1 ads +wg 1 1 1 adserver +wg 1 1 1 adv +wg 1 1 1 advanced +wg 1 1 1 advertise +wg 1 1 1 advertising +wg 1 1 1 affiliate +wg 1 1 1 affiliates +wg 1 1 1 agenda +wg 1 1 1 agent +wg 1 1 1 agents +wg 1 1 1 ajax +wg 1 1 1 akamai +wg 1 1 1 album +wg 1 1 1 albums +wg 1 1 1 alcatel +wg 1 1 1 alert +wg 1 1 1 alerts +wg 1 1 1 alias +wg 1 1 1 aliases +wg 1 1 1 all +ws 1 1 1 all-wcprops +wg 1 1 1 alpha +wg 1 1 1 alumni +wg 1 1 1 amanda +wg 1 1 1 amazon +wg 1 1 1 analog +wg 1 1 1 android +wg 1 1 1 announcement +wg 1 1 1 announcements +wg 1 1 1 annual +wg 1 1 1 anon +wg 1 1 1 anonymous +wg 1 1 1 ansi +wg 1 1 1 apac +wg 1 1 1 apache +wg 1 1 1 apexec +wg 1 1 1 api +wg 1 1 1 apis +wg 1 1 1 app +wg 1 1 1 appeal +wg 1 1 1 appeals +wg 1 1 1 append +wg 1 1 1 appl +wg 1 1 1 apple +wg 1 1 1 appliation +wg 1 1 1 applications +wg 1 1 1 apps +wg 1 1 1 apr +wg 1 1 1 arch +wg 1 1 1 archiv +wg 1 1 1 archive +wg 1 1 1 archives +wg 1 1 1 array +wg 1 1 1 art +wg 1 1 1 article +wg 1 1 1 articles +wg 1 1 1 artwork +wg 1 1 1 ascii +wg 1 1 1 asdf +wg 1 1 1 ashley +wg 1 1 1 asset +wg 1 1 1 assets +wg 1 1 1 aspnet_client +wg 1 1 1 atom +wg 1 1 1 attach +wg 1 1 1 attachment +wg 1 1 1 attachments +wg 1 1 1 attachs +wg 1 1 1 attic +wg 1 1 1 auction +wg 1 1 1 audio +wg 1 1 1 audit +wg 1 1 1 audits +wg 1 1 1 auth +wg 1 1 1 author +ws 1 1 1 authorized_keys +ws 1 1 1 authorized_keys2 +wg 1 1 1 authors +wg 1 1 1 auto +wg 1 1 1 autocomplete +wg 1 1 1 automatic +wg 1 1 1 automation +wg 1 1 1 avatar +wg 1 1 1 avatars +wg 1 1 1 award +wg 1 1 1 awards +wg 1 1 1 awl +wg 1 1 1 awstats +ws 1 1 1 axis +ws 1 1 1 axis-admin +ws 1 1 1 axis2 +ws 1 1 1 axis2-admin +wg 1 1 1 b +wg 1 1 1 b2b +wg 1 1 1 b2c +wg 1 1 1 back +wg 1 1 1 backdoor +wg 1 1 1 backend +wg 1 1 1 backup +wg 1 1 1 backup-db +wg 1 1 1 backups +wg 1 1 1 balance +wg 1 1 1 balances +wg 1 1 1 bandwidth +wg 1 1 1 bank +wg 1 1 1 banking +wg 1 1 1 banks +wg 1 1 1 banner +wg 1 1 1 banners +wg 1 1 1 bar +wg 1 1 1 base +wg 1 1 1 bash +wg 1 1 1 basic +wg 1 1 1 basket +wg 1 1 1 baskets +wg 1 1 1 batch +wg 1 1 1 baz +wg 1 1 1 bb +ws 1 1 1 bb-hist +ws 1 1 1 bb-histlog +wg 1 1 1 bboard +wg 1 1 1 bbs +wg 1 1 1 bean +wg 1 1 1 beans +wg 1 1 1 beehive +wg 1 1 1 benefits +wg 1 1 1 beta +wg 1 1 1 bfc +wg 1 1 1 big +wg 1 1 1 bigip +wg 1 1 1 bill +wg 1 1 1 billing +wg 1 1 1 binaries +wg 1 1 1 binary +wg 1 1 1 bins +wg 1 1 1 bio +wg 1 1 1 bios +wg 1 1 1 biz +wg 1 1 1 bkup +wg 1 1 1 blah +wg 1 1 1 blank +wg 1 1 1 blog +wg 1 1 1 blogger +wg 1 1 1 bloggers +wg 1 1 1 blogs +wg 1 1 1 blogspot +wg 1 1 1 board +wg 1 1 1 boards +wg 1 1 1 bob +wg 1 1 1 bofh +wg 1 1 1 book +wg 1 1 1 books +wg 1 1 1 boot +wg 1 1 1 bottom +wg 1 1 1 broken +wg 1 1 1 broker +wg 1 1 1 browse +wg 1 1 1 browser +wg 1 1 1 bs +wg 1 1 1 bsd +wg 1 1 1 bug +wg 1 1 1 bugs +wg 1 1 1 build +wg 1 1 1 builder +wg 1 1 1 buildr +wg 1 1 1 bulk +wg 1 1 1 bullet +wg 1 1 1 business +wg 1 1 1 button +wg 1 1 1 buttons +wg 1 1 1 buy +wg 1 1 1 buynow +wg 1 1 1 bypass +wg 1 1 1 ca +wg 1 1 1 cache +wg 1 1 1 caches +wg 1 1 1 cal +wg 1 1 1 calendar +wg 1 1 1 camel +wg 1 1 1 car +wg 1 1 1 card +wg 1 1 1 cards +wg 1 1 1 career +wg 1 1 1 careers +wg 1 1 1 cars +wg 1 1 1 cart +wg 1 1 1 carts +wg 1 1 1 cat +wg 1 1 1 catalog +wg 1 1 1 catalogs +wg 1 1 1 catalyst +wg 1 1 1 categories +wg 1 1 1 category +wg 1 1 1 catinfo +wg 1 1 1 cats +wg 1 1 1 ccbill +wg 1 1 1 cd +wg 1 1 1 cert +wg 1 1 1 certificate +wg 1 1 1 certificates +wg 1 1 1 certified +wg 1 1 1 certs +wg 1 1 1 cf +ws 1 1 1 cfcache +wg 1 1 1 cfdocs +wg 1 1 1 cfide +wg 1 1 1 cfusion +ws 1 1 1 cgi-bin +ws 1 1 1 cgi-bin2 +ws 1 1 1 cgi-home +ws 1 1 1 cgi-local +ws 1 1 1 cgi-pub +ws 1 1 1 cgi-script +ws 1 1 1 cgi-shl +ws 1 1 1 cgi-sys +ws 1 1 1 cgi-web +ws 1 1 1 cgi-win +ws 1 1 1 cgibin +ws 1 1 1 cgiwrap +ws 1 1 1 cgm-web +wg 1 1 1 change +wg 1 1 1 changed +wg 1 1 1 changes +wg 1 1 1 charge +wg 1 1 1 charges +wg 1 1 1 chat +wg 1 1 1 chats +wg 1 1 1 check +wg 1 1 1 checking +wg 1 1 1 checkout +wg 1 1 1 checkpoint +wg 1 1 1 checks +wg 1 1 1 child +wg 1 1 1 children +wg 1 1 1 chris +wg 1 1 1 chrome +wg 1 1 1 cisco +wg 1 1 1 cisweb +wg 1 1 1 citrix +wg 1 1 1 cl +wg 1 1 1 claim +wg 1 1 1 claims +wg 1 1 1 classes +wg 1 1 1 classified +wg 1 1 1 classifieds +wg 1 1 1 clear +wg 1 1 1 cli +wg 1 1 1 click +wg 1 1 1 clicks +wg 1 1 1 client +ws 1 1 1 clientaccesspolicy +wg 1 1 1 clients +wg 1 1 1 clk +wg 1 1 1 clock +wg 1 1 1 close +wg 1 1 1 closed +wg 1 1 1 closing +wg 1 1 1 club +wg 1 1 1 cluster +wg 1 1 1 clusters +wg 1 1 1 cmd +wg 1 1 1 cms +wg 1 1 1 cnt +wg 1 1 1 cocoon +wg 1 1 1 code +wg 1 1 1 codec +wg 1 1 1 codecs +wg 1 1 1 codes +wg 1 1 1 cognos +wg 1 1 1 coldfusion +wg 1 1 1 columns +wg 1 1 1 com +wg 1 1 1 comment +wg 1 1 1 comments +wg 1 1 1 commerce +wg 1 1 1 commercial +wg 1 1 1 common +wg 1 1 1 communicator +wg 1 1 1 community +wg 1 1 1 compact +wg 1 1 1 company +wg 1 1 1 compat +wg 1 1 1 complaint +wg 1 1 1 complaints +wg 1 1 1 compliance +wg 1 1 1 component +wg 1 1 1 components +wg 1 1 1 compose +wg 1 1 1 compress +wg 1 1 1 compressed +wg 1 1 1 computer +wg 1 1 1 computers +wg 1 1 1 computing +wg 1 1 1 conference +wg 1 1 1 conferences +wg 1 1 1 configs +wg 1 1 1 console +wg 1 1 1 consumer +wg 1 1 1 contact +wg 1 1 1 contacts +wg 1 1 1 content +wg 1 1 1 contents +wg 1 1 1 contest +wg 1 1 1 contract +wg 1 1 1 contracts +wg 1 1 1 control +wg 1 1 1 controller +wg 1 1 1 controlpanel +wg 1 1 1 cookie +wg 1 1 1 cookies +wg 1 1 1 copies +wg 1 1 1 copy +wg 1 1 1 copyright +wg 1 1 1 corp +wg 1 1 1 corpo +wg 1 1 1 corporate +wg 1 1 1 corrections +wg 1 1 1 count +wg 1 1 1 counter +wg 1 1 1 counters +wg 1 1 1 counts +wg 1 1 1 course +wg 1 1 1 courses +wg 1 1 1 cover +wg 1 1 1 coverage +wg 1 1 1 cpadmin +wg 1 1 1 cpanel +wg 1 1 1 cpx.php +wg 1 1 1 cr +wg 1 1 1 crack +wg 1 1 1 crash +wg 1 1 1 crashes +wg 1 1 1 create +wg 1 1 1 creator +wg 1 1 1 credit +wg 1 1 1 credits +wg 1 1 1 crm +wg 1 1 1 cron +wg 1 1 1 crons +wg 1 1 1 crontab +wg 1 1 1 crontabs +wg 1 1 1 crossdomain +wg 1 1 1 crypt +wg 1 1 1 crypto +wg 1 1 1 css +wg 1 1 1 current +wg 1 1 1 custom +ws 1 1 1 custom-log +ws 1 1 1 custom_log +wg 1 1 1 customer +wg 1 1 1 customers +wg 1 1 1 cute +wg 1 1 1 cv +wg 1 1 1 cxf +wg 1 1 1 czcmdcvt +wg 1 1 1 d +wg 1 1 1 daemon +wg 1 1 1 daily +wg 1 1 1 dan +wg 1 1 1 dana-na +wg 1 1 1 data +wg 1 1 1 database +wg 1 1 1 databases +wg 1 1 1 date +wg 1 1 1 day +wg 1 1 1 db_connect +wg 1 1 1 dba +wg 1 1 1 dbase +wg 1 1 1 dblclk +wg 1 1 1 dbman +wg 1 1 1 dbmodules +wg 1 1 1 dbutil +wg 1 1 1 dc +wg 1 1 1 dcforum +wg 1 1 1 dclk +wg 1 1 1 de +wg 1 1 1 dealer +wg 1 1 1 debug +wg 1 1 1 decl +wg 1 1 1 declaration +wg 1 1 1 declarations +wg 1 1 1 decode +wg 1 1 1 decoder +wg 1 1 1 decrypt +wg 1 1 1 decrypted +wg 1 1 1 decryption +wg 1 1 1 def +wg 1 1 1 default +wg 1 1 1 defaults +wg 1 1 1 definition +wg 1 1 1 definitions +wg 1 1 1 del +wg 1 1 1 delete +wg 1 1 1 deleted +wg 1 1 1 demo +wg 1 1 1 demos +wg 1 1 1 denied +wg 1 1 1 deny +wg 1 1 1 design +wg 1 1 1 desktop +wg 1 1 1 desktops +wg 1 1 1 detail +wg 1 1 1 details +wg 1 1 1 dev +wg 1 1 1 devel +wg 1 1 1 developer +wg 1 1 1 developers +wg 1 1 1 development +wg 1 1 1 device +wg 1 1 1 devices +wg 1 1 1 devs +wg 1 1 1 df +wg 1 1 1 dialog +wg 1 1 1 dialogs +wg 1 1 1 diff +wg 1 1 1 diffs +wg 1 1 1 digest +wg 1 1 1 digg +wg 1 1 1 dir +ws 1 1 1 dir-prop-base +wg 1 1 1 directories +wg 1 1 1 directory +wg 1 1 1 dirs +wg 1 1 1 disabled +wg 1 1 1 disclaimer +wg 1 1 1 display +wg 1 1 1 django +wg 1 1 1 dl +wg 1 1 1 dm +ws 1 1 1 dm-config +wg 1 1 1 dms +wg 1 1 1 dms0 +wg 1 1 1 dns +wg 1 1 1 docebo +wg 1 1 1 dock +wg 1 1 1 docroot +wg 1 1 1 docs +wg 1 1 1 document +wg 1 1 1 documentation +wg 1 1 1 documents +wg 1 1 1 domain +wg 1 1 1 domains +wg 1 1 1 donate +wg 1 1 1 done +wg 1 1 1 doubleclick +wg 1 1 1 down +wg 1 1 1 download +wg 1 1 1 downloader +wg 1 1 1 downloads +wg 1 1 1 drop +wg 1 1 1 dropped +wg 1 1 1 drupal +wg 1 1 1 dummy +wg 1 1 1 dumps +wg 1 1 1 dvd +wg 1 1 1 dwr +wg 1 1 1 dyn +wg 1 1 1 dynamic +wg 1 1 1 e +wg 1 1 1 e2fs +wg 1 1 1 ear +wg 1 1 1 ecommerce +wg 1 1 1 edge +wg 1 1 1 edit +wg 1 1 1 editor +wg 1 1 1 edits +wg 1 1 1 edp +wg 1 1 1 edu +wg 1 1 1 education +wg 1 1 1 ee +wg 1 1 1 effort +wg 1 1 1 efforts +wg 1 1 1 egress +wg 1 1 1 ejb +wg 1 1 1 element +wg 1 1 1 elements +wg 1 1 1 em +wg 1 1 1 email +wg 1 1 1 emails +wg 1 1 1 embed +wg 1 1 1 embedded +wg 1 1 1 emea +wg 1 1 1 employees +wg 1 1 1 employment +wg 1 1 1 empty +wg 1 1 1 emu +wg 1 1 1 emulator +wg 1 1 1 en +ws 1 1 1 en_US +wg 1 1 1 enc +wg 1 1 1 encode +wg 1 1 1 encoder +wg 1 1 1 encrypt +wg 1 1 1 encrypted +wg 1 1 1 encyption +wg 1 1 1 eng +wg 1 1 1 engine +wg 1 1 1 english +wg 1 1 1 enterprise +wg 1 1 1 entertainment +wg 1 1 1 entries +wg 1 1 1 entry +wg 1 1 1 env +wg 1 1 1 environ +wg 1 1 1 environment +wg 1 1 1 epages +wg 1 1 1 eric +wg 1 1 1 error +ws 1 1 1 error-log +ws 1 1 1 error_log +wg 1 1 1 errors +wg 1 1 1 es +wg 1 1 1 esale +wg 1 1 1 esales +wg 1 1 1 etc +wg 1 1 1 eu +wg 1 1 1 europe +wg 1 1 1 event +wg 1 1 1 events +wg 1 1 1 evil +wg 1 1 1 evt +wg 1 1 1 ews +wg 1 1 1 ex +wg 1 1 1 example +wg 1 1 1 examples +wg 1 1 1 excalibur +wg 1 1 1 exchange +wg 1 1 1 exec +wg 1 1 1 explorer +wg 1 1 1 export +wg 1 1 1 ext +wg 1 1 1 ext2 +wg 1 1 1 extern +wg 1 1 1 external +wg 1 1 1 extras +wg 1 1 1 ezshopper +wg 1 1 1 f +wg 1 1 1 fabric +wg 1 1 1 face +wg 1 1 1 facebook +wg 1 1 1 faces +wg 1 1 1 faculty +wg 1 1 1 fail +wg 1 1 1 failure +wg 1 1 1 fake +wg 1 1 1 family +wg 1 1 1 faq +wg 1 1 1 faqs +wg 1 1 1 favorite +wg 1 1 1 favorites +wg 1 1 1 fb +wg 1 1 1 fbook +wg 1 1 1 fc +ws 1 1 1 fcgi-bin +wg 1 1 1 fckeditor +wg 1 1 1 feature +wg 1 1 1 features +wg 1 1 1 feed +wg 1 1 1 feedback +wg 1 1 1 feeds +wg 1 1 1 felix +wg 1 1 1 fetch +wg 1 1 1 field +wg 1 1 1 fields +wg 1 1 1 file +wg 1 1 1 fileadmin +wg 1 1 1 filelist +wg 1 1 1 files +wg 1 1 1 filez +wg 1 1 1 finance +wg 1 1 1 financial +wg 1 1 1 find +wg 1 1 1 finger +wg 1 1 1 firefox +wg 1 1 1 firewall +wg 1 1 1 firmware +wg 1 1 1 first +wg 1 1 1 fixed +wg 1 1 1 flags +wg 1 1 1 flash +wg 1 1 1 flow +wg 1 1 1 flows +wg 1 1 1 flv +wg 1 1 1 fn +wg 1 1 1 folder +wg 1 1 1 folders +wg 1 1 1 font +wg 1 1 1 fonts +wg 1 1 1 foo +wg 1 1 1 footer +wg 1 1 1 footers +wg 1 1 1 form +wg 1 1 1 format +wg 1 1 1 formatting +wg 1 1 1 formmail +wg 1 1 1 forms +wg 1 1 1 forrest +wg 1 1 1 fortune +wg 1 1 1 forum +wg 1 1 1 forum1 +wg 1 1 1 forum2 +wg 1 1 1 forumdisplay +wg 1 1 1 forums +wg 1 1 1 forward +wg 1 1 1 foto +wg 1 1 1 foundation +wg 1 1 1 fr +wg 1 1 1 frame +wg 1 1 1 frames +wg 1 1 1 framework +wg 1 1 1 free +wg 1 1 1 freebsd +wg 1 1 1 friend +wg 1 1 1 friends +wg 1 1 1 frob +wg 1 1 1 frontend +wg 1 1 1 fs +wg 1 1 1 ftp +wg 1 1 1 fuck +wg 1 1 1 fuckoff +wg 1 1 1 fuckyou +wg 1 1 1 full +wg 1 1 1 fun +wg 1 1 1 func +wg 1 1 1 funcs +wg 1 1 1 function +wg 1 1 1 functions +wg 1 1 1 fund +wg 1 1 1 funding +wg 1 1 1 funds +wg 1 1 1 fusion +wg 1 1 1 fw +wg 1 1 1 g +wg 1 1 1 gadget +wg 1 1 1 gadgets +wg 1 1 1 galleries +wg 1 1 1 gallery +wg 1 1 1 game +wg 1 1 1 games +wg 1 1 1 ganglia +wg 1 1 1 garbage +wg 1 1 1 gateway +wg 1 1 1 gb +wg 1 1 1 geeklog +wg 1 1 1 general +wg 1 1 1 geronimo +wg 1 1 1 get +wg 1 1 1 getaccess +wg 1 1 1 getjobid +wg 1 1 1 gfx +wg 1 1 1 gid +wg 1 1 1 git +wg 1 1 1 gitweb +wg 1 1 1 glimpse +wg 1 1 1 global +ws 1 1 1 global.asax +wg 1 1 1 globals +wg 1 1 1 glossary +wg 1 1 1 go +wg 1 1 1 goaway +wg 1 1 1 google +wg 1 1 1 government +wg 1 1 1 gprs +wg 1 1 1 grant +wg 1 1 1 grants +wg 1 1 1 graphics +wg 1 1 1 group +wg 1 1 1 groupcp +wg 1 1 1 groups +wg 1 1 1 gsm +wg 1 1 1 guest +wg 1 1 1 guestbook +wg 1 1 1 guests +wg 1 1 1 guide +wg 1 1 1 guides +wg 1 1 1 gump +wg 1 1 1 gwt +wg 1 1 1 h +wg 1 1 1 hack +wg 1 1 1 hacker +wg 1 1 1 hacking +wg 1 1 1 hackme +wg 1 1 1 hadoop +wg 1 1 1 hardcore +wg 1 1 1 hardware +wg 1 1 1 harmony +wg 1 1 1 head +wg 1 1 1 header +wg 1 1 1 headers +wg 1 1 1 health +wg 1 1 1 hello +wg 1 1 1 help +wg 1 1 1 helper +wg 1 1 1 helpers +wg 1 1 1 hi +wg 1 1 1 hidden +wg 1 1 1 hide +wg 1 1 1 high +wg 1 1 1 hipaa +wg 1 1 1 hire +wg 1 1 1 history +wg 1 1 1 hit +wg 1 1 1 hits +wg 1 1 1 hole +wg 1 1 1 home +wg 1 1 1 homepage +wg 1 1 1 hop +wg 1 1 1 horde +wg 1 1 1 hosting +wg 1 1 1 hosts +wg 1 1 1 hour +wg 1 1 1 hourly +wg 1 1 1 howto +wg 1 1 1 hp +wg 1 1 1 hr +wg 1 1 1 hta +wg 1 1 1 htaccess +ws 1 1 1 htbin +ws 1 1 1 htdoc +ws 1 1 1 htdocs +wg 1 1 1 htpasswd +wg 1 1 1 htsrv +wg 1 1 1 http +wg 1 1 1 httpd +wg 1 1 1 https +wg 1 1 1 httpuser +wg 1 1 1 hu +wg 1 1 1 hyper +wg 1 1 1 i +wg 1 1 1 ia +wg 1 1 1 ibm +wg 1 1 1 icat +wg 1 1 1 ico +wg 1 1 1 icon +wg 1 1 1 icons +wg 1 1 1 id +wg 1 1 1 idea +wg 1 1 1 ideas +wg 1 1 1 ids +wg 1 1 1 ie +wg 1 1 1 iframe +wg 1 1 1 ig +wg 1 1 1 ignore +ws 1 1 1 iisadmin +ws 1 1 1 iisadmpwd +ws 1 1 1 iissamples +wg 1 1 1 image +wg 1 1 1 imagefolio +wg 1 1 1 images +wg 1 1 1 img +wg 1 1 1 imgs +wg 1 1 1 imp +wg 1 1 1 import +wg 1 1 1 important +wg 1 1 1 in +wg 1 1 1 inbound +wg 1 1 1 incl +wg 1 1 1 include +wg 1 1 1 includes +wg 1 1 1 incoming +wg 1 1 1 incubator +wg 1 1 1 index +wg 1 1 1 index1 +wg 1 1 1 index2 +wg 1 1 1 index_1 +wg 1 1 1 index_2 +ws 1 1 1 inetpub +ws 1 1 1 inetsrv +wg 1 1 1 inf +wg 1 1 1 info +wg 1 1 1 information +wg 1 1 1 ingress +wg 1 1 1 init +wg 1 1 1 inline +wg 1 1 1 input +wg 1 1 1 inquire +wg 1 1 1 inquiries +wg 1 1 1 inquiry +wg 1 1 1 insert +wg 1 1 1 install +wg 1 1 1 installation +wg 1 1 1 int +wg 1 1 1 intel +wg 1 1 1 intelligence +wg 1 1 1 inter +wg 1 1 1 interim +wg 1 1 1 intermediate +wg 1 1 1 internal +wg 1 1 1 international +wg 1 1 1 internet +wg 1 1 1 intl +wg 1 1 1 intra +wg 1 1 1 intranet +wg 1 1 1 intro +wg 1 1 1 ip +wg 1 1 1 ipc +wg 1 1 1 iphone +wg 1 1 1 ips +wg 1 1 1 irc +wg 1 1 1 is +wg 1 1 1 isapi +wg 1 1 1 iso +wg 1 1 1 issues +wg 1 1 1 it +wg 1 1 1 item +wg 1 1 1 items +wg 1 1 1 j +wg 1 1 1 j2ee +wg 1 1 1 j2me +wg 1 1 1 jacob +wg 1 1 1 jakarta +wg 1 1 1 java-plugin +wg 1 1 1 javadoc +wg 1 1 1 javascript +wg 1 1 1 javax +wg 1 1 1 jboss +wg 1 1 1 jbossas +wg 1 1 1 jbossws +wg 1 1 1 jdbc +wg 1 1 1 jennifer +wg 1 1 1 jessica +wg 1 1 1 jigsaw +wg 1 1 1 jira +wg 1 1 1 jj +ws 1 1 1 jmx-console +wg 1 1 1 job +wg 1 1 1 jobs +wg 1 1 1 joe +wg 1 1 1 john +wg 1 1 1 join +wg 1 1 1 joomla +wg 1 1 1 journal +wg 1 1 1 jp +wg 1 1 1 jpa +wg 1 1 1 jre +wg 1 1 1 jrun +wg 1 1 1 js +wg 1 1 1 json +wg 1 1 1 jsso +wg 1 1 1 jsx +wg 1 1 1 juniper +wg 1 1 1 junk +wg 1 1 1 jvm +wg 1 1 1 k +wg 1 1 1 kboard +wg 1 1 1 keep +wg 1 1 1 kernel +wg 1 1 1 keygen +wg 1 1 1 keys +wg 1 1 1 kids +wg 1 1 1 kill +ws 1 1 1 known_hosts +wg 1 1 1 l +wg 1 1 1 la +wg 1 1 1 labs +wg 1 1 1 lang +wg 1 1 1 language +wg 1 1 1 large +wg 1 1 1 law +wg 1 1 1 layout +wg 1 1 1 layouts +wg 1 1 1 ldap +wg 1 1 1 leader +wg 1 1 1 leaders +wg 1 1 1 left +wg 1 1 1 legacy +wg 1 1 1 legal +wg 1 1 1 lenya +wg 1 1 1 letters +wg 1 1 1 level +wg 1 1 1 lg +wg 1 1 1 libraries +wg 1 1 1 library +wg 1 1 1 libs +wg 1 1 1 license +wg 1 1 1 licenses +wg 1 1 1 limit +wg 1 1 1 line +wg 1 1 1 link +wg 1 1 1 links +wg 1 1 1 linux +wg 1 1 1 list +wg 1 1 1 listinfo +wg 1 1 1 lists +wg 1 1 1 live +wg 1 1 1 lo +wg 1 1 1 loader +wg 1 1 1 loading +wg 1 1 1 loc +wg 1 1 1 local +wg 1 1 1 location +wg 1 1 1 lock +wg 1 1 1 locked +wg 1 1 1 log4j +wg 1 1 1 log4net +wg 1 1 1 logfile +wg 1 1 1 logger +wg 1 1 1 logging +wg 1 1 1 login +wg 1 1 1 logins +wg 1 1 1 logo +wg 1 1 1 logoff +wg 1 1 1 logon +wg 1 1 1 logos +wg 1 1 1 logout +wg 1 1 1 logs +wg 1 1 1 lost +ws 1 1 1 lost+found +wg 1 1 1 low +wg 1 1 1 ls +wg 1 1 1 lucene +wg 1 1 1 m +wg 1 1 1 mac +wg 1 1 1 macromedia +wg 1 1 1 maestro +ws 1 1 1 magento-check.php +wg 1 1 1 mail +wg 1 1 1 mailer +wg 1 1 1 mailing +wg 1 1 1 mailman +wg 1 1 1 mails +wg 1 1 1 main +wg 1 1 1 mambo +wg 1 1 1 manage +wg 1 1 1 managed +wg 1 1 1 management +wg 1 1 1 manager +wg 1 1 1 manual +wg 1 1 1 manuals +wg 1 1 1 map +wg 1 1 1 maps +wg 1 1 1 mark +wg 1 1 1 marketing +wg 1 1 1 master +ws 1 1 1 master.passwd +wg 1 1 1 match +wg 1 1 1 matrix +wg 1 1 1 matt +wg 1 1 1 maven +wg 1 1 1 mbox +wg 1 1 1 me +wg 1 1 1 media +wg 1 1 1 medium +wg 1 1 1 mem +wg 1 1 1 member +wg 1 1 1 memberlist +wg 1 1 1 members +wg 1 1 1 membership +wg 1 1 1 memory +wg 1 1 1 menu +wg 1 1 1 menus +wg 1 1 1 message +wg 1 1 1 messages +wg 1 1 1 messaging +wg 1 1 1 michael +wg 1 1 1 microsoft +wg 1 1 1 migrate +wg 1 1 1 migrated +wg 1 1 1 migration +wg 1 1 1 mina +wg 1 1 1 mini +wg 1 1 1 minute +wg 1 1 1 mirror +wg 1 1 1 mirrors +wg 1 1 1 misc +wg 1 1 1 mission +wg 1 1 1 mix +wg 1 1 1 mlist +wg 1 1 1 mms +wg 1 1 1 mobi +wg 1 1 1 mobile +wg 1 1 1 mock +wg 1 1 1 mod +wg 1 1 1 modify +wg 1 1 1 mods +wg 1 1 1 module +wg 1 1 1 modules +wg 1 1 1 mojo +wg 1 1 1 money +wg 1 1 1 monitor +wg 1 1 1 monitoring +wg 1 1 1 monitors +wg 1 1 1 month +wg 1 1 1 monthly +wg 1 1 1 more +wg 1 1 1 motd +wg 1 1 1 move +wg 1 1 1 moved +wg 1 1 1 movie +wg 1 1 1 movies +wg 1 1 1 mp +wg 1 1 1 mp3 +wg 1 1 1 mp3s +wg 1 1 1 ms +wg 1 1 1 ms-sql +wg 1 1 1 msadc +wg 1 1 1 msadm +wg 1 1 1 msft +wg 1 1 1 msie +wg 1 1 1 msql +wg 1 1 1 mssql +wg 1 1 1 mta +wg 1 1 1 multi +wg 1 1 1 multimedia +wg 1 1 1 music +wg 1 1 1 mx +wg 1 1 1 my +wg 1 1 1 myadmin +wg 1 1 1 myfaces +wg 1 1 1 myphpnuke +wg 1 1 1 mysql +wg 1 1 1 mysqld +wg 1 1 1 n +wg 1 1 1 nav +wg 1 1 1 navigation +wg 1 1 1 nc +wg 1 1 1 net +wg 1 1 1 netbsd +wg 1 1 1 netcat +wg 1 1 1 nethome +wg 1 1 1 nets +wg 1 1 1 network +wg 1 1 1 networking +wg 1 1 1 new +wg 1 1 1 news +wg 1 1 1 newsletter +wg 1 1 1 newsletters +wg 1 1 1 newticket +wg 1 1 1 next +wg 1 1 1 nfs +wg 1 1 1 nice +wg 1 1 1 nl +wg 1 1 1 nobody +wg 1 1 1 node +wg 1 1 1 noindex +wg 1 1 1 none +wg 1 1 1 note +wg 1 1 1 notes +wg 1 1 1 notification +wg 1 1 1 notifications +wg 1 1 1 notified +wg 1 1 1 notifier +wg 1 1 1 notify +wg 1 1 1 novell +wg 1 1 1 ns +wg 1 1 1 nude +wg 1 1 1 nuke +wg 1 1 1 nul +wg 1 1 1 null +ws 1 1 1 oa_servlets +wg 1 1 1 oauth +wg 1 1 1 obdc +wg 1 1 1 objects +wg 1 1 1 obsolete +wg 1 1 1 obsoleted +wg 1 1 1 odbc +wg 1 1 1 ode +wg 1 1 1 oem +wg 1 1 1 ofbiz +wg 1 1 1 office +wg 1 1 1 offices +wg 1 1 1 onbound +wg 1 1 1 online +wg 1 1 1 onlinestats +wg 1 1 1 op +wg 1 1 1 open +wg 1 1 1 openbsd +wg 1 1 1 opencart +wg 1 1 1 opendir +wg 1 1 1 openejb +wg 1 1 1 openjpa +wg 1 1 1 opera +wg 1 1 1 operations +wg 1 1 1 opinion +ws 1 1 1 oprocmgr-status +wg 1 1 1 opt +wg 1 1 1 option +wg 1 1 1 options +wg 1 1 1 oracle +ws 1 1 1 oracle.xml.xsql.XSQLServlet +wg 1 1 1 order +wg 1 1 1 ordered +wg 1 1 1 orders +wg 1 1 1 org +wg 1 1 1 osc +wg 1 1 1 oscommerce +wg 1 1 1 other +wg 1 1 1 outcome +wg 1 1 1 outgoing +wg 1 1 1 outline +wg 1 1 1 output +wg 1 1 1 outreach +wg 1 1 1 overview +wg 1 1 1 owa +wg 1 1 1 owl +wg 1 1 1 ows +ws 1 1 1 ows-bin +wg 1 1 1 p +wg 1 1 1 p2p +wg 1 1 1 pack +wg 1 1 1 package +wg 1 1 1 packaged +wg 1 1 1 packages +wg 1 1 1 packed +wg 1 1 1 page +wg 1 1 1 page1 +wg 1 1 1 page2 +wg 1 1 1 page_1 +wg 1 1 1 page_2 +wg 1 1 1 pages +wg 1 1 1 paid +wg 1 1 1 panel +wg 1 1 1 paper +wg 1 1 1 papers +wg 1 1 1 parse +wg 1 1 1 partner +wg 1 1 1 partners +wg 1 1 1 party +wg 1 1 1 pass +wg 1 1 1 passive +wg 1 1 1 passwd +wg 1 1 1 password +wg 1 1 1 passwords +wg 1 1 1 past +wg 1 1 1 patch +wg 1 1 1 patches +wg 1 1 1 pay +wg 1 1 1 payment +wg 1 1 1 payments +wg 1 1 1 paypal +wg 1 1 1 pbo +wg 1 1 1 pc +wg 1 1 1 pci +wg 1 1 1 pda +wg 1 1 1 pdfs +wg 1 1 1 pear +wg 1 1 1 peek +wg 1 1 1 pending +wg 1 1 1 people +wg 1 1 1 perf +wg 1 1 1 performance +wg 1 1 1 perl +wg 1 1 1 personal +wg 1 1 1 pg +wg 1 1 1 phf +wg 1 1 1 phone +wg 1 1 1 phones +wg 1 1 1 phorum +wg 1 1 1 photo +wg 1 1 1 photos +ws 1 1 1 phpBB +ws 1 1 1 phpBB2 +ws 1 1 1 phpEventCalendar +ws 1 1 1 phpMyAdmin +ws 1 1 1 phpbb +ws 1 1 1 phpmyadmin +ws 1 1 1 phpnuke +wg 1 1 1 phps +wg 1 1 1 pic +wg 1 1 1 pics +wg 1 1 1 pictures +wg 1 1 1 pii +wg 1 1 1 ping +wg 1 1 1 pipe +wg 1 1 1 pipermail +wg 1 1 1 piranha +wg 1 1 1 pivot +wg 1 1 1 pix +wg 1 1 1 pixel +wg 1 1 1 pkg +wg 1 1 1 pkgs +wg 1 1 1 plain +wg 1 1 1 play +wg 1 1 1 player +wg 1 1 1 playing +wg 1 1 1 playlist +wg 1 1 1 pls +wg 1 1 1 plugin +wg 1 1 1 plugins +ws 1 1 1 plus +wg 1 1 1 poc +wg 1 1 1 poi +wg 1 1 1 policies +wg 1 1 1 policy +wg 1 1 1 politics +wg 1 1 1 poll +wg 1 1 1 polls +wg 1 1 1 pool +wg 1 1 1 pop +wg 1 1 1 pop3 +wg 1 1 1 popup +wg 1 1 1 porn +wg 1 1 1 port +wg 1 1 1 portal +wg 1 1 1 portals +wg 1 1 1 portfolio +wg 1 1 1 pos +wg 1 1 1 post +wg 1 1 1 posted +wg 1 1 1 postgres +wg 1 1 1 postgresql +wg 1 1 1 postnuke +wg 1 1 1 postpaid +wg 1 1 1 posts +wg 1 1 1 pr +wg 1 1 1 pr0n +wg 1 1 1 premium +wg 1 1 1 prepaid +wg 1 1 1 presentation +wg 1 1 1 presentations +wg 1 1 1 preserve +wg 1 1 1 press +wg 1 1 1 preview +wg 1 1 1 previews +wg 1 1 1 previous +wg 1 1 1 pricing +wg 1 1 1 print +wg 1 1 1 prins +wg 1 1 1 printenv +wg 1 1 1 printer +wg 1 1 1 printers +wg 1 1 1 priv +wg 1 1 1 privacy +wg 1 1 1 private +wg 1 1 1 pro +wg 1 1 1 problems +wg 1 1 1 proc +wg 1 1 1 procedures +wg 1 1 1 procure +wg 1 1 1 procurement +wg 1 1 1 prod +wg 1 1 1 product +wg 1 1 1 product_info +wg 1 1 1 production +wg 1 1 1 products +wg 1 1 1 profile +wg 1 1 1 profiles +wg 1 1 1 profiling +wg 1 1 1 program +wg 1 1 1 programming +wg 1 1 1 programs +wg 1 1 1 progress +wg 1 1 1 project +wg 1 1 1 projects +wg 1 1 1 promo +wg 1 1 1 promoted +wg 1 1 1 promotion +wg 1 1 1 prop +ws 1 1 1 prop-base +wg 1 1 1 property +wg 1 1 1 props +wg 1 1 1 prot +wg 1 1 1 protect +wg 1 1 1 protected +wg 1 1 1 protection +wg 1 1 1 proto +wg 1 1 1 proxies +wg 1 1 1 proxy +wg 1 1 1 prv +wg 1 1 1 ps +wg 1 1 1 psql +wg 1 1 1 pt +wg 1 1 1 pub +wg 1 1 1 public +wg 1 1 1 publication +wg 1 1 1 publications +wg 1 1 1 publish +wg 1 1 1 pubs +wg 1 1 1 pull +wg 1 1 1 purchase +wg 1 1 1 purchases +wg 1 1 1 purchasing +wg 1 1 1 push +wg 1 1 1 pw +wg 1 1 1 pwd +wg 1 1 1 python +wg 1 1 1 q +wg 1 1 1 q1 +wg 1 1 1 q2 +wg 1 1 1 q3 +wg 1 1 1 q4 +wg 1 1 1 qotd +wg 1 1 1 qpid +wg 1 1 1 quarterly +wg 1 1 1 queries +wg 1 1 1 query +wg 1 1 1 queue +wg 1 1 1 queues +wg 1 1 1 quote +wg 1 1 1 quotes +wg 1 1 1 r +wg 1 1 1 radio +wg 1 1 1 random +wg 1 1 1 rate +wg 1 1 1 rdf +wg 1 1 1 read +wg 1 1 1 readme +wg 1 1 1 real +wg 1 1 1 realestate +wg 1 1 1 receive +wg 1 1 1 received +wg 1 1 1 recharge +wg 1 1 1 record +wg 1 1 1 recorded +wg 1 1 1 recorder +wg 1 1 1 records +wg 1 1 1 recovery +wg 1 1 1 recycle +wg 1 1 1 recycled +wg 1 1 1 redir +wg 1 1 1 redirect +wg 1 1 1 reference +wg 1 1 1 reg +wg 1 1 1 register +wg 1 1 1 registered +wg 1 1 1 registration +wg 1 1 1 registrations +wg 1 1 1 release +wg 1 1 1 releases +wg 1 1 1 remind +wg 1 1 1 reminder +wg 1 1 1 remote +wg 1 1 1 removal +wg 1 1 1 removals +wg 1 1 1 remove +wg 1 1 1 removed +wg 1 1 1 render +wg 1 1 1 rendered +wg 1 1 1 rep +wg 1 1 1 repl +wg 1 1 1 replica +wg 1 1 1 replicas +wg 1 1 1 replicate +wg 1 1 1 replicated +wg 1 1 1 replication +wg 1 1 1 replicator +wg 1 1 1 reply +wg 1 1 1 report +wg 1 1 1 reporting +wg 1 1 1 reports +wg 1 1 1 reprints +wg 1 1 1 req +wg 1 1 1 reqs +wg 1 1 1 request +wg 1 1 1 requests +wg 1 1 1 requisition +wg 1 1 1 requisitions +wg 1 1 1 res +wg 1 1 1 research +wg 1 1 1 resin +wg 1 1 1 resize +wg 1 1 1 resolution +wg 1 1 1 resolve +wg 1 1 1 resolved +wg 1 1 1 resource +wg 1 1 1 resources +wg 1 1 1 rest +wg 1 1 1 restore +wg 1 1 1 restored +wg 1 1 1 restricted +wg 1 1 1 result +wg 1 1 1 results +wg 1 1 1 retail +wg 1 1 1 reverse +wg 1 1 1 reversed +wg 1 1 1 revert +wg 1 1 1 reverted +wg 1 1 1 review +wg 1 1 1 reviews +wg 1 1 1 right +wg 1 1 1 roam +wg 1 1 1 roaming +wg 1 1 1 robot +wg 1 1 1 robots +wg 1 1 1 roller +wg 1 1 1 room +wg 1 1 1 root +wg 1 1 1 rpc +wg 1 1 1 rsa +wg 1 1 1 ru +wg 1 1 1 ruby +wg 1 1 1 rule +wg 1 1 1 rules +wg 1 1 1 run +wg 1 1 1 rwservlet +wg 1 1 1 s +wg 1 1 1 sale +wg 1 1 1 sales +wg 1 1 1 salesforce +wg 1 1 1 sam +wg 1 1 1 samba +wg 1 1 1 saml +wg 1 1 1 sample +wg 1 1 1 samples +wg 1 1 1 san +wg 1 1 1 sav +wg 1 1 1 saved +wg 1 1 1 saves +wg 1 1 1 sbin +wg 1 1 1 scan +wg 1 1 1 scanned +wg 1 1 1 scans +wg 1 1 1 sched +wg 1 1 1 schedule +wg 1 1 1 scheduled +wg 1 1 1 scheduling +wg 1 1 1 schema +wg 1 1 1 science +wg 1 1 1 screen +wg 1 1 1 screens +wg 1 1 1 screenshot +wg 1 1 1 screenshots +wg 1 1 1 script +wg 1 1 1 scriptlet +wg 1 1 1 scriptlets +wg 1 1 1 scripts +wg 1 1 1 scw +wg 1 1 1 sdk +wg 1 1 1 se +wg 1 1 1 search +wg 1 1 1 sec +wg 1 1 1 second +wg 1 1 1 secret +wg 1 1 1 section +wg 1 1 1 sections +wg 1 1 1 secure +wg 1 1 1 secured +wg 1 1 1 security +wg 1 1 1 seed +wg 1 1 1 select +wg 1 1 1 sell +wg 1 1 1 send +wg 1 1 1 sendmail +wg 1 1 1 sendto +wg 1 1 1 sent +wg 1 1 1 serial +wg 1 1 1 serv +wg 1 1 1 serve +wg 1 1 1 server +ws 1 1 1 server-info +ws 1 1 1 server-status +wg 1 1 1 servers +wg 1 1 1 service +wg 1 1 1 services +wg 1 1 1 servlet +wg 1 1 1 servlets +wg 1 1 1 session +wg 1 1 1 sessions +wg 1 1 1 setting +wg 1 1 1 settings +wg 1 1 1 setup +wg 1 1 1 sex +wg 1 1 1 shadow +wg 1 1 1 share +wg 1 1 1 shared +wg 1 1 1 shares +wg 1 1 1 shell +wg 1 1 1 ship +wg 1 1 1 shipped +wg 1 1 1 shipping +wg 1 1 1 shockwave +wg 1 1 1 shop +wg 1 1 1 shopadmin +wg 1 1 1 shopper +wg 1 1 1 shopping +wg 1 1 1 shops +wg 1 1 1 shoutbox +wg 1 1 1 show +wg 1 1 1 show_post +wg 1 1 1 show_thread +wg 1 1 1 showcat +wg 1 1 1 showenv +wg 1 1 1 showjobs +wg 1 1 1 showmap +wg 1 1 1 showmsg +wg 1 1 1 showpost +wg 1 1 1 showthread +wg 1 1 1 sign +wg 1 1 1 signed +wg 1 1 1 signer +wg 1 1 1 signin +wg 1 1 1 signing +wg 1 1 1 signoff +wg 1 1 1 signon +wg 1 1 1 signout +wg 1 1 1 signup +wg 1 1 1 simple +wg 1 1 1 sink +wg 1 1 1 site +wg 1 1 1 siteadmin +ws 1 1 1 site-map +ws 1 1 1 site_map +wg 1 1 1 sitemap +wg 1 1 1 sites +wg 1 1 1 skel +wg 1 1 1 skin +wg 1 1 1 skins +wg 1 1 1 skip +wg 1 1 1 sl +wg 1 1 1 sling +wg 1 1 1 sm +wg 1 1 1 small +wg 1 1 1 smf +wg 1 1 1 smile +wg 1 1 1 smiles +wg 1 1 1 sms +wg 1 1 1 smtp +wg 1 1 1 snoop +wg 1 1 1 soap +wg 1 1 1 soaprouter +wg 1 1 1 soft +wg 1 1 1 software +wg 1 1 1 solaris +wg 1 1 1 sold +wg 1 1 1 solution +wg 1 1 1 solutions +wg 1 1 1 solve +wg 1 1 1 solved +wg 1 1 1 source +wg 1 1 1 sources +wg 1 1 1 sox +wg 1 1 1 sp +wg 1 1 1 space +wg 1 1 1 spacer +wg 1 1 1 spam +wg 1 1 1 special +wg 1 1 1 specials +wg 1 1 1 sponsor +wg 1 1 1 sponsors +wg 1 1 1 spool +wg 1 1 1 sport +wg 1 1 1 sports +wg 1 1 1 sqlnet +wg 1 1 1 squirrel +wg 1 1 1 squirrelmail +wg 1 1 1 src +wg 1 1 1 srv +wg 1 1 1 ss +wg 1 1 1 ssh +wg 1 1 1 ssi +wg 1 1 1 ssl +ws 1 1 1 sslvpn +wg 1 1 1 ssn +wg 1 1 1 sso +wg 1 1 1 staff +wg 1 1 1 staging +wg 1 1 1 standalone +wg 1 1 1 standard +wg 1 1 1 standards +wg 1 1 1 star +wg 1 1 1 start +wg 1 1 1 stat +wg 1 1 1 statement +wg 1 1 1 statements +wg 1 1 1 static +wg 1 1 1 staticpages +wg 1 1 1 statistic +wg 1 1 1 statistics +wg 1 1 1 stats +wg 1 1 1 status +wg 1 1 1 stock +wg 1 1 1 storage +wg 1 1 1 store +wg 1 1 1 stored +wg 1 1 1 stores +wg 1 1 1 stories +wg 1 1 1 story +wg 1 1 1 strut +wg 1 1 1 struts +wg 1 1 1 student +wg 1 1 1 students +wg 1 1 1 stuff +wg 1 1 1 style +wg 1 1 1 styles +wg 1 1 1 submissions +wg 1 1 1 submit +wg 1 1 1 subscribe +wg 1 1 1 subscribed +wg 1 1 1 subscriber +wg 1 1 1 subscribers +wg 1 1 1 subscription +wg 1 1 1 subscriptions +wg 1 1 1 success +wg 1 1 1 suite +wg 1 1 1 suites +wg 1 1 1 sun +wg 1 1 1 sunos +wg 1 1 1 super +wg 1 1 1 support +wg 1 1 1 surf +wg 1 1 1 survey +wg 1 1 1 surveys +wg 1 1 1 sws +wg 1 1 1 synapse +wg 1 1 1 sync +wg 1 1 1 synced +wg 1 1 1 sys +wg 1 1 1 sysmanager +wg 1 1 1 system +wg 1 1 1 systems +wg 1 1 1 sysuser +wg 1 1 1 t +wg 1 1 1 tag +wg 1 1 1 tags +wg 1 1 1 tail +wg 1 1 1 tape +wg 1 1 1 tapes +wg 1 1 1 tapestry +wg 1 1 1 tb +wg 1 1 1 tcl +wg 1 1 1 team +wg 1 1 1 tech +wg 1 1 1 technical +wg 1 1 1 technology +wg 1 1 1 tel +wg 1 1 1 tele +wg 1 1 1 temp +wg 1 1 1 templ +wg 1 1 1 template +wg 1 1 1 templates +wg 1 1 1 terms +ws 1 1 1 test-cgi +ws 1 1 1 test-env +wg 1 1 1 test1 +wg 1 1 1 test123 +wg 1 1 1 test1234 +wg 1 1 1 test2 +wg 1 1 1 test3 +wg 1 1 1 testimonial +wg 1 1 1 testimonials +wg 1 1 1 testing +wg 1 1 1 tests +wg 1 1 1 texis +wg 1 1 1 text +ws 1 1 1 text-base +wg 1 1 1 texts +wg 1 1 1 theme +wg 1 1 1 themes +wg 1 1 1 thread +wg 1 1 1 threads +wg 1 1 1 thumb +wg 1 1 1 thumbnail +wg 1 1 1 thumbnails +wg 1 1 1 thumbs +wg 1 1 1 tickets +wg 1 1 1 tiki +wg 1 1 1 tiles +wg 1 1 1 tip +wg 1 1 1 tips +wg 1 1 1 title +wg 1 1 1 tls +wg 1 1 1 tmpl +wg 1 1 1 tmps +wg 1 1 1 tn +wg 1 1 1 toc +wg 1 1 1 todo +wg 1 1 1 toggle +wg 1 1 1 tomcat +wg 1 1 1 tool +wg 1 1 1 toolbar +wg 1 1 1 toolkit +wg 1 1 1 tools +wg 1 1 1 top +wg 1 1 1 topic +wg 1 1 1 topics +wg 1 1 1 torrent +wg 1 1 1 torrents +wg 1 1 1 tos +wg 1 1 1 tour +wg 1 1 1 tpv +wg 1 1 1 tr +wg 1 1 1 traceroute +wg 1 1 1 traces +wg 1 1 1 track +wg 1 1 1 trackback +wg 1 1 1 tracker +wg 1 1 1 trackers +wg 1 1 1 tracking +wg 1 1 1 tracks +wg 1 1 1 traffic +wg 1 1 1 trailer +wg 1 1 1 trailers +wg 1 1 1 training +wg 1 1 1 trans +wg 1 1 1 transaction +wg 1 1 1 transactions +wg 1 1 1 transparent +wg 1 1 1 transport +wg 1 1 1 trash +wg 1 1 1 travel +wg 1 1 1 treasury +wg 1 1 1 tree +wg 1 1 1 trees +wg 1 1 1 trial +wg 1 1 1 true +wg 1 1 1 trunk +wg 1 1 1 tsweb +wg 1 1 1 tt +wg 1 1 1 turbine +wg 1 1 1 tuscany +wg 1 1 1 tutorial +wg 1 1 1 tutorials +wg 1 1 1 tv +wg 1 1 1 tweak +wg 1 1 1 twitter +wg 1 1 1 type +ws 1 1 1 typo +ws 1 1 1 typo3 +ws 1 1 1 typo3conf +wg 1 1 1 u +wg 1 1 1 ubb +wg 1 1 1 ucp +wg 1 1 1 uds +wg 1 1 1 uk +wg 1 1 1 umts +wg 1 1 1 union +wg 1 1 1 unix +wg 1 1 1 unlock +wg 1 1 1 unpaid +wg 1 1 1 unreg +wg 1 1 1 unregister +wg 1 1 1 unsubscribe +wg 1 1 1 up +wg 1 1 1 upd +wg 1 1 1 update +wg 1 1 1 updated +wg 1 1 1 updater +wg 1 1 1 updates +wg 1 1 1 upload +wg 1 1 1 uploader +wg 1 1 1 uploads +wg 1 1 1 url +wg 1 1 1 urls +wg 1 1 1 us +wg 1 1 1 usa +wg 1 1 1 usage +wg 1 1 1 user +wg 1 1 1 userlog +wg 1 1 1 users +wg 1 1 1 usr +wg 1 1 1 util +wg 1 1 1 utilities +wg 1 1 1 utility +wg 1 1 1 utils +wg 1 1 1 v +wg 1 1 1 v1 +wg 1 1 1 v2 +wg 1 1 1 var +wg 1 1 1 vault +wg 1 1 1 vector +wg 1 1 1 velocity +wg 1 1 1 vendor +wg 1 1 1 vendors +wg 1 1 1 ver +wg 1 1 1 ver1 +wg 1 1 1 ver2 +wg 1 1 1 version +wg 1 1 1 vfs +wg 1 1 1 video +wg 1 1 1 videos +wg 1 1 1 view +wg 1 1 1 view-source +wg 1 1 1 viewcvs +wg 1 1 1 viewforum +wg 1 1 1 viewonline +wg 1 1 1 views +wg 1 1 1 viewsource +wg 1 1 1 viewsvn +wg 1 1 1 viewtopic +wg 1 1 1 viewvc +wg 1 1 1 virtual +wg 1 1 1 vm +wg 1 1 1 voip +wg 1 1 1 vol +wg 1 1 1 vote +wg 1 1 1 voter +wg 1 1 1 votes +wg 1 1 1 vpn +wg 1 1 1 vuln +wg 1 1 1 w +wg 1 1 1 w3 +wg 1 1 1 w3c +wg 1 1 1 wa +wg 1 1 1 wap +wg 1 1 1 warez +ws 1 1 1 way-board +wg 1 1 1 wbboard +wg 1 1 1 wc +wg 1 1 1 weather +wg 1 1 1 web +wg 1 1 1 web-beans +wg 1 1 1 web-console +wg 1 1 1 webaccess +wg 1 1 1 webadmin +wg 1 1 1 webagent +wg 1 1 1 webalizer +wg 1 1 1 webapp +wg 1 1 1 webb +wg 1 1 1 webbbs +wg 1 1 1 webboard +wg 1 1 1 webcalendar +wg 1 1 1 webcart +wg 1 1 1 webcasts +wg 1 1 1 webcgi +wg 1 1 1 webchat +wg 1 1 1 webdata +wg 1 1 1 webdav +wg 1 1 1 webdb +wg 1 1 1 weblog +wg 1 1 1 weblogic +wg 1 1 1 weblogs +wg 1 1 1 webmail +wg 1 1 1 webplus +wg 1 1 1 webshop +wg 1 1 1 webservice +wg 1 1 1 website +wg 1 1 1 websphere +wg 1 1 1 websql +wg 1 1 1 webstats +wg 1 1 1 websvn +wg 1 1 1 webwork +wg 1 1 1 week +wg 1 1 1 weekly +wg 1 1 1 welcome +wg 1 1 1 whitepapers +wg 1 1 1 whois +wg 1 1 1 whosonline +wg 1 1 1 wicket +wg 1 1 1 wiki +wg 1 1 1 win +ws 1 1 1 win32 +wg 1 1 1 windows +ws 1 1 1 winnt +wg 1 1 1 wireless +wg 1 1 1 wml +wg 1 1 1 word +wg 1 1 1 wordpress +wg 1 1 1 work +wg 1 1 1 working +wg 1 1 1 world +wg 1 1 1 wow +wg 1 1 1 wp +ws 1 1 1 wp- +ws 1 1 1 wp-admin +ws 1 1 1 wp-content +ws 1 1 1 wp-dbmanager +ws 1 1 1 wp-includes +ws 1 1 1 wp-login +ws 1 1 1 wp-syntax +wg 1 1 1 wrap +ws 1 1 1 ws-client +ws 1 1 1 ws_ftp +wg 1 1 1 wsdl +wg 1 1 1 wtai +wg 1 1 1 www +wg 1 1 1 www-sql +wg 1 1 1 www1 +wg 1 1 1 www2 +wg 1 1 1 www3 +wg 1 1 1 wwwboard +wg 1 1 1 wwwroot +wg 1 1 1 wwwstats +wg 1 1 1 wwwthreads +wg 1 1 1 wwwuser +wg 1 1 1 wysiwyg +wg 1 1 1 x +wg 1 1 1 xalan +wg 1 1 1 xerces +wg 1 1 1 xhtml +wg 1 1 1 xn +wg 1 1 1 xmlrpc +wg 1 1 1 xsql +wg 1 1 1 xxx +wg 1 1 1 xyzzy +wg 1 1 1 y +wg 1 1 1 yahoo +wg 1 1 1 year +wg 1 1 1 yearly +wg 1 1 1 youtube +wg 1 1 1 yt +wg 1 1 1 z +wg 1 1 1 zboard +wg 1 1 1 zencart +wg 1 1 1 zend +wg 1 1 1 zero +wg 1 1 1 zimbra +wg 1 1 1 zipfiles +wg 1 1 1 zips +wg 1 1 1 zoom +wg 1 1 1 zope +wg 1 1 1 zorum +ws 1 1 1 ~admin +ws 1 1 1 ~amanda +ws 1 1 1 ~apache +ws 1 1 1 ~ashley +ws 1 1 1 ~bin +ws 1 1 1 ~bob +ws 1 1 1 ~chris +ws 1 1 1 ~dan +ws 1 1 1 ~eric +ws 1 1 1 ~ftp +ws 1 1 1 ~guest +ws 1 1 1 ~http +ws 1 1 1 ~httpd +ws 1 1 1 ~jacob +ws 1 1 1 ~jennifer +ws 1 1 1 ~jessica +ws 1 1 1 ~john +ws 1 1 1 ~log +ws 1 1 1 ~logs +ws 1 1 1 ~lp +ws 1 1 1 ~mark +ws 1 1 1 ~matt +ws 1 1 1 ~michael +ws 1 1 1 ~nobody +ws 1 1 1 ~root +ws 1 1 1 ~test +ws 1 1 1 ~tmp +ws 1 1 1 ~www diff -Nru skipfish-2.02b/dictionaries/extensions-only.wl skipfish-2.10b/dictionaries/extensions-only.wl --- skipfish-2.02b/dictionaries/extensions-only.wl 2011-06-21 19:35:29.000000000 +0000 +++ skipfish-2.10b/dictionaries/extensions-only.wl 2012-12-04 13:27:53.000000000 +0000 @@ -1,3 +1,4 @@ +#ro e 1 1 1 7z e 1 1 1 as e 1 1 1 asmx diff -Nru skipfish-2.02b/dictionaries/medium.wl skipfish-2.10b/dictionaries/medium.wl --- skipfish-2.02b/dictionaries/medium.wl 2011-07-03 05:59:42.000000000 +0000 +++ skipfish-2.10b/dictionaries/medium.wl 2012-12-04 13:27:53.000000000 +0000 @@ -1,429 +1,430 @@ -e 1 1 1 asmx -e 1 1 1 asp -e 1 1 1 aspx -e 1 1 1 bak -e 1 1 1 bat -e 1 1 1 cc -e 1 1 1 cfg -e 1 1 1 cfm -e 1 1 1 cgi -e 1 1 1 class -e 1 1 1 cnf -e 1 1 1 conf -e 1 1 1 config -e 1 1 1 core -e 1 1 1 cpp -e 1 1 1 csproj -e 1 1 1 csv -e 1 1 1 dat -e 1 1 1 db -e 1 1 1 dll -e 1 1 1 err -e 1 1 1 error -e 1 1 1 exe -e 1 1 1 fcgi -e 1 1 1 gz -e 1 1 1 htm -e 1 1 1 html -e 1 1 1 inc -e 1 1 1 ini -e 1 1 1 jar -e 1 1 1 java -e 1 1 1 jhtml -e 1 1 1 js -e 1 1 1 jsf -e 1 1 1 jsp -e 1 1 1 key -e 1 1 1 log -e 1 1 1 mdb -e 1 1 1 nsf -e 1 1 1 old -e 1 1 1 ora -e 1 1 1 orig -e 1 1 1 out -e 1 1 1 part -e 1 1 1 php -e 1 1 1 php3 -e 1 1 1 phtml -e 1 1 1 pl -e 1 1 1 pm -e 1 1 1 py -e 1 1 1 rb -e 1 1 1 rss -e 1 1 1 sh -e 1 1 1 shtml -e 1 1 1 sql -e 1 1 1 stackdump -e 1 1 1 svn-base -e 1 1 1 tar.gz -e 1 1 1 temp -e 1 1 1 test -e 1 1 1 tgz -e 1 1 1 tmp -e 1 1 1 txt -e 1 1 1 vb -e 1 1 1 vbs -e 1 1 1 ws -e 1 1 1 xls -e 1 1 1 xml -e 1 1 1 xsl -e 1 1 1 xslt -e 1 1 1 zip -w 1 1 1 .bash_history -w 1 1 1 .bashrc -w 1 1 1 .cvsignore -w 1 1 1 .history -w 1 1 1 .htaccess -w 1 1 1 .htpasswd -w 1 1 1 .passwd -w 1 1 1 .perf -w 1 1 1 .ssh -w 1 1 1 .svn -w 1 1 1 .web -w 1 1 1 0 -w 1 1 1 00 -w 1 1 1 01 -w 1 1 1 02 -w 1 1 1 03 -w 1 1 1 04 -w 1 1 1 05 -w 1 1 1 06 -w 1 1 1 07 -w 1 1 1 08 -w 1 1 1 09 -w 1 1 1 1 -w 1 1 1 10 -w 1 1 1 100 -w 1 1 1 1000 -w 1 1 1 1001 -w 1 1 1 101 -w 1 1 1 11 -w 1 1 1 12 -w 1 1 1 13 -w 1 1 1 14 -w 1 1 1 15 -w 1 1 1 1990 -w 1 1 1 1991 -w 1 1 1 1992 -w 1 1 1 1993 -w 1 1 1 1994 -w 1 1 1 1995 -w 1 1 1 1996 -w 1 1 1 1997 -w 1 1 1 1998 -w 1 1 1 1999 -w 1 1 1 2 -w 1 1 1 20 -w 1 1 1 200 -w 1 1 1 2000 -w 1 1 1 2001 -w 1 1 1 2002 -w 1 1 1 2003 -w 1 1 1 2004 -w 1 1 1 2005 -w 1 1 1 2006 -w 1 1 1 2007 -w 1 1 1 2008 -w 1 1 1 2009 -w 1 1 1 2010 -w 1 1 1 2011 -w 1 1 1 2012 -w 1 1 1 2013 -w 1 1 1 2014 -w 1 1 1 21 -w 1 1 1 22 -w 1 1 1 23 -w 1 1 1 24 -w 1 1 1 25 -w 1 1 1 2g -w 1 1 1 3 -w 1 1 1 300 -w 1 1 1 3g -w 1 1 1 4 -w 1 1 1 42 -w 1 1 1 5 -w 1 1 1 50 -w 1 1 1 500 -w 1 1 1 51 -w 1 1 1 6 -w 1 1 1 7 -w 1 1 1 7z -w 1 1 1 8 -w 1 1 1 9 -w 1 1 1 ADM -w 1 1 1 ADMIN -w 1 1 1 Admin -w 1 1 1 AdminService -w 1 1 1 AggreSpy -w 1 1 1 AppsLocalLogin -w 1 1 1 AppsLogin -w 1 1 1 BUILD -w 1 1 1 CMS -w 1 1 1 CVS -w 1 1 1 DB -w 1 1 1 DMSDump -w 1 1 1 Documents and Settings -w 1 1 1 Entries -w 1 1 1 FCKeditor -w 1 1 1 JMXSoapAdapter -w 1 1 1 LICENSE -w 1 1 1 MANIFEST.MF -w 1 1 1 META-INF -w 1 1 1 Makefile -w 1 1 1 OA -w 1 1 1 OAErrorDetailPage -w 1 1 1 OA_HTML -w 1 1 1 Program Files -w 1 1 1 README -w 1 1 1 Rakefile -w 1 1 1 Readme -w 1 1 1 Recycled -w 1 1 1 Root -w 1 1 1 SERVER-INF -w 1 1 1 SOAPMonitor -w 1 1 1 SQL -w 1 1 1 SUNWmc -w 1 1 1 SiteScope -w 1 1 1 SiteServer -w 1 1 1 Spy -w 1 1 1 TEMP -w 1 1 1 TMP -w 1 1 1 TODO -w 1 1 1 Thumbs.db -w 1 1 1 WEB-INF -w 1 1 1 WS_FTP -w 1 1 1 XXX -w 1 1 1 _ -w 1 1 1 _adm -w 1 1 1 _admin -w 1 1 1 _common -w 1 1 1 _conf -w 1 1 1 _files -w 1 1 1 _include -w 1 1 1 _js -w 1 1 1 _mem_bin -w 1 1 1 _old -w 1 1 1 _pages -w 1 1 1 _private -w 1 1 1 _res -w 1 1 1 _source -w 1 1 1 _src -w 1 1 1 _test -w 1 1 1 _vti_bin -w 1 1 1 _vti_cnf -w 1 1 1 _vti_pvt -w 1 1 1 _vti_txt -w 1 1 1 _www -w 1 1 1 a -w 1 1 1 aa -w 1 1 1 aaa -w 1 1 1 abc -w 1 1 1 abc123 -w 1 1 1 abcd -w 1 1 1 abcd1234 -w 1 1 1 about -w 1 1 1 access -w 1 1 1 access-log -w 1 1 1 access-log.1 -w 1 1 1 access.1 -w 1 1 1 access_log -w 1 1 1 access_log.1 -w 1 1 1 accessibility -w 1 1 1 account -w 1 1 1 accounting -w 1 1 1 accounts -w 1 1 1 action -w 1 1 1 actions -w 1 1 1 active -w 1 1 1 activex -w 1 1 1 ad -w 1 1 1 adclick -w 1 1 1 add -w 1 1 1 addpost -w 1 1 1 addressbook -w 1 1 1 adm -w 1 1 1 admin -w 1 1 1 admin-console -w 1 1 1 admin_ -w 1 1 1 admins -w 1 1 1 adobe -w 1 1 1 adodb -w 1 1 1 ads -w 1 1 1 adv -w 1 1 1 advanced -w 1 1 1 advertise -w 1 1 1 advertising -w 1 1 1 affiliate -w 1 1 1 affiliates -w 1 1 1 agenda -w 1 1 1 agent -w 1 1 1 agents -w 1 1 1 ajax -w 1 1 1 akamai -w 1 1 1 album -w 1 1 1 albums -w 1 1 1 alcatel -w 1 1 1 alert -w 1 1 1 alerts -w 1 1 1 alias -w 1 1 1 aliases -w 1 1 1 all -w 1 1 1 all-wcprops -w 1 1 1 alpha -w 1 1 1 alumni -w 1 1 1 amanda -w 1 1 1 amazon -w 1 1 1 analog -w 1 1 1 android -w 1 1 1 announcement -w 1 1 1 announcements -w 1 1 1 annual -w 1 1 1 anon -w 1 1 1 anonymous -w 1 1 1 ansi -w 1 1 1 apac -w 1 1 1 apache -w 1 1 1 apexec -w 1 1 1 api -w 1 1 1 apis -w 1 1 1 app -w 1 1 1 appeal -w 1 1 1 appeals -w 1 1 1 append -w 1 1 1 appl -w 1 1 1 apple -w 1 1 1 appliation -w 1 1 1 applications -w 1 1 1 apps -w 1 1 1 apr -w 1 1 1 arch -w 1 1 1 archive -w 1 1 1 archives -w 1 1 1 array -w 1 1 1 art -w 1 1 1 article -w 1 1 1 articles -w 1 1 1 artwork -w 1 1 1 as -w 1 1 1 ascii -w 1 1 1 asdf -w 1 1 1 ashley -w 1 1 1 asset -w 1 1 1 assets -w 1 1 1 atom -w 1 1 1 attach -w 1 1 1 attachment -w 1 1 1 attachments -w 1 1 1 attachs -w 1 1 1 attic -w 1 1 1 auction -w 1 1 1 audio -w 1 1 1 audit -w 1 1 1 audits -w 1 1 1 auth -w 1 1 1 author -w 1 1 1 authorized_keys -w 1 1 1 authors -w 1 1 1 auto -w 1 1 1 automatic -w 1 1 1 automation -w 1 1 1 avatar -w 1 1 1 avatars -w 1 1 1 award -w 1 1 1 awards -w 1 1 1 awl -w 1 1 1 awstats -w 1 1 1 axis -w 1 1 1 axis-admin -w 1 1 1 axis2 -w 1 1 1 axis2-admin -w 1 1 1 b -w 1 1 1 b2b -w 1 1 1 b2c -w 1 1 1 back -w 1 1 1 backdoor -w 1 1 1 backend -w 1 1 1 backup -w 1 1 1 backups -w 1 1 1 balance -w 1 1 1 balances -w 1 1 1 bandwidth -w 1 1 1 bank -w 1 1 1 banking -w 1 1 1 banks -w 1 1 1 banner -w 1 1 1 banners -w 1 1 1 bar -w 1 1 1 base -w 1 1 1 bash -w 1 1 1 basic -w 1 1 1 basket -w 1 1 1 baskets -w 1 1 1 batch -w 1 1 1 baz -w 1 1 1 bb -w 1 1 1 bb-hist -w 1 1 1 bb-histlog -w 1 1 1 bboard -w 1 1 1 bbs -w 1 1 1 bean -w 1 1 1 beans -w 1 1 1 beehive -w 1 1 1 benefits -w 1 1 1 beta -w 1 1 1 bfc -w 1 1 1 big -w 1 1 1 bigip -w 1 1 1 bill -w 1 1 1 billing -w 1 1 1 bin -w 1 1 1 binaries -w 1 1 1 binary -w 1 1 1 bins -w 1 1 1 bio -w 1 1 1 bios -w 1 1 1 biz -w 1 1 1 bkup -w 1 1 1 blah -w 1 1 1 blank -w 1 1 1 blog -w 1 1 1 blogger -w 1 1 1 bloggers -w 1 1 1 blogs -w 1 1 1 blogspot -w 1 1 1 board -w 1 1 1 boards -w 1 1 1 bob -w 1 1 1 bofh -w 1 1 1 book -w 1 1 1 books -w 1 1 1 boot -w 1 1 1 bottom -w 1 1 1 broken -w 1 1 1 broker -w 1 1 1 browse -w 1 1 1 browser -w 1 1 1 bs -w 1 1 1 bsd -w 1 1 1 bug -w 1 1 1 bugs -w 1 1 1 build -w 1 1 1 builder -w 1 1 1 buildr -w 1 1 1 bulk -w 1 1 1 bullet -w 1 1 1 business -w 1 1 1 button -w 1 1 1 buttons -w 1 1 1 buy -w 1 1 1 buynow -w 1 1 1 bypass -w 1 1 1 bz2 -w 1 1 1 c -w 1 1 1 ca -w 1 1 1 cache -w 1 1 1 cal -w 1 1 1 calendar +#ro +es 1 1 1 asmx +es 1 1 1 asp +es 1 1 1 aspx +eg 1 1 1 bak +es 1 1 1 bat +es 1 1 1 cc +eg 1 1 1 cfg +es 1 1 1 cfm +es 1 1 1 cgi +es 1 1 1 class +eg 1 1 1 cnf +eg 1 1 1 conf +eg 1 1 1 config +eg 1 1 1 core +es 1 1 1 cpp +es 1 1 1 csproj +eg 1 1 1 csv +eg 1 1 1 dat +eg 1 1 1 db +es 1 1 1 dll +eg 1 1 1 err +eg 1 1 1 error +es 1 1 1 exe +es 1 1 1 fcgi +eg 1 1 1 gz +es 1 1 1 htm +es 1 1 1 html +es 1 1 1 inc +es 1 1 1 ini +es 1 1 1 jar +es 1 1 1 java +es 1 1 1 jhtml +eg 1 1 1 js +es 1 1 1 jsf +es 1 1 1 jsp +eg 1 1 1 key +eg 1 1 1 log +eg 1 1 1 mdb +es 1 1 1 nsf +eg 1 1 1 old +eg 1 1 1 ora +eg 1 1 1 orig +eg 1 1 1 out +eg 1 1 1 part +es 1 1 1 php +es 1 1 1 php3 +es 1 1 1 phtml +es 1 1 1 pl +es 1 1 1 pm +es 1 1 1 py +es 1 1 1 rb +es 1 1 1 rss +es 1 1 1 sh +es 1 1 1 shtml +es 1 1 1 sql +eg 1 1 1 stackdump +es 1 1 1 svn-base +eg 1 1 1 tar.gz +eg 1 1 1 temp +eg 1 1 1 test +eg 1 1 1 tgz +eg 1 1 1 tmp +eg 1 1 1 txt +es 1 1 1 vb +es 1 1 1 vbs +es 1 1 1 ws +es 1 1 1 xls +eg 1 1 1 xml +es 1 1 1 xsl +es 1 1 1 xslt +eg 1 1 1 zip +ws 1 1 1 .bash_history +ws 1 1 1 .bashrc +ws 1 1 1 .cvsignore +ws 1 1 1 .history +ws 1 1 1 .htaccess +ws 1 1 1 .htpasswd +ws 1 1 1 .passwd +ws 1 1 1 .perf +ws 1 1 1 .ssh +ws 1 1 1 .svn +ws 1 1 1 .web +wg 1 1 1 0 +wg 1 1 1 00 +wg 1 1 1 01 +wg 1 1 1 02 +wg 1 1 1 03 +wg 1 1 1 04 +wg 1 1 1 05 +wg 1 1 1 06 +wg 1 1 1 07 +wg 1 1 1 08 +wg 1 1 1 09 +wg 1 1 1 1 +wg 1 1 1 10 +wg 1 1 1 100 +wg 1 1 1 1000 +wg 1 1 1 1001 +wg 1 1 1 101 +wg 1 1 1 11 +wg 1 1 1 12 +wg 1 1 1 13 +wg 1 1 1 14 +wg 1 1 1 15 +wg 1 1 1 1990 +wg 1 1 1 1991 +wg 1 1 1 1992 +wg 1 1 1 1993 +wg 1 1 1 1994 +wg 1 1 1 1995 +wg 1 1 1 1996 +wg 1 1 1 1997 +wg 1 1 1 1998 +wg 1 1 1 1999 +wg 1 1 1 2 +wg 1 1 1 20 +wg 1 1 1 200 +wg 1 1 1 2000 +wg 1 1 1 2001 +wg 1 1 1 2002 +wg 1 1 1 2003 +wg 1 1 1 2004 +wg 1 1 1 2005 +wg 1 1 1 2006 +wg 1 1 1 2007 +wg 1 1 1 2008 +wg 1 1 1 2009 +wg 1 1 1 2010 +wg 1 1 1 2011 +wg 1 1 1 2012 +wg 1 1 1 2013 +wg 1 1 1 2014 +wg 1 1 1 21 +wg 1 1 1 22 +wg 1 1 1 23 +wg 1 1 1 24 +wg 1 1 1 25 +wg 1 1 1 2g +wg 1 1 1 3 +wg 1 1 1 300 +wg 1 1 1 3g +wg 1 1 1 4 +wg 1 1 1 42 +wg 1 1 1 5 +wg 1 1 1 50 +wg 1 1 1 500 +wg 1 1 1 51 +wg 1 1 1 6 +wg 1 1 1 7 +wg 1 1 1 7z +wg 1 1 1 8 +wg 1 1 1 9 +wg 1 1 1 ADM +wg 1 1 1 ADMIN +wg 1 1 1 Admin +wg 1 1 1 AdminService +wg 1 1 1 AggreSpy +wg 1 1 1 AppsLocalLogin +wg 1 1 1 AppsLogin +ws 1 1 1 BUILD +wg 1 1 1 CMS +wg 1 1 1 CVS +wg 1 1 1 DB +wg 1 1 1 DMSDump +ws 1 1 1 Documents and Settings +ws 1 1 1 Entries +wg 1 1 1 FCKeditor +wg 1 1 1 JMXSoapAdapter +wg 1 1 1 LICENSE +ws 1 1 1 MANIFEST.MF +ws 1 1 1 META-INF +ws 1 1 1 Makefile +wg 1 1 1 OA +wg 1 1 1 OAErrorDetailPage +ws 1 1 1 OA_HTML +ws 1 1 1 Program Files +wg 1 1 1 README +ws 1 1 1 Rakefile +wg 1 1 1 Readme +ws 1 1 1 Recycled +wg 1 1 1 Root +ws 1 1 1 SERVER-INF +wg 1 1 1 SOAPMonitor +wg 1 1 1 SQL +wg 1 1 1 SUNWmc +wg 1 1 1 SiteScope +wg 1 1 1 SiteServer +wg 1 1 1 Spy +wg 1 1 1 TEMP +wg 1 1 1 TMP +wg 1 1 1 TODO +ws 1 1 1 Thumbs.db +ws 1 1 1 WEB-INF +ws 1 1 1 WS_FTP +wg 1 1 1 XXX +ws 1 1 1 _ +ws 1 1 1 _adm +ws 1 1 1 _admin +ws 1 1 1 _common +ws 1 1 1 _conf +ws 1 1 1 _files +ws 1 1 1 _include +ws 1 1 1 _js +ws 1 1 1 _mem_bin +ws 1 1 1 _old +ws 1 1 1 _pages +ws 1 1 1 _private +ws 1 1 1 _res +ws 1 1 1 _source +ws 1 1 1 _src +ws 1 1 1 _test +ws 1 1 1 _vti_bin +ws 1 1 1 _vti_cnf +ws 1 1 1 _vti_pvt +ws 1 1 1 _vti_txt +ws 1 1 1 _www +wg 1 1 1 a +wg 1 1 1 aa +wg 1 1 1 aaa +wg 1 1 1 abc +wg 1 1 1 abc123 +wg 1 1 1 abcd +wg 1 1 1 abcd1234 +wg 1 1 1 about +wg 1 1 1 access +ws 1 1 1 access-log +ws 1 1 1 access-log.1 +ws 1 1 1 access.1 +ws 1 1 1 access_log +ws 1 1 1 access_log.1 +wg 1 1 1 accessibility +wg 1 1 1 account +wg 1 1 1 accounting +wg 1 1 1 accounts +wg 1 1 1 action +wg 1 1 1 actions +wg 1 1 1 active +wg 1 1 1 activex +wg 1 1 1 ad +wg 1 1 1 adclick +wg 1 1 1 add +wg 1 1 1 addpost +wg 1 1 1 addressbook +wg 1 1 1 adm +wg 1 1 1 admin +wg 1 1 1 admin-console +ws 1 1 1 admin_ +wg 1 1 1 admins +wg 1 1 1 adobe +wg 1 1 1 adodb +wg 1 1 1 ads +wg 1 1 1 adv +wg 1 1 1 advanced +wg 1 1 1 advertise +wg 1 1 1 advertising +wg 1 1 1 affiliate +wg 1 1 1 affiliates +wg 1 1 1 agenda +wg 1 1 1 agent +wg 1 1 1 agents +wg 1 1 1 ajax +wg 1 1 1 akamai +wg 1 1 1 album +wg 1 1 1 albums +wg 1 1 1 alcatel +wg 1 1 1 alert +wg 1 1 1 alerts +wg 1 1 1 alias +wg 1 1 1 aliases +wg 1 1 1 all +ws 1 1 1 all-wcprops +wg 1 1 1 alpha +wg 1 1 1 alumni +wg 1 1 1 amanda +wg 1 1 1 amazon +wg 1 1 1 analog +wg 1 1 1 android +wg 1 1 1 announcement +wg 1 1 1 announcements +wg 1 1 1 annual +wg 1 1 1 anon +wg 1 1 1 anonymous +wg 1 1 1 ansi +wg 1 1 1 apac +wg 1 1 1 apache +wg 1 1 1 apexec +wg 1 1 1 api +wg 1 1 1 apis +wg 1 1 1 app +wg 1 1 1 appeal +wg 1 1 1 appeals +wg 1 1 1 append +wg 1 1 1 appl +wg 1 1 1 apple +wg 1 1 1 appliation +wg 1 1 1 applications +wg 1 1 1 apps +wg 1 1 1 apr +wg 1 1 1 arch +wg 1 1 1 archive +wg 1 1 1 archives +wg 1 1 1 array +wg 1 1 1 art +wg 1 1 1 article +wg 1 1 1 articles +wg 1 1 1 artwork +ws 1 1 1 as +wg 1 1 1 ascii +wg 1 1 1 asdf +wg 1 1 1 ashley +wg 1 1 1 asset +wg 1 1 1 assets +wg 1 1 1 atom +wg 1 1 1 attach +wg 1 1 1 attachment +wg 1 1 1 attachments +wg 1 1 1 attachs +wg 1 1 1 attic +wg 1 1 1 auction +wg 1 1 1 audio +wg 1 1 1 audit +wg 1 1 1 audits +wg 1 1 1 auth +wg 1 1 1 author +ws 1 1 1 authorized_keys +wg 1 1 1 authors +wg 1 1 1 auto +wg 1 1 1 automatic +wg 1 1 1 automation +wg 1 1 1 avatar +wg 1 1 1 avatars +wg 1 1 1 award +wg 1 1 1 awards +wg 1 1 1 awl +wg 1 1 1 awstats +ws 1 1 1 axis +ws 1 1 1 axis-admin +ws 1 1 1 axis2 +ws 1 1 1 axis2-admin +wg 1 1 1 b +wg 1 1 1 b2b +wg 1 1 1 b2c +wg 1 1 1 back +wg 1 1 1 backdoor +wg 1 1 1 backend +wg 1 1 1 backup +wg 1 1 1 backups +wg 1 1 1 balance +wg 1 1 1 balances +wg 1 1 1 bandwidth +wg 1 1 1 bank +wg 1 1 1 banking +wg 1 1 1 banks +wg 1 1 1 banner +wg 1 1 1 banners +wg 1 1 1 bar +wg 1 1 1 base +wg 1 1 1 bash +wg 1 1 1 basic +wg 1 1 1 basket +wg 1 1 1 baskets +wg 1 1 1 batch +wg 1 1 1 baz +wg 1 1 1 bb +ws 1 1 1 bb-hist +ws 1 1 1 bb-histlog +wg 1 1 1 bboard +wg 1 1 1 bbs +wg 1 1 1 bean +wg 1 1 1 beans +wg 1 1 1 beehive +wg 1 1 1 benefits +wg 1 1 1 beta +wg 1 1 1 bfc +wg 1 1 1 big +wg 1 1 1 bigip +wg 1 1 1 bill +wg 1 1 1 billing +wg 1 1 1 bin +wg 1 1 1 binaries +wg 1 1 1 binary +wg 1 1 1 bins +wg 1 1 1 bio +wg 1 1 1 bios +wg 1 1 1 biz +wg 1 1 1 bkup +wg 1 1 1 blah +wg 1 1 1 blank +wg 1 1 1 blog +wg 1 1 1 blogger +wg 1 1 1 bloggers +wg 1 1 1 blogs +wg 1 1 1 blogspot +wg 1 1 1 board +wg 1 1 1 boards +wg 1 1 1 bob +wg 1 1 1 bofh +wg 1 1 1 book +wg 1 1 1 books +wg 1 1 1 boot +wg 1 1 1 bottom +wg 1 1 1 broken +wg 1 1 1 broker +wg 1 1 1 browse +wg 1 1 1 browser +wg 1 1 1 bs +wg 1 1 1 bsd +wg 1 1 1 bug +wg 1 1 1 bugs +wg 1 1 1 build +wg 1 1 1 builder +wg 1 1 1 buildr +wg 1 1 1 bulk +wg 1 1 1 bullet +wg 1 1 1 business +wg 1 1 1 button +wg 1 1 1 buttons +wg 1 1 1 buy +wg 1 1 1 buynow +wg 1 1 1 bypass +wg 1 1 1 bz2 +ws 1 1 1 c +wg 1 1 1 ca +wg 1 1 1 cache +wg 1 1 1 cal +wg 1 1 1 calendar w 1 1 1 call w 1 1 1 callback w 1 1 1 callee @@ -431,1741 +432,1741 @@ w 1 1 1 callin w 1 1 1 calling w 1 1 1 callout -w 1 1 1 camel -w 1 1 1 car -w 1 1 1 card -w 1 1 1 cards -w 1 1 1 career -w 1 1 1 careers -w 1 1 1 cars -w 1 1 1 cart -w 1 1 1 carts -w 1 1 1 cat -w 1 1 1 catalog -w 1 1 1 catalogs -w 1 1 1 catalyst -w 1 1 1 categories -w 1 1 1 category -w 1 1 1 catinfo -w 1 1 1 cats -w 1 1 1 ccbill -w 1 1 1 cd -w 1 1 1 cert -w 1 1 1 certificate -w 1 1 1 certificates -w 1 1 1 certified -w 1 1 1 certs -w 1 1 1 cf -w 1 1 1 cfcache -w 1 1 1 cfdocs -w 1 1 1 cfide -w 1 1 1 cfusion -w 1 1 1 cgi-bin -w 1 1 1 cgi-bin2 -w 1 1 1 cgi-home -w 1 1 1 cgi-local -w 1 1 1 cgi-pub -w 1 1 1 cgi-script -w 1 1 1 cgi-shl -w 1 1 1 cgi-sys -w 1 1 1 cgi-web -w 1 1 1 cgi-win -w 1 1 1 cgibin -w 1 1 1 cgiwrap -w 1 1 1 cgm-web -w 1 1 1 change -w 1 1 1 changed -w 1 1 1 changes -w 1 1 1 charge -w 1 1 1 charges -w 1 1 1 chat -w 1 1 1 chats -w 1 1 1 check -w 1 1 1 checking -w 1 1 1 checkout -w 1 1 1 checkpoint -w 1 1 1 checks -w 1 1 1 child -w 1 1 1 children -w 1 1 1 chris -w 1 1 1 chrome -w 1 1 1 cisco -w 1 1 1 cisweb -w 1 1 1 citrix -w 1 1 1 cl -w 1 1 1 claim -w 1 1 1 claims -w 1 1 1 classes -w 1 1 1 classified -w 1 1 1 classifieds -w 1 1 1 clear -w 1 1 1 click -w 1 1 1 clicks -w 1 1 1 client -w 1 1 1 clientaccesspolicy -w 1 1 1 clients -w 1 1 1 clk -w 1 1 1 clock -w 1 1 1 close -w 1 1 1 closed -w 1 1 1 closing -w 1 1 1 club -w 1 1 1 cluster -w 1 1 1 clusters -w 1 1 1 cmd -w 1 1 1 cms -w 1 1 1 cnt -w 1 1 1 cocoon -w 1 1 1 code -w 1 1 1 codec -w 1 1 1 codecs -w 1 1 1 codes -w 1 1 1 cognos -w 1 1 1 coldfusion -w 1 1 1 columns -w 1 1 1 com -w 1 1 1 comment -w 1 1 1 comments -w 1 1 1 commerce -w 1 1 1 commercial -w 1 1 1 common -w 1 1 1 communicator -w 1 1 1 community -w 1 1 1 compact -w 1 1 1 company -w 1 1 1 compat -w 1 1 1 complaint -w 1 1 1 complaints -w 1 1 1 compliance -w 1 1 1 component -w 1 1 1 components -w 1 1 1 compress -w 1 1 1 compressed -w 1 1 1 computer -w 1 1 1 computers -w 1 1 1 computing -w 1 1 1 conference -w 1 1 1 conferences -w 1 1 1 configs -w 1 1 1 console -w 1 1 1 consumer -w 1 1 1 contact -w 1 1 1 contacts -w 1 1 1 content -w 1 1 1 contents -w 1 1 1 contest -w 1 1 1 contract -w 1 1 1 contracts -w 1 1 1 control -w 1 1 1 controller -w 1 1 1 controlpanel -w 1 1 1 cookie -w 1 1 1 cookies -w 1 1 1 copies -w 1 1 1 copy -w 1 1 1 copyright -w 1 1 1 corp -w 1 1 1 corpo -w 1 1 1 corporate -w 1 1 1 corrections -w 1 1 1 count -w 1 1 1 counter -w 1 1 1 counters -w 1 1 1 counts -w 1 1 1 course -w 1 1 1 courses -w 1 1 1 cover -w 1 1 1 cpadmin -w 1 1 1 cpanel -w 1 1 1 cr -w 1 1 1 crack -w 1 1 1 crash -w 1 1 1 crashes -w 1 1 1 create -w 1 1 1 creator -w 1 1 1 credit -w 1 1 1 credits -w 1 1 1 crm -w 1 1 1 cron -w 1 1 1 crons -w 1 1 1 crontab -w 1 1 1 crontabs -w 1 1 1 crossdomain -w 1 1 1 crypt -w 1 1 1 crypto -w 1 1 1 cs -w 1 1 1 css -w 1 1 1 current -w 1 1 1 custom -w 1 1 1 custom-log -w 1 1 1 custom_log -w 1 1 1 customer -w 1 1 1 customers -w 1 1 1 cute -w 1 1 1 cv -w 1 1 1 cxf -w 1 1 1 czcmdcvt -w 1 1 1 d -w 1 1 1 daemon -w 1 1 1 daily -w 1 1 1 dan -w 1 1 1 dana-na -w 1 1 1 data -w 1 1 1 database -w 1 1 1 databases -w 1 1 1 date -w 1 1 1 day -w 1 1 1 db_connect -w 1 1 1 dba -w 1 1 1 dbase -w 1 1 1 dblclk -w 1 1 1 dbman -w 1 1 1 dbmodules -w 1 1 1 dbutil -w 1 1 1 dc -w 1 1 1 dcforum -w 1 1 1 dclk -w 1 1 1 de -w 1 1 1 dealer -w 1 1 1 debug -w 1 1 1 decl -w 1 1 1 declaration -w 1 1 1 declarations -w 1 1 1 decode -w 1 1 1 decoder -w 1 1 1 decrypt -w 1 1 1 decrypted -w 1 1 1 decryption -w 1 1 1 def -w 1 1 1 default -w 1 1 1 defaults -w 1 1 1 definition -w 1 1 1 definitions -w 1 1 1 del -w 1 1 1 delete -w 1 1 1 deleted -w 1 1 1 demo -w 1 1 1 demos -w 1 1 1 denied -w 1 1 1 deny -w 1 1 1 design -w 1 1 1 desktop -w 1 1 1 desktops -w 1 1 1 detail -w 1 1 1 details -w 1 1 1 dev -w 1 1 1 devel -w 1 1 1 developer -w 1 1 1 developers -w 1 1 1 development -w 1 1 1 device -w 1 1 1 devices -w 1 1 1 devs -w 1 1 1 df -w 1 1 1 dialog -w 1 1 1 dialogs -w 1 1 1 diff -w 1 1 1 diffs -w 1 1 1 digest -w 1 1 1 digg -w 1 1 1 dir -w 1 1 1 dir-prop-base -w 1 1 1 directories -w 1 1 1 directory -w 1 1 1 dirs -w 1 1 1 disabled -w 1 1 1 disclaimer -w 1 1 1 display -w 1 1 1 django -w 1 1 1 dl -w 1 1 1 dm -w 1 1 1 dm-config -w 1 1 1 dms -w 1 1 1 dms0 -w 1 1 1 dns -w 1 1 1 do -w 1 1 1 doc -w 1 1 1 docebo -w 1 1 1 dock -w 1 1 1 docroot -w 1 1 1 docs -w 1 1 1 document -w 1 1 1 documentation -w 1 1 1 documents -w 1 1 1 domain -w 1 1 1 domains -w 1 1 1 donate -w 1 1 1 done -w 1 1 1 doubleclick -w 1 1 1 down -w 1 1 1 download -w 1 1 1 downloader -w 1 1 1 downloads -w 1 1 1 drop -w 1 1 1 dropped -w 1 1 1 drupal -w 1 1 1 dummy -w 1 1 1 dump -w 1 1 1 dumps -w 1 1 1 dvd -w 1 1 1 dwr -w 1 1 1 dyn -w 1 1 1 dynamic -w 1 1 1 e -w 1 1 1 e2fs -w 1 1 1 ear -w 1 1 1 ecommerce -w 1 1 1 edge -w 1 1 1 edit -w 1 1 1 editor -w 1 1 1 edits -w 1 1 1 edp -w 1 1 1 edu -w 1 1 1 education -w 1 1 1 ee -w 1 1 1 effort -w 1 1 1 efforts -w 1 1 1 egress -w 1 1 1 ejb -w 1 1 1 element -w 1 1 1 elements -w 1 1 1 em -w 1 1 1 email -w 1 1 1 emails -w 1 1 1 embed -w 1 1 1 embedded -w 1 1 1 emea -w 1 1 1 employees -w 1 1 1 employment -w 1 1 1 empty -w 1 1 1 emu -w 1 1 1 emulator -w 1 1 1 en -w 1 1 1 en_US -w 1 1 1 enc -w 1 1 1 encode -w 1 1 1 encoder -w 1 1 1 encrypt -w 1 1 1 encrypted -w 1 1 1 encyption -w 1 1 1 eng -w 1 1 1 engine -w 1 1 1 english -w 1 1 1 enterprise -w 1 1 1 entertainment -w 1 1 1 entries -w 1 1 1 entry -w 1 1 1 env -w 1 1 1 environ -w 1 1 1 environment -w 1 1 1 ep -w 1 1 1 eric -w 1 1 1 error-log -w 1 1 1 error_log -w 1 1 1 errors -w 1 1 1 es -w 1 1 1 esale -w 1 1 1 esales -w 1 1 1 etc -w 1 1 1 eu -w 1 1 1 europe -w 1 1 1 event -w 1 1 1 events -w 1 1 1 evil -w 1 1 1 evt -w 1 1 1 ews -w 1 1 1 ex -w 1 1 1 example -w 1 1 1 examples -w 1 1 1 excalibur -w 1 1 1 exchange -w 1 1 1 exec -w 1 1 1 explorer -w 1 1 1 export -w 1 1 1 ext -w 1 1 1 ext2 -w 1 1 1 extern -w 1 1 1 external -w 1 1 1 extras -w 1 1 1 ezshopper -w 1 1 1 f -w 1 1 1 fabric -w 1 1 1 face -w 1 1 1 facebook -w 1 1 1 faces -w 1 1 1 faculty -w 1 1 1 fail -w 1 1 1 failure -w 1 1 1 fake -w 1 1 1 family -w 1 1 1 faq -w 1 1 1 faqs -w 1 1 1 favorite -w 1 1 1 favorites -w 1 1 1 fb -w 1 1 1 fbook -w 1 1 1 fc -w 1 1 1 fcgi-bin -w 1 1 1 feature -w 1 1 1 features -w 1 1 1 feed -w 1 1 1 feedback -w 1 1 1 feeds -w 1 1 1 felix -w 1 1 1 fetch -w 1 1 1 field -w 1 1 1 fields -w 1 1 1 file -w 1 1 1 fileadmin -w 1 1 1 filelist -w 1 1 1 files -w 1 1 1 filez -w 1 1 1 finance -w 1 1 1 financial -w 1 1 1 find -w 1 1 1 finger -w 1 1 1 firefox -w 1 1 1 firewall -w 1 1 1 firmware -w 1 1 1 first -w 1 1 1 fixed -w 1 1 1 flags -w 1 1 1 flash -w 1 1 1 flow -w 1 1 1 flows -w 1 1 1 flv -w 1 1 1 fn -w 1 1 1 folder -w 1 1 1 folders -w 1 1 1 font -w 1 1 1 fonts -w 1 1 1 foo -w 1 1 1 footer -w 1 1 1 footers -w 1 1 1 form -w 1 1 1 format -w 1 1 1 formatting -w 1 1 1 formmail -w 1 1 1 forms -w 1 1 1 forrest -w 1 1 1 fortune -w 1 1 1 forum -w 1 1 1 forum1 -w 1 1 1 forum2 -w 1 1 1 forumdisplay -w 1 1 1 forums -w 1 1 1 forward -w 1 1 1 foto -w 1 1 1 foundation -w 1 1 1 fr -w 1 1 1 frame -w 1 1 1 frames -w 1 1 1 framework -w 1 1 1 free -w 1 1 1 freebsd -w 1 1 1 friend -w 1 1 1 friends -w 1 1 1 frob -w 1 1 1 frontend -w 1 1 1 fs -w 1 1 1 ftp -w 1 1 1 fuck -w 1 1 1 fuckoff -w 1 1 1 fuckyou -w 1 1 1 full -w 1 1 1 fun -w 1 1 1 func -w 1 1 1 funcs -w 1 1 1 function -w 1 1 1 functions -w 1 1 1 fund -w 1 1 1 funding -w 1 1 1 funds -w 1 1 1 fusion -w 1 1 1 fw -w 1 1 1 g -w 1 1 1 gadget -w 1 1 1 gadgets -w 1 1 1 galleries -w 1 1 1 gallery -w 1 1 1 game -w 1 1 1 games -w 1 1 1 ganglia -w 1 1 1 garbage -w 1 1 1 gateway -w 1 1 1 gb -w 1 1 1 geeklog -w 1 1 1 general -w 1 1 1 geronimo -w 1 1 1 get -w 1 1 1 getaccess -w 1 1 1 getjobid -w 1 1 1 gfx -w 1 1 1 gid -w 1 1 1 gif -w 1 1 1 git -w 1 1 1 gitweb -w 1 1 1 glimpse -w 1 1 1 global -w 1 1 1 globals -w 1 1 1 glossary -w 1 1 1 go -w 1 1 1 goaway -w 1 1 1 google -w 1 1 1 government -w 1 1 1 gprs -w 1 1 1 grant -w 1 1 1 grants -w 1 1 1 graphics -w 1 1 1 group -w 1 1 1 groupcp -w 1 1 1 groups -w 1 1 1 gsm -w 1 1 1 guest -w 1 1 1 guestbook -w 1 1 1 guests -w 1 1 1 guide -w 1 1 1 guides -w 1 1 1 gump -w 1 1 1 gwt -w 1 1 1 h -w 1 1 1 hack -w 1 1 1 hacker -w 1 1 1 hacking -w 1 1 1 hackme -w 1 1 1 hadoop -w 1 1 1 hardcore -w 1 1 1 hardware -w 1 1 1 harmony -w 1 1 1 head -w 1 1 1 header -w 1 1 1 headers -w 1 1 1 health -w 1 1 1 hello -w 1 1 1 help -w 1 1 1 helper -w 1 1 1 helpers -w 1 1 1 hi -w 1 1 1 hidden -w 1 1 1 hide -w 1 1 1 high -w 1 1 1 hipaa -w 1 1 1 hire -w 1 1 1 history -w 1 1 1 hit -w 1 1 1 hits -w 1 1 1 hole -w 1 1 1 home -w 1 1 1 homepage -w 1 1 1 hop -w 1 1 1 horde -w 1 1 1 hosting -w 1 1 1 hosts -w 1 1 1 hour -w 1 1 1 hourly -w 1 1 1 howto -w 1 1 1 hp -w 1 1 1 hr -w 1 1 1 hta -w 1 1 1 htbin -w 1 1 1 htdoc -w 1 1 1 htdocs -w 1 1 1 htpasswd -w 1 1 1 http -w 1 1 1 httpd -w 1 1 1 https -w 1 1 1 httpuser -w 1 1 1 hu -w 1 1 1 hyper -w 1 1 1 i -w 1 1 1 ia -w 1 1 1 ibm -w 1 1 1 icat -w 1 1 1 ico -w 1 1 1 icon -w 1 1 1 icons -w 1 1 1 id -w 1 1 1 idea -w 1 1 1 ideas -w 1 1 1 ids -w 1 1 1 ie -w 1 1 1 iframe -w 1 1 1 ig -w 1 1 1 ignore -w 1 1 1 iisadmin -w 1 1 1 iisadmpwd -w 1 1 1 iissamples -w 1 1 1 image -w 1 1 1 imagefolio -w 1 1 1 images -w 1 1 1 img -w 1 1 1 imgs -w 1 1 1 imp -w 1 1 1 import -w 1 1 1 important -w 1 1 1 in -w 1 1 1 inbound -w 1 1 1 incl -w 1 1 1 include -w 1 1 1 includes -w 1 1 1 incoming -w 1 1 1 incubator -w 1 1 1 index -w 1 1 1 index1 -w 1 1 1 index2 -w 1 1 1 index_1 -w 1 1 1 index_2 -w 1 1 1 inetpub -w 1 1 1 inetsrv -w 1 1 1 inf -w 1 1 1 info -w 1 1 1 information -w 1 1 1 ingress -w 1 1 1 init -w 1 1 1 inline -w 1 1 1 input -w 1 1 1 inquire -w 1 1 1 inquiries -w 1 1 1 inquiry -w 1 1 1 insert -w 1 1 1 install -w 1 1 1 int -w 1 1 1 intel -w 1 1 1 intelligence -w 1 1 1 inter -w 1 1 1 interim -w 1 1 1 intermediate -w 1 1 1 internal -w 1 1 1 international -w 1 1 1 internet -w 1 1 1 intl -w 1 1 1 intra -w 1 1 1 intranet -w 1 1 1 intro -w 1 1 1 ip -w 1 1 1 ipc -w 1 1 1 iphone -w 1 1 1 ips -w 1 1 1 irc -w 1 1 1 is -w 1 1 1 isapi -w 1 1 1 iso -w 1 1 1 issues -w 1 1 1 it -w 1 1 1 item -w 1 1 1 items -w 1 1 1 j -w 1 1 1 j2ee -w 1 1 1 j2me -w 1 1 1 jacob -w 1 1 1 jakarta -w 1 1 1 java-plugin -w 1 1 1 javadoc -w 1 1 1 javascript -w 1 1 1 javax -w 1 1 1 jboss -w 1 1 1 jbossas -w 1 1 1 jbossws -w 1 1 1 jdbc -w 1 1 1 jennifer -w 1 1 1 jessica -w 1 1 1 jigsaw -w 1 1 1 jira -w 1 1 1 jj -w 1 1 1 jmx-console -w 1 1 1 job -w 1 1 1 jobs -w 1 1 1 joe -w 1 1 1 john -w 1 1 1 join -w 1 1 1 joomla -w 1 1 1 journal -w 1 1 1 jp -w 1 1 1 jpa -w 1 1 1 jpg -w 1 1 1 jre -w 1 1 1 jrun -w 1 1 1 json -w 1 1 1 jsso -w 1 1 1 jsx -w 1 1 1 juniper -w 1 1 1 junk -w 1 1 1 jvm -w 1 1 1 k -w 1 1 1 kboard -w 1 1 1 keep -w 1 1 1 kernel -w 1 1 1 keygen -w 1 1 1 keys -w 1 1 1 kids -w 1 1 1 kill -w 1 1 1 known_hosts -w 1 1 1 l -w 1 1 1 la -w 1 1 1 labs -w 1 1 1 lang -w 1 1 1 large -w 1 1 1 law -w 1 1 1 layout -w 1 1 1 layouts -w 1 1 1 ldap -w 1 1 1 leader -w 1 1 1 leaders -w 1 1 1 left -w 1 1 1 legacy -w 1 1 1 legal -w 1 1 1 lenya -w 1 1 1 letters -w 1 1 1 level -w 1 1 1 lg -w 1 1 1 lib -w 1 1 1 library -w 1 1 1 libs -w 1 1 1 license -w 1 1 1 licenses -w 1 1 1 limit -w 1 1 1 line -w 1 1 1 link -w 1 1 1 links -w 1 1 1 linux -w 1 1 1 list -w 1 1 1 listinfo -w 1 1 1 lists -w 1 1 1 live -w 1 1 1 lo -w 1 1 1 loader -w 1 1 1 loading -w 1 1 1 loc -w 1 1 1 local -w 1 1 1 location -w 1 1 1 lock -w 1 1 1 locked -w 1 1 1 log4j -w 1 1 1 log4net -w 1 1 1 logfile -w 1 1 1 logger -w 1 1 1 logging -w 1 1 1 login -w 1 1 1 logins -w 1 1 1 logo -w 1 1 1 logoff -w 1 1 1 logon -w 1 1 1 logos -w 1 1 1 logout -w 1 1 1 logs -w 1 1 1 lost -w 1 1 1 lost+found -w 1 1 1 low -w 1 1 1 ls -w 1 1 1 lst -w 1 1 1 lucene -w 1 1 1 m -w 1 1 1 mac -w 1 1 1 macromedia -w 1 1 1 maestro -w 1 1 1 mail -w 1 1 1 mailer -w 1 1 1 mailing -w 1 1 1 mailman -w 1 1 1 mails -w 1 1 1 main -w 1 1 1 mambo -w 1 1 1 manage -w 1 1 1 managed -w 1 1 1 management -w 1 1 1 manager -w 1 1 1 manifest -w 1 1 1 manual -w 1 1 1 manuals -w 1 1 1 map -w 1 1 1 maps -w 1 1 1 mark -w 1 1 1 marketing -w 1 1 1 master -w 1 1 1 master.passwd -w 1 1 1 match -w 1 1 1 matrix -w 1 1 1 matt -w 1 1 1 maven -w 1 1 1 mbox -w 1 1 1 me -w 1 1 1 media -w 1 1 1 medium -w 1 1 1 mem -w 1 1 1 member -w 1 1 1 members -w 1 1 1 membership -w 1 1 1 memory -w 1 1 1 menu -w 1 1 1 menus -w 1 1 1 message -w 1 1 1 messages -w 1 1 1 messaging -w 1 1 1 meta -w 1 1 1 michael -w 1 1 1 microsoft -w 1 1 1 migrate -w 1 1 1 migrated -w 1 1 1 migration -w 1 1 1 mina -w 1 1 1 mini -w 1 1 1 minute -w 1 1 1 mirror -w 1 1 1 mirrors -w 1 1 1 misc -w 1 1 1 mission -w 1 1 1 mix -w 1 1 1 mlist -w 1 1 1 mms -w 1 1 1 mobi -w 1 1 1 mobile -w 1 1 1 mock -w 1 1 1 mod -w 1 1 1 modify -w 1 1 1 mods -w 1 1 1 module -w 1 1 1 modules -w 1 1 1 mojo -w 1 1 1 money -w 1 1 1 monitor -w 1 1 1 monitoring -w 1 1 1 monitors -w 1 1 1 month -w 1 1 1 monthly -w 1 1 1 more -w 1 1 1 motd -w 1 1 1 move -w 1 1 1 moved -w 1 1 1 movie -w 1 1 1 movies -w 1 1 1 mp -w 1 1 1 mp3 -w 1 1 1 mp3s -w 1 1 1 ms -w 1 1 1 ms-sql -w 1 1 1 msadc -w 1 1 1 msadm -w 1 1 1 msft -w 1 1 1 msg -w 1 1 1 msie -w 1 1 1 msql -w 1 1 1 mssql -w 1 1 1 mta -w 1 1 1 multi -w 1 1 1 multimedia -w 1 1 1 music -w 1 1 1 mx -w 1 1 1 my -w 1 1 1 myadmin -w 1 1 1 myfaces -w 1 1 1 myphpnuke -w 1 1 1 mysql -w 1 1 1 mysqld -w 1 1 1 n -w 1 1 1 nav -w 1 1 1 navigation -w 1 1 1 nc -w 1 1 1 net -w 1 1 1 netbsd -w 1 1 1 netcat -w 1 1 1 nethome -w 1 1 1 nets -w 1 1 1 network -w 1 1 1 networking -w 1 1 1 new -w 1 1 1 news -w 1 1 1 newsletter -w 1 1 1 newsletters -w 1 1 1 newticket -w 1 1 1 next -w 1 1 1 nfs -w 1 1 1 nice -w 1 1 1 nl -w 1 1 1 nobody -w 1 1 1 node -w 1 1 1 none -w 1 1 1 note -w 1 1 1 notes -w 1 1 1 notification -w 1 1 1 notifications -w 1 1 1 notified -w 1 1 1 notifier -w 1 1 1 notify -w 1 1 1 novell -w 1 1 1 ns -w 1 1 1 nude -w 1 1 1 nuke -w 1 1 1 nul -w 1 1 1 null -w 1 1 1 o -w 1 1 1 oa_servlets -w 1 1 1 oauth -w 1 1 1 obdc -w 1 1 1 obsolete -w 1 1 1 obsoleted -w 1 1 1 odbc -w 1 1 1 ode -w 1 1 1 oem -w 1 1 1 ofbiz -w 1 1 1 office -w 1 1 1 offices -w 1 1 1 onbound -w 1 1 1 online -w 1 1 1 op -w 1 1 1 open -w 1 1 1 openbsd -w 1 1 1 opencart -w 1 1 1 opendir -w 1 1 1 openejb -w 1 1 1 openjpa -w 1 1 1 opera -w 1 1 1 operations -w 1 1 1 opinion -w 1 1 1 oprocmgr-status -w 1 1 1 opt -w 1 1 1 option -w 1 1 1 options -w 1 1 1 oracle -w 1 1 1 oracle.xml.xsql.XSQLServlet -w 1 1 1 order -w 1 1 1 ordered -w 1 1 1 orders -w 1 1 1 org -w 1 1 1 osc -w 1 1 1 oscommerce -w 1 1 1 other -w 1 1 1 outcome -w 1 1 1 outgoing -w 1 1 1 outline -w 1 1 1 output -w 1 1 1 outreach -w 1 1 1 overview -w 1 1 1 owa -w 1 1 1 owl -w 1 1 1 ows -w 1 1 1 ows-bin -w 1 1 1 p -w 1 1 1 p2p -w 1 1 1 pack -w 1 1 1 package -w 1 1 1 packaged -w 1 1 1 packages -w 1 1 1 packed -w 1 1 1 page -w 1 1 1 page1 -w 1 1 1 page2 -w 1 1 1 page_1 -w 1 1 1 page_2 -w 1 1 1 pages -w 1 1 1 paid -w 1 1 1 panel -w 1 1 1 paper -w 1 1 1 papers -w 1 1 1 parse -w 1 1 1 partner -w 1 1 1 partners -w 1 1 1 party -w 1 1 1 pass -w 1 1 1 passive -w 1 1 1 passwd -w 1 1 1 password -w 1 1 1 passwords -w 1 1 1 past -w 1 1 1 patch -w 1 1 1 patches -w 1 1 1 pay -w 1 1 1 payment -w 1 1 1 payments -w 1 1 1 paypal -w 1 1 1 pbo -w 1 1 1 pc -w 1 1 1 pci -w 1 1 1 pda -w 1 1 1 pdf -w 1 1 1 pdfs -w 1 1 1 pear -w 1 1 1 peek -w 1 1 1 pem -w 1 1 1 pending -w 1 1 1 people -w 1 1 1 perf -w 1 1 1 performance -w 1 1 1 perl -w 1 1 1 personal -w 1 1 1 pfx -w 1 1 1 pg -w 1 1 1 phf -w 1 1 1 phone -w 1 1 1 phones -w 1 1 1 phorum -w 1 1 1 photo -w 1 1 1 photos -w 1 1 1 phpBB -w 1 1 1 phpBB2 -w 1 1 1 phpEventCalendar -w 1 1 1 phpMyAdmin -w 1 1 1 phpbb -w 1 1 1 phpmyadmin -w 1 1 1 phpnuke -w 1 1 1 phps -w 1 1 1 pic -w 1 1 1 pics -w 1 1 1 pictures -w 1 1 1 pii -w 1 1 1 ping -w 1 1 1 pipe -w 1 1 1 pipermail -w 1 1 1 piranha -w 1 1 1 pivot -w 1 1 1 pix -w 1 1 1 pixel -w 1 1 1 pkg -w 1 1 1 pkgs -w 1 1 1 plain -w 1 1 1 play -w 1 1 1 player -w 1 1 1 playing -w 1 1 1 playlist -w 1 1 1 pls -w 1 1 1 plugin -w 1 1 1 plugins -w 1 1 1 png -w 1 1 1 poc -w 1 1 1 poi -w 1 1 1 policies -w 1 1 1 policy -w 1 1 1 politics -w 1 1 1 poll -w 1 1 1 polls -w 1 1 1 pool -w 1 1 1 pop -w 1 1 1 pop3 -w 1 1 1 popup -w 1 1 1 porn -w 1 1 1 port -w 1 1 1 portal -w 1 1 1 portals -w 1 1 1 portfolio -w 1 1 1 pos -w 1 1 1 post -w 1 1 1 posted -w 1 1 1 postgres -w 1 1 1 postgresql -w 1 1 1 postnuke -w 1 1 1 postpaid -w 1 1 1 posts -w 1 1 1 ppt -w 1 1 1 pr -w 1 1 1 pr0n -w 1 1 1 premium -w 1 1 1 prepaid -w 1 1 1 presentation -w 1 1 1 presentations -w 1 1 1 preserve -w 1 1 1 press -w 1 1 1 preview -w 1 1 1 previews -w 1 1 1 previous -w 1 1 1 pricing -w 1 1 1 print -w 1 1 1 printenv -w 1 1 1 printer -w 1 1 1 printers -w 1 1 1 priv -w 1 1 1 privacy -w 1 1 1 private -w 1 1 1 pro -w 1 1 1 problems -w 1 1 1 proc -w 1 1 1 procedures -w 1 1 1 procure -w 1 1 1 procurement -w 1 1 1 prod -w 1 1 1 product -w 1 1 1 product_info -w 1 1 1 production -w 1 1 1 products -w 1 1 1 profile -w 1 1 1 profiles -w 1 1 1 profiling -w 1 1 1 program -w 1 1 1 programming -w 1 1 1 programs -w 1 1 1 progress -w 1 1 1 project -w 1 1 1 projects -w 1 1 1 promo -w 1 1 1 promoted -w 1 1 1 promotion -w 1 1 1 prop -w 1 1 1 prop-base -w 1 1 1 properties -w 1 1 1 property -w 1 1 1 props -w 1 1 1 prot -w 1 1 1 protect -w 1 1 1 protected -w 1 1 1 protection -w 1 1 1 proto -w 1 1 1 proxies -w 1 1 1 proxy -w 1 1 1 prv -w 1 1 1 ps -w 1 1 1 psql -w 1 1 1 pt -w 1 1 1 pub -w 1 1 1 public -w 1 1 1 publication -w 1 1 1 publications -w 1 1 1 pubs -w 1 1 1 pull -w 1 1 1 purchase -w 1 1 1 purchases -w 1 1 1 purchasing -w 1 1 1 push -w 1 1 1 pw -w 1 1 1 pwd -w 1 1 1 python -w 1 1 1 q -w 1 1 1 q1 -w 1 1 1 q2 -w 1 1 1 q3 -w 1 1 1 q4 -w 1 1 1 qotd -w 1 1 1 qpid -w 1 1 1 quarterly -w 1 1 1 queries -w 1 1 1 query -w 1 1 1 queue -w 1 1 1 queues -w 1 1 1 quote -w 1 1 1 quotes -w 1 1 1 r -w 1 1 1 radio -w 1 1 1 random -w 1 1 1 rar -w 1 1 1 rdf -w 1 1 1 read -w 1 1 1 readme -w 1 1 1 real -w 1 1 1 realestate -w 1 1 1 receive -w 1 1 1 received -w 1 1 1 recharge -w 1 1 1 record -w 1 1 1 recorded -w 1 1 1 recorder -w 1 1 1 records -w 1 1 1 recovery -w 1 1 1 recycle -w 1 1 1 recycled -w 1 1 1 redir -w 1 1 1 redirect -w 1 1 1 reference -w 1 1 1 reg -w 1 1 1 register -w 1 1 1 registered -w 1 1 1 registration -w 1 1 1 registrations -w 1 1 1 release -w 1 1 1 releases -w 1 1 1 remind -w 1 1 1 reminder -w 1 1 1 remote -w 1 1 1 removal -w 1 1 1 removals -w 1 1 1 remove -w 1 1 1 removed -w 1 1 1 render -w 1 1 1 rendered -w 1 1 1 rep -w 1 1 1 repl -w 1 1 1 replica -w 1 1 1 replicas -w 1 1 1 replicate -w 1 1 1 replicated -w 1 1 1 replication -w 1 1 1 replicator -w 1 1 1 reply -w 1 1 1 report -w 1 1 1 reporting -w 1 1 1 reports -w 1 1 1 reprints -w 1 1 1 req -w 1 1 1 reqs -w 1 1 1 request -w 1 1 1 requests -w 1 1 1 requisition -w 1 1 1 requisitions -w 1 1 1 res -w 1 1 1 research -w 1 1 1 resin -w 1 1 1 resize -w 1 1 1 resolution -w 1 1 1 resolve -w 1 1 1 resolved -w 1 1 1 resource -w 1 1 1 resources -w 1 1 1 rest -w 1 1 1 restore -w 1 1 1 restored -w 1 1 1 restricted -w 1 1 1 result -w 1 1 1 results -w 1 1 1 retail -w 1 1 1 reverse -w 1 1 1 reversed -w 1 1 1 revert -w 1 1 1 reverted -w 1 1 1 review -w 1 1 1 reviews -w 1 1 1 rhtml -w 1 1 1 right -w 1 1 1 roam -w 1 1 1 roaming -w 1 1 1 robot -w 1 1 1 robots -w 1 1 1 roller -w 1 1 1 room -w 1 1 1 root -w 1 1 1 rpc -w 1 1 1 rsa -w 1 1 1 rtf -w 1 1 1 ru -w 1 1 1 ruby -w 1 1 1 rule -w 1 1 1 rules -w 1 1 1 run -w 1 1 1 rwservlet -w 1 1 1 s -w 1 1 1 sale -w 1 1 1 sales -w 1 1 1 salesforce -w 1 1 1 sam -w 1 1 1 samba -w 1 1 1 saml -w 1 1 1 sample -w 1 1 1 samples -w 1 1 1 san -w 1 1 1 sav -w 1 1 1 save -w 1 1 1 saved -w 1 1 1 saves -w 1 1 1 sbin -w 1 1 1 scan -w 1 1 1 scanned -w 1 1 1 scans -w 1 1 1 sched -w 1 1 1 schedule -w 1 1 1 scheduled -w 1 1 1 scheduling -w 1 1 1 schema -w 1 1 1 science -w 1 1 1 screen -w 1 1 1 screens -w 1 1 1 screenshot -w 1 1 1 screenshots -w 1 1 1 script -w 1 1 1 scriptlet -w 1 1 1 scriptlets -w 1 1 1 scripts -w 1 1 1 sdk -w 1 1 1 se -w 1 1 1 search -w 1 1 1 sec -w 1 1 1 second -w 1 1 1 secret -w 1 1 1 section -w 1 1 1 sections -w 1 1 1 secure -w 1 1 1 secured -w 1 1 1 security -w 1 1 1 seed -w 1 1 1 select -w 1 1 1 sell -w 1 1 1 send -w 1 1 1 sendmail -w 1 1 1 sendto -w 1 1 1 sent -w 1 1 1 serial -w 1 1 1 serv -w 1 1 1 serve -w 1 1 1 server -w 1 1 1 server-info -w 1 1 1 server-status -w 1 1 1 servers -w 1 1 1 service -w 1 1 1 services -w 1 1 1 servlet -w 1 1 1 servlets -w 1 1 1 session -w 1 1 1 sessions -w 1 1 1 setting -w 1 1 1 settings -w 1 1 1 setup -w 1 1 1 sex -w 1 1 1 shadow -w 1 1 1 share -w 1 1 1 shared -w 1 1 1 shares -w 1 1 1 shell -w 1 1 1 ship -w 1 1 1 shipped -w 1 1 1 shipping -w 1 1 1 shockwave -w 1 1 1 shop -w 1 1 1 shopper -w 1 1 1 shopping -w 1 1 1 shops -w 1 1 1 shoutbox -w 1 1 1 show -w 1 1 1 show_post -w 1 1 1 show_thread -w 1 1 1 showcat -w 1 1 1 showenv -w 1 1 1 showjobs -w 1 1 1 showmap -w 1 1 1 showmsg -w 1 1 1 showpost -w 1 1 1 showthread -w 1 1 1 sign -w 1 1 1 signed -w 1 1 1 signer -w 1 1 1 signin -w 1 1 1 signing -w 1 1 1 signoff -w 1 1 1 signon -w 1 1 1 signout -w 1 1 1 signup -w 1 1 1 simple -w 1 1 1 sink -w 1 1 1 site -w 1 1 1 site-map -w 1 1 1 site_map -w 1 1 1 sitemap -w 1 1 1 sites -w 1 1 1 skel -w 1 1 1 skin -w 1 1 1 skins -w 1 1 1 skip -w 1 1 1 sl -w 1 1 1 sling -w 1 1 1 sm -w 1 1 1 small -w 1 1 1 smile -w 1 1 1 smiles -w 1 1 1 sms -w 1 1 1 smtp -w 1 1 1 snoop -w 1 1 1 so -w 1 1 1 soap -w 1 1 1 soaprouter -w 1 1 1 soft -w 1 1 1 software -w 1 1 1 solaris -w 1 1 1 sold -w 1 1 1 solution -w 1 1 1 solutions -w 1 1 1 solve -w 1 1 1 solved -w 1 1 1 source -w 1 1 1 sources -w 1 1 1 sox -w 1 1 1 sp -w 1 1 1 space -w 1 1 1 spacer -w 1 1 1 spam -w 1 1 1 special -w 1 1 1 specials -w 1 1 1 sponsor -w 1 1 1 sponsors -w 1 1 1 spool -w 1 1 1 sport -w 1 1 1 sports -w 1 1 1 sqlnet -w 1 1 1 squirrel -w 1 1 1 squirrelmail -w 1 1 1 src -w 1 1 1 srv -w 1 1 1 ss -w 1 1 1 ssh -w 1 1 1 ssi -w 1 1 1 ssl -w 1 1 1 sslvpn -w 1 1 1 ssn -w 1 1 1 sso -w 1 1 1 staff -w 1 1 1 staging -w 1 1 1 standalone -w 1 1 1 standard -w 1 1 1 standards -w 1 1 1 star -w 1 1 1 start -w 1 1 1 stat -w 1 1 1 statement -w 1 1 1 statements -w 1 1 1 static -w 1 1 1 staticpages -w 1 1 1 statistic -w 1 1 1 statistics -w 1 1 1 stats -w 1 1 1 status -w 1 1 1 stock -w 1 1 1 storage -w 1 1 1 store -w 1 1 1 stored -w 1 1 1 stores -w 1 1 1 stories -w 1 1 1 story -w 1 1 1 strut -w 1 1 1 struts -w 1 1 1 student -w 1 1 1 students -w 1 1 1 stuff -w 1 1 1 style -w 1 1 1 styles -w 1 1 1 submissions -w 1 1 1 submit -w 1 1 1 subscribe -w 1 1 1 subscribed -w 1 1 1 subscriber -w 1 1 1 subscribers -w 1 1 1 subscription -w 1 1 1 subscriptions -w 1 1 1 success -w 1 1 1 suite -w 1 1 1 suites -w 1 1 1 sun -w 1 1 1 sunos -w 1 1 1 super -w 1 1 1 support -w 1 1 1 surf -w 1 1 1 survey -w 1 1 1 surveys -w 1 1 1 swf -w 1 1 1 sws -w 1 1 1 synapse -w 1 1 1 sync -w 1 1 1 synced -w 1 1 1 sys -w 1 1 1 sysmanager -w 1 1 1 system -w 1 1 1 systems -w 1 1 1 sysuser -w 1 1 1 t -w 1 1 1 tag -w 1 1 1 tags -w 1 1 1 tail -w 1 1 1 tape -w 1 1 1 tapes -w 1 1 1 tapestry -w 1 1 1 tar -w 1 1 1 tar.bz2 -w 1 1 1 tb -w 1 1 1 tcl -w 1 1 1 team -w 1 1 1 tech -w 1 1 1 technical -w 1 1 1 technology -w 1 1 1 tel -w 1 1 1 tele -w 1 1 1 templ -w 1 1 1 template -w 1 1 1 templates -w 1 1 1 terms -w 1 1 1 test-cgi -w 1 1 1 test-env -w 1 1 1 test1 -w 1 1 1 test123 -w 1 1 1 test1234 -w 1 1 1 test2 -w 1 1 1 test3 -w 1 1 1 testimonial -w 1 1 1 testimonials -w 1 1 1 testing -w 1 1 1 tests -w 1 1 1 texis -w 1 1 1 text -w 1 1 1 text-base -w 1 1 1 texts -w 1 1 1 theme -w 1 1 1 themes -w 1 1 1 thread -w 1 1 1 threads -w 1 1 1 thumb -w 1 1 1 thumbnail -w 1 1 1 thumbnails -w 1 1 1 thumbs -w 1 1 1 tickets -w 1 1 1 tiki -w 1 1 1 tiles -w 1 1 1 tip -w 1 1 1 tips -w 1 1 1 title -w 1 1 1 tls -w 1 1 1 tmpl -w 1 1 1 tmps -w 1 1 1 tn -w 1 1 1 toc -w 1 1 1 todo -w 1 1 1 toggle -w 1 1 1 tomcat -w 1 1 1 tool -w 1 1 1 toolbar -w 1 1 1 toolkit -w 1 1 1 tools -w 1 1 1 top -w 1 1 1 topic -w 1 1 1 topics -w 1 1 1 torrent -w 1 1 1 torrents -w 1 1 1 tos -w 1 1 1 tour -w 1 1 1 tpl -w 1 1 1 tpv -w 1 1 1 tr -w 1 1 1 trace -w 1 1 1 traceroute -w 1 1 1 traces -w 1 1 1 track -w 1 1 1 trackback -w 1 1 1 tracker -w 1 1 1 trackers -w 1 1 1 tracking -w 1 1 1 tracks -w 1 1 1 traffic -w 1 1 1 trailer -w 1 1 1 trailers -w 1 1 1 training -w 1 1 1 trans -w 1 1 1 transaction -w 1 1 1 transactions -w 1 1 1 transparent -w 1 1 1 transport -w 1 1 1 trash -w 1 1 1 travel -w 1 1 1 treasury -w 1 1 1 tree -w 1 1 1 trees -w 1 1 1 trial -w 1 1 1 true -w 1 1 1 trunk -w 1 1 1 tsweb -w 1 1 1 tt -w 1 1 1 turbine -w 1 1 1 tuscany -w 1 1 1 tutorial -w 1 1 1 tutorials -w 1 1 1 tv -w 1 1 1 tweak -w 1 1 1 twitter -w 1 1 1 type -w 1 1 1 typo3 -w 1 1 1 typo3conf -w 1 1 1 u -w 1 1 1 ubb -w 1 1 1 uds -w 1 1 1 uk -w 1 1 1 umts -w 1 1 1 union -w 1 1 1 unix -w 1 1 1 unlock -w 1 1 1 unpaid -w 1 1 1 unreg -w 1 1 1 unregister -w 1 1 1 unsubscribe -w 1 1 1 up -w 1 1 1 upd -w 1 1 1 update -w 1 1 1 updated -w 1 1 1 updater -w 1 1 1 updates -w 1 1 1 upload -w 1 1 1 uploader -w 1 1 1 uploads -w 1 1 1 url -w 1 1 1 urls -w 1 1 1 us -w 1 1 1 usa -w 1 1 1 usage -w 1 1 1 user -w 1 1 1 userlog -w 1 1 1 users -w 1 1 1 usr -w 1 1 1 util -w 1 1 1 utilities -w 1 1 1 utility -w 1 1 1 utils -w 1 1 1 v -w 1 1 1 v1 -w 1 1 1 v2 -w 1 1 1 var -w 1 1 1 vault -w 1 1 1 vector -w 1 1 1 velocity -w 1 1 1 vendor -w 1 1 1 vendors -w 1 1 1 ver -w 1 1 1 ver1 -w 1 1 1 ver2 -w 1 1 1 version -w 1 1 1 vfs -w 1 1 1 video -w 1 1 1 videos -w 1 1 1 view -w 1 1 1 view-source -w 1 1 1 viewcvs -w 1 1 1 viewforum -w 1 1 1 viewonline -w 1 1 1 views -w 1 1 1 viewsource -w 1 1 1 viewsvn -w 1 1 1 viewtopic -w 1 1 1 viewvc -w 1 1 1 virtual -w 1 1 1 vm -w 1 1 1 voip -w 1 1 1 vol -w 1 1 1 vote -w 1 1 1 voter -w 1 1 1 votes -w 1 1 1 vpn -w 1 1 1 vuln -w 1 1 1 w -w 1 1 1 w3 -w 1 1 1 w3c -w 1 1 1 wa -w 1 1 1 wap -w 1 1 1 war -w 1 1 1 warez -w 1 1 1 way-board -w 1 1 1 wbboard -w 1 1 1 wc -w 1 1 1 weather -w 1 1 1 web -w 1 1 1 web-beans -w 1 1 1 web-console -w 1 1 1 webaccess -w 1 1 1 webadmin -w 1 1 1 webagent -w 1 1 1 webalizer -w 1 1 1 webapp -w 1 1 1 webb -w 1 1 1 webbbs -w 1 1 1 webboard -w 1 1 1 webcalendar -w 1 1 1 webcart -w 1 1 1 webcasts -w 1 1 1 webcgi -w 1 1 1 webchat -w 1 1 1 webdata -w 1 1 1 webdav -w 1 1 1 webdb -w 1 1 1 weblog -w 1 1 1 weblogic -w 1 1 1 weblogs -w 1 1 1 webmail -w 1 1 1 webplus -w 1 1 1 webshop -w 1 1 1 website -w 1 1 1 websphere -w 1 1 1 websql -w 1 1 1 webstats -w 1 1 1 websvn -w 1 1 1 webwork -w 1 1 1 week -w 1 1 1 weekly -w 1 1 1 welcome -w 1 1 1 whitepapers -w 1 1 1 whois -w 1 1 1 whosonline -w 1 1 1 wicket -w 1 1 1 wiki -w 1 1 1 win -w 1 1 1 win32 -w 1 1 1 windows -w 1 1 1 winnt -w 1 1 1 wireless -w 1 1 1 wml -w 1 1 1 word -w 1 1 1 wordpress -w 1 1 1 work -w 1 1 1 working -w 1 1 1 world -w 1 1 1 wow -w 1 1 1 wp -w 1 1 1 wp-content -w 1 1 1 wp-dbmanager -w 1 1 1 wp-includes -w 1 1 1 wp-login -w 1 1 1 wp-syntax -w 1 1 1 wrap -w 1 1 1 ws-client -w 1 1 1 ws_ftp -w 1 1 1 wsdl -w 1 1 1 wtai -w 1 1 1 www -w 1 1 1 www-sql -w 1 1 1 www1 -w 1 1 1 www2 -w 1 1 1 www3 -w 1 1 1 wwwboard -w 1 1 1 wwwroot -w 1 1 1 wwwstats -w 1 1 1 wwwthreads -w 1 1 1 wwwuser -w 1 1 1 wysiwyg -w 1 1 1 x -w 1 1 1 xalan -w 1 1 1 xerces -w 1 1 1 xhtml -w 1 1 1 xmlrpc -w 1 1 1 xsql -w 1 1 1 xxx -w 1 1 1 xyzzy -w 1 1 1 y -w 1 1 1 yahoo -w 1 1 1 year -w 1 1 1 yearly -w 1 1 1 yml -w 1 1 1 youtube -w 1 1 1 yt -w 1 1 1 z -w 1 1 1 zboard -w 1 1 1 zencart -w 1 1 1 zend -w 1 1 1 zero -w 1 1 1 zimbra -w 1 1 1 zipfiles -w 1 1 1 zips -w 1 1 1 zoom -w 1 1 1 zope -w 1 1 1 zorum -w 1 1 1 ~admin -w 1 1 1 ~amanda -w 1 1 1 ~apache -w 1 1 1 ~ashley -w 1 1 1 ~bin -w 1 1 1 ~bob -w 1 1 1 ~chris -w 1 1 1 ~dan -w 1 1 1 ~eric -w 1 1 1 ~ftp -w 1 1 1 ~guest -w 1 1 1 ~http -w 1 1 1 ~httpd -w 1 1 1 ~jacob -w 1 1 1 ~jennifer -w 1 1 1 ~jessica -w 1 1 1 ~john -w 1 1 1 ~log -w 1 1 1 ~logs -w 1 1 1 ~lp -w 1 1 1 ~mark -w 1 1 1 ~matt -w 1 1 1 ~michael -w 1 1 1 ~nobody -w 1 1 1 ~root -w 1 1 1 ~test -w 1 1 1 ~tmp -w 1 1 1 ~www +wg 1 1 1 camel +wg 1 1 1 car +wg 1 1 1 card +wg 1 1 1 cards +wg 1 1 1 career +wg 1 1 1 careers +wg 1 1 1 cars +wg 1 1 1 cart +wg 1 1 1 carts +wg 1 1 1 cat +wg 1 1 1 catalog +wg 1 1 1 catalogs +wg 1 1 1 catalyst +wg 1 1 1 categories +wg 1 1 1 category +wg 1 1 1 catinfo +wg 1 1 1 cats +wg 1 1 1 ccbill +wg 1 1 1 cd +wg 1 1 1 cert +wg 1 1 1 certificate +wg 1 1 1 certificates +wg 1 1 1 certified +wg 1 1 1 certs +wg 1 1 1 cf +ws 1 1 1 cfcache +wg 1 1 1 cfdocs +wg 1 1 1 cfide +wg 1 1 1 cfusion +ws 1 1 1 cgi-bin +ws 1 1 1 cgi-bin2 +ws 1 1 1 cgi-home +ws 1 1 1 cgi-local +ws 1 1 1 cgi-pub +ws 1 1 1 cgi-script +ws 1 1 1 cgi-shl +ws 1 1 1 cgi-sys +ws 1 1 1 cgi-web +ws 1 1 1 cgi-win +ws 1 1 1 cgibin +ws 1 1 1 cgiwrap +ws 1 1 1 cgm-web +wg 1 1 1 change +wg 1 1 1 changed +wg 1 1 1 changes +wg 1 1 1 charge +wg 1 1 1 charges +wg 1 1 1 chat +wg 1 1 1 chats +wg 1 1 1 check +wg 1 1 1 checking +wg 1 1 1 checkout +wg 1 1 1 checkpoint +wg 1 1 1 checks +wg 1 1 1 child +wg 1 1 1 children +wg 1 1 1 chris +wg 1 1 1 chrome +wg 1 1 1 cisco +wg 1 1 1 cisweb +wg 1 1 1 citrix +wg 1 1 1 cl +wg 1 1 1 claim +wg 1 1 1 claims +wg 1 1 1 classes +wg 1 1 1 classified +wg 1 1 1 classifieds +wg 1 1 1 clear +wg 1 1 1 click +wg 1 1 1 clicks +wg 1 1 1 client +ws 1 1 1 clientaccesspolicy +wg 1 1 1 clients +wg 1 1 1 clk +wg 1 1 1 clock +wg 1 1 1 close +wg 1 1 1 closed +wg 1 1 1 closing +wg 1 1 1 club +wg 1 1 1 cluster +wg 1 1 1 clusters +wg 1 1 1 cmd +wg 1 1 1 cms +wg 1 1 1 cnt +wg 1 1 1 cocoon +wg 1 1 1 code +wg 1 1 1 codec +wg 1 1 1 codecs +wg 1 1 1 codes +wg 1 1 1 cognos +wg 1 1 1 coldfusion +wg 1 1 1 columns +wg 1 1 1 com +wg 1 1 1 comment +wg 1 1 1 comments +wg 1 1 1 commerce +wg 1 1 1 commercial +wg 1 1 1 common +wg 1 1 1 communicator +wg 1 1 1 community +wg 1 1 1 compact +wg 1 1 1 company +wg 1 1 1 compat +wg 1 1 1 complaint +wg 1 1 1 complaints +wg 1 1 1 compliance +wg 1 1 1 component +wg 1 1 1 components +wg 1 1 1 compress +wg 1 1 1 compressed +wg 1 1 1 computer +wg 1 1 1 computers +wg 1 1 1 computing +wg 1 1 1 conference +wg 1 1 1 conferences +wg 1 1 1 configs +wg 1 1 1 console +wg 1 1 1 consumer +wg 1 1 1 contact +wg 1 1 1 contacts +wg 1 1 1 content +wg 1 1 1 contents +wg 1 1 1 contest +wg 1 1 1 contract +wg 1 1 1 contracts +wg 1 1 1 control +wg 1 1 1 controller +wg 1 1 1 controlpanel +wg 1 1 1 cookie +wg 1 1 1 cookies +wg 1 1 1 copies +wg 1 1 1 copy +wg 1 1 1 copyright +wg 1 1 1 corp +wg 1 1 1 corpo +wg 1 1 1 corporate +wg 1 1 1 corrections +wg 1 1 1 count +wg 1 1 1 counter +wg 1 1 1 counters +wg 1 1 1 counts +wg 1 1 1 course +wg 1 1 1 courses +wg 1 1 1 cover +wg 1 1 1 cpadmin +wg 1 1 1 cpanel +wg 1 1 1 cr +wg 1 1 1 crack +wg 1 1 1 crash +wg 1 1 1 crashes +wg 1 1 1 create +wg 1 1 1 creator +wg 1 1 1 credit +wg 1 1 1 credits +wg 1 1 1 crm +wg 1 1 1 cron +wg 1 1 1 crons +wg 1 1 1 crontab +wg 1 1 1 crontabs +wg 1 1 1 crossdomain +wg 1 1 1 crypt +wg 1 1 1 crypto +ws 1 1 1 cs +wg 1 1 1 css +wg 1 1 1 current +wg 1 1 1 custom +ws 1 1 1 custom-log +ws 1 1 1 custom_log +wg 1 1 1 customer +wg 1 1 1 customers +wg 1 1 1 cute +wg 1 1 1 cv +wg 1 1 1 cxf +wg 1 1 1 czcmdcvt +wg 1 1 1 d +wg 1 1 1 daemon +wg 1 1 1 daily +wg 1 1 1 dan +wg 1 1 1 dana-na +wg 1 1 1 data +wg 1 1 1 database +wg 1 1 1 databases +wg 1 1 1 date +wg 1 1 1 day +wg 1 1 1 db_connect +wg 1 1 1 dba +wg 1 1 1 dbase +wg 1 1 1 dblclk +wg 1 1 1 dbman +wg 1 1 1 dbmodules +wg 1 1 1 dbutil +wg 1 1 1 dc +wg 1 1 1 dcforum +wg 1 1 1 dclk +wg 1 1 1 de +wg 1 1 1 dealer +wg 1 1 1 debug +wg 1 1 1 decl +wg 1 1 1 declaration +wg 1 1 1 declarations +wg 1 1 1 decode +wg 1 1 1 decoder +wg 1 1 1 decrypt +wg 1 1 1 decrypted +wg 1 1 1 decryption +wg 1 1 1 def +wg 1 1 1 default +wg 1 1 1 defaults +wg 1 1 1 definition +wg 1 1 1 definitions +wg 1 1 1 del +wg 1 1 1 delete +wg 1 1 1 deleted +wg 1 1 1 demo +wg 1 1 1 demos +wg 1 1 1 denied +wg 1 1 1 deny +wg 1 1 1 design +wg 1 1 1 desktop +wg 1 1 1 desktops +wg 1 1 1 detail +wg 1 1 1 details +wg 1 1 1 dev +wg 1 1 1 devel +wg 1 1 1 developer +wg 1 1 1 developers +wg 1 1 1 development +wg 1 1 1 device +wg 1 1 1 devices +wg 1 1 1 devs +wg 1 1 1 df +wg 1 1 1 dialog +wg 1 1 1 dialogs +wg 1 1 1 diff +wg 1 1 1 diffs +wg 1 1 1 digest +wg 1 1 1 digg +wg 1 1 1 dir +ws 1 1 1 dir-prop-base +wg 1 1 1 directories +wg 1 1 1 directory +wg 1 1 1 dirs +wg 1 1 1 disabled +wg 1 1 1 disclaimer +wg 1 1 1 display +wg 1 1 1 django +wg 1 1 1 dl +wg 1 1 1 dm +ws 1 1 1 dm-config +wg 1 1 1 dms +wg 1 1 1 dms0 +wg 1 1 1 dns +wg 1 1 1 do +ws 1 1 1 doc +wg 1 1 1 docebo +wg 1 1 1 dock +wg 1 1 1 docroot +wg 1 1 1 docs +wg 1 1 1 document +wg 1 1 1 documentation +wg 1 1 1 documents +wg 1 1 1 domain +wg 1 1 1 domains +wg 1 1 1 donate +wg 1 1 1 done +wg 1 1 1 doubleclick +wg 1 1 1 down +wg 1 1 1 download +wg 1 1 1 downloader +wg 1 1 1 downloads +wg 1 1 1 drop +wg 1 1 1 dropped +wg 1 1 1 drupal +wg 1 1 1 dummy +wg 1 1 1 dump +wg 1 1 1 dumps +wg 1 1 1 dvd +wg 1 1 1 dwr +wg 1 1 1 dyn +wg 1 1 1 dynamic +wg 1 1 1 e +wg 1 1 1 e2fs +wg 1 1 1 ear +wg 1 1 1 ecommerce +wg 1 1 1 edge +wg 1 1 1 edit +wg 1 1 1 editor +wg 1 1 1 edits +wg 1 1 1 edp +wg 1 1 1 edu +wg 1 1 1 education +wg 1 1 1 ee +wg 1 1 1 effort +wg 1 1 1 efforts +wg 1 1 1 egress +wg 1 1 1 ejb +wg 1 1 1 element +wg 1 1 1 elements +wg 1 1 1 em +wg 1 1 1 email +wg 1 1 1 emails +wg 1 1 1 embed +wg 1 1 1 embedded +wg 1 1 1 emea +wg 1 1 1 employees +wg 1 1 1 employment +wg 1 1 1 empty +wg 1 1 1 emu +wg 1 1 1 emulator +wg 1 1 1 en +ws 1 1 1 en_US +wg 1 1 1 enc +wg 1 1 1 encode +wg 1 1 1 encoder +wg 1 1 1 encrypt +wg 1 1 1 encrypted +wg 1 1 1 encyption +wg 1 1 1 eng +wg 1 1 1 engine +wg 1 1 1 english +wg 1 1 1 enterprise +wg 1 1 1 entertainment +wg 1 1 1 entries +wg 1 1 1 entry +wg 1 1 1 env +wg 1 1 1 environ +wg 1 1 1 environment +ws 1 1 1 ep +wg 1 1 1 eric +ws 1 1 1 error-log +ws 1 1 1 error_log +wg 1 1 1 errors +wg 1 1 1 es +wg 1 1 1 esale +wg 1 1 1 esales +wg 1 1 1 etc +wg 1 1 1 eu +wg 1 1 1 europe +wg 1 1 1 event +wg 1 1 1 events +wg 1 1 1 evil +wg 1 1 1 evt +wg 1 1 1 ews +wg 1 1 1 ex +wg 1 1 1 example +wg 1 1 1 examples +wg 1 1 1 excalibur +wg 1 1 1 exchange +wg 1 1 1 exec +wg 1 1 1 explorer +wg 1 1 1 export +wg 1 1 1 ext +wg 1 1 1 ext2 +wg 1 1 1 extern +wg 1 1 1 external +wg 1 1 1 extras +wg 1 1 1 ezshopper +wg 1 1 1 f +wg 1 1 1 fabric +wg 1 1 1 face +wg 1 1 1 facebook +wg 1 1 1 faces +wg 1 1 1 faculty +wg 1 1 1 fail +wg 1 1 1 failure +wg 1 1 1 fake +wg 1 1 1 family +wg 1 1 1 faq +wg 1 1 1 faqs +wg 1 1 1 favorite +wg 1 1 1 favorites +wg 1 1 1 fb +wg 1 1 1 fbook +wg 1 1 1 fc +ws 1 1 1 fcgi-bin +wg 1 1 1 feature +wg 1 1 1 features +wg 1 1 1 feed +wg 1 1 1 feedback +wg 1 1 1 feeds +wg 1 1 1 felix +wg 1 1 1 fetch +wg 1 1 1 field +wg 1 1 1 fields +wg 1 1 1 file +wg 1 1 1 fileadmin +wg 1 1 1 filelist +wg 1 1 1 files +wg 1 1 1 filez +wg 1 1 1 finance +wg 1 1 1 financial +wg 1 1 1 find +wg 1 1 1 finger +wg 1 1 1 firefox +wg 1 1 1 firewall +wg 1 1 1 firmware +wg 1 1 1 first +wg 1 1 1 fixed +wg 1 1 1 flags +wg 1 1 1 flash +wg 1 1 1 flow +wg 1 1 1 flows +wg 1 1 1 flv +wg 1 1 1 fn +wg 1 1 1 folder +wg 1 1 1 folders +wg 1 1 1 font +wg 1 1 1 fonts +wg 1 1 1 foo +wg 1 1 1 footer +wg 1 1 1 footers +wg 1 1 1 form +wg 1 1 1 format +wg 1 1 1 formatting +wg 1 1 1 formmail +wg 1 1 1 forms +wg 1 1 1 forrest +wg 1 1 1 fortune +wg 1 1 1 forum +wg 1 1 1 forum1 +wg 1 1 1 forum2 +wg 1 1 1 forumdisplay +wg 1 1 1 forums +wg 1 1 1 forward +wg 1 1 1 foto +wg 1 1 1 foundation +wg 1 1 1 fr +wg 1 1 1 frame +wg 1 1 1 frames +wg 1 1 1 framework +wg 1 1 1 free +wg 1 1 1 freebsd +wg 1 1 1 friend +wg 1 1 1 friends +wg 1 1 1 frob +wg 1 1 1 frontend +wg 1 1 1 fs +wg 1 1 1 ftp +wg 1 1 1 fuck +wg 1 1 1 fuckoff +wg 1 1 1 fuckyou +wg 1 1 1 full +wg 1 1 1 fun +wg 1 1 1 func +wg 1 1 1 funcs +wg 1 1 1 function +wg 1 1 1 functions +wg 1 1 1 fund +wg 1 1 1 funding +wg 1 1 1 funds +wg 1 1 1 fusion +wg 1 1 1 fw +wg 1 1 1 g +wg 1 1 1 gadget +wg 1 1 1 gadgets +wg 1 1 1 galleries +wg 1 1 1 gallery +wg 1 1 1 game +wg 1 1 1 games +wg 1 1 1 ganglia +wg 1 1 1 garbage +wg 1 1 1 gateway +wg 1 1 1 gb +wg 1 1 1 geeklog +wg 1 1 1 general +wg 1 1 1 geronimo +wg 1 1 1 get +wg 1 1 1 getaccess +wg 1 1 1 getjobid +wg 1 1 1 gfx +wg 1 1 1 gid +ws 1 1 1 gif +wg 1 1 1 git +wg 1 1 1 gitweb +wg 1 1 1 glimpse +wg 1 1 1 global +wg 1 1 1 globals +wg 1 1 1 glossary +wg 1 1 1 go +wg 1 1 1 goaway +wg 1 1 1 google +wg 1 1 1 government +wg 1 1 1 gprs +wg 1 1 1 grant +wg 1 1 1 grants +wg 1 1 1 graphics +wg 1 1 1 group +wg 1 1 1 groupcp +wg 1 1 1 groups +wg 1 1 1 gsm +wg 1 1 1 guest +wg 1 1 1 guestbook +wg 1 1 1 guests +wg 1 1 1 guide +wg 1 1 1 guides +wg 1 1 1 gump +wg 1 1 1 gwt +wg 1 1 1 h +wg 1 1 1 hack +wg 1 1 1 hacker +wg 1 1 1 hacking +wg 1 1 1 hackme +wg 1 1 1 hadoop +wg 1 1 1 hardcore +wg 1 1 1 hardware +wg 1 1 1 harmony +wg 1 1 1 head +wg 1 1 1 header +wg 1 1 1 headers +wg 1 1 1 health +wg 1 1 1 hello +wg 1 1 1 help +wg 1 1 1 helper +wg 1 1 1 helpers +wg 1 1 1 hi +wg 1 1 1 hidden +wg 1 1 1 hide +wg 1 1 1 high +wg 1 1 1 hipaa +wg 1 1 1 hire +wg 1 1 1 history +wg 1 1 1 hit +wg 1 1 1 hits +wg 1 1 1 hole +wg 1 1 1 home +wg 1 1 1 homepage +wg 1 1 1 hop +wg 1 1 1 horde +wg 1 1 1 hosting +wg 1 1 1 hosts +wg 1 1 1 hour +wg 1 1 1 hourly +wg 1 1 1 howto +wg 1 1 1 hp +wg 1 1 1 hr +wg 1 1 1 hta +ws 1 1 1 htbin +ws 1 1 1 htdoc +ws 1 1 1 htdocs +wg 1 1 1 htpasswd +wg 1 1 1 http +wg 1 1 1 httpd +wg 1 1 1 https +wg 1 1 1 httpuser +wg 1 1 1 hu +wg 1 1 1 hyper +wg 1 1 1 i +wg 1 1 1 ia +wg 1 1 1 ibm +wg 1 1 1 icat +wg 1 1 1 ico +wg 1 1 1 icon +wg 1 1 1 icons +wg 1 1 1 id +wg 1 1 1 idea +wg 1 1 1 ideas +wg 1 1 1 ids +wg 1 1 1 ie +wg 1 1 1 iframe +wg 1 1 1 ig +wg 1 1 1 ignore +ws 1 1 1 iisadmin +ws 1 1 1 iisadmpwd +ws 1 1 1 iissamples +wg 1 1 1 image +wg 1 1 1 imagefolio +wg 1 1 1 images +wg 1 1 1 img +wg 1 1 1 imgs +wg 1 1 1 imp +wg 1 1 1 import +wg 1 1 1 important +wg 1 1 1 in +wg 1 1 1 inbound +wg 1 1 1 incl +wg 1 1 1 include +wg 1 1 1 includes +wg 1 1 1 incoming +wg 1 1 1 incubator +wg 1 1 1 index +wg 1 1 1 index1 +wg 1 1 1 index2 +wg 1 1 1 index_1 +wg 1 1 1 index_2 +ws 1 1 1 inetpub +ws 1 1 1 inetsrv +wg 1 1 1 inf +wg 1 1 1 info +wg 1 1 1 information +wg 1 1 1 ingress +wg 1 1 1 init +wg 1 1 1 inline +wg 1 1 1 input +wg 1 1 1 inquire +wg 1 1 1 inquiries +wg 1 1 1 inquiry +wg 1 1 1 insert +wg 1 1 1 install +wg 1 1 1 int +wg 1 1 1 intel +wg 1 1 1 intelligence +wg 1 1 1 inter +wg 1 1 1 interim +wg 1 1 1 intermediate +wg 1 1 1 internal +wg 1 1 1 international +wg 1 1 1 internet +wg 1 1 1 intl +wg 1 1 1 intra +wg 1 1 1 intranet +wg 1 1 1 intro +wg 1 1 1 ip +wg 1 1 1 ipc +wg 1 1 1 iphone +wg 1 1 1 ips +wg 1 1 1 irc +wg 1 1 1 is +wg 1 1 1 isapi +wg 1 1 1 iso +wg 1 1 1 issues +wg 1 1 1 it +wg 1 1 1 item +wg 1 1 1 items +wg 1 1 1 j +wg 1 1 1 j2ee +wg 1 1 1 j2me +wg 1 1 1 jacob +wg 1 1 1 jakarta +wg 1 1 1 java-plugin +wg 1 1 1 javadoc +wg 1 1 1 javascript +wg 1 1 1 javax +wg 1 1 1 jboss +wg 1 1 1 jbossas +wg 1 1 1 jbossws +wg 1 1 1 jdbc +wg 1 1 1 jennifer +wg 1 1 1 jessica +wg 1 1 1 jigsaw +wg 1 1 1 jira +wg 1 1 1 jj +ws 1 1 1 jmx-console +wg 1 1 1 job +wg 1 1 1 jobs +wg 1 1 1 joe +wg 1 1 1 john +wg 1 1 1 join +wg 1 1 1 joomla +wg 1 1 1 journal +wg 1 1 1 jp +wg 1 1 1 jpa +ws 1 1 1 jpg +wg 1 1 1 jre +wg 1 1 1 jrun +wg 1 1 1 json +wg 1 1 1 jsso +wg 1 1 1 jsx +wg 1 1 1 juniper +wg 1 1 1 junk +wg 1 1 1 jvm +wg 1 1 1 k +wg 1 1 1 kboard +wg 1 1 1 keep +wg 1 1 1 kernel +wg 1 1 1 keygen +wg 1 1 1 keys +wg 1 1 1 kids +wg 1 1 1 kill +ws 1 1 1 known_hosts +wg 1 1 1 l +wg 1 1 1 la +wg 1 1 1 labs +wg 1 1 1 lang +wg 1 1 1 large +wg 1 1 1 law +wg 1 1 1 layout +wg 1 1 1 layouts +wg 1 1 1 ldap +wg 1 1 1 leader +wg 1 1 1 leaders +wg 1 1 1 left +wg 1 1 1 legacy +wg 1 1 1 legal +wg 1 1 1 lenya +wg 1 1 1 letters +wg 1 1 1 level +wg 1 1 1 lg +wg 1 1 1 lib +wg 1 1 1 library +wg 1 1 1 libs +wg 1 1 1 license +wg 1 1 1 licenses +wg 1 1 1 limit +wg 1 1 1 line +wg 1 1 1 link +wg 1 1 1 links +wg 1 1 1 linux +wg 1 1 1 list +wg 1 1 1 listinfo +wg 1 1 1 lists +wg 1 1 1 live +wg 1 1 1 lo +wg 1 1 1 loader +wg 1 1 1 loading +wg 1 1 1 loc +wg 1 1 1 local +wg 1 1 1 location +wg 1 1 1 lock +wg 1 1 1 locked +wg 1 1 1 log4j +wg 1 1 1 log4net +wg 1 1 1 logfile +wg 1 1 1 logger +wg 1 1 1 logging +wg 1 1 1 login +wg 1 1 1 logins +wg 1 1 1 logo +wg 1 1 1 logoff +wg 1 1 1 logon +wg 1 1 1 logos +wg 1 1 1 logout +wg 1 1 1 logs +wg 1 1 1 lost +ws 1 1 1 lost+found +wg 1 1 1 low +wg 1 1 1 ls +wg 1 1 1 lst +wg 1 1 1 lucene +wg 1 1 1 m +wg 1 1 1 mac +wg 1 1 1 macromedia +wg 1 1 1 maestro +wg 1 1 1 mail +wg 1 1 1 mailer +wg 1 1 1 mailing +wg 1 1 1 mailman +wg 1 1 1 mails +wg 1 1 1 main +wg 1 1 1 mambo +wg 1 1 1 manage +wg 1 1 1 managed +wg 1 1 1 management +wg 1 1 1 manager +ws 1 1 1 manifest +wg 1 1 1 manual +wg 1 1 1 manuals +wg 1 1 1 map +wg 1 1 1 maps +wg 1 1 1 mark +wg 1 1 1 marketing +wg 1 1 1 master +ws 1 1 1 master.passwd +wg 1 1 1 match +wg 1 1 1 matrix +wg 1 1 1 matt +wg 1 1 1 maven +wg 1 1 1 mbox +wg 1 1 1 me +wg 1 1 1 media +wg 1 1 1 medium +wg 1 1 1 mem +wg 1 1 1 member +wg 1 1 1 members +wg 1 1 1 membership +wg 1 1 1 memory +wg 1 1 1 menu +wg 1 1 1 menus +wg 1 1 1 message +wg 1 1 1 messages +wg 1 1 1 messaging +wg 1 1 1 meta +wg 1 1 1 michael +wg 1 1 1 microsoft +wg 1 1 1 migrate +wg 1 1 1 migrated +wg 1 1 1 migration +wg 1 1 1 mina +wg 1 1 1 mini +wg 1 1 1 minute +wg 1 1 1 mirror +wg 1 1 1 mirrors +wg 1 1 1 misc +wg 1 1 1 mission +wg 1 1 1 mix +wg 1 1 1 mlist +wg 1 1 1 mms +wg 1 1 1 mobi +wg 1 1 1 mobile +wg 1 1 1 mock +wg 1 1 1 mod +wg 1 1 1 modify +wg 1 1 1 mods +wg 1 1 1 module +wg 1 1 1 modules +wg 1 1 1 mojo +wg 1 1 1 money +wg 1 1 1 monitor +wg 1 1 1 monitoring +wg 1 1 1 monitors +wg 1 1 1 month +wg 1 1 1 monthly +wg 1 1 1 more +wg 1 1 1 motd +wg 1 1 1 move +wg 1 1 1 moved +wg 1 1 1 movie +wg 1 1 1 movies +wg 1 1 1 mp +wg 1 1 1 mp3 +wg 1 1 1 mp3s +wg 1 1 1 ms +wg 1 1 1 ms-sql +wg 1 1 1 msadc +wg 1 1 1 msadm +wg 1 1 1 msft +wg 1 1 1 msg +wg 1 1 1 msie +wg 1 1 1 msql +wg 1 1 1 mssql +wg 1 1 1 mta +wg 1 1 1 multi +wg 1 1 1 multimedia +wg 1 1 1 music +wg 1 1 1 mx +wg 1 1 1 my +wg 1 1 1 myadmin +wg 1 1 1 myfaces +wg 1 1 1 myphpnuke +wg 1 1 1 mysql +wg 1 1 1 mysqld +wg 1 1 1 n +wg 1 1 1 nav +wg 1 1 1 navigation +wg 1 1 1 nc +wg 1 1 1 net +wg 1 1 1 netbsd +wg 1 1 1 netcat +wg 1 1 1 nethome +wg 1 1 1 nets +wg 1 1 1 network +wg 1 1 1 networking +wg 1 1 1 new +wg 1 1 1 news +wg 1 1 1 newsletter +wg 1 1 1 newsletters +wg 1 1 1 newticket +wg 1 1 1 next +wg 1 1 1 nfs +wg 1 1 1 nice +wg 1 1 1 nl +wg 1 1 1 nobody +wg 1 1 1 node +wg 1 1 1 none +wg 1 1 1 note +wg 1 1 1 notes +wg 1 1 1 notification +wg 1 1 1 notifications +wg 1 1 1 notified +wg 1 1 1 notifier +wg 1 1 1 notify +wg 1 1 1 novell +wg 1 1 1 ns +wg 1 1 1 nude +wg 1 1 1 nuke +wg 1 1 1 nul +wg 1 1 1 null +wg 1 1 1 o +ws 1 1 1 oa_servlets +wg 1 1 1 oauth +wg 1 1 1 obdc +wg 1 1 1 obsolete +wg 1 1 1 obsoleted +wg 1 1 1 odbc +wg 1 1 1 ode +wg 1 1 1 oem +wg 1 1 1 ofbiz +wg 1 1 1 office +wg 1 1 1 offices +wg 1 1 1 onbound +wg 1 1 1 online +wg 1 1 1 op +wg 1 1 1 open +wg 1 1 1 openbsd +wg 1 1 1 opencart +wg 1 1 1 opendir +wg 1 1 1 openejb +wg 1 1 1 openjpa +wg 1 1 1 opera +wg 1 1 1 operations +wg 1 1 1 opinion +ws 1 1 1 oprocmgr-status +wg 1 1 1 opt +wg 1 1 1 option +wg 1 1 1 options +wg 1 1 1 oracle +ws 1 1 1 oracle.xml.xsql.XSQLServlet +wg 1 1 1 order +wg 1 1 1 ordered +wg 1 1 1 orders +wg 1 1 1 org +wg 1 1 1 osc +wg 1 1 1 oscommerce +wg 1 1 1 other +wg 1 1 1 outcome +wg 1 1 1 outgoing +wg 1 1 1 outline +wg 1 1 1 output +wg 1 1 1 outreach +wg 1 1 1 overview +wg 1 1 1 owa +wg 1 1 1 owl +wg 1 1 1 ows +ws 1 1 1 ows-bin +wg 1 1 1 p +wg 1 1 1 p2p +wg 1 1 1 pack +wg 1 1 1 package +wg 1 1 1 packaged +wg 1 1 1 packages +wg 1 1 1 packed +wg 1 1 1 page +wg 1 1 1 page1 +wg 1 1 1 page2 +wg 1 1 1 page_1 +wg 1 1 1 page_2 +wg 1 1 1 pages +wg 1 1 1 paid +wg 1 1 1 panel +wg 1 1 1 paper +wg 1 1 1 papers +wg 1 1 1 parse +wg 1 1 1 partner +wg 1 1 1 partners +wg 1 1 1 party +wg 1 1 1 pass +wg 1 1 1 passive +wg 1 1 1 passwd +wg 1 1 1 password +wg 1 1 1 passwords +wg 1 1 1 past +wg 1 1 1 patch +wg 1 1 1 patches +wg 1 1 1 pay +wg 1 1 1 payment +wg 1 1 1 payments +wg 1 1 1 paypal +wg 1 1 1 pbo +wg 1 1 1 pc +wg 1 1 1 pci +wg 1 1 1 pda +ws 1 1 1 pdf +wg 1 1 1 pdfs +wg 1 1 1 pear +wg 1 1 1 peek +ws 1 1 1 pem +wg 1 1 1 pending +wg 1 1 1 people +wg 1 1 1 perf +wg 1 1 1 performance +wg 1 1 1 perl +wg 1 1 1 personal +ws 1 1 1 pfx +wg 1 1 1 pg +wg 1 1 1 phf +wg 1 1 1 phone +wg 1 1 1 phones +wg 1 1 1 phorum +wg 1 1 1 photo +wg 1 1 1 photos +ws 1 1 1 phpBB +ws 1 1 1 phpBB2 +ws 1 1 1 phpEventCalendar +ws 1 1 1 phpMyAdmin +ws 1 1 1 phpbb +ws 1 1 1 phpmyadmin +ws 1 1 1 phpnuke +wg 1 1 1 phps +wg 1 1 1 pic +wg 1 1 1 pics +wg 1 1 1 pictures +wg 1 1 1 pii +wg 1 1 1 ping +wg 1 1 1 pipe +wg 1 1 1 pipermail +wg 1 1 1 piranha +wg 1 1 1 pivot +wg 1 1 1 pix +wg 1 1 1 pixel +wg 1 1 1 pkg +wg 1 1 1 pkgs +wg 1 1 1 plain +wg 1 1 1 play +wg 1 1 1 player +wg 1 1 1 playing +wg 1 1 1 playlist +wg 1 1 1 pls +wg 1 1 1 plugin +wg 1 1 1 plugins +ws 1 1 1 png +wg 1 1 1 poc +wg 1 1 1 poi +wg 1 1 1 policies +wg 1 1 1 policy +wg 1 1 1 politics +wg 1 1 1 poll +wg 1 1 1 polls +wg 1 1 1 pool +wg 1 1 1 pop +wg 1 1 1 pop3 +wg 1 1 1 popup +wg 1 1 1 porn +wg 1 1 1 port +wg 1 1 1 portal +wg 1 1 1 portals +wg 1 1 1 portfolio +wg 1 1 1 pos +wg 1 1 1 post +wg 1 1 1 posted +wg 1 1 1 postgres +wg 1 1 1 postgresql +wg 1 1 1 postnuke +wg 1 1 1 postpaid +wg 1 1 1 posts +ws 1 1 1 ppt +wg 1 1 1 pr +wg 1 1 1 pr0n +wg 1 1 1 premium +wg 1 1 1 prepaid +wg 1 1 1 presentation +wg 1 1 1 presentations +wg 1 1 1 preserve +wg 1 1 1 press +wg 1 1 1 preview +wg 1 1 1 previews +wg 1 1 1 previous +wg 1 1 1 pricing +wg 1 1 1 print +wg 1 1 1 printenv +wg 1 1 1 printer +wg 1 1 1 printers +wg 1 1 1 priv +wg 1 1 1 privacy +wg 1 1 1 private +wg 1 1 1 pro +wg 1 1 1 problems +wg 1 1 1 proc +wg 1 1 1 procedures +wg 1 1 1 procure +wg 1 1 1 procurement +wg 1 1 1 prod +wg 1 1 1 product +wg 1 1 1 product_info +wg 1 1 1 production +wg 1 1 1 products +wg 1 1 1 profile +wg 1 1 1 profiles +wg 1 1 1 profiling +wg 1 1 1 program +wg 1 1 1 programming +wg 1 1 1 programs +wg 1 1 1 progress +wg 1 1 1 project +wg 1 1 1 projects +wg 1 1 1 promo +wg 1 1 1 promoted +wg 1 1 1 promotion +wg 1 1 1 prop +ws 1 1 1 prop-base +wg 1 1 1 properties +wg 1 1 1 property +wg 1 1 1 props +wg 1 1 1 prot +wg 1 1 1 protect +wg 1 1 1 protected +wg 1 1 1 protection +wg 1 1 1 proto +wg 1 1 1 proxies +wg 1 1 1 proxy +wg 1 1 1 prv +wg 1 1 1 ps +wg 1 1 1 psql +wg 1 1 1 pt +wg 1 1 1 pub +wg 1 1 1 public +wg 1 1 1 publication +wg 1 1 1 publications +wg 1 1 1 pubs +wg 1 1 1 pull +wg 1 1 1 purchase +wg 1 1 1 purchases +wg 1 1 1 purchasing +wg 1 1 1 push +wg 1 1 1 pw +wg 1 1 1 pwd +wg 1 1 1 python +wg 1 1 1 q +wg 1 1 1 q1 +wg 1 1 1 q2 +wg 1 1 1 q3 +wg 1 1 1 q4 +wg 1 1 1 qotd +wg 1 1 1 qpid +wg 1 1 1 quarterly +wg 1 1 1 queries +wg 1 1 1 query +wg 1 1 1 queue +wg 1 1 1 queues +wg 1 1 1 quote +wg 1 1 1 quotes +wg 1 1 1 r +wg 1 1 1 radio +wg 1 1 1 random +wg 1 1 1 rar +wg 1 1 1 rdf +wg 1 1 1 read +wg 1 1 1 readme +wg 1 1 1 real +wg 1 1 1 realestate +wg 1 1 1 receive +wg 1 1 1 received +wg 1 1 1 recharge +wg 1 1 1 record +wg 1 1 1 recorded +wg 1 1 1 recorder +wg 1 1 1 records +wg 1 1 1 recovery +wg 1 1 1 recycle +wg 1 1 1 recycled +wg 1 1 1 redir +wg 1 1 1 redirect +wg 1 1 1 reference +wg 1 1 1 reg +wg 1 1 1 register +wg 1 1 1 registered +wg 1 1 1 registration +wg 1 1 1 registrations +wg 1 1 1 release +wg 1 1 1 releases +wg 1 1 1 remind +wg 1 1 1 reminder +wg 1 1 1 remote +wg 1 1 1 removal +wg 1 1 1 removals +wg 1 1 1 remove +wg 1 1 1 removed +wg 1 1 1 render +wg 1 1 1 rendered +wg 1 1 1 rep +wg 1 1 1 repl +wg 1 1 1 replica +wg 1 1 1 replicas +wg 1 1 1 replicate +wg 1 1 1 replicated +wg 1 1 1 replication +wg 1 1 1 replicator +wg 1 1 1 reply +wg 1 1 1 report +wg 1 1 1 reporting +wg 1 1 1 reports +wg 1 1 1 reprints +wg 1 1 1 req +wg 1 1 1 reqs +wg 1 1 1 request +wg 1 1 1 requests +wg 1 1 1 requisition +wg 1 1 1 requisitions +wg 1 1 1 res +wg 1 1 1 research +wg 1 1 1 resin +wg 1 1 1 resize +wg 1 1 1 resolution +wg 1 1 1 resolve +wg 1 1 1 resolved +wg 1 1 1 resource +wg 1 1 1 resources +wg 1 1 1 rest +wg 1 1 1 restore +wg 1 1 1 restored +wg 1 1 1 restricted +wg 1 1 1 result +wg 1 1 1 results +wg 1 1 1 retail +wg 1 1 1 reverse +wg 1 1 1 reversed +wg 1 1 1 revert +wg 1 1 1 reverted +wg 1 1 1 review +wg 1 1 1 reviews +ws 1 1 1 rhtml +wg 1 1 1 right +wg 1 1 1 roam +wg 1 1 1 roaming +wg 1 1 1 robot +wg 1 1 1 robots +wg 1 1 1 roller +wg 1 1 1 room +wg 1 1 1 root +wg 1 1 1 rpc +wg 1 1 1 rsa +ws 1 1 1 rtf +wg 1 1 1 ru +wg 1 1 1 ruby +wg 1 1 1 rule +wg 1 1 1 rules +wg 1 1 1 run +wg 1 1 1 rwservlet +wg 1 1 1 s +wg 1 1 1 sale +wg 1 1 1 sales +wg 1 1 1 salesforce +wg 1 1 1 sam +wg 1 1 1 samba +wg 1 1 1 saml +wg 1 1 1 sample +wg 1 1 1 samples +wg 1 1 1 san +wg 1 1 1 sav +wg 1 1 1 save +wg 1 1 1 saved +wg 1 1 1 saves +wg 1 1 1 sbin +wg 1 1 1 scan +wg 1 1 1 scanned +wg 1 1 1 scans +wg 1 1 1 sched +wg 1 1 1 schedule +wg 1 1 1 scheduled +wg 1 1 1 scheduling +wg 1 1 1 schema +wg 1 1 1 science +wg 1 1 1 screen +wg 1 1 1 screens +wg 1 1 1 screenshot +wg 1 1 1 screenshots +wg 1 1 1 script +wg 1 1 1 scriptlet +wg 1 1 1 scriptlets +wg 1 1 1 scripts +wg 1 1 1 sdk +wg 1 1 1 se +wg 1 1 1 search +wg 1 1 1 sec +wg 1 1 1 second +wg 1 1 1 secret +wg 1 1 1 section +wg 1 1 1 sections +wg 1 1 1 secure +wg 1 1 1 secured +wg 1 1 1 security +wg 1 1 1 seed +wg 1 1 1 select +wg 1 1 1 sell +wg 1 1 1 send +wg 1 1 1 sendmail +wg 1 1 1 sendto +wg 1 1 1 sent +wg 1 1 1 serial +wg 1 1 1 serv +wg 1 1 1 serve +wg 1 1 1 server +ws 1 1 1 server-info +ws 1 1 1 server-status +wg 1 1 1 servers +wg 1 1 1 service +wg 1 1 1 services +wg 1 1 1 servlet +wg 1 1 1 servlets +wg 1 1 1 session +wg 1 1 1 sessions +wg 1 1 1 setting +wg 1 1 1 settings +wg 1 1 1 setup +wg 1 1 1 sex +wg 1 1 1 shadow +wg 1 1 1 share +wg 1 1 1 shared +wg 1 1 1 shares +wg 1 1 1 shell +wg 1 1 1 ship +wg 1 1 1 shipped +wg 1 1 1 shipping +wg 1 1 1 shockwave +wg 1 1 1 shop +wg 1 1 1 shopper +wg 1 1 1 shopping +wg 1 1 1 shops +wg 1 1 1 shoutbox +wg 1 1 1 show +wg 1 1 1 show_post +wg 1 1 1 show_thread +wg 1 1 1 showcat +wg 1 1 1 showenv +wg 1 1 1 showjobs +wg 1 1 1 showmap +wg 1 1 1 showmsg +wg 1 1 1 showpost +wg 1 1 1 showthread +wg 1 1 1 sign +wg 1 1 1 signed +wg 1 1 1 signer +wg 1 1 1 signin +wg 1 1 1 signing +wg 1 1 1 signoff +wg 1 1 1 signon +wg 1 1 1 signout +wg 1 1 1 signup +wg 1 1 1 simple +wg 1 1 1 sink +wg 1 1 1 site +ws 1 1 1 site-map +ws 1 1 1 site_map +wg 1 1 1 sitemap +wg 1 1 1 sites +wg 1 1 1 skel +wg 1 1 1 skin +wg 1 1 1 skins +wg 1 1 1 skip +wg 1 1 1 sl +wg 1 1 1 sling +wg 1 1 1 sm +wg 1 1 1 small +wg 1 1 1 smile +wg 1 1 1 smiles +wg 1 1 1 sms +wg 1 1 1 smtp +wg 1 1 1 snoop +ws 1 1 1 so +wg 1 1 1 soap +wg 1 1 1 soaprouter +wg 1 1 1 soft +wg 1 1 1 software +wg 1 1 1 solaris +wg 1 1 1 sold +wg 1 1 1 solution +wg 1 1 1 solutions +wg 1 1 1 solve +wg 1 1 1 solved +wg 1 1 1 source +wg 1 1 1 sources +wg 1 1 1 sox +wg 1 1 1 sp +wg 1 1 1 space +wg 1 1 1 spacer +wg 1 1 1 spam +wg 1 1 1 special +wg 1 1 1 specials +wg 1 1 1 sponsor +wg 1 1 1 sponsors +wg 1 1 1 spool +wg 1 1 1 sport +wg 1 1 1 sports +wg 1 1 1 sqlnet +wg 1 1 1 squirrel +wg 1 1 1 squirrelmail +wg 1 1 1 src +wg 1 1 1 srv +wg 1 1 1 ss +wg 1 1 1 ssh +wg 1 1 1 ssi +wg 1 1 1 ssl +ws 1 1 1 sslvpn +wg 1 1 1 ssn +wg 1 1 1 sso +wg 1 1 1 staff +wg 1 1 1 staging +wg 1 1 1 standalone +wg 1 1 1 standard +wg 1 1 1 standards +wg 1 1 1 star +wg 1 1 1 start +wg 1 1 1 stat +wg 1 1 1 statement +wg 1 1 1 statements +wg 1 1 1 static +wg 1 1 1 staticpages +wg 1 1 1 statistic +wg 1 1 1 statistics +wg 1 1 1 stats +wg 1 1 1 status +wg 1 1 1 stock +wg 1 1 1 storage +wg 1 1 1 store +wg 1 1 1 stored +wg 1 1 1 stores +wg 1 1 1 stories +wg 1 1 1 story +wg 1 1 1 strut +wg 1 1 1 struts +wg 1 1 1 student +wg 1 1 1 students +wg 1 1 1 stuff +wg 1 1 1 style +wg 1 1 1 styles +wg 1 1 1 submissions +wg 1 1 1 submit +wg 1 1 1 subscribe +wg 1 1 1 subscribed +wg 1 1 1 subscriber +wg 1 1 1 subscribers +wg 1 1 1 subscription +wg 1 1 1 subscriptions +wg 1 1 1 success +wg 1 1 1 suite +wg 1 1 1 suites +wg 1 1 1 sun +wg 1 1 1 sunos +wg 1 1 1 super +wg 1 1 1 support +wg 1 1 1 surf +wg 1 1 1 survey +wg 1 1 1 surveys +ws 1 1 1 swf +wg 1 1 1 sws +wg 1 1 1 synapse +wg 1 1 1 sync +wg 1 1 1 synced +wg 1 1 1 sys +wg 1 1 1 sysmanager +wg 1 1 1 system +wg 1 1 1 systems +wg 1 1 1 sysuser +wg 1 1 1 t +wg 1 1 1 tag +wg 1 1 1 tags +wg 1 1 1 tail +wg 1 1 1 tape +wg 1 1 1 tapes +wg 1 1 1 tapestry +wg 1 1 1 tar +wg 1 1 1 tar.bz2 +wg 1 1 1 tb +wg 1 1 1 tcl +wg 1 1 1 team +wg 1 1 1 tech +wg 1 1 1 technical +wg 1 1 1 technology +wg 1 1 1 tel +wg 1 1 1 tele +wg 1 1 1 templ +wg 1 1 1 template +wg 1 1 1 templates +wg 1 1 1 terms +ws 1 1 1 test-cgi +ws 1 1 1 test-env +wg 1 1 1 test1 +wg 1 1 1 test123 +wg 1 1 1 test1234 +wg 1 1 1 test2 +wg 1 1 1 test3 +wg 1 1 1 testimonial +wg 1 1 1 testimonials +wg 1 1 1 testing +wg 1 1 1 tests +wg 1 1 1 texis +wg 1 1 1 text +ws 1 1 1 text-base +wg 1 1 1 texts +wg 1 1 1 theme +wg 1 1 1 themes +wg 1 1 1 thread +wg 1 1 1 threads +wg 1 1 1 thumb +wg 1 1 1 thumbnail +wg 1 1 1 thumbnails +wg 1 1 1 thumbs +wg 1 1 1 tickets +wg 1 1 1 tiki +wg 1 1 1 tiles +wg 1 1 1 tip +wg 1 1 1 tips +wg 1 1 1 title +wg 1 1 1 tls +wg 1 1 1 tmpl +wg 1 1 1 tmps +wg 1 1 1 tn +wg 1 1 1 toc +wg 1 1 1 todo +wg 1 1 1 toggle +wg 1 1 1 tomcat +wg 1 1 1 tool +wg 1 1 1 toolbar +wg 1 1 1 toolkit +wg 1 1 1 tools +wg 1 1 1 top +wg 1 1 1 topic +wg 1 1 1 topics +wg 1 1 1 torrent +wg 1 1 1 torrents +wg 1 1 1 tos +wg 1 1 1 tour +ws 1 1 1 tpl +wg 1 1 1 tpv +wg 1 1 1 tr +wg 1 1 1 trace +wg 1 1 1 traceroute +wg 1 1 1 traces +wg 1 1 1 track +wg 1 1 1 trackback +wg 1 1 1 tracker +wg 1 1 1 trackers +wg 1 1 1 tracking +wg 1 1 1 tracks +wg 1 1 1 traffic +wg 1 1 1 trailer +wg 1 1 1 trailers +wg 1 1 1 training +wg 1 1 1 trans +wg 1 1 1 transaction +wg 1 1 1 transactions +wg 1 1 1 transparent +wg 1 1 1 transport +wg 1 1 1 trash +wg 1 1 1 travel +wg 1 1 1 treasury +wg 1 1 1 tree +wg 1 1 1 trees +wg 1 1 1 trial +wg 1 1 1 true +wg 1 1 1 trunk +wg 1 1 1 tsweb +wg 1 1 1 tt +wg 1 1 1 turbine +wg 1 1 1 tuscany +wg 1 1 1 tutorial +wg 1 1 1 tutorials +wg 1 1 1 tv +wg 1 1 1 tweak +wg 1 1 1 twitter +wg 1 1 1 type +ws 1 1 1 typo3 +ws 1 1 1 typo3conf +wg 1 1 1 u +wg 1 1 1 ubb +wg 1 1 1 uds +wg 1 1 1 uk +wg 1 1 1 umts +wg 1 1 1 union +wg 1 1 1 unix +wg 1 1 1 unlock +wg 1 1 1 unpaid +wg 1 1 1 unreg +wg 1 1 1 unregister +wg 1 1 1 unsubscribe +wg 1 1 1 up +wg 1 1 1 upd +wg 1 1 1 update +wg 1 1 1 updated +wg 1 1 1 updater +wg 1 1 1 updates +wg 1 1 1 upload +wg 1 1 1 uploader +wg 1 1 1 uploads +wg 1 1 1 url +wg 1 1 1 urls +wg 1 1 1 us +wg 1 1 1 usa +wg 1 1 1 usage +wg 1 1 1 user +wg 1 1 1 userlog +wg 1 1 1 users +wg 1 1 1 usr +wg 1 1 1 util +wg 1 1 1 utilities +wg 1 1 1 utility +wg 1 1 1 utils +wg 1 1 1 v +wg 1 1 1 v1 +wg 1 1 1 v2 +wg 1 1 1 var +wg 1 1 1 vault +wg 1 1 1 vector +wg 1 1 1 velocity +wg 1 1 1 vendor +wg 1 1 1 vendors +wg 1 1 1 ver +wg 1 1 1 ver1 +wg 1 1 1 ver2 +wg 1 1 1 version +wg 1 1 1 vfs +wg 1 1 1 video +wg 1 1 1 videos +wg 1 1 1 view +wg 1 1 1 view-source +wg 1 1 1 viewcvs +wg 1 1 1 viewforum +wg 1 1 1 viewonline +wg 1 1 1 views +wg 1 1 1 viewsource +wg 1 1 1 viewsvn +wg 1 1 1 viewtopic +wg 1 1 1 viewvc +wg 1 1 1 virtual +wg 1 1 1 vm +wg 1 1 1 voip +wg 1 1 1 vol +wg 1 1 1 vote +wg 1 1 1 voter +wg 1 1 1 votes +wg 1 1 1 vpn +wg 1 1 1 vuln +wg 1 1 1 w +wg 1 1 1 w3 +wg 1 1 1 w3c +wg 1 1 1 wa +wg 1 1 1 wap +ws 1 1 1 war +wg 1 1 1 warez +ws 1 1 1 way-board +wg 1 1 1 wbboard +wg 1 1 1 wc +wg 1 1 1 weather +wg 1 1 1 web +wg 1 1 1 web-beans +wg 1 1 1 web-console +wg 1 1 1 webaccess +wg 1 1 1 webadmin +wg 1 1 1 webagent +wg 1 1 1 webalizer +wg 1 1 1 webapp +wg 1 1 1 webb +wg 1 1 1 webbbs +wg 1 1 1 webboard +wg 1 1 1 webcalendar +wg 1 1 1 webcart +wg 1 1 1 webcasts +wg 1 1 1 webcgi +wg 1 1 1 webchat +wg 1 1 1 webdata +wg 1 1 1 webdav +wg 1 1 1 webdb +wg 1 1 1 weblog +wg 1 1 1 weblogic +wg 1 1 1 weblogs +wg 1 1 1 webmail +wg 1 1 1 webplus +wg 1 1 1 webshop +wg 1 1 1 website +wg 1 1 1 websphere +wg 1 1 1 websql +wg 1 1 1 webstats +wg 1 1 1 websvn +wg 1 1 1 webwork +wg 1 1 1 week +wg 1 1 1 weekly +wg 1 1 1 welcome +wg 1 1 1 whitepapers +wg 1 1 1 whois +wg 1 1 1 whosonline +wg 1 1 1 wicket +wg 1 1 1 wiki +wg 1 1 1 win +ws 1 1 1 win32 +wg 1 1 1 windows +ws 1 1 1 winnt +wg 1 1 1 wireless +wg 1 1 1 wml +wg 1 1 1 word +wg 1 1 1 wordpress +wg 1 1 1 work +wg 1 1 1 working +wg 1 1 1 world +wg 1 1 1 wow +wg 1 1 1 wp +ws 1 1 1 wp-content +ws 1 1 1 wp-dbmanager +ws 1 1 1 wp-includes +ws 1 1 1 wp-login +ws 1 1 1 wp-syntax +wg 1 1 1 wrap +ws 1 1 1 ws-client +ws 1 1 1 ws_ftp +wg 1 1 1 wsdl +wg 1 1 1 wtai +wg 1 1 1 www +wg 1 1 1 www-sql +wg 1 1 1 www1 +wg 1 1 1 www2 +wg 1 1 1 www3 +wg 1 1 1 wwwboard +wg 1 1 1 wwwroot +wg 1 1 1 wwwstats +wg 1 1 1 wwwthreads +wg 1 1 1 wwwuser +wg 1 1 1 wysiwyg +wg 1 1 1 x +wg 1 1 1 xalan +wg 1 1 1 xerces +wg 1 1 1 xhtml +wg 1 1 1 xmlrpc +wg 1 1 1 xsql +wg 1 1 1 xxx +wg 1 1 1 xyzzy +wg 1 1 1 y +wg 1 1 1 yahoo +wg 1 1 1 year +wg 1 1 1 yearly +ws 1 1 1 yml +wg 1 1 1 youtube +wg 1 1 1 yt +wg 1 1 1 z +wg 1 1 1 zboard +wg 1 1 1 zencart +wg 1 1 1 zend +wg 1 1 1 zero +wg 1 1 1 zimbra +wg 1 1 1 zipfiles +wg 1 1 1 zips +wg 1 1 1 zoom +wg 1 1 1 zope +wg 1 1 1 zorum +ws 1 1 1 ~admin +ws 1 1 1 ~amanda +ws 1 1 1 ~apache +ws 1 1 1 ~ashley +ws 1 1 1 ~bin +ws 1 1 1 ~bob +ws 1 1 1 ~chris +ws 1 1 1 ~dan +ws 1 1 1 ~eric +ws 1 1 1 ~ftp +ws 1 1 1 ~guest +ws 1 1 1 ~http +ws 1 1 1 ~httpd +ws 1 1 1 ~jacob +ws 1 1 1 ~jennifer +ws 1 1 1 ~jessica +ws 1 1 1 ~john +ws 1 1 1 ~log +ws 1 1 1 ~logs +ws 1 1 1 ~lp +ws 1 1 1 ~mark +ws 1 1 1 ~matt +ws 1 1 1 ~michael +ws 1 1 1 ~nobody +ws 1 1 1 ~root +ws 1 1 1 ~test +ws 1 1 1 ~tmp +ws 1 1 1 ~www diff -Nru skipfish-2.02b/dictionaries/minimal.wl skipfish-2.10b/dictionaries/minimal.wl --- skipfish-2.02b/dictionaries/minimal.wl 2011-07-03 05:59:47.000000000 +0000 +++ skipfish-2.10b/dictionaries/minimal.wl 2012-12-04 13:27:53.000000000 +0000 @@ -1,392 +1,393 @@ -e 1 1 1 bak -e 1 1 1 cfg -e 1 1 1 class -e 1 1 1 cnf -e 1 1 1 conf -e 1 1 1 config -e 1 1 1 csv -e 1 1 1 err -e 1 1 1 error -e 1 1 1 html -e 1 1 1 inc -e 1 1 1 ini -e 1 1 1 jar -e 1 1 1 java -e 1 1 1 key -e 1 1 1 log -e 1 1 1 old -e 1 1 1 orig -e 1 1 1 out -e 1 1 1 part -e 1 1 1 pl -e 1 1 1 sql -e 1 1 1 svn-base -e 1 1 1 temp -e 1 1 1 test -e 1 1 1 tmp -e 1 1 1 txt -e 1 1 1 xml -e 1 1 1 xslt -e 1 1 1 zip -w 1 1 1 .bash_history -w 1 1 1 .bashrc -w 1 1 1 .cvsignore -w 1 1 1 .history -w 1 1 1 .htaccess -w 1 1 1 .htpasswd -w 1 1 1 .passwd -w 1 1 1 .perf -w 1 1 1 .ssh -w 1 1 1 .svn -w 1 1 1 .web -w 1 1 1 0 -w 1 1 1 00 -w 1 1 1 01 -w 1 1 1 02 -w 1 1 1 03 -w 1 1 1 04 -w 1 1 1 05 -w 1 1 1 06 -w 1 1 1 07 -w 1 1 1 08 -w 1 1 1 09 -w 1 1 1 1 -w 1 1 1 10 -w 1 1 1 100 -w 1 1 1 1000 -w 1 1 1 1001 -w 1 1 1 101 -w 1 1 1 11 -w 1 1 1 12 -w 1 1 1 13 -w 1 1 1 14 -w 1 1 1 15 -w 1 1 1 1990 -w 1 1 1 1991 -w 1 1 1 1992 -w 1 1 1 1993 -w 1 1 1 1994 -w 1 1 1 1995 -w 1 1 1 1996 -w 1 1 1 1997 -w 1 1 1 1998 -w 1 1 1 1999 -w 1 1 1 2 -w 1 1 1 20 -w 1 1 1 200 -w 1 1 1 2000 -w 1 1 1 2001 -w 1 1 1 2002 -w 1 1 1 2003 -w 1 1 1 2004 -w 1 1 1 2005 -w 1 1 1 2006 -w 1 1 1 2007 -w 1 1 1 2008 -w 1 1 1 2009 -w 1 1 1 2010 -w 1 1 1 2011 -w 1 1 1 2012 -w 1 1 1 2013 -w 1 1 1 2014 -w 1 1 1 21 -w 1 1 1 22 -w 1 1 1 23 -w 1 1 1 24 -w 1 1 1 25 -w 1 1 1 2g -w 1 1 1 3 -w 1 1 1 300 -w 1 1 1 3g -w 1 1 1 4 -w 1 1 1 42 -w 1 1 1 5 -w 1 1 1 50 -w 1 1 1 500 -w 1 1 1 51 -w 1 1 1 6 -w 1 1 1 7 -w 1 1 1 7z -w 1 1 1 8 -w 1 1 1 9 -w 1 1 1 ADM -w 1 1 1 ADMIN -w 1 1 1 Admin -w 1 1 1 AdminService -w 1 1 1 AggreSpy -w 1 1 1 AppsLocalLogin -w 1 1 1 AppsLogin -w 1 1 1 BUILD -w 1 1 1 CMS -w 1 1 1 CVS -w 1 1 1 DB -w 1 1 1 DMSDump -w 1 1 1 Documents and Settings -w 1 1 1 Entries -w 1 1 1 FCKeditor -w 1 1 1 JMXSoapAdapter -w 1 1 1 LICENSE -w 1 1 1 MANIFEST.MF -w 1 1 1 META-INF -w 1 1 1 Makefile -w 1 1 1 OA -w 1 1 1 OAErrorDetailPage -w 1 1 1 OA_HTML -w 1 1 1 Program Files -w 1 1 1 README -w 1 1 1 Rakefile -w 1 1 1 Readme -w 1 1 1 Recycled -w 1 1 1 Root -w 1 1 1 SERVER-INF -w 1 1 1 SOAPMonitor -w 1 1 1 SQL -w 1 1 1 SUNWmc -w 1 1 1 SiteScope -w 1 1 1 SiteServer -w 1 1 1 Spy -w 1 1 1 TEMP -w 1 1 1 TMP -w 1 1 1 TODO -w 1 1 1 Thumbs.db -w 1 1 1 WEB-INF -w 1 1 1 WS_FTP -w 1 1 1 XXX -w 1 1 1 _ -w 1 1 1 _adm -w 1 1 1 _admin -w 1 1 1 _common -w 1 1 1 _conf -w 1 1 1 _files -w 1 1 1 _include -w 1 1 1 _js -w 1 1 1 _mem_bin -w 1 1 1 _old -w 1 1 1 _pages -w 1 1 1 _private -w 1 1 1 _res -w 1 1 1 _source -w 1 1 1 _src -w 1 1 1 _test -w 1 1 1 _vti_bin -w 1 1 1 _vti_cnf -w 1 1 1 _vti_pvt -w 1 1 1 _vti_txt -w 1 1 1 _www -w 1 1 1 a -w 1 1 1 aa -w 1 1 1 aaa -w 1 1 1 abc -w 1 1 1 abc123 -w 1 1 1 abcd -w 1 1 1 abcd1234 -w 1 1 1 about -w 1 1 1 access -w 1 1 1 access-log -w 1 1 1 access-log.1 -w 1 1 1 access.1 -w 1 1 1 access_log -w 1 1 1 access_log.1 -w 1 1 1 accessibility -w 1 1 1 account -w 1 1 1 accounting -w 1 1 1 accounts -w 1 1 1 action -w 1 1 1 actions -w 1 1 1 active -w 1 1 1 activex -w 1 1 1 ad -w 1 1 1 adclick -w 1 1 1 add -w 1 1 1 addpost -w 1 1 1 addressbook -w 1 1 1 adm -w 1 1 1 admin -w 1 1 1 admin-console -w 1 1 1 admin_ -w 1 1 1 admins -w 1 1 1 adobe -w 1 1 1 adodb -w 1 1 1 ads -w 1 1 1 adv -w 1 1 1 advanced -w 1 1 1 advertise -w 1 1 1 advertising -w 1 1 1 affiliate -w 1 1 1 affiliates -w 1 1 1 agenda -w 1 1 1 agent -w 1 1 1 agents -w 1 1 1 ajax -w 1 1 1 akamai -w 1 1 1 album -w 1 1 1 albums -w 1 1 1 alcatel -w 1 1 1 alert -w 1 1 1 alerts -w 1 1 1 alias -w 1 1 1 aliases -w 1 1 1 all -w 1 1 1 all-wcprops -w 1 1 1 alpha -w 1 1 1 alumni -w 1 1 1 amanda -w 1 1 1 amazon -w 1 1 1 analog -w 1 1 1 android -w 1 1 1 announcement -w 1 1 1 announcements -w 1 1 1 annual -w 1 1 1 anon -w 1 1 1 anonymous -w 1 1 1 ansi -w 1 1 1 apac -w 1 1 1 apache -w 1 1 1 apexec -w 1 1 1 api -w 1 1 1 apis -w 1 1 1 app -w 1 1 1 appeal -w 1 1 1 appeals -w 1 1 1 append -w 1 1 1 appl -w 1 1 1 apple -w 1 1 1 appliation -w 1 1 1 applications -w 1 1 1 apps -w 1 1 1 apr -w 1 1 1 arch -w 1 1 1 archive -w 1 1 1 archives -w 1 1 1 array -w 1 1 1 art -w 1 1 1 article -w 1 1 1 articles -w 1 1 1 artwork -w 1 1 1 as -w 1 1 1 ascii -w 1 1 1 asdf -w 1 1 1 ashley -w 1 1 1 asmx -w 1 1 1 asp -w 1 1 1 aspx -w 1 1 1 asset -w 1 1 1 assets -w 1 1 1 atom -w 1 1 1 attach -w 1 1 1 attachment -w 1 1 1 attachments -w 1 1 1 attachs -w 1 1 1 attic -w 1 1 1 auction -w 1 1 1 audio -w 1 1 1 audit -w 1 1 1 audits -w 1 1 1 auth -w 1 1 1 author -w 1 1 1 authorized_keys -w 1 1 1 authors -w 1 1 1 auto -w 1 1 1 automatic -w 1 1 1 automation -w 1 1 1 avatar -w 1 1 1 avatars -w 1 1 1 award -w 1 1 1 awards -w 1 1 1 awl -w 1 1 1 awstats -w 1 1 1 axis -w 1 1 1 axis-admin -w 1 1 1 axis2 -w 1 1 1 axis2-admin -w 1 1 1 b -w 1 1 1 b2b -w 1 1 1 b2c -w 1 1 1 back -w 1 1 1 backdoor -w 1 1 1 backend -w 1 1 1 backup -w 1 1 1 backups -w 1 1 1 balance -w 1 1 1 balances -w 1 1 1 bandwidth -w 1 1 1 bank -w 1 1 1 banking -w 1 1 1 banks -w 1 1 1 banner -w 1 1 1 banners -w 1 1 1 bar -w 1 1 1 base -w 1 1 1 bash -w 1 1 1 basic -w 1 1 1 basket -w 1 1 1 baskets -w 1 1 1 bat -w 1 1 1 batch -w 1 1 1 baz -w 1 1 1 bb -w 1 1 1 bb-hist -w 1 1 1 bb-histlog -w 1 1 1 bboard -w 1 1 1 bbs -w 1 1 1 bean -w 1 1 1 beans -w 1 1 1 beehive -w 1 1 1 benefits -w 1 1 1 beta -w 1 1 1 bfc -w 1 1 1 big -w 1 1 1 bigip -w 1 1 1 bill -w 1 1 1 billing -w 1 1 1 bin -w 1 1 1 binaries -w 1 1 1 binary -w 1 1 1 bins -w 1 1 1 bio -w 1 1 1 bios -w 1 1 1 biz -w 1 1 1 bkup -w 1 1 1 blah -w 1 1 1 blank -w 1 1 1 blog -w 1 1 1 blogger -w 1 1 1 bloggers -w 1 1 1 blogs -w 1 1 1 blogspot -w 1 1 1 board -w 1 1 1 boards -w 1 1 1 bob -w 1 1 1 bofh -w 1 1 1 book -w 1 1 1 books -w 1 1 1 boot -w 1 1 1 bottom -w 1 1 1 broken -w 1 1 1 broker -w 1 1 1 browse -w 1 1 1 browser -w 1 1 1 bs -w 1 1 1 bsd -w 1 1 1 bug -w 1 1 1 bugs -w 1 1 1 build -w 1 1 1 builder -w 1 1 1 buildr -w 1 1 1 bulk -w 1 1 1 bullet -w 1 1 1 business -w 1 1 1 button -w 1 1 1 buttons -w 1 1 1 buy -w 1 1 1 buynow -w 1 1 1 bypass -w 1 1 1 bz2 -w 1 1 1 c -w 1 1 1 ca -w 1 1 1 cache -w 1 1 1 cal -w 1 1 1 calendar +#ro +eg 1 1 1 bak +eg 1 1 1 cfg +es 1 1 1 class +eg 1 1 1 cnf +eg 1 1 1 conf +eg 1 1 1 config +eg 1 1 1 csv +eg 1 1 1 err +eg 1 1 1 error +es 1 1 1 html +es 1 1 1 inc +es 1 1 1 ini +es 1 1 1 jar +es 1 1 1 java +eg 1 1 1 key +eg 1 1 1 log +eg 1 1 1 old +eg 1 1 1 orig +eg 1 1 1 out +eg 1 1 1 part +es 1 1 1 pl +es 1 1 1 sql +es 1 1 1 svn-base +eg 1 1 1 temp +eg 1 1 1 test +eg 1 1 1 tmp +eg 1 1 1 txt +eg 1 1 1 xml +es 1 1 1 xslt +eg 1 1 1 zip +ws 1 1 1 .bash_history +ws 1 1 1 .bashrc +ws 1 1 1 .cvsignore +ws 1 1 1 .history +ws 1 1 1 .htaccess +ws 1 1 1 .htpasswd +ws 1 1 1 .passwd +ws 1 1 1 .perf +ws 1 1 1 .ssh +ws 1 1 1 .svn +ws 1 1 1 .web +wg 1 1 1 0 +wg 1 1 1 00 +wg 1 1 1 01 +wg 1 1 1 02 +wg 1 1 1 03 +wg 1 1 1 04 +wg 1 1 1 05 +wg 1 1 1 06 +wg 1 1 1 07 +wg 1 1 1 08 +wg 1 1 1 09 +wg 1 1 1 1 +wg 1 1 1 10 +wg 1 1 1 100 +wg 1 1 1 1000 +wg 1 1 1 1001 +wg 1 1 1 101 +wg 1 1 1 11 +wg 1 1 1 12 +wg 1 1 1 13 +wg 1 1 1 14 +wg 1 1 1 15 +wg 1 1 1 1990 +wg 1 1 1 1991 +wg 1 1 1 1992 +wg 1 1 1 1993 +wg 1 1 1 1994 +wg 1 1 1 1995 +wg 1 1 1 1996 +wg 1 1 1 1997 +wg 1 1 1 1998 +wg 1 1 1 1999 +wg 1 1 1 2 +wg 1 1 1 20 +wg 1 1 1 200 +wg 1 1 1 2000 +wg 1 1 1 2001 +wg 1 1 1 2002 +wg 1 1 1 2003 +wg 1 1 1 2004 +wg 1 1 1 2005 +wg 1 1 1 2006 +wg 1 1 1 2007 +wg 1 1 1 2008 +wg 1 1 1 2009 +wg 1 1 1 2010 +wg 1 1 1 2011 +wg 1 1 1 2012 +wg 1 1 1 2013 +wg 1 1 1 2014 +wg 1 1 1 21 +wg 1 1 1 22 +wg 1 1 1 23 +wg 1 1 1 24 +wg 1 1 1 25 +wg 1 1 1 2g +wg 1 1 1 3 +wg 1 1 1 300 +wg 1 1 1 3g +wg 1 1 1 4 +wg 1 1 1 42 +wg 1 1 1 5 +wg 1 1 1 50 +wg 1 1 1 500 +wg 1 1 1 51 +wg 1 1 1 6 +wg 1 1 1 7 +wg 1 1 1 7z +wg 1 1 1 8 +wg 1 1 1 9 +wg 1 1 1 ADM +wg 1 1 1 ADMIN +wg 1 1 1 Admin +wg 1 1 1 AdminService +wg 1 1 1 AggreSpy +wg 1 1 1 AppsLocalLogin +wg 1 1 1 AppsLogin +ws 1 1 1 BUILD +wg 1 1 1 CMS +wg 1 1 1 CVS +wg 1 1 1 DB +wg 1 1 1 DMSDump +ws 1 1 1 Documents and Settings +ws 1 1 1 Entries +wg 1 1 1 FCKeditor +wg 1 1 1 JMXSoapAdapter +wg 1 1 1 LICENSE +ws 1 1 1 MANIFEST.MF +ws 1 1 1 META-INF +ws 1 1 1 Makefile +wg 1 1 1 OA +wg 1 1 1 OAErrorDetailPage +ws 1 1 1 OA_HTML +ws 1 1 1 Program Files +wg 1 1 1 README +ws 1 1 1 Rakefile +wg 1 1 1 Readme +ws 1 1 1 Recycled +wg 1 1 1 Root +ws 1 1 1 SERVER-INF +wg 1 1 1 SOAPMonitor +wg 1 1 1 SQL +wg 1 1 1 SUNWmc +wg 1 1 1 SiteScope +wg 1 1 1 SiteServer +wg 1 1 1 Spy +wg 1 1 1 TEMP +wg 1 1 1 TMP +wg 1 1 1 TODO +ws 1 1 1 Thumbs.db +ws 1 1 1 WEB-INF +ws 1 1 1 WS_FTP +wg 1 1 1 XXX +ws 1 1 1 _ +ws 1 1 1 _adm +ws 1 1 1 _admin +ws 1 1 1 _common +ws 1 1 1 _conf +ws 1 1 1 _files +ws 1 1 1 _include +ws 1 1 1 _js +ws 1 1 1 _mem_bin +ws 1 1 1 _old +ws 1 1 1 _pages +ws 1 1 1 _private +ws 1 1 1 _res +ws 1 1 1 _source +ws 1 1 1 _src +ws 1 1 1 _test +ws 1 1 1 _vti_bin +ws 1 1 1 _vti_cnf +ws 1 1 1 _vti_pvt +ws 1 1 1 _vti_txt +ws 1 1 1 _www +wg 1 1 1 a +wg 1 1 1 aa +wg 1 1 1 aaa +wg 1 1 1 abc +wg 1 1 1 abc123 +wg 1 1 1 abcd +wg 1 1 1 abcd1234 +wg 1 1 1 about +wg 1 1 1 access +ws 1 1 1 access-log +ws 1 1 1 access-log.1 +ws 1 1 1 access.1 +ws 1 1 1 access_log +ws 1 1 1 access_log.1 +wg 1 1 1 accessibility +wg 1 1 1 account +wg 1 1 1 accounting +wg 1 1 1 accounts +wg 1 1 1 action +wg 1 1 1 actions +wg 1 1 1 active +wg 1 1 1 activex +wg 1 1 1 ad +wg 1 1 1 adclick +wg 1 1 1 add +wg 1 1 1 addpost +wg 1 1 1 addressbook +wg 1 1 1 adm +wg 1 1 1 admin +wg 1 1 1 admin-console +ws 1 1 1 admin_ +wg 1 1 1 admins +wg 1 1 1 adobe +wg 1 1 1 adodb +wg 1 1 1 ads +wg 1 1 1 adv +wg 1 1 1 advanced +wg 1 1 1 advertise +wg 1 1 1 advertising +wg 1 1 1 affiliate +wg 1 1 1 affiliates +wg 1 1 1 agenda +wg 1 1 1 agent +wg 1 1 1 agents +wg 1 1 1 ajax +wg 1 1 1 akamai +wg 1 1 1 album +wg 1 1 1 albums +wg 1 1 1 alcatel +wg 1 1 1 alert +wg 1 1 1 alerts +wg 1 1 1 alias +wg 1 1 1 aliases +wg 1 1 1 all +ws 1 1 1 all-wcprops +wg 1 1 1 alpha +wg 1 1 1 alumni +wg 1 1 1 amanda +wg 1 1 1 amazon +wg 1 1 1 analog +wg 1 1 1 android +wg 1 1 1 announcement +wg 1 1 1 announcements +wg 1 1 1 annual +wg 1 1 1 anon +wg 1 1 1 anonymous +wg 1 1 1 ansi +wg 1 1 1 apac +wg 1 1 1 apache +wg 1 1 1 apexec +wg 1 1 1 api +wg 1 1 1 apis +wg 1 1 1 app +wg 1 1 1 appeal +wg 1 1 1 appeals +wg 1 1 1 append +wg 1 1 1 appl +wg 1 1 1 apple +wg 1 1 1 appliation +wg 1 1 1 applications +wg 1 1 1 apps +wg 1 1 1 apr +wg 1 1 1 arch +wg 1 1 1 archive +wg 1 1 1 archives +wg 1 1 1 array +wg 1 1 1 art +wg 1 1 1 article +wg 1 1 1 articles +wg 1 1 1 artwork +ws 1 1 1 as +wg 1 1 1 ascii +wg 1 1 1 asdf +wg 1 1 1 ashley +ws 1 1 1 asmx +ws 1 1 1 asp +ws 1 1 1 aspx +wg 1 1 1 asset +wg 1 1 1 assets +wg 1 1 1 atom +wg 1 1 1 attach +wg 1 1 1 attachment +wg 1 1 1 attachments +wg 1 1 1 attachs +wg 1 1 1 attic +wg 1 1 1 auction +wg 1 1 1 audio +wg 1 1 1 audit +wg 1 1 1 audits +wg 1 1 1 auth +wg 1 1 1 author +ws 1 1 1 authorized_keys +wg 1 1 1 authors +wg 1 1 1 auto +wg 1 1 1 automatic +wg 1 1 1 automation +wg 1 1 1 avatar +wg 1 1 1 avatars +wg 1 1 1 award +wg 1 1 1 awards +wg 1 1 1 awl +wg 1 1 1 awstats +ws 1 1 1 axis +ws 1 1 1 axis-admin +ws 1 1 1 axis2 +ws 1 1 1 axis2-admin +wg 1 1 1 b +wg 1 1 1 b2b +wg 1 1 1 b2c +wg 1 1 1 back +wg 1 1 1 backdoor +wg 1 1 1 backend +wg 1 1 1 backup +wg 1 1 1 backups +wg 1 1 1 balance +wg 1 1 1 balances +wg 1 1 1 bandwidth +wg 1 1 1 bank +wg 1 1 1 banking +wg 1 1 1 banks +wg 1 1 1 banner +wg 1 1 1 banners +wg 1 1 1 bar +wg 1 1 1 base +wg 1 1 1 bash +wg 1 1 1 basic +wg 1 1 1 basket +wg 1 1 1 baskets +ws 1 1 1 bat +wg 1 1 1 batch +wg 1 1 1 baz +wg 1 1 1 bb +ws 1 1 1 bb-hist +ws 1 1 1 bb-histlog +wg 1 1 1 bboard +wg 1 1 1 bbs +wg 1 1 1 bean +wg 1 1 1 beans +wg 1 1 1 beehive +wg 1 1 1 benefits +wg 1 1 1 beta +wg 1 1 1 bfc +wg 1 1 1 big +wg 1 1 1 bigip +wg 1 1 1 bill +wg 1 1 1 billing +wg 1 1 1 bin +wg 1 1 1 binaries +wg 1 1 1 binary +wg 1 1 1 bins +wg 1 1 1 bio +wg 1 1 1 bios +wg 1 1 1 biz +wg 1 1 1 bkup +wg 1 1 1 blah +wg 1 1 1 blank +wg 1 1 1 blog +wg 1 1 1 blogger +wg 1 1 1 bloggers +wg 1 1 1 blogs +wg 1 1 1 blogspot +wg 1 1 1 board +wg 1 1 1 boards +wg 1 1 1 bob +wg 1 1 1 bofh +wg 1 1 1 book +wg 1 1 1 books +wg 1 1 1 boot +wg 1 1 1 bottom +wg 1 1 1 broken +wg 1 1 1 broker +wg 1 1 1 browse +wg 1 1 1 browser +wg 1 1 1 bs +wg 1 1 1 bsd +wg 1 1 1 bug +wg 1 1 1 bugs +wg 1 1 1 build +wg 1 1 1 builder +wg 1 1 1 buildr +wg 1 1 1 bulk +wg 1 1 1 bullet +wg 1 1 1 business +wg 1 1 1 button +wg 1 1 1 buttons +wg 1 1 1 buy +wg 1 1 1 buynow +wg 1 1 1 bypass +wg 1 1 1 bz2 +ws 1 1 1 c +wg 1 1 1 ca +wg 1 1 1 cache +wg 1 1 1 cal +wg 1 1 1 calendar w 1 1 1 call w 1 1 1 callback w 1 1 1 callee @@ -394,1778 +395,1778 @@ w 1 1 1 callin w 1 1 1 calling w 1 1 1 callout -w 1 1 1 camel -w 1 1 1 car -w 1 1 1 card -w 1 1 1 cards -w 1 1 1 career -w 1 1 1 careers -w 1 1 1 cars -w 1 1 1 cart -w 1 1 1 carts -w 1 1 1 cat -w 1 1 1 catalog -w 1 1 1 catalogs -w 1 1 1 catalyst -w 1 1 1 categories -w 1 1 1 category -w 1 1 1 catinfo -w 1 1 1 cats -w 1 1 1 cc -w 1 1 1 ccbill -w 1 1 1 cd -w 1 1 1 cert -w 1 1 1 certificate -w 1 1 1 certificates -w 1 1 1 certified -w 1 1 1 certs -w 1 1 1 cf -w 1 1 1 cfcache -w 1 1 1 cfdocs -w 1 1 1 cfide -w 1 1 1 cfm -w 1 1 1 cfusion -w 1 1 1 cgi -w 1 1 1 cgi-bin -w 1 1 1 cgi-bin2 -w 1 1 1 cgi-home -w 1 1 1 cgi-local -w 1 1 1 cgi-pub -w 1 1 1 cgi-script -w 1 1 1 cgi-shl -w 1 1 1 cgi-sys -w 1 1 1 cgi-web -w 1 1 1 cgi-win -w 1 1 1 cgibin -w 1 1 1 cgiwrap -w 1 1 1 cgm-web -w 1 1 1 change -w 1 1 1 changed -w 1 1 1 changes -w 1 1 1 charge -w 1 1 1 charges -w 1 1 1 chat -w 1 1 1 chats -w 1 1 1 check -w 1 1 1 checking -w 1 1 1 checkout -w 1 1 1 checkpoint -w 1 1 1 checks -w 1 1 1 child -w 1 1 1 children -w 1 1 1 chris -w 1 1 1 chrome -w 1 1 1 cisco -w 1 1 1 cisweb -w 1 1 1 citrix -w 1 1 1 cl -w 1 1 1 claim -w 1 1 1 claims -w 1 1 1 classes -w 1 1 1 classified -w 1 1 1 classifieds -w 1 1 1 clear -w 1 1 1 click -w 1 1 1 clicks -w 1 1 1 client -w 1 1 1 clientaccesspolicy -w 1 1 1 clients -w 1 1 1 clk -w 1 1 1 clock -w 1 1 1 close -w 1 1 1 closed -w 1 1 1 closing -w 1 1 1 club -w 1 1 1 cluster -w 1 1 1 clusters -w 1 1 1 cmd -w 1 1 1 cms -w 1 1 1 cnt -w 1 1 1 cocoon -w 1 1 1 code -w 1 1 1 codec -w 1 1 1 codecs -w 1 1 1 codes -w 1 1 1 cognos -w 1 1 1 coldfusion -w 1 1 1 columns -w 1 1 1 com -w 1 1 1 comment -w 1 1 1 comments -w 1 1 1 commerce -w 1 1 1 commercial -w 1 1 1 common -w 1 1 1 communicator -w 1 1 1 community -w 1 1 1 compact -w 1 1 1 company -w 1 1 1 compat -w 1 1 1 complaint -w 1 1 1 complaints -w 1 1 1 compliance -w 1 1 1 component -w 1 1 1 components -w 1 1 1 compress -w 1 1 1 compressed -w 1 1 1 computer -w 1 1 1 computers -w 1 1 1 computing -w 1 1 1 conference -w 1 1 1 conferences -w 1 1 1 configs -w 1 1 1 console -w 1 1 1 consumer -w 1 1 1 contact -w 1 1 1 contacts -w 1 1 1 content -w 1 1 1 contents -w 1 1 1 contest -w 1 1 1 contract -w 1 1 1 contracts -w 1 1 1 control -w 1 1 1 controller -w 1 1 1 controlpanel -w 1 1 1 cookie -w 1 1 1 cookies -w 1 1 1 copies -w 1 1 1 copy -w 1 1 1 copyright -w 1 1 1 core -w 1 1 1 corp -w 1 1 1 corpo -w 1 1 1 corporate -w 1 1 1 corrections -w 1 1 1 count -w 1 1 1 counter -w 1 1 1 counters -w 1 1 1 counts -w 1 1 1 course -w 1 1 1 courses -w 1 1 1 cover -w 1 1 1 cpadmin -w 1 1 1 cpanel -w 1 1 1 cpp -w 1 1 1 cr -w 1 1 1 crack -w 1 1 1 crash -w 1 1 1 crashes -w 1 1 1 create -w 1 1 1 creator -w 1 1 1 credit -w 1 1 1 credits -w 1 1 1 crm -w 1 1 1 cron -w 1 1 1 crons -w 1 1 1 crontab -w 1 1 1 crontabs -w 1 1 1 crossdomain -w 1 1 1 crypt -w 1 1 1 crypto -w 1 1 1 cs -w 1 1 1 csproj -w 1 1 1 css -w 1 1 1 current -w 1 1 1 custom -w 1 1 1 custom-log -w 1 1 1 custom_log -w 1 1 1 customer -w 1 1 1 customers -w 1 1 1 cute -w 1 1 1 cv -w 1 1 1 cxf -w 1 1 1 czcmdcvt -w 1 1 1 d -w 1 1 1 daemon -w 1 1 1 daily -w 1 1 1 dan -w 1 1 1 dana-na -w 1 1 1 dat -w 1 1 1 data -w 1 1 1 database -w 1 1 1 databases -w 1 1 1 date -w 1 1 1 day -w 1 1 1 db -w 1 1 1 db_connect -w 1 1 1 dba -w 1 1 1 dbase -w 1 1 1 dblclk -w 1 1 1 dbman -w 1 1 1 dbmodules -w 1 1 1 dbutil -w 1 1 1 dc -w 1 1 1 dcforum -w 1 1 1 dclk -w 1 1 1 de -w 1 1 1 dealer -w 1 1 1 debug -w 1 1 1 decl -w 1 1 1 declaration -w 1 1 1 declarations -w 1 1 1 decode -w 1 1 1 decoder -w 1 1 1 decrypt -w 1 1 1 decrypted -w 1 1 1 decryption -w 1 1 1 def -w 1 1 1 default -w 1 1 1 defaults -w 1 1 1 definition -w 1 1 1 definitions -w 1 1 1 del -w 1 1 1 delete -w 1 1 1 deleted -w 1 1 1 demo -w 1 1 1 demos -w 1 1 1 denied -w 1 1 1 deny -w 1 1 1 design -w 1 1 1 desktop -w 1 1 1 desktops -w 1 1 1 detail -w 1 1 1 details -w 1 1 1 dev -w 1 1 1 devel -w 1 1 1 developer -w 1 1 1 developers -w 1 1 1 development -w 1 1 1 device -w 1 1 1 devices -w 1 1 1 devs -w 1 1 1 df -w 1 1 1 dialog -w 1 1 1 dialogs -w 1 1 1 diff -w 1 1 1 diffs -w 1 1 1 digest -w 1 1 1 digg -w 1 1 1 dir -w 1 1 1 dir-prop-base -w 1 1 1 directories -w 1 1 1 directory -w 1 1 1 dirs -w 1 1 1 disabled -w 1 1 1 disclaimer -w 1 1 1 display -w 1 1 1 django -w 1 1 1 dl -w 1 1 1 dll -w 1 1 1 dm -w 1 1 1 dm-config -w 1 1 1 dms -w 1 1 1 dms0 -w 1 1 1 dns -w 1 1 1 do -w 1 1 1 doc -w 1 1 1 docebo -w 1 1 1 dock -w 1 1 1 docroot -w 1 1 1 docs -w 1 1 1 document -w 1 1 1 documentation -w 1 1 1 documents -w 1 1 1 domain -w 1 1 1 domains -w 1 1 1 donate -w 1 1 1 done -w 1 1 1 doubleclick -w 1 1 1 down -w 1 1 1 download -w 1 1 1 downloader -w 1 1 1 downloads -w 1 1 1 drop -w 1 1 1 dropped -w 1 1 1 drupal -w 1 1 1 dummy -w 1 1 1 dump -w 1 1 1 dumps -w 1 1 1 dvd -w 1 1 1 dwr -w 1 1 1 dyn -w 1 1 1 dynamic -w 1 1 1 e -w 1 1 1 e2fs -w 1 1 1 ear -w 1 1 1 ecommerce -w 1 1 1 edge -w 1 1 1 edit -w 1 1 1 editor -w 1 1 1 edits -w 1 1 1 edp -w 1 1 1 edu -w 1 1 1 education -w 1 1 1 ee -w 1 1 1 effort -w 1 1 1 efforts -w 1 1 1 egress -w 1 1 1 ejb -w 1 1 1 element -w 1 1 1 elements -w 1 1 1 em -w 1 1 1 email -w 1 1 1 emails -w 1 1 1 embed -w 1 1 1 embedded -w 1 1 1 emea -w 1 1 1 employees -w 1 1 1 employment -w 1 1 1 empty -w 1 1 1 emu -w 1 1 1 emulator -w 1 1 1 en -w 1 1 1 en_US -w 1 1 1 enc -w 1 1 1 encode -w 1 1 1 encoder -w 1 1 1 encrypt -w 1 1 1 encrypted -w 1 1 1 encyption -w 1 1 1 eng -w 1 1 1 engine -w 1 1 1 english -w 1 1 1 enterprise -w 1 1 1 entertainment -w 1 1 1 entries -w 1 1 1 entry -w 1 1 1 env -w 1 1 1 environ -w 1 1 1 environment -w 1 1 1 ep -w 1 1 1 eric -w 1 1 1 error-log -w 1 1 1 error_log -w 1 1 1 errors -w 1 1 1 es -w 1 1 1 esale -w 1 1 1 esales -w 1 1 1 etc -w 1 1 1 eu -w 1 1 1 europe -w 1 1 1 event -w 1 1 1 events -w 1 1 1 evil -w 1 1 1 evt -w 1 1 1 ews -w 1 1 1 ex -w 1 1 1 example -w 1 1 1 examples -w 1 1 1 excalibur -w 1 1 1 exchange -w 1 1 1 exe -w 1 1 1 exec -w 1 1 1 explorer -w 1 1 1 export -w 1 1 1 ext -w 1 1 1 ext2 -w 1 1 1 extern -w 1 1 1 external -w 1 1 1 extras -w 1 1 1 ezshopper -w 1 1 1 f -w 1 1 1 fabric -w 1 1 1 face -w 1 1 1 facebook -w 1 1 1 faces -w 1 1 1 faculty -w 1 1 1 fail -w 1 1 1 failure -w 1 1 1 fake -w 1 1 1 family -w 1 1 1 faq -w 1 1 1 faqs -w 1 1 1 favorite -w 1 1 1 favorites -w 1 1 1 fb -w 1 1 1 fbook -w 1 1 1 fc -w 1 1 1 fcgi -w 1 1 1 fcgi-bin -w 1 1 1 feature -w 1 1 1 features -w 1 1 1 feed -w 1 1 1 feedback -w 1 1 1 feeds -w 1 1 1 felix -w 1 1 1 fetch -w 1 1 1 field -w 1 1 1 fields -w 1 1 1 file -w 1 1 1 fileadmin -w 1 1 1 filelist -w 1 1 1 files -w 1 1 1 filez -w 1 1 1 finance -w 1 1 1 financial -w 1 1 1 find -w 1 1 1 finger -w 1 1 1 firefox -w 1 1 1 firewall -w 1 1 1 firmware -w 1 1 1 first -w 1 1 1 fixed -w 1 1 1 flags -w 1 1 1 flash -w 1 1 1 flow -w 1 1 1 flows -w 1 1 1 flv -w 1 1 1 fn -w 1 1 1 folder -w 1 1 1 folders -w 1 1 1 font -w 1 1 1 fonts -w 1 1 1 foo -w 1 1 1 footer -w 1 1 1 footers -w 1 1 1 form -w 1 1 1 format -w 1 1 1 formatting -w 1 1 1 formmail -w 1 1 1 forms -w 1 1 1 forrest -w 1 1 1 fortune -w 1 1 1 forum -w 1 1 1 forum1 -w 1 1 1 forum2 -w 1 1 1 forumdisplay -w 1 1 1 forums -w 1 1 1 forward -w 1 1 1 foto -w 1 1 1 foundation -w 1 1 1 fr -w 1 1 1 frame -w 1 1 1 frames -w 1 1 1 framework -w 1 1 1 free -w 1 1 1 freebsd -w 1 1 1 friend -w 1 1 1 friends -w 1 1 1 frob -w 1 1 1 frontend -w 1 1 1 fs -w 1 1 1 ftp -w 1 1 1 fuck -w 1 1 1 fuckoff -w 1 1 1 fuckyou -w 1 1 1 full -w 1 1 1 fun -w 1 1 1 func -w 1 1 1 funcs -w 1 1 1 function -w 1 1 1 functions -w 1 1 1 fund -w 1 1 1 funding -w 1 1 1 funds -w 1 1 1 fusion -w 1 1 1 fw -w 1 1 1 g -w 1 1 1 gadget -w 1 1 1 gadgets -w 1 1 1 galleries -w 1 1 1 gallery -w 1 1 1 game -w 1 1 1 games -w 1 1 1 ganglia -w 1 1 1 garbage -w 1 1 1 gateway -w 1 1 1 gb -w 1 1 1 geeklog -w 1 1 1 general -w 1 1 1 geronimo -w 1 1 1 get -w 1 1 1 getaccess -w 1 1 1 getjobid -w 1 1 1 gfx -w 1 1 1 gid -w 1 1 1 gif -w 1 1 1 git -w 1 1 1 gitweb -w 1 1 1 glimpse -w 1 1 1 global -w 1 1 1 globals -w 1 1 1 glossary -w 1 1 1 go -w 1 1 1 goaway -w 1 1 1 google -w 1 1 1 government -w 1 1 1 gprs -w 1 1 1 grant -w 1 1 1 grants -w 1 1 1 graphics -w 1 1 1 group -w 1 1 1 groupcp -w 1 1 1 groups -w 1 1 1 gsm -w 1 1 1 guest -w 1 1 1 guestbook -w 1 1 1 guests -w 1 1 1 guide -w 1 1 1 guides -w 1 1 1 gump -w 1 1 1 gwt -w 1 1 1 gz -w 1 1 1 h -w 1 1 1 hack -w 1 1 1 hacker -w 1 1 1 hacking -w 1 1 1 hackme -w 1 1 1 hadoop -w 1 1 1 hardcore -w 1 1 1 hardware -w 1 1 1 harmony -w 1 1 1 head -w 1 1 1 header -w 1 1 1 headers -w 1 1 1 health -w 1 1 1 hello -w 1 1 1 help -w 1 1 1 helper -w 1 1 1 helpers -w 1 1 1 hi -w 1 1 1 hidden -w 1 1 1 hide -w 1 1 1 high -w 1 1 1 hipaa -w 1 1 1 hire -w 1 1 1 history -w 1 1 1 hit -w 1 1 1 hits -w 1 1 1 hole -w 1 1 1 home -w 1 1 1 homepage -w 1 1 1 hop -w 1 1 1 horde -w 1 1 1 hosting -w 1 1 1 hosts -w 1 1 1 hour -w 1 1 1 hourly -w 1 1 1 howto -w 1 1 1 hp -w 1 1 1 hr -w 1 1 1 hta -w 1 1 1 htbin -w 1 1 1 htdoc -w 1 1 1 htdocs -w 1 1 1 htm -w 1 1 1 htpasswd -w 1 1 1 http -w 1 1 1 httpd -w 1 1 1 https -w 1 1 1 httpuser -w 1 1 1 hu -w 1 1 1 hyper -w 1 1 1 i -w 1 1 1 ia -w 1 1 1 ibm -w 1 1 1 icat -w 1 1 1 ico -w 1 1 1 icon -w 1 1 1 icons -w 1 1 1 id -w 1 1 1 idea -w 1 1 1 ideas -w 1 1 1 ids -w 1 1 1 ie -w 1 1 1 iframe -w 1 1 1 ig -w 1 1 1 ignore -w 1 1 1 iisadmin -w 1 1 1 iisadmpwd -w 1 1 1 iissamples -w 1 1 1 image -w 1 1 1 imagefolio -w 1 1 1 images -w 1 1 1 img -w 1 1 1 imgs -w 1 1 1 imp -w 1 1 1 import -w 1 1 1 important -w 1 1 1 in -w 1 1 1 inbound -w 1 1 1 incl -w 1 1 1 include -w 1 1 1 includes -w 1 1 1 incoming -w 1 1 1 incubator -w 1 1 1 index -w 1 1 1 index1 -w 1 1 1 index2 -w 1 1 1 index_1 -w 1 1 1 index_2 -w 1 1 1 inetpub -w 1 1 1 inetsrv -w 1 1 1 inf -w 1 1 1 info -w 1 1 1 information -w 1 1 1 ingress -w 1 1 1 init -w 1 1 1 inline -w 1 1 1 input -w 1 1 1 inquire -w 1 1 1 inquiries -w 1 1 1 inquiry -w 1 1 1 insert -w 1 1 1 install -w 1 1 1 int -w 1 1 1 intel -w 1 1 1 intelligence -w 1 1 1 inter -w 1 1 1 interim -w 1 1 1 intermediate -w 1 1 1 internal -w 1 1 1 international -w 1 1 1 internet -w 1 1 1 intl -w 1 1 1 intra -w 1 1 1 intranet -w 1 1 1 intro -w 1 1 1 ip -w 1 1 1 ipc -w 1 1 1 iphone -w 1 1 1 ips -w 1 1 1 irc -w 1 1 1 is -w 1 1 1 isapi -w 1 1 1 iso -w 1 1 1 issues -w 1 1 1 it -w 1 1 1 item -w 1 1 1 items -w 1 1 1 j -w 1 1 1 j2ee -w 1 1 1 j2me -w 1 1 1 jacob -w 1 1 1 jakarta -w 1 1 1 java-plugin -w 1 1 1 javadoc -w 1 1 1 javascript -w 1 1 1 javax -w 1 1 1 jboss -w 1 1 1 jbossas -w 1 1 1 jbossws -w 1 1 1 jdbc -w 1 1 1 jennifer -w 1 1 1 jessica -w 1 1 1 jhtml -w 1 1 1 jigsaw -w 1 1 1 jira -w 1 1 1 jj -w 1 1 1 jmx-console -w 1 1 1 job -w 1 1 1 jobs -w 1 1 1 joe -w 1 1 1 john -w 1 1 1 join -w 1 1 1 joomla -w 1 1 1 journal -w 1 1 1 jp -w 1 1 1 jpa -w 1 1 1 jpg -w 1 1 1 jre -w 1 1 1 jrun -w 1 1 1 js -w 1 1 1 jsf -w 1 1 1 json -w 1 1 1 jsp -w 1 1 1 jsso -w 1 1 1 jsx -w 1 1 1 juniper -w 1 1 1 junk -w 1 1 1 jvm -w 1 1 1 k -w 1 1 1 kboard -w 1 1 1 keep -w 1 1 1 kernel -w 1 1 1 keygen -w 1 1 1 keys -w 1 1 1 kids -w 1 1 1 kill -w 1 1 1 known_hosts -w 1 1 1 l -w 1 1 1 la -w 1 1 1 labs -w 1 1 1 lang -w 1 1 1 large -w 1 1 1 law -w 1 1 1 layout -w 1 1 1 layouts -w 1 1 1 ldap -w 1 1 1 leader -w 1 1 1 leaders -w 1 1 1 left -w 1 1 1 legacy -w 1 1 1 legal -w 1 1 1 lenya -w 1 1 1 letters -w 1 1 1 level -w 1 1 1 lg -w 1 1 1 lib -w 1 1 1 library -w 1 1 1 libs -w 1 1 1 license -w 1 1 1 licenses -w 1 1 1 limit -w 1 1 1 line -w 1 1 1 link -w 1 1 1 links -w 1 1 1 linux -w 1 1 1 list -w 1 1 1 listinfo -w 1 1 1 lists -w 1 1 1 live -w 1 1 1 lo -w 1 1 1 loader -w 1 1 1 loading -w 1 1 1 loc -w 1 1 1 local -w 1 1 1 location -w 1 1 1 lock -w 1 1 1 locked -w 1 1 1 log4j -w 1 1 1 log4net -w 1 1 1 logfile -w 1 1 1 logger -w 1 1 1 logging -w 1 1 1 login -w 1 1 1 logins -w 1 1 1 logo -w 1 1 1 logoff -w 1 1 1 logon -w 1 1 1 logos -w 1 1 1 logout -w 1 1 1 logs -w 1 1 1 lost -w 1 1 1 lost+found -w 1 1 1 low -w 1 1 1 ls -w 1 1 1 lst -w 1 1 1 lucene -w 1 1 1 m -w 1 1 1 mac -w 1 1 1 macromedia -w 1 1 1 maestro -w 1 1 1 mail -w 1 1 1 mailer -w 1 1 1 mailing -w 1 1 1 mailman -w 1 1 1 mails -w 1 1 1 main -w 1 1 1 mambo -w 1 1 1 manage -w 1 1 1 managed -w 1 1 1 management -w 1 1 1 manager -w 1 1 1 manifest -w 1 1 1 manual -w 1 1 1 manuals -w 1 1 1 map -w 1 1 1 maps -w 1 1 1 mark -w 1 1 1 marketing -w 1 1 1 master -w 1 1 1 master.passwd -w 1 1 1 match -w 1 1 1 matrix -w 1 1 1 matt -w 1 1 1 maven -w 1 1 1 mbox -w 1 1 1 mdb -w 1 1 1 me -w 1 1 1 media -w 1 1 1 medium -w 1 1 1 mem -w 1 1 1 member -w 1 1 1 members -w 1 1 1 membership -w 1 1 1 memory -w 1 1 1 menu -w 1 1 1 menus -w 1 1 1 message -w 1 1 1 messages -w 1 1 1 messaging -w 1 1 1 meta -w 1 1 1 michael -w 1 1 1 microsoft -w 1 1 1 migrate -w 1 1 1 migrated -w 1 1 1 migration -w 1 1 1 mina -w 1 1 1 mini -w 1 1 1 minute -w 1 1 1 mirror -w 1 1 1 mirrors -w 1 1 1 misc -w 1 1 1 mission -w 1 1 1 mix -w 1 1 1 mlist -w 1 1 1 mms -w 1 1 1 mobi -w 1 1 1 mobile -w 1 1 1 mock -w 1 1 1 mod -w 1 1 1 modify -w 1 1 1 mods -w 1 1 1 module -w 1 1 1 modules -w 1 1 1 mojo -w 1 1 1 money -w 1 1 1 monitor -w 1 1 1 monitoring -w 1 1 1 monitors -w 1 1 1 month -w 1 1 1 monthly -w 1 1 1 more -w 1 1 1 motd -w 1 1 1 move -w 1 1 1 moved -w 1 1 1 movie -w 1 1 1 movies -w 1 1 1 mp -w 1 1 1 mp3 -w 1 1 1 mp3s -w 1 1 1 ms -w 1 1 1 ms-sql -w 1 1 1 msadc -w 1 1 1 msadm -w 1 1 1 msft -w 1 1 1 msg -w 1 1 1 msie -w 1 1 1 msql -w 1 1 1 mssql -w 1 1 1 mta -w 1 1 1 multi -w 1 1 1 multimedia -w 1 1 1 music -w 1 1 1 mx -w 1 1 1 my -w 1 1 1 myadmin -w 1 1 1 myfaces -w 1 1 1 myphpnuke -w 1 1 1 mysql -w 1 1 1 mysqld -w 1 1 1 n -w 1 1 1 nav -w 1 1 1 navigation -w 1 1 1 nc -w 1 1 1 net -w 1 1 1 netbsd -w 1 1 1 netcat -w 1 1 1 nethome -w 1 1 1 nets -w 1 1 1 network -w 1 1 1 networking -w 1 1 1 new -w 1 1 1 news -w 1 1 1 newsletter -w 1 1 1 newsletters -w 1 1 1 newticket -w 1 1 1 next -w 1 1 1 nfs -w 1 1 1 nice -w 1 1 1 nl -w 1 1 1 nobody -w 1 1 1 node -w 1 1 1 none -w 1 1 1 note -w 1 1 1 notes -w 1 1 1 notification -w 1 1 1 notifications -w 1 1 1 notified -w 1 1 1 notifier -w 1 1 1 notify -w 1 1 1 novell -w 1 1 1 ns -w 1 1 1 nsf -w 1 1 1 nude -w 1 1 1 nuke -w 1 1 1 nul -w 1 1 1 null -w 1 1 1 o -w 1 1 1 oa_servlets -w 1 1 1 oauth -w 1 1 1 obdc -w 1 1 1 obsolete -w 1 1 1 obsoleted -w 1 1 1 odbc -w 1 1 1 ode -w 1 1 1 oem -w 1 1 1 ofbiz -w 1 1 1 office -w 1 1 1 offices -w 1 1 1 onbound -w 1 1 1 online -w 1 1 1 op -w 1 1 1 open -w 1 1 1 openbsd -w 1 1 1 opencart -w 1 1 1 opendir -w 1 1 1 openejb -w 1 1 1 openjpa -w 1 1 1 opera -w 1 1 1 operations -w 1 1 1 opinion -w 1 1 1 oprocmgr-status -w 1 1 1 opt -w 1 1 1 option -w 1 1 1 options -w 1 1 1 ora -w 1 1 1 oracle -w 1 1 1 oracle.xml.xsql.XSQLServlet -w 1 1 1 order -w 1 1 1 ordered -w 1 1 1 orders -w 1 1 1 org -w 1 1 1 osc -w 1 1 1 oscommerce -w 1 1 1 other -w 1 1 1 outcome -w 1 1 1 outgoing -w 1 1 1 outline -w 1 1 1 output -w 1 1 1 outreach -w 1 1 1 overview -w 1 1 1 owa -w 1 1 1 owl -w 1 1 1 ows -w 1 1 1 ows-bin -w 1 1 1 p -w 1 1 1 p2p -w 1 1 1 pack -w 1 1 1 package -w 1 1 1 packaged -w 1 1 1 packages -w 1 1 1 packed -w 1 1 1 page -w 1 1 1 page1 -w 1 1 1 page2 -w 1 1 1 page_1 -w 1 1 1 page_2 -w 1 1 1 pages -w 1 1 1 paid -w 1 1 1 panel -w 1 1 1 paper -w 1 1 1 papers -w 1 1 1 parse -w 1 1 1 partner -w 1 1 1 partners -w 1 1 1 party -w 1 1 1 pass -w 1 1 1 passive -w 1 1 1 passwd -w 1 1 1 password -w 1 1 1 passwords -w 1 1 1 past -w 1 1 1 patch -w 1 1 1 patches -w 1 1 1 pay -w 1 1 1 payment -w 1 1 1 payments -w 1 1 1 paypal -w 1 1 1 pbo -w 1 1 1 pc -w 1 1 1 pci -w 1 1 1 pda -w 1 1 1 pdf -w 1 1 1 pdfs -w 1 1 1 pear -w 1 1 1 peek -w 1 1 1 pem -w 1 1 1 pending -w 1 1 1 people -w 1 1 1 perf -w 1 1 1 performance -w 1 1 1 perl -w 1 1 1 personal -w 1 1 1 pfx -w 1 1 1 pg -w 1 1 1 phf -w 1 1 1 phone -w 1 1 1 phones -w 1 1 1 phorum -w 1 1 1 photo -w 1 1 1 photos -w 1 1 1 php -w 1 1 1 php3 -w 1 1 1 phpBB -w 1 1 1 phpBB2 -w 1 1 1 phpEventCalendar -w 1 1 1 phpMyAdmin -w 1 1 1 phpbb -w 1 1 1 phpmyadmin -w 1 1 1 phpnuke -w 1 1 1 phps -w 1 1 1 phtml -w 1 1 1 pic -w 1 1 1 pics -w 1 1 1 pictures -w 1 1 1 pii -w 1 1 1 ping -w 1 1 1 pipe -w 1 1 1 pipermail -w 1 1 1 piranha -w 1 1 1 pivot -w 1 1 1 pix -w 1 1 1 pixel -w 1 1 1 pkg -w 1 1 1 pkgs -w 1 1 1 plain -w 1 1 1 play -w 1 1 1 player -w 1 1 1 playing -w 1 1 1 playlist -w 1 1 1 pls -w 1 1 1 plugin -w 1 1 1 plugins -w 1 1 1 pm -w 1 1 1 png -w 1 1 1 poc -w 1 1 1 poi -w 1 1 1 policies -w 1 1 1 policy -w 1 1 1 politics -w 1 1 1 poll -w 1 1 1 polls -w 1 1 1 pool -w 1 1 1 pop -w 1 1 1 pop3 -w 1 1 1 popup -w 1 1 1 porn -w 1 1 1 port -w 1 1 1 portal -w 1 1 1 portals -w 1 1 1 portfolio -w 1 1 1 pos -w 1 1 1 post -w 1 1 1 posted -w 1 1 1 postgres -w 1 1 1 postgresql -w 1 1 1 postnuke -w 1 1 1 postpaid -w 1 1 1 posts -w 1 1 1 ppt -w 1 1 1 pr -w 1 1 1 pr0n -w 1 1 1 premium -w 1 1 1 prepaid -w 1 1 1 presentation -w 1 1 1 presentations -w 1 1 1 preserve -w 1 1 1 press -w 1 1 1 preview -w 1 1 1 previews -w 1 1 1 previous -w 1 1 1 pricing -w 1 1 1 print -w 1 1 1 printenv -w 1 1 1 printer -w 1 1 1 printers -w 1 1 1 priv -w 1 1 1 privacy -w 1 1 1 private -w 1 1 1 pro -w 1 1 1 problems -w 1 1 1 proc -w 1 1 1 procedures -w 1 1 1 procure -w 1 1 1 procurement -w 1 1 1 prod -w 1 1 1 product -w 1 1 1 product_info -w 1 1 1 production -w 1 1 1 products -w 1 1 1 profile -w 1 1 1 profiles -w 1 1 1 profiling -w 1 1 1 program -w 1 1 1 programming -w 1 1 1 programs -w 1 1 1 progress -w 1 1 1 project -w 1 1 1 projects -w 1 1 1 promo -w 1 1 1 promoted -w 1 1 1 promotion -w 1 1 1 prop -w 1 1 1 prop-base -w 1 1 1 properties -w 1 1 1 property -w 1 1 1 props -w 1 1 1 prot -w 1 1 1 protect -w 1 1 1 protected -w 1 1 1 protection -w 1 1 1 proto -w 1 1 1 proxies -w 1 1 1 proxy -w 1 1 1 prv -w 1 1 1 ps -w 1 1 1 psql -w 1 1 1 pt -w 1 1 1 pub -w 1 1 1 public -w 1 1 1 publication -w 1 1 1 publications -w 1 1 1 pubs -w 1 1 1 pull -w 1 1 1 purchase -w 1 1 1 purchases -w 1 1 1 purchasing -w 1 1 1 push -w 1 1 1 pw -w 1 1 1 pwd -w 1 1 1 py -w 1 1 1 python -w 1 1 1 q -w 1 1 1 q1 -w 1 1 1 q2 -w 1 1 1 q3 -w 1 1 1 q4 -w 1 1 1 qotd -w 1 1 1 qpid -w 1 1 1 quarterly -w 1 1 1 queries -w 1 1 1 query -w 1 1 1 queue -w 1 1 1 queues -w 1 1 1 quote -w 1 1 1 quotes -w 1 1 1 r -w 1 1 1 radio -w 1 1 1 random -w 1 1 1 rar -w 1 1 1 rb -w 1 1 1 rdf -w 1 1 1 read -w 1 1 1 readme -w 1 1 1 real -w 1 1 1 realestate -w 1 1 1 receive -w 1 1 1 received -w 1 1 1 recharge -w 1 1 1 record -w 1 1 1 recorded -w 1 1 1 recorder -w 1 1 1 records -w 1 1 1 recovery -w 1 1 1 recycle -w 1 1 1 recycled -w 1 1 1 redir -w 1 1 1 redirect -w 1 1 1 reference -w 1 1 1 reg -w 1 1 1 register -w 1 1 1 registered -w 1 1 1 registration -w 1 1 1 registrations -w 1 1 1 release -w 1 1 1 releases -w 1 1 1 remind -w 1 1 1 reminder -w 1 1 1 remote -w 1 1 1 removal -w 1 1 1 removals -w 1 1 1 remove -w 1 1 1 removed -w 1 1 1 render -w 1 1 1 rendered -w 1 1 1 rep -w 1 1 1 repl -w 1 1 1 replica -w 1 1 1 replicas -w 1 1 1 replicate -w 1 1 1 replicated -w 1 1 1 replication -w 1 1 1 replicator -w 1 1 1 reply -w 1 1 1 report -w 1 1 1 reporting -w 1 1 1 reports -w 1 1 1 reprints -w 1 1 1 req -w 1 1 1 reqs -w 1 1 1 request -w 1 1 1 requests -w 1 1 1 requisition -w 1 1 1 requisitions -w 1 1 1 res -w 1 1 1 research -w 1 1 1 resin -w 1 1 1 resize -w 1 1 1 resolution -w 1 1 1 resolve -w 1 1 1 resolved -w 1 1 1 resource -w 1 1 1 resources -w 1 1 1 rest -w 1 1 1 restore -w 1 1 1 restored -w 1 1 1 restricted -w 1 1 1 result -w 1 1 1 results -w 1 1 1 retail -w 1 1 1 reverse -w 1 1 1 reversed -w 1 1 1 revert -w 1 1 1 reverted -w 1 1 1 review -w 1 1 1 reviews -w 1 1 1 rhtml -w 1 1 1 right -w 1 1 1 roam -w 1 1 1 roaming -w 1 1 1 robot -w 1 1 1 robots -w 1 1 1 roller -w 1 1 1 room -w 1 1 1 root -w 1 1 1 rpc -w 1 1 1 rsa -w 1 1 1 rss -w 1 1 1 rtf -w 1 1 1 ru -w 1 1 1 ruby -w 1 1 1 rule -w 1 1 1 rules -w 1 1 1 run -w 1 1 1 rwservlet -w 1 1 1 s -w 1 1 1 sale -w 1 1 1 sales -w 1 1 1 salesforce -w 1 1 1 sam -w 1 1 1 samba -w 1 1 1 saml -w 1 1 1 sample -w 1 1 1 samples -w 1 1 1 san -w 1 1 1 sav -w 1 1 1 save -w 1 1 1 saved -w 1 1 1 saves -w 1 1 1 sbin -w 1 1 1 scan -w 1 1 1 scanned -w 1 1 1 scans -w 1 1 1 sched -w 1 1 1 schedule -w 1 1 1 scheduled -w 1 1 1 scheduling -w 1 1 1 schema -w 1 1 1 science -w 1 1 1 screen -w 1 1 1 screens -w 1 1 1 screenshot -w 1 1 1 screenshots -w 1 1 1 script -w 1 1 1 scriptlet -w 1 1 1 scriptlets -w 1 1 1 scripts -w 1 1 1 sdk -w 1 1 1 se -w 1 1 1 search -w 1 1 1 sec -w 1 1 1 second -w 1 1 1 secret -w 1 1 1 section -w 1 1 1 sections -w 1 1 1 secure -w 1 1 1 secured -w 1 1 1 security -w 1 1 1 seed -w 1 1 1 select -w 1 1 1 sell -w 1 1 1 send -w 1 1 1 sendmail -w 1 1 1 sendto -w 1 1 1 sent -w 1 1 1 serial -w 1 1 1 serv -w 1 1 1 serve -w 1 1 1 server -w 1 1 1 server-info -w 1 1 1 server-status -w 1 1 1 servers -w 1 1 1 service -w 1 1 1 services -w 1 1 1 servlet -w 1 1 1 servlets -w 1 1 1 session -w 1 1 1 sessions -w 1 1 1 setting -w 1 1 1 settings -w 1 1 1 setup -w 1 1 1 sex -w 1 1 1 sh -w 1 1 1 shadow -w 1 1 1 share -w 1 1 1 shared -w 1 1 1 shares -w 1 1 1 shell -w 1 1 1 ship -w 1 1 1 shipped -w 1 1 1 shipping -w 1 1 1 shockwave -w 1 1 1 shop -w 1 1 1 shopper -w 1 1 1 shopping -w 1 1 1 shops -w 1 1 1 shoutbox -w 1 1 1 show -w 1 1 1 show_post -w 1 1 1 show_thread -w 1 1 1 showcat -w 1 1 1 showenv -w 1 1 1 showjobs -w 1 1 1 showmap -w 1 1 1 showmsg -w 1 1 1 showpost -w 1 1 1 showthread -w 1 1 1 shtml -w 1 1 1 sign -w 1 1 1 signed -w 1 1 1 signer -w 1 1 1 signin -w 1 1 1 signing -w 1 1 1 signoff -w 1 1 1 signon -w 1 1 1 signout -w 1 1 1 signup -w 1 1 1 simple -w 1 1 1 sink -w 1 1 1 site -w 1 1 1 site-map -w 1 1 1 site_map -w 1 1 1 sitemap -w 1 1 1 sites -w 1 1 1 skel -w 1 1 1 skin -w 1 1 1 skins -w 1 1 1 skip -w 1 1 1 sl -w 1 1 1 sling -w 1 1 1 sm -w 1 1 1 small -w 1 1 1 smile -w 1 1 1 smiles -w 1 1 1 sms -w 1 1 1 smtp -w 1 1 1 snoop -w 1 1 1 so -w 1 1 1 soap -w 1 1 1 soaprouter -w 1 1 1 soft -w 1 1 1 software -w 1 1 1 solaris -w 1 1 1 sold -w 1 1 1 solution -w 1 1 1 solutions -w 1 1 1 solve -w 1 1 1 solved -w 1 1 1 source -w 1 1 1 sources -w 1 1 1 sox -w 1 1 1 sp -w 1 1 1 space -w 1 1 1 spacer -w 1 1 1 spam -w 1 1 1 special -w 1 1 1 specials -w 1 1 1 sponsor -w 1 1 1 sponsors -w 1 1 1 spool -w 1 1 1 sport -w 1 1 1 sports -w 1 1 1 sqlnet -w 1 1 1 squirrel -w 1 1 1 squirrelmail -w 1 1 1 src -w 1 1 1 srv -w 1 1 1 ss -w 1 1 1 ssh -w 1 1 1 ssi -w 1 1 1 ssl -w 1 1 1 sslvpn -w 1 1 1 ssn -w 1 1 1 sso -w 1 1 1 stackdump -w 1 1 1 staff -w 1 1 1 staging -w 1 1 1 standalone -w 1 1 1 standard -w 1 1 1 standards -w 1 1 1 star -w 1 1 1 start -w 1 1 1 stat -w 1 1 1 statement -w 1 1 1 statements -w 1 1 1 static -w 1 1 1 staticpages -w 1 1 1 statistic -w 1 1 1 statistics -w 1 1 1 stats -w 1 1 1 status -w 1 1 1 stock -w 1 1 1 storage -w 1 1 1 store -w 1 1 1 stored -w 1 1 1 stores -w 1 1 1 stories -w 1 1 1 story -w 1 1 1 strut -w 1 1 1 struts -w 1 1 1 student -w 1 1 1 students -w 1 1 1 stuff -w 1 1 1 style -w 1 1 1 styles -w 1 1 1 submissions -w 1 1 1 submit -w 1 1 1 subscribe -w 1 1 1 subscribed -w 1 1 1 subscriber -w 1 1 1 subscribers -w 1 1 1 subscription -w 1 1 1 subscriptions -w 1 1 1 success -w 1 1 1 suite -w 1 1 1 suites -w 1 1 1 sun -w 1 1 1 sunos -w 1 1 1 super -w 1 1 1 support -w 1 1 1 surf -w 1 1 1 survey -w 1 1 1 surveys -w 1 1 1 swf -w 1 1 1 sws -w 1 1 1 synapse -w 1 1 1 sync -w 1 1 1 synced -w 1 1 1 sys -w 1 1 1 sysmanager -w 1 1 1 system -w 1 1 1 systems -w 1 1 1 sysuser -w 1 1 1 t -w 1 1 1 tag -w 1 1 1 tags -w 1 1 1 tail -w 1 1 1 tape -w 1 1 1 tapes -w 1 1 1 tapestry -w 1 1 1 tar -w 1 1 1 tar.bz2 -w 1 1 1 tar.gz -w 1 1 1 tb -w 1 1 1 tcl -w 1 1 1 team -w 1 1 1 tech -w 1 1 1 technical -w 1 1 1 technology -w 1 1 1 tel -w 1 1 1 tele -w 1 1 1 templ -w 1 1 1 template -w 1 1 1 templates -w 1 1 1 terms -w 1 1 1 test-cgi -w 1 1 1 test-env -w 1 1 1 test1 -w 1 1 1 test123 -w 1 1 1 test1234 -w 1 1 1 test2 -w 1 1 1 test3 -w 1 1 1 testimonial -w 1 1 1 testimonials -w 1 1 1 testing -w 1 1 1 tests -w 1 1 1 texis -w 1 1 1 text -w 1 1 1 text-base -w 1 1 1 texts -w 1 1 1 tgz -w 1 1 1 theme -w 1 1 1 themes -w 1 1 1 thread -w 1 1 1 threads -w 1 1 1 thumb -w 1 1 1 thumbnail -w 1 1 1 thumbnails -w 1 1 1 thumbs -w 1 1 1 tickets -w 1 1 1 tiki -w 1 1 1 tiles -w 1 1 1 tip -w 1 1 1 tips -w 1 1 1 title -w 1 1 1 tls -w 1 1 1 tmpl -w 1 1 1 tmps -w 1 1 1 tn -w 1 1 1 toc -w 1 1 1 todo -w 1 1 1 toggle -w 1 1 1 tomcat -w 1 1 1 tool -w 1 1 1 toolbar -w 1 1 1 toolkit -w 1 1 1 tools -w 1 1 1 top -w 1 1 1 topic -w 1 1 1 topics -w 1 1 1 torrent -w 1 1 1 torrents -w 1 1 1 tos -w 1 1 1 tour -w 1 1 1 tpl -w 1 1 1 tpv -w 1 1 1 tr -w 1 1 1 trace -w 1 1 1 traceroute -w 1 1 1 traces -w 1 1 1 track -w 1 1 1 trackback -w 1 1 1 tracker -w 1 1 1 trackers -w 1 1 1 tracking -w 1 1 1 tracks -w 1 1 1 traffic -w 1 1 1 trailer -w 1 1 1 trailers -w 1 1 1 training -w 1 1 1 trans -w 1 1 1 transaction -w 1 1 1 transactions -w 1 1 1 transparent -w 1 1 1 transport -w 1 1 1 trash -w 1 1 1 travel -w 1 1 1 treasury -w 1 1 1 tree -w 1 1 1 trees -w 1 1 1 trial -w 1 1 1 true -w 1 1 1 trunk -w 1 1 1 tsweb -w 1 1 1 tt -w 1 1 1 turbine -w 1 1 1 tuscany -w 1 1 1 tutorial -w 1 1 1 tutorials -w 1 1 1 tv -w 1 1 1 tweak -w 1 1 1 twitter -w 1 1 1 type -w 1 1 1 typo3 -w 1 1 1 typo3conf -w 1 1 1 u -w 1 1 1 ubb -w 1 1 1 uds -w 1 1 1 uk -w 1 1 1 umts -w 1 1 1 union -w 1 1 1 unix -w 1 1 1 unlock -w 1 1 1 unpaid -w 1 1 1 unreg -w 1 1 1 unregister -w 1 1 1 unsubscribe -w 1 1 1 up -w 1 1 1 upd -w 1 1 1 update -w 1 1 1 updated -w 1 1 1 updater -w 1 1 1 updates -w 1 1 1 upload -w 1 1 1 uploader -w 1 1 1 uploads -w 1 1 1 url -w 1 1 1 urls -w 1 1 1 us -w 1 1 1 usa -w 1 1 1 usage -w 1 1 1 user -w 1 1 1 userlog -w 1 1 1 users -w 1 1 1 usr -w 1 1 1 util -w 1 1 1 utilities -w 1 1 1 utility -w 1 1 1 utils -w 1 1 1 v -w 1 1 1 v1 -w 1 1 1 v2 -w 1 1 1 var -w 1 1 1 vault -w 1 1 1 vb -w 1 1 1 vbs -w 1 1 1 vector -w 1 1 1 velocity -w 1 1 1 vendor -w 1 1 1 vendors -w 1 1 1 ver -w 1 1 1 ver1 -w 1 1 1 ver2 -w 1 1 1 version -w 1 1 1 vfs -w 1 1 1 video -w 1 1 1 videos -w 1 1 1 view -w 1 1 1 view-source -w 1 1 1 viewcvs -w 1 1 1 viewforum -w 1 1 1 viewonline -w 1 1 1 views -w 1 1 1 viewsource -w 1 1 1 viewsvn -w 1 1 1 viewtopic -w 1 1 1 viewvc -w 1 1 1 virtual -w 1 1 1 vm -w 1 1 1 voip -w 1 1 1 vol -w 1 1 1 vote -w 1 1 1 voter -w 1 1 1 votes -w 1 1 1 vpn -w 1 1 1 vuln -w 1 1 1 w -w 1 1 1 w3 -w 1 1 1 w3c -w 1 1 1 wa -w 1 1 1 wap -w 1 1 1 war -w 1 1 1 warez -w 1 1 1 way-board -w 1 1 1 wbboard -w 1 1 1 wc -w 1 1 1 weather -w 1 1 1 web -w 1 1 1 web-beans -w 1 1 1 web-console -w 1 1 1 webaccess -w 1 1 1 webadmin -w 1 1 1 webagent -w 1 1 1 webalizer -w 1 1 1 webapp -w 1 1 1 webb -w 1 1 1 webbbs -w 1 1 1 webboard -w 1 1 1 webcalendar -w 1 1 1 webcart -w 1 1 1 webcasts -w 1 1 1 webcgi -w 1 1 1 webchat -w 1 1 1 webdata -w 1 1 1 webdav -w 1 1 1 webdb -w 1 1 1 weblog -w 1 1 1 weblogic -w 1 1 1 weblogs -w 1 1 1 webmail -w 1 1 1 webplus -w 1 1 1 webshop -w 1 1 1 website -w 1 1 1 websphere -w 1 1 1 websql -w 1 1 1 webstats -w 1 1 1 websvn -w 1 1 1 webwork -w 1 1 1 week -w 1 1 1 weekly -w 1 1 1 welcome -w 1 1 1 whitepapers -w 1 1 1 whois -w 1 1 1 whosonline -w 1 1 1 wicket -w 1 1 1 wiki -w 1 1 1 win -w 1 1 1 win32 -w 1 1 1 windows -w 1 1 1 winnt -w 1 1 1 wireless -w 1 1 1 wml -w 1 1 1 word -w 1 1 1 wordpress -w 1 1 1 work -w 1 1 1 working -w 1 1 1 world -w 1 1 1 wow -w 1 1 1 wp -w 1 1 1 wp-content -w 1 1 1 wp-dbmanager -w 1 1 1 wp-includes -w 1 1 1 wp-login -w 1 1 1 wp-syntax -w 1 1 1 wrap -w 1 1 1 ws -w 1 1 1 ws-client -w 1 1 1 ws_ftp -w 1 1 1 wsdl -w 1 1 1 wtai -w 1 1 1 www -w 1 1 1 www-sql -w 1 1 1 www1 -w 1 1 1 www2 -w 1 1 1 www3 -w 1 1 1 wwwboard -w 1 1 1 wwwroot -w 1 1 1 wwwstats -w 1 1 1 wwwthreads -w 1 1 1 wwwuser -w 1 1 1 wysiwyg -w 1 1 1 x -w 1 1 1 xalan -w 1 1 1 xerces -w 1 1 1 xhtml -w 1 1 1 xls -w 1 1 1 xmlrpc -w 1 1 1 xsl -w 1 1 1 xsql -w 1 1 1 xxx -w 1 1 1 xyzzy -w 1 1 1 y -w 1 1 1 yahoo -w 1 1 1 year -w 1 1 1 yearly -w 1 1 1 yml -w 1 1 1 youtube -w 1 1 1 yt -w 1 1 1 z -w 1 1 1 zboard -w 1 1 1 zencart -w 1 1 1 zend -w 1 1 1 zero -w 1 1 1 zimbra -w 1 1 1 zipfiles -w 1 1 1 zips -w 1 1 1 zoom -w 1 1 1 zope -w 1 1 1 zorum -w 1 1 1 ~admin -w 1 1 1 ~amanda -w 1 1 1 ~apache -w 1 1 1 ~ashley -w 1 1 1 ~bin -w 1 1 1 ~bob -w 1 1 1 ~chris -w 1 1 1 ~dan -w 1 1 1 ~eric -w 1 1 1 ~ftp -w 1 1 1 ~guest -w 1 1 1 ~http -w 1 1 1 ~httpd -w 1 1 1 ~jacob -w 1 1 1 ~jennifer -w 1 1 1 ~jessica -w 1 1 1 ~john -w 1 1 1 ~log -w 1 1 1 ~logs -w 1 1 1 ~lp -w 1 1 1 ~mark -w 1 1 1 ~matt -w 1 1 1 ~michael -w 1 1 1 ~nobody -w 1 1 1 ~root -w 1 1 1 ~test -w 1 1 1 ~tmp -w 1 1 1 ~www +wg 1 1 1 camel +wg 1 1 1 car +wg 1 1 1 card +wg 1 1 1 cards +wg 1 1 1 career +wg 1 1 1 careers +wg 1 1 1 cars +wg 1 1 1 cart +wg 1 1 1 carts +wg 1 1 1 cat +wg 1 1 1 catalog +wg 1 1 1 catalogs +wg 1 1 1 catalyst +wg 1 1 1 categories +wg 1 1 1 category +wg 1 1 1 catinfo +wg 1 1 1 cats +ws 1 1 1 cc +wg 1 1 1 ccbill +wg 1 1 1 cd +wg 1 1 1 cert +wg 1 1 1 certificate +wg 1 1 1 certificates +wg 1 1 1 certified +wg 1 1 1 certs +wg 1 1 1 cf +ws 1 1 1 cfcache +wg 1 1 1 cfdocs +wg 1 1 1 cfide +ws 1 1 1 cfm +wg 1 1 1 cfusion +ws 1 1 1 cgi +ws 1 1 1 cgi-bin +ws 1 1 1 cgi-bin2 +ws 1 1 1 cgi-home +ws 1 1 1 cgi-local +ws 1 1 1 cgi-pub +ws 1 1 1 cgi-script +ws 1 1 1 cgi-shl +ws 1 1 1 cgi-sys +ws 1 1 1 cgi-web +ws 1 1 1 cgi-win +ws 1 1 1 cgibin +ws 1 1 1 cgiwrap +ws 1 1 1 cgm-web +wg 1 1 1 change +wg 1 1 1 changed +wg 1 1 1 changes +wg 1 1 1 charge +wg 1 1 1 charges +wg 1 1 1 chat +wg 1 1 1 chats +wg 1 1 1 check +wg 1 1 1 checking +wg 1 1 1 checkout +wg 1 1 1 checkpoint +wg 1 1 1 checks +wg 1 1 1 child +wg 1 1 1 children +wg 1 1 1 chris +wg 1 1 1 chrome +wg 1 1 1 cisco +wg 1 1 1 cisweb +wg 1 1 1 citrix +wg 1 1 1 cl +wg 1 1 1 claim +wg 1 1 1 claims +wg 1 1 1 classes +wg 1 1 1 classified +wg 1 1 1 classifieds +wg 1 1 1 clear +wg 1 1 1 click +wg 1 1 1 clicks +wg 1 1 1 client +ws 1 1 1 clientaccesspolicy +wg 1 1 1 clients +wg 1 1 1 clk +wg 1 1 1 clock +wg 1 1 1 close +wg 1 1 1 closed +wg 1 1 1 closing +wg 1 1 1 club +wg 1 1 1 cluster +wg 1 1 1 clusters +wg 1 1 1 cmd +wg 1 1 1 cms +wg 1 1 1 cnt +wg 1 1 1 cocoon +wg 1 1 1 code +wg 1 1 1 codec +wg 1 1 1 codecs +wg 1 1 1 codes +wg 1 1 1 cognos +wg 1 1 1 coldfusion +wg 1 1 1 columns +wg 1 1 1 com +wg 1 1 1 comment +wg 1 1 1 comments +wg 1 1 1 commerce +wg 1 1 1 commercial +wg 1 1 1 common +wg 1 1 1 communicator +wg 1 1 1 community +wg 1 1 1 compact +wg 1 1 1 company +wg 1 1 1 compat +wg 1 1 1 complaint +wg 1 1 1 complaints +wg 1 1 1 compliance +wg 1 1 1 component +wg 1 1 1 components +wg 1 1 1 compress +wg 1 1 1 compressed +wg 1 1 1 computer +wg 1 1 1 computers +wg 1 1 1 computing +wg 1 1 1 conference +wg 1 1 1 conferences +wg 1 1 1 configs +wg 1 1 1 console +wg 1 1 1 consumer +wg 1 1 1 contact +wg 1 1 1 contacts +wg 1 1 1 content +wg 1 1 1 contents +wg 1 1 1 contest +wg 1 1 1 contract +wg 1 1 1 contracts +wg 1 1 1 control +wg 1 1 1 controller +wg 1 1 1 controlpanel +wg 1 1 1 cookie +wg 1 1 1 cookies +wg 1 1 1 copies +wg 1 1 1 copy +wg 1 1 1 copyright +wg 1 1 1 core +wg 1 1 1 corp +wg 1 1 1 corpo +wg 1 1 1 corporate +wg 1 1 1 corrections +wg 1 1 1 count +wg 1 1 1 counter +wg 1 1 1 counters +wg 1 1 1 counts +wg 1 1 1 course +wg 1 1 1 courses +wg 1 1 1 cover +wg 1 1 1 cpadmin +wg 1 1 1 cpanel +ws 1 1 1 cpp +wg 1 1 1 cr +wg 1 1 1 crack +wg 1 1 1 crash +wg 1 1 1 crashes +wg 1 1 1 create +wg 1 1 1 creator +wg 1 1 1 credit +wg 1 1 1 credits +wg 1 1 1 crm +wg 1 1 1 cron +wg 1 1 1 crons +wg 1 1 1 crontab +wg 1 1 1 crontabs +wg 1 1 1 crossdomain +wg 1 1 1 crypt +wg 1 1 1 crypto +ws 1 1 1 cs +ws 1 1 1 csproj +wg 1 1 1 css +wg 1 1 1 current +wg 1 1 1 custom +ws 1 1 1 custom-log +ws 1 1 1 custom_log +wg 1 1 1 customer +wg 1 1 1 customers +wg 1 1 1 cute +wg 1 1 1 cv +wg 1 1 1 cxf +wg 1 1 1 czcmdcvt +wg 1 1 1 d +wg 1 1 1 daemon +wg 1 1 1 daily +wg 1 1 1 dan +wg 1 1 1 dana-na +wg 1 1 1 dat +wg 1 1 1 data +wg 1 1 1 database +wg 1 1 1 databases +wg 1 1 1 date +wg 1 1 1 day +wg 1 1 1 db +wg 1 1 1 db_connect +wg 1 1 1 dba +wg 1 1 1 dbase +wg 1 1 1 dblclk +wg 1 1 1 dbman +wg 1 1 1 dbmodules +wg 1 1 1 dbutil +wg 1 1 1 dc +wg 1 1 1 dcforum +wg 1 1 1 dclk +wg 1 1 1 de +wg 1 1 1 dealer +wg 1 1 1 debug +wg 1 1 1 decl +wg 1 1 1 declaration +wg 1 1 1 declarations +wg 1 1 1 decode +wg 1 1 1 decoder +wg 1 1 1 decrypt +wg 1 1 1 decrypted +wg 1 1 1 decryption +wg 1 1 1 def +wg 1 1 1 default +wg 1 1 1 defaults +wg 1 1 1 definition +wg 1 1 1 definitions +wg 1 1 1 del +wg 1 1 1 delete +wg 1 1 1 deleted +wg 1 1 1 demo +wg 1 1 1 demos +wg 1 1 1 denied +wg 1 1 1 deny +wg 1 1 1 design +wg 1 1 1 desktop +wg 1 1 1 desktops +wg 1 1 1 detail +wg 1 1 1 details +wg 1 1 1 dev +wg 1 1 1 devel +wg 1 1 1 developer +wg 1 1 1 developers +wg 1 1 1 development +wg 1 1 1 device +wg 1 1 1 devices +wg 1 1 1 devs +wg 1 1 1 df +wg 1 1 1 dialog +wg 1 1 1 dialogs +wg 1 1 1 diff +wg 1 1 1 diffs +wg 1 1 1 digest +wg 1 1 1 digg +wg 1 1 1 dir +ws 1 1 1 dir-prop-base +wg 1 1 1 directories +wg 1 1 1 directory +wg 1 1 1 dirs +wg 1 1 1 disabled +wg 1 1 1 disclaimer +wg 1 1 1 display +wg 1 1 1 django +wg 1 1 1 dl +ws 1 1 1 dll +wg 1 1 1 dm +ws 1 1 1 dm-config +wg 1 1 1 dms +wg 1 1 1 dms0 +wg 1 1 1 dns +wg 1 1 1 do +ws 1 1 1 doc +wg 1 1 1 docebo +wg 1 1 1 dock +wg 1 1 1 docroot +wg 1 1 1 docs +wg 1 1 1 document +wg 1 1 1 documentation +wg 1 1 1 documents +wg 1 1 1 domain +wg 1 1 1 domains +wg 1 1 1 donate +wg 1 1 1 done +wg 1 1 1 doubleclick +wg 1 1 1 down +wg 1 1 1 download +wg 1 1 1 downloader +wg 1 1 1 downloads +wg 1 1 1 drop +wg 1 1 1 dropped +wg 1 1 1 drupal +wg 1 1 1 dummy +wg 1 1 1 dump +wg 1 1 1 dumps +wg 1 1 1 dvd +wg 1 1 1 dwr +wg 1 1 1 dyn +wg 1 1 1 dynamic +wg 1 1 1 e +wg 1 1 1 e2fs +wg 1 1 1 ear +wg 1 1 1 ecommerce +wg 1 1 1 edge +wg 1 1 1 edit +wg 1 1 1 editor +wg 1 1 1 edits +wg 1 1 1 edp +wg 1 1 1 edu +wg 1 1 1 education +wg 1 1 1 ee +wg 1 1 1 effort +wg 1 1 1 efforts +wg 1 1 1 egress +wg 1 1 1 ejb +wg 1 1 1 element +wg 1 1 1 elements +wg 1 1 1 em +wg 1 1 1 email +wg 1 1 1 emails +wg 1 1 1 embed +wg 1 1 1 embedded +wg 1 1 1 emea +wg 1 1 1 employees +wg 1 1 1 employment +wg 1 1 1 empty +wg 1 1 1 emu +wg 1 1 1 emulator +wg 1 1 1 en +ws 1 1 1 en_US +wg 1 1 1 enc +wg 1 1 1 encode +wg 1 1 1 encoder +wg 1 1 1 encrypt +wg 1 1 1 encrypted +wg 1 1 1 encyption +wg 1 1 1 eng +wg 1 1 1 engine +wg 1 1 1 english +wg 1 1 1 enterprise +wg 1 1 1 entertainment +wg 1 1 1 entries +wg 1 1 1 entry +wg 1 1 1 env +wg 1 1 1 environ +wg 1 1 1 environment +ws 1 1 1 ep +wg 1 1 1 eric +ws 1 1 1 error-log +ws 1 1 1 error_log +wg 1 1 1 errors +wg 1 1 1 es +wg 1 1 1 esale +wg 1 1 1 esales +wg 1 1 1 etc +wg 1 1 1 eu +wg 1 1 1 europe +wg 1 1 1 event +wg 1 1 1 events +wg 1 1 1 evil +wg 1 1 1 evt +wg 1 1 1 ews +wg 1 1 1 ex +wg 1 1 1 example +wg 1 1 1 examples +wg 1 1 1 excalibur +wg 1 1 1 exchange +ws 1 1 1 exe +wg 1 1 1 exec +wg 1 1 1 explorer +wg 1 1 1 export +wg 1 1 1 ext +wg 1 1 1 ext2 +wg 1 1 1 extern +wg 1 1 1 external +wg 1 1 1 extras +wg 1 1 1 ezshopper +wg 1 1 1 f +wg 1 1 1 fabric +wg 1 1 1 face +wg 1 1 1 facebook +wg 1 1 1 faces +wg 1 1 1 faculty +wg 1 1 1 fail +wg 1 1 1 failure +wg 1 1 1 fake +wg 1 1 1 family +wg 1 1 1 faq +wg 1 1 1 faqs +wg 1 1 1 favorite +wg 1 1 1 favorites +wg 1 1 1 fb +wg 1 1 1 fbook +wg 1 1 1 fc +ws 1 1 1 fcgi +ws 1 1 1 fcgi-bin +wg 1 1 1 feature +wg 1 1 1 features +wg 1 1 1 feed +wg 1 1 1 feedback +wg 1 1 1 feeds +wg 1 1 1 felix +wg 1 1 1 fetch +wg 1 1 1 field +wg 1 1 1 fields +wg 1 1 1 file +wg 1 1 1 fileadmin +wg 1 1 1 filelist +wg 1 1 1 files +wg 1 1 1 filez +wg 1 1 1 finance +wg 1 1 1 financial +wg 1 1 1 find +wg 1 1 1 finger +wg 1 1 1 firefox +wg 1 1 1 firewall +wg 1 1 1 firmware +wg 1 1 1 first +wg 1 1 1 fixed +wg 1 1 1 flags +wg 1 1 1 flash +wg 1 1 1 flow +wg 1 1 1 flows +wg 1 1 1 flv +wg 1 1 1 fn +wg 1 1 1 folder +wg 1 1 1 folders +wg 1 1 1 font +wg 1 1 1 fonts +wg 1 1 1 foo +wg 1 1 1 footer +wg 1 1 1 footers +wg 1 1 1 form +wg 1 1 1 format +wg 1 1 1 formatting +wg 1 1 1 formmail +wg 1 1 1 forms +wg 1 1 1 forrest +wg 1 1 1 fortune +wg 1 1 1 forum +wg 1 1 1 forum1 +wg 1 1 1 forum2 +wg 1 1 1 forumdisplay +wg 1 1 1 forums +wg 1 1 1 forward +wg 1 1 1 foto +wg 1 1 1 foundation +wg 1 1 1 fr +wg 1 1 1 frame +wg 1 1 1 frames +wg 1 1 1 framework +wg 1 1 1 free +wg 1 1 1 freebsd +wg 1 1 1 friend +wg 1 1 1 friends +wg 1 1 1 frob +wg 1 1 1 frontend +wg 1 1 1 fs +wg 1 1 1 ftp +wg 1 1 1 fuck +wg 1 1 1 fuckoff +wg 1 1 1 fuckyou +wg 1 1 1 full +wg 1 1 1 fun +wg 1 1 1 func +wg 1 1 1 funcs +wg 1 1 1 function +wg 1 1 1 functions +wg 1 1 1 fund +wg 1 1 1 funding +wg 1 1 1 funds +wg 1 1 1 fusion +wg 1 1 1 fw +wg 1 1 1 g +wg 1 1 1 gadget +wg 1 1 1 gadgets +wg 1 1 1 galleries +wg 1 1 1 gallery +wg 1 1 1 game +wg 1 1 1 games +wg 1 1 1 ganglia +wg 1 1 1 garbage +wg 1 1 1 gateway +wg 1 1 1 gb +wg 1 1 1 geeklog +wg 1 1 1 general +wg 1 1 1 geronimo +wg 1 1 1 get +wg 1 1 1 getaccess +wg 1 1 1 getjobid +wg 1 1 1 gfx +wg 1 1 1 gid +ws 1 1 1 gif +wg 1 1 1 git +wg 1 1 1 gitweb +wg 1 1 1 glimpse +wg 1 1 1 global +wg 1 1 1 globals +wg 1 1 1 glossary +wg 1 1 1 go +wg 1 1 1 goaway +wg 1 1 1 google +wg 1 1 1 government +wg 1 1 1 gprs +wg 1 1 1 grant +wg 1 1 1 grants +wg 1 1 1 graphics +wg 1 1 1 group +wg 1 1 1 groupcp +wg 1 1 1 groups +wg 1 1 1 gsm +wg 1 1 1 guest +wg 1 1 1 guestbook +wg 1 1 1 guests +wg 1 1 1 guide +wg 1 1 1 guides +wg 1 1 1 gump +wg 1 1 1 gwt +wg 1 1 1 gz +wg 1 1 1 h +wg 1 1 1 hack +wg 1 1 1 hacker +wg 1 1 1 hacking +wg 1 1 1 hackme +wg 1 1 1 hadoop +wg 1 1 1 hardcore +wg 1 1 1 hardware +wg 1 1 1 harmony +wg 1 1 1 head +wg 1 1 1 header +wg 1 1 1 headers +wg 1 1 1 health +wg 1 1 1 hello +wg 1 1 1 help +wg 1 1 1 helper +wg 1 1 1 helpers +wg 1 1 1 hi +wg 1 1 1 hidden +wg 1 1 1 hide +wg 1 1 1 high +wg 1 1 1 hipaa +wg 1 1 1 hire +wg 1 1 1 history +wg 1 1 1 hit +wg 1 1 1 hits +wg 1 1 1 hole +wg 1 1 1 home +wg 1 1 1 homepage +wg 1 1 1 hop +wg 1 1 1 horde +wg 1 1 1 hosting +wg 1 1 1 hosts +wg 1 1 1 hour +wg 1 1 1 hourly +wg 1 1 1 howto +wg 1 1 1 hp +wg 1 1 1 hr +wg 1 1 1 hta +ws 1 1 1 htbin +ws 1 1 1 htdoc +ws 1 1 1 htdocs +ws 1 1 1 htm +wg 1 1 1 htpasswd +wg 1 1 1 http +wg 1 1 1 httpd +wg 1 1 1 https +wg 1 1 1 httpuser +wg 1 1 1 hu +wg 1 1 1 hyper +wg 1 1 1 i +wg 1 1 1 ia +wg 1 1 1 ibm +wg 1 1 1 icat +wg 1 1 1 ico +wg 1 1 1 icon +wg 1 1 1 icons +wg 1 1 1 id +wg 1 1 1 idea +wg 1 1 1 ideas +wg 1 1 1 ids +wg 1 1 1 ie +wg 1 1 1 iframe +wg 1 1 1 ig +wg 1 1 1 ignore +ws 1 1 1 iisadmin +ws 1 1 1 iisadmpwd +ws 1 1 1 iissamples +wg 1 1 1 image +wg 1 1 1 imagefolio +wg 1 1 1 images +wg 1 1 1 img +wg 1 1 1 imgs +wg 1 1 1 imp +wg 1 1 1 import +wg 1 1 1 important +wg 1 1 1 in +wg 1 1 1 inbound +wg 1 1 1 incl +wg 1 1 1 include +wg 1 1 1 includes +wg 1 1 1 incoming +wg 1 1 1 incubator +wg 1 1 1 index +wg 1 1 1 index1 +wg 1 1 1 index2 +wg 1 1 1 index_1 +wg 1 1 1 index_2 +ws 1 1 1 inetpub +ws 1 1 1 inetsrv +wg 1 1 1 inf +wg 1 1 1 info +wg 1 1 1 information +wg 1 1 1 ingress +wg 1 1 1 init +wg 1 1 1 inline +wg 1 1 1 input +wg 1 1 1 inquire +wg 1 1 1 inquiries +wg 1 1 1 inquiry +wg 1 1 1 insert +wg 1 1 1 install +wg 1 1 1 int +wg 1 1 1 intel +wg 1 1 1 intelligence +wg 1 1 1 inter +wg 1 1 1 interim +wg 1 1 1 intermediate +wg 1 1 1 internal +wg 1 1 1 international +wg 1 1 1 internet +wg 1 1 1 intl +wg 1 1 1 intra +wg 1 1 1 intranet +wg 1 1 1 intro +wg 1 1 1 ip +wg 1 1 1 ipc +wg 1 1 1 iphone +wg 1 1 1 ips +wg 1 1 1 irc +wg 1 1 1 is +wg 1 1 1 isapi +wg 1 1 1 iso +wg 1 1 1 issues +wg 1 1 1 it +wg 1 1 1 item +wg 1 1 1 items +wg 1 1 1 j +wg 1 1 1 j2ee +wg 1 1 1 j2me +wg 1 1 1 jacob +wg 1 1 1 jakarta +wg 1 1 1 java-plugin +wg 1 1 1 javadoc +wg 1 1 1 javascript +wg 1 1 1 javax +wg 1 1 1 jboss +wg 1 1 1 jbossas +wg 1 1 1 jbossws +wg 1 1 1 jdbc +wg 1 1 1 jennifer +wg 1 1 1 jessica +ws 1 1 1 jhtml +wg 1 1 1 jigsaw +wg 1 1 1 jira +wg 1 1 1 jj +ws 1 1 1 jmx-console +wg 1 1 1 job +wg 1 1 1 jobs +wg 1 1 1 joe +wg 1 1 1 john +wg 1 1 1 join +wg 1 1 1 joomla +wg 1 1 1 journal +wg 1 1 1 jp +wg 1 1 1 jpa +ws 1 1 1 jpg +wg 1 1 1 jre +wg 1 1 1 jrun +wg 1 1 1 js +ws 1 1 1 jsf +wg 1 1 1 json +ws 1 1 1 jsp +wg 1 1 1 jsso +wg 1 1 1 jsx +wg 1 1 1 juniper +wg 1 1 1 junk +wg 1 1 1 jvm +wg 1 1 1 k +wg 1 1 1 kboard +wg 1 1 1 keep +wg 1 1 1 kernel +wg 1 1 1 keygen +wg 1 1 1 keys +wg 1 1 1 kids +wg 1 1 1 kill +ws 1 1 1 known_hosts +wg 1 1 1 l +wg 1 1 1 la +wg 1 1 1 labs +wg 1 1 1 lang +wg 1 1 1 large +wg 1 1 1 law +wg 1 1 1 layout +wg 1 1 1 layouts +wg 1 1 1 ldap +wg 1 1 1 leader +wg 1 1 1 leaders +wg 1 1 1 left +wg 1 1 1 legacy +wg 1 1 1 legal +wg 1 1 1 lenya +wg 1 1 1 letters +wg 1 1 1 level +wg 1 1 1 lg +wg 1 1 1 lib +wg 1 1 1 library +wg 1 1 1 libs +wg 1 1 1 license +wg 1 1 1 licenses +wg 1 1 1 limit +wg 1 1 1 line +wg 1 1 1 link +wg 1 1 1 links +wg 1 1 1 linux +wg 1 1 1 list +wg 1 1 1 listinfo +wg 1 1 1 lists +wg 1 1 1 live +wg 1 1 1 lo +wg 1 1 1 loader +wg 1 1 1 loading +wg 1 1 1 loc +wg 1 1 1 local +wg 1 1 1 location +wg 1 1 1 lock +wg 1 1 1 locked +wg 1 1 1 log4j +wg 1 1 1 log4net +wg 1 1 1 logfile +wg 1 1 1 logger +wg 1 1 1 logging +wg 1 1 1 login +wg 1 1 1 logins +wg 1 1 1 logo +wg 1 1 1 logoff +wg 1 1 1 logon +wg 1 1 1 logos +wg 1 1 1 logout +wg 1 1 1 logs +wg 1 1 1 lost +ws 1 1 1 lost+found +wg 1 1 1 low +wg 1 1 1 ls +wg 1 1 1 lst +wg 1 1 1 lucene +wg 1 1 1 m +wg 1 1 1 mac +wg 1 1 1 macromedia +wg 1 1 1 maestro +wg 1 1 1 mail +wg 1 1 1 mailer +wg 1 1 1 mailing +wg 1 1 1 mailman +wg 1 1 1 mails +wg 1 1 1 main +wg 1 1 1 mambo +wg 1 1 1 manage +wg 1 1 1 managed +wg 1 1 1 management +wg 1 1 1 manager +ws 1 1 1 manifest +wg 1 1 1 manual +wg 1 1 1 manuals +wg 1 1 1 map +wg 1 1 1 maps +wg 1 1 1 mark +wg 1 1 1 marketing +wg 1 1 1 master +ws 1 1 1 master.passwd +wg 1 1 1 match +wg 1 1 1 matrix +wg 1 1 1 matt +wg 1 1 1 maven +wg 1 1 1 mbox +wg 1 1 1 mdb +wg 1 1 1 me +wg 1 1 1 media +wg 1 1 1 medium +wg 1 1 1 mem +wg 1 1 1 member +wg 1 1 1 members +wg 1 1 1 membership +wg 1 1 1 memory +wg 1 1 1 menu +wg 1 1 1 menus +wg 1 1 1 message +wg 1 1 1 messages +wg 1 1 1 messaging +wg 1 1 1 meta +wg 1 1 1 michael +wg 1 1 1 microsoft +wg 1 1 1 migrate +wg 1 1 1 migrated +wg 1 1 1 migration +wg 1 1 1 mina +wg 1 1 1 mini +wg 1 1 1 minute +wg 1 1 1 mirror +wg 1 1 1 mirrors +wg 1 1 1 misc +wg 1 1 1 mission +wg 1 1 1 mix +wg 1 1 1 mlist +wg 1 1 1 mms +wg 1 1 1 mobi +wg 1 1 1 mobile +wg 1 1 1 mock +wg 1 1 1 mod +wg 1 1 1 modify +wg 1 1 1 mods +wg 1 1 1 module +wg 1 1 1 modules +wg 1 1 1 mojo +wg 1 1 1 money +wg 1 1 1 monitor +wg 1 1 1 monitoring +wg 1 1 1 monitors +wg 1 1 1 month +wg 1 1 1 monthly +wg 1 1 1 more +wg 1 1 1 motd +wg 1 1 1 move +wg 1 1 1 moved +wg 1 1 1 movie +wg 1 1 1 movies +wg 1 1 1 mp +wg 1 1 1 mp3 +wg 1 1 1 mp3s +wg 1 1 1 ms +wg 1 1 1 ms-sql +wg 1 1 1 msadc +wg 1 1 1 msadm +wg 1 1 1 msft +wg 1 1 1 msg +wg 1 1 1 msie +wg 1 1 1 msql +wg 1 1 1 mssql +wg 1 1 1 mta +wg 1 1 1 multi +wg 1 1 1 multimedia +wg 1 1 1 music +wg 1 1 1 mx +wg 1 1 1 my +wg 1 1 1 myadmin +wg 1 1 1 myfaces +wg 1 1 1 myphpnuke +wg 1 1 1 mysql +wg 1 1 1 mysqld +wg 1 1 1 n +wg 1 1 1 nav +wg 1 1 1 navigation +wg 1 1 1 nc +wg 1 1 1 net +wg 1 1 1 netbsd +wg 1 1 1 netcat +wg 1 1 1 nethome +wg 1 1 1 nets +wg 1 1 1 network +wg 1 1 1 networking +wg 1 1 1 new +wg 1 1 1 news +wg 1 1 1 newsletter +wg 1 1 1 newsletters +wg 1 1 1 newticket +wg 1 1 1 next +wg 1 1 1 nfs +wg 1 1 1 nice +wg 1 1 1 nl +wg 1 1 1 nobody +wg 1 1 1 node +wg 1 1 1 none +wg 1 1 1 note +wg 1 1 1 notes +wg 1 1 1 notification +wg 1 1 1 notifications +wg 1 1 1 notified +wg 1 1 1 notifier +wg 1 1 1 notify +wg 1 1 1 novell +wg 1 1 1 ns +ws 1 1 1 nsf +wg 1 1 1 nude +wg 1 1 1 nuke +wg 1 1 1 nul +wg 1 1 1 null +wg 1 1 1 o +ws 1 1 1 oa_servlets +wg 1 1 1 oauth +wg 1 1 1 obdc +wg 1 1 1 obsolete +wg 1 1 1 obsoleted +wg 1 1 1 odbc +wg 1 1 1 ode +wg 1 1 1 oem +wg 1 1 1 ofbiz +wg 1 1 1 office +wg 1 1 1 offices +wg 1 1 1 onbound +wg 1 1 1 online +wg 1 1 1 op +wg 1 1 1 open +wg 1 1 1 openbsd +wg 1 1 1 opencart +wg 1 1 1 opendir +wg 1 1 1 openejb +wg 1 1 1 openjpa +wg 1 1 1 opera +wg 1 1 1 operations +wg 1 1 1 opinion +ws 1 1 1 oprocmgr-status +wg 1 1 1 opt +wg 1 1 1 option +wg 1 1 1 options +wg 1 1 1 ora +wg 1 1 1 oracle +ws 1 1 1 oracle.xml.xsql.XSQLServlet +wg 1 1 1 order +wg 1 1 1 ordered +wg 1 1 1 orders +wg 1 1 1 org +wg 1 1 1 osc +wg 1 1 1 oscommerce +wg 1 1 1 other +wg 1 1 1 outcome +wg 1 1 1 outgoing +wg 1 1 1 outline +wg 1 1 1 output +wg 1 1 1 outreach +wg 1 1 1 overview +wg 1 1 1 owa +wg 1 1 1 owl +wg 1 1 1 ows +ws 1 1 1 ows-bin +wg 1 1 1 p +wg 1 1 1 p2p +wg 1 1 1 pack +wg 1 1 1 package +wg 1 1 1 packaged +wg 1 1 1 packages +wg 1 1 1 packed +wg 1 1 1 page +wg 1 1 1 page1 +wg 1 1 1 page2 +wg 1 1 1 page_1 +wg 1 1 1 page_2 +wg 1 1 1 pages +wg 1 1 1 paid +wg 1 1 1 panel +wg 1 1 1 paper +wg 1 1 1 papers +wg 1 1 1 parse +wg 1 1 1 partner +wg 1 1 1 partners +wg 1 1 1 party +wg 1 1 1 pass +wg 1 1 1 passive +wg 1 1 1 passwd +wg 1 1 1 password +wg 1 1 1 passwords +wg 1 1 1 past +wg 1 1 1 patch +wg 1 1 1 patches +wg 1 1 1 pay +wg 1 1 1 payment +wg 1 1 1 payments +wg 1 1 1 paypal +wg 1 1 1 pbo +wg 1 1 1 pc +wg 1 1 1 pci +wg 1 1 1 pda +ws 1 1 1 pdf +wg 1 1 1 pdfs +wg 1 1 1 pear +wg 1 1 1 peek +ws 1 1 1 pem +wg 1 1 1 pending +wg 1 1 1 people +wg 1 1 1 perf +wg 1 1 1 performance +wg 1 1 1 perl +wg 1 1 1 personal +ws 1 1 1 pfx +wg 1 1 1 pg +wg 1 1 1 phf +wg 1 1 1 phone +wg 1 1 1 phones +wg 1 1 1 phorum +wg 1 1 1 photo +wg 1 1 1 photos +ws 1 1 1 php +ws 1 1 1 php3 +ws 1 1 1 phpBB +ws 1 1 1 phpBB2 +ws 1 1 1 phpEventCalendar +ws 1 1 1 phpMyAdmin +ws 1 1 1 phpbb +ws 1 1 1 phpmyadmin +ws 1 1 1 phpnuke +wg 1 1 1 phps +ws 1 1 1 phtml +wg 1 1 1 pic +wg 1 1 1 pics +wg 1 1 1 pictures +wg 1 1 1 pii +wg 1 1 1 ping +wg 1 1 1 pipe +wg 1 1 1 pipermail +wg 1 1 1 piranha +wg 1 1 1 pivot +wg 1 1 1 pix +wg 1 1 1 pixel +wg 1 1 1 pkg +wg 1 1 1 pkgs +wg 1 1 1 plain +wg 1 1 1 play +wg 1 1 1 player +wg 1 1 1 playing +wg 1 1 1 playlist +wg 1 1 1 pls +wg 1 1 1 plugin +wg 1 1 1 plugins +ws 1 1 1 pm +ws 1 1 1 png +wg 1 1 1 poc +wg 1 1 1 poi +wg 1 1 1 policies +wg 1 1 1 policy +wg 1 1 1 politics +wg 1 1 1 poll +wg 1 1 1 polls +wg 1 1 1 pool +wg 1 1 1 pop +wg 1 1 1 pop3 +wg 1 1 1 popup +wg 1 1 1 porn +wg 1 1 1 port +wg 1 1 1 portal +wg 1 1 1 portals +wg 1 1 1 portfolio +wg 1 1 1 pos +wg 1 1 1 post +wg 1 1 1 posted +wg 1 1 1 postgres +wg 1 1 1 postgresql +wg 1 1 1 postnuke +wg 1 1 1 postpaid +wg 1 1 1 posts +ws 1 1 1 ppt +wg 1 1 1 pr +wg 1 1 1 pr0n +wg 1 1 1 premium +wg 1 1 1 prepaid +wg 1 1 1 presentation +wg 1 1 1 presentations +wg 1 1 1 preserve +wg 1 1 1 press +wg 1 1 1 preview +wg 1 1 1 previews +wg 1 1 1 previous +wg 1 1 1 pricing +wg 1 1 1 print +wg 1 1 1 printenv +wg 1 1 1 printer +wg 1 1 1 printers +wg 1 1 1 priv +wg 1 1 1 privacy +wg 1 1 1 private +wg 1 1 1 pro +wg 1 1 1 problems +wg 1 1 1 proc +wg 1 1 1 procedures +wg 1 1 1 procure +wg 1 1 1 procurement +wg 1 1 1 prod +wg 1 1 1 product +wg 1 1 1 product_info +wg 1 1 1 production +wg 1 1 1 products +wg 1 1 1 profile +wg 1 1 1 profiles +wg 1 1 1 profiling +wg 1 1 1 program +wg 1 1 1 programming +wg 1 1 1 programs +wg 1 1 1 progress +wg 1 1 1 project +wg 1 1 1 projects +wg 1 1 1 promo +wg 1 1 1 promoted +wg 1 1 1 promotion +wg 1 1 1 prop +ws 1 1 1 prop-base +wg 1 1 1 properties +wg 1 1 1 property +wg 1 1 1 props +wg 1 1 1 prot +wg 1 1 1 protect +wg 1 1 1 protected +wg 1 1 1 protection +wg 1 1 1 proto +wg 1 1 1 proxies +wg 1 1 1 proxy +wg 1 1 1 prv +wg 1 1 1 ps +wg 1 1 1 psql +wg 1 1 1 pt +wg 1 1 1 pub +wg 1 1 1 public +wg 1 1 1 publication +wg 1 1 1 publications +wg 1 1 1 pubs +wg 1 1 1 pull +wg 1 1 1 purchase +wg 1 1 1 purchases +wg 1 1 1 purchasing +wg 1 1 1 push +wg 1 1 1 pw +wg 1 1 1 pwd +ws 1 1 1 py +wg 1 1 1 python +wg 1 1 1 q +wg 1 1 1 q1 +wg 1 1 1 q2 +wg 1 1 1 q3 +wg 1 1 1 q4 +wg 1 1 1 qotd +wg 1 1 1 qpid +wg 1 1 1 quarterly +wg 1 1 1 queries +wg 1 1 1 query +wg 1 1 1 queue +wg 1 1 1 queues +wg 1 1 1 quote +wg 1 1 1 quotes +wg 1 1 1 r +wg 1 1 1 radio +wg 1 1 1 random +wg 1 1 1 rar +ws 1 1 1 rb +wg 1 1 1 rdf +wg 1 1 1 read +wg 1 1 1 readme +wg 1 1 1 real +wg 1 1 1 realestate +wg 1 1 1 receive +wg 1 1 1 received +wg 1 1 1 recharge +wg 1 1 1 record +wg 1 1 1 recorded +wg 1 1 1 recorder +wg 1 1 1 records +wg 1 1 1 recovery +wg 1 1 1 recycle +wg 1 1 1 recycled +wg 1 1 1 redir +wg 1 1 1 redirect +wg 1 1 1 reference +wg 1 1 1 reg +wg 1 1 1 register +wg 1 1 1 registered +wg 1 1 1 registration +wg 1 1 1 registrations +wg 1 1 1 release +wg 1 1 1 releases +wg 1 1 1 remind +wg 1 1 1 reminder +wg 1 1 1 remote +wg 1 1 1 removal +wg 1 1 1 removals +wg 1 1 1 remove +wg 1 1 1 removed +wg 1 1 1 render +wg 1 1 1 rendered +wg 1 1 1 rep +wg 1 1 1 repl +wg 1 1 1 replica +wg 1 1 1 replicas +wg 1 1 1 replicate +wg 1 1 1 replicated +wg 1 1 1 replication +wg 1 1 1 replicator +wg 1 1 1 reply +wg 1 1 1 report +wg 1 1 1 reporting +wg 1 1 1 reports +wg 1 1 1 reprints +wg 1 1 1 req +wg 1 1 1 reqs +wg 1 1 1 request +wg 1 1 1 requests +wg 1 1 1 requisition +wg 1 1 1 requisitions +wg 1 1 1 res +wg 1 1 1 research +wg 1 1 1 resin +wg 1 1 1 resize +wg 1 1 1 resolution +wg 1 1 1 resolve +wg 1 1 1 resolved +wg 1 1 1 resource +wg 1 1 1 resources +wg 1 1 1 rest +wg 1 1 1 restore +wg 1 1 1 restored +wg 1 1 1 restricted +wg 1 1 1 result +wg 1 1 1 results +wg 1 1 1 retail +wg 1 1 1 reverse +wg 1 1 1 reversed +wg 1 1 1 revert +wg 1 1 1 reverted +wg 1 1 1 review +wg 1 1 1 reviews +ws 1 1 1 rhtml +wg 1 1 1 right +wg 1 1 1 roam +wg 1 1 1 roaming +wg 1 1 1 robot +wg 1 1 1 robots +wg 1 1 1 roller +wg 1 1 1 room +wg 1 1 1 root +wg 1 1 1 rpc +wg 1 1 1 rsa +ws 1 1 1 rss +ws 1 1 1 rtf +wg 1 1 1 ru +wg 1 1 1 ruby +wg 1 1 1 rule +wg 1 1 1 rules +wg 1 1 1 run +wg 1 1 1 rwservlet +wg 1 1 1 s +wg 1 1 1 sale +wg 1 1 1 sales +wg 1 1 1 salesforce +wg 1 1 1 sam +wg 1 1 1 samba +wg 1 1 1 saml +wg 1 1 1 sample +wg 1 1 1 samples +wg 1 1 1 san +wg 1 1 1 sav +wg 1 1 1 save +wg 1 1 1 saved +wg 1 1 1 saves +wg 1 1 1 sbin +wg 1 1 1 scan +wg 1 1 1 scanned +wg 1 1 1 scans +wg 1 1 1 sched +wg 1 1 1 schedule +wg 1 1 1 scheduled +wg 1 1 1 scheduling +wg 1 1 1 schema +wg 1 1 1 science +wg 1 1 1 screen +wg 1 1 1 screens +wg 1 1 1 screenshot +wg 1 1 1 screenshots +wg 1 1 1 script +wg 1 1 1 scriptlet +wg 1 1 1 scriptlets +wg 1 1 1 scripts +wg 1 1 1 sdk +wg 1 1 1 se +wg 1 1 1 search +wg 1 1 1 sec +wg 1 1 1 second +wg 1 1 1 secret +wg 1 1 1 section +wg 1 1 1 sections +wg 1 1 1 secure +wg 1 1 1 secured +wg 1 1 1 security +wg 1 1 1 seed +wg 1 1 1 select +wg 1 1 1 sell +wg 1 1 1 send +wg 1 1 1 sendmail +wg 1 1 1 sendto +wg 1 1 1 sent +wg 1 1 1 serial +wg 1 1 1 serv +wg 1 1 1 serve +wg 1 1 1 server +ws 1 1 1 server-info +ws 1 1 1 server-status +wg 1 1 1 servers +wg 1 1 1 service +wg 1 1 1 services +wg 1 1 1 servlet +wg 1 1 1 servlets +wg 1 1 1 session +wg 1 1 1 sessions +wg 1 1 1 setting +wg 1 1 1 settings +wg 1 1 1 setup +wg 1 1 1 sex +ws 1 1 1 sh +wg 1 1 1 shadow +wg 1 1 1 share +wg 1 1 1 shared +wg 1 1 1 shares +wg 1 1 1 shell +wg 1 1 1 ship +wg 1 1 1 shipped +wg 1 1 1 shipping +wg 1 1 1 shockwave +wg 1 1 1 shop +wg 1 1 1 shopper +wg 1 1 1 shopping +wg 1 1 1 shops +wg 1 1 1 shoutbox +wg 1 1 1 show +wg 1 1 1 show_post +wg 1 1 1 show_thread +wg 1 1 1 showcat +wg 1 1 1 showenv +wg 1 1 1 showjobs +wg 1 1 1 showmap +wg 1 1 1 showmsg +wg 1 1 1 showpost +wg 1 1 1 showthread +ws 1 1 1 shtml +wg 1 1 1 sign +wg 1 1 1 signed +wg 1 1 1 signer +wg 1 1 1 signin +wg 1 1 1 signing +wg 1 1 1 signoff +wg 1 1 1 signon +wg 1 1 1 signout +wg 1 1 1 signup +wg 1 1 1 simple +wg 1 1 1 sink +wg 1 1 1 site +ws 1 1 1 site-map +ws 1 1 1 site_map +wg 1 1 1 sitemap +wg 1 1 1 sites +wg 1 1 1 skel +wg 1 1 1 skin +wg 1 1 1 skins +wg 1 1 1 skip +wg 1 1 1 sl +wg 1 1 1 sling +wg 1 1 1 sm +wg 1 1 1 small +wg 1 1 1 smile +wg 1 1 1 smiles +wg 1 1 1 sms +wg 1 1 1 smtp +wg 1 1 1 snoop +ws 1 1 1 so +wg 1 1 1 soap +wg 1 1 1 soaprouter +wg 1 1 1 soft +wg 1 1 1 software +wg 1 1 1 solaris +wg 1 1 1 sold +wg 1 1 1 solution +wg 1 1 1 solutions +wg 1 1 1 solve +wg 1 1 1 solved +wg 1 1 1 source +wg 1 1 1 sources +wg 1 1 1 sox +wg 1 1 1 sp +wg 1 1 1 space +wg 1 1 1 spacer +wg 1 1 1 spam +wg 1 1 1 special +wg 1 1 1 specials +wg 1 1 1 sponsor +wg 1 1 1 sponsors +wg 1 1 1 spool +wg 1 1 1 sport +wg 1 1 1 sports +wg 1 1 1 sqlnet +wg 1 1 1 squirrel +wg 1 1 1 squirrelmail +wg 1 1 1 src +wg 1 1 1 srv +wg 1 1 1 ss +wg 1 1 1 ssh +wg 1 1 1 ssi +wg 1 1 1 ssl +ws 1 1 1 sslvpn +wg 1 1 1 ssn +wg 1 1 1 sso +wg 1 1 1 stackdump +wg 1 1 1 staff +wg 1 1 1 staging +wg 1 1 1 standalone +wg 1 1 1 standard +wg 1 1 1 standards +wg 1 1 1 star +wg 1 1 1 start +wg 1 1 1 stat +wg 1 1 1 statement +wg 1 1 1 statements +wg 1 1 1 static +wg 1 1 1 staticpages +wg 1 1 1 statistic +wg 1 1 1 statistics +wg 1 1 1 stats +wg 1 1 1 status +wg 1 1 1 stock +wg 1 1 1 storage +wg 1 1 1 store +wg 1 1 1 stored +wg 1 1 1 stores +wg 1 1 1 stories +wg 1 1 1 story +wg 1 1 1 strut +wg 1 1 1 struts +wg 1 1 1 student +wg 1 1 1 students +wg 1 1 1 stuff +wg 1 1 1 style +wg 1 1 1 styles +wg 1 1 1 submissions +wg 1 1 1 submit +wg 1 1 1 subscribe +wg 1 1 1 subscribed +wg 1 1 1 subscriber +wg 1 1 1 subscribers +wg 1 1 1 subscription +wg 1 1 1 subscriptions +wg 1 1 1 success +wg 1 1 1 suite +wg 1 1 1 suites +wg 1 1 1 sun +wg 1 1 1 sunos +wg 1 1 1 super +wg 1 1 1 support +wg 1 1 1 surf +wg 1 1 1 survey +wg 1 1 1 surveys +ws 1 1 1 swf +wg 1 1 1 sws +wg 1 1 1 synapse +wg 1 1 1 sync +wg 1 1 1 synced +wg 1 1 1 sys +wg 1 1 1 sysmanager +wg 1 1 1 system +wg 1 1 1 systems +wg 1 1 1 sysuser +wg 1 1 1 t +wg 1 1 1 tag +wg 1 1 1 tags +wg 1 1 1 tail +wg 1 1 1 tape +wg 1 1 1 tapes +wg 1 1 1 tapestry +wg 1 1 1 tar +wg 1 1 1 tar.bz2 +wg 1 1 1 tar.gz +wg 1 1 1 tb +wg 1 1 1 tcl +wg 1 1 1 team +wg 1 1 1 tech +wg 1 1 1 technical +wg 1 1 1 technology +wg 1 1 1 tel +wg 1 1 1 tele +wg 1 1 1 templ +wg 1 1 1 template +wg 1 1 1 templates +wg 1 1 1 terms +ws 1 1 1 test-cgi +ws 1 1 1 test-env +wg 1 1 1 test1 +wg 1 1 1 test123 +wg 1 1 1 test1234 +wg 1 1 1 test2 +wg 1 1 1 test3 +wg 1 1 1 testimonial +wg 1 1 1 testimonials +wg 1 1 1 testing +wg 1 1 1 tests +wg 1 1 1 texis +wg 1 1 1 text +ws 1 1 1 text-base +wg 1 1 1 texts +wg 1 1 1 tgz +wg 1 1 1 theme +wg 1 1 1 themes +wg 1 1 1 thread +wg 1 1 1 threads +wg 1 1 1 thumb +wg 1 1 1 thumbnail +wg 1 1 1 thumbnails +wg 1 1 1 thumbs +wg 1 1 1 tickets +wg 1 1 1 tiki +wg 1 1 1 tiles +wg 1 1 1 tip +wg 1 1 1 tips +wg 1 1 1 title +wg 1 1 1 tls +wg 1 1 1 tmpl +wg 1 1 1 tmps +wg 1 1 1 tn +wg 1 1 1 toc +wg 1 1 1 todo +wg 1 1 1 toggle +wg 1 1 1 tomcat +wg 1 1 1 tool +wg 1 1 1 toolbar +wg 1 1 1 toolkit +wg 1 1 1 tools +wg 1 1 1 top +wg 1 1 1 topic +wg 1 1 1 topics +wg 1 1 1 torrent +wg 1 1 1 torrents +wg 1 1 1 tos +wg 1 1 1 tour +ws 1 1 1 tpl +wg 1 1 1 tpv +wg 1 1 1 tr +wg 1 1 1 trace +wg 1 1 1 traceroute +wg 1 1 1 traces +wg 1 1 1 track +wg 1 1 1 trackback +wg 1 1 1 tracker +wg 1 1 1 trackers +wg 1 1 1 tracking +wg 1 1 1 tracks +wg 1 1 1 traffic +wg 1 1 1 trailer +wg 1 1 1 trailers +wg 1 1 1 training +wg 1 1 1 trans +wg 1 1 1 transaction +wg 1 1 1 transactions +wg 1 1 1 transparent +wg 1 1 1 transport +wg 1 1 1 trash +wg 1 1 1 travel +wg 1 1 1 treasury +wg 1 1 1 tree +wg 1 1 1 trees +wg 1 1 1 trial +wg 1 1 1 true +wg 1 1 1 trunk +wg 1 1 1 tsweb +wg 1 1 1 tt +wg 1 1 1 turbine +wg 1 1 1 tuscany +wg 1 1 1 tutorial +wg 1 1 1 tutorials +wg 1 1 1 tv +wg 1 1 1 tweak +wg 1 1 1 twitter +wg 1 1 1 type +ws 1 1 1 typo3 +ws 1 1 1 typo3conf +wg 1 1 1 u +wg 1 1 1 ubb +wg 1 1 1 uds +wg 1 1 1 uk +wg 1 1 1 umts +wg 1 1 1 union +wg 1 1 1 unix +wg 1 1 1 unlock +wg 1 1 1 unpaid +wg 1 1 1 unreg +wg 1 1 1 unregister +wg 1 1 1 unsubscribe +wg 1 1 1 up +wg 1 1 1 upd +wg 1 1 1 update +wg 1 1 1 updated +wg 1 1 1 updater +wg 1 1 1 updates +wg 1 1 1 upload +wg 1 1 1 uploader +wg 1 1 1 uploads +wg 1 1 1 url +wg 1 1 1 urls +wg 1 1 1 us +wg 1 1 1 usa +wg 1 1 1 usage +wg 1 1 1 user +wg 1 1 1 userlog +wg 1 1 1 users +wg 1 1 1 usr +wg 1 1 1 util +wg 1 1 1 utilities +wg 1 1 1 utility +wg 1 1 1 utils +wg 1 1 1 v +wg 1 1 1 v1 +wg 1 1 1 v2 +wg 1 1 1 var +wg 1 1 1 vault +ws 1 1 1 vb +ws 1 1 1 vbs +wg 1 1 1 vector +wg 1 1 1 velocity +wg 1 1 1 vendor +wg 1 1 1 vendors +wg 1 1 1 ver +wg 1 1 1 ver1 +wg 1 1 1 ver2 +wg 1 1 1 version +wg 1 1 1 vfs +wg 1 1 1 video +wg 1 1 1 videos +wg 1 1 1 view +wg 1 1 1 view-source +wg 1 1 1 viewcvs +wg 1 1 1 viewforum +wg 1 1 1 viewonline +wg 1 1 1 views +wg 1 1 1 viewsource +wg 1 1 1 viewsvn +wg 1 1 1 viewtopic +wg 1 1 1 viewvc +wg 1 1 1 virtual +wg 1 1 1 vm +wg 1 1 1 voip +wg 1 1 1 vol +wg 1 1 1 vote +wg 1 1 1 voter +wg 1 1 1 votes +wg 1 1 1 vpn +wg 1 1 1 vuln +wg 1 1 1 w +wg 1 1 1 w3 +wg 1 1 1 w3c +wg 1 1 1 wa +wg 1 1 1 wap +ws 1 1 1 war +wg 1 1 1 warez +ws 1 1 1 way-board +wg 1 1 1 wbboard +wg 1 1 1 wc +wg 1 1 1 weather +wg 1 1 1 web +wg 1 1 1 web-beans +wg 1 1 1 web-console +wg 1 1 1 webaccess +wg 1 1 1 webadmin +wg 1 1 1 webagent +wg 1 1 1 webalizer +wg 1 1 1 webapp +wg 1 1 1 webb +wg 1 1 1 webbbs +wg 1 1 1 webboard +wg 1 1 1 webcalendar +wg 1 1 1 webcart +wg 1 1 1 webcasts +wg 1 1 1 webcgi +wg 1 1 1 webchat +wg 1 1 1 webdata +wg 1 1 1 webdav +wg 1 1 1 webdb +wg 1 1 1 weblog +wg 1 1 1 weblogic +wg 1 1 1 weblogs +wg 1 1 1 webmail +wg 1 1 1 webplus +wg 1 1 1 webshop +wg 1 1 1 website +wg 1 1 1 websphere +wg 1 1 1 websql +wg 1 1 1 webstats +wg 1 1 1 websvn +wg 1 1 1 webwork +wg 1 1 1 week +wg 1 1 1 weekly +wg 1 1 1 welcome +wg 1 1 1 whitepapers +wg 1 1 1 whois +wg 1 1 1 whosonline +wg 1 1 1 wicket +wg 1 1 1 wiki +wg 1 1 1 win +ws 1 1 1 win32 +wg 1 1 1 windows +ws 1 1 1 winnt +wg 1 1 1 wireless +wg 1 1 1 wml +wg 1 1 1 word +wg 1 1 1 wordpress +wg 1 1 1 work +wg 1 1 1 working +wg 1 1 1 world +wg 1 1 1 wow +wg 1 1 1 wp +ws 1 1 1 wp-content +ws 1 1 1 wp-dbmanager +ws 1 1 1 wp-includes +ws 1 1 1 wp-login +ws 1 1 1 wp-syntax +wg 1 1 1 wrap +ws 1 1 1 ws +ws 1 1 1 ws-client +ws 1 1 1 ws_ftp +wg 1 1 1 wsdl +wg 1 1 1 wtai +wg 1 1 1 www +wg 1 1 1 www-sql +wg 1 1 1 www1 +wg 1 1 1 www2 +wg 1 1 1 www3 +wg 1 1 1 wwwboard +wg 1 1 1 wwwroot +wg 1 1 1 wwwstats +wg 1 1 1 wwwthreads +wg 1 1 1 wwwuser +wg 1 1 1 wysiwyg +wg 1 1 1 x +wg 1 1 1 xalan +wg 1 1 1 xerces +wg 1 1 1 xhtml +ws 1 1 1 xls +wg 1 1 1 xmlrpc +ws 1 1 1 xsl +wg 1 1 1 xsql +wg 1 1 1 xxx +wg 1 1 1 xyzzy +wg 1 1 1 y +wg 1 1 1 yahoo +wg 1 1 1 year +wg 1 1 1 yearly +ws 1 1 1 yml +wg 1 1 1 youtube +wg 1 1 1 yt +wg 1 1 1 z +wg 1 1 1 zboard +wg 1 1 1 zencart +wg 1 1 1 zend +wg 1 1 1 zero +wg 1 1 1 zimbra +wg 1 1 1 zipfiles +wg 1 1 1 zips +wg 1 1 1 zoom +wg 1 1 1 zope +wg 1 1 1 zorum +ws 1 1 1 ~admin +ws 1 1 1 ~amanda +ws 1 1 1 ~apache +ws 1 1 1 ~ashley +ws 1 1 1 ~bin +ws 1 1 1 ~bob +ws 1 1 1 ~chris +ws 1 1 1 ~dan +ws 1 1 1 ~eric +ws 1 1 1 ~ftp +ws 1 1 1 ~guest +ws 1 1 1 ~http +ws 1 1 1 ~httpd +ws 1 1 1 ~jacob +ws 1 1 1 ~jennifer +ws 1 1 1 ~jessica +ws 1 1 1 ~john +ws 1 1 1 ~log +ws 1 1 1 ~logs +ws 1 1 1 ~lp +ws 1 1 1 ~mark +ws 1 1 1 ~matt +ws 1 1 1 ~michael +ws 1 1 1 ~nobody +ws 1 1 1 ~root +ws 1 1 1 ~test +ws 1 1 1 ~tmp +ws 1 1 1 ~www diff -Nru skipfish-2.02b/doc/authentication.txt skipfish-2.10b/doc/authentication.txt --- skipfish-2.02b/doc/authentication.txt 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/doc/authentication.txt 2012-12-04 13:27:54.000000000 +0000 @@ -0,0 +1,98 @@ + + +This document describes 3 different methods you can use to run +authenticated skipfish scans. + + 1) Form authentication + 2) Cookie authentication + 3) Basic HTTP authentication + + + +----------------------- +1. Form authentication +---------------------- + +With form authentication, skipfish will submit credentials using the +given login form. The server is expected to reply with authenticated +cookies which will than be used during the rest of the scan. + +An example to login using this feature: + +$ ./skipfish --auth-form http://example.org/login \ + --auth-user myuser \ + --auth-pass mypass \ + --auth-verify-url http://example.org/profile \ + [...other options...] + +This is how it works: + +1. Upon start of the scan, the authentication form at /login will be + fetched by skipfish. We will try to complete the username and password + fields and submit the form. + +2. Once a server response is obtained, skipfish will fetch the + verification URL twice: once with the new session cookies and once + without any cookies. Both responses are expected to be different. + +3. During the scan, the verification URL will be used many times to + test whether we are authenticated. If at some point our session has + been terminated server-side, skipfish will re-authenticate using the + --auth-form (/login in our example) . + +Verifying whether the session is still active requires a good verification +URL where an authenticated request is going to get a different response +than an anonymous request. For example a 'profile' or 'my account' page. + +Troubleshooting: +---------------- + +1. Login field names not recognized + + If the username and password form fields are not recognized, skipfish + will complain. In this case, you should specify the field names using + the --auth-user-field and --auth-pass-field flags. + +2. The form is not submitted to the right location + + If the login form doesn't specify an action="" location, skipfish + will submit the form's content to the form URL. This will fail in some + occasions. For example, when the login page uses Javascript to submit + the form to a different location. + + Use the --auth-form-target flag to specify the URL where you want skipfish + to submit the form to. + +3. Skipfish keeps getting logged out + + Make sure you blacklist any URLs that will log you out. For example, + using the " -X /logout" + + +------------------------- +2. Cookie authentication +------------------------- + +Alternatively, if the site relies on HTTP cookies you can also feed these +to skipfish manually. To do this log in using your browser or using a +simple curl script, and then provide skipfish with a session cookie: + +$ ./skipfish -C name=val [...other options...] + +Other session cookies may be passed the same way, one per each -C option. + +The -N option, which causes new cookies to be rejected by skipfish, +is almost always a good choice when running cookie authenticated scans +(e.g. to avoid your precious cookies from being overwritten). + +$ ./skipfish -N -C name=val [...other options...] + +----------------------------- +3. Basic HTTP authentication +----------------------------- + +For simple HTTP credentials, you can use the -A option to pass the +credentials. + +$ ./skipfish -A user:pass [...other options...] + diff -Nru skipfish-2.02b/doc/dictionaries.txt skipfish-2.10b/doc/dictionaries.txt --- skipfish-2.02b/doc/dictionaries.txt 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/doc/dictionaries.txt 2012-12-04 13:27:54.000000000 +0000 @@ -0,0 +1,228 @@ +This directory contains four alternative, hand-picked Skipfish dictionaries. + +PLEASE READ THESE INSTRUCTIONS CAREFULLY BEFORE PICKING ONE. This is *critical* +to getting decent results in your scans. + +------------------------ +Key command-line options +------------------------ + +Skipfish automatically builds and maintain dictionaries based on the URLs +and HTML content encountered while crawling the site. These dictionaries +are extremely useful for subsequent scans of the same target, or for +future assessments of other platforms belonging to the same customer. + +Exactly one read-write dictionary needs to be specified for every scan. To +create a blank one and feed it to skipfish, you can use this syntax: + +$ touch new_dict.wl +$ ./skipfish -W new_dict.wl [...other options...] + +In addition, it is almost always beneficial to seed the scanner with any +number of supplemental, read-only dictionaries with common keywords useful +for brute-force attacks against any website. Supplemental dictionaries can +be loaded using the following syntax: + +$ ./skipfish -S supplemental_dict1.wl -S supplemental_dict2.wl \ + -W new_dict.wl [...other options...] + +Note that the -W option should be used with care. The target file will be +modified at the end of the scan. The -S dictionaries, on the other hand, are +purely read-only. + +If you don't want to create a dictionary and store discovered keywords, you +can use -W- (an alias for -W /dev/null). + +You can and should share read-only dictionaries across unrelated scans, but +a separate read-write dictionary should be used for scans of unrelated targets. +Otherwise, undesirable interference may occur. + +With this out of the way, let's quickly review the options that may be used +to fine-tune various aspects of dictionary handling: + + -L - do not automatically learn new keywords based on site content. + + The scanner will still use keywords found in the specified + dictionaries, if any; but will not go beyond that set. + + This option should not be normally used in most scanning + modes; if supplied, the scanner will not be able to discover + and leverage technology-specific terms and file extensions + unique to the architecture of the targeted site. + + -G num - change jar size for keyword candidates. + + Up to candidates are randomly selected from site + content, and periodically retried during brute-force checks; + when one of them results in a unique non-404 response, it is + promoted to the dictionary proper. Unsuccessful candidates are + gradually replaced with new picks, and then discarded at the + end of the scan. The default jar size is 256. + + -R num - purge all entries in the read-write that had no non-404 hits for + the last scans. + + This option prevents dictionary creep in repeated assessments, + but needs to be used with care: it will permanently nuke a + part of the dictionary. + + -Y - inhibit full ${filename}.${extension} brute-force. + + In this mode, the scanner will only brute-force one component + at a time, trying all possible keywords without any extension, + and then trying to append extensions to any otherwise discovered + content. + + This greatly improves scan times, but reduces coverage. Scan modes + 2 and 3 shown in the next section make use of this flag. + +-------------- +Scanning modes +-------------- + +The basic dictionary-dependent modes you should be aware of (in order of the +associated request cost): + +1) Orderly crawl with no DirBuster-like brute-force at all. In this mode, the + scanner will not discover non-linked resources such as /admin, + /index.php.old, etc: + + $ ./skipfish -W- -L [...other options...] + + This mode is very fast, but *NOT* recommended for general use because + the lack of dictionary bruteforcing will limited the coverage. Use + only where absolutely necessary. + +2) Orderly scan with minimal extension brute-force. In this mode, the scanner + will not discover resources such as /admin, but will discover cases such as + /index.php.old (once index.php itself is spotted during an orderly crawl): + + $ touch new_dict.wl + $ ./skipfish -S dictionaries/extensions-only.wl -W new_dict.wl -Y \ + [...other options...] + + This method is only slightly more request-intensive than #1, and therefore, + is a marginally better alternative in cases where time is of essence. It's + still not recommended for most uses. The cost is about 100 requests per + fuzzed location. + +3) Directory OR extension brute-force only. In this mode, the scanner will only + try fuzzing the file name, or the extension, at any given time - but will + not try every possible ${filename}.${extension} pair from the dictionary. + + $ touch new_dict.wl + $ ./skipfish -S dictionaries/complete.wl -W new_dict.wl -Y \ + [...other options...] + + This method has a cost of about 2,000 requests per fuzzed location, and is + recommended for rapid assessments, especially when working with slow + servers or very large services. + +4) Normal dictionary fuzzing. In this mode, every ${filename}.${extension} + pair will be attempted. This mode is significantly slower, but offers + superior coverage, and should be your starting point. + + $ touch new_dict.wl + $ ./skipfish -S dictionaries/XXX.wl -W new_dict.wl [...other options...] + + Replace XXX with: + + minimal - recommended starter dictionary, mostly focusing on + backup and source files, about 60,000 requests + per fuzzed location. + + medium - more thorough dictionary, focusing on common + frameworks, about 140,000 requests. + + complete - all-inclusive dictionary, over 210,000 requests. + + Normal fuzzing mode is recommended when doing thorough assessments of + reasonably responsive servers; but it may be prohibitively expensive + when dealing with very large or very slow sites. + +---------------------------- +More about dictionary design +---------------------------- + +Each dictionary may consist of a number of extensions, and a number of +"regular" keywords. Extensions are considered just a special subset of the +keyword list. + +You can create custom dictionaries, conforming to this format: + +type hits total_age last_age keyword + +...where 'type' is either 'e' or 'w' (extension or keyword), followed by a +qualifier (explained below); 'hits' is the total number of times this keyword +resulted in a non-404 hit in all previous scans; 'total_age' is the number of scan +cycles this word is in the dictionary; 'last_age' is the number of scan cycles +since the last 'hit'; and 'keyword' is the actual keyword. + +Qualifiers alter the meaning of an entry in the following way: + + wg - generic keyword that is not associated with any specific server-side + technology. Examples include 'backup', 'accounting', or 'logs'. These + will be indiscriminately combined with every known extension (e.g., + 'backup.php') during the fuzzing process. + + ws - technology-specific keyword that are unlikely to have a random + extension; for example, with 'cgi-bin', testing for 'cgi-bin.php' is + usually a waste of time. Keywords tagged this way will be combined only + with a small set of technology-agnostic extensions - e.g., 'cgi-bin.old'. + + NOTE: Technology-specific keywords that in the real world, are always + paired with a single, specific extension, should be combined with said + extension in the 'ws' entry itself, rather than trying to accommodate + them with 'wg' rules. For example, 'MANIFEST.MF' is OK. + + eg - generic extension that is not specific to any well-defined technology, + or may pop-up in administrator- or developer-created auxiliary content. + Examples include 'bak', 'old', 'txt', or 'log'. + + es - technology-specific extension, such as 'php', or 'cgi', that are + unlikely to spontaneously accompany random 'ws' keywords. + +Skipfish leverages this distinction by only trying the following brute-force +combinations: + + /some/path/wg_keyword ('index') + /some/path/ws_keyword ('cgi-bin') + /some/path/wg_extension ('old') + /some/path/ws_extension ('php') + + /some/path/wg_keyword.wg_extension ('index.old') + /some/path/wg_keyword.ws_extension ('index.php') + + /some/path/ws_keyword.ws_extension ('cgi-bin.old') + +To decide between 'wg' and 'ws', consider if you are likely to ever encounter +files such as ${this_word}.php or ${this_word}.class. If not, tag the keyword +as 'ws'. + +Similarly, to decide between 'eg' and 'es', think about the possibility of +encountering cgi-bin.${this_ext} or zencart.${this_ext}. If it seems unlikely, +choose 'es'. + +For your convenience, all legacy keywords and extensions, as well as any entries +detected automatically, will be stored in the dictionary with a '?' qualifier. +This is equivalent to 'g', and is meant to assist the user in reviewing and +triaging any automatically acquired dictionary data. + +Other notes about dictionaries: + + - Do not duplicate extensions as keywords - if you already have 'html' as an + 'e' entry, there is no need to also create a 'w' one. + + - There must be no empty or malformed lines, or comments, in the wordlist + file. Extension keywords must have no leading dot (e.g., 'exe', not '.exe'), + and all keywords should be NOT url-encoded (e.g., 'Program Files', not + 'Program%20Files'). No keyword should exceed 64 characters. + + - Any valuable dictionary can be tagged with an optional '#ro' line at the + beginning. This prevents it from being loaded in read-write mode. + + - Tread carefully; poor wordlists are one of the reasons why some web security + scanners perform worse than expected. You will almost always be better off + narrowing down or selectively extending the supplied set (and possibly + contributing back your changes upstream!), than importing a giant wordlist + scored elsewhere. diff -Nru skipfish-2.02b/doc/signatures.txt skipfish-2.10b/doc/signatures.txt --- skipfish-2.02b/doc/signatures.txt 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/doc/signatures.txt 2012-12-04 13:27:54.000000000 +0000 @@ -0,0 +1,193 @@ + +----------------- + 1. Introduction +----------------- + +With skipfish signatures it is possible to find interesting content, +or even vulnerabilities, in server responses. The signatures follow +a Snort-like syntax and most keywords behave similarly as well. + +Signatures focus on detecting web application vulnerabilities, information +leaks and can recognize interesting web applications, such as phpmyadmin +or phpinfo() pages. + +Signatures could also detect vulnerable software packages (e.g. old +WordPress instances) but this is a task that fits vulnerability scanners, +like Nessus and Nikto, better. + +----------------- + 2. Contributing +----------------- + +The current signature list is nice but far from complete. If you have +new signatures or can optimize existing ones, please help out by reporting +this via our issue tracker: + +https://code.google.com/p/skipfish/issues/entry?template=Content%20signatures + +----------------------- + 3. Signature keywords +----------------------- + +=== content:[!]"" + +The content keyword is used to specify a string that we try to match +against the server response. The value can either be a static string or +a regular expression (the latter requires the type:regex; modifier). + +Multiple content strings can be specified per signature and, unless the +signature specifies a mime type, there should be at least one. + +Modifiers can be specified per content keyword to influence how the string +is matches against the payload. For example, with the 'depth' keyword +you can specify how far in the payload we should look for the string. + +When ! is specified before the content string, the test is positive when +the string is NOT present. This is mainly useful in case your signature +has multiple content values. + +Note: content string modifiers should be specified _after_ the content +string to which they apply. + +=== content modifier: depth: + +With depth you can limit the amount of bytes we should search for the +string. Initially the depth is relative to the beginning of the +payload. However, when multiple 'content' strings are used, the depth +is relative to the first byte after the previous content match. + +Using the depth keyword has two advantages: increase performance and +increase signature accuracy. + +1) Performance: A signature that matches on a tag doesn't need + to be applied to the whole payload. Instead, a depth of 512 or even + 1024 bytes will help to improve performance. + +2) Accuracy: In a signature with two 'content' keywords, you can force the + second keyword to be searched within a very short depth of the previous + content match. + +=== content modifier: offset:<int> + +The content string searching will start at the given offset value. For +the first content string this is relative to the beginning of the +payload. For the following content strings, this is relative to the +first byte of the last match. + +=== content modifier: type:["regex|static"] + +Indicates whether the content string should be treated as a regular +expression or a static string. Content strings are treated as static by +default so you can leave this keyword out unless you're using a regular +expression. + +In a signature that has multiple content strings, static strings can be +mixed with regular expressions. You'll likely get the best performance +by starting with a static string before applying a regular expression. + +=== content modifier: regex_match:"<string>" + +Regular expressions can capture substrings and with regex_match, it is +possible to compare <string> with the first substring that is returned +by the regular expression. + +Given "Apache/2.2.14" as payload and "Apache\/([\d\.]+)" as regex, +you could use regex_match:"2.2.14" to find this specific Apache version. + +=== content modifier: nocase + +When "nocase" is specified, the content string is matched without case +sensitivity. + +This keyword requires no value. + +=== header:"<string>" + +By default signature matching is performed on the respose body. By +specifying a header name using the "header" keyword, this behavior is +changed: the matching will occur on the header value. + +The header name is not case sensitive and header signatures are treated +exactly the same as content signatures meaning that you can use multiple +content strings and their modifiers. + +=== mime:"<string>" + +The given value will be compared with the MIME type specified by the +server. This is a "begins with" comparison so a partial MIME string, +like "javascript/" will match with a server value of "javascript/foo". + +=== memo:"<string>" + +The memo message is displayed in the report when the signature +matches. The content should be a short but meaningful problem title. + +=== sev:[1-4] + +The severity with which a signature match should be reported where: + + - 1 is High + - 2 is Medium + - 3 is Low + - 4 is Info (default) + +=== prob:"<string>" + +All issue types are defined in database.h and, by default, signature +matches are reported with generic (signature) issue types. + +Using the prob keyword, a signature match can be reported as any other +known issue. For example, issue 40401 stands for interesting files and +is already used for several signatures. + +The advantage of using an existing issue ID is that it's severity and +description will be used to report the signature match. + +=== check:<int> + +Injection tests have their own ID which are specified in checks.h. Using +the "check" keyword, it is possible to bind a signature to a specific +injection test. + +The idea is to allow context specific signatures to be written. Take the +following scenario as an example: During a scan, file disclosure tests +might not fully succeed to highlight a vulnerability. Errors thrown +during these tests can still reveal that there is more than likely a +file disclosure problem. While generic server error detection will +highlight these errors, it is more useful if we can detect that these +errors are related to our tests and report them as such. + +=== id:<int> + +The unique signature ID. This is for documentation purpose and for using +the depend keyword which allows signature chaining. + +Note that the signature ID is also included in the report files +(e.g. samples.js). + +=== depend:<int> + +A signature can be made dependent on another signature by specifying it's +signature ID as the value of this keyword. This means that the signature +will be skipped unless the dependent signature was successfully matched +already. + +One example use case could be a global signature that identifies a +framework, say Wordpress, and dependent signatures that detect wordpress +specific issues. + +=== proto:"[http|https]" + +The "proto" keyword can be used to make a signature only applicable for +either "http" or "https" type URLs. + +This changes the default behavior where every signature is applied to +both http and https URLs. + +=== report:"[once|always]" + +Some signatures are to find host specific problems and only need to be +reported once. This can be acchieved by using report:"once"; + +This keywords default value is "always". + diff -Nru skipfish-2.02b/doc/skipfish.1 skipfish-2.10b/doc/skipfish.1 --- skipfish-2.02b/doc/skipfish.1 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/doc/skipfish.1 2012-12-04 13:43:50.000000000 +0000 @@ -0,0 +1,296 @@ +.\" vi:set wm=5 +.TH SKIPFISH 1 "May 6, 2012" +.SH NAME +skipfish \- web application security scanner +.SH SYNOPSIS +.B skipfish +.RI [ options ] " -o output-directory [ start-url | @url-file [ start-url2 ... ]]" +.br +.SH DESCRIPTION +.PP +\fBskipfish\fP is an active web application security reconnaissance tool. +It prepares an interactive sitemap for the targeted site by carrying out a recursive crawl and dictionary-based probes. The resulting map is then annotated with the output from a number of active (but hopefully non-disruptive) security checks. The final report generated by the tool is meant to serve as a foundation for professional web application security assessments. +.SH OPTIONS SUMMARY +.PP +.sp +.if n \{\ +.RS 4 +.\} +.fam C +.ps -1 +.nf +.BB lightgray +Authentication and access options: + \-A user:pass \- use specified HTTP authentication credentials + \-F host=IP \- pretend that \'host\' resolves to \'IP\' + \-C name=val \- append a custom cookie to all requests + \-H name=val \- append a custom HTTP header to all requests + \-b (i|f|p) \- use headers consistent with MSIE / Firefox / iPhone + \-N \- do not accept any new cookies + +Crawl scope options: + \-d max_depth \- maximum crawl tree depth (16) + \-c max_child \- maximum children to index per node (512) + \-x max_desc \- maximum descendants to index per branch (8192) + \-r r_limit \- max total number of requests to send (100000000) + \-p crawl% \- node and link crawl probability (100%) + \-q hex \- repeat probabilistic scan with given seed + \-I string \- only follow URLs matching \'string\' + \-X string \- exclude URLs matching \'string\' + \-K string \- do not fuzz parameters named \'string\' + \-D domain \- crawl cross\-site links to another domain + \-B domain \- trust, but do not crawl, another domain + \-Z \- do not descend into 5xx locations + \-O \- do not submit any forms + \-P \- do not parse HTML, etc, to find new links + +Reporting options: + \-o dir \- write output to specified directory (required) + \-M \- log warnings about mixed content / non\-SSL passwords + \-E \- log all caching intent mismatches + \-U \- log all external URLs and e\-mails seen + \-Q \- completely suppress duplicate nodes in reports + \-u \- be quiet, disable realtime progress stats + +Dictionary management options: + \-W wordlist \- use a specified read\-write wordlist (required) + \-S wordlist \- load a supplemental read\-only wordlist + \-L \- do not auto\-learn new keywords for the site + \-Y \- do not fuzz extensions in directory brute\-force + \-R age \- purge words hit more than \'age\' scans ago + \-T name=val \- add new form auto\-fill rule + \-G max_guess \- maximum number of keyword guesses to keep (256) + +Performance settings: + \-l max_req \- max requests per second (0\..000000) + \-g max_conn \- max simultaneous TCP connections, global (40) + \-m host_conn \- max simultaneous connections, per target IP (10) + \-f max_fail \- max number of consecutive HTTP errors (100) + \-t req_tmout \- total request response timeout (20 s) + \-w rw_tmout \- individual network I/O timeout (10 s) + \-i idle_tmout \- timeout on idle HTTP connections (10 s) + \-s s_limit \- response size limit (200000 B) + \-e \- do not keep binary responses for reporting + +Other settings: + \-k duration \- stop scanning after the given duration h:m:s + \--config file \- load specified configuration file + +.SH AUTHENTICATION AND ACCESS +.PP +Some sites require authentication, and skipfish supports this in different ways. First there is basic HTTP authentication, for which you can use the \-A flag. Second, and more common, are sites that require authentication on a web application level. For these sites, the best approach is to capture authenticated session cookies and provide them to skipfish using the \-C flag (multiple if needed). Last, you'll need to put some effort in protecting the session from being destroyed by excluding logout links with \-X and/or by rejecting new cookies with \-N. + +.IP "-F/--host <ip:hostname>" +Using this flag, you can set the \'\fIHost:\fP\' header value to define a custom mapping between a host and an IP (bypassing the resolver). This feature is particularly useful for not-yet-launched or legacy services that don't have the necessary DNS entries. + +.IP "-H/--header <header:value>" +When it comes to customizing your HTTP requests, you can also use the -H option to insert any additional, non-standard headers. This flag also allows the default headers to be overwritten. + +.IP "-C/--cookie <cookie:value>" +This flag can be used to add a cookie to the skipfish HTTP requests; This is particularly useful to perform authenticated scans by providing session cookies. When doing so, keep in mind that cetain URLs (e.g. /logout) may destroy your session; you can combat this in two ways: by using the -N option, which causes the scanner to reject attempts to set or delete cookies; or by using the -X option to exclude logout URLs. + +.IP "-b/--user-agent <i|f|p>" +This flag allows the user-agent to be specified where \'\fIi\fP\' stands for Internet Explorer, \'\fIf\fP\' for Firefox and \'\fIp\fP\' for iPhone. Using this flag is recommended in case the target site shows different behavior based on the user-agent (e.g some sites use different templates for mobiles and desktop clients). + +.IP "-N/--reject-cookies" +This flag causes skipfish to ignore cookies that are being set by the site. This helps to enforce stateless tests and also prevent that cookies set with \'-C\' are not overwritten. + +.IP "-A/--auth <username:password>" +For sites requiring basic HTTP authentication, you can use this flag to specify your credentials. + +.IP "--auth-form <URL>" +The login form to use with form authentication. By default skipfish will use the form's action URL to submit the credentials. If this is missing than the login data is send to the form URL. In case that is wrong, you can set the form handler URL with --auth-form-target <URL> . + +.IP "--auth-user <username>" +The username to be used during form authentication. Skipfish will try to detect the correct form field to use but if it fails to do so (and gives an error), then you can specify the form field name with --auth-user-field. + +.IP "--auth-pass <password>" +The password to be used during form authentication. Similar to auth-user, the form field name can (optionally) be set with --auth-pass-field. + +.IP "--auth-verify-url <URL>" +This URL allows skipfish to verify whether authentication was successful. This requires a URL where anonymous and authenticated requests are answered with a different response. + + +.SH CRAWLING SCOPE +.PP +Some sites may be too big to scan in a reasonable timeframe. If the site features well-defined tarpits - for example, 100,000 nearly identical user profiles as a part of a social network - these specific locations can be excluded with -X or -S. In other cases, you may need to resort to other settings: -d limits crawl depth to a specified number of subdirectories; -c limits the number of children per directory; -x limits the total number of descendants per crawl tree branch; and -r limits the total number of requests to send in a scan. + +.IP "-d/--max-crawl-depth <depth>" +Limit the depth of subdirectories being crawled (see above). +.IP "-c/--max-crawl-child <childs>" +Limit the amount of subdirectories per directory we crawl into (see above). +.IP "-x/--max-crawl-descendants <descendants>" +Limit the total number of descendants per crawl tree branch (see above). +.IP "-r/--max-request-total <request>" +The maximum number of requests can be limited with this flag. +.IP "-p/--crawl-probability <0-100>" +By specifying a percentage between 1 and 100%, it is possible to tell the crawler to follow fewer than 100% of all links, and try fewer than 100% of all dictionary entries. This \- naturally \- limits the completeness of a scan, but unlike most other settings, it does so in a balanced, non-deterministic manner. It is extremely useful when you are setting up time-bound, but periodic assessments of your infrastructure. +.IP "-q/--seed <seed>" +This flag sets the initial random seed for the crawler to a specified value. This can be used to exactly reproduce a previous scan to compare results. Randomness is relied upon most heavily in the -p mode, but also influences a couple of other scan management decisions. + +.IP "-I/--include-string <domain/path>" +With this flag, you can tell skipfish to only crawl and test URLs that match a certain string. This can help to narrow down the scope of a scan by only whitelisting certain sections of a web site (e.g. \-I /shop). + +.IP "-X/--exclude-string <domain/path>" +The \-X option can be used to exclude files / directories from the scan. This is useful to avoid session termination (i.e. by excluding /logout) or just for speeding up your scans by excluding static content directories like /icons/, /doc/, /manuals/, and other standard, mundane locations along these lines. + +.IP "-K/--skip-parameter <parameter name>" +This flag allows you to specify parameter names not to fuzz. (useful for applications that put session IDs in the URL, to minimize noise). + +.IP "-D/--include-domain <domain>" +Allows you to specify additional hosts or domains to be in-scope for the test. By default, all hosts appearing in the command-line URLs are added to the list - but you can use -D to broaden these rules. The result of this will be that the crawler will follow links and tests links that point to these additional hosts. + +.IP "-B/--trust-domain <domain>" +In some cases, you do not want to actually crawl a third-party domain, but you trust the owner of that domain enough not to worry about cross-domain content inclusion from that location. To suppress warnings, you can use the \-B option + +.IP "-Z/--skip-error-pages" +Do not crawl into pages / directories that give an error 5XX. + +.IP "-O/--no-form-submits" +Using this flag will cause forms to be ignored during the scan. + +.IP "-P/--no-html-parsing" +This flag will disable link extracting and effectively disables crawling. Using \-P is useful when you want to test one specific URL or when you want to feed skipfish a list of URLs that were collected with an external crawler. + +.SH TESTING SCOPE +.PP + +.IP "--checks" +EXPERIMENTAL: Displays the crawler injection tests. The output shows the index number (useful for \-\-checks\-toggle), the check name and whether the check is enabled. + +.IP "--checks-toggle <check1,check2,..>" +EXPERIMENTAL: Every injection test can be enabled/disabled with using this flag. As value, you need to provide the check numbers which can be obtained with the \-\-checks flag. Multiple checks can be toggled via a comma separated value (i.e. \-\-checks\-toggle 1,2 ) + +.IP "--no-injection-tests" +EXPERIMENTAL: Disables all injection tests for this scan and limits the scan to crawling and, optionally, bruteforcing. As with all scans, the output directory will contain a pivots.txt file. This file can be used to feed future scans. + +.SH REPORTING OPTIONS +.PP + +.IP "-o/--output <dir>" +The report wil be written to this location. The directory is one of the two mandatory options and must not exist upon starting the scan. + +.IP "-M/--log-mixed-content" +Enable the logging of mixed content. This is highly recommended when scanning SSL-only sites to detect insecure content inclusion via non-SSL protected links. + +.IP "-E/--log-cache-mismatches" +This will cause additonal content caching error to be reported. + +.IP "-U/--log-external-urls" +Log all external URLs and email addresses that were seen during the scan. + +.IP "-Q/--log-unique-nodes" +Enable this to completely suppress duplicate nodes in reports. + +.IP "-u/--quiet" +This will cause skipfish to suppress all console output during the scan. + +.IP "-v/--verbose" +EXPERIMENTAL: Use this flag to enable runtime reporting of, for example, problems that are detected. Can be used multiple times to increase verbosity and should be used in combination with \-u unless you run skipfish with stderr redirected to a file. + +.SH DICTIONARY MANAGEMENT +.PP +Make sure you've read the instructions provided in doc/dictionaries.txt to select the right dictionary file and configure it correctly. This step has a profound impact on the quality of scan results later on. + +.IP "-S/--wordlist <file>" +Load the specified (read-only) wordlist for use during the scan. This flag is optional but use of a dictionary is highly recommended when performing a blackbox scan as it will highlight hidden files and directories. + +.IP "-W/--rw-wordlist <file>" +Specify an initially empty file for any newly learned site-specific keywords (which will come handy in future assessments). You can use \-W\- or \-W /dev/null if you don't want to store auto-learned keywords anywhere. Typically you will want to use one of the packaged dictonaries (i.e. complete.wl) and possibly add a custom dictionary. + +.IP "-L/--no-keyword-learning" +During the scan, skipfish will try to learn and use new keywords. This flag disables that behavior and should be used when any form of brute-forcing is not desired. + +.IP "-Y/--no-extension-brute" +This flag will disable extension guessing during directory bruteforcing. + +.IP "-R <age>" +Use of this flag allows old words to be purged from wordlists. It is intended to help keeping dictionaries clean when used in recurring scans. + +.IP "-T/--form-value <name=value>" +Skipfish also features a form auto-completion mechanism in order to maximize scan coverage. The values should be non-malicious, as they are not meant to implement security checks \- but rather, to get past input validation logic. You can define additional rules, or override existing ones, with the \-T option (\-T form_field_name=field_value, e.g. \-T login=test123 \-T password=test321 - although note that \-C and \-A are a much better method of logging in). + +.IP "-G <max guesses>" +During the scan, a temporary buffer of newly detected keywords is maintained. The size of this buffer can be changed with this flag and doing so influences bruteforcing. + +.SH PERFORMANCE OPTIONS +The default performance setting should be fine for most servers but when the report indicates there were connection problems, you might want to tweak some of the values here. For unstable servers, the scan coverage is likely to improve when using low values for rate and connection flags. + +.IP "-l/--max-request-rate <rate>" +This flag can be used to limit the amount of requests per second. This is very useful when the target server can't keep up with the high amount of requests that are generated by skipfish. Keeping the amount requests per second low can also help preventing some rate-based DoS protection mechanisms from kicking in and ruining the scan. + +.IP "-g/--max-connections <number>" +The max simultaneous TCP connections (global) can be set with this flag. + +.IP "-m/--max-host-connections <number>" +The max simultaneous TCP connections, per target IP, can be set with this flag. + +.IP "-f/--max-failed-requests <number>" +Controls the maximum number of consecutive HTTP errors you are willing to see before aborting the scan. For large scans, you probably want to set a higher value here. + +.IP "-t/--request-timeout <timeout>" +Set the total request timeout, to account for really slow or really fast sites. + +.IP "-w/--network-timeout <timeout>" +Set the network I/O timeout. + +.IP "-i/--idle-timeout <timeout>" +Specify the timeout for idle HTTP connections. + +.IP "-s/--response-size <size>" +Sets the maximum length of a response to fetch and parse (longer responses will be truncated). + +.IP "-e/--discard-binary" +This prevents binary documents from being kept in memory for reporting purposes, and frees up a lot of RAM. + +.IP "--flush-to-disk" +This causes request / response data to be flushed to disk instead of being kept in memory. As a result, the memory usage for large scans will be significant lower. + +.SH EXAMPLES +\fBScan type: config\fP +.br +skipfish \-\-config config/example.conf http://example.com +.br + +.br +\fBScan type: quick\fP +.br +skipfish \-o output/dir/ http://example.com +.br + +.br +\fBScan type: extensive bruteforce\fP +.br +skipfish [...other options..] \fI\-S dictionaries/complete.wl\fP http://example.com +.br + +.br +\fBScan type: without bruteforcing\fP +.br +skipfish [...other options..] -LY http://example.com +.br + +\fBScan type: authenticated (basic)\fP +.br +skipfish [...other options..] \fI-A username:password\fP http://example.com +.br + +\fBScan type: authenticated (cookie)\fP +.br +skipfish [...other options..] \-C jsession=myauthcookiehere \-X /logout http://example.com +.br + +\fBScan type: flaky server\fP +.br +skipfish [...other options..] -l 5 -g 2 -t 30 -i 15 http://example.com +.br + +.SH NOTES +The default values for all flags can be viewed by running \'./skipfish -h\' . + +.SH AUTHOR +skipfish was written by Michal Zalewski <lcamtuf@google.com>, +with contributions from Niels Heinen <heinenn@google.com>, +Sebastian Roschke <s.roschke@googlemail.com>, and other parties. +.PP +This manual page was written with the help of Thorsten Schifferdecker <tsd@debian.systs.org>. diff -Nru skipfish-2.02b/http_client.c skipfish-2.10b/http_client.c --- skipfish-2.02b/http_client.c 2011-07-03 06:49:04.000000000 +0000 +++ skipfish-2.10b/http_client.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2632 +0,0 @@ -/* - skipfish - high-performance, single-process asynchronous HTTP client - -------------------------------------------------------------------- - - Author: Michal Zalewski <lcamtuf@google.com> - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <ctype.h> -#include <string.h> -#include <netdb.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/poll.h> -#include <fcntl.h> -#include <sys/time.h> -#include <time.h> - -#include <openssl/ssl.h> -#include <openssl/err.h> -#include <idna.h> -#include <zlib.h> - -#include "types.h" -#include "alloc-inl.h" -#include "string-inl.h" -#include "database.h" - -#include "http_client.h" - -/* Assorted exported settings: */ - -u32 max_connections = MAX_CONNECTIONS, - max_conn_host = MAX_CONN_HOST, - max_requests = MAX_REQUESTS, - max_fail = MAX_FAIL, - idle_tmout = IDLE_TMOUT, - resp_tmout = RESP_TMOUT, - rw_tmout = RW_TMOUT, - size_limit = SIZE_LIMIT; - -u8 browser_type = BROWSER_FAST; -u8 auth_type = AUTH_NONE; - -struct param_array global_http_par; - -/* Counters: */ - -u32 req_errors_net, - req_errors_http, - req_errors_cur, - req_count, - req_dropped, - queue_cur, - conn_cur, - conn_count, - conn_idle_tmout, - conn_busy_tmout, - conn_failed, - req_retried, - url_scope; - -u64 bytes_sent, - bytes_recv, - bytes_deflated, - bytes_inflated; - -u8 *auth_user, - *auth_pass; - -#ifdef PROXY_SUPPORT -u8* use_proxy; -u32 use_proxy_addr; -u16 use_proxy_port; -#endif /* PROXY_SUPPORT */ - -u8 ignore_cookies; - -/* Internal globals for queue management: */ - -static struct queue_entry* queue; -static struct conn_entry* conn; -static struct dns_entry* dns; - -#ifdef QUEUE_FILO -static struct queue_entry* q_tail; -#endif /* QUEUE_FILO */ - -static u8 tear_down_idle; - - -/* Extracts parameter value from param_array. Name is matched if - non-NULL. Returns pointer to value data, not a duplicate string; - NULL if no match found. */ - -u8* get_value(u8 type, u8* name, u32 offset, - struct param_array* par) { - - u32 i, coff = 0; - - for (i=0;i<par->c;i++) { - if (type != par->t[i]) continue; - if (name && strcasecmp((char*)par->n[i], (char*)name)) continue; - if (offset != coff) { coff++; continue; } - return par->v[i]; - } - - return NULL; - -} - - -/* Inserts or overwrites parameter value in param_array. If offset - == -1, will append parameter to list. Duplicates strings, - name and val can be NULL. */ - -void set_value(u8 type, u8* name, u8* val, - s32 offset, struct param_array* par) { - - u32 i, coff = 0, matched = -1; - - /* If offset specified, try to find an entry to replace. */ - - if (offset >= 0) - for (i=0;i<par->c;i++) { - if (type != par->t[i]) continue; - if (name && (!par->n[i] || strcasecmp((char*)par->n[i], (char*)name))) - continue; - if (offset != coff) { coff++; continue; } - matched = i; - break; - } - - if (matched == -1) { - - /* No offset or no match - append to the end of list. */ - - par->t = ck_realloc(par->t, (par->c + 1) * sizeof(u8)); - par->n = ck_realloc(par->n, (par->c + 1) * sizeof(u8*)); - par->v = ck_realloc(par->v, (par->c + 1) * sizeof(u8*)); - par->t[par->c] = type; - par->n[par->c] = ck_strdup(name); - par->v[par->c] = ck_strdup(val); - par->c++; - - } else { - - /* Matched - replace name & value. */ - - ck_free(par->n[matched]); - ck_free(par->v[matched]); - par->n[matched] = ck_strdup(name); - par->v[matched] = ck_strdup(val); - - } - -} - - -/* Convert a fully-qualified or relative URL string to a proper http_request - representation. Returns 0 on success, 1 on format error. */ - -u8 parse_url(u8* url, struct http_request* req, struct http_request* ref) { - - u8* cur = url; - u32 maybe_proto = strcspn((char*)url, ":/?#@"); - u8 has_host = 0, add_slash = 1; - - if (strlen((char*)url) > MAX_URL_LEN) return 1; - req->orig_url = ck_strdup(url); - - /* Interpret, skip protocol string if the URL seems to be fully-qualified; - otherwise, copy from referring URL. We could be stricter here, as - browsers bail out on seemingly invalid chars in proto names, but... */ - - if (maybe_proto && url[maybe_proto] == ':') { - - if (!case_prefix(url, "http:")) { - req->proto = PROTO_HTTP; - cur += 5; - } else if (!case_prefix(url, "https:")) { - req->proto = PROTO_HTTPS; - cur += 6; - } else return 1; - - } else { - - if (!ref || !ref->proto) return 1; - req->proto = ref->proto; - - } - - /* Interpret, skip //[login[:pass@](\[ipv4\]|\[ipv6\]|host)[:port] part of the - URL, if present. Note that "http:blarg" is a valid relative URL to most - browsers, and "//example.com/blarg" is a valid non-FQ absolute one. - We need to mimick this, which complicates the code a bit. - - We only accept /, ?, #, and : to mark the end of a host name. Some browsers - also allow \ or ;, but it's unlikely that we need to obey this. */ - - if (cur[0] == '/' && cur[1] == '/') { - - u32 path_st; - u8 *at_sign, *host, *x; - u8 has_utf = 0; - - cur += 2; - - /* Detect, skip login[:pass]@; we only use cmdline-supplied credentials or - wordlists into account. Be sure to report any embedded auth, though. - - Trivia: Firefox takes the rightmost, not the leftmost @ char into - account. Not very important, but amusing. */ - - at_sign = (u8*)strchr((char*)cur, '@'); - path_st = strcspn((char*)cur, "/?#"); - - if (at_sign && path_st > (at_sign - cur)) { - cur = at_sign + 1; - if (!req->pivot) return 1; - problem(PROB_URL_AUTH, ref, 0, url, req->pivot, 0); - } - - path_st = strcspn((char*)cur, ":/?#"); - - /* No support for IPv6 or [ip] notation for now, so let's just refuse to - parse the URL. Also, refuse excessively long domain names for sanity. */ - - if (*cur == '[') return 1; - if (path_st > MAX_DNS_LEN) return 1; - - x = host = ck_memdup(cur, path_st + 1); - host[path_st] = 0; - - /* Scan, normalize extracted host name. */ - - while (*x) { - - switch (*x) { - - case 'A' ... 'Z': - *x = tolower(*x); - break; - - case 'a' ... 'z': - case '0' ... '9': - case '.': - case '-': - case '_': - break; - - case 0x80 ... 0xff: - has_utf = 1; - break; - - default: - /* Uh-oh, invalid characters in a host name - abandon ship. */ - ck_free(host); - return 1; - - } - - x++; - - } - - /* Host names that contained high bits need to be converted to Punycode - in order to resolve properly. */ - - if (has_utf) { - - char* output = 0; - - if (idna_to_ascii_8z((char*)host, &output, 0) != IDNA_SUCCESS || - strlen(output) > MAX_DNS_LEN) { - ck_free(host); - free(output); - return 1; - } - - ck_free(host); - host = ck_strdup((u8*)output); - free(output); - - } - - req->host = host; - cur += path_st; - - /* All right, moving on: if host name is followed by :, let's try to - parse and validate port number; otherwise, assume 80 / 443, depending - on protocol. */ - - if (*cur == ':') { - - u32 digit_cnt = strspn((char*)++cur, "0123456789"); - u32 port = atoi((char*)cur); - if (!digit_cnt || (cur[digit_cnt] && !strchr("/?#", cur[digit_cnt]))) - return 1; - req->port = port; - cur += digit_cnt; - - } else { - - if (req->proto == PROTO_HTTPS) req->port = 443; else req->port = 80; - - } - - has_host = 1; - - } else { - - /* No host name found - copy from referring request instead. */ - - if (!ref || !ref->host) return 1; - - req->host = ck_strdup(ref->host); - req->addr = ref->addr; - req->port = ref->port; - - } - - if (!*cur || *cur == '#') { - u32 i; - - /* No-op path. If the URL does not specify host (e.g., #foo), copy - everything from referring request, call it a day. Otherwise - (e.g., http://example.com#foo), let tokenize_path() run to - add NULL-"" entry to the list. */ - - if (!has_host) { - for (i=0;i<ref->par.c;i++) - if (PATH_SUBTYPE(ref->par.t[i]) || QUERY_SUBTYPE(ref->par.t[i])) - set_value(ref->par.t[i], ref->par.n[i], ref->par.v[i], -1, &req->par); - return 0; - } - - } - - if (!has_host && *cur == '?') { - u32 i; - - /* URL begins with ? and does not specify host (e.g., ?foo=bar). Copy all - path segments, but no query, then fall through to parse the query - string. */ - - for (i=0;i<ref->par.c;i++) - if (PATH_SUBTYPE(ref->par.t[i])) - set_value(ref->par.t[i], ref->par.n[i], ref->par.v[i], -1, &req->par); - - /* In this case, we do not want tokenize_path() to tinker with the path - in any way. */ - - add_slash = 0; - - } else if (!has_host && *cur != '/') { - - /* The URL does not begin with / or ?, and does not specify host (e.g., - foo/bar?baz). Copy path from referrer, but drop the last "proper" - path segment and everything that follows it. This mimicks browser - behavior (for URLs ending with /, it just drops the final NULL-"" - pair). */ - - u32 i; - u32 path_cnt = 0, path_cur = 0; - - for (i=0;i<ref->par.c;i++) - if (ref->par.t[i] == PARAM_PATH) path_cnt++; - - for (i=0;i<ref->par.c;i++) { - if (ref->par.t[i] == PARAM_PATH) path_cur++; - if (path_cur < path_cnt && PATH_SUBTYPE(ref->par.t[i])) - set_value(ref->par.t[i], ref->par.n[i], ref->par.v[i], -1, &req->par); - } - - } - - /* Tokenize the remaining path on top of what we parsed / copied over. */ - - tokenize_path(cur, req, add_slash); - return 0; - -} - - -/* URL-decodes a string. 'Plus' parameter governs the behavior on + - signs (as they have a special meaning only in query params, not in path). */ - -u8* url_decode_token(u8* str, u32 len, u8 plus) { - u8 *ret = ck_alloc(len + 1); - u8 *src = str, *dst = ret; - char *hex_str = "0123456789abcdef"; - - while (len--) { - u8 c = *(src++); - char *f, *s; - - if (plus && c == '+') c = ' '; - - if (c == '%' && len >= 2 && - (f = strchr(hex_str, tolower(src[0]))) && - (s = strchr(hex_str, tolower(src[1])))) { - c = ((f - hex_str) << 4) | (s - hex_str); - src += 2; len -= 2; - } - - /* We can't handle NUL-terminators gracefully when deserializing request - parameters, because param_array values are NUL-terminated themselves. - Let's encode \0 as \xFF instead, and hope nobody notices. */ - - if (!c) c = 0xff; - - *(dst++) = c; - - } - - *(dst++) = 0; - - ret = ck_realloc(ret, dst - ret); - - return ret; -} - - -/* URL-encodes a string according to custom rules. The assumption here is that - the data is already tokenized at "special" boundaries such as ?, =, &, /, - ;, !, $, and , so these characters must always be escaped if present in - tokens. We otherwise let pretty much everything else go through, as it - may help with the exploitation of certain vulnerabilities. */ - -u8* url_encode_token(u8* str, u32 len, u8 also_slash) { - - u8 *ret = ck_alloc(len * 3 + 1); - u8 *src = str, *dst = ret; - - while (len--) { - u8 c = *(src++); - - if (c <= 0x20 || c >= 0x80 || strchr("#%&=+;,!$?", c) || - (also_slash && c == '/')) { - if (c == 0xFF) c = 0; - sprintf((char*)dst, "%%%02X", c); - dst += 3; - } else *(dst++) = c; - - } - - *(dst++) = 0; - - ret = ck_realloc(ret, dst - ret); - - return ret; - -} - - -/* Split path at known "special" character boundaries, URL decode values, - then put them in the provided http_request struct. */ - -void tokenize_path(u8* str, struct http_request* req, u8 add_slash) { - - u8* cur; - u8 know_dir = 0; - - while (*str == '/') str++; - cur = str; - - /* Parse path elements first. */ - - while (*cur && !strchr("?#", *cur)) { - - u32 next_seg, next_eq; - - u8 *name = NULL, *value = NULL; - u8 first_el = (str == cur); - - - if (first_el || *cur == '/') { - - /* Optimize out //, /\0, /./, and /.\0. They do indicate - we are looking at a directory, so mark this. */ - - if (!first_el && (cur[1] == '/' || !cur[1])) { - cur++; - know_dir = 1; - continue; - } - - if (cur[0 + !first_el] == '.' && (cur[1 + !first_el] == '/' || - !cur[1 + !first_el])) { - cur += 1 + !first_el; - know_dir = 1; - continue; - } - - /* Also optimize out our own \.\ prefix injected in directory - probes. This is to avoid recursion if it actually worked in some - way. */ - - if (!prefix(cur, "/\\.\\") && (cur[4] == '/' || !cur[4])) { - cur += 4; - continue; - } - - if (!case_prefix(cur, "/%5c.%5c") && - (cur[8] == '/' || !cur[8])) { - cur += 8; - continue; - } - - /* If we encountered /../ or /..\0, remove everything up to and - including the last "true" path element. It's also indicative - of a directory, by the way. */ - - if (cur[0 + !first_el] == '.' && cur[1 + !first_el] == '.' && - (cur[2 + !first_el] == '/' || !cur[2 + !first_el])) { - - u32 i, last_p = req->par.c; - - for (i=0;i<req->par.c;i++) - if (req->par.t[i] == PARAM_PATH) last_p = i; - - for (i=last_p;i<req->par.c;i++) { - req->par.t[i] = PARAM_NONE; - } - - cur += 2 + !first_el; - know_dir = 1; - continue; - - } - - } - - /* If we're here, we have an actual item to add; cur points to - the string if it's the first element, or to field separator - if one of the subsequent ones. */ - - next_seg = strcspn((char*)cur + 1, "/;,!$?#") + 1, - next_eq = strcspn((char*)cur + 1, "=/;,!$?#") + 1; - know_dir = 0; - - if (next_eq < next_seg) { - name = url_decode_token(cur + !first_el, next_eq - !first_el, 0); - value = url_decode_token(cur + next_eq + 1, next_seg - next_eq - 1, 0); - } else { - value = url_decode_token(cur + !first_el, next_seg - !first_el, 0); - } - - /* If the extracted segment is just '.' or '..', but is followed by - something else than '/', skip one separator. */ - - if (!name && cur[next_seg] && cur[next_seg] != '/' && - (!strcmp((char*)value, ".") || !strcmp((char*)value, ".."))) { - - next_seg = strcspn((char*)cur + next_seg + 1, "/;,!$?#") + next_seg + 1, - - ck_free(name); - ck_free(value); - - value = url_decode_token(cur + !first_el, next_seg - !first_el, 0); - - } - - - switch (first_el ? '/' : *cur) { - - case ';': set_value(PARAM_PATH_S, name, value, -1, &req->par); break; - case ',': set_value(PARAM_PATH_C, name, value, -1, &req->par); break; - case '!': set_value(PARAM_PATH_E, name, value, -1, &req->par); break; - case '$': set_value(PARAM_PATH_D, name, value, -1, &req->par); break; - default: set_value(PARAM_PATH, name, value, -1, &req->par); - - } - - ck_free(name); - ck_free(value); - - cur += next_seg; - - } - - /* If the last segment was /, /./, or /../, *or* if we never added - anything to the path to begin with, we want to store a NULL-"" - entry to denote it's a directory. */ - - if (know_dir || (add_slash && (!*str || strchr("?#", *str)))) - set_value(PARAM_PATH, NULL, (u8*)"", -1, &req->par); - - /* Deal with regular query parameters now. This is much simpler, - obviously. */ - - while (*cur && !strchr("#", *cur)) { - - u32 next_seg = strcspn((char*)cur + 1, "#&;,!$") + 1; - u32 next_eq = strcspn((char*)cur + 1, "=#&;,!$") + 1; - u8 *name = NULL, *value = NULL; - - /* foo=bar syntax... */ - - if (next_eq < next_seg) { - name = url_decode_token(cur + 1, next_eq - 1, 1); - value = url_decode_token(cur + next_eq + 1, next_seg - next_eq - 1, 1); - } else { - value = url_decode_token(cur + 1, next_seg - 1, 1); - } - - switch (*cur) { - - case ';': set_value(PARAM_QUERY_S, name, value, -1, &req->par); break; - case ',': set_value(PARAM_QUERY_C, name, value, -1, &req->par); break; - case '!': set_value(PARAM_QUERY_E, name, value, -1, &req->par); break; - case '$': set_value(PARAM_QUERY_D, name, value, -1, &req->par); break; - default: set_value(PARAM_QUERY, name, value, -1, &req->par); - - } - - ck_free(name); - ck_free(value); - - cur += next_seg; - - } - -} - - -/* Reconstructs URI from http_request data. Includes protocol and host - if with_host is non-zero. */ - -u8* serialize_path(struct http_request* req, u8 with_host, u8 with_post) { - u32 i, cur_pos; - u8 got_search = 0; - u8* ret; - - NEW_STR(ret, cur_pos); - -#define ASD(_p3) ADD_STR_DATA(ret, cur_pos, _p3) - - /* For human-readable uses... */ - - if (with_host) { - ASD("http"); - if (req->proto == PROTO_HTTPS) ASD("s"); - ASD("://"); - ASD(req->host); - - if ((req->proto == PROTO_HTTP && req->port != 80) || - (req->proto == PROTO_HTTPS && req->port != 443)) { - u8 port[7]; - sprintf((char*)port, ":%u", req->port); - ASD(port); - } - - } - - /* First print path... */ - - for (i=0;i<req->par.c;i++) - if (PATH_SUBTYPE(req->par.t[i])) { - - switch (req->par.t[i]) { - - case PARAM_PATH_S: ASD(";"); break; - case PARAM_PATH_C: ASD(","); break; - case PARAM_PATH_E: ASD("!"); break; - case PARAM_PATH_D: ASD("$"); break; - default: ASD("/"); - - } - - if (req->par.n[i]) { - u32 len = strlen((char*)req->par.n[i]); - u8* str = url_encode_token(req->par.n[i], len, 1); - ASD(str); ASD("="); - ck_free(str); - } - if (req->par.v[i]) { - u32 len = strlen((char*)req->par.v[i]); - u8* str = url_encode_token(req->par.v[i], len, 1); - ASD(str); - ck_free(str); - } - - } - - /* Then actual parameters. */ - - for (i=0;i<req->par.c;i++) - if (QUERY_SUBTYPE(req->par.t[i])) { - - if (!got_search) { - ASD("?"); - got_search = 1; - } else switch (req->par.t[i]) { - - case PARAM_QUERY_S: ASD(";"); break; - case PARAM_QUERY_C: ASD(","); break; - case PARAM_QUERY_E: ASD("!"); break; - case PARAM_QUERY_D: ASD("$"); break; - default: ASD("&"); - - } - - if (req->par.n[i]) { - u32 len = strlen((char*)req->par.n[i]); - u8* str = url_encode_token(req->par.n[i], len, 0); - ASD(str); ASD("="); - ck_free(str); - } - if (req->par.v[i]) { - u32 len = strlen((char*)req->par.v[i]); - u8* str = url_encode_token(req->par.v[i], len, 0); - ASD(str); - ck_free(str); - } - - } - - got_search = 0; - - if (with_post) - for (i=0;i<req->par.c;i++) - if (POST_SUBTYPE(req->par.t[i])) { - - if (!got_search) { - ASD(" DATA:"); - got_search = 1; - } else ASD("&"); - - if (req->par.n[i]) { - u32 len = strlen((char*)req->par.n[i]); - u8* str = url_encode_token(req->par.n[i], len, 0); - ASD(str); ASD("="); - ck_free(str); - } - if (req->par.v[i]) { - u32 len = strlen((char*)req->par.v[i]); - u8* str = url_encode_token(req->par.v[i], len, 0); - ASD(str); - ck_free(str); - } - - } - -#undef ASD - - TRIM_STR(ret, cur_pos); - return ret; - -} - - -/* Looks up IP for a particular host, returns data in network order. - Uses standard resolver, so it is slow and blocking, but we only - expect to call it a couple of times during a typical assessment. - There are some good async DNS libraries to consider in the long run. */ - -u32 maybe_lookup_host(u8* name) { - struct hostent* h; - struct dns_entry *d = dns, *prev = NULL; - u32 ret_addr = 0; - struct in_addr in; - -#ifdef PROXY_SUPPORT - - /* If configured to use proxy, look up proxy IP once; and return that - address for all host names. */ - - if (use_proxy) { - - if (!use_proxy_addr) { - - /* Don't bother resolving raw IP addresses, naturally. */ - - if (inet_aton((char*)use_proxy, &in)) - return (use_proxy_addr = (u32)in.s_addr); - - h = gethostbyname((char*)use_proxy); - - /* If lookup fails with a transient error, be nice - try again. */ - - if (!h && h_errno == TRY_AGAIN) h = gethostbyname((char*)name); - - if (!h || !(use_proxy_addr = *(u32*)h->h_addr_list[0])) - FATAL("Unable to resolve proxy host name '%s'.", use_proxy); - - } - - return use_proxy_addr; - - } - - /* If no proxy... */ - -#endif /* PROXY_SUPPORT */ - - /* Don't bother resolving raw IP addresses, naturally. */ - - if (inet_aton((char*)name, &in)) - return (u32)in.s_addr; - - while (d) { - if (!strcasecmp((char*)name, (char*)d->name)) return d->addr; - prev = d; - d = d->next; - } - - h = gethostbyname((char*)name); - - /* If lookup fails with a transient error, be nice - try again. */ - - if (!h && h_errno == TRY_AGAIN) h = gethostbyname((char*)name); - - if (h) { - - u32 i = 0; - - /* For each address associated with the host, see if we have any - other hosts that resolved to that same IP. If yes, return - that address; otherwise, just return first. This is for HTTP - performance and bookkeeping reasons. */ - - while (h->h_addr_list[i]) { - d = dns; - while (d) { - if (d->addr == *(u32*)h->h_addr_list[i]) { - ret_addr = d->addr; - goto dns_got_name; - } - d = d->next; - } - i++; - } - - ret_addr = *(u32*)h->h_addr_list[0]; - - } - -dns_got_name: - - if (!prev) d = dns = ck_alloc(sizeof(struct dns_entry)); - else d = prev->next = ck_alloc(sizeof(struct dns_entry)); - - d->name = ck_strdup(name); - d->addr = ret_addr; - - return ret_addr; - -} - - -/* Creates an ad hoc DNS cache entry, to override NS lookups. */ - -void fake_host(u8* name, u32 addr) { - struct dns_entry *d = dns, *prev = dns; - - while (d && d->next) { prev = d ; d = d->next;} - - if (!dns) d = dns = ck_alloc(sizeof(struct dns_entry)); - else d = prev->next = ck_alloc(sizeof(struct dns_entry)); - - d->name = ck_strdup(name); - d->addr = addr; - -} - - -/* Prepares a serialized HTTP buffer to be sent over the network. */ - -u8* build_request_data(struct http_request* req) { - - u8 *ret_buf, *ck_buf, *pay_buf, *path; - u32 ret_pos, ck_pos, pay_pos, i; - u8 req_type = PARAM_NONE; - - if (req->proto == PROTO_NONE) - FATAL("uninitialized http_request"); - - NEW_STR(ret_buf, ret_pos); - - path = serialize_path(req, 0, 0); - -#define ASD(_p3) ADD_STR_DATA(ret_buf, ret_pos, _p3) - - if (req->method) ASD(req->method); else ASD((u8*)"GET"); - ASD(" "); - -#ifdef PROXY_SUPPORT - - /* For non-CONNECT proxy requests, insert http://host[:port] too. */ - - if (use_proxy && req->proto == PROTO_HTTP) { - ASD("http://"); - ASD(req->host); - - if (req->port != 80) { - char port[7]; - sprintf((char*)port, ":%u", req->port); - ASD(port); - } - - } - -#endif /* PROXY_SUPPORT */ - - ASD(path); - ASD(" HTTP/1.1\r\n"); - ck_free(path); - - ASD("Host: "); - ASD(req->host); - - if ((req->proto == PROTO_HTTP && req->port != 80) || - (req->proto == PROTO_HTTPS && req->port != 443)) { - char port[7]; - sprintf((char*)port, ":%u", req->port); - ASD(port); - } - - ASD("\r\n"); - - /* Insert generic browser headers first. */ - - if (browser_type == BROWSER_FAST) { - - ASD("Accept-Encoding: gzip\r\n"); - ASD("Connection: keep-alive\r\n"); - - if (!GET_HDR((u8*)"User-Agent", &req->par)) - ASD("User-Agent: Mozilla/5.0 SF/" VERSION "\r\n"); - - /* Some servers will reject to gzip responses unless "Mozilla/..." - is seen in User-Agent. Bleh. */ - - } else if (browser_type == BROWSER_FFOX) { - - if (!GET_HDR((u8*)"User-Agent", &req->par)) - ASD("User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; " - "rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 SF/" VERSION "\r\n"); - - ASD("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;" - "q=0.8\r\n"); - - if (!GET_HDR((u8*)"Accept-Language", &req->par)) - ASD("Accept-Language: en-us,en\r\n"); - - ASD("Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"); - ASD("Keep-Alive: 300\r\n"); - ASD("Connection: keep-alive\r\n"); - - } else if (browser_type == BROWSER_MSIE) { - - ASD("Accept: */*\r\n"); - - if (!GET_HDR((u8*)"Accept-Language", &req->par)) - ASD("Accept-Language: en,en-US;q=0.5\r\n"); - - if (!GET_HDR((u8*)"User-Agent", &req->par)) - ASD("User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; " - "Trident/4.0; .NET CLR 1.1.4322; InfoPath.1; .NET CLR " - "2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; SF/" - VERSION ")\r\n"); - - ASD("Accept-Encoding: gzip, deflate\r\n"); - ASD("Connection: Keep-Alive\r\n"); - - } else /* iPhone */ { - - if (!GET_HDR((u8*)"User-Agent", &req->par)) - ASD("User-Agent: Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_1 like Mac OS " - "X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 " - "Mobile/8B117 Safari/6531.22.7 SF/" VERSION "\r\n"); - - ASD("Accept: application/xml,application/xhtml+xml,text/html;q=0.9," - "text/plain;q=0.8,image/png,*/*;q=0.5\r\n"); - - if (!GET_HDR((u8*)"Accept-Language", &req->par)) - ASD("Accept-Language: en-us\r\n"); - - ASD("Accept-Encoding: gzip, deflate\r\n"); - ASD("Connection: keep-alive\r\n"); - - } - - /* Request a limited range up front to minimize unwanted traffic. - Note that some Oracle servers apparently fail on certain ranged - requests, so allowing -H override seems like a good idea. */ - - if (!GET_HDR((u8*)"Range", &global_http_par)) { - u8 limit[32]; - sprintf((char*)limit, "Range: bytes=0-%u\r\n", size_limit - 1); - ASD(limit); - } - - /* Include a dummy "Referer" header, to avoid certain XSRF checks. */ - - if (!GET_HDR((u8*)"Referer", &req->par)) { - ASD("Referer: http"); - if (req->proto == PROTO_HTTPS) ASD("s"); - ASD("://"); - ASD(req->host); - ASD("/\r\n"); - } - - /* Take care of HTTP authentication next. */ - - if (auth_type == AUTH_BASIC) { - u8* lp = ck_alloc(strlen((char*)auth_user) + strlen((char*)auth_pass) + 2); - u8* lpb64; - - sprintf((char*)lp, "%s:%s", auth_user, auth_pass); - - lpb64 = b64_encode(lp, strlen((char*)lp)); - - ASD("Authorization: Basic "); - ASD(lpb64); - ASD("\r\n"); - - ck_free(lpb64); - ck_free(lp); - - } - - /* Append any other requested headers and cookies. */ - - NEW_STR(ck_buf, ck_pos); - - for (i=0;i<req->par.c;i++) { - if (req->par.t[i] == PARAM_HEADER) { - ASD(req->par.n[i]); - ASD(": "); - ASD(req->par.v[i]); - ASD("\r\n"); - } else if (req->par.t[i] == PARAM_COOKIE) { - if (ck_pos) ADD_STR_DATA(ck_buf, ck_pos, ";"); - ADD_STR_DATA(ck_buf, ck_pos, req->par.n[i]); - ADD_STR_DATA(ck_buf, ck_pos, "="); - ADD_STR_DATA(ck_buf, ck_pos, req->par.v[i]); - } - } - - /* Also include extra globals, if any (but avoid dupes). */ - - for (i=0;i<global_http_par.c;i++) { - if (global_http_par.t[i] == PARAM_HEADER && - !GET_HDR(global_http_par.n[i], &req->par)) { - ASD(global_http_par.n[i]); - ASD(": "); - ASD(global_http_par.v[i]); - ASD("\r\n"); - } else if (global_http_par.t[i] == PARAM_COOKIE && - !GET_CK(global_http_par.n[i], &req->par)) { - if (ck_pos) ADD_STR_DATA(ck_buf, ck_pos, ";"); - ADD_STR_DATA(ck_buf, ck_pos, global_http_par.n[i]); - ADD_STR_DATA(ck_buf, ck_pos, "="); - ADD_STR_DATA(ck_buf, ck_pos, global_http_par.v[i]); - } - } - - if (ck_pos) { - ASD("Cookie: "); - ASD(ck_buf); - ASD("\r\n"); - } - - ck_free(ck_buf); - - /* Now, let's serialize the payload, if necessary. */ - - for (i=0;i<req->par.c;i++) { - switch (req->par.t[i]) { - case PARAM_POST_F: - case PARAM_POST_O: - req_type = req->par.t[i]; - break; - case PARAM_POST: - if (req_type == PARAM_NONE) req_type = PARAM_POST; - break; - } - } - - NEW_STR(pay_buf, pay_pos); - - if (req_type == PARAM_POST) { - - /* The default case: application/x-www-form-urlencoded. */ - - for (i=0;i<req->par.c;i++) - if (req->par.t[i] == PARAM_POST) { - if (pay_pos) ADD_STR_DATA(pay_buf, pay_pos, "&"); - if (req->par.n[i]) { - u32 len = strlen((char*)req->par.n[i]); - u8* str = url_encode_token(req->par.n[i], len, 0); - ADD_STR_DATA(pay_buf, pay_pos, str); - ADD_STR_DATA(pay_buf, pay_pos, "="); - ck_free(str); - } - if (req->par.v[i]) { - u32 len = strlen((char*)req->par.v[i]); - u8* str = url_encode_token(req->par.v[i], len, 0); - ADD_STR_DATA(pay_buf, pay_pos, str); - ck_free(str); - } - } - - ASD("Content-Type: application/x-www-form-urlencoded\r\n"); - - } else if (req_type == PARAM_POST_O) { - - /* Opaque, non-escaped data of some sort. */ - - for (i=0;i<req->par.c;i++) - if (req->par.t[i] == PARAM_POST_O && req->par.v[i]) - ADD_STR_DATA(pay_buf, pay_pos, req->par.v[i]); - - ASD("Content-Type: text/plain\r\n"); - - } else if (req_type == PARAM_POST_F) { - u8 bound[20]; - - /* MIME envelopes: multipart/form-data */ - - sprintf((char*)bound, "sf%u", R(1000000)); - - for (i=0;i<req->par.c;i++) - if (req->par.t[i] == PARAM_POST || req->par.t[i] == PARAM_POST_F) { - - ADD_STR_DATA(pay_buf, pay_pos, "--"); - ADD_STR_DATA(pay_buf, pay_pos, bound); - ADD_STR_DATA(pay_buf, pay_pos, "\r\n" - "Content-Disposition: form-data; name=\""); - if (req->par.n[i]) - ADD_STR_DATA(pay_buf, pay_pos, req->par.n[i]); - - if (req->par.t[i] == PARAM_POST_F) { - u8 tmp[64]; - sprintf((char*)tmp, "\"; filename=\"sfish%u." DUMMY_EXT "\"\r\n" - "Content-Type: " DUMMY_MIME "\r\n\r\n", R(16)); - ADD_STR_DATA(pay_buf, pay_pos, tmp); - ADD_STR_DATA(pay_buf, pay_pos, new_xss_tag((u8*)DUMMY_FILE)); - register_xss_tag(req); - } else { - ADD_STR_DATA(pay_buf, pay_pos, "\"\r\n\r\n"); - if (req->par.v[i]) - ADD_STR_DATA(pay_buf, pay_pos, req->par.v[i]); - } - - ADD_STR_DATA(pay_buf, pay_pos, "\r\n"); - } - - ADD_STR_DATA(pay_buf, pay_pos, "--"); - ADD_STR_DATA(pay_buf, pay_pos, bound); - ADD_STR_DATA(pay_buf, pay_pos, "--\r\n"); - - ASD("Content-Type: multipart/form-data; boundary="); - ASD(bound); - ASD("\r\n"); - - } else if (req_type == 0) ASD("\r\n"); - - /* Finalize HTTP payload... */ - - for (i=0;i<pay_pos;i++) - if (pay_buf[i] == 0xff) pay_buf[i] = 0x00; - - TRIM_STR(pay_buf, pay_pos); - - if (pay_pos) { - u8 cl[40]; - sprintf((char*)cl, "Content-Length: %u\r\n\r\n", pay_pos); - ASD(cl); - ASD(pay_buf); - } - - ck_free(pay_buf); - -#undef ASD - - /* Phew! */ - - TRIM_STR(ret_buf, ret_pos); - return ret_buf; - -} - - -/* Internal helper for parsing lines for parse_response(), etc. */ - -static u8* grab_line(u8* data, u32* cur_pos, u32 data_len) { - u8 *cur_ptr = data + *cur_pos, - *start_ptr = cur_ptr, - *end_ptr = data + data_len, - *ret; - - if (start_ptr == end_ptr) return 0; - while (cur_ptr < end_ptr && *cur_ptr != '\n') cur_ptr++; - if (cur_ptr != end_ptr) cur_ptr++; - - *cur_pos += cur_ptr - start_ptr; - - while (cur_ptr > start_ptr && strchr("\r\n", *(cur_ptr-1))) cur_ptr--; - - ret = ck_alloc(cur_ptr - start_ptr + 1); - memcpy(ret, start_ptr, cur_ptr - start_ptr); - ret[cur_ptr - start_ptr] = 0; - - return ret; - -} - - -/* Builds response fingerprint data. These fingerprints are used to - find "roughly comparable" pages based on their word length - distributions (divided into FP_SIZE buckets). */ - -void fprint_response(struct http_response* res) { - u32 i, c_len = 0, in_space = 0; - - res->sig.code = res->code; - - for (i=0;i<res->pay_len;i++) - - if (res->payload[i] <= 0x20 || - res->payload[i] == '<' || res->payload[i] == '>' || - res->payload[i] == '\'' || res->payload[i] == '"') { - if (!in_space) { - in_space = 1; - if (c_len <= FP_MAX_LEN) - res->sig.data[c_len % FP_SIZE]++; - c_len = 0; - } else c_len++; - } else { - if (in_space) { - in_space = 0; - if (c_len <= FP_MAX_LEN) - res->sig.data[c_len % FP_SIZE]++; - c_len = 0; - } else c_len++; - } - - res->sig.data[c_len % FP_SIZE]++; - -} - - -/* Parses a network buffer containing raw HTTP response received over the - network ('more' == the socket is still available for reading). Returns 0 - if response parses OK, 1 if more data should be read from the socket, - 2 if the response seems invalid, 3 if response OK but connection must be - closed. */ - -u8 parse_response(struct http_request* req, struct http_response* res, - u8* data, u32 data_len, u8 more) { - u8* cur_line = 0; - s32 pay_len = -1; - u32 cur_data_off = 0, - total_chunk = 0, - http_ver; - u8 chunked = 0, compressed = 0, must_close = 0; - - if (res->code) - FATAL("struct http_response reused! Original code '%u'.", res->code); - -#define NEXT_LINE() do { \ - if (cur_line) ck_free(cur_line); \ - cur_line = grab_line(data, &cur_data_off, data_len); \ - } while (0) - - /* First, let's do a superficial request completeness check. Be - prepared for a premature end at any point. */ - - NEXT_LINE(); /* HTTP/1.x xxx ... */ - - if (!cur_line) return more ? 1 : 2; - - if (strlen((char*)cur_line) < 7 && more) { - ck_free(cur_line); - return 1; - } - - if (prefix(cur_line, "HTTP/1.")) { - ck_free(cur_line); - return 2; - } - - /* Scan headers for Content-Length, Transfer-Encoding, etc. */ - - while (1) { - - NEXT_LINE(); /* Next header or empty line. */ - - /* If headers end prematurely, and more data might arrive, ask for - it; otherwise, just assume end of headers and continue. */ - - if (!cur_line) { - if (more) return 1; - res->warn |= WARN_PARTIAL; - break; - } - - /* Empty line indicates the beginning of a payload. */ - - if (!cur_line[0]) break; - - if (!case_prefix(cur_line, "Content-Length:")) { - - /* The value in Content-Length header would be useful for seeing if we - have all the requested data already. Reject invalid values to avoid - integer overflows, etc, though. */ - - if (sscanf((char*)cur_line + 15, "%d", &pay_len) == 1) { - if (pay_len < 0 || pay_len > 1000000000 /* 1 GB */) { - ck_free(cur_line); - return 2; - } - } else pay_len = -1; - - } else if (!case_prefix(cur_line, "Transfer-Encoding:")) { - - /* Transfer-Encoding: chunked must be accounted for to properly - determine if we received all the data when Content-Length not found. */ - - u8* x = cur_line + 18; - - while (isspace(*x)) x++; - if (!strcasecmp((char*)x, "chunked")) chunked = 1; - - } else if (!case_prefix(cur_line, "Content-Encoding:")) { - - /* Content-Encoding is good to know, too. */ - - u8* x = cur_line + 17; - - while (isspace(*x)) x++; - - if (!strcasecmp((char*)x, "deflate") || !strcasecmp((char*)x, "gzip")) - compressed = 1; - - } else if (!case_prefix(cur_line, "Connection:")) { - - u8* x = cur_line + 11; - - while (isspace(*x)) x++; - - if (!strcasecmp((char*)x, "close")) must_close = 1; - - } - } - - /* We are now at the beginning of the payload. Firstly, how about decoding - 'chunked' to see if we received a complete 0-byte terminator chunk - already? */ - - if (chunked) { - while (1) { - u32 chunk_len; - - NEXT_LINE(); /* Should be chunk size, hex. */ - - if (!cur_line || sscanf((char*)cur_line, "%x", &chunk_len) != 1) { - if (more) { ck_free(cur_line); return 1; } - res->warn |= WARN_PARTIAL; - break; - } - - if (chunk_len > 1000000000 || total_chunk > 1000000000 /* 1 GB */) { - ck_free(cur_line); - return 2; - } - - /* See if we actually enough buffer to skip the chunk. Bail out if - not and more data might be coming; otherwise, adjust chunk size - accordingly. */ - - if (cur_data_off + chunk_len > data_len) { - - if (more) { ck_free(cur_line); return 1; } - chunk_len = data_len - cur_data_off; - total_chunk += chunk_len; - - res->warn |= WARN_PARTIAL; - break; - } - - total_chunk += chunk_len; - - cur_data_off += chunk_len; - NEXT_LINE(); - - /* No newline? */ - if (!cur_line) { - if (more) return 1; - res->warn |= WARN_PARTIAL; - } - - /* All right, so that was the last, complete 0-size chunk? - Exit the loop if so. */ - - if (!chunk_len) break; - - } - - if (cur_data_off != data_len) res->warn |= WARN_TRAIL; - - } else if (pay_len == -1 && more) { - - /* If in a mode other than 'chunked', and C-L not received, but more - data might be available - try to request it. */ - - ck_free(cur_line); - return 1; - - } else if (pay_len != 1) { - - if (cur_data_off + pay_len > data_len) { - - /* If C-L seen, but not nough data in the buffer, try to request more - if possible, otherwise tag the response as partial. */ - - if (more) { ck_free(cur_line); return 1; } - res->warn |= WARN_PARTIAL; - - } else if (cur_data_off + pay_len < data_len) res->warn |= WARN_TRAIL; - - } - - /* Rewind, then properly parse HTTP headers, parsing cookies. */ - - cur_data_off = 0; - - NEXT_LINE(); - - if (strlen((char*)cur_line) < 13 || - sscanf((char*)cur_line, "HTTP/1.%u %u ", &http_ver, &res->code) != 2 || - res->code < 100 || res->code > 999) { - ck_free(cur_line); - return 2; - } - - /* Some servers, when presented with 'Range' header, will return 200 on - some queries for a particular resource, and 206 on other queries (e.g., - with query string), despite returning exactly as much data. As an - ugly workaround... */ - - if (res->code == 206) res->code = 200; - - if (http_ver == 0) must_close = 1; - - res->msg = ck_strdup(cur_line + 13); - - while (1) { - u8* val; - - NEXT_LINE(); /* Next header or empty line. */ - - if (!cur_line) return 2; - if (!cur_line[0]) break; - - /* Split field name and value */ - - val = (u8*) strchr((char*)cur_line, ':'); - if (!val) { ck_free(cur_line); return 2; } - - *val = 0; - while (isspace(*(++val))); - - if (!strcasecmp((char*)cur_line, "Set-Cookie") || - !strcasecmp((char*)cur_line, "Set-Cookie2")) { - - /* We could bother with a proper tokenizer here, but contrary to "teh - standards", browsers generally don't accept multiple cookies in - Set-Cookie headers, handle quoted-string encoding inconsistently, - etc. So let's just grab the first value naively and move on. */ - - u8* cval; - u8* orig_val; - - cval = (u8*) strchr((char*)val, ';'); - if (cval) *cval = 0; - cval = (u8*) strchr((char*)val, '='); - if (cval) { *cval = 0; cval++; } - - /* If proper value not found, use NULL name and put whatever was - found in the value field. */ - - if (!cval) { cval = val; val = 0; } - - if (cval) SET_CK(val, cval, &res->hdr); - - if (val) { - - /* New or drastically changed cookies are noteworthy. */ - - orig_val = GET_CK(val, &global_http_par); - - if (!orig_val || (strlen((char*)orig_val) != strlen((char*)cval) && - strncmp((char*)cval, (char*)orig_val, 3))) { - res->cookies_set = 1; - problem(PROB_NEW_COOKIE, req, res, val, req->pivot, 0); - } - - /* Set cookie globally, but ignore obvious attempts to delete - existing ones. */ - - if (!ignore_cookies && val && cval[0]) - SET_CK(val, cval, &global_http_par); - - } - - } else SET_HDR(cur_line, val, &res->hdr); - - /* Content-Type is worth mining for MIME, charset data at this point. */ - - if (!strcasecmp((char*)cur_line, "Content-Type")) { - - if (res->header_mime) { - - /* Duplicate Content-Type. Fetch previous value, if different, - complain. */ - - u8* tmp = GET_HDR((u8*)"Content-Type", &res->hdr); - if (strcasecmp((char*)tmp, (char*)val)) res->warn |= WARN_CFL_HDR; - - } else { - u8 *tmp = (u8*)strchr((char*)val, ';'), *cset; - - if (tmp) { - *tmp = 0; - if ((cset = (u8*)strchr((char*)tmp + 1, '='))) - res->header_charset = ck_strdup(cset + 1); - } - - res->header_mime = ck_strdup(val); - if (tmp) *tmp = ';'; - } - - } - - } - - /* At the beginning of the payload again! */ - - if (!chunked) { - - /* Identity. Ignore actual C-L data, use just as much as we collected. */ - - res->pay_len = data_len - cur_data_off; - res->payload = ck_alloc(res->pay_len + 1); - res->payload[res->pay_len] = 0; /* NUL-terminate for safer parsing. */ - - memcpy(res->payload, data + cur_data_off, res->pay_len); - - } else { - - u32 chunk_off = 0; - - /* Chunked - we should have the authoritative length of chunk - contents in total_chunk already, and the overall structure - validated, so let's just reparse quickly. */ - - res->pay_len = total_chunk; - res->payload = ck_alloc(total_chunk + 1); - res->payload[res->pay_len] = 0; - - while (1) { - u32 chunk_len; - - NEXT_LINE(); - - if (!cur_line || sscanf((char*)cur_line, "%x", &chunk_len) != 1) break; - - if (cur_data_off + chunk_len > data_len) - chunk_len = data_len - cur_data_off; - - memcpy(res->payload + chunk_off, data + cur_data_off, chunk_len); - - chunk_off += chunk_len; - cur_data_off += chunk_len; - - NEXT_LINE(); - - if (!chunk_len) break; - } - - } - - ck_free(cur_line); - - if (compressed) { - - u8* tmp_buf; - - /* Deflate or gzip - zlib can handle both the same way. We lazily allocate - a size_limit output buffer, then truncate it if necessary. */ - - z_stream d; - s32 err; - - tmp_buf = ck_alloc(size_limit + 1); - - d.zalloc = 0; - d.zfree = 0; - d.opaque = 0; - d.next_in = res->payload; - d.avail_in = res->pay_len; - d.next_out = tmp_buf; - d.avail_out = size_limit; - - /* Say hello to third-party vulnerabilities! */ - - if (inflateInit2(&d, 32 + 15) != Z_OK) { - inflateEnd(&d); - ck_free(tmp_buf); - return 2; - } - - err = inflate(&d, Z_FINISH); - inflateEnd(&d); - - if (err != Z_BUF_ERROR && err != Z_OK && err != Z_STREAM_END) { - ck_free(tmp_buf); - return 2; - } - - ck_free(res->payload); - - bytes_deflated += res->pay_len; - - res->pay_len = size_limit - d.avail_out; - res->payload = ck_realloc(tmp_buf, res->pay_len + 1); - res->payload[res->pay_len] = 0; - - - bytes_inflated += res->pay_len; - - } - -#undef NEXT_LINE - - fprint_response(res); - - return must_close ? 3 : 0; -} - - -/* Performs a deep free() of struct http_request */ - -void destroy_request(struct http_request* req) { - u32 i; - - for (i=0;i<req->par.c;i++) { - ck_free(req->par.n[i]); - ck_free(req->par.v[i]); - } - - ck_free(req->par.t); - ck_free(req->par.n); - ck_free(req->par.v); - - ck_free(req->method); - ck_free(req->host); - ck_free(req->orig_url); - ck_free(req); - -} - - -/* Performs a deep free() of struct http_response */ - -void destroy_response(struct http_response* res) { - u32 i; - - for (i=0;i<res->hdr.c;i++) { - ck_free(res->hdr.n[i]); - ck_free(res->hdr.v[i]); - } - - ck_free(res->hdr.t); - ck_free(res->hdr.n); - ck_free(res->hdr.v); - - ck_free(res->meta_charset); - ck_free(res->header_charset); - ck_free(res->header_mime); - - ck_free(res->msg); - ck_free(res->payload); - ck_free(res); - -} - - -/* Performs a deep free(), unlinking of struct queue_entry, and the - underlying request / response pair. */ - -static void destroy_unlink_queue(struct queue_entry* q, u8 keep) { - if (!keep) { - if (q->req) destroy_request(q->req); - if (q->res) destroy_response(q->res); - } - if (!q->prev) queue = q->next; else q->prev->next = q->next; -#ifdef QUEUE_FILO - if (!q->next) q_tail = q->prev; -#endif /* QUEUE_FILO */ - if (q->next) q->next->prev = q->prev; - ck_free(q); - queue_cur--; -} - - -/* Performs a deep free(), unlinking, network shutdown for struct - conn_entry, as well as the underlying queue entry, request - and response structs. */ - -static void destroy_unlink_conn(struct conn_entry* c, u8 keep) { - if (c->q) destroy_unlink_queue(c->q, keep); - if (!c->prev) conn = c->next; else c->prev->next = c->next; - if (c->next) c->next->prev = c->prev; - if (c->srv_ssl) SSL_free(c->srv_ssl); - if (c->srv_ctx) SSL_CTX_free(c->srv_ctx); - ck_free(c->write_buf); - ck_free(c->read_buf); - close(c->fd); - ck_free(c); - conn_cur--; -} - - -/* Performs struct conn_entry for reuse following a clean shutdown. */ - -static void reuse_conn(struct conn_entry* c, u8 keep) { - if (c->q) destroy_unlink_queue(c->q, keep); - c->q = 0; - ck_free(c->read_buf); - ck_free(c->write_buf); - c->read_buf = c->write_buf = NULL; - c->read_len = c->write_len = c->write_off = 0; - c->SSL_rd_w_wr = c->SSL_wr_w_rd = 0; -} - - -/* Schedules a new asynchronous request (does not make a copy of the - original http_request struct, may deallocate it immediately or - later on); req->callback() will be invoked when the request is - completed (or fails - maybe right away). */ - -void async_request(struct http_request* req) { - struct queue_entry *qe; - struct http_response *res; - - if (req->proto == PROTO_NONE || !req->callback) - FATAL("uninitialized http_request"); - - res = ck_alloc(sizeof(struct http_response)); - - req->addr = maybe_lookup_host(req->host); - - /* Don't try to issue extra requests if max_fail - consecutive failures exceeded; but still try to - wrap up the (partial) scan. */ - - if (req_errors_cur > max_fail) { - DEBUG("!!! Too many subsequent request failures!\n"); - res->state = STATE_SUPPRESS; - if (!req->callback(req, res)) { - destroy_request(req); - destroy_response(res); - } - req_dropped++; - return; - } - - /* DNS errors mean instant fail. */ - - if (!req->addr) { - DEBUG("!!! DNS error!\n"); - res->state = STATE_DNSERR; - if (!req->callback(req, res)) { - destroy_request(req); - destroy_response(res); - } - req_errors_net++; - conn_count++; - conn_failed++; - return; - } - - /* Enforce user limits. */ - - if (req_count > max_requests) { - DEBUG("!!! Total request limit exceeded!\n"); - res->state = STATE_SUPPRESS; - if (!req->callback(req, res)) { - destroy_request(req); - destroy_response(res); - } - req_dropped++; - return; - } - - /* OK, looks like we're good to go. Insert the request - into the the queue. */ - -#ifdef QUEUE_FILO - - qe = q_tail; - q_tail = ck_alloc(sizeof(struct queue_entry)); - q_tail->req = req; - q_tail->res = res; - q_tail->prev = qe; - - if (q_tail->prev) q_tail->prev->next = q_tail; - - if (!queue) queue = q_tail; - -#else - - qe = queue; - - queue = ck_alloc(sizeof(struct queue_entry)); - queue->req = req; - queue->res = res; - queue->next = qe; - - if (queue->next) queue->next->prev = queue; - -#endif /* ^QUEUE_FILO */ - - queue_cur++; - req_count++; - -} - - -/* Check SSL properties, raise security alerts if necessary. We do not perform - a very thorough validation - we do not check for valid root CAs, bad ciphers, - SSLv2 support, etc - as these are covered well by network-level security - assessment tools anyway. - - We might eventually want to check aliases or support TLS SNI. */ - -static void check_ssl(struct conn_entry* c) { - X509 *p; - - p = SSL_get_peer_certificate(c->srv_ssl); - - if (p) { - u32 cur_time = time(0); - char *issuer, *host, *req_host; - - /* Check for certificate expiration... */ - - if (ASN1_UTCTIME_cmp_time_t(p->cert_info->validity->notBefore, cur_time) - != -1 || - ASN1_UTCTIME_cmp_time_t(p->cert_info->validity->notAfter, cur_time) - != 1) - problem(PROB_SSL_CERT_DATE, c->q->req, 0, 0, - host_pivot(c->q->req->pivot), 0); - - /* Check for self-signed certs or no issuer data. */ - - issuer = X509_NAME_oneline(p->cert_info->issuer,NULL,0); - - if (!issuer || !p->name || !strcmp(issuer, p->name)) - problem(PROB_SSL_SELF_CERT, c->q->req, 0, (u8*)issuer, - host_pivot(c->q->req->pivot), 0); - else - problem(PROB_SSL_CERT, c->q->req, 0, (u8*)issuer, - host_pivot(c->q->req->pivot), 0); - - free(issuer); - - /* Extract CN= from certificate name, compare to destination host. */ - - host = strrchr(p->name, '='); - req_host = (char*)c->q->req->host; - - if (host) { - host++; - if (host[0] == '*' && host[1] == '.') { - host++; - if (strlen(req_host) > strlen(host)) - req_host += strlen(req_host) - strlen(host); - } - } - - if (!host || strcasecmp(host, req_host)) - problem(PROB_SSL_BAD_HOST, c->q->req, 0, (u8*)host, - host_pivot(c->q->req->pivot), 0); - - X509_free(p); - - } else problem(PROB_SSL_NO_CERT, c->q->req, 0, 0, - host_pivot(c->q->req->pivot), 0); - - c->ssl_checked = 1; -} - - -/* Associates a queue entry with an existing connection (if 'use_c' is - non-NULL), or creates a new connection to host (if 'use_c' NULL). */ - -static void conn_associate(struct conn_entry* use_c, struct queue_entry* q) { - struct conn_entry* c; - - if (use_c) { - - c = use_c; - c->reused = 1; - - } else { - - struct sockaddr_in sin; - - /* OK, we need to create a new connection list entry and connect - it to a target host. */ - - c = ck_alloc(sizeof(struct conn_entry)); - - conn_count++; - - c->proto = q->req->proto; - c->addr = q->req->addr; - c->port = q->req->port; - - c->fd = socket(PF_INET, SOCK_STREAM, 0); - - if (c->fd < 0) { - -connect_error: - - if (c->fd >=0) close(c->fd); - q->res->state = STATE_LOCALERR; - destroy_unlink_queue(q, q->req->callback(q->req, q->res)); - req_errors_net++; - req_errors_cur++; - - ck_free(c); - conn_failed++; - return; - } - - sin.sin_family = PF_INET; - -#ifdef PROXY_SUPPORT - sin.sin_port = htons(use_proxy ? use_proxy_port : c->port); -#else - sin.sin_port = htons(c->port); -#endif /* ^PROXY_SUPPORT */ - - memcpy(&sin.sin_addr, &q->req->addr, 4); - - fcntl(c->fd, F_SETFL, O_NONBLOCK); - - if (connect(c->fd, (struct sockaddr*) &sin, sizeof(struct sockaddr_in)) && - (errno != EINPROGRESS)) goto connect_error; - - /* HTTPS also requires SSL state to be initialized at this point. */ - - if (c->proto == PROTO_HTTPS) { - - c->srv_ctx = SSL_CTX_new(SSLv23_client_method()); - - if (!c->srv_ctx) goto connect_error; - - SSL_CTX_set_mode(c->srv_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); - - c->srv_ssl = SSL_new(c->srv_ctx); - - if (!c->srv_ssl) { - SSL_CTX_free(c->srv_ctx); - goto connect_error; - } - - SSL_set_fd(c->srv_ssl, c->fd); - SSL_set_connect_state(c->srv_ssl); - - } - - /* Make it official. */ - - c->next = conn; - conn = c; - if (c->next) c->next->prev = c; - - conn_cur++; - - } - - c->q = q; - q->c = c; - - q->res->state = STATE_CONNECT; - c->req_start = c->last_rw = time(0); - c->write_buf = build_request_data(q->req); - c->write_len = strlen((char*)c->write_buf); - -} - - -/* Processes the queue. Returns the number of queue entries remaining, - 0 if none. Will do a blocking select() to wait for socket state changes - (or timeouts) if no data available to process. This is the main - routine for the scanning loop. */ - -u32 next_from_queue(void) { - - u32 cur_time = time(0); - - if (conn_cur) { - static struct pollfd* p; - - struct conn_entry* c = conn; - u32 i = 0; - - /* First, go through all connections, handle connects, SSL handshakes, data - reads and writes, and exceptions. */ - - if (!p) - p = __DFL_ck_alloc(sizeof(struct pollfd) * max_connections); - - while (c) { - p[i].fd = c->fd; - p[i].events = POLLIN | POLLERR | POLLHUP; - if (c->write_len - c->write_off || c->SSL_rd_w_wr) - p[i].events |= POLLOUT; - p[i].revents = 0; - c = c->next; - i++; - } - - poll(p, conn_cur, 100); - - c = conn; - - for (i=0;i<conn_cur;i++) { - - struct conn_entry* next = c->next; - - /* Connection closed: see if we have any pending data to write. If yes, - fail. If not, try parse_response() to see if we have all the data. - Clean up. */ - - if (p[i].revents & (POLLERR|POLLHUP)) { - - u8 keep; - -network_error: - - keep = 0; - - /* Retry requests that were sent on old keep-alive connections - and failed instantly with no data read; might be just that - the server got bored. */ - - if (c->q && !c->q->retrying && c->reused && !c->read_len) { - - c->q->res->state = STATE_NOTINIT; - c->q->retrying = 1; - c->q->c = 0; - c->q = 0; - - req_retried++; - - } else if (c->q) { - - if (c->write_len - c->write_off || !c->read_len) { - c->q->res->state = STATE_CONNERR; - keep = c->q->req->callback(c->q->req, c->q->res); - req_errors_net++; - req_errors_cur++; - } else { - if (parse_response(c->q->req, c->q->res, c->read_buf, - c->read_len, 0) != 2) { - c->q->res->state = STATE_OK; - keep = c->q->req->callback(c->q->req, c->q->res); - if (req_errors_cur <= max_fail) - req_errors_cur = 0; - } else { - c->q->res->state = STATE_CONNERR; - keep = c->q->req->callback(c->q->req, c->q->res); - req_errors_net++; - req_errors_cur++; - } - } - - } - - destroy_unlink_conn(c, keep); - - } else - - /* Incoming data (when SSL_write() did not request a read) or - continuation of SSL_read() possible (if SSL_read() wanted to write). - Process data, call parse_response() to see if w have all we wanted. - Update event timers. */ - - if (((p[i].revents & POLLIN) && !c->SSL_wr_w_rd) || - ((p[i].revents & POLLOUT) && c->SSL_rd_w_wr)) { - - if (c->q) { - s32 read_res; - u8 p_ret; - -SSL_read_more: - - c->read_buf = ck_realloc(c->read_buf, c->read_len + READ_CHUNK + 1); - - if (c->proto == PROTO_HTTPS) { - s32 ssl_err; - - c->SSL_rd_w_wr = 0; - - read_res = SSL_read(c->srv_ssl, c->read_buf + c->read_len, - READ_CHUNK); - - if (!read_res) goto network_error; - - if (read_res < 0) { - ssl_err = SSL_get_error(c->srv_ssl, read_res); - if (ssl_err == SSL_ERROR_WANT_WRITE) c->SSL_rd_w_wr = 1; - else if (ssl_err != SSL_ERROR_WANT_READ) goto network_error; - read_res = 0; - } - - } else { - read_res = read(c->fd, c->read_buf + c->read_len, READ_CHUNK); - if (read_res <= 0) goto network_error; - } - - bytes_recv += read_res; - - c->read_len += read_res; - c->read_buf = ck_realloc(c->read_buf, c->read_len + 1); - - /* Retry reading until SSL_ERROR_WANT_READ. */ - - if (c->proto == PROTO_HTTPS && - read_res && c->read_len < size_limit) goto SSL_read_more; - - c->read_buf[c->read_len] = 0; /* NUL-terminate for sanity. */ - - /* We force final parse_response() if response length exceeded - size_limit by more than 4 kB. The assumption here is that - it is less expensive to redo the connection than it is - to continue receiving an unknown amount of extra data. */ - - p_ret = parse_response(c->q->req, c->q->res, c->read_buf, c->read_len, - (c->read_len > (size_limit + READ_CHUNK)) ? 0 : 1); - - if (!p_ret || p_ret == 3) { - - u8 keep; - - c->q->res->state = STATE_OK; - keep = c->q->req->callback(c->q->req, c->q->res); - - /* If we got all data without hitting the limit, and if - "Connection: close" is not indicated, we might want - to keep the connection for future use. */ - - if (c->read_len > (size_limit + READ_CHUNK) || p_ret) - destroy_unlink_conn(c, keep); else reuse_conn(c, keep); - - if (req_errors_cur <= max_fail) - req_errors_cur = 0; - - } else if (p_ret == 2) { - c->q->res->state = STATE_RESPERR; - destroy_unlink_conn(c, c->q->req->callback(c->q->req, c->q->res)); - req_errors_http++; - req_errors_cur++; - } else { - c->last_rw = cur_time; - c->q->res->state = STATE_RECEIVE; - } - - } else destroy_unlink_conn(c, 0); /* Unsolicited response! */ - - } else - - /* Write possible (if SSL_read() did not request a write), or - continuation of SSL_write() possible (if SSL_write() wanted to - read). Send data, update timers, etc. */ - - if (((p[i].revents & POLLOUT) && !c->SSL_rd_w_wr) || - ((p[i].revents & POLLIN) && c->SSL_wr_w_rd)) { - - if (c->write_len - c->write_off) { - s32 write_res; - - if (c->proto == PROTO_HTTPS) { - s32 ssl_err; - - c->SSL_wr_w_rd = 0; - - write_res = SSL_write(c->srv_ssl, c->write_buf + c->write_off, - c->write_len - c->write_off); - - if (!write_res) goto network_error; - - if (write_res < 0) { - ssl_err = SSL_get_error(c->srv_ssl, write_res); - if (ssl_err == SSL_ERROR_WANT_READ) c->SSL_wr_w_rd = 1; - else if (ssl_err != SSL_ERROR_WANT_WRITE) goto network_error; - write_res = 0; - } else if (!c->ssl_checked) check_ssl(c); - - } else { - write_res = write(c->fd, c->write_buf + c->write_off, - c->write_len - c->write_off); - if (write_res <= 0) goto network_error; - } - - bytes_sent += write_res; - - c->write_off += write_res; - - c->q->res->state = STATE_SEND; - - c->last_rw = cur_time; - - } - - } else - - /* Nothing happened. Check timeouts, kill stale connections. - Active (c->q) connections get checked for total and last I/O - timeouts. Non-active connections must just not exceed - idle_tmout. */ - - if (!p[i].revents) { - - u8 keep = 0; - - if ((c->q && (cur_time - c->last_rw > rw_tmout || - cur_time - c->req_start > resp_tmout)) || - (!c->q && (cur_time - c->last_rw > idle_tmout)) || - (!c->q && tear_down_idle)) { - - if (c->q) { - c->q->res->state = STATE_CONNERR; - keep = c->q->req->callback(c->q->req, c->q->res); - req_errors_net++; - req_errors_cur++; - conn_busy_tmout++; - } else { - conn_idle_tmout++; - tear_down_idle = 0; - } - - destroy_unlink_conn(c, keep); - - } - - } - - c = next; - - } - - } - - /* OK, connection-handling affairs taken care of! Next, let's go through all - queue entries NOT currently associated with a connection, and try to - pair them up with something. */ - - if (queue_cur) { - struct queue_entry *q = queue; - - while (q) { - struct queue_entry* next = q->next; - u32 to_host = 0; - - if (!q->c) { - - struct conn_entry* c = conn; - - /* Let's try to find a matching, idle connection first. */ - - while (c) { - struct conn_entry* cnext = c->next; - - if (c->addr == q->req->addr && (++to_host) && - c->port == q->req->port && - c->proto == q->req->proto && !c->q) { - conn_associate(c, q); - goto next_q_entry; - } - - c = cnext; - } - - /* No match. If we are out of slots, request some other idle - connection to be nuked soon. */ - - if (to_host < max_conn_host && conn_cur < max_connections) { - conn_associate(0, q); - goto next_q_entry; - } else tear_down_idle = 1; - - } - -next_q_entry: - - q = next; - - } - - } - - return queue_cur; -} - - -/* Helper function for request / response dumpers: */ -static void dump_params(struct param_array* par) { - u32 i; - - for (i=0;i<par->c;i++) { - - switch (par->t[i]) { - case PARAM_NONE: SAY(" <<<<"); break; - case PARAM_PATH: SAY(" PATH"); break; - case PARAM_PATH_S: SAY(" PT_S"); break; - case PARAM_PATH_C: SAY(" PT_C"); break; - case PARAM_PATH_E: SAY(" PT_E"); break; - case PARAM_PATH_D: SAY(" PT_D"); break; - case PARAM_QUERY: SAY(" QUER"); break; - case PARAM_QUERY_S: SAY(" QR_S"); break; - case PARAM_QUERY_C: SAY(" QR_C"); break; - case PARAM_QUERY_E: SAY(" QR_E"); break; - case PARAM_QUERY_D: SAY(" QR_D"); break; - case PARAM_POST: SAY(" POST"); break; - case PARAM_POST_F: SAY(" FILE"); break; - case PARAM_POST_O: SAY(" OPAQ"); break; - case PARAM_HEADER: SAY(" head"); break; - case PARAM_COOKIE: SAY(" cook"); break; - default: SAY(" ????"); - } - - SAY(":%-20s = '%s'\n", - par->n[i] ? par->n[i] : (u8*)"-", - par->v[i] ? par->v[i] : (u8*)"-"); - - } -} - - -/* Creates a working copy of a request. If all is 0, does not copy - path, query parameters, or POST data (but still copies headers). */ - -struct http_request* req_copy(struct http_request* req, struct pivot_desc* pv, - u8 all) { - struct http_request* ret; - u32 i; - - if (!req) return NULL; - - ret = ck_alloc(sizeof(struct http_request)); - - ret->proto = req->proto; - - if (all) - ret->method = ck_strdup(req->method); - else - ret->method = ck_strdup((u8*)"GET"); - - ret->host = ck_strdup(req->host); - ret->addr = req->addr; - ret->port = req->port; - ret->pivot = pv; - ret->user_val = req->user_val; - - /* Copy all the requested data. */ - - for (i=0;i<req->par.c;i++) - if (all || HEADER_SUBTYPE(req->par.t[i])) - set_value(req->par.t[i], req->par.n[i], req->par.v[i], -1, - &ret->par); - - memcpy(&ret->same_sig, &req->same_sig, sizeof(struct http_sig)); - - return ret; - -} - - -/* Creates a copy of a response. */ - -struct http_response* res_copy(struct http_response* res) { - struct http_response* ret; - u32 i; - - if (!res) return NULL; - - ret = ck_alloc(sizeof(struct http_response)); - - ret->state = res->state; - ret->code = res->code; - ret->msg = res->msg ? ck_strdup(res->msg) : NULL; - ret->warn = res->warn; - - for (i=0;i<res->hdr.c;i++) - set_value(res->hdr.t[i], res->hdr.n[i], res->hdr.v[i], -1, &ret->hdr); - - ret->pay_len = res->pay_len; - - if (res->pay_len) { - ret->payload = ck_alloc(res->pay_len); - memcpy(ret->payload, res->payload, res->pay_len); - } - - memcpy(&ret->sig, &res->sig, sizeof(struct http_sig)); - - ret->sniff_mime_id = res->sniff_mime_id; - ret->decl_mime_id = res->decl_mime_id; - ret->doc_type = res->doc_type; - ret->css_type = res->css_type; - ret->js_type = res->js_type; - ret->json_safe = res->json_safe; - ret->stuff_checked = res->stuff_checked; - ret->scraped = res->scraped; - - if (res->meta_charset) - ret->meta_charset = ck_strdup(res->meta_charset); - - if (res->header_charset) - ret->header_charset = ck_strdup(res->header_charset); - - if (res->header_mime) - ret->header_mime = ck_strdup(res->header_mime); - - ret->sniffed_mime = res->sniffed_mime; - - return ret; - -} - - -/* Dumps HTTP request data, for diagnostic purposes: */ - -void dump_http_request(struct http_request* r) { - - u8 *new_url, *tmp; - - SAY("\n== HTTP REQUEST %p ==\n\nBasic values:\n", r); - - SAY(" Proto = %u\n", r->proto); - SAY(" Method = %s\n", r->method ? r->method : (u8*)"(GET)"); - SAY(" Host = %s\n", r->host); - SAY(" Addr = %u.%u.%u.%u\n", ((u8*)&r->addr)[0], ((u8*)&r->addr)[1], - ((u8*)&r->addr)[2], ((u8*)&r->addr)[3]); - SAY(" Port = %d\n", r->port); - SAY(" Xrefs = pivot %p, handler %p, user %d\n", r->pivot, - r->callback, r->user_val); - - new_url = serialize_path(r, 1, 0); - - SAY("\nURLs:\n Original = %s\n" - " Synthetic = %s\n", r->orig_url ? r->orig_url : (u8*)"[none]", - new_url); - - ck_free(new_url); - - SAY("\nParameter array:\n"); - - dump_params(&r->par); - - SAY("\nRaw request data:\n\n"); - - tmp = build_request_data(r); - SAY("%s\n",tmp); - ck_free(tmp); - - SAY("\n== END OF REQUEST ==\n"); - -} - - -/* Dumps HTTP response data, likewise: */ - -void dump_http_response(struct http_response* r) { - - SAY("\n== HTTP RESPONSE %p ==\n\nBasic values:\n", r); - - SAY(" State = %u\n", r->state); - SAY(" Response = %u ('%s')\n", r->code, r->msg); - SAY(" Flags = %08x\n", r->warn); - SAY(" Data len = %u\n", r->pay_len); - - SAY("\nParameter array:\n"); - - dump_params(&r->hdr); - - if (r->payload) SAY("\nPayload data (%u):\n\n%s\n", r->pay_len, r->payload); - - SAY("\n== END OF RESPONSE ==\n"); - -} - -/* Destroys http state information, for memory profiling. */ - -void destroy_http() { - u32 i; - struct dns_entry* cur; - - for (i=0;i<global_http_par.c;i++) { - ck_free(global_http_par.n[i]); - ck_free(global_http_par.v[i]); - } - - ck_free(global_http_par.t); - ck_free(global_http_par.n); - ck_free(global_http_par.v); - - while (conn) destroy_unlink_conn(conn,0 ); - while (queue) destroy_unlink_queue(queue,0 ); - - cur = dns; - - while (cur) { - struct dns_entry* next = cur->next; - ck_free(cur->name); - ck_free(cur); - cur = next; - } - -} - - -/* Shows some pretty statistics. */ - -void http_stats(u64 st_time) { - u64 en_time; - struct timeval tv; - - gettimeofday(&tv, NULL); - en_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; - - SAY(cLBL "Scan statistics:\n\n" - cGRA " Scan time : " cNOR "%u:%02u:%02u.%03u\n" - cGRA " HTTP requests : " cNOR "%u (%.01f/s), %llu kB in, " - "%llu kB out (%.01f kB/s) \n" - cGRA " Compression : " cNOR "%llu kB in, %llu kB out " - "(%.01f%% gain) \n" - cGRA " HTTP faults : " cNOR "%u net errors, %u proto errors, " - "%u retried, %u drops\n" - cGRA " TCP handshakes : " cNOR "%u total (%.01f req/conn) \n" - cGRA " TCP faults : " cNOR "%u failures, %u timeouts, %u purged\n" - cGRA " External links : " cNOR "%u skipped\n" - cGRA " Reqs pending : " cNOR "%u \n", - - /* hrs */ (u32)((en_time - st_time) / 1000 / 60 / 60), - /* min */ (u32)((en_time - st_time) / 1000 / 60) % 60, - /* sec */ (u32)((en_time - st_time) / 1000) % 60, - /* ms */ (u32)((en_time - st_time) % 1000), - - req_count - queue_cur, - (float) (req_count - queue_cur / 1.15) * 1000 / (en_time - st_time + 1), - (unsigned long long int) bytes_recv / 1024, - (unsigned long long int) bytes_sent / 1024, - (float) (bytes_recv + bytes_sent) / 1.024 / (en_time - st_time + 1), - - (unsigned long long int) bytes_deflated / 1024, - (unsigned long long int) bytes_inflated / 1024, - ((float) bytes_inflated - bytes_deflated) / (bytes_inflated + - bytes_deflated + 1) * 100, - - req_errors_net, req_errors_http, req_retried, req_dropped, - - conn_count, (float) req_count / conn_count, - conn_failed, conn_busy_tmout, conn_idle_tmout, - url_scope, queue_cur); -} - - -/* Show currently handled requests. */ - -#define SP70 \ - " " - -void http_req_list(void) { - u32 i; - struct conn_entry* c = conn; - - SAY(cLBL "In-flight requests (max 15 shown):\n\n"); - - for (i=0;i<15;i++) { - - SAY(" " cGRA "[" cBLU "%02d" cGRA "] " cBRI, i + 1); - - if (c && c->q) { - u8* p = serialize_path(c->q->req, 1, 0); - u32 l = strlen((char*)p); - - if (l > 70) { - SAY("%.30s" cGRA "..." cBRI "%.37s\n", p, p + l - 37); - } else { - SAY("%s%s\n", p, SP70 + l); - } - - ck_free(p); - - } else SAY(cLGN "<slot idle>%s\n", SP70 + 11); - - if (c) c = c->next; - - } - -} - diff -Nru skipfish-2.02b/http_client.h skipfish-2.10b/http_client.h --- skipfish-2.02b/http_client.h 2011-07-03 06:49:09.000000000 +0000 +++ skipfish-2.10b/http_client.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,431 +0,0 @@ -/* - skipfish - high-performance, single-process asynchronous HTTP client - -------------------------------------------------------------------- - - Author: Michal Zalewski <lcamtuf@google.com> - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#ifndef _HAVE_HTTP_CLIENT_H -#define _HAVE_HTTP_CLIENT_H - -#include <openssl/ssl.h> - -#include "config.h" -#include "types.h" -#include "alloc-inl.h" -#include "string-inl.h" - -/* Generic type-name-value array, used for HTTP headers, etc: */ - -struct param_array { - u8* t; /* Type */ - u8** n; /* Name */ - u8** v; /* Value */ - u32 c; /* Count */ -}; - -/* Flags for http_request protocol: */ - -#define PROTO_NONE 0 /* Illegal value */ -#define PROTO_HTTP 1 /* Plain-text HTTP */ -#define PROTO_HTTPS 2 /* TLS/SSL wrapper */ - -/* Flags for http_request parameter list entries: */ - -#define PARAM_NONE 0 /* Empty parameter slot */ - -#define PARAM_PATH 10 /* Path or parametrized path */ -#define PARAM_PATH_S 11 /* - Semicolon element */ -#define PARAM_PATH_C 12 /* - Comma element */ -#define PARAM_PATH_E 13 /* - Exclamation mark element */ -#define PARAM_PATH_D 14 /* - Dollar sign element */ - -#define PATH_SUBTYPE(_x) ((_x) >= PARAM_PATH && (_x) < PARAM_QUERY) - -#define PARAM_QUERY 20 /* Query parameter */ -#define PARAM_QUERY_S 21 /* - Semicolon element */ -#define PARAM_QUERY_C 22 /* - Comma element */ -#define PARAM_QUERY_E 23 /* - Exclamation mark element */ -#define PARAM_QUERY_D 24 /* - Dollar sign element */ - -#define QUERY_SUBTYPE(_x) ((_x) >= PARAM_QUERY && (_x) < PARAM_POST) - -#define PARAM_POST 50 /* Post parameter */ -#define PARAM_POST_F 51 /* - File field */ -#define PARAM_POST_O 52 /* - Non-standard (e.g., JSON) */ - -#define POST_SUBTYPE(_x) ((_x) >= PARAM_POST && (_x) < PARAM_HEADER) - -#define PARAM_HEADER 100 /* Generic HTTP header */ -#define PARAM_COOKIE 101 /* - HTTP cookie */ - -#define HEADER_SUBTYPE(_x) ((_x) >= PARAM_HEADER) - -struct http_response; -struct queue_entry; - -/* HTTP response signature. */ - -struct http_sig { - u32 code; /* HTTP response code */ - u32 data[FP_SIZE]; /* Response fingerprint data */ -}; - -/* HTTP request descriptor: */ - -struct http_request { - - u8 proto; /* Protocol (PROTO_*) */ - u8* method; /* HTTP method (GET, POST, ...) */ - u8* host; /* Host name */ - u32 addr; /* Resolved IP address */ - u16 port; /* Port number to connect to */ - - u8* orig_url; /* Copy of the original URL */ - struct param_array par; /* Parameters, headers, cookies */ - - struct pivot_desc *pivot; /* Pivot descriptor */ - - u32 user_val; /* Can be used freely */ - - u8 (*callback)(struct http_request*, struct http_response*); - /* Callback to invoke when done */ - - struct http_sig same_sig; /* Used by secondary ext fuzz. */ - - /* Used by directory brute-force: */ - - u8* trying_key; /* Current keyword ptr */ - u8 trying_spec; /* Keyword specificity info */ - -}; - -/* Flags for http_response completion state: */ - -#define STATE_NOTINIT 0 /* Request not sent */ -#define STATE_CONNECT 1 /* Connecting... */ -#define STATE_SEND 2 /* Sending request */ -#define STATE_RECEIVE 3 /* Waiting for response */ - -#define STATE_OK 100 /* Proper fetch */ -#define STATE_DNSERR 101 /* DNS error */ -#define STATE_LOCALERR 102 /* Socket or routing error */ -#define STATE_CONNERR 103 /* Connection failed */ -#define STATE_RESPERR 104 /* Response not valid */ -#define STATE_SUPPRESS 200 /* Dropped (limits / errors) */ - -/* Flags for http_response warnings: */ - -#define WARN_NONE 0 /* No warnings */ -#define WARN_PARTIAL 1 /* Incomplete read */ -#define WARN_TRAIL 2 /* Trailing request garbage */ -#define WARN_CFL_HDR 4 /* Conflicting headers */ - -/* HTTP response descriptor: */ - -struct http_response { - - u32 state; /* HTTP convo state (STATE_*) */ - u32 code; /* HTTP response code */ - u8* msg; /* HTTP response message */ - u32 warn; /* Warning flags */ - - u8 cookies_set; /* Sets cookies? */ - - struct param_array hdr; /* Server header, cookie list */ - - u32 pay_len; /* Response payload length */ - u8* payload; /* Response payload data */ - - struct http_sig sig; /* Response signature data */ - - /* Various information populated by content checks: */ - - u8 sniff_mime_id; /* Sniffed MIME (MIME_*) */ - u8 decl_mime_id; /* Declared MIME (MIME_*) */ - - u8* meta_charset; /* META tag charset value */ - u8* header_charset; /* Content-Type charset value */ - u8* header_mime; /* Content-Type MIME type */ - u8* sniffed_mime; /* Detected MIME type (ref) */ - - /* Everything below is of interest to scrape_response() only: */ - - u8 doc_type; /* 0 - tbd, 1 - bin, 2 - ascii */ - u8 css_type; /* 0 - tbd, 1 - other, 2 - css */ - u8 js_type; /* 0 - tbd, 1 - other, 2 - js */ - u8 json_safe; /* 0 - no, 1 - yes */ - u8 stuff_checked; /* check_stuff() called? */ - u8 scraped; /* scrape_response() called? */ - -}; - -/* Open keep-alive connection descriptor: */ - -struct conn_entry { - - s32 fd; /* The actual file descriptor */ - - u8 proto; /* Protocol (PROTO_*) */ - u32 addr; /* Destination IP */ - u32 port; /* Destination port */ - - u8 reused; /* Used for earier requests? */ - - u32 req_start; /* Unix time: request start */ - u32 last_rw; /* Unix time: last read / write */ - - SSL_CTX *srv_ctx; /* SSL context */ - SSL *srv_ssl; - u8 SSL_rd_w_wr; /* SSL_read() wants to write? */ - u8 SSL_wr_w_rd; /* SSL_write() wants to read? */ - u8 ssl_checked; /* SSL state checked? */ - - u8* read_buf; /* Current read buffer */ - u32 read_len; - u8* write_buf; /* Pending write buffer */ - u32 write_off; /* Current write offset */ - u32 write_len; - - struct queue_entry* q; /* Current queue entry */ - - struct conn_entry* prev; /* Previous connection entry */ - struct conn_entry* next; /* Next connection entry */ - -}; - -/* Request queue descriptor: */ - -struct queue_entry { - struct http_request* req; /* Request descriptor */ - struct http_response* res; /* Response descriptor */ - struct conn_entry* c; /* Connection currently used */ - struct queue_entry* prev; /* Previous queue entry */ - struct queue_entry* next; /* Next queue entry */ - u8 retrying; /* Request being retried? */ -}; - -/* DNS cache item: */ - -struct dns_entry { - u8* name; /* Name requested */ - u32 addr; /* IP address (0 = bad host) */ - struct dns_entry* next; /* Next cache entry */ -}; - - -/* Simplified macros to manipulate param_arrays: */ - -#define ADD(_ar,_t,_n,_v) do { \ - u32 _cur = (_ar)->c++; \ - (_ar)->t = ck_realloc((_ar)->t, (_ar)->c); \ - (_ar)->n = ck_realloc((_ar)->n, (_ar)->c * sizeof(u8*)); \ - (_ar)->v = ck_realloc((_ar)->v, (_ar)->c * sizeof(u8*)); \ - (_ar)->t[cur] = _t; \ - (_ar)->n[cur] = (_n) ? ck_strdup(_n) : 0; \ - (_ar)->v[cur] = (_v) ? ck_strdup(_v) : 0; \ - } while (0) - -#define FREE(_ar) do { \ - while ((_ar)->c--) { \ - ck_free((_ar)->n[(_ar)->c]); \ - ck_free((_ar)->v[(_ar)->c]); \ - } \ - ck_free((_ar)->t); \ - ck_free((_ar)->n); \ - ck_free((_ar)->v); \ - } while (0) - - -/* Extracts parameter value from param_array. Name is matched if - non-NULL. Returns pointer to value data, not a duplicate string; - NULL if no match found. */ - -u8* get_value(u8 type, u8* name, u32 offset, struct param_array* par); - -/* Inserts or overwrites parameter value in param_array. If offset - == -1, will append parameter to list. Duplicates strings, - name and val can be NULL. */ - -void set_value(u8 type, u8* name, u8* val, s32 offset, struct param_array* par); - -/* Simplified macros for value table access: */ - -#define GET_HDR(_name, _p) get_value(PARAM_HEADER, _name, 0, _p) -#define SET_HDR(_name, _val, _p) set_value(PARAM_HEADER, _name, _val, -1, _p) -#define GET_CK(_name, _p) get_value(PARAM_COOKIE, _name, 0, _p) -#define SET_CK(_name, _val, _p) set_value(PARAM_COOKIE, _name, _val, 0, _p) - -void tokenize_path(u8* str, struct http_request* req, u8 add_slash); - -/* Convert a fully-qualified or relative URL string to a proper http_request - representation. Returns 0 on success, 1 on format error. */ - -u8 parse_url(u8* url, struct http_request* req, struct http_request* ref); - -/* URL-decodes a string. 'Plus' parameter governs the behavior on + - signs (as they have a special meaning only in query params, not in path). */ - -u8* url_decode_token(u8* str, u32 len, u8 plus); - -/* URL-encodes a string according to custom rules. The assumption here is that - the data is already tokenized as "special" boundaries such as ?, =, &, /, - ;, so these characters must always be escaped if present in tokens. We - otherwise let pretty much everything else go through, as it may help with - the exploitation of certain vulnerabilities. */ - -u8* url_encode_token(u8* str, u32 len, u8 also_slash); - -/* Reconstructs URI from http_request data. Includes protocol and host - if with_host is non-zero. */ - -u8* serialize_path(struct http_request* req, u8 with_host, u8 with_post); - -/* Looks up IP for a particular host, returns data in network order. - Uses standard resolver, so it is slow and blocking, but we only - expect to call it a couple of times. */ - -u32 maybe_lookup_host(u8* name); - -/* Creates an ad hoc DNS cache entry, to override NS lookups. */ - -void fake_host(u8* name, u32 addr); - -/* Schedules a new asynchronous request; req->callback() will be invoked when - the request is completed. */ - -void async_request(struct http_request* req); - -/* Prepares a serialized HTTP buffer to be sent over the network. */ - -u8* build_request_data(struct http_request* req); - -/* Parses a network buffer containing raw HTTP response received over the - network ('more' == the socket is still available for reading). Returns 0 - if response parses OK, 1 if more data should be read from the socket, - 2 if the response seems invalid. */ - -u8 parse_response(struct http_request* req, struct http_response* res, u8* data, - u32 data_len, u8 more); - -/* Processes the queue. Returns the number of queue entries remaining, - 0 if none. Will do a blocking select() to wait for socket state changes - (or timeouts) if no data available to process. This is the main - routine for the scanning loop. */ - -u32 next_from_queue(void); - -/* Dumps HTTP request stats, for debugging purposes: */ - -void dump_http_request(struct http_request* r); - -/* Dumps HTTP response stats, for debugging purposes: */ - -void dump_http_response(struct http_response* r); - -/* Fingerprints a response: */ - -void fprint_response(struct http_response* res); - -/* Performs a deep free() of sturct http_request */ - -void destroy_request(struct http_request* req); - -/* Performs a deep free() of sturct http_response */ - -void destroy_response(struct http_response* res); - -/* Creates a working copy of a request. If all is 0, does not copy - path, query parameters, or POST data (but still copies headers). */ - -struct http_request* req_copy(struct http_request* req, struct pivot_desc* pv, - u8 all); - -/* Creates a copy of a response. */ - -struct http_response* res_copy(struct http_response* res); - -/* Various settings and counters exported to other modules: */ - -extern u32 max_connections, - max_conn_host, - max_requests, - max_fail, - idle_tmout, - resp_tmout, - rw_tmout, - size_limit, - req_errors_net, - req_errors_http, - req_errors_cur, - req_count, - req_dropped, - req_retried, - url_scope, - conn_count, - conn_idle_tmout, - conn_busy_tmout, - conn_failed, - queue_cur; - -extern u64 bytes_sent, - bytes_recv, - bytes_deflated, - bytes_inflated; - -extern u8 ignore_cookies; - -/* Flags for browser type: */ - -#define BROWSER_FAST 0 /* Minimimal HTTP headers */ -#define BROWSER_MSIE 1 /* Try to mimic MSIE */ -#define BROWSER_FFOX 2 /* Try to mimic Firefox */ -#define BROWSER_PHONE 3 /* Try to mimic iPhone */ - -extern u8 browser_type; - -/* Flags for authentication type: */ - -#define AUTH_NONE 0 /* No authentication */ -#define AUTH_BASIC 1 /* 'Basic' HTTP auth */ - -extern u8 auth_type; - -extern u8 *auth_user, - *auth_pass; - -#ifdef PROXY_SUPPORT -extern u8* use_proxy; -extern u32 use_proxy_addr; -extern u16 use_proxy_port; -#endif /* PROXY_SUPPORT */ - -/* Global HTTP cookies, extra headers: */ - -extern struct param_array global_http_par; - -/* Destroys http state information, for memory profiling. */ - -void destroy_http(); - -/* Shows some pretty statistics. */ - -void http_stats(u64 st_time); -void http_req_list(void); - -#endif /* !_HAVE_HTTP_CLIENT_H */ diff -Nru skipfish-2.02b/report.c skipfish-2.10b/report.c --- skipfish-2.02b/report.c 2011-07-02 06:35:05.000000000 +0000 +++ skipfish-2.10b/report.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,850 +0,0 @@ -/* - skipfish - post-processing and reporting - ---------------------------------------- - - Author: Michal Zalewski <lcamtuf@google.com> - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#include <unistd.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <time.h> -#include <dirent.h> -#include <sys/fcntl.h> - -#include "debug.h" -#include "config.h" -#include "types.h" -#include "http_client.h" -#include "database.h" -#include "crawler.h" -#include "analysis.h" - -/* Pivot and issue signature data. */ - -struct p_sig_desc { - u8 type; /* Pivot type */ - struct http_sig* res_sig; /* Response signature */ - u32 issue_sig; /* Issues fingerprint */ - u32 child_sig; /* Children fingerprint */ -}; - - -static struct p_sig_desc* p_sig; -static u32 p_sig_cnt; -u8 suppress_dupes; - - -/* Response, issue sample data. */ - -struct mime_sample_desc { - u8* det_mime; - struct http_request** req; - struct http_response** res; - u32 sample_cnt; -}; - - -struct issue_sample_desc { - u32 type; - struct issue_desc** i; - u32 sample_cnt; -}; - -static struct mime_sample_desc* m_samp; -static struct issue_sample_desc* i_samp; -static u32 m_samp_cnt, i_samp_cnt; - - -/* qsort() helper for sort_annotate_pivot(). */ - -static int pivot_compar(const void* par1, const void* par2) { - const struct pivot_desc *p1 = *(struct pivot_desc**)par1, - *p2 = *(struct pivot_desc**)par2; - - /* Force directories to appear before files, etc. */ - - if (p1->type < p2->type) return -1; - if (p1->type > p2->type) return 1; - - return strcasecmp((char*)p1->name, (char*)p2->name); -} - -static int issue_compar(const void* par1, const void* par2) { - const struct issue_desc *i1 = par1, *i2 = par2; - return i2->type - i1->type; -} - - -/* Recursively annotates and sorts pivots. */ - -static void sort_annotate_pivot(struct pivot_desc* pv) { - u32 i, path_child = 0; - static u32 proc_cnt; - u8 *q1, *q2; - - /* Add notes to all non-dir nodes with dir or file children... */ - - for (i=0;i<pv->child_cnt;i++) { - if (pv->child[i]->type == PIVOT_FILE || pv->child[i]->type == PIVOT_DIR) path_child = 1; - sort_annotate_pivot(pv->child[i]); - } - - if (pv->type != PIVOT_DIR && pv->type != PIVOT_SERV && - pv->type != PIVOT_ROOT && path_child) - problem(PROB_NOT_DIR, pv->req, pv->res, 0, pv, 0); - - /* Non-parametric nodes with digits in the name were not brute-forced, - but the user might be interested in doing so. Skip images here. */ - - if (pv->fuzz_par == -1 && pv->res && - (pv->res->sniff_mime_id < MIME_IMG_JPEG || - pv->res->sniff_mime_id > MIME_AV_WMEDIA) && - (pv->type == PIVOT_DIR || pv->type == PIVOT_FILE || - pv->type == PIVOT_PATHINFO) && !pv->missing) { - i = strlen((char*)pv->name); - while (i--) - if (isdigit(pv->name[i])) { - problem(PROB_FUZZ_DIGIT, pv->req, pv->res, 0, pv, 0); - break; - } - } - - /* Parametric nodes that seem to contain queries in parameters, and are not - marked as bogus_par, should be marked as dangerous. */ - - if (pv->fuzz_par != -1 && !pv->bogus_par && - (((q1 = (u8*)strchr((char*)pv->req->par.v[pv->fuzz_par], '(')) && - (q2 = (u8*)strchr((char*)pv->req->par.v[pv->fuzz_par], ')')) && q1 < q2 && - !isdigit(q1[1])) || - ((inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)"SELECT ") || - inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)"DELETE ") ) && - inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)" FROM ")) || - (inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)"UPDATE ") || - inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)" WHERE ")) || - inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)"DROP TABLE ") || - inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)" ORDER BY "))) - problem(PROB_SQL_PARAM, pv->req, pv->res, 0, pv, 0); - - /* Sort children nodes and issues as appropriate. */ - - if (pv->child_cnt > 1) - qsort(pv->child, pv->child_cnt, sizeof(struct pivot_desc*), pivot_compar); - - if (pv->issue_cnt > 1) - qsort(pv->issue, pv->issue_cnt, sizeof(struct issue_desc), issue_compar); - - if ((!(proc_cnt++ % 50)) || pv->type == PIVOT_ROOT) { - SAY(cLGN "\r[+] " cNOR "Sorting and annotating crawl nodes: %u", proc_cnt); - fflush(0); - } - -} - - -/* Issue extra hashing helper. */ - -static inline u32 hash_extra(u8* str) { - register u32 ret = 0; - register u8 cur; - - if (str) - while ((cur=*str)) { - ret = ~ret ^ (cur) ^ - (cur << 5) ^ (~cur >> 5) ^ - (cur << 10) ^ (~cur << 15) ^ - (cur << 20) ^ (~cur << 25) ^ - (cur << 30); - str++; - } - - return ret; -} - - - -/* Registers a new pivot signature, or updates an existing one. */ - -static void maybe_add_sig(struct pivot_desc* pv) { - u32 i, issue_sig = ~(pv->issue_cnt | (pv->desc_issue_cnt << 16)), - child_sig = ~(pv->desc_cnt | (pv->child_cnt << 16)); - - if (!pv->res) return; - - /* Compute a rough children node signature based on children types. */ - - for (i=0;i<pv->child_cnt;i++) - child_sig ^= (hash_extra(pv->child[i]->name) ^ - pv->child[i]->type) << (i % 16); - - /* Do the same for all recorded issues. */ - - for (i=0;i<pv->issue_cnt;i++) - issue_sig ^= (hash_extra(pv->issue[i].extra) ^ - pv->issue[i].type) << (i % 16); - - /* Assign a simplified signature to the pivot. */ - - pv->pv_sig = (pv->type << 16) ^ ~child_sig ^ issue_sig; - - /* See if a matching signature already exists. */ - - for (i=0;i<p_sig_cnt;i++) - if (p_sig[i].type == pv->type && p_sig[i].issue_sig == issue_sig && - p_sig[i].child_sig == child_sig && - same_page(p_sig[i].res_sig, &pv->res->sig)) { - - pv->dupe = 1; - return; - - } - - /* No match - create a new one. */ - - p_sig = ck_realloc(p_sig, (p_sig_cnt + 1) * sizeof(struct p_sig_desc)); - - p_sig[p_sig_cnt].type = pv->type; - p_sig[p_sig_cnt].res_sig = &pv->res->sig; - p_sig[p_sig_cnt].issue_sig = issue_sig; - p_sig[p_sig_cnt].child_sig = child_sig; - p_sig_cnt++; - -} - - - - -/* Recursively collects unique signatures for pivots. */ - -static void collect_signatures(struct pivot_desc* pv) { - u32 i; - static u32 proc_cnt; - - maybe_add_sig(pv); - for (i=0;i<pv->child_cnt;i++) collect_signatures(pv->child[i]); - - if ((!(proc_cnt++ % 50)) || pv->type == PIVOT_ROOT) { - SAY(cLGN "\r[+] " cNOR "Looking for duplicate entries: %u", proc_cnt); - fflush(0); - } - -} - - -/* Destroys signature data (for memory profiling purposes). */ - -void destroy_signatures(void) { - u32 i; - - ck_free(p_sig); - - for (i=0;i<m_samp_cnt;i++) { - ck_free(m_samp[i].req); - ck_free(m_samp[i].res); - } - - for (i=0;i<i_samp_cnt;i++) - ck_free(i_samp[i].i); - - ck_free(m_samp); - ck_free(i_samp); -} - - -/* Prepares issue, pivot stats, backtracing through all children. - Do not count nodes that seem duplicate. */ - -static void compute_counts(struct pivot_desc* pv) { - u32 i; - struct pivot_desc* tmp = pv->parent; - static u32 proc_cnt; - - for (i=0;i<pv->child_cnt;i++) compute_counts(pv->child[i]); - - if (pv->dupe) return; - - while (tmp) { - tmp->total_child_cnt++; - tmp = tmp->parent; - } - - for (i=0;i<pv->issue_cnt;i++) { - u8 sev = PSEV(pv->issue[i].type); - tmp = pv; - while (tmp) { - tmp->total_issues[sev]++; - tmp = tmp->parent; - } - } - - if ((!(proc_cnt++ % 50)) || pv->type == PIVOT_ROOT) { - SAY(cLGN "\r[+] " cNOR "Counting unique nodes: %u", proc_cnt); - fflush(0); - } - -} - - -/* Helper to JS-escape data. Static buffer, will be destroyed on - subsequent calls. */ - -static inline u8* js_escape(u8* str, u8 sp) { - u32 len; - static u8* ret; - u8* opos; - - if (!str) return (u8*)"[none]"; - - len = strlen((char*)str); - - if (ret) __DFL_ck_free(ret); - opos = ret = __DFL_ck_alloc(len * 4 + 1); - - while (len--) { - if (*str > (sp ? 0x20 : 0x1f) && *str < 0x80 && !strchr("<>\\'\"", *str)) { - *(opos++) = *(str++); - } else { - sprintf((char*)opos, "\\x%02x", *(str++)); - opos += 4; - } - } - - *opos = 0; - - return ret; - -} - - -static void output_scan_info(u64 scan_time, u32 seed) { - FILE* f; - time_t t = time(NULL); - u8* ct = (u8*)ctime(&t); - - if (isspace(ct[strlen((char*)ct)-1])) - ct[strlen((char*)ct)-1] = 0; - - f = fopen("summary.js", "w"); - if (!f) PFATAL("Cannot open 'summary.js'"); - - fprintf(f, "var sf_version = '%s';\n", VERSION); - fprintf(f, "var scan_date = '%s';\n", js_escape(ct, 0)); - fprintf(f, "var scan_seed = '0x%08x';\n", seed); - fprintf(f, "var scan_ms = %llu;\n", (long long)scan_time); - - fclose(f); - -} - - -/* Helper to save request, response data. */ - -static void describe_res(FILE* f, struct http_response* res) { - - if (!res) { - fprintf(f, "'fetched': false, 'error': 'Content not fetched'"); - return; - } - - switch (res->state) { - - case 0 ... STATE_OK - 1: - fprintf(f, "'fetched': false, 'error': '(Reported while fetch in progress)'"); - break; - - case STATE_OK: - fprintf(f, "'fetched': true, 'code': %u, 'len': %u, 'decl_mime': '%s', ", - res->code, res->pay_len, - js_escape(res->header_mime, 0)); - - fprintf(f, "'sniff_mime': '%s', 'cset': '%s'", - res->sniffed_mime ? res->sniffed_mime : (u8*)"[none]", - js_escape(res->header_charset ? res->header_charset - : res->meta_charset, 0)); - break; - - case STATE_DNSERR: - fprintf(f, "'fetched': false, 'error': 'DNS error'"); - break; - - case STATE_LOCALERR: - fprintf(f, "'fetched': false, 'error': 'Local network error'"); - break; - - case STATE_CONNERR: - fprintf(f, "'fetched': false, 'error': 'Connection error'"); - break; - - case STATE_RESPERR: - fprintf(f, "'fetched': false, 'error': 'Malformed HTTP response'"); - break; - - case STATE_SUPPRESS: - fprintf(f, "'fetched': false, 'error': 'Limits exceeded'"); - break; - - - default: - fprintf(f, "'fetched': false, 'error': 'Unknown error'"); - - } - -} - - -/* Helper to save request, response data. */ - -static void save_req_res(struct http_request* req, struct http_response* res, u8 sample) { - FILE* f; - - if (req) { - u8* rd = build_request_data(req); - f = fopen("request.dat", "w"); - if (!f) PFATAL("Cannot create 'request.dat'"); - fwrite(rd, strlen((char*)rd), 1, f); - fclose(f); - ck_free(rd); - } - - if (res && res->state == STATE_OK) { - u32 i; - f = fopen("response.dat", "w"); - if (!f) PFATAL("Cannot create 'response.dat'"); - fprintf(f, "HTTP/1.1 %u %s\n", res->code, res->msg); - - for (i=0;i<res->hdr.c;i++) - if (res->hdr.t[i] == PARAM_HEADER) - fprintf(f, "%s: %s\n", res->hdr.n[i], res->hdr.v[i]); - else - fprintf(f, "Set-Cookie: %s=%s\n", res->hdr.n[i], res->hdr.v[i]); - - fprintf(f, "\n"); - fwrite(res->payload, res->pay_len, 1, f); - fclose(f); - - /* Also collect MIME samples at this point. */ - - if (!req->pivot->dupe && res->sniffed_mime && sample) { - - for (i=0;i<m_samp_cnt;i++) - if (!strcmp((char*)m_samp[i].det_mime, (char*)res->sniffed_mime)) break; - - if (i == m_samp_cnt) { - m_samp = ck_realloc(m_samp, (i + 1) * sizeof(struct mime_sample_desc)); - m_samp[i].det_mime = res->sniffed_mime; - m_samp_cnt++; - } else { - u32 c; - - /* If we already have something that looks very much the same on the - list, don't bother reporting it again. */ - - for (c=0;c<m_samp[i].sample_cnt;c++) - if (same_page(&m_samp[i].res[c]->sig, &res->sig)) return; - } - - m_samp[i].req = ck_realloc(m_samp[i].req, (m_samp[i].sample_cnt + 1) * - sizeof(struct http_request*)); - m_samp[i].res = ck_realloc(m_samp[i].res, (m_samp[i].sample_cnt + 1) * - sizeof(struct http_response*)); - m_samp[i].req[m_samp[i].sample_cnt] = req; - m_samp[i].res[m_samp[i].sample_cnt] = res; - m_samp[i].sample_cnt++; - - } - - } - -} - - -/* Dumps the actual crawl data. */ - -static void output_crawl_tree(struct pivot_desc* pv) { - u32 i; - FILE* f; - static u32 proc_cnt; - - /* Save request, response. */ - - save_req_res(pv->req, pv->res, 1); - - /* Write children information. Don't crawl children just yet, - because we could run out of file descriptors on a particularly - deep tree if we keep one open and recurse. */ - - f = fopen("child_index.js", "w"); - if (!f) PFATAL("Cannot create 'child_index.js'."); - - fprintf(f, "var child = [\n"); - - for (i=0;i<pv->child_cnt;i++) { - u8 tmp[32]; - u8* p; - - if (suppress_dupes && pv->child[i]->dupe && - !pv->child[i]->total_child_cnt) continue; - - /* Also completely suppress nodes that seem identical to the - previous one, and have a common prefix (as this implies - a mod_rewrite or htaccess filter). */ - - if (i && pv->child[i-1]->pv_sig == pv->child[i]->pv_sig) { - u8 *pn = pv->child[i-1]->name, *cn = pv->child[i]->name; - u32 pnd = strcspn((char*)pn, "."); - if (!strncasecmp((char*)pn, (char*)cn, pnd)) continue; - } - - sprintf((char*)tmp, "c%u", i); - - fprintf(f, " { 'dupe': %s, 'type': %u, 'name': '%s%s", - pv->child[i]->dupe ? "true" : "false", - pv->child[i]->type, js_escape(pv->child[i]->name, 0), - (pv->child[i]->fuzz_par == -1 || pv->child[i]->type == PIVOT_VALUE) - ? (u8*)"" : (u8*)"="); - - fprintf(f, "%s', 'dir': '%s', 'linked': %d, ", - (pv->child[i]->fuzz_par == -1 || pv->child[i]->type == PIVOT_VALUE) - ? (u8*)"" : - js_escape(pv->child[i]->req->par.v[pv->child[i]->fuzz_par], 0), - tmp, pv->child[i]->linked); - - p = serialize_path(pv->child[i]->req, 1, 1); - fprintf(f, "'url': '%s', ", js_escape(p, 0)); - ck_free(p); - - describe_res(f, pv->child[i]->res); - - fprintf(f,", 'missing': %s, 'csens': %s, 'child_cnt': %u, " - "'issue_cnt': [ %u, %u, %u, %u, %u ], 'sig': 0x%x }%s\n", - pv->child[i]->missing ? "true" : "false", - pv->child[i]->csens ? "true" : "false", - pv->child[i]->total_child_cnt, pv->child[i]->total_issues[1], - pv->child[i]->total_issues[2], pv->child[i]->total_issues[3], - pv->child[i]->total_issues[4], pv->child[i]->total_issues[5], - pv->child[i]->pv_sig, - (i == pv->child_cnt - 1) ? "" : ","); - } - - fprintf(f, "];\n"); - fclose(f); - - /* Write issue index, issue dumps. */ - - f = fopen("issue_index.js", "w"); - if (!f) PFATAL("Cannot create 'issue_index.js'."); - - fprintf(f, "var issue = [\n"); - - for (i=0;i<pv->issue_cnt;i++) { - u8 tmp[32]; - sprintf((char*)tmp, "i%u", i); - - fprintf(f, " { 'severity': %u, 'type': %u, 'extra': '%s', ", - PSEV(pv->issue[i].type) - 1, pv->issue[i].type, - pv->issue[i].extra ? js_escape(pv->issue[i].extra, 0) : (u8*)""); - - describe_res(f, pv->issue[i].res); - - fprintf(f, ", 'dir': '%s' }%s\n", - tmp, (i == pv->issue_cnt - 1) ? "" : ","); - - if (mkdir((char*)tmp, 0755)) PFATAL("Cannot create '%s'.", tmp); - if (chdir((char*)tmp)) PFATAL("chdir unexpectedly fails!"); - save_req_res(pv->issue[i].req, pv->issue[i].res, 1); - if (chdir((char*)"..")) PFATAL("chdir unexpectedly fails!"); - - /* Issue samples next! */ - - if (!pv->dupe) { - u32 c; - for (c=0;c<i_samp_cnt;c++) - if (i_samp[c].type == pv->issue[i].type) break; - - if (c == i_samp_cnt) { - i_samp = ck_realloc(i_samp, (c + 1) * sizeof(struct issue_sample_desc)); - i_samp_cnt++; - i_samp[c].type = pv->issue[i].type; - } - - i_samp[c].i = ck_realloc(i_samp[c].i, (i_samp[c].sample_cnt + 1) * - sizeof(struct issue_desc*)); - i_samp[c].i[i_samp[c].sample_cnt] = &pv->issue[i]; - i_samp[c].sample_cnt++; - } - - } - - fprintf(f, "];\n"); - fclose(f); - - /* Actually crawl children. */ - - for (i=0;i<pv->child_cnt;i++) { - u8 tmp[32]; - sprintf((char*)tmp, "c%u", i); - if (mkdir((char*)tmp, 0755)) PFATAL("Cannot create '%s'.", tmp); - if (chdir((char*)tmp)) PFATAL("chdir unexpectedly fails!"); - output_crawl_tree(pv->child[i]); - if (chdir((char*)"..")) PFATAL("chdir unexpectedly fails!"); - } - - if ((!(proc_cnt++ % 50)) || pv->type == PIVOT_ROOT) { - SAY(cLGN "\r[+] " cNOR "Writing crawl tree: %u", proc_cnt); - fflush(0); - } - -} - - -/* Writes previews of MIME types, issues. */ - -static int m_samp_qsort(const void* ptr1, const void* ptr2) { - const struct mime_sample_desc *p1 = ptr1, *p2 = ptr2; - return strcasecmp((char*)p1->det_mime, (char*)p2->det_mime); -} - -static int i_samp_qsort(const void* ptr1, const void* ptr2) { - const struct issue_sample_desc *p1 = ptr1, *p2 = ptr2; - return p2->type - p1->type; -} - - -static void output_summary_views() { - u32 i; - FILE* f; - - f = fopen("samples.js", "w"); - if (!f) PFATAL("Cannot create 'samples.js'."); - - qsort(m_samp, m_samp_cnt, sizeof(struct mime_sample_desc), m_samp_qsort); - qsort(i_samp, i_samp_cnt, sizeof(struct issue_sample_desc), i_samp_qsort); - - fprintf(f, "var mime_samples = [\n"); - - for (i=0;i<m_samp_cnt;i++) { - u32 c; - u8 tmp[32]; - u32 use_samp = (m_samp[i].sample_cnt > MAX_SAMPLES ? MAX_SAMPLES : - m_samp[i].sample_cnt); - - sprintf((char*)tmp, "_m%u", i); - if (mkdir((char*)tmp, 0755)) PFATAL("Cannot create '%s'.", tmp); - if (chdir((char*)tmp)) PFATAL("chdir unexpectedly fails!"); - - fprintf(f, " { 'mime': '%s', 'samples': [\n", m_samp[i].det_mime); - - for (c=0;c<use_samp;c++) { - u8 tmp2[32]; - u8* p = serialize_path(m_samp[i].req[c], 1, 0); - sprintf((char*)tmp2, "%u", c); - if (mkdir((char*)tmp2, 0755)) PFATAL("Cannot create '%s'.", tmp2); - if (chdir((char*)tmp2)) PFATAL("chdir unexpectedly fails!"); - save_req_res(m_samp[i].req[c], m_samp[i].res[c], 0); - if (chdir("..")) PFATAL("chdir unexpectedly fails!"); - fprintf(f, " { 'url': '%s', 'dir': '%s/%s', 'linked': %d, 'len': %d" - " }%s\n", js_escape(p, 0), tmp, tmp2, - m_samp[i].req[c]->pivot->linked, m_samp[i].res[c]->pay_len, - (c == use_samp - 1) ? " ]" : ","); - ck_free(p); - } - - fprintf(f, " }%s\n", (i == m_samp_cnt - 1) ? "" : ","); - if (chdir("..")) PFATAL("chdir unexpectedly fails!"); - } - - fprintf(f, "];\n\n"); - - fprintf(f, "var issue_samples = [\n"); - - for (i=0;i<i_samp_cnt;i++) { - u32 c; - u8 tmp[32]; - u32 use_samp = (i_samp[i].sample_cnt > MAX_SAMPLES ? MAX_SAMPLES : - i_samp[i].sample_cnt); - - sprintf((char*)tmp, "_i%u", i); - if (mkdir((char*)tmp, 0755)) PFATAL("Cannot create '%s'.", tmp); - if (chdir((char*)tmp)) PFATAL("chdir unexpectedly fails!"); - - fprintf(f, " { 'severity': %d, 'type': %d, 'samples': [\n", - PSEV(i_samp[i].type) - 1, i_samp[i].type); - - for (c=0;c<use_samp;c++) { - u8 tmp2[32]; - u8* p = serialize_path(i_samp[i].i[c]->req, 1, 0); - sprintf((char*)tmp2, "%u", c); - if (mkdir((char*)tmp2, 0755)) PFATAL("Cannot create '%s'.", tmp2); - if (chdir((char*)tmp2)) PFATAL("chdir unexpectedly fails!"); - save_req_res(i_samp[i].i[c]->req, i_samp[i].i[c]->res, 0); - if (chdir("..")) PFATAL("chdir unexpectedly fails!"); - fprintf(f, " { 'url': '%s', ", js_escape(p, 0)); - fprintf(f, "'extra': '%s', 'dir': '%s/%s' }%s\n", - i_samp[i].i[c]->extra ? js_escape(i_samp[i].i[c]->extra, 0) : - (u8*)"", tmp, tmp2, - (c == use_samp - 1) ? " ]" : ","); - ck_free(p); - } - - fprintf(f, " }%s\n", (i == i_samp_cnt - 1) ? "" : ","); - if (chdir("..")) PFATAL("chdir unexpectedly fails!"); - } - - fprintf(f, "];\n\n"); - fclose(f); - -} - - -/* Copies over assets to target directory. */ - -static u8* ca_out_dir; - -static int copy_asset(const struct dirent* d) { - u8 *itmp, *otmp, buf[1024]; - s32 i, o; - - if (d->d_name[0] == '.' || !strcmp(d->d_name, "COPYING")) return 0; - - itmp = ck_alloc(strlen(ASSETS_DIR) + strlen(d->d_name) + 2); - sprintf((char*)itmp, "%s/%s", ASSETS_DIR, d->d_name); - i = open((char*)itmp, O_RDONLY); - - otmp = ck_alloc(strlen((char*)ca_out_dir) + strlen(d->d_name) + 2); - sprintf((char*)otmp, "%s/%s", ca_out_dir, d->d_name); - o = open((char*)otmp, O_WRONLY | O_CREAT | O_EXCL, 0644); - - if (i >= 0 && o >= 0) { - s32 c; - while ((c = read(i, buf, 1024)) > 0) - if (write(o, buf, c) != c) break; - } - - close(i); - close(o); - - ck_free(itmp); - ck_free(otmp); - - return 0; - -} - - -static void copy_static_code(u8* out_dir) { - struct dirent** d; - ca_out_dir = out_dir; - scandir(ASSETS_DIR, &d, copy_asset, NULL); -} - - -/* Saves all pivots for use by third-party tools. */ - -static void save_pivots(FILE* f, struct pivot_desc* cur) { - - u32 i; - - if (cur->req) { - u8* url = serialize_path(cur->req, 1, 1); - - fprintf(f, "%s %s ", cur->req->method ? cur->req->method : (u8*)"GET", - js_escape(url, 0)); - - ck_free(url); - - fprintf(f, "name=%s ", js_escape(cur->name, 1)); - - switch (cur->type) { - case PIVOT_SERV: fprintf(f, "type=serv "); break; - case PIVOT_DIR: fprintf(f, "type=dir "); break; - case PIVOT_FILE: fprintf(f, "type=file "); break; - case PIVOT_PATHINFO: fprintf(f, "type=pathinfo "); break; - case PIVOT_VALUE: fprintf(f, "type=value "); break; - case PIVOT_UNKNOWN: fprintf(f, "type=unknown "); break; - case PIVOT_PARAM: fprintf(f, "type=param "); break; - default: fprintf(f, "type=??? "); - } - - switch (cur->linked) { - case 0: fprintf(f, "linked=no "); break; - case 1: fprintf(f, "linked=maybe "); break; - default: fprintf(f, "linked=yes "); - } - - if (cur->res) - fprintf(f, "dup=%u %s%scode=%u len=%u notes=%u\n", cur->dupe, - cur->bogus_par ? "bogus " : "", - cur->missing ? "returns_404 " : "", - cur->res->code, cur->res->pay_len, cur->issue_cnt); - else - fprintf(f, "not_fetched\n"); - - } - - for (i=0;i<cur->child_cnt;i++) save_pivots(f, cur->child[i]); - -} - - -static void save_all_pivots(void) { - FILE *f = fopen("pivots.txt", "w"); - - if (!f) PFATAL("Cannot create 'pivots.txt'."); - - save_pivots(f, &root_pivot); - - fclose(f); -} - - - -/* Writes report to index.html in the current directory. Will create - subdirectories, helper files, etc. */ - -void write_report(u8* out_dir, u64 scan_time, u32 seed) { - - SAY(cLGN "[+] " cNOR "Copying static resources...\n"); - copy_static_code(out_dir); - - if (chdir((char*)out_dir)) PFATAL("Cannot chdir to '%s'", out_dir); - - sort_annotate_pivot(&root_pivot); - SAY("\n"); - - collect_signatures(&root_pivot); - SAY("\n"); - - compute_counts(&root_pivot); - SAY("\n"); - - SAY(cLGN "[+] " cNOR "Saving pivot data for third-party tools...\n"); - save_all_pivots(); - - SAY(cLGN "[+] " cNOR "Writing scan description...\n"); - output_scan_info(scan_time, seed); - - output_crawl_tree(&root_pivot); - SAY("\n"); - - SAY(cLGN "[+] " cNOR "Generating summary views...\n"); - output_summary_views(); - - SAY(cLGN "[+] " cNOR "Report saved to '" cLBL "%s/index.html" cNOR "' [" - cLBL "0x%08x" cNOR "].\n", out_dir, seed); - -} diff -Nru skipfish-2.02b/report.h skipfish-2.10b/report.h --- skipfish-2.02b/report.h 2011-07-02 06:35:05.000000000 +0000 +++ skipfish-2.10b/report.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -/* - skipfish - post-processing and reporting - ---------------------------------------- - - Author: Michal Zalewski <lcamtuf@google.com> - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#ifndef _HAVE_REPORT_H - -#include "types.h" - -extern u8 suppress_dupes; - -/* Writes report to index.html in the current directory. Will create - subdirectories, helper files, etc. */ - -void write_report(u8* out_dir, u64 scan_time, u32 seed); - -/* Destroys all signatures created for pivot and issue clustering purposes. */ - -void destroy_signatures(void); - -#endif /* !_HAVE_REPORT_H */ diff -Nru skipfish-2.02b/same_test.c skipfish-2.10b/same_test.c --- skipfish-2.02b/same_test.c 2011-07-02 06:35:05.000000000 +0000 +++ skipfish-2.10b/same_test.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -/* - skipfish - same_page() test utility - ----------------------------------- - - Author: Michal Zalewski <lcamtuf@google.com> - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <signal.h> -#include <getopt.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/time.h> -#include <time.h> -#include <sys/stat.h> - -#include "types.h" -#include "alloc-inl.h" -#include "string-inl.h" - -#include "crawler.h" -#include "analysis.h" -#include "database.h" -#include "http_client.h" -#include "report.h" - -#ifdef DEBUG_ALLOCATOR -struct __AD_trk_obj* __AD_trk[ALLOC_BUCKETS]; -u32 __AD_trk_cnt[ALLOC_BUCKETS]; -#endif /* DEBUG_ALLOCATOR */ - -#define MAX_LEN (1024*1024) - -u8 p1[MAX_LEN], p2[MAX_LEN]; - -int main(int argc, char** argv) { - static struct http_response r1, r2; - s32 l1, l2; - - l1 = read(8, p1, MAX_LEN); - l2 = read(9, p2, MAX_LEN); - - if (l1 < 0 || l2 < 0) - FATAL("Usage: ./same_test 8<file1 9<file2"); - - r1.code = 123; - r2.code = 123; - - r1.payload = p1; - r2.payload = p2; - r1.pay_len = l1; - r2.pay_len = l2; - - fprint_response(&r1); - fprint_response(&r2); - - debug_same_page(&r1.sig, &r2.sig); - - if (same_page(&r1.sig, &r2.sig)) - DEBUG("=== PAGES SEEM THE SAME ===\n"); - else - DEBUG("=== PAGES ARE DIFFERENT ===\n"); - - return 0; - -} diff -Nru skipfish-2.02b/sfscandiff skipfish-2.10b/sfscandiff --- skipfish-2.02b/sfscandiff 2010-11-17 23:53:23.000000000 +0000 +++ skipfish-2.10b/sfscandiff 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -#!/bin/bash - -echo "sfscandiff - skipfish scan result comparator (lcamtuf@google.com)" 1>&2 - -if [ ! "$#" = "2" ]; then - echo "Usage: $0 /path/to/old/scan/ /path/to/new/scan/" 1>&2 - exit 1 -fi - -if [ ! -s "$1/summary.js" ]; then - echo "ERROR: First parameter does not point to a valid skipfish scan directory." 1>&2 - exit 1 -fi - -if [ ! -s "$2/summary.js" ]; then - echo "ERROR: Second parameter does not point to a valid skipfish scan directory." 1>&2 - exit 1 -fi - -OLD_SCAN="$1" -NEW_SCAN="$2" - -# Takes two parameters: old scan subdir and new scan subdir - -function check_dir { - - # echo "Comparing: old=[$1] new=[$2]..." - - echo "0" >"$2/.diff_cnt" - - echo "var diff_data = {" >"$2/diff_data.js" - - grep "'dir':" "$2/child_index.js" | awk -F "'dir': " '{print $2}' | \ - sed "s/,.*'sig'://" | sed "s/[,}]*$//" |sed "s/'//g" | \ - while read -r dir sig; do - - # echo " Checking dir=[$dir] sig=[$sig]" - - # Find matching child node first. - - MATCH_DIR=`grep -E "'sig': $sig[, ]" "$1/child_index.js" 2>/dev/null | \ - awk -F "'dir': " '{print $2}' | cut -d"'" -f2 | head -1` - - test "$MATCH_DIR" = "" && MATCH_DIR="not_found" - - # Recurse into children first, to get an accurate count of differences - # for all descendants. - - check_dir "$1/$MATCH_DIR" "$2/$dir" - - # Read difference count from descendands. If node does not appear in - # old scan, add 1 to the count. Store count. - - DIFF_CNT=`cat "$2/$dir/.diff_cnt" 2>/dev/null` - test "$DIFF_CNT" = "" && DIFF_CNT=0 - - test "$MATCH_DIR" = "not_found" && DIFF_CNT=$[DIFF_CNT+1] - - echo " '$dir': $DIFF_CNT," >>"$2/diff_data.js" - - # Update total count for parent node ($2) - - TOTAL_DIFF_CNT=`cat "$2/.diff_cnt" 2>/dev/null` - TOTAL_DIFF_CNT=$[TOTAL_DIFF_CNT+DIFF_CNT] - echo "$TOTAL_DIFF_CNT" >"$2/.diff_cnt" - - done - - # Now, for every issue, see if a matching issue appears in old scan. - # If not, add it to diff_data. - - grep "'severity':" "$2/issue_index.js" | while read -r line; do - - LOOK_FOR=`echo "$line" | awk -F"'fetched':" '{print $1}'` - ISSUE_DIR=`echo "$line" | awk -F"'dir':" '{print $2}'|cut -d"'" -f2` - - # echo " Checking issue=[$ISSUE_DIR]" - - if ! grep -qF "$LOOK_FOR" "$1/issue_index.js" 2>/dev/null; then - echo " '$ISSUE_DIR': 1," >>"$2/diff_data.js" - fi - - done - - echo " '_eof': 0" >>"$2/diff_data.js" - echo "};" >>"$2/diff_data.js" - -} - -echo -n "Finding new results in $NEW_SCAN... " - -check_dir "$OLD_SCAN" "$NEW_SCAN" - -TOTAL=`cat "$NEW_SCAN/.diff_cnt"` - -if [ "$TOTAL" = "0" ]; then - echo "no new findings." -elif [ "$TOTAL" = "1" ]; then - echo "one new or modified node found." -else - echo "$TOTAL new or modified nodes found." -fi - -grep -qF "var diff_mode" "$NEW_SCAN/summary.js" || - echo "var diff_mode = true;" >>"$NEW_SCAN/summary.js" - -exit 0 diff -Nru skipfish-2.02b/signatures/apps.sigs skipfish-2.10b/signatures/apps.sigs --- skipfish-2.02b/signatures/apps.sigs 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/signatures/apps.sigs 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,31 @@ + +# ############################################## +# Detect interesting apps / pages that leak info +# and where their exposure is a security risk by +# default. + +# A phpinfo() page +id:11001; sev:3; content:"<title>phpinfo()phpMyAdmin '; depth:1024; \ + content:'"; depth:512; \ + content:'"; depth:512; \ + content:''; depth:512; + +# Web.xml config file +id:31010; sev:3; memo:"web.xml config file"; \ + content:""; \ + content:!""; +id:32005; sev:3; memo:"JSP source"; \ + content:"<%@"; \ + content:"%>"; +id:32006; sev:3; memo:"ASP source"; \ + content:"<%"; \ + content:"%>"; + +# These two need to be improved! +id:32007; sev:3; memo:"DOS batch script"; \ + content:"@echo "; depth:256; +id:32008; sev:3; memo:"Windows shell script"; \ + content:"(\"Wscript."; depth:256; + diff -Nru skipfish-2.02b/signatures/messages.sigs skipfish-2.10b/signatures/messages.sigs --- skipfish-2.02b/signatures/messages.sigs 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/signatures/messages.sigs 2012-12-04 13:27:54.000000000 +0000 @@ -0,0 +1,53 @@ + +##################################### +# INTERESTING SERVER ERRORS + +# SQL related error strings +id:21001; prob:40402; content:"Warning: MySQL: "; memo:"MySQL error string"; +id:21002; prob:40402; content:"Unclosed quotation mark"; memo:"SQL error string"; +id:21003; prob:40402; content:"java.sql.SQLException:"; memo:"Java SQL exception"; +id:21004; prob:40402; content:"SqlClient.SqlException: Syntax error"; memo:"SqlClient exception"; +id:21005; prob:40402; content:"PostgreSQL query failed"; memo:"PostgreSQL query failed"; +id:21006; prob:40402; content:"Dynamic SQL Error"; memo:"SQL error string"; +id:21007; prob:40402; content:"unable to perform query"; memo:"Possible SQL error string"; +id:21008; prob:40402; content:"Microsoft OLE DB Provider for ODBC Drivers
    "; memo:"OLE SQL error"; +id:21009; prob:40402; content:"[Microsoft][ODBC SQL Server Driver]"; memo:"Microsoft SQL error"; +id:21010; prob:40402; content:"Syntax error in string in query expression"; memo:"SQL syntax string"; +id:21011; prob:40402; content:"You have an error in your SQL syntax; "; memo:"SQL syntax error"; +id:21012; prob:40402; content:"Incorrect syntax near"; memo:"SQL syntax error"; +id:21013; prob:40402; content:"[DM_QUERY_E_SYNTAX]"; memo:"SQL syntax error"; + +# Stacktraces and server errors +id:22002; prob:40402; content:"error '"; memo:"Microsoft runtime error"; +id:22003; prob:40402; content:"[an error occurred while processing"; memo:"SHTML error"; +id:22004; prob:40402; content:"Traceback (most recent call last):"; memo:"Python error"; +id:22005; prob:40402; content:"JRun Servlet Error"; memo:"JRun servlet error"; + +# Java exceptions +id:22006; prob:40402; content:"Stacktrace:"; \ + content:"javax.servlet."; \ + content:"note The full stack trace"; \ + memo:"Java server stacktrace"; + +id:22007; prob:40402; content:"at java.lang.Thread.run"; \ + content:".java:"; \ + memo:"Java runtime stacktrace"; + +id:22020; prob:40402; content:"type Exception report

    "; \ + content:"

    description The server "; depth:512; \ + memo:"Java server exception"; + +# PHP HTML and text errors. The text and HTML sigs can perhaps be merged, +id:22008; prob:40402; content:"Fatal error: "; content:" on line "; depth:512; memo:"PHP error (HTML)"; +id:22009; prob:40402; content:"Fatal error: "; content:" on line "; depth:512; memo:"PHP error (text)"; +id:22010; prob:40402; content:"Parse error: "; content:" on line "; depth:512; memo:"PHP parse error (HTML)"; +id:22011; prob:40402; content:"Parse error: "; content:" on line "; depth:512; memo:"PHP parse error (text)"; +id:22012; prob:40402; content:"Notice: "; content:" on line "; depth:512; memo:"PHP notice (HTML)"; +id:22013; prob:40402; content:"Notice: "; content:" on line "; depth:512; memo:"PHP notice (text)"; +id:22014; prob:40402; content:"Strict Standards: "; content:" on line "; depth:512; memo:"PHP warning (HTML)"; +id:22015; prob:40402; content:"Strict Standards: "; content:" on line "; depth:512; memo:"PHP warning (text)"; +id:22016; prob:40402; content:"Catchable fatal error: "; content:" on line "; depth:512; memo:"PHP error (HTML)"; +id:22017; prob:40402; content:"Catchable fatal error: "; content:" on line "; depth:512; memo:"PHP error (text)"; +id:22018; prob:40402; content:"Warning: "; content:" on line "; depth:512; memo:"PHP warning (HTML)"; +id:22019; prob:40402; content:"Warning: "; content:" on line "; depth:512; memo:"PHP warning (text)"; + diff -Nru skipfish-2.02b/signatures/mime.sigs skipfish-2.10b/signatures/mime.sigs --- skipfish-2.02b/signatures/mime.sigs 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/signatures/mime.sigs 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,13 @@ + +#################################### +# INTERESTING MIME TYPES + +id:41001; sev:4; mime:"application/vnd.ms-excel"; memo:"Microsoft Excel spreadsheet (mime)"; +id:41002; sev:4; mime:"application/vnd.ms-project"; memo:"Microsoft Project file (mime)"; +id:41003; sev:4; mime:"application/x-httpd-php-source"; memo:"PHP source file (mime)"; +id:41004; sev:4; mime:"application/x-msdos-program"; memo:"DOS executable or script (mime)"; +id:41005; sev:4; mime:"application/x-msi"; memo:"MSI installer (mime)"; +id:41006; sev:3; mime:"application/x-python-code"; memo:"Python code (mime)"; +id:41007; sev:3; mime:"application/x-shellscript"; memo:"Shell script (mime)"; + + diff -Nru skipfish-2.02b/signatures/signatures.conf skipfish-2.10b/signatures/signatures.conf --- skipfish-2.02b/signatures/signatures.conf 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/signatures/signatures.conf 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,28 @@ +############################################# +## +## Master signature file. +## + +# The mime signatures warn about server responses that have an interesting +# mime. For example anything that is presented as php-source will likely +# be interesting +include signatures/mime.sigs + +# The files signature will use the content to determine if a response +# is an interesting file. For example, a SVN file. +include signatures/files.sigs + +# The messages signatures look for interesting server messages. Most +# are based on errors, such as caused by incorrect SQL queries or PHP +# execution failures. +include signatures/messages.sigs + +# The apps signatures will help to find pages and applications who's +# functionality is a security risk by default. For example, phpinfo() +# pages that leak information or CMS admin interfaces. +include signatures/apps.sigs + +# Context signatures are linked to injection tests. They look for strings +# that are relevant to the current injection test and help to highlight +# potential vulnerabilities. +include signatures/context.sigs diff -Nru skipfish-2.02b/skipfish.1 skipfish-2.10b/skipfish.1 --- skipfish-2.02b/skipfish.1 2011-06-23 07:25:09.000000000 +0000 +++ skipfish-2.10b/skipfish.1 1970-01-01 00:00:00.000000000 +0000 @@ -1,157 +0,0 @@ -.\" vi:set wm=5 -.TH SKIPFISH 1 "March 23, 2010" -.SH NAME -skipfish \- active web application security reconnaissance tool -.SH SYNOPSIS -.B skipfish -.RI [ options ] " -o output-directory start-url [start-url2 ...]" -.br -.SH DESCRIPTION -.PP -\fBskipfish\fP is an active web application security reconnaissance tool. -It prepares an interactive sitemap for the targeted site by carrying out a recursive crawl and dictionary-based probes. -The resulting map is then annotated with the output from a number of active (but hopefully non-disruptive) security checks. -The final report generated by the tool is meant to serve as a foundation for professional web application security assessments. -.SH OPTIONS - -.SS Authentication and access options: -.TP -.B \-A user:pass -use specified HTTP authentication credentials -.TP -.B \-F host=IP -pretend that 'host' resolves to 'IP' -.TP -.B \-C name=val -append a custom cookie to all requests -.TP -.B \-H name=val -append a custom HTTP header to all requests -.TP -.B \-b (i|f|p) -use headers consistent with MSIE / Firefox / iPhone -.TP -.B \-N -do not accept any new cookies - -.SS Crawl scope options: -.TP -.B \-d max_depth -maximum crawl tree depth (default: 16) -.TP -.B \-c max_child -maximum children to index per node (default: 512) -.TP -.B \-x max_desc -maximum descendants to index per crawl tree branch (default: 8192) -.TP -.B \-r r_limit -max total number of requests to send (default: 100000000) -.TP -.B \-p crawl% -node and link crawl probability (default: 100%) -.TP -.B \-q hex -repeat a scan with a particular random seed -.TP -.B \-I string -only follow URLs matching 'string' -.TP -.B \-X string -exclude URLs matching 'string' -.TP -.B \-K string -do not fuzz query parameters or form fields named 'string' -.TP -.B \-Z -do not descend into directories that return HTTP 500 code -.TP -.B \-D domain -also crawl cross-site links to a specified domain -.TP -.B \-B domain -trust, but do not crawl, content included from a third-party domain -.TP -.B \-O -do not submit any forms -.TP -.B \-P -do not parse HTML and other documents to find new links - -.SS Reporting options: -.TP -.B \-o dir -write output to specified directory (required) -.TP -.B \-M -log warnings about mixed content or non-SSL password forms -.TP -.B \-E -log all HTTP/1.0 / HTTP/1.1 caching intent mismatches -.TP -.B \-U -log all external URLs and e-mails seen -.TP -.B \-Q -completely suppress duplicate nodes in reports -.TP -.B \-u -be quiet, do not display realtime scan statistics - -.SS Dictionary management options: -.TP -.B \-W wordlist -load an alternative wordlist (skipfish.wl) -.TP -.B \-L -do not auto-learn new keywords for the site -.TP -.B \-V -do not update wordlist based on scan results -.TP -.B \-Y -do not fuzz extensions during most directory brute-force steps -.TP -.B \-R age -purge words that resulted in a hit more than 'age' scans ago -.TP -.B \-T name=val -add new form auto-fill rule -.TP -.B \-G max_guess -maximum number of keyword guesses to keep in the jar (default: 256) - -.SS Performance settings: -.TP -.B \-g max_conn -maximum simultaneous TCP connections, global (default: 50) -.TP -.B \-m host_conn -maximum simultaneous connections, per target IP (default: 10) -.TP -.B \-f max_fail -maximum number of consecutive HTTP errors to accept (default: 100) -.TP -.B \-t req_tmout -total request response timeout (default: 20 s) -.TP -.B \-w rw_tmout -individual network I/O timeout (default: 10 s) -.TP -.B \-i idle_tmout -timeout on idle HTTP connections (default: 10 s) -.TP -.B \-s s_limit -response size limit (default: 200000 B) -.TP -.B \-e -do not keep binary responses for reporting - -.TP -.B \-h, \-\-help -Show summary of options. -.SH AUTHOR -skipfish was written by Michal Zalewski . -.PP -This manual page was written by Thorsten Schifferdecker , -for the Debian project (and may be used by others). diff -Nru skipfish-2.02b/skipfish.c skipfish-2.10b/skipfish.c --- skipfish-2.02b/skipfish.c 2011-07-02 08:55:17.000000000 +0000 +++ skipfish-2.10b/skipfish.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,660 +0,0 @@ -/* - skipfish - main entry point - --------------------------- - - Author: Michal Zalewski - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "types.h" -#include "alloc-inl.h" -#include "string-inl.h" - -#include "crawler.h" -#include "analysis.h" -#include "database.h" -#include "http_client.h" -#include "report.h" - -#ifdef DEBUG_ALLOCATOR -struct __AD_trk_obj* __AD_trk[ALLOC_BUCKETS]; -u32 __AD_trk_cnt[ALLOC_BUCKETS]; -#endif /* DEBUG_ALLOCATOR */ - -/* Ctrl-C handler... */ - -static u8 stop_soon, clear_screen; - -static void ctrlc_handler(int sig) { - stop_soon = 1; -} - - -/* Screen resizing handler. */ - -static void resize_handler(int sig) { - clear_screen = 1; -} - - -/* Usage info. */ - -static void usage(char* argv0) { - SAY("Usage: %s [ options ... ] -o output_dir start_url [ start_url2 ... ]\n\n" - - "Authentication and access options:\n\n" - - " -A user:pass - use specified HTTP authentication credentials\n" - " -F host=IP - pretend that 'host' resolves to 'IP'\n" - " -C name=val - append a custom cookie to all requests\n" - " -H name=val - append a custom HTTP header to all requests\n" - " -b (i|f|p) - use headers consistent with MSIE / Firefox / iPhone\n" -#ifdef PROXY_SUPPORT - " -J proxy - use a specified HTTP proxy server\n" -#endif /* PROXY_SUPPORT */ - " -N - do not accept any new cookies\n\n" - - "Crawl scope options:\n\n" - - " -d max_depth - maximum crawl tree depth (%u)\n" - " -c max_child - maximum children to index per node (%u)\n" - " -x max_desc - maximum descendants to index per branch (%u)\n" - " -r r_limit - max total number of requests to send (%u)\n" - " -p crawl%% - node and link crawl probability (100%%)\n" - " -q hex - repeat probabilistic scan with given seed\n" - " -I string - only follow URLs matching 'string'\n" - " -X string - exclude URLs matching 'string'\n" - " -K string - do not fuzz parameters named 'string'\n" - " -D domain - crawl cross-site links to another domain\n" - " -B domain - trust, but do not crawl, another domain\n" - " -Z - do not descend into 5xx locations\n" - " -O - do not submit any forms\n" - " -P - do not parse HTML, etc, to find new links\n\n" - - "Reporting options:\n\n" - - " -o dir - write output to specified directory (required)\n" - " -M - log warnings about mixed content / non-SSL passwords\n" - " -E - log all HTTP/1.0 / HTTP/1.1 caching intent mismatches\n" - " -U - log all external URLs and e-mails seen\n" - " -Q - completely suppress duplicate nodes in reports\n" - " -u - be quiet, disable realtime progress stats\n\n" - - "Dictionary management options:\n\n" - - " -W wordlist - load an alternative wordlist (%s)\n" - " -L - do not auto-learn new keywords for the site\n" - " -V - do not update wordlist based on scan results\n" - " -Y - do not fuzz extensions in directory brute-force\n" - " -R age - purge words hit more than 'age' scans ago\n" - " -T name=val - add new form auto-fill rule\n" - " -G max_guess - maximum number of keyword guesses to keep (%d)\n\n" - - "Performance settings:\n\n" - - " -g max_conn - max simultaneous TCP connections, global (%u)\n" - " -m host_conn - max simultaneous connections, per target IP (%u)\n" - " -f max_fail - max number of consecutive HTTP errors (%u)\n" - " -t req_tmout - total request response timeout (%u s)\n" - " -w rw_tmout - individual network I/O timeout (%u s)\n" - " -i idle_tmout - timeout on idle HTTP connections (%u s)\n" - " -s s_limit - response size limit (%u B)\n" - " -e - do not keep binary responses for reporting\n\n" - - "Send comments and complaints to .\n", argv0, - max_depth, max_children, max_descendants, max_requests, DEF_WORDLIST, - MAX_GUESSES, max_connections, max_conn_host, max_fail, resp_tmout, - rw_tmout, idle_tmout, size_limit); - - exit(1); -} - - -/* Welcome screen. */ - -#ifdef SHOW_SPLASH -void splash_screen(void) { - char keybuf[8]; - u32 time_cnt = 0; - - SAY("\x1b[H\x1b[J"); - - SAY(cBRI "Welcome to " cYEL "skipfish" cBRI ". Here are some useful tips:\n\n" - - "1) To abort the scan at any time, press " cCYA "Ctrl-C" cBRI ". A partial report will be written\n" - " to the specified location. To view a list of currently scanned URLs, you can\n" - " press " cCYA "space" cBRI " at any time during the scan.\n\n" - - "2) Watch the number requests per second shown on the main screen. If this figure\n" - " drops below 100-200, the scan will likely take a very long time.\n\n" - - "3) The scanner does not auto-limit the scope of the scan; on complex sites, you\n" - " may need to specify locations to exclude, or limit brute-force steps.\n\n" - - "4) There are several new releases of the scanner every month. If you run into\n" - " trouble, check for a newer version first, let the author know next.\n\n" - - "More info: " cYEL "http://code.google.com/p/skipfish/wiki/KnownIssues\n\n" cBRI); - - if (!no_fuzz_ext && (keyword_orig_cnt * extension_cnt) > 1000) { - - SAY(cLRD - - "NOTE: The scanner is currently configured for directory brute-force attacks,\n" - "and will make about " cBRI "%u" cLRD " requests per every fuzzable location. If this is\n" - "not what you wanted, stop now and consult the documentation.\n\n", - keyword_orig_cnt * extension_cnt); - - } - - SAY(cLBL "Press any key to continue (or wait 60 seconds)... "); - - while (!stop_soon && fread(keybuf, 1, sizeof(keybuf), stdin) == 0 && time_cnt++ < 600) - usleep(100000); - -} -#endif /* SHOW_SPLASH */ - - -/* Load URLs from file. */ - -static void read_urls(u8* fn) { - FILE* f = fopen((char*)fn, "r"); - u8 tmp[MAX_URL_LEN]; - u32 loaded = 0; - - if (!f) FATAL("Unable to open '%s'.", fn); - - while (fgets((char*)tmp, MAX_URL_LEN, f)) { - struct http_request *req; - u8* url = tmp; - u32 l; - - while (isspace(*url)) url++; - - l = strlen((char*)url); - while (l && isspace(url[l-1])) l--; - url[l] = 0; - - if (*url == '#' || !*url) continue; - - req = ck_alloc(sizeof(struct http_request)); - - if (parse_url(url, req, NULL)) - FATAL("Scan target '%s' in file '%s' is not a valid absolute URL.", url, fn); - - if (!url_allowed_host(req)) - APPEND_FILTER(allow_domains, num_allow_domains, - __DFL_ck_strdup(req->host)); - - if (!url_allowed(req)) - FATAL("URL '%s' in file '%s' explicitly excluded by -I / -X rules.", - url, fn); - - maybe_add_pivot(req, NULL, 2); - destroy_request(req); - loaded++; - - } - - fclose(f); - - if (!loaded) FATAL("No valid URLs found in '%s'.", fn); - -} - - -/* Main entry point */ - -int main(int argc, char** argv) { - s32 opt; - u32 loop_cnt = 0, purge_age = 0, seed; - u8 dont_save_words = 0, show_once = 0, be_quiet = 0, display_mode = 0, - has_fake = 0; - u8 *wordlist = NULL, *output_dir = NULL; - - struct termios term; - struct timeval tv; - u64 st_time, en_time; - - signal(SIGINT, ctrlc_handler); - signal(SIGWINCH, resize_handler); - signal(SIGPIPE, SIG_IGN); - SSL_library_init(); - - /* Come up with a quasi-decent random seed. */ - - gettimeofday(&tv, NULL); - seed = tv.tv_usec ^ (tv.tv_sec << 16) ^ getpid(); - - SAY("skipfish version " VERSION " by \n"); - - while ((opt = getopt(argc, argv, - "+A:F:C:H:b:Nd:c:x:r:p:I:X:D:POYQMZUEK:W:LVT:J:G:R:B:q:g:m:f:t:w:i:s:o:hue")) > 0) - - switch (opt) { - - case 'A': { - u8* x = (u8*)strchr(optarg, ':'); - if (!x) FATAL("Credentials must be in 'user:pass' form."); - *(x++) = 0; - auth_user = (u8*)optarg; - auth_pass = x; - auth_type = AUTH_BASIC; - break; - } - -#ifdef PROXY_SUPPORT - case 'J': { - u8* x = (u8*)strchr(optarg, ':'); - if (!x) FATAL("Proxy data must be in 'host:port' form."); - *(x++) = 0; - use_proxy = (u8*)optarg; - use_proxy_port = atoi((char*)x); - if (!use_proxy_port) FATAL("Incorrect proxy port number."); - break; - } -#endif /* PROXY_SUPPORT */ - - case 'F': { - u8* x = (u8*)strchr(optarg, '='); - u32 fake_addr; - if (!x) FATAL("Fake mappings must be in 'host=IP' form."); - *x = 0; - fake_addr = inet_addr((char*)x + 1); - if (fake_addr == (u32)-1) - FATAL("Could not parse IP address '%s'.", x + 1); - fake_host((u8*)optarg, fake_addr); - has_fake = 1; - break; - } - - case 'H': { - u8* x = (u8*)strchr(optarg, '='); - if (!x) FATAL("Extra headers must be in 'name=value' form."); - *x = 0; - if (!strcasecmp(optarg, "Cookie")) - FATAL("Do not use -H to set cookies (try -C instead)."); - SET_HDR((u8*)optarg, x + 1, &global_http_par); - break; - } - - case 'C': { - u8* x = (u8*)strchr(optarg, '='); - if (!x) FATAL("Cookies must be in 'name=value' form."); - if (strchr(optarg, ';')) - FATAL("Split multiple cookies into separate -C options."); - *x = 0; - SET_CK((u8*)optarg, x + 1, &global_http_par); - break; - } - - case 'D': - if (*optarg == '*') optarg++; - APPEND_FILTER(allow_domains, num_allow_domains, optarg); - break; - - case 'K': - APPEND_FILTER(skip_params, num_skip_params, optarg); - break; - - case 'B': - if (*optarg == '*') optarg++; - APPEND_FILTER(trust_domains, num_trust_domains, optarg); - break; - - case 'I': - if (*optarg == '*') optarg++; - APPEND_FILTER(allow_urls, num_allow_urls, optarg); - break; - - case 'X': - if (*optarg == '*') optarg++; - APPEND_FILTER(deny_urls, num_deny_urls, optarg); - break; - - case 'T': { - u8* x = (u8*)strchr(optarg, '='); - if (!x) FATAL("Rules must be in 'name=value' form."); - *x = 0; - add_form_hint((u8*)optarg, x + 1); - break; - } - - case 'N': - ignore_cookies = 1; - break; - - case 'Y': - no_fuzz_ext = 1; - break; - - case 'q': - if (sscanf(optarg, "0x%08x", &seed) != 1) - FATAL("Invalid seed format."); - srandom(seed); - break; - - case 'Q': - suppress_dupes = 1; - break; - - case 'P': - no_parse = 1; - break; - - case 'V': - dont_save_words = 1; - break; - - case 'M': - warn_mixed = 1; - break; - - case 'U': - log_ext_urls = 1; - break; - - case 'L': - dont_add_words = 1; - break; - - case 'E': - pedantic_cache = 1; - break; - - case 'O': - no_forms = 1; - break; - - case 'R': - purge_age = atoi(optarg); - if (purge_age < 3) FATAL("Purge age invalid or too low (min 3)."); - break; - - case 'd': - max_depth = atoi(optarg); - if (max_depth < 2) FATAL("Invalid value '%s'.", optarg); - break; - - case 'c': - max_children = atoi(optarg); - if (!max_children) FATAL("Invalid value '%s'.", optarg); - break; - - case 'x': - max_descendants = atoi(optarg); - if (!max_descendants) FATAL("Invalid value '%s'.", optarg); - break; - - case 'p': - crawl_prob = atoi(optarg); - if (!crawl_prob) FATAL("Invalid value '%s'.", optarg); - break; - - case 'W': - if (optarg[0] == '+') load_keywords((u8*)optarg + 1, 1, 0); - else { - if (wordlist) - FATAL("Only one -W parameter permitted (unless '+' used)."); - wordlist = (u8*)optarg; - } - break; - - case 'b': - if (optarg[0] == 'i') browser_type = BROWSER_MSIE; else - if (optarg[0] == 'f') browser_type = BROWSER_FFOX; else - if (optarg[0] == 'p') browser_type = BROWSER_PHONE; else - usage(argv[0]); - break; - - case 'g': - max_connections = atoi(optarg); - if (!max_connections) FATAL("Invalid value '%s'.", optarg); - break; - - case 'm': - max_conn_host = atoi(optarg); - if (!max_conn_host) FATAL("Invalid value '%s'.", optarg); - break; - - case 'G': - max_guesses = atoi(optarg); - if (!max_guesses) FATAL("Invalid value '%s'.", optarg); - break; - - case 'r': - max_requests = atoi(optarg); - if (!max_requests) FATAL("Invalid value '%s'.", optarg); - break; - - case 'f': - max_fail = atoi(optarg); - if (!max_fail) FATAL("Invalid value '%s'.", optarg); - break; - - case 't': - resp_tmout = atoi(optarg); - if (!resp_tmout) FATAL("Invalid value '%s'.", optarg); - break; - - case 'w': - rw_tmout = atoi(optarg); - if (!rw_tmout) FATAL("Invalid value '%s'.", optarg); - break; - - case 'i': - idle_tmout = atoi(optarg); - if (!idle_tmout) FATAL("Invalid value '%s'.", optarg); - break; - - case 's': - size_limit = atoi(optarg); - if (!size_limit) FATAL("Invalid value '%s'.", optarg); - break; - - case 'o': - if (output_dir) FATAL("Multiple -o options not allowed."); - output_dir = (u8*)optarg; - - rmdir(optarg); - - if (mkdir(optarg, 0755)) - PFATAL("Unable to create '%s'.", output_dir); - - break; - - case 'u': - be_quiet = 1; - break; - - case 'e': - delete_bin = 1; - break; - - case 'Z': - no_500_dir = 1; - break; - - default: - usage(argv[0]); - - } - -#ifdef PROXY_SUPPORT - if (has_fake && use_proxy) - FATAL("-F and -J should not be used together."); -#endif /* PROXY_SUPPORT */ - - if (access(ASSETS_DIR "/index.html", R_OK)) - PFATAL("Unable to access '%s/index.html' - wrong directory?", ASSETS_DIR); - - srandom(seed); - - if (optind == argc) - FATAL("Scan target not specified (try -h for help)."); - - if (!output_dir) - FATAL("Output directory not specified (try -h for help)."); - - if (resp_tmout < rw_tmout) - resp_tmout = rw_tmout; - - if (max_connections < max_conn_host) - max_connections = max_conn_host; - - if (!wordlist) wordlist = (u8*)DEF_WORDLIST; - - load_keywords(wordlist, 0, purge_age); - - /* Schedule all URLs in the command line for scanning. */ - - while (optind < argc) { - - struct http_request *req; - - /* Support @ notation for reading URL lists from files. */ - - if (argv[optind][0] == '@') { - read_urls((u8*)argv[optind++] + 1); - continue; - } - - req = ck_alloc(sizeof(struct http_request)); - - if (parse_url((u8*)argv[optind], req, NULL)) - FATAL("Scan target '%s' is not a valid absolute URL.", argv[optind]); - - if (!url_allowed_host(req)) - APPEND_FILTER(allow_domains, num_allow_domains, - __DFL_ck_strdup(req->host)); - - if (!url_allowed(req)) - FATAL("URL '%s' explicitly excluded by -I / -X rules.", argv[optind]); - - maybe_add_pivot(req, NULL, 2); - destroy_request(req); - - optind++; - } - - /* Char-by char stdin. */ - - tcgetattr(0, &term); - term.c_lflag &= ~ICANON; - tcsetattr(0, TCSANOW, &term); - fcntl(0, F_SETFL, O_NONBLOCK); - - gettimeofday(&tv, NULL); - st_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; - -#ifdef SHOW_SPLASH - if (!be_quiet) splash_screen(); -#endif /* SHOW_SPLASH */ - - if (!be_quiet) SAY("\x1b[H\x1b[J"); - else SAY(cLGN "[*] " cBRI "Scan in progress, please stay tuned...\n"); - - while ((next_from_queue() && !stop_soon) || (!show_once++)) { - - u8 keybuf[8]; - - if (be_quiet || ((loop_cnt++ % 100) && !show_once)) continue; - - if (clear_screen) { - SAY("\x1b[H\x1b[2J"); - clear_screen = 0; - } - - SAY(cYEL "\x1b[H" - "skipfish version " VERSION " by \n\n" - cBRI " -" cPIN " %s " cBRI "-\n\n" cNOR, - allow_domains[0]); - - - if (!display_mode) { - http_stats(st_time); - SAY("\n"); - database_stats(); - } else { - http_req_list(); - } - - SAY(" \r"); - - if (fread(keybuf, 1, sizeof(keybuf), stdin) > 0) { - display_mode ^= 1; - clear_screen = 1; - } - - } - - gettimeofday(&tv, NULL); - en_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; - - SAY("\n"); - - if (stop_soon) - SAY(cYEL "[!] " cBRI "Scan aborted by user, bailing out!" cNOR "\n"); - - term.c_lflag |= ICANON; - tcsetattr(0, TCSANOW, &term); - fcntl(0, F_SETFL, O_SYNC); - - if (!dont_save_words) save_keywords((u8*)wordlist); - - write_report(output_dir, en_time - st_time, seed); - -#ifdef LOG_STDERR - SAY("\n== PIVOT DEBUG ==\n"); - dump_pivots(0, 0); - SAY("\n== END OF DUMP ==\n\n"); -#endif /* LOG_STDERR */ - - SAY(cLGN "[+] " cBRI "This was a great day for science!" cRST "\n\n"); - -#ifdef DEBUG_ALLOCATOR - if (!stop_soon) { - destroy_database(); - destroy_http(); - destroy_signatures(); - __AD_report(); - } -#endif /* DEBUG_ALLOCATOR */ - - fflush(0); - - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); - - return 0; - -} diff -Nru skipfish-2.02b/src/alloc-inl.h skipfish-2.10b/src/alloc-inl.h --- skipfish-2.02b/src/alloc-inl.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/alloc-inl.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,502 @@ +/* + skipfish - error-checking, memory-zeroing alloc routines + -------------------------------------------------------- + + Note: when DEBUG_ALLOCATOR is set, a horribly slow but pedantic + allocation tracker is used. Don't enable this in production. + + Author: Michal Zalewski + + Copyright 2009 - 2012 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#ifndef _HAVE_ALLOC_INL_H +#define _HAVE_ALLOC_INL_H + +#include +#include + +#include "config.h" +#include "types.h" +#include "debug.h" + +#define ALLOC_CHECK_SIZE(_s) do { \ + if ((_s) > MAX_ALLOC) \ + ABORT("Bad alloc request: %u bytes", (_s)); \ + } while (0) + +#define ALLOC_CHECK_RESULT(_r,_s) do { \ + if (!(_r)) \ + ABORT("Out of memory: can't allocate %u bytes", (_s)); \ + } while (0) + +#define ALLOC_MAGIC 0xFF00 +#define ALLOC_MAGIC_F 0xFE00 + +#define ALLOC_C(_ptr) (((u16*)(_ptr))[-3]) +#define ALLOC_S(_ptr) (((u32*)(_ptr))[-1]) + +#define CHECK_PTR(_p) do { \ + if ((_p) && ALLOC_C(_p) != ALLOC_MAGIC) {\ + if (ALLOC_C(_p) == ALLOC_MAGIC_F) \ + ABORT("Use after free."); \ + else \ + ABORT("Bad alloc canary."); \ + } \ + } while (0) + + +#define CHECK_PTR_EXPR(_p) ({ \ + typeof (_p) _tmp = (_p); \ + CHECK_PTR(_tmp); \ + _tmp; \ + }) + +#ifdef CHECK_UAF +# define CP(_p) CHECK_PTR_EXPR(_p) +#else +# define CP(_p) (_p) +#endif /* ^CHECK_UAF */ + +#ifdef ALIGN_ACCESS +# define ALLOC_OFF 8 +#else +# define ALLOC_OFF 6 +#endif /* ^ALIGN_ACCESS */ + + +static inline void* __DFL_ck_alloc(u32 size) { + void* ret; + + if (!size) return NULL; + + ALLOC_CHECK_SIZE(size); + ret = malloc(size + ALLOC_OFF); + ALLOC_CHECK_RESULT(ret, size); + + ret += ALLOC_OFF; + + ALLOC_C(ret) = ALLOC_MAGIC; + ALLOC_S(ret) = size; + + return memset(ret, 0, size); +} + + +static inline void* __DFL_ck_realloc(void* orig, u32 size) { + void* ret; + u32 old_size = 0; + + if (!size) { + if (orig) { + + CHECK_PTR(orig); + + /* Catch pointer issues sooner. */ +#ifdef DEBUG_ALLOCATOR + memset(orig - ALLOC_OFF, 0xFF, ALLOC_S(orig) + ALLOC_OFF); + ALLOC_C(orig) = ALLOC_MAGIC_F; +#endif /* DEBUG_ALLOCATOR */ + + free(orig - ALLOC_OFF); + } + + return NULL; + } + + if (orig) { + + CHECK_PTR(orig); + +#ifndef DEBUG_ALLOCATOR + ALLOC_C(orig) = ALLOC_MAGIC_F; +#endif /* !DEBUG_ALLOCATOR */ + + old_size = ALLOC_S(orig); + orig -= ALLOC_OFF; + + ALLOC_CHECK_SIZE(old_size); + + } + + ALLOC_CHECK_SIZE(size); + +#ifndef DEBUG_ALLOCATOR + ret = realloc(orig, size + ALLOC_OFF); + ALLOC_CHECK_RESULT(ret, size); +#else + + /* Catch pointer issues sooner: force relocation and make sure that the + original buffer is wiped. */ + + ret = malloc(size + ALLOC_OFF); + ALLOC_CHECK_RESULT(ret, size); + + if (orig) { + + memcpy(ret + ALLOC_OFF, orig + ALLOC_OFF, MIN(size, old_size)); + memset(orig, 0xFF, old_size + ALLOC_OFF); + + ALLOC_C(orig + ALLOC_OFF) = ALLOC_MAGIC_F; + + free(orig); + + } + +#endif /* ^!DEBUG_ALLOCATOR */ + + ret += ALLOC_OFF; + + ALLOC_C(ret) = ALLOC_MAGIC; + ALLOC_S(ret) = size; + + if (size > old_size) + memset(ret + old_size, 0, size - old_size); + + return ret; +} + + +static inline void* __DFL_ck_realloc_kb(void* orig, u32 size) { + +#ifndef DEBUG_ALLOCATOR + + if (orig) { + + CHECK_PTR(orig); + + if (ALLOC_S(orig) >= size) return orig; + + size = ((size >> 10) + 1) << 10; + } + +#endif /* !DEBUG_ALLOCATOR */ + + return __DFL_ck_realloc(orig, size); +} + + +static inline u8* __DFL_ck_strdup(u8* str) { + void* ret; + u32 size; + + if (!str) return NULL; + + size = strlen((char*)str) + 1; + + ALLOC_CHECK_SIZE(size); + ret = malloc(size + ALLOC_OFF); + ALLOC_CHECK_RESULT(ret, size); + + ret += ALLOC_OFF; + + ALLOC_C(ret) = ALLOC_MAGIC; + ALLOC_S(ret) = size; + + return memcpy(ret, str, size); +} + + +static inline void* __DFL_ck_memdup(void* mem, u32 size) { + void* ret; + + if (!mem || !size) return NULL; + + ALLOC_CHECK_SIZE(size); + ret = malloc(size + ALLOC_OFF); + ALLOC_CHECK_RESULT(ret, size); + + ret += ALLOC_OFF; + + ALLOC_C(ret) = ALLOC_MAGIC; + ALLOC_S(ret) = size; + + return memcpy(ret, mem, size); +} + + +static inline u8* __DFL_ck_memdup_str(u8* mem, u32 size) { + u8* ret; + + if (!mem || !size) return NULL; + + ALLOC_CHECK_SIZE(size); + ret = malloc(size + ALLOC_OFF + 1); + ALLOC_CHECK_RESULT(ret, size); + + ret += ALLOC_OFF; + + ALLOC_C(ret) = ALLOC_MAGIC; + ALLOC_S(ret) = size; + + memcpy(ret, mem, size); + ret[size] = 0; + + return ret; +} + + +static inline void __DFL_ck_free(void* mem) { + + if (mem) { + + CHECK_PTR(mem); + +#ifdef DEBUG_ALLOCATOR + + /* Catch pointer issues sooner. */ + memset(mem - ALLOC_OFF, 0xFF, ALLOC_S(mem) + ALLOC_OFF); + +#endif /* DEBUG_ALLOCATOR */ + + ALLOC_C(mem) = ALLOC_MAGIC_F; + + free(mem - ALLOC_OFF); + + } + +} + +#ifndef DEBUG_ALLOCATOR + +/* Non-debugging mode - straightforward aliasing. */ + +#define ck_alloc __DFL_ck_alloc +#define ck_realloc __DFL_ck_realloc +#define ck_realloc_kb __DFL_ck_realloc_kb +#define ck_strdup __DFL_ck_strdup +#define ck_memdup __DFL_ck_memdup +#define ck_memdup_str __DFL_ck_memdup_str +#define ck_free __DFL_ck_free + +#else + +/* Debugging mode - include additional structures and support code. */ + +#define ALLOC_BUCKETS 4096 +#define ALLOC_TRK_CHUNK 256 + +struct TRK_obj { + void *ptr; + char *file, *func; + u32 line; +}; + + +extern struct TRK_obj* TRK[ALLOC_BUCKETS]; +extern u32 TRK_cnt[ALLOC_BUCKETS]; + +#ifndef __LP64__ +#define TRKH(_ptr) (((((u32)_ptr) >> 16) ^ ((u32)_ptr)) % ALLOC_BUCKETS) +#else +#define TRKH(_ptr) (((((u64)_ptr) >> 16) ^ ((u64)_ptr)) % ALLOC_BUCKETS) +#endif + +/* Adds a new entry to the list of allocated objects. */ + +static inline void TRK_alloc_buf(void* ptr, const char* file, const char* func, + u32 line) { + + u32 i, bucket; + + if (!ptr) return; + + bucket = TRKH(ptr); + + for (i = 0; i < TRK_cnt[bucket]; i++) + + if (!TRK[bucket][i].ptr) { + + TRK[bucket][i].ptr = ptr; + TRK[bucket][i].file = (char*)file; + TRK[bucket][i].func = (char*)func; + TRK[bucket][i].line = line; + return; + + } + + /* No space available. */ + //TRK[bucket] = __DFL_ck_realloc(TRK[bucket], + // (TRK_cnt[bucket] + 1) * sizeof(struct TRK_obj)); + + + if (!(i % ALLOC_TRK_CHUNK)) { + + TRK[bucket] = __DFL_ck_realloc(TRK[bucket], + TRK_cnt[bucket] + ALLOC_TRK_CHUNK * sizeof(struct TRK_obj)); + + } + + TRK[bucket][i].ptr = ptr; + TRK[bucket][i].file = (char*)file; + TRK[bucket][i].func = (char*)func; + TRK[bucket][i].line = line; + + TRK_cnt[bucket]++; + +} + + +/* Removes entry from the list of allocated objects. */ + +static inline void TRK_free_buf(void* ptr, const char* file, const char* func, + u32 line) { + + u32 i, bucket; + + if (!ptr) return; + + bucket = TRKH(ptr); + + for (i = 0; i < TRK_cnt[bucket]; i++) + + if (TRK[bucket][i].ptr == ptr) { + + TRK[bucket][i].ptr = 0; + return; + + } + + WARN("ALLOC: Attempt to free non-allocated memory in %s (%s:%u)", + func, file, line); + +} + + +/* Does a final report on all non-deallocated objects. */ + +static inline void __TRK_report(void) { + + u32 i, bucket; + + fflush(0); + + for (bucket = 0; bucket < ALLOC_BUCKETS; bucket++) + for (i = 0; i < TRK_cnt[bucket]; i++) + if (TRK[bucket][i].ptr) + WARN("ALLOC: Memory never freed, created in %s (%s:%u)", + TRK[bucket][i].func, TRK[bucket][i].file, TRK[bucket][i].line); + +} + + +/* Simple wrappers for non-debugging functions: */ + +static inline void* TRK_ck_alloc(u32 size, const char* file, const char* func, + u32 line) { + + void* ret = __DFL_ck_alloc(size); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_realloc(void* orig, u32 size, const char* file, + const char* func, u32 line) { + + void* ret = __DFL_ck_realloc(orig, size); + TRK_free_buf(orig, file, func, line); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_realloc_kb(void* orig, u32 size, const char* file, + const char* func, u32 line) { + + void* ret = __DFL_ck_realloc_kb(orig, size); + TRK_free_buf(orig, file, func, line); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_strdup(u8* str, const char* file, const char* func, + u32 line) { + + void* ret = __DFL_ck_strdup(str); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_memdup(void* mem, u32 size, const char* file, + const char* func, u32 line) { + + void* ret = __DFL_ck_memdup(mem, size); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void* TRK_ck_memdup_str(void* mem, u32 size, const char* file, + const char* func, u32 line) { + + void* ret = __DFL_ck_memdup_str(mem, size); + TRK_alloc_buf(ret, file, func, line); + return ret; + +} + + +static inline void TRK_ck_free(void* ptr, const char* file, + const char* func, u32 line) { + + TRK_free_buf(ptr, file, func, line); + __DFL_ck_free(ptr); + +} + +/* Alias user-facing names to tracking functions: */ + +#define ck_alloc(_p1) \ + TRK_ck_alloc(_p1, __FILE__, __FUNCTION__, __LINE__) + +#define ck_realloc(_p1, _p2) \ + TRK_ck_realloc(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) + +#define ck_realloc_kb(_p1, _p2) \ + TRK_ck_realloc_kb(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) + +#define ck_strdup(_p1) \ + TRK_ck_strdup(_p1, __FILE__, __FUNCTION__, __LINE__) + +#define ck_memdup(_p1, _p2) \ + TRK_ck_memdup(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) + +#define ck_memdup_str(_p1, _p2) \ + TRK_ck_memdup_str(_p1, _p2, __FILE__, __FUNCTION__, __LINE__) + +#define ck_free(_p1) \ + TRK_ck_free(_p1, __FILE__, __FUNCTION__, __LINE__) + +#endif /* ^!DEBUG_ALLOCATOR */ + +#define alloc_printf(_str...) ({ \ + u8* _tmp; \ + s32 _len = snprintf(NULL, 0, _str); \ + if (_len < 0) FATAL("Whoa, snprintf() fails?!"); \ + _tmp = ck_alloc(_len + 1); \ + snprintf((char*)_tmp, _len + 1, _str); \ + _tmp; \ + }) + +#endif /* ! _HAVE_ALLOC_INL_H */ diff -Nru skipfish-2.02b/src/analysis.c skipfish-2.10b/src/analysis.c --- skipfish-2.02b/src/analysis.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/analysis.c 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,2557 @@ +/* + skipfish - content analysis + --------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#define _VIA_ANALYSIS_C + +#include "debug.h" +#include "config.h" +#include "types.h" +#include "http_client.h" +#include "database.h" +#include "crawler.h" +#include "analysis.h" +#include "signatures.h" +#include "report.h" +#include "pcre.h" + +u8 no_parse, /* Disable HTML link detection */ + warn_mixed, /* Warn on mixed content */ + log_ext_urls, /* Log all external URLs */ + no_forms, /* Do not submit forms */ + pedantic_cache, /* Match HTTP/1.0 and HTTP/1.1 */ + no_checks; /* No checks / crawl only mode */ + +/* Form autofill hints: */ + +u8** addl_form_name; +u8** addl_form_value; +u32 addl_form_cnt; + + +/* Runs some rudimentary checks on top-level pivot HTTP responses. */ + +void pivot_header_checks(struct http_request* req, + struct http_response* res) { + + u32 i; + u8 *par_hdr, *cur_hdr; + + DEBUG_CALLBACK(req, res); + + /* Server: change. */ + + cur_hdr = GET_HDR((u8*)"Server", &res->hdr); + if (!RPAR(req)->res) par_hdr = NULL; + else par_hdr = GET_HDR((u8*)"Server", &RPAR(req)->res->hdr); + + if (!cur_hdr) cur_hdr = (u8*)"[none]"; + if (!par_hdr) par_hdr = (u8*)"[none]"; + + if (strcmp((char*)cur_hdr, (char*)par_hdr)) + problem(PROB_SERVER_CHANGE, req, res, cur_hdr, req->pivot, 0); + + /* Via: appears or disappears. */ + + cur_hdr = GET_HDR((u8*)"Via", &res->hdr); + if (!RPAR(req)->res) par_hdr = NULL; + else par_hdr = GET_HDR((u8*)"Via", &RPAR(req)->res->hdr); + + if (cur_hdr != par_hdr) + problem(PROB_VIA_CHANGE, req, res, cur_hdr ? cur_hdr : (u8*)"[none]", + req->pivot, 0); + + /* New X-* header appears. */ + + for (i=0;ihdr.c;i++) { + + if (res->hdr.t[i] != PARAM_HEADER || + case_prefix(res->hdr.n[i], "X-")) continue; + + if (!RPAR(req)->res) par_hdr = NULL; + else par_hdr = GET_HDR(res->hdr.n[i], &RPAR(req)->res->hdr); + + if (!par_hdr) + problem(PROB_X_CHANGE, req, res, res->hdr.n[i], req->pivot,0); + + } + + /* Old X-* header disappears. */ + + if (RPAR(req)->res) + for (i=0;ires->hdr.c;i++) { + + if (RPAR(req)->res->hdr.t[i] != PARAM_HEADER || + case_prefix(RPAR(req)->res->hdr.n[i], "X-")) continue; + + cur_hdr = GET_HDR(RPAR(req)->res->hdr.n[i], &res->hdr); + + if (!cur_hdr) + problem(PROB_X_CHANGE, req, res, RPAR(req)->res->hdr.n[i], req->pivot, 0); + + } + +} + + +/* Helper for scrape_response(). Tries to add a previously extracted link, + also checks for cross-site and mixed content issues and similar woes. + Subres is: 1 - redirect; 2 - IMG; 3 - IFRAME, EMBED, OBJECT, APPLET; + 4 - SCRIPT, LINK REL=STYLESHEET; 5 - form; 0 - everything else. */ + +static void test_add_link(u8* str, struct http_request* ref, + struct http_response* res, u8 subres, u8 sure) { + struct http_request* n; + + DEBUG_CALLBACK(ref,res); + DEBUG("* Alleged URL = '%s' [%u]\n", str, subres); + + /* Don't add injected links. */ + + if (!case_prefix(str, "skipfish:") || + !case_prefix(str, "//skipfish.invalid/") || + inl_strcasestr(str, (u8*) "/" BOGUS_FILE) || + !case_prefix(str, "http://skipfish.invalid/")) return; + + /* Don't add links that look like they came from JS code with fragmented HTML + snippets, etc. */ + + if (!sure && (strchr("!()\"' +,^:", *str) || + (*str == '/' && strchr("()\"' +,^", str[1])))) return; + + if ((str[0] == '\'' || str[0] == '"') && (str[1] == '+' || str[1] == ' ')) + return; + + if (!case_prefix(str, "mailto:")) { + + if (log_ext_urls) { + u8* qmark = (u8*)strchr((char*)str, '?'); + if (qmark) *qmark = 0; + problem(PROB_MAIL_ADDR, ref, res, str + 7, host_pivot(ref->pivot),0); + if (qmark) *qmark = '?'; + } + + return; + } + + n = ck_alloc(sizeof(struct http_request)); + + n->pivot = ref->pivot; + + if (!parse_url(str, n, ref)) { + + if (R(100) < crawl_prob) maybe_add_pivot(n, NULL, sure ? 2 : 1); + + /* Link to a third-party site? */ + + if (!url_allowed_host(n) && !url_trusted_host(n)) + switch (subres) { + + case 0: + if (log_ext_urls) + problem(PROB_EXT_LINK, ref, res, str, host_pivot(ref->pivot), 0); + break; + + case 1: + if (log_ext_urls) + problem(PROB_EXT_REDIR, ref, res, str, ref->pivot, 0); + break; + + case 2: + case 3: + problem(PROB_EXT_OBJ, ref, res, str, ref->pivot, 0); + break; + + case 4: + problem(PROB_EXT_SUB, ref, res, str, ref->pivot, 0); + break; + + } + + /* Mixed content? We don't care about or redirectors + here, though. */ + + if (ref->proto == PROTO_HTTPS && n->proto == PROTO_HTTP && + subres > 2 && warn_mixed) + switch (subres) { + case 4: problem(PROB_MIXED_SUB, ref, res, str, ref->pivot, 0); break; + case 5: problem(PROB_MIXED_FORM, ref, res, str, ref->pivot, 0); break; + default: problem(PROB_MIXED_OBJ, ref, res, str, ref->pivot, 0); + } + + } else if (!ref->proto) { + + /* Parser didn't recognize the protocol. If it's a + hierarchical URL (foo://), log it. */ + + u8* x = str; + + while (isalnum(*x)) x++; + + if (str != x && *x == ':' && x[1] == '/') + problem(PROB_UNKNOWN_PROTO, ref, res, str, ref->pivot, 0); + + } + + destroy_request(n); +} + + +/* Another scrape_response() helper - decodes HTML escaping, + maybe also JS escaping, from URLs. Returns a dynamically + allocated copy. */ + +static u8* html_decode_param(u8* url, u8 also_js) { + u32 len = strlen((char*)url); + u8* ret = ck_alloc(len + 1); + u32 i, pos = 0; + + /* If directed to do so, decode \x, \u, and \char sequences + first. */ + + if (also_js) { + + for (i=0;i 0xff) act_val = '?'; + + ret[pos++] = act_val; + + } else ret[pos++] = url[i]; + + } + + ret[pos] = 0; + url = ret; + len = pos; + pos = 0; + ret = ck_alloc(len + 1); + + } + + /* Next, do old-school HTML decoding. There are many other named + entities, of course, but the odds of them appearing in URLs + without %-encoding are negligible. */ + + for (i=0;i 0xff) act_val = '?'; + ret[pos++] = act_val; + i += strcspn((char*)url + i, ";"); + continue; + } + + /* Fall through and output the sequence as-is. */ + + } + + } else if (url[i] == '\r' || url[i] == '\n') continue; + + ret[pos++] = url[i]; + + } + + ret[pos] = 0; + if (also_js) ck_free(url); + + return ret; + +} + + +/* Macro to test for tag names */ + +#define ISTAG(_val, _tag) \ + (!strncasecmp((char*)(_val), _tag, strlen((char*)(_tag))) && \ + (isspace((_val)[strlen((char*)_tag)]) || !(_val)[strlen((char*)_tag)])) + +/* Macro to find and move past parameter name (saves result in + _store, NULL if not found). Buffer needs to be NUL-terminated + at nearest >. The macro supports the "name\s*=aaa" cases. */ + +#define FIND_AND_MOVE(_store, _val, _param) { \ + (_store) = inl_strcasestr((u8*)_val, (u8*)_param); \ + if (_store) { \ + if (!isspace((_store)[-1])) { \ + (_store) = NULL; \ + } else { \ + (_store) += strlen((char*)_param); \ + while (_store && isspace((_store)[0])) (_store)++; \ + if (_store && (_store)[0] == '=') (_store)++; \ + else (_store) = NULL; \ + } \ + } \ + } while (0) + +/* Macro to extract parameter value, handling quotes. */ + +#define EXTRACT_ALLOC_VAL(_store, _val) do { \ + u32 _val_len; \ + if (*(_val) == '\'') _val_len = strcspn((char*)++(_val), "'"); else \ + if (*(_val) == '"') _val_len = strcspn((char*)++(_val), "\""); else \ + _val_len = strcspn((char*)(_val), "> \t\r\n"); \ + (_store) = ck_memdup((_val), (_val_len) + 1); \ + (_store)[(_val_len)] = 0; \ + } while (0) + + +/* Adds a new item to the form hint system. */ + +void add_form_hint(u8* name, u8* value) { + addl_form_name = ck_realloc(addl_form_name, + (addl_form_cnt + 1) * sizeof(u8*)); + + addl_form_value = ck_realloc(addl_form_value, + (addl_form_cnt + 1) * sizeof(u8*)); + + addl_form_name[addl_form_cnt] = name; + addl_form_value[addl_form_cnt] = value; + addl_form_cnt++; + +} + + +/* Helper for collect_form_data() - comes up with a fitting value for + a checkbox. Returns a static buffer. */ + +static u8* make_up_form_value(u8* name, struct http_request* req, + struct http_response* res) { + u32 i; + + for (i=0;ipivot), 0); + + return (u8*)form_suggestion[i][1]; + +} + + +/* Helper for collect_form_data() - checks for probable anti-XSRF token + values. */ + +static u8 maybe_xsrf(u8* token) { + u8* tmp; + u32 digit_cnt = 0, upper_cnt = 0, slash_cnt = 0;; + static u8 tm_prefix[8]; + + if (!tm_prefix[0]) + sprintf((char*)tm_prefix, "%lu", (long int)(time(0) / 100000)); + + /* Unix time is not a valid token. */ + + if (!case_prefix(token, tm_prefix)) return 0; + + tmp = token; + while (*tmp && (isdigit(*tmp) || strchr("abcdef", tolower(*tmp)))) { + if (isdigit(*tmp)) digit_cnt++; + tmp++; + } + + /* Looks like base 10 or 16... */ + + if (!*tmp) { + u32 len = tmp - token; + if (len >= XSRF_B16_MIN && len <= XSRF_B16_MAX && digit_cnt >= XSRF_B16_NUM) + return 1; + return 0; + } + + digit_cnt = 0; + tmp = token; + while (*tmp && (isalnum(*tmp) || strchr("=+/", *tmp))) { + if (isdigit(*tmp)) digit_cnt++; + if (isupper(*tmp)) upper_cnt++; + if (*tmp == '/') slash_cnt++; + tmp++; + } + + /* Looks like base 32 or 64... */ + + if (!*tmp) { + u32 len = tmp - token; + if (len >= XSRF_B64_MIN && len <= XSRF_B64_MAX && ((digit_cnt >= + XSRF_B64_NUM && upper_cnt >= XSRF_B64_CASE) || digit_cnt >= + XSRF_B64_NUM2) && slash_cnt <= XSRF_B64_SLASH) return 1; + return 0; + } + + /* Looks like... not a numerical token at all. */ + + return 0; + +} + + +/* Another helper for scrape_response(): examines all tags + up until , then adds them as parameters to current request. */ + +void collect_form_data(struct http_request* req, + struct http_request* orig_req, + struct http_response* orig_res, + u8* cur_str, u8 is_post) { + + u8 has_xsrf = 0, pass_form = 0, file_form = 0; + u32 tag_cnt = 0; + + DEBUG("* collect_form_data() entered\n"); + + do { + + u8* tag_end; + + if (*cur_str == '<' && (tag_end = (u8*)strchr((char*)cur_str + 1, '>'))) { + + cur_str++; + *tag_end = 0; + + if (!case_prefix(cur_str, "/form")) { + *tag_end = '>'; + goto final_checks; + } + + if (ISTAG(cur_str, "input") || ISTAG(cur_str, "textarea") || + ISTAG(cur_str, "select") || ISTAG(cur_str, "button")) { + + u8 *tag_name, *tag_value, *tag_type, *clean_name = NULL, + *clean_value = NULL; + + FIND_AND_MOVE(tag_name, cur_str, "name"); + FIND_AND_MOVE(tag_value, cur_str, "value"); + FIND_AND_MOVE(tag_type, cur_str, "type"); + + if (!tag_name) goto next_tag; + + EXTRACT_ALLOC_VAL(tag_name, tag_name); + clean_name = html_decode_param(tag_name, 0); + ck_free(tag_name); + tag_name = 0; + + if (tag_value) { + EXTRACT_ALLOC_VAL(tag_value, tag_value); + clean_value = html_decode_param(tag_value, 0); + + ck_free(tag_value); + tag_value = 0; + } + + if (tag_type) + EXTRACT_ALLOC_VAL(tag_type, tag_type); + else tag_type = ck_strdup((u8*)"text"); + + tag_cnt++; + + if (!strcasecmp((char*)tag_type, "file")) { + + if (!is_post) { + ck_free(req->method); + req->method = ck_strdup((u8*)"POST"); + is_post = 1; + } + + set_value(PARAM_POST_F, clean_name, clean_value ? + clean_value : (u8*)"", 0, &req->par); + + } else if (!strcasecmp((char*)tag_type, "reset")) { + + /* Do nothing - do not store. */ + tag_cnt--; + + } else if (!strcasecmp((char*)tag_type, "button") || + !strcasecmp((char*)tag_type, "submit")) { + + set_value(is_post ? PARAM_POST : PARAM_QUERY, clean_name, + clean_value ? clean_value : (u8*)"", 0, &req->par); + + } else if (!strcasecmp((char*)tag_type, "checkbox")) { + + /* Turn checkboxes on. */ + + set_value(is_post ? PARAM_POST : PARAM_QUERY, clean_name, + (u8*)"on", 0, &req->par); + + } else { + + u8* use_value = clean_value; + + /* Don't second-guess hidden fields. */ + + if (strcasecmp((char*)tag_type, "hidden") && + (!use_value || !use_value[0])) { + use_value = make_up_form_value(clean_name, orig_req, orig_res); + } else { + if (!use_value) use_value = (u8*)""; + } + + /* Radio buttons are rolled back into a single parameter + because we always replace offset 0 for given clean_name. */ + + set_value(is_post ? PARAM_POST : PARAM_QUERY, + clean_name, use_value, 0, &req->par); + + if (!strcasecmp((char*)tag_type, "hidden") && + maybe_xsrf(use_value)) has_xsrf = 1; + + } + + if (!strcasecmp((char*)tag_type, "password") || + inl_strcasestr(tag_name, (u8*) "passw")) pass_form = 1; + else if (!strcasecmp((char*)tag_type, "file")) file_form = 1; + + ck_free(tag_name); + ck_free(tag_type); + ck_free(tag_value); + ck_free(clean_name); + ck_free(clean_value); + + } + +next_tag: + + *tag_end = '>'; + + } else tag_end = cur_str; + + /* Skip to next tag. */ + + cur_str = (u8*)strchr((char*)tag_end + 1, '<'); + + } while (cur_str); + +final_checks: + + if (pass_form) { + + if (warn_mixed && (req->proto != PROTO_HTTPS || orig_req->proto != PROTO_HTTPS)) + problem(PROB_PASS_NOSSL, req, orig_res, NULL, req->pivot, 0); + else + problem(PROB_PASS_FORM, req, orig_res, NULL, req->pivot, 0); + + } else { + + if (tag_cnt && !has_xsrf) { + if (file_form) + problem(PROB_FILE_FORM, req, orig_res, NULL, req->pivot, 0); + problem(PROB_VULN_FORM, req, orig_res, NULL, req->pivot, 0); + } else { + if (file_form) + problem(PROB_FILE_FORM, req, orig_res, NULL, req->pivot, 0); + else + problem(PROB_FORM, req, orig_res, NULL, req->pivot, 0); + } + } + +} + + +/* Helper for scrape_response() and content_checks: is the + file mostly ASCII? */ + +static u8 is_mostly_ascii(struct http_response* res) { + u32 i, total, printable = 0; + + if (res->doc_type) return (res->doc_type == 2); + + total = (res->pay_len > 128) ? 128 : res->pay_len; + + if (!total) { res->doc_type = 2; return 1; } + + for (i=0;ipayload[i] >= 0x20 && res->payload[i] <= 0x7f) + || (res->payload[i] && strchr("\t\r\n", res->payload[i]))) + printable++; + + if (printable * 100 / total < 90) { + DEBUG("* looks like binary data (print = %u, total = %u)\n", + printable, total); + res->doc_type = 1; + return 1; + } + + DEBUG("* looks like text file (print = %u, total = %u)\n", + printable, total); + + res->doc_type = 2; + return 1; + +} + + + +struct http_request* make_form_req(struct http_request *req, + struct http_request *base, + struct http_response *res, + u8* cur_str, u8* target) { + + u8 *method, *clean_url; + u8 *dirty_url; + struct http_request* n; + u8 parse_form = 1; + + FIND_AND_MOVE(dirty_url, cur_str, "action"); + FIND_AND_MOVE(method, cur_str, "method"); + + /* See if we need to POST this form or not. */ + + if (method && *method) { + if (strchr("\"'", *method)) method++; + if (tolower(method[0]) == 'p') parse_form = 2; + } + + /* If a form target is specified, we need to use that */ + + if (target) { + dirty_url = ck_strdup(target); + } else if (!dirty_url || !*dirty_url || !prefix(dirty_url, "\"\"") || + !prefix(dirty_url, "''")) { + + /* Forms with no URL submit to current location. */ + dirty_url = serialize_path(req, 1, 0); + } else { + /* Last, extract the URL from the tag */ + EXTRACT_ALLOC_VAL(dirty_url, dirty_url); + } + + clean_url = html_decode_param(dirty_url, 0); + ck_free(dirty_url); + + /* Add to the pivot's so we can test it later */ + test_add_link(clean_url, base ? base : req, res, 4, 1); + + n = ck_alloc(sizeof(struct http_request)); + + n->pivot = req->pivot; + if (parse_form == 2) { + ck_free(n->method); + n->method = ck_strdup((u8*)"POST"); + } else { + /* On GET forms, strip existing query params to get a submission + target. */ + u8* qmark = (u8*)strchr((char*)clean_url, '?'); + if (qmark) *qmark = 0; + } + + if (parse_url(clean_url, n, base ? base : req)) { + DEBUG("Unable to parse_url from form: %s\n", clean_url); + ck_free(clean_url); + destroy_request(n); + return NULL; + } + + ck_free(clean_url); + return n; + +} + + +/* Analyzes response headers (Location, etc), body to extract new links, + keyword guesses. This code is designed to be simple and fast, but it + does not even try to understand the intricacies of HTML or whatever + the response might be wrapped in. */ + +void scrape_response(struct http_request* req, struct http_response* res) { + + struct http_request *base = NULL; + u8* cur_str; + u32 i; + + DEBUG_CALLBACK(req, res); + + if (no_parse || res->scraped) return; + + res->scraped = 1; + + /* Do not scrape pages that are identical to their parent, or are parented + by suspicious locations. */ + + if (RPAR(req)->res && (same_page(&res->sig, &RPAR(req)->res->sig) || + RPAR(req)->bad_parent)) { + DEBUG("* Not extracting links because page looks the same as parent.\n"); + return; + } + + /* Handle Location, Refresh headers first. */ + + if ((cur_str = GET_HDR((u8*)"Location", &res->hdr))) + test_add_link(cur_str, req, res, 1, 1); + + if ((cur_str = GET_HDR((u8*)"Refresh", &res->hdr)) && + (cur_str = (u8*)strchr((char*)cur_str, '='))) + test_add_link(cur_str + 1, req, res, 1, 1); + + if (!res->payload || !is_mostly_ascii(res)) return; + + cur_str = res->payload; + + /* PASS 1: Do a simplified check to what looks like proper, + known HTML parameters bearing URLs. Note that payload is + conveniently NUL-terminated. */ + + do { + + u8 *tag_end; + + if (*cur_str == '<' && (tag_end = (u8*)strchr((char*)cur_str + 1, '>'))) { + + u32 link_type = 0; + u8 set_base = 0; + u8 is_post = 0; + u8 *dirty_url = NULL, *clean_url = NULL, *meta_url = NULL, + *delete_dirty = NULL; + + cur_str++; + *tag_end = 0; + + /* Several tags we need to handle specially, either because they + denote a particularly interesting content type (marked in + link_type, see test_add_link()), or because they use a + non-standard parameter for URL data. */ + + if (ISTAG(cur_str, "meta")) { + + link_type = 1; + FIND_AND_MOVE(dirty_url, cur_str, "content"); + + if (dirty_url) { + EXTRACT_ALLOC_VAL(meta_url, dirty_url); + dirty_url = inl_strcasestr(meta_url, (u8*)"URL="); + if (dirty_url) dirty_url += 4; + } + + } else if (ISTAG(cur_str, "img")) { + + link_type = 2; + FIND_AND_MOVE(dirty_url, cur_str, "src"); + + } else if (ISTAG(cur_str, "object") || ISTAG(cur_str, "embed") || + ISTAG(cur_str, "applet") || ISTAG(cur_str, "iframe") || + ISTAG(cur_str, "frame")) { + + link_type = 3; + FIND_AND_MOVE(dirty_url, cur_str, "src"); + if (!dirty_url) FIND_AND_MOVE(dirty_url, cur_str, "codebase"); + + } else if (ISTAG(cur_str, "param") && inl_strcasestr(cur_str, + (u8*)"movie")) { + + link_type = 3; + FIND_AND_MOVE(dirty_url, cur_str, "value"); + + } else if (ISTAG(cur_str, "script")) { + + link_type = 4; + FIND_AND_MOVE(dirty_url, cur_str, "src"); + + } else if (ISTAG(cur_str, "link") && inl_strcasestr(cur_str, + (u8*)"stylesheet")) { + + link_type = 4; + FIND_AND_MOVE(dirty_url, cur_str, "href"); + + } else if (ISTAG(cur_str, "base")) { + + set_base = 1; + FIND_AND_MOVE(dirty_url, cur_str, "href"); + + } else if (ISTAG(cur_str, "form")) { + + /* Parse the form and kick off a new pivot for further testing */ + struct http_request* n = make_form_req(req, base, res, cur_str, NULL); + if (n) { + if (url_allowed(n) && R(100) < crawl_prob && !no_forms) { + is_post = (n->method && !strcmp((char*)n->method, "POST")); + + collect_form_data(n, req, res, tag_end + 1, is_post); + maybe_add_pivot(n, NULL, 5); + } + destroy_request(n); + } + + } else { + + /* All other tags - other types, , - + are handled in a generic way. */ + + FIND_AND_MOVE(dirty_url, cur_str, "href"); + if (!dirty_url) FIND_AND_MOVE(dirty_url, cur_str, "src"); + + } + + /* If we found no URL to speak of, we're done. */ + + if (!dirty_url) { + ck_free(meta_url); + goto next_tag; + } + + /* De-quotify and decode the value. */ + + EXTRACT_ALLOC_VAL(dirty_url, dirty_url); + clean_url = html_decode_param(dirty_url, 0); + ck_free(dirty_url); + ck_free(delete_dirty); + ck_free(meta_url); + + if (!*clean_url) goto next_tag; + + test_add_link(clean_url, base ? base : req, res, link_type, 1); + + /* If we are dealing with a tag, we need to create + a new dummy request to use as a referrer. */ + + if (set_base) { + + struct http_request* n = ck_alloc(sizeof(struct http_request)); + n->pivot = req->pivot; + if (!parse_url(clean_url, n, base ? base : req)) base = n; + + } + +next_tag: + + *tag_end = '>'; + + if (clean_url) ck_free(clean_url); + + } else tag_end = cur_str; + + /* Skip to next tag. */ + + if (*tag_end) cur_str = (u8*)strchr((char*)tag_end + 1, '<'); + else cur_str = 0; + + } while (cur_str); + + cur_str = res->payload; + + /* PASS 2: Extract links from non-HTML body, JS, etc; add keywords. */ + + do { + + u32 clean_len, alpha_cnt = 0, lower_cnt = 0, lead = 0, seg_len; + u8 *ext, *token, *clean_url, *tmp, *pos_at; + u8 last = 0, saved; + + /* Skip leading whitespaces, terminators. */ + + seg_len = strspn((char*)cur_str, " \t\r\n<>\"'"); + cur_str += seg_len; + + /* If there's a = character preceeded only by alnums or underscores, + skip this chunk (to handle something=http://www.example.com/ neatly) */ + + tmp = cur_str; + while (*tmp && (isalnum(*tmp) || *tmp == '_')) tmp++; + if (*tmp == '=') cur_str = tmp + 1; + + if (!*cur_str) break; + seg_len = strcspn((char*)cur_str + 1, " \t\r\n<>\"'") + 1; + + /* Extract the segment, decoding JS and HTML on the go. */ + + saved = cur_str[seg_len]; + cur_str[seg_len] = 0; + clean_url = html_decode_param(cur_str, 1); + cur_str[seg_len] = saved; + + tmp = clean_url; + + /* We want the entire extracted segment to consist only of nice + characters we would expect in a URL. If not, panic. */ + + while (*tmp) { + if (!isalnum(*tmp) && !isspace(*tmp) && + !strchr("_-.:@/?&=#%;$!+~()[]{}\\|^*", *tmp)) goto url_done; + tmp++; + } + + clean_len = tmp - clean_url; + + /* Strip trailing characters that are unlikely to appear in valid URLs + anyway, and could be a part of some message. */ + + while (clean_len && + strchr(".,:?!-$&", clean_url[clean_len-1])) clean_len--; + + clean_url[clean_len] = 0; + + /* URL CHECK 1: Things that start with ./ or ../ are obviously URLs. + We do not make assumptins about syntax such as /foo/, though, as + it could very well be a regex in a JS block. */ + + if (!prefix(clean_url, "./") || !prefix(clean_url, "../")) { +add_link: + test_add_link(clean_url, base ? base : req, res, 0, 0); + goto url_done; + } + + /* URL CHECK 2: Things that start with :// are quite + clearly URLs. */ + + while (clean_url[lead] && (isalnum(clean_url[lead]))) lead++; + + if (lead && !prefix(clean_url + lead, "://") && + clean_url[lead + 3]) goto add_link; + + /* URL CHECK 3: If the result ends with ., + and contains a slash anywhere, assume URL (without that + slash check, we would get duped by 'domain.com'. */ + + if (strchr((char*)clean_url, '/')) { + + i = 0; + + while ((ext = wordlist_get_extension(i++, 0))) { + u32 ext_len = strlen((char*)ext); + + if (clean_len > ext_len + 2 && + !strncasecmp((char*)clean_url + clean_len - ext_len, + (char*)ext, ext_len) && + clean_url[clean_len - ext_len - 1] == '.') goto add_link; + + } + + } + + if (!(pos_at = (u8*)strchr((char*)clean_url, '@'))) { + + /* URL CHECK 4: ?= syntax is strongly indicative of + an URL (only if not e-mail). */ + + u8 *pos_qmark = (u8*)strchr((char*)clean_url, '?'), + *pos_eq = (u8*)strchr((char*)clean_url, '='), + *pos_amp = (u8*)strchr((char*)clean_url, '&'); + + if (pos_qmark && pos_eq && pos_qmark + 1 < pos_eq && + pos_eq[1] && (!pos_amp || pos_amp > pos_eq) && + pos_eq[1] != '=' && !strchr((char*)clean_url, '(') && + !strchr((char*)clean_url, '[') && + (u8*)strchr((char*)clean_url, ':') < pos_eq) + goto add_link; + + } else if (log_ext_urls) { + + /* EMAIL CHECK: If the string uses a limited set of characters, + starts with alpha, ahs at least one period after @, and both + @ and the period are immediately followed by alpha - assume + e-mail. */ + + u8 *pos_dot, + *pos_qmark = (u8*)strchr((char*)clean_url, '?'); + + if (pos_qmark && pos_qmark > pos_at) *pos_qmark = 0; + + lead = 0; + + while (clean_url[lead] && (isalnum(clean_url[lead]) || + strchr("._-+@", clean_url[lead]))) lead++; + + pos_dot = (u8*)strchr((char*)pos_at + 1, '.'); + + if (!clean_url[lead] && pos_at && pos_dot && isalpha(clean_url[0]) && + isalpha(pos_at[1]) && isalpha(pos_dot[1])) { + problem(PROB_MAIL_ADDR, req, res, clean_url, host_pivot(req->pivot), 0); + goto url_done; + } + + } + + /* LAST CHANCE: Try to detect base64; if the segment does not look like + base64, add each segment to try_list. */ + + tmp = clean_url; + + while (*tmp) { + if (isalpha(*tmp)) { + alpha_cnt++; + if (islower(*tmp)) lower_cnt++; + } + tmp++; + } + + if (alpha_cnt > 20 && (lower_cnt * 100 / alpha_cnt) > 35 && + (lower_cnt * 100 / alpha_cnt) < 65) goto url_done; + + token = clean_url; + + do { + while (*token && !isalnum(*token)) token++; + tmp = token; + while (*tmp && isalnum(*tmp)) tmp++; + if (!*tmp) last = 1; + *tmp = 0; + if (R(100) < GUESS_PROB) wordlist_add_guess(token); + token = tmp + 1; + } while (!last); + +url_done: + + ck_free(clean_url); + + cur_str += seg_len; + + } while (*cur_str); + + if (base) destroy_request(base); + + /* Phew! */ + +} + + +/* Returns 1 if document looks like standalone CSS. */ + +static u8 is_css(struct http_response* res) { + u8* text = res->payload; + u8 first = 0, last = 0; + + if (res->css_type) return (res->css_type == 2); + if (!text || !is_mostly_ascii(res)) return 0; + + do { + + /* Skip whitespaces... */ + + while (isspace(*text)) text++; + + /* Skip HTML, CSS comments. */ + + if (!prefix(text, ""); + if (next) { + tmp = next + 3; + continue; + } + } + + /* Grab tag name. */ + + tag_name = ck_memdup(tmp, len + 1); + tag_name[len] = 0; + tmp += len; + + /* Handle all parameters. */ + + while (*tmp && *tmp != '>') { + u8* param_name; + u8* clean_val = NULL; + u8* sfi_pos; + + /* Shoo, whitespaces. */ + + space_len = strspn((char*)tmp, " \t\r\n"); + tmp += space_len; + + /* Grab parameter name. */ + + len = strcspn((char*)tmp, "=> \t\r\n"); + param_name = ck_memdup(tmp, len + 1); + param_name[len] = 0; + tmp += len; + + /* Name followed by '='? Grab value. */ + + u8 quote = 0; + if (*tmp == '=') { + u32 vlen; + u8 save; + + tmp++; + + if (*tmp == '\'') { + quote = 1; + vlen = strcspn((char*)++tmp, "'"); + } else if (*tmp == '"') { + quote = 1; + vlen = strcspn((char*)++tmp, "\""); + } else vlen = strcspn((char*)tmp, " \t\r\n>"); + + save = tmp[vlen]; + tmp[vlen] = 0; + clean_val = html_decode_param(tmp, 0); + tmp[vlen] = save; + tmp += vlen + quote; + } + + /* CHECK X.X: Unquoted value can allow parameter XSS */ + if (!quote && clean_val && + !case_prefix(clean_val, "skipfish:")) + problem(PROB_TAG_XSS, req, res, tag_name, req->pivot, 0); + + if (!strcasecmp((char*)tag_name, "script") && + !strcasecmp((char*)param_name, "src")) remote_script = 1; + + /* CHECK 3.1: URL XSS and redirection issues. */ + + if ((!strcasecmp((char*)param_name, "href") || + !strcasecmp((char*)param_name, "src") || + !strcasecmp((char*)param_name, "action") || + !strcasecmp((char*)param_name, "cite") || + !strcasecmp((char*)param_name, "longdesc") || + /* pivot, 0); + + /* A bit hairy, but in essence, links to attacker-supplied + stylesheets or scripts are super-bad; OBJECTs and IFRAMEs + are sorta noteworthy, depending on context; and A links + are usually of little relevance. */ + + if (!case_prefix(clean_val, "http://skipfish.invalid/") || + !case_prefix(clean_val, "//skipfish.invalid/")) { + + if (!strcasecmp((char*)tag_name, "script") || + !strcasecmp((char*)tag_name, "link")) + problem(PROB_USER_URL_ACT, req, res, tag_name, req->pivot, 0); + else if (!strcasecmp((char*)tag_name, "a")) + problem(PROB_USER_LINK, req, res, tag_name, req->pivot, 0); + else + problem(PROB_USER_URL, req, res, tag_name, req->pivot, 0); + + } + + } + + /* CHECK 3.2: META REFRESH XSSes, redirection. Also extract + charset, if available */ + + if (!strcasecmp((char*)tag_name, "meta") && + !strcasecmp((char*)param_name, "content") && clean_val) { + u8* url = inl_strcasestr(clean_val, (u8*)"URL="); + u8 semi_safe = 0; + + if (url) { + url += 4; + if (*url == '\'' || *url == '"') { url++; semi_safe = 1; } + + if (!case_prefix(url, "http://skipfish.invalid/") || + !case_prefix(url, "//skipfish.invalid/") || + !case_prefix(url, "skipfish:")) + problem(PROB_URL_REDIR, req, res, (u8*)"injected URL in META refresh", + req->pivot, 0); + + /* Unescaped semicolon in Refresh headers is unsafe with + MSIE6: injecting an extra URL with javascript: scheme can + lead to an XSS */ + + if ((strstr((char*)url, "/invalid/;") || + inl_strcasestr(url, (u8*)"%2finvalid%2f;")) && + !semi_safe) + problem(PROB_URL_REDIR, req, res, (u8*)"unescaped semi-colon in META refresh (IE6)", + req->pivot, 0); + + } else { + u8* cset = inl_strcasestr(clean_val, (u8*)"charset="); + if (cset) { + if (res->meta_charset) { + if (strcasecmp((char*)cset+8, (char*)res->meta_charset)) + res->warn |= WARN_CFL_HDR; + } else res->meta_charset = ck_strdup(cset + 8); + } + } + + } + + /* CHECK 3.3: JavaScript on*=, CSS style= parameters. */ + + if ((!case_prefix(param_name, "on") || + !strcasecmp((char*)param_name, "style")) && clean_val) + check_js_xss(req, res, clean_val); + + /* CHECK 3.4: What looks like our sfi tags, not fully escaped. */ + + if ((sfi_pos = (u8*)strstr((char*)param_name, "sfi")) && + sscanf((char*)sfi_pos, "sfi%06uv%06u", &tag_id, &scan_id) == 2) { + + struct http_request* orig = get_xss_request(tag_id, scan_id); + + if (orig) + problem(PROB_BODY_XSS, orig, res, (u8*) + "injected 'sfi..' parameter value in a tag", + req->pivot, 0); + else + problem(PROB_BODY_XSS, req, res, (u8*) + "injected 'sfi...' parameter value in a tag (from previous" + " scans)", req->pivot, 0); + + } + + ck_free(clean_val); + ck_free(param_name); + + } + + /* CHECK 3.5: Phew. Parameters analyzed. Let's check for XSS tags... */ + + if (sscanf((char*)tag_name, "sfi%06uv%06u", &tag_id, &scan_id) == 2) { + struct http_request* orig = get_xss_request(tag_id, scan_id); + + if (orig) + problem(PROB_BODY_XSS, orig, res, (u8*) + "injected '' tag seen in HTML", req->pivot, 0); + else + problem(PROB_BODY_XSS, req, res, (u8*) + "injected '' tag seen in HTML (from previous scans)", + req->pivot, 0); + } + + /* CHECK 3.6: Non-remote SCRIPTs are of interest to JS XSS logic. */ + + if (!strcasecmp((char*)tag_name, "script") && !remote_script) { + + u8* next = inl_strcasestr(tmp, (u8*)""); + if (next) *next = 0; + check_js_xss(req, res, tmp); + if (next) *next = '<'; + /* Don't skip right away, as there might be some nested HTML inside. */ + } + + /* CHECK 3.7: ...and so are stylesheets. */ + + if (!strcasecmp((char*)tag_name, "style")) { + + u8* next = inl_strcasestr(tmp, (u8*)""); + if (next) *next = 0; + check_js_xss(req, res, tmp); + if (next) *next = '<'; + + } + + ck_free(tag_name); + + } else tmp = (u8*)strchr((char*)tmp, '<'); + + } while (tmp && *tmp); + + /* CHECK 4: Known exceptions / error pages, etc. */ + + detect_mime(req, res); + res->sniffed_mime = (u8*)mime_map[res->sniff_mime_id][0]; + + check_for_stuff(req, res); + +binary_checks: + + detect_mime(req, res); + res->sniffed_mime = (u8*)mime_map[res->sniff_mime_id][0]; + + /* No MIME checks on Content-Disposition: attachment responses. */ + + if ((tmp = GET_HDR((u8*)"Content-Disposition", &res->hdr)) && + inl_strcasestr(tmp, (u8*)"attachment")) return 0; + +// if (!relaxed_mime) { +// +// /* CHECK 5A: Renderable documents that are not CSS or static JS are of +// particular interest when it comes to MIME / charset mistakes. */ +// +// if (is_mostly_ascii(res) && !is_css(res) && (!is_javascript(res) || +// (!strstr((char*)res->payload, "function ") && +// !strstr((char*)res->payload, "function(")))) high_risk = 1; +// +// } else + + { + + /* CHECK 5B: Documents with skipfish signature strings echoed back + are of particular interest when it comes to MIME / charset mistakes. */ + + u8* tmp = (u8*)strstr((char*)res->payload, "sfi"); + + if ((tmp && isdigit(tmp[3]) && tmp[9] == 'v') || + strstr((char*)res->payload, "sfish") || + strstr((char*)res->payload, "skipfish")) high_risk = 1; + + } + + /* CHECK 6: MIME mismatch? Ignore cases where the response had a valid + MIME type declared in headers, but we failed to map it to a known + value... and also failed to sniff. + + Mismatch between MIME_ASC_HTML and MIME_XML_XHTML is not worth + complaining about; the same about JS or CSS responses being + sniffed as "unknown ASCII". */ + + if (res->sniff_mime_id != res->decl_mime_id && + !((res->decl_mime_id == MIME_ASC_JAVASCRIPT || + res->decl_mime_id == MIME_ASC_CSS) && + res->sniff_mime_id == MIME_ASC_GENERIC) && + !(res->decl_mime_id == MIME_ASC_HTML && + res->sniff_mime_id == MIME_XML_XHTML) && + !(res->decl_mime_id == MIME_XML_XHTML && + res->sniff_mime_id == MIME_ASC_HTML) && + !(res->header_mime && !res->decl_mime_id && + (res->sniff_mime_id == MIME_ASC_GENERIC || + res->sniff_mime_id == MIME_BIN_GENERIC))) + problem(high_risk ? PROB_BAD_MIME_DYN : PROB_BAD_MIME_STAT, + req, res, res->sniffed_mime, req->pivot, 0); + + /* CHECK 7: application/octet-stream or text/plain; both have + unintended consequences (but complain only if 3 didn't fire). */ + + else if (res->header_mime && (!strcasecmp((char*)res->header_mime, + "application/octet-stream") || !strcasecmp((char*)res->header_mime, + "text/plain"))) + problem(high_risk ? PROB_GEN_MIME_DYN : PROB_GEN_MIME_STAT, + req, res, res->sniffed_mime, req->pivot, 0); + + /* CHECK 8: Missing charset? */ + + if (is_mostly_ascii(res) && !res->meta_charset && !res->header_charset) + problem(high_risk ? PROB_BAD_CSET_DYN : PROB_BAD_CSET_STAT, + req, res, 0, req->pivot, 0); + + /* CHECK 9: Duplicate, inconsistent C-T or charset? */ + + if (is_mostly_ascii(res) && (res->warn & WARN_CFL_HDR || + (res->meta_charset && res->header_charset && + strcasecmp((char*)res->meta_charset, (char*)res->header_charset)))) + problem(high_risk ? PROB_CFL_HDRS_DYN : PROB_CFL_HDRS_STAT, + req, res, 0, req->pivot, 0); + + /* CHECK 10: Made up charset? */ + + if (res->header_charset || res->meta_charset) { + u32 i = 0; + + while (valid_charsets[i]) { + if (!strcasecmp((char*)valid_charsets[i], (char*)(res->header_charset ? + res->header_charset : res->meta_charset))) break; + i++; + } + + if (!valid_charsets[i]) + problem(high_risk ? PROB_BAD_CSET_DYN : PROB_BAD_CSET_STAT, + req, res, res->header_charset ? + res->header_charset : res->meta_charset, req->pivot, 0); + + } + + return 0; +} + + +/* Does MIME detection on a message. Most of this logic is reused from + ratproxy, with some improvements and additions. */ + +static void detect_mime(struct http_request* req, struct http_response* res) { + u8 sniffbuf[SNIFF_LEN]; + s32 fuzzy_match = -1; + + if (res->sniff_mime_id) return; + + /* First, classify declared response MIME, if any. */ + + if (res->header_mime) { + u32 i; + + for (i=0;iheader_mime, + strlen((char*)mime_map[i][j] + 1))) fuzzy_match = i; + } else { + if (!strcasecmp((char*)mime_map[i][j], (char*)res->header_mime)) + break; + } + j++; + } + + if (mime_map[i][j]) break; + + } + + if (i != MIME_COUNT) { + res->decl_mime_id = i; + } else if (fuzzy_match != -1) { + res->decl_mime_id = fuzzy_match; + } + } + + /* Next, work out the actual MIME that should be set. Mostly + self-explanatory. */ + + memcpy(sniffbuf, res->payload, + (res->pay_len > SNIFF_LEN - 1) ? (SNIFF_LEN - 1) : res->pay_len); + + sniffbuf[(res->pay_len > SNIFF_LEN - 1) ? (SNIFF_LEN - 1) : res->pay_len] = 0; + + if (is_mostly_ascii(res)) { + + /* ASCII checks. */ + + if (is_javascript(res)) { + res->sniff_mime_id = MIME_ASC_JAVASCRIPT; + return; + } + + if (is_css(res)) { + res->sniff_mime_id = MIME_ASC_CSS; + return; + } + + + if (!prefix(sniffbuf, "%!PS")) { + res->sniff_mime_id = MIME_ASC_POSTSCRIPT; + return; + } + + if (!prefix(sniffbuf, "{\\rtf")) { + res->sniff_mime_id = MIME_ASC_RTF; + return; + } + + /* Adobe PDF (may be mostly ASCII in some cases). */ + + if (!prefix(sniffbuf, "%PDF")) { + res->sniff_mime_id = MIME_EXT_PDF; + return; + } + + /* Several types of XML documents, taking into account that + they might be missing their xmlns=, etc: */ + + if (strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_OPENSEARCH; + return; + } + + if (strstr((char*)sniffbuf, "") || + strstr((char*)sniffbuf, "") || + strstr((char*)sniffbuf, "") || + strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_RSS; + return; + } + + if (strstr((char*)sniffbuf, "")) { + res->sniff_mime_id = MIME_XML_ATOM; + return; + } + + if (strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_WML; + return; + } + + if (strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_SVG; + return; + } + + if (strstr((char*)sniffbuf, "")) { + res->sniff_mime_id = MIME_XML_CROSSDOMAIN; + return; + } + + if (strstr((char*)sniffbuf, "sniff_mime_id = MIME_XML_XHTML; + else + res->sniff_mime_id = MIME_XML_GENERIC; + + return; + + } + + /* Do an unconvincing check for HTML once we ruled out + known XML cases. */ + + if (inl_strcasestr(sniffbuf, (u8*)"") || + inl_strcasestr(sniffbuf, (u8*)"href=")) { + res->sniff_mime_id = MIME_ASC_HTML; + return; + } + + /* OK, we're out of ideas. Let's do a last-resort check for XML again, + now that HTML is also off the table. */ + + if (strstr((char*)sniffbuf, "")) { + res->sniff_mime_id = MIME_XML_GENERIC; + return; + } + + res->sniff_mime_id = MIME_ASC_GENERIC; + + } else { + + /* Binary checks. Start with simple images (JPG, GIF, PNG, TIFF, BMP). */ + + if (sniffbuf[0] == 0xFF && sniffbuf[1] == 0xD8 && sniffbuf[2] == 0xFF) { + res->sniff_mime_id = MIME_IMG_JPEG; + return; + } + + if (!prefix(sniffbuf, "GIF8")) { + res->sniff_mime_id = MIME_IMG_GIF; + return; + } + + if (sniffbuf[0] == 0x89 && !prefix(sniffbuf + 1, "PNG")) { + res->sniff_mime_id = MIME_IMG_PNG; + return; + } + + if (!prefix(sniffbuf, "BM")) { + res->sniff_mime_id = MIME_IMG_BMP; + return; + } + + if (!prefix(sniffbuf, "II") && sniffbuf[2] == 42 /* dec */) { + res->sniff_mime_id = MIME_IMG_TIFF; + return; + } + + /* Next: RIFF containers (AVI, ANI, WAV). */ + + if (!prefix(sniffbuf, "RIFF")) { + + if (sniffbuf[8] == 'A') { + if (sniffbuf[9] == 'C') + res->sniff_mime_id = MIME_IMG_ANI; + else + res->sniff_mime_id = MIME_AV_AVI; + } else res->sniff_mime_id = MIME_AV_WAV; + + return; + + } + + /* Cursor / ICO drama (we roll it back into BMP, because few sites + make the distinction anyway, and cursors are unlikely to be + attacker-supplied)... */ + + if (res->pay_len > 3 && !sniffbuf[0] && !sniffbuf[1] && + sniffbuf[2] && !sniffbuf[3]) { + res->sniff_mime_id = MIME_IMG_BMP; + return; + } + + /* Windows Media container (WMV, WMA, ASF). */ + + if (sniffbuf[0] == 0x30 && sniffbuf[1] == 0x26 && sniffbuf[2] == 0xB2) { + res->sniff_mime_id = MIME_AV_WMEDIA; + return; + } + + /* MPEG formats, Ogg Vorbis, QuickTime, RealAudio, RealVideo. */ + + if (sniffbuf[0] == 0xFF && sniffbuf[1] == 0xFB) { + res->sniff_mime_id = MIME_AV_MP3; + return; + } + + if (sniffbuf[0] == 0x00 && sniffbuf[1] == 0x00 && sniffbuf[2] == 0x01 && + (sniffbuf[3] >> 4) == 0x0B) { + res->sniff_mime_id = MIME_AV_MPEG; + return; + } + + if (!prefix(sniffbuf, "OggS")) { + res->sniff_mime_id = MIME_AV_OGG; + return; + } + + if (sniffbuf[0] == 0x28 && !prefix(sniffbuf + 1, "RMF")) { + res->sniff_mime_id = MIME_AV_RA; + return; + } + + if (sniffbuf[0] == 0x2E && !prefix(sniffbuf + 1, "RMF")) { + res->sniff_mime_id = MIME_AV_RV; + return; + } + + if (!prefix(sniffbuf + 4, "free") || + !prefix(sniffbuf + 4, "mdat") || + !prefix(sniffbuf + 4, "wide") || + !prefix(sniffbuf + 4, "pnot") || + !prefix(sniffbuf + 4, "skip") || + !prefix(sniffbuf + 4, "moov")) { + /* Oookay, that was weird... */ + res->sniff_mime_id = MIME_AV_QT; + return; + } + + /* Flash and FLV. */ + + if (!prefix(sniffbuf, "FLV")) { + res->sniff_mime_id = MIME_AV_FLV; + return; + } + + if (!prefix(sniffbuf, "FCWS") || + !prefix(sniffbuf, "CWS")) { + res->sniff_mime_id = MIME_EXT_FLASH; + return; + } + + /* Adobe PDF. */ + + if (!prefix(sniffbuf, "%PDF")) { + res->sniff_mime_id = MIME_EXT_PDF; + return; + } + + /* JAR versus ZIP. A bit tricky, because, well, they are both just + ZIP archives. */ + + if (!prefix(sniffbuf, "PK") && + sniffbuf[2] < 6 && sniffbuf[3] < 7) { + + if (inl_memmem(res->payload, res->pay_len, "META-INF/", 9)) + res->sniff_mime_id = MIME_EXT_JAR; + else + res->sniff_mime_id = MIME_BIN_ZIP; + return; + + } + + /* Java class files. */ + + if (sniffbuf[0] == 0xCA && sniffbuf[1] == 0xFE && sniffbuf[2] == 0xBA && + sniffbuf[3] == 0xBE) { + res->sniff_mime_id = MIME_EXT_CLASS; + return; + } + + /* The joy of Microsoft Office containers. */ + + if (res->pay_len > 512 && sniffbuf[0] == 0xD0 && sniffbuf[1] == 0xCF && + sniffbuf[2] == 0x11 && sniffbuf[3] == 0xE0) { + + switch (sniffbuf[512]) { + case 0xEC: res->sniff_mime_id = MIME_EXT_WORD; return; + case 0xFD: + case 0x09: res->sniff_mime_id = MIME_EXT_EXCEL; return; + case 0x00: + case 0x0F: + case 0xA0: res->sniff_mime_id = MIME_EXT_PPNT; return; + } + + } + + /* GZIP. Unfortunately, tar has no discernible header to speak of, + so we just let it slide - few sites are serving tars on purpose + anyway. */ + + if (sniffbuf[0] == 0x1F && sniffbuf[1] == 0x8B && sniffbuf[2] == 0x08) { + res->sniff_mime_id = MIME_BIN_GZIP; + return; + } + + /* CAB. */ + + if (sniffbuf[0] == 'M' && sniffbuf[1] == 'S' && sniffbuf[2] == 'C' && + sniffbuf[3] == 'F' && !sniffbuf[4]) { + res->sniff_mime_id = MIME_BIN_CAB; + return; + } + + res->sniff_mime_id = MIME_BIN_GENERIC; + + } + + /* No more ideas? */ + +} + + +/* "Stuff" means various error messages and cool, unusual content. For large + files, this function may be sort-of expensive, but we need to look through + the entire response, as not all messages are full-page errors, etc. */ + +static void check_for_stuff(struct http_request* req, + struct http_response* res) { + + u8 sniffbuf[SNIFF_LEN]; + u8* tmp; + + if (!res->pay_len || !is_mostly_ascii(res) || res->stuff_checked) return; + + /* We will use sniffbuf for checks that do not need to look through the + entire file. */ + + memcpy(sniffbuf, res->payload, + (res->pay_len > SNIFF_LEN - 1) ? (SNIFF_LEN - 1) : res->pay_len); + + sniffbuf[(res->pay_len > SNIFF_LEN - 1) ? (SNIFF_LEN - 1) : res->pay_len] = 0; + + res->stuff_checked = 1; + + /* Assorted interesting error messages. */ + + if (((tmp = (u8*)strstr((char*)res->payload, "ORA-")) || + (tmp = (u8*)strstr((char*)res->payload, "FRM-"))) && + isdigit(tmp[4]) && tmp[9] == ':') { + problem(PROB_ERROR_POI, req, res, (u8*)"Oracle server error", req->pivot, 0); + return; + } + + + if (inl_strcasestr(sniffbuf, (u8*)"\nAuthType ") || + (inl_strcasestr(sniffbuf, (u8*)"\nOptions ") && ( + inl_strcasestr(sniffbuf, (u8*)"\nOptions +") || + inl_strcasestr(sniffbuf, (u8*)"\nOptions -") || + inl_strcasestr(sniffbuf, (u8*)"\nOptions All") || + inl_strcasestr(sniffbuf, (u8*)"\nOptions Exec") || + inl_strcasestr(sniffbuf, (u8*)"\nOptions Follow") || + inl_strcasestr(sniffbuf, (u8*)"\nOptions In") || + inl_strcasestr(sniffbuf, (u8*)"\nOptions Mult") || + inl_strcasestr(sniffbuf, (u8*)"\nOptions Sym")) + ) || + inl_strcasestr(sniffbuf, (u8*)"\npivot, 0); + return; + } + + if (res->sniff_mime_id == MIME_ASC_GENERIC) { + u8* x = sniffbuf; + + /* Generic something:something[:...] password syntax. */ + + while (*x && (isalnum(*x) || strchr("._-+$", *x)) && + (x - sniffbuf) < 64) x++; + + if (x != sniffbuf && *x == ':' && x[1] != '/' && x[1] != '.') { + u8* start_x = ++x; + + while (*x && (isalnum(*x) || strchr("./*!+=$", *x)) && + (x - sniffbuf) < 128) x++; + + if (*x == ':' || ((start_x != x) && (!*x || *x == '\r' || *x == '\n'))) + problem(PROB_FILE_POI, req, res, (u8*) + "Possible password file", req->pivot, 0); + + } + } + + /* Add more directory signatures here... */ + + if (strstr((char*)sniffbuf, "") || + strstr((char*)sniffbuf, "") || + strstr((char*)sniffbuf, "

    Index of /") || + strstr((char*)sniffbuf, ">[To Parent Directory]<")) { + problem(PROB_DIR_LIST, req, res, (u8*)"Directory listing", req->pivot, 0); + + /* Since we have the listing, we'll skip bruteforcing directory */ + req->pivot->no_fuzz = 3; + return; + } + + if (res->sniff_mime_id == MIME_ASC_GENERIC) { + u8* x = sniffbuf; + u32 slashes = 0; + + /* Five slashes in the first line in a plaintext file should be a + reasonably good check for CVS. */ + + while (*x && *x != '\n' && + (x - sniffbuf) < 256) { + if (*x == '/') slashes++; + x++; + } + + if (slashes == 5) { + problem(PROB_FILE_POI, req, res, (u8*)"CVS RCS data", req->pivot, 0); + return; + } + + } + + if (strstr((char*)res->payload, "End Sub\n") || + strstr((char*)res->payload, "End Sub\r")) { + problem(PROB_FILE_POI, req, res, (u8*)"Visual Basic source", req->pivot, 0); + return; + } + + + /* Plain text, and every line contains ;, comma, or |? */ + + if (res->sniff_mime_id == MIME_ASC_GENERIC) { + u8* cur = res->payload; + u8 all_delim = 0; + u8* eol; + + do { + u32 del = strcspn((char*)cur, ",|;\n"); + + eol = (u8*)strchr((char*)cur, '\n'); + if(!eol) break; + + if (!cur[del] || cur[del] == '\n' || (cur[del] == ',' && + cur[del+1] == ' ')) { + all_delim = 0; + break; + } + + all_delim = 1; + cur = eol + 1; + + } while (eol && cur && *cur); + + if (all_delim) { + problem(PROB_FILE_POI, req, res, + (u8*)"Delimited database dump", req->pivot, 0); + return; + } + + } + + /* This is a bit dodgy, but the most prominent sign of non-browser JS on + Windows is the instantiation of obscure ActiveX objects to access local + filesystem, create documents, etc. Unfortunately, some sites may also be + creating obscure ActiveX objects; these would likely need to be just + blacklisted here. */ + + if (is_javascript(res) && strstr((char*)res->payload, "new ActiveXObject(") && + !strstr((char*)res->payload, "XMLHTTP") && + !strstr((char*)res->payload, "ShockwaveFlash")) { + problem(PROB_FILE_POI, req, res, (u8*)"server-side JavaScript source", + req->pivot, 0); + return; + } + +} + +/* This is called when pivot enters PSTATE_DONE. It deletes payload of + binary responses if requested and can flush pivot payloads to disk. */ + +void maybe_delete_payload(struct pivot_desc* pv) { + u8 tmp[64]; + u32 i; + + /* Return if there is nothing we should do */ + if (!delete_bin && !flush_pivot_data) + return; + + /* Delete binary payload when desired */ + if (delete_bin && pv->res && pv->res->pay_len > 256 && + !is_mostly_ascii(pv->res)) { + + ck_free(pv->res->payload); + sprintf((char*)tmp, "[Deleted binary payload (%u bytes)]", pv->res->pay_len); + pv->res->payload = ck_strdup(tmp); + pv->res->pay_len = strlen((char*)tmp); + } + + /* Flush issue req/res payloads */ + if (flush_pivot_data) + flush_payload(pv->req, pv->res); + + for (i=0;iissue_cnt;i++) { + + if (delete_bin && pv->issue[i].res && pv->issue[i].res->pay_len > 256 && + !is_mostly_ascii(pv->issue[i].res)) { + ck_free(pv->issue[i].res->payload); + sprintf((char*)tmp, "[Deleted binary payload (%u bytes)]", + pv->issue[i].res->pay_len); + pv->issue[i].res->payload = ck_strdup(tmp); + pv->issue[i].res->pay_len = strlen((char*)tmp); + } + + /* Flush pivot req/res payloads */ + if (flush_pivot_data) + flush_payload(pv->issue[i].req, pv->issue[i].res); + + } +} diff -Nru skipfish-2.02b/src/analysis.h skipfish-2.10b/src/analysis.h --- skipfish-2.02b/src/analysis.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/analysis.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,235 @@ +/* + skipfish - content analysis + --------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#ifndef _HAVE_ANALYSIS_C + +#include "types.h" +#include "http_client.h" +#include "database.h" + +extern u8 no_parse, /* Disable HTML link detection */ + warn_mixed, /* Warn on mixed content */ + log_ext_urls, /* Log all external URLs */ + no_forms, /* Do not submit forms */ + pedantic_cache; /* Match HTTP/1.0 and HTTP/1.1 */ + +/* Helper macros to group various useful checks: */ + +#define PIVOT_CHECKS(_req, _res) do { \ + pivot_header_checks(_req, _res); \ + content_checks(_req, _res); \ + scrape_response(_req, _res); \ + } while (0) + + +#define RESP_CHECKS(_req, _res) do { \ + content_checks(_req, _res); \ + scrape_response(_req, _res); \ + } while (0) + +/* Form autofill hints: */ + +extern u8** addl_form_name; +extern u8** addl_form_value; +extern u32 addl_form_cnt; + + +/* Runs some rudimentary checks on top-level pivot HTTP responses. */ + +void pivot_header_checks(struct http_request* req, + struct http_response* res); + +/* Adds a new item to the form hint system. */ + +void add_form_hint(u8* name, u8* value); + +/* Analyzes response headers (Location, etc), body to extract new links, + keyword guesses, examine forms, mixed content issues, etc. */ + +void scrape_response(struct http_request* req, struct http_response* res); + +/* Analyzes response headers and body to detect stored XSS, redirection, + 401, 500 codes, exception messages, source code, offensive comments, etc. */ + +u8 content_checks(struct http_request* req, struct http_response* res); + +/* Deletes payload of binary responses if requested. */ + +void maybe_delete_payload(struct pivot_desc* pv); + + + +/* Examines all tags up until , then adds them as + parameters to current request. */ + +void collect_form_data(struct http_request* req, + struct http_request* orig_req, + struct http_response* orig_res, + u8* cur_str, u8 is_post); + + +/* Create a http_request from an HTML form structure */ + +struct http_request* make_form_req(struct http_request *req, + struct http_request *base, + struct http_response* res, + u8* cur_str, u8* target); + +/* MIME detector output codes: */ + +#define MIME_NONE 0 /* Checks missing or failed */ + +#define MIME_ASC_GENERIC 1 /* Unknown, but mostly 7bit */ +#define MIME_ASC_HTML 2 /* Plain, non-XML HTML */ +#define MIME_ASC_JAVASCRIPT 3 /* JavaScript or JSON */ +#define MIME_ASC_CSS 4 /* Cascading Style Sheets */ +#define MIME_ASC_POSTSCRIPT 5 /* PostScript */ +#define MIME_ASC_RTF 6 /* Rich Text Format */ + +#define MIME_XML_GENERIC 7 /* XML not recognized otherwise */ +#define MIME_XML_OPENSEARCH 8 /* OpenSearch specification */ +#define MIME_XML_RSS 9 /* Real Simple Syndication */ +#define MIME_XML_ATOM 10 /* Atom feeds */ +#define MIME_XML_WML 11 /* WAP WML */ +#define MIME_XML_CROSSDOMAIN 12 /* crossdomain.xml (Flash) */ +#define MIME_XML_SVG 13 /* Scalable Vector Graphics */ +#define MIME_XML_XHTML 14 /* XML-based XHTML */ + +#define MIME_IMG_JPEG 15 /* JPEG */ +#define MIME_IMG_GIF 16 /* GIF */ +#define MIME_IMG_PNG 17 /* PNG */ +#define MIME_IMG_BMP 18 /* Windows BMP (including ICO) */ +#define MIME_IMG_TIFF 19 /* TIFF */ +#define MIME_IMG_ANI 20 /* RIFF: ANI animated cursor */ + +#define MIME_AV_WAV 21 /* RIFF: WAV sound file */ +#define MIME_AV_MP3 22 /* MPEG audio (commonly MP3) */ +#define MIME_AV_OGG 23 /* Ogg Vorbis */ +#define MIME_AV_RA 24 /* Real audio */ + +#define MIME_AV_AVI 25 /* RIFF: AVI container */ +#define MIME_AV_MPEG 26 /* MPEG video */ +#define MIME_AV_QT 27 /* QuickTime */ +#define MIME_AV_FLV 28 /* Flash video */ +#define MIME_AV_RV 29 /* Real video */ + +#define MIME_AV_WMEDIA 30 /* Windows Media audio */ + +#define MIME_EXT_FLASH 31 /* Adobe Flash */ +#define MIME_EXT_PDF 32 /* Adobe PDF */ +#define MIME_EXT_JAR 33 /* Sun Java archive */ +#define MIME_EXT_CLASS 34 /* Sun Java class */ +#define MIME_EXT_WORD 35 /* Microsoft Word */ +#define MIME_EXT_EXCEL 36 /* Microsoft Excel */ +#define MIME_EXT_PPNT 37 /* Microsoft Powerpoint */ + +#define MIME_BIN_ZIP 38 /* ZIP not recognized otherwise */ +#define MIME_BIN_GZIP 39 /* GZIP */ +#define MIME_BIN_CAB 40 /* CAB */ + +#define MIME_BIN_GENERIC 41 /* Binary, unknown type */ + +#define MIME_COUNT (MIME_BIN_GENERIC + 1) + +/* NULL-terminated MIME mapping sets. Canonical name should go first; do not + put misspelled or made up entries here. This is used to match server intent + with the outcome of MIME sniffing. */ + +#ifdef _VIA_ANALYSIS_C + +static char* mime_map[MIME_COUNT][8] = { + +/* MIME_NONE */ { 0 }, + +/* MIME_ASC_GENERIC */ { "text/plain", "?text/x-", "?text/vnd.", + "?application/x-httpd-", "text/csv", 0 }, +/* MIME_ASC_HTML */ { "text/html", 0 }, +/* MIME_ASC_JAVASCRIPT */ { "application/javascript", + "application/x-javascript", + "application/json", "text/javascript", 0 }, +/* MIME_ASC_CSS */ { "text/css", 0 }, +/* MIME_ASC_POSTSCRIPT */ { "application/postscript", 0 }, +/* MIME_ASC_RTF */ { "text/rtf", "application/rtf", 0 }, + +/* MIME_XML_GENERIC */ { "text/xml", "application/xml", 0 }, +/* MIME_XML_OPENSEARCH */ { "application/opensearchdescription+xml", 0 }, +/* MIME_XML_RSS */ { "application/rss+xml", 0 }, +/* MIME_XML_ATOM */ { "application/atom+xml", 0 }, +/* MIME_XML_WML */ { "text/vnd.wap.wml", 0 }, +/* MIME_XML_CROSSDOMAIN */ { "text/x-cross-domain-policy", 0 }, +/* MIME_XML_SVG */ { "image/svg+xml", 0 }, +/* MIME_XML_XHTML */ { "application/xhtml+xml", 0 }, + +/* MIME_IMG_JPEG */ { "image/jpeg", 0 }, +/* MIME_IMG_GIF */ { "image/gif", 0 }, +/* MIME_IMG_PNG */ { "image/png", 0 }, +/* MIME_IMG_BMP */ { "image/x-ms-bmp", "image/bmp", "image/x-icon", 0 }, +/* MIME_IMG_TIFF */ { "image/tiff", 0 }, +/* MIME_IMG_ANI */ { "application/x-navi-animation", 0 }, + +/* MIME_AV_WAV */ { "audio/x-wav", "audio/wav", 0 }, +/* MIME_AV_MP3 */ { "audio/mpeg", 0 }, +/* MIME_AV_OGG */ { "application/ogg", 0 }, +/* MIME_AV_RA */ { "audio/vnd.rn-realaudio", + "audio/x-pn-realaudio", "audio/x-realaudio", 0 }, + +/* MIME_AV_AVI */ { "video/avi", 0 }, +/* MIME_AV_MPEG */ { "video/mpeg", "video/mp4", 0 }, +/* MIME_AV_QT */ { "video/quicktime", 0 }, +/* MIME_AV_FLV */ { "video/flv", "video/x-flv", 0 }, +/* MIME_AV_RV */ { "video/vnd.rn-realvideo", 0 }, + +/* MIME_AV_WMEDIA */ { "video/x-ms-wmv", "audio/x-ms-wma", + "video/x-ms-asf", 0 }, + +/* MIME_EXT_FLASH */ { "application/x-shockwave-flash", 0 }, +/* MIME_EXT_PDF */ { "application/pdf", 0 }, +/* MIME_EXT_JAR */ { "application/java-archive", 0 }, +/* MIME_EXT_CLASS */ { "application/java-vm", 0 }, +/* MIME_EXT_WORD */ { "application/msword", 0 }, +/* MIME_EXT_EXCEL */ { "application/vnd.ms-excel", 0 }, +/* MIME_EXT_PPNT */ { "application/vnd.ms-powerpoint", 0 }, + +/* MIME_BIN_ZIP */ { "application/zip", "application/x-zip-compressed", 0 }, +/* MIME_BIN_GZIP */ { "application/x-gzip", "application/x-gunzip", + "application/x-tar-gz", 0 }, +/* MIME_BIN_CAB */ { "application/vnd.ms-cab-compressed", 0 }, + +/* MIME_BIN_GENERIC */ { "application/binary", "application/octet-stream", + 0 } + +}; + +/* A set of headers that we check to see if our injection string ended + up in their value. This list should only contain headers where control + over the value could potentially be exploited. */ + +static const char* injection_headers[] = { + "Set-Cookie", + "Set-Cookie2", + "Content-Type", + 0, +}; + +#endif /* _VIA_ANALYSIS_C */ + +#endif /* !_HAVE_ANALYSIS_H */ diff -Nru skipfish-2.02b/src/auth.c skipfish-2.10b/src/auth.c --- skipfish-2.02b/src/auth.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/auth.c 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,294 @@ +/* + skipfish - form authentication + ------------------------------ + + Author: Niels Heinen + + Copyright 2012 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#include + +#define _VIA_AUTH_C + +#include "debug.h" +#include "config.h" +#include "types.h" +#include "http_client.h" +#include "database.h" +#include "crawler.h" +#include "analysis.h" +#include "auth.h" + +u8 *auth_form; /* Auth form location */ +u8 *auth_form_target; /* Auth form submit target */ +u8 *auth_user; /* User name */ +u8 *auth_user_field; /* Username field id */ +u8 *auth_pass; /* Password */ +u8 *auth_pass_field; /* Password input field id */ +u8 *auth_verify_url; /* Auth verify URL */ +u8 auth_state; /* Stores the auth state */ + +void authenticate() { + + struct http_request* req; + + DEBUGC(L1, "*- Authentication starts\n"); + + if (!auth_form || !auth_user || !auth_pass) + return; + + struct pivot_desc *fake = ck_alloc(sizeof(struct pivot_desc)); + fake->type = PIVOT_FILE; + fake->state = PSTATE_FETCH; + + /* When in session, do nothing */ + if (auth_state != ASTATE_NONE) + return; + + auth_state = ASTATE_START; + req = ck_alloc(sizeof(struct http_request)); + + /* Create a request struct. Note that in this case, we don't care about + * whether the URL is whitelisted or not */ + if (parse_url(auth_form, req, NULL)) + FATAL("Auth form URL could not be parsed\n"); + + req->pivot = fake; + req->callback = submit_auth_form; + + async_request(req); + +} + +/* Helper function to find form fields to put the credentials in */ + +static u8 find_and_set_field(struct param_array *par, const char **test_fields, + u32 par_type, u8* par_value) { + + u32 i, k; + + /* Try to find the field */ + for (i=0; ic; i++) { + if (!par->n[i] || par->t[i] != par_type) continue; + + /* Match it with the strings */ + for (k=0; test_fields[k]; k++) { + if (inl_strcasestr(par->n[i], (u8*)test_fields[k])) { + DEBUGC(L1, "*-- Authentication - found login field: %s\n", par->n[i]); + if (par->v[i]) ck_free(par->v[i]); + par->v[i] = ck_strdup(par_value); + return 1; + } + } + } + + /* None found..*/ + return 0; +} + + +/* Main function to submit the authentication, login form. This function + will try find the right form and , unless form fields are specified on + command-line, try to find the right fields in order to store the username + and password. */ + +u8 submit_auth_form(struct http_request* req, + struct http_response* res) { + + u8 *form, *form_ptr; + u8 *vurl = NULL; + u8 is_post = 1; + u8 par_type = PARAM_POST; + struct http_request* n = NULL; + + DEBUG_CALLBACK(req, res); + + /* Loop over the forms till we get our password form */ + form_ptr = res->payload; + + do { + + form = inl_strcasestr(form_ptr, (u8*)"method && !strcmp((char*)n->method, "POST")); + par_type = is_post ? PARAM_POST : PARAM_QUERY; + + n->pivot = req->pivot; + collect_form_data(n, req, res, form, is_post); + + /* If the form field was specified per command-line, we'll check if + it's present. When it's not present: move on to next form. + + Now when no form field was specified via command-line: try + to find one by using the strings from the "user_fields" array + (defined in auth.h). + */ + if (auth_user_field) { + if(!get_value(par_type, auth_user_field, 0, &n->par)) + continue; + + set_value(par_type, auth_user_field, ck_strdup(auth_user), 0, &n->par); + DEBUGC(L1, "*-- Authentication - auth_user field set (%s)\n", + auth_user_field); + } else if (!find_and_set_field(&n->par, user_fields, par_type, auth_user)) { + continue; + } + + if (auth_pass_field) { + if(!get_value(par_type, auth_pass_field, 0, &n->par)) + continue; + + set_value(par_type, auth_pass_field, ck_strdup(auth_pass), 0, &n->par); + DEBUGC(L1, "*-- Authentication - auth_pass field set (%s)\n", + auth_pass_field); + } else if (!find_and_set_field(&n->par, pass_fields, par_type, auth_pass)) { + continue; + } + + /* If we get here: credentials are set */ + n->callback = auth_form_callback; + DEBUGC(L1, "*-- Submitting authentication form\n"); +#ifdef LOG_STDERR + dump_http_request(n); +#endif + async_request(n); + auth_state = ASTATE_SEND; + break; + + } while (form); + + if (auth_state != ASTATE_SEND) + DEBUGC(L1, "*-- Could not login. Please check the URL and form fields\n"); + + return 0; +} + +/* After submitting the form and receiving a response, this is called */ + +u8 auth_form_callback(struct http_request* req, + struct http_response* res) { + + DEBUG_CALLBACK(req, res); + DEBUGC(L1, "*-- Received form response\n"); + + /* Parse the payload which will make sure cookies are stored. */ + content_checks(req, res); + + /* Compare an authenticated and anonymous request to the verification URL. The + * response should be different in order to determine that we are indeed + * authenticated */ + + if (!auth_verify_url) { + auth_state = ASTATE_DONE; + return 0; + } + + auth_state = ASTATE_VERIFY; + auth_verify_tests(req->pivot); + + return 0; +} + +/* Sends two requests to the verification URL. The first request is + authenticated (or should be) while the second request is anonymous */ + +u8 auth_verify_tests(struct pivot_desc* pivot) { + + /* When we have no verification URL or the scan is no authenticated: + return */ + + DEBUG("In auth verify\n"); + + if (!auth_verify_url || (auth_state != ASTATE_DONE && + auth_state != ASTATE_VERIFY)) + return 1; + + u8* vurl = ck_strdup(auth_verify_url); + struct http_request *n = ck_alloc(sizeof(struct http_request)); + + n->pivot = pivot; + if (parse_url(vurl, n, NULL)) + FATAL("Unable to parse verification URL: %s\n", vurl); + + /* One: authenticated request */ + n->callback = auth_verify_checks; + n->user_val = 0; + async_request(n); + + /* Two: anonymous request */ + n = req_copy(n, pivot, 1); + n->no_cookies = 1; + n->user_val = 1; + n->callback = auth_verify_checks; + async_request(n); + + return 0; +} + +/* Receives two requests to the verification URL. If there is a difference, than + we'll trust that it's because one request was authenticated while the other + wasn't */ + +u8 auth_verify_checks(struct http_request* req, struct http_response* res) { + DEBUG_CALLBACK(req, res); + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during auth verification tests", 0); + return 0; + } + + req->pivot->misc_req[req->user_val] = req; + req->pivot->misc_res[req->user_val] = res; + + /* We need two responses */ + if ((++req->pivot->misc_cnt) != 2) return 1; + + /* Compare the two response. The authenticates response should be + different to the anonymous request */ + + if (same_page(&MRES(0)->sig, &MRES(1)->sig)) { + DEBUGC(L1, "*- Unable to verify authentication using provided URL.\n"); + dump_signature(&MRES(0)->sig); + dump_signature(&MRES(1)->sig); + auth_state = ASTATE_FAIL; + } + + + destroy_misc_data(req->pivot, req); + + /* Re-authenticate upon failure */ + if (auth_state == ASTATE_FAIL) { + authenticate(); + DEBUG("* Going to re-authenticate\n"); + } else { + auth_state = ASTATE_DONE; + DEBUGC(L1, "*- Authenticated\n"); + } + + return 0; +} diff -Nru skipfish-2.02b/src/auth.h skipfish-2.10b/src/auth.h --- skipfish-2.02b/src/auth.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/auth.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,74 @@ +/* + skipfish - form authentication matching + ---------------------------------------- + + Author: Niels Heinen + + Copyright 2012 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#ifndef _HAVE_AUTH_H + +void authenticate(); + +u8 submit_auth_form(struct http_request* req, + struct http_response* res); + +u8 auth_form_callback(struct http_request* req, + struct http_response* res); + +u8 auth_verify_tests(struct pivot_desc* pivot); +u8 auth_verify_checks(struct http_request* req, struct http_response* res); + +extern u8 *auth_form, /* Auth form location */ + *auth_form_target, /* Auth form submit target */ + *auth_user, /* User name */ + *auth_pass, /* Password */ + *auth_user_field, /* Username field id */ + *auth_pass_field, /* Password input field id */ + *auth_verify_url; /* Auth verify URL */ + +extern u8 auth_state; + +#define ASTATE_NONE 0 +#define ASTATE_START 1 +#define ASTATE_SEND 2 +#define ASTATE_VERIFY 3 +#define ASTATE_DONE 4 +#define ASTATE_FAIL 5 + +#ifdef _VIA_AUTH_C + +/* These strings are used to find the username field */ + +static const char* user_fields[] = { + "user", + "name", + "email", + 0 +}; + +/* These strings are used to find the password field */ + +static const char* pass_fields[] = { + "pass", + "secret", + "pin", + 0 +}; + +#endif /* !_VIA_AUTH_C */ +#endif /* !_HAVE_AUTH_H */ diff -Nru skipfish-2.02b/src/checks.c skipfish-2.10b/src/checks.c --- skipfish-2.02b/src/checks.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/checks.c 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,2195 @@ +/* + skipfish - injection tests + --------------------------- + + Author: Niels Heinen , + Michal Zalewski + + Copyright 2009 - 2012 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + + +#define _VIA_CHECKS_C + +#include "crawler.h" +#include "analysis.h" +#include "http_client.h" +#include "checks.h" +#include "auth.h" + + + +static u8 inject_prologue_tests(struct pivot_desc* pivot); +static u8 inject_prologue_check(struct http_request*, struct http_response*); + +static u8 dir_ips_tests(struct pivot_desc* pivot); +static u8 dir_ips_check(struct http_request*, struct http_response*); + +static u8 inject_xml_tests(struct pivot_desc* pivot); +static u8 inject_xml_check(struct http_request*, struct http_response*); + +static u8 inject_xss_tests(struct pivot_desc* pivot); +static u8 inject_xss_check(struct http_request*, struct http_response*); + +static u8 inject_shell_tests(struct pivot_desc* pivot); +static u8 inject_shell_check(struct http_request*, struct http_response*); + +static u8 inject_diff_shell_tests(struct pivot_desc* pivot); +static u8 inject_diff_shell_check(struct http_request*, struct http_response*); + +static u8 inject_dir_listing_tests(struct pivot_desc* pivot); +static u8 inject_dir_listing_check(struct http_request*, struct http_response*); + +static u8 inject_lfi_tests(struct pivot_desc* pivot); +static u8 inject_lfi_check(struct http_request*, struct http_response*); + +static u8 inject_rfi_tests(struct pivot_desc* pivot); +static u8 inject_rfi_check(struct http_request*, struct http_response*); + +static u8 inject_split_tests(struct pivot_desc* pivot); +static u8 inject_split_check(struct http_request*, struct http_response*); + +static u8 inject_redir_tests(struct pivot_desc* pivot); +static u8 inject_redir_check(struct http_request*, struct http_response*); + +static u8 inject_sql_tests(struct pivot_desc* pivot); +static u8 inject_sql_check(struct http_request*, struct http_response*); + +static u8 inject_format_tests(struct pivot_desc* pivot); +static u8 inject_format_check(struct http_request*, struct http_response*); + +static u8 inject_integer_tests(struct pivot_desc* pivot); +static u8 inject_integer_check(struct http_request*, struct http_response*); + +static u8 put_upload_tests(struct pivot_desc* pivot); +static u8 put_upload_check(struct http_request*, struct http_response*); + +static u8 inject_behavior_tests(struct pivot_desc* pivot); +static u8 inject_behavior_check(struct http_request*, struct http_response*); + +static u8 param_behavior_tests(struct pivot_desc* pivot); +static u8 param_behavior_check(struct http_request*, struct http_response*); + +static u8 agent_behavior_tests(struct pivot_desc* pivot); +static u8 agent_behavior_check(struct http_request*, struct http_response*); + +static u8 param_ognl_tests(struct pivot_desc* pivot); +static u8 param_ognl_check(struct http_request*, struct http_response*); + +static u8 xssi_tests(struct pivot_desc* pivot); +static u8 xssi_check(struct http_request*, struct http_response*); + + + +/* The crawl structure defines the tests by combining function pointers and + flags. The values given indicate the following: + + 1- Amount of responses expected + 2- Whether to keep requests and responses before calling the check + 3- Whether the check accepted pivots with res_varies set + 4- Whether the check is time sensitive + 5- Whether we should scrape the response for links. + 6- The type of PIVOT that the test/check accepts + 7- Pointer to the function that scheduled the test(s) requests + 8- Pointer to the function that checks the result + 9- Whether to skip this test + + At the end, inject_done() is called: + - we move on with additional tests (e.g. parameter) + - or continue with the next pivot + + Point 8 allows command-line flags to toggle enabled/disabled + tests. For example, shell injection tests are not so relevant on + Windows environments so this allow them to be disabled. + +*/ + +u32 cb_handle_cnt = 21; /* Total of checks */ +u32 cb_handle_off = 4; /* Checks after the offset are optional */ + +static struct cb_handle cb_handles[] = { + /* Authentication check */ + { 2, 0, 0, 0, 0, 0, + CHK_SESSION, (u8*)"session check", + auth_verify_tests, auth_verify_checks, 0 }, + + /* Behavior checks for dirs/params */ + { BH_CHECKS, 1, 0, 0, 2, PIVOT_PARAM, + CHK_BEHAVE, (u8*)"param behavior", + param_behavior_tests, param_behavior_check, 0 }, + + { 2, 1, 0, 0, 2, PIVOT_PARAM, + CHK_OGNL, (u8*)"param OGNL", + param_ognl_tests, param_ognl_check, 0 }, + + { BH_CHECKS, 1, 0, 0, 2, PIVOT_DIR|PIVOT_FILE, + CHK_BEHAVE, (u8*)"inject behavior", + inject_behavior_tests, inject_behavior_check, 0 }, + + /* All the injection tests */ + { 2, 1, 0, 0, 2, PIVOT_DIR, + CHK_IPS, (u8*)"IPS check", + dir_ips_tests, dir_ips_check, 0 }, + + { 3, 1, 0, 0, 0, PIVOT_DIR|PIVOT_FILE, + CHK_AGENT, (u8*)"User agent behavior", + agent_behavior_tests, agent_behavior_check, 0 }, + + { 2, 1, 0, 0, 0, PIVOT_DIR|PIVOT_SERV, + CHK_PUT, (u8*)"PUT upload", + put_upload_tests, put_upload_check, 0 }, + + { 4, 1, 0, 0, 1, PIVOT_DIR|PIVOT_PARAM, + CHK_DIR_LIST, (u8*)"dir traversal", + inject_dir_listing_tests, inject_dir_listing_check, 0 }, + + { 48, 1, 1, 0, 1, 0, + CHK_LFI, (u8*)"local file inclusion", + inject_lfi_tests, inject_lfi_check, 0 }, + + { 1, 0, 1, 0, 1, 0, + CHK_RFI, (u8*)"remote file inclusion", + inject_rfi_tests, inject_rfi_check, 0 }, + + { 4, 0, 1, 0, 1, 0, + CHK_XSS, (u8*)"XSS injection", + inject_xss_tests, inject_xss_check, 0 }, + + { 1, 1, 1, 0, 1, 0, + CHK_XSSI, (u8*)"XSSI protection", + xssi_tests, xssi_check, 0 }, + + { 1, 0, 1, 0, 1, 0, + CHK_PROLOG, (u8*)"prologue injection", + inject_prologue_tests, inject_prologue_check, 0 }, + + { 2, 1, 1, 0, 1, 0, + CHK_RSPLIT, (u8*)"Header injection", + inject_split_tests, inject_split_check, 0 }, + + { 5, 1, 1, 0, 1, PIVOT_PARAM, + CHK_REDIR, (u8*)"Redirect injection", + inject_redir_tests, inject_redir_check, 0 }, + + { 10, 1, 0, 0, 1, 0, + CHK_SQL, (u8*)"SQL injection", + inject_sql_tests, inject_sql_check, 0 }, + + { 2, 1, 0, 0, 1, 0, + CHK_XML, (u8*)"XML injection", + inject_xml_tests, inject_xml_check, 0 }, + + { 12, 1, 0, 0, 1, 0, + CHK_SHELL_DIFF, (u8*)"Shell injection (diff)", + inject_diff_shell_tests, inject_diff_shell_check, 0 }, + + { 12, 1, 1, 1, 1, 0, + CHK_SHELL_SPEC, (u8*)"Shell injection (spec)", + inject_shell_tests, inject_shell_check, 0 }, + + { 2, 1, 0, 0, 1, 0, + CHK_FORMAT, (u8*)"format string", + inject_format_tests, inject_format_check, 1 }, + + { 9, 1, 0, 0, 1, 0, + CHK_INTEGER, (u8*)"integer handling", + inject_integer_tests, inject_integer_check, 1 } + +}; + +/* Dump the checks to stdout */ + +void display_injection_checks(void) { + u32 i; + + SAY("\n[*] Available injection tests:\n\n"); + for (i=cb_handle_off; i cb_handle_cnt) + FATAL("Unable to parse checks toggle string"); + + tnr += offset; + /* User values are array index nr + 1 */ + if (enable && cb_handles[tnr].skip) { + cb_handles[tnr].skip = 0; + DEBUG(" Enabled test: %d : %s\n", tnr, cb_handles[tnr].name); + } else { + cb_handles[tnr].skip = 1; + DEBUG(" Disabled test: %d : %s\n", tnr, cb_handles[tnr].name); + } + + ptr = (u8*)strtok(NULL, ","); + } + + ck_free(ids); +} + +/* The inject state manager which uses the list ot check structs to + decide what test to schedule next */ + +u8 inject_state_manager(struct http_request* req, struct http_response* res) { + + u32 i; + s32 check = req->pivot->check_idx; + + DEBUG_CALLBACK(req, res); + + /* If we are in crawler only more, jump to inject_done to effectively + disable all checks for the pivot */ + + if(no_checks) goto inject_done; + + /* First test that gets us in the loop? This means we'll immediately go and + schedule some tests */ + + if (check == -1) goto schedule_tests; + + /* Safety check */ + if (check > cb_handle_cnt) + FATAL("Check number %d exceeds handle count %d!",check,cb_handle_cnt); + + /* If requests failed for a test than we might have chosen to not + proceed with it by adding the check to i_skip. Here we check if this + is the case. */ + + if (req->pivot->i_skip[check]) + return 0; + + /* For simple injection tests, we do not abort at 503, 504's. But for + differential tests, we have to. */ + + if (res->state != STATE_OK || (!cb_handles[check].allow_varies && + (res->code == 503 || res->code == 504))) { + handle_error(req, res, (u8*)cb_handles[check].name, 0); + + content_checks(req, res); + req->pivot->i_skip[check] = 1; + return 0; + } + + + /* Store req/res which is used by checks that like to have multiple req/res + pairs before getting called. */ + + if (cb_handles[check].res_keep) { + req->pivot->misc_req[req->user_val] = req; + req->pivot->misc_res[req->user_val] = res; + req->pivot->misc_cnt++; + + /* Check and return if we need more responses. */ + if (cb_handles[check].res_num && + req->pivot->misc_cnt != cb_handles[check].res_num) + return 1; + } + + /* Check the results of previously scheduled tests and, if that goes + well, schedule new tests. When the callback returns 1, this means + more requests are needed before we can can do the final checks. */ + + + DEBUG_STATE_CALLBACK(req, cb_handles[check].name, 1); + + /* Check if we got all responses to avoid handing over NULL poiners to the + * checks() functions */ + + if (cb_handles[check].res_keep) { + for (i=0; ipivot->misc_cnt; i++) { + if (!MREQ(i) || !MRES(i)) { + DEBUG("-- Misc request #%d failed\n", i); + problem(PROB_FETCH_FAIL, req, res, (u8*)"During injection testing", req->pivot, 0); + + /* Today, we'll give up on this test. In the next release: reschedule */ + goto content_checks; + } + + } + } + + if (cb_handles[check].checks(req,res)) + return 1; + + if (!cb_handles[check].res_keep && + (cb_handles[check].res_num && ++req->pivot->misc_cnt != cb_handles[check].res_num)) + return 0; + +content_checks: + + /* If we get here, we're done and can move on. First make sure that + all responses have been checked. Than free memory and schedule the + next test */ + + if (cb_handles[check].res_keep && req->pivot->misc_cnt) { + for (i=0; ipivot->misc_cnt; i++) { + + /* Only check content once */ + if (!MRES(i) || !MREQ(i) || MRES(i)->stuff_checked) + continue; + + /* Only scrape for checks that want it + 0 = don't scrape + 1 = check content + 2 = check content and extract links */ + if (cb_handles[check].scrape > 0) { + content_checks(MREQ(i), MRES(i)); + if (cb_handles[check].scrape == 2) + scrape_response(MREQ(i), MRES(i)); + } + } + } + +schedule_tests: + + destroy_misc_data(req->pivot, req); + + check = ++req->pivot->check_idx; + if (check < cb_handle_cnt) { + + /* Move to the next test in case it's marked... */ + if (cb_handles[check].skip) goto schedule_tests; + + /* Move to the next test in case the page is unstable and the test doesn't want it. */ + if ((req->pivot->res_varies && !cb_handles[check].allow_varies) || + (req->pivot->res_time_exceeds && cb_handles[check].time_sensitive)) + goto schedule_tests; + + /* Move to the next test in case of pivot type mismatch */ + if (cb_handles[check].pv_flag > 0 && !(cb_handles[check].pv_flag & req->pivot->type)) + goto schedule_tests; + + DEBUG_STATE_CALLBACK(req, cb_handles[check].name, 0); + + /* Do the tests and return upon success or move on to the next upon + a return value of 1. We store the ID of the check in the pivot to + allow other functions, that use the pivot, to find out the current + injection test */ + req->pivot->check_id = cb_handles[check].id; + if (cb_handles[check].tests(req->pivot) == 1) + goto schedule_tests; + + return 0; + } + +inject_done: + + /* All injection tests done. Reset the counter and call inject_done() + to finish (or proceed with param tests */ + + + DEBUG_STATE_CALLBACK(req, "inject_done", 1); + + req->pivot->check_idx = -1; + inject_done(req->pivot); + + return 0; +} + + +static u8 xssi_tests(struct pivot_desc* pv) { + struct http_request* n; + + DEBUG_HELPER(pv); + + /* We only want Javascript that does not have inclusion protection. This + * test, should be moved to the injection manager whenever we have more + * content specific tests (e.g. css ones) */ + + if(pv->res->js_type != 2 || pv->res->json_safe) + return 1; + + n = req_copy(pv->req, pv, 1); + n->callback = inject_state_manager; + n->no_cookies = 1; + async_request(n); + + return 0; + +} + +static u8 xssi_check(struct http_request* req, + struct http_response* res) { + + DEBUG_MISC_CALLBACK(req, res); + + /* When the response with cookie is different from the cookie-less response, + * than the content is session depended. In case of Javascript without XSSI + * protection, this is more than likely an issue. */ + + + if (!same_page(&RPRES(req)->sig, &MRES(0)->sig)) { + + /* Responses that do not contain the term "function", "if", "for", "while", etc, + are much more likely to be dynamic JSON than just static scripts. Let's + try to highlight these. */ + + if ((!req->method || !strcmp((char*)req->method, "GET")) && + !inl_findstr(res->payload, (u8*)"if (", 2048) && + !inl_findstr(res->payload, (u8*)"if(", 2048) && + !inl_findstr(res->payload, (u8*)"for (", 2048) && + !inl_findstr(res->payload, (u8*)"for(", 2048) && + !inl_findstr(res->payload, (u8*)"while (", 2048) && + !inl_findstr(res->payload, (u8*)"while(", 2048) && + !inl_findstr(res->payload, (u8*)"function ", 2048) && + !inl_findstr(res->payload, (u8*)"function(", 2048)) { + + problem(PROB_JS_XSSI, req, res, (u8*)"Cookie-less JSON is different", req->pivot, 0); + + } else { + problem(PROB_JS_XSSI, req, res, (u8*)"Cookie-less Javascript response is different", req->pivot, 0); + } + } + + /* Now this is interesting. We can lookup the issues in the pivot and if + * analysis.c thinks this page has an XSSI, we can kill that assumption */ + + remove_issue(req->pivot, PROB_JS_XSSI); + + return 0; +} + +static u8 inject_behavior_tests(struct pivot_desc* pv) { + struct http_request* n; + u32 i; + + DEBUG_HELPER(pv); + + for (i=0;ireq, pv, 1); + n->callback = inject_state_manager; + n->user_val = i; + async_request(n); + } + + return 0; +} + +static u8 inject_behavior_check(struct http_request* req, + struct http_response* res) { + + u32 i; + + /* pv->state may change after async_request() calls in + insta-fail mode, so we should cache accordingly. */ + + DEBUG_CALLBACK(req, res); + + for (i=0; ipivot->misc_cnt; i++) { + if (!same_page(&RPRES(req)->sig, &MRES(i)->sig)) { + problem(PROB_VARIES, MREQ(i), MRES(i), 0, MREQ(i)->pivot, 0); + return 0; + } + } + + return 0; +} + +static u8 put_upload_tests(struct pivot_desc* pv) { + + struct http_request* n; + DEBUG_HELPER(pv); + + /* First a PUT request */ + n = req_copy(pv->req, pv, 1); + if (n->method) ck_free(n->method); + n->method = ck_strdup((u8*)"PUT"); + n->user_val = 0; + n->callback = inject_state_manager; + replace_slash(n, (u8*)("PUT-" BOGUS_FILE)); + async_request(n); + + /* Second a FOO for false positives */ + n = req_copy(pv->req, pv, 1); + if (n->method) ck_free(n->method); + n->method = ck_strdup((u8*)"FOO"); + n->user_val = 1; + n->callback = inject_state_manager; + replace_slash(n, (u8*)("FOO-" BOGUS_FILE)); + async_request(n); + + return 0; +} + +static u8 put_upload_check(struct http_request* req, + struct http_response* res) { + + DEBUG_MISC_CALLBACK(req, res); + + /* If PUT and FOO of the page does not give the same result. And if + additionally we get a 2xx code, than we'll mark the issue as detected */ + if (!same_page(&MRES(0)->sig, &MRES(1)->sig) && + MRES(0)->code >= 200 && MRES(1)->code < 300) + problem(PROB_PUT_DIR, MREQ(0), MRES(0), 0, req->pivot, 0); + + return 0; +} + + +/* The prologue test checks whether it is possible to inject a string in the + first bytes of the response because this can lead to utf-7 or third party + browser plugin attacks */ + +static u8 inject_prologue_tests(struct pivot_desc* pivot) { + + u32 orig_state = pivot->state; + struct http_request* n; + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, (u8*)"+/skipfish-bom"); + n->callback = inject_state_manager; + async_request(n); + + return 0; +} + +static u8 inject_prologue_check(struct http_request* req, + struct http_response* res) { + + DEBUG_CALLBACK(req, res); + + if (res->pay_len && !prefix(res->payload, (u8*)"+/skipfish-bom") && + !GET_HDR((u8*)"Content-Disposition", &res->hdr)) + problem(PROB_PROLOGUE, req, res, NULL, req->pivot, 0); + + return 0; +} + + +/* XML injection checks evaluates multiple server responses and determined + whether the injected string caused a difference in behavior/reponse */ + +static u8 inject_xml_tests(struct pivot_desc* pivot) { + + /* Backend XML injection - 2 requests. */ + u32 orig_state = pivot->state; + struct http_request* n; + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "sfish>'>\">"); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "sfish>'>\">"); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + return 0; +} + +static u8 inject_xml_check(struct http_request* req, + struct http_response* res) { + + DEBUG_MISC_CALLBACK(req, res); + + /* Got all responses: + + misc[0] = valid XML + misc[1] = bad XML + + If misc[0] != misc[1], we probably have XML injection on backend side. */ + + if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) { + problem(PROB_XML_INJECT, MREQ(0), MRES(0), + (u8*)"responses for and look different", + req->pivot, 0); + RESP_CHECKS(MREQ(1), MRES(1)); + } + + return 0; +} + + +static u8 inject_shell_tests(struct pivot_desc* pivot) { + + /* Shell command injection - 12 requests. */ + + u32 orig_state = pivot->state; + u8* tmp; + struct http_request* n; + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, (u8*)"`echo skip12``echo 34fish`"); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, (u8*)"`echo skip12``echo 34fish`"); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, (u8*)"`echo${IFS}skip12``echo${IFS}34fish`"); + n->callback = inject_state_manager; + n->user_val = 2; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, (u8*)"`echo${IFS}skip12``echo${IFS}34fish`"); + n->callback = inject_state_manager; + n->user_val = 3; + async_request(n); + + /* We use the measured time_base as an offset for the sleep test. The + value is limited to MAX_RES_DURATION and the result is < 10 */ + + tmp = ck_alloc(10); + sprintf((char*)tmp, (char*)"`sleep %d`", pivot->res_time_base + SLEEP_TEST_ONE); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n,tmp ); + n->callback = inject_state_manager; + n->user_val = 4; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, tmp); + n->callback = inject_state_manager; + n->user_val = 5; + async_request(n); + + sprintf((char*)tmp, (char*)"`sleep %d`", pivot->res_time_base + SLEEP_TEST_TWO); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, tmp); + n->callback = inject_state_manager; + n->user_val = 6; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, tmp); + n->callback = inject_state_manager; + n->user_val = 7; + async_request(n); + + tmp = ck_realloc(tmp, 15); + sprintf((char*)tmp, (char*)"`sleep${IFS}%d`", pivot->res_time_base + SLEEP_TEST_ONE); + + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, tmp); + n->callback = inject_state_manager; + n->user_val = 8; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, tmp); + n->callback = inject_state_manager; + n->user_val = 9; + async_request(n); + + sprintf((char*)tmp, (char*)"`sleep${IFS}%d`", pivot->res_time_base + SLEEP_TEST_TWO); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, tmp); + n->callback = inject_state_manager; + n->user_val = 10; + async_request(n); + + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, tmp); + n->callback = inject_state_manager; + n->user_val = 11; + async_request(n); + + ck_free(tmp); + + return 0; +} + +static u8 inject_shell_check(struct http_request* req, + struct http_response* res) { + u32 i; + + DEBUG_MISC_CALLBACK(req, res); + + /* Look in the first 4 requests to find our concatenated string */ + + for (i = 0; i < 3; i++) { + if (inl_findstr(MRES(i)->payload, (u8*)"skip1234fish", 1024)) + problem(PROB_SH_INJECT, MREQ(i), MRES(i), + (u8*)"Confirmed shell injection (echo test)", req->pivot, 0); + } + + /* Check that the request was delayed by our sleep. The sleep delay is + calculated by using the time_base in order to avoid FPs */ + + u32 test_one = req->pivot->res_time_base + SLEEP_TEST_ONE; + u32 test_two = req->pivot->res_time_base + SLEEP_TEST_TWO; + + /* Now we check if the request duration was influenced by the sleep. We + do this by testing if the total request time was longer (or equal) + to: the average request time + the sleep time (3 or 5 seconds). + + We allow the `sleep` request to take 1 second longer than + expected which is the final measure to reduce FPs. + + */ + + if ((RTIME(4) >= test_one && RTIME(4) < test_one + 1) && + (RTIME(6) >= test_two && RTIME(6) < test_two + 1)) { + problem(PROB_SH_INJECT, MREQ(4), MRES(4), + (u8*)"Confirmed shell injection (sleep test)", req->pivot, 0); + } + + if ((RTIME(5) >= test_one && RTIME(5) < test_one + 1) && + (RTIME(7) >= test_two && RTIME(7) < test_two + 1)) { + problem(PROB_SH_INJECT, MREQ(5), MRES(5), + (u8*)"Confirmed shell injection (sleep test)", req->pivot, 0); + } + + if ((RTIME(8) >= test_one && RTIME(8) < test_one + 1) && + (RTIME(10) >= test_two && RTIME(10) < test_two + 1)) { + problem(PROB_SH_INJECT, MREQ(8), MRES(8), + (u8*)"Confirmed shell injection (sleep test)", req->pivot, 0); + } + + if ((RTIME(9) >= test_one && RTIME(9) < test_one + 1) && + (RTIME(11) >= test_two && RTIME(11) < test_two + 1)) { + problem(PROB_SH_INJECT, MREQ(9), MRES(9), + (u8*)"Confirmed shell injection (sleep test)", req->pivot, 0); + } + + return 0; +} + + +static u8 inject_diff_shell_tests(struct pivot_desc* pivot) { + + /* Shell command injection - 12 requests. */ + + u32 orig_state = pivot->state; + struct http_request* n; + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "`true`"); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "`false`"); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "`uname`"); + n->callback = inject_state_manager; + n->user_val = 2; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "\"`true`\""); + n->callback = inject_state_manager; + n->user_val = 3; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "\"`false`\""); + n->callback = inject_state_manager; + n->user_val = 4; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "\"`uname`\""); + n->callback = inject_state_manager; + n->user_val = 5; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "'`true`'"); + n->callback = inject_state_manager; + n->user_val = 6; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "'`false`'"); + n->callback = inject_state_manager; + n->user_val = 7; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "'`uname`'"); + n->callback = inject_state_manager; + n->user_val = 8; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "`true`"); + n->callback = inject_state_manager; + n->user_val = 9; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "`false`"); + n->callback = inject_state_manager; + n->user_val = 10; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "`uname`"); + n->callback = inject_state_manager; + n->user_val = 11; + async_request(n); + + return 0; +} + + +static u8 inject_diff_shell_check(struct http_request* req, + struct http_response* res) { + + DEBUG_MISC_CALLBACK(req, res); + + /* Got all responses: + + misc[0] = `true` + misc[1] = `false` + misc[2] = `uname` + misc[3] = "`true`" + misc[4] = "`false`" + misc[5] = "`uname`" + misc[6] = '`true`' + misc[7] = "`false`" + misc[8] = '`uname`' + + And a variant that replaces the original values (instead of appending) + + misc[9] = `true` + misc[10] = `false` + misc[11] = `uname` + + If misc[0] == misc[1], but misc[0] != misc[2], we probably have shell + injection. Ditto for the remaining triplets. We use the `false` case + to avoid errors on search fields, etc. */ + + if (same_page(&MRES(0)->sig, &MRES(1)->sig) && + !same_page(&MRES(1)->sig, &MRES(2)->sig)) { + problem(PROB_SH_INJECT, MREQ(1), MRES(1), + (u8*)"responses to `true` and `false` different than to `uname`", + req->pivot, 0); + } + + if (same_page(&MRES(3)->sig, &MRES(4)->sig) && + !same_page(&MRES(4)->sig, &MRES(5)->sig)) { + problem(PROB_SH_INJECT, MREQ(3), MRES(3), + (u8*)"responses to `true` and `false` different than to `uname`", + req->pivot, 0); + } + + if (same_page(&MRES(6)->sig, &MRES(7)->sig) && + !same_page(&MRES(6)->sig, &MRES(8)->sig)) { + problem(PROB_SH_INJECT, MREQ(6), MRES(6), + (u8*)"responses to `true` and `false` different than to `uname`", + req->pivot, 0); + } + + if (same_page(&MRES(9)->sig, &MRES(10)->sig) && + !same_page(&MRES(10)->sig, &MRES(11)->sig)) { + problem(PROB_SH_INJECT, MREQ(9), MRES(9), + (u8*)"responses to `true` and `false` different than to `uname`", + req->pivot, 0); + } + + return 0; +} + + + +static u8 inject_xss_tests(struct pivot_desc* pivot) { + + /* Cross-site scripting - three requests (also test common + "special" error pages). */ + + struct http_request* n; + u32 orig_state = pivot->state; + u32 i, uval; + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, new_xss_tag(NULL)); + set_value(PARAM_HEADER, (u8*)"Referer", new_xss_tag(NULL), 0, &n->par); + register_xss_tag(n); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, new_xss_tag((u8*)".htaccess.aspx")); + register_xss_tag(n); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + /* A last ones with only header injections. The User-Agent injection + doesn't seems to be very useful for reflective XSS scenario's + but could reveal persistant XSS problems (i.e. in log / backend + interfaces) */ + + n = req_copy(pivot->req, pivot, 1); + set_value(PARAM_HEADER, (u8*)"Referer", new_xss_tag(NULL), 0, &n->par); + set_value(PARAM_HEADER, (u8*)"User-Agent", new_xss_tag(NULL), 0, &n->par); + register_xss_tag(n); + n->callback = inject_state_manager; + n->user_val = 2; + async_request(n); + + /* One for testing HTTP_HOST XSS types which are somewhat unlikely + but still have abuse potential (e.g. stored XSS') */ + n = req_copy(pivot->req, pivot, 1); + set_value(PARAM_HEADER, (u8*)"Host", new_xss_tag(NULL), 0, &n->par); + register_xss_tag(n); + n->callback = inject_state_manager; + n->user_val = 3; + async_request(n); + + + /* Finally we tests the cookies, one by one to avoid breaking the + session */ + + uval = 3; + for (i=0;ireq, pivot, 1); + set_value(PARAM_COOKIE, global_http_par.n[i], + new_xss_tag(NULL), 0, &n->par); + register_xss_tag(n); + n->callback = inject_xss_check; + n->user_val = ++uval; + async_request(n); + } + + return 0; +} + + +static u8 inject_xss_check(struct http_request* req, + struct http_response* res) { + + DEBUG_CALLBACK(req, res); + + if (!req || !res || FETCH_FAIL(res)) + return 0; + + /* Content checks do automatic HTML parsing and XSS detection. + scrape_page() is generally not advisable here. This is not a very + exiting check and we'll be able to get rid of it in future updated. */ + + content_checks(req, res); + return 0; +} + +static u8 inject_dir_listing_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u8* tmp = NULL; + u32 orig_state = pivot->state; + + /* Directory listing - 4 requests. The logic here is a bit + different for parametric targets (which are easy to examine with + a ./ trick) and directories (which require a more complex + comparison). */ + + pivot->misc_cnt = 0; + + n = req_copy(pivot->req, pivot, 1); + + if (orig_state == PSTATE_CHILD_INJECT) { + replace_slash(n, (u8*)"."); + set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->par); + } else { + tmp = ck_alloc(strlen((char*)TPAR(n)) + 5); + sprintf((char*)tmp, ".../%s", TPAR(n)); + ck_free(TPAR(n)); + TPAR(n) = ck_strdup(tmp); + } + + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + + if (orig_state == PSTATE_CHILD_INJECT) { + replace_slash(n, (u8*)".sf"); + set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->par); + } else { + ck_free(TPAR(n)); + TPAR(n) = ck_strdup(tmp + 2); + } + + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + + if (orig_state == PSTATE_CHILD_INJECT) { + replace_slash(n, (u8*)"\\.\\"); + } else { + tmp[3] = '\\'; + ck_free(TPAR(n)); + TPAR(n) = ck_strdup(tmp); + } + n->callback = inject_state_manager; + n->user_val = 2; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + + if (orig_state == PSTATE_CHILD_INJECT) { + replace_slash(n, (u8*)"\\.sf\\"); + } else { + ck_free(TPAR(n)); + TPAR(n) = ck_strdup(tmp + 2); + ck_free(tmp); + } + + n->callback = inject_state_manager; + n->user_val = 3; + async_request(n); + + return 0; +} + + +static u8 inject_dir_listing_check(struct http_request* req, + struct http_response* res) { + u32 orig_state = req->pivot->state; + + DEBUG_MISC_CALLBACK(req, res); + + /* Got all responses. For directories, this is: + + pivot = / + misc[0] = /./ + misc[1] = /.sf/ + misc[2] = \.\ + misc[3] = \.sf\ + + Here, if pivot != misc[0], and misc[0] != misc[1], we probably + managed to list a hidden dir. The same test is carried out for + misc[2] and misc[3]. + + For parameters, this is: + + misc[0] = .../known_val + misc[1] = ./known_val + misc[2] = ...\known_val + misc[3] = .\known_val + + Here, the test is simpler: if misc[1] != misc[0], or misc[3] != + misc[2], we probably have a bug. The same if misc[4] or misc[5] + contain magic strings, but misc[0] doesn't. + + */ + + if (orig_state == PSTATE_CHILD_INJECT) { + + if (MRES(0)->code < 300 && + !same_page(&MRES(0)->sig, &RPRES(req)->sig) && + !same_page(&MRES(0)->sig, &MRES(1)->sig)) { + problem(PROB_DIR_LIST_BYPASS, MREQ(0), MRES(0), + (u8*)"unique response for /./", + req->pivot, 0); + + /* Use pivot's request, rather than MREQ(0), for link scraping; + MREQ(0) contains an "illegal" manually constructed path. */ + + RESP_CHECKS(RPREQ(req), MRES(0)); + } + + if (MRES(2)->code < 300 && + !same_page(&MRES(2)->sig, &RPRES(req)->sig) && + !same_page(&MRES(2)->sig, &MRES(3)->sig)) { + problem(PROB_DIR_LIST_BYPASS, MREQ(2), MRES(2), + (u8*)"unique response for \\.\\", + req->pivot, 0); + RESP_CHECKS(MREQ(2), MRES(2)); + } + + } else { + + if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) { + problem(PROB_DIR_TRAVERSAL, MREQ(1), MRES(1), + (u8*)"responses for ./val and .../val look different", + req->pivot, 0); + RESP_CHECKS(MREQ(0), MRES(0)); + } + + if (!same_page(&MRES(2)->sig, &MRES(3)->sig)) { + problem(PROB_DIR_TRAVERSAL, MREQ(3), MRES(3), + (u8*)"responses for .\\val and ...\\val look different", + req->pivot, 0); + RESP_CHECKS(MREQ(2), MRES(2)); + } + + } + + return 0; +} + + +static u8 inject_lfi_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u32 i, v; + u32 count = 0; + + /* Perhaps do this in state manager ?*/ + if (pivot->state == PSTATE_CHILD_INJECT) + return 1; + + /* We combine the traversal and file disclosure attacks here since + the checks are almost identical */ + + i = 0; + while (i <= MAX_LFI_INDEX) { + + v = 0; + while (lfi_tests[i].vectors[v]) { + + /* ONE: no encoding */ + n = req_copy(pivot->req, pivot, 1); + + n->fuzz_par_enc = (u8*)ENC_NULL; + + ck_free(TPAR(n)); + TPAR(n) = ck_strdup((u8*)lfi_tests[i].vectors[v]); + + n->callback = inject_state_manager; + n->check_subid = i; + n->user_val = count++; + async_request(n); + + /* TWO: path encoding used */ + n = req_copy(pivot->req, pivot, 1); + + n->fuzz_par_enc = (u8*)ENC_PATH; + + ck_free(TPAR(n)); + TPAR(n) = ck_strdup((u8*)lfi_tests[i].vectors[v]); + + n->callback = inject_state_manager; + n->check_subid = i; + n->user_val = count++; + async_request(n); + + /* THREE: double path encoding */ + + n = req_copy(pivot->req, pivot, 1); + + n->fuzz_par_enc = (u8*)ENC_PATH; + + ck_free(TPAR(n)); + TPAR(n) = url_encode_token((u8*)lfi_tests[i].vectors[v], + strlen(lfi_tests[i].vectors[v]), + (u8*)ENC_PATH); + + n->callback = inject_state_manager; + n->check_subid = i; + n->user_val = count++; + async_request(n); + + + /* FOUR: path encoding, with NULL byte and extension */ + n = req_copy(pivot->req, pivot, 1); + + ck_free(TPAR(n)); + u8 *tmp = url_encode_token((u8*)lfi_tests[i].vectors[v], + strlen(lfi_tests[i].vectors[v]), + (u8*)ENC_PATH); + + TPAR(n) = ck_alloc(strlen((char*)tmp) + 10); + sprintf((char*)TPAR(n), "%s%%00%%2ejs", (char*)tmp); + ck_free(tmp); + + n->fuzz_par_enc = (u8*)ENC_NULL; + n->callback = inject_state_manager; + n->check_subid = i; + n->user_val = count++; + async_request(n); + + v++; + } + i++; + } + + + u8 *web1 = ck_strdup((u8*)"/WEB-INF/web.xml"); + u8 *web2 = ck_strdup((u8*)"/WEB-INF%2fweb%2exml"); + u8 *web3 = ck_strdup((u8*)"/WEB-INF%2fweb%2exml%00.js"); + + for (v=0; v<8; v++) { + + PREFIX_STRING(web1, "/.."); + PREFIX_STRING(web2, "%2f%2e%2e"); + PREFIX_STRING(web3, "%2f%2e%2e"); + + DEBUG("WEB: %s\n", web1); + + n = req_copy(pivot->req, pivot, 1); + + n->fuzz_par_enc = (u8*)ENC_NULL; + + ck_free(TPAR(n)); + TPAR(n) = ck_strdup(web1); + + n->callback = inject_state_manager; + n->check_subid = i; + n->user_val = count++; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + + /* No % encoding for these requests */ + n->fuzz_par_enc = (u8*)ENC_NULL; + + ck_free(TPAR(n)); + TPAR(n) = ck_strdup(web2); + + n->callback = inject_state_manager; + n->check_subid = i; + n->user_val = count++; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + + /* No % encoding for these requests */ + n->fuzz_par_enc = (u8*)ENC_NULL; + + ck_free(TPAR(n)); + TPAR(n) = ck_strdup(web3); + + n->callback = inject_state_manager; + n->check_subid = i; + n->user_val = count++; + async_request(n); + } + + ck_free(web1); + ck_free(web2); + ck_free(web3); + + pivot->pending = count; + + return 0; +} + + +static u8 inject_lfi_check(struct http_request* req, + struct http_response* res) { + + DEBUG_MISC_CALLBACK(req, res); + + u8 found = 0; + u32 p = 0; + + + /* + Perform directory traveral and file inclusion tests. + + In every request, the check_subid points to the relevant traversal + test string. We look up the string and compare it with the response. + + Exception to this are the web.xml requests for which the injection + strings are dynamically generated. We don't know where the web.xml + is located on the file system (unlike /etc/passwd), we cannot use + an arbitrary amount of ../'s. Instead, we need to use the exact + amount in order to be able to disclose the file. + + */ + + for (p=0; p < req->pivot->pending; p++) { + + + if (MREQ(p)->check_subid <= MAX_LFI_INDEX) { + + /* Test the parent and current response */ + if (!inl_findstr(RPRES(req)->payload, + (u8*)lfi_tests[MREQ(p)->check_subid].test_string, 1024) && + inl_findstr(MRES(p)->payload, + (u8*)lfi_tests[MREQ(p)->check_subid].test_string, 1024)) { + + problem(PROB_FI_LOCAL, MREQ(p), MRES(p), + (u8*)lfi_tests[MREQ(p)->check_subid].description, req->pivot, 0); + found = 1; + } + } else if (MREQ(p)->check_subid == MAX_LFI_INDEX + 1) { + + /* Check the web.xml disclosure */ + if (!inl_findstr(RPRES(req)->payload, (u8*)"payload, (u8*)"payload, (u8*)"", 2048) || + inl_findstr(MRES(p)->payload, (u8*)"", 2048) || + inl_findstr(MRES(p)->payload, (u8*)"", 2048)) { + problem(PROB_FI_LOCAL, MREQ(p), MRES(p), + (u8*)"response resembles web.xml.", req->pivot, 0); + found = 1; + } + + } + } + + } + + /* If we disclosed something: suppress the more generic traversal + warnings by removing the issue. */ + + if (found) + remove_issue(req->pivot, PROB_DIR_TRAVERSAL); + + return 0; +} + +static u8 inject_rfi_tests(struct pivot_desc* pivot) { + + struct http_request* n; + + /* Perhaps do this in state manager ?*/ + if (pivot->state == PSTATE_CHILD_INJECT) + return 1; + + n = req_copy(pivot->req, pivot, 1); + + ck_free(TPAR(n)); + TPAR(n) = ck_strdup((u8*)RFI_HOST); + + n->callback = inject_state_manager; + async_request(n); + + return 0; + +} + +static u8 inject_rfi_check(struct http_request* req, + struct http_response* res) { + + + DEBUG_CALLBACK(req, res); + + if (!inl_findstr(RPRES(req)->payload, (u8*)RFI_STRING, 1024) && + inl_findstr(res->payload, (u8*)RFI_STRING, 1024)) { + problem(PROB_FI_REMOTE, req, res, + (u8*)"remote file inclusion", req->pivot, 0); + } + + return 0; +} + + + +static u8 inject_redir_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u32 orig_state = pivot->state; + + /* XSS checks - 5 requests */ + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "http://skipfish.invalid/;?"); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "//skipfish.invalid/;?"); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "skipfish://invalid/;?"); + n->callback = inject_state_manager; + n->user_val = 2; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "'skip'''\"fish\"\"\""); + n->callback = inject_state_manager; + n->user_val = 3; + async_request(n); + + /* Finally an encoded version which is aimed to detect injection + problems in JS handlers, such as onclick, which executes HTML encoded + strings. */ + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "'skip'''"fish""""); + n->callback = inject_state_manager; + n->user_val = 4; + async_request(n); + + return 0; +} + + +static u8 inject_redir_check(struct http_request* req, + struct http_response* res) { + u8* val; + u32 i; + + DEBUG_MISC_CALLBACK(req, res); + + /* Check Location, Refresh headers. */ + + for (i=0; i < req->pivot->misc_cnt; i++) { + + val = GET_HDR((u8*)"Location", &MRES(i)->hdr); + + if (val) { + + if (!case_prefix(val, "http://skipfish.invalid/") || + !case_prefix(val, "//skipfish.invalid/")) + problem(PROB_URL_REDIR, MREQ(i), MRES(i), (u8*)"injected URL in 'Location' header", + req->pivot, 0); + + if (!case_prefix(val, "skipfish:")) + problem(PROB_URL_XSS, MREQ(i), MRES(i), (u8*)"injected URL in 'Location' header", + req->pivot, 0); + + } + + val = GET_HDR((u8*)"Refresh", &MRES(i)->hdr); + + if (val && (val = (u8*)strchr((char*)val, '=')) && val++) { + u8 semi_safe = 0; + + if (*val == '\'' || *val == '"') { val++; semi_safe++; } + + if (!case_prefix(val, "http://skipfish.invalid/") || + !case_prefix(val, "//skipfish.invalid/")) + problem(PROB_URL_REDIR, MREQ(i), MRES(i), (u8*)"injected URL in 'Refresh' header", + req->pivot, 0); + + /* Unescaped semicolon in Refresh headers is unsafe with MSIE6. */ + + if (!case_prefix(val, "skipfish:") || + (!semi_safe && strchr((char*)val, ';'))) + problem(PROB_URL_XSS, MREQ(i), MRES(i), (u8*)"injected URL in 'Refresh' header", + req->pivot, 0); + + } + + /* META tags and JS will be checked by content_checks(). We're not + calling scrape_page(), because we don't want to accumulate bogus, + injected links. */ + + content_checks(MREQ(i), MRES(i)); + + } + + return 0; +} + + +static u8 inject_split_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u32 orig_state = pivot->state; + + /* Header splitting - 2 requests */ + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "bogus\nSkipfish-Inject:bogus"); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "bogus\rSkipfish-Inject:bogus"); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + return 0; + +} + + +static u8 inject_split_check(struct http_request* req, + struct http_response* res) { + + DEBUG_MISC_CALLBACK(req, res); + + /* Not differential. */ + + if (res->state != STATE_OK) { + handle_error(req, res, (u8*)"during header injection attacks", 0); + return 0; + } + + /* Check headers - that's all! */ + + if (GET_HDR((u8*)"Skipfish-Inject", &MRES(0)->hdr)) + problem(PROB_HTTP_INJECT, MREQ(0), MRES(0), + (u8*)"successfully injected 'Skipfish-Inject' header into response", + req->pivot, 0); + + if (GET_HDR((u8*)"Skipfish-Inject", &MRES(1)->hdr)) + problem(PROB_HTTP_INJECT, MREQ(1), MRES(1), + (u8*)"successfully injected 'Skipfish-Inject' header into response", + req->pivot, 0); + + return 0; +} + + +static u8 inject_sql_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u32 orig_state = pivot->state; + u8 is_num = 0; + + /* SQL injection - 10 requests */ + + if (orig_state != PSTATE_CHILD_INJECT) { + u8* pstr = TPAR(pivot->req); + u32 c = strspn((char*)pstr, "01234567890.+-"); + if (pstr[0] && !pstr[c]) is_num = 1; + } + + n = req_copy(pivot->req, pivot, 1); + if (!is_num) SET_VECTOR(orig_state, n, "9-8"); + else APPEND_VECTOR(orig_state, n, "-0"); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + if (!is_num) SET_VECTOR(orig_state, n, "8-7"); + else APPEND_VECTOR(orig_state, n, "-0-0"); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + if (!is_num) SET_VECTOR(orig_state, n, "9-1"); + else APPEND_VECTOR(orig_state, n, "-0-9"); + n->callback = inject_state_manager; + n->user_val = 2; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "\\\'\\\""); + set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish\\\'\\\"", 0, &n->par); + set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish\\\'\\\"", 0, &n->par); + set_value(PARAM_HEADER, (u8*)"Accept-Language", (u8*)"sfish\\\'\\\",en", 0, + &n->par); + n->callback = inject_state_manager; + n->user_val = 3; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "\'\""); + set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish\'\"", 0, &n->par); + set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish\'\"", 0, &n->par); + set_value(PARAM_HEADER, (u8*)"Accept-Language", (u8*)"sfish\'\",en", 0, + &n->par); + n->callback = inject_state_manager; + n->user_val = 4; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "\\\\\'\\\\\""); + set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish\\\\\'\\\\\"", 0, &n->par); + set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish\\\\\'\\\\\"", 0, &n->par); + set_value(PARAM_HEADER, (u8*)"Accept-Language", (u8*)"sfish\\\\\'\\\\\",en", 0, + &n->par); + n->callback = inject_state_manager; + n->user_val = 5; + async_request(n); + + /* This is a special case to trigger fault on blind numerical injection. */ + + n = req_copy(pivot->req, pivot, 1); + if (!is_num) SET_VECTOR(orig_state, n, "9 - 1"); + else APPEND_VECTOR(orig_state, n, " - 0 - 0"); + n->callback = inject_state_manager; + n->user_val = 6; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + if (!is_num) SET_VECTOR(orig_state, n, "9 1 -"); + else APPEND_VECTOR(orig_state, n, " 0 0 - -"); + n->callback = inject_state_manager; + n->user_val = 7; + async_request(n); + + /* Another round of SQL injection checks for a different escaping style. */ + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "''''\"\"\"\""); + set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish''''\"\"\"\"", 0, + &n->par); + set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish''''\"\"\"\"", 0, &n->par); + set_value(PARAM_HEADER, (u8*)"Accept-Language", (u8*)"sfish''''\"\"\"\",en", + 0, &n->par); + n->callback = inject_state_manager; + n->user_val = 8; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + APPEND_VECTOR(orig_state, n, "'\"'\"'\"'\""); + set_value(PARAM_HEADER, (u8*)"User-Agent", (u8*)"sfish'\"'\"'\"'\"", 0, + &n->par); + set_value(PARAM_HEADER, (u8*)"Referer", (u8*)"sfish'\"'\"'\"'\"", 0, + &n->par); + set_value(PARAM_HEADER, (u8*)"Accept-Language", + (u8*)"sfish'\"'\"'\"'\",en", 0, &n->par); + n->callback = inject_state_manager; + n->user_val = 9; + async_request(n); + + /* Todo: cookies */ + + return 0; +} + + +static u8 inject_sql_check(struct http_request* req, + struct http_response* res) { + + DEBUG_MISC_CALLBACK(req, res); + + /* Got all data: + + misc[0] = 9-8 (or orig-0) + misc[1] = 8-7 (or orig-0-0) + misc[2] = 9-1 (or orig-0-9) + misc[3] = [orig]\'\" + misc[4] = [orig]'" + misc[5] = [orig]\\'\\" + misc[6] = 9 - 1 (or orig - 0 - 0) + misc[7] = 9 1 - (or orig 0 0 - -) + + misc[8] == [orig]''''"""" + misc[9] == [orig]'"'"'"'" + + If misc[0] == misc[1], but misc[0] != misc[2], probable (numeric) SQL + injection. Ditto for misc[1] == misc[6], but misc[6] != misc[7]. + + If misc[3] != misc[4] and misc[3] != misc[5], probable text SQL + injection. + + If misc[4] == misc[9], and misc[8] != misc[9], probable text SQL + injection. + + */ + + if (same_page(&MRES(0)->sig, &MRES(1)->sig) && + !same_page(&MRES(1)->sig, &MRES(2)->sig)) { + problem(PROB_SQL_INJECT, MREQ(0), MRES(0), + (u8*)"response suggests arithmetic evaluation on server side (type 1)", + req->pivot, 0); + } + + if (same_page(&MRES(1)->sig, &MRES(6)->sig) && + !same_page(&MRES(6)->sig, &MRES(7)->sig)) { + problem(PROB_SQL_INJECT, MREQ(7), MRES(7), + (u8*)"response suggests arithmetic evaluation on server side (type 2)", + req->pivot, 0); + } + + if (same_page(&MRES(3)->sig, &MRES(4)->sig) && + !same_page(&MRES(4)->sig, &MRES(5)->sig)) { + problem(PROB_SQL_INJECT, MREQ(4), MRES(4), + (u8*)"response to '\" different than to \\'\\\"", req->pivot, 0); + } + + if (same_page(&MRES(4)->sig, &MRES(9)->sig) && + !same_page(&MRES(8)->sig, &MRES(9)->sig)) { + problem(PROB_SQL_INJECT, MREQ(4), MRES(4), + (u8*)"response to ''''\"\"\"\" different than to '\"'\"'\"'\"", req->pivot, 0); + } + + return 0; +} + + +static u8 inject_format_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u32 orig_state = pivot->state; + + /* Format string attacks - 2 requests. */ + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "sfish%dn%dn%dn%dn%dn%dn%dn%dn"); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "sfish%nd%nd%nd%nd%nd%nd%nd%nd"); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + return 0; +} + + +static u8 inject_format_check(struct http_request* req, + struct http_response* res) { + DEBUG_MISC_CALLBACK(req, res); + + /* Got all data: + + misc[0] = %dn... (harmless) + misc[1] = %nd... (crashy) + + If misc[0] != misc[1], probable format string vuln. + + */ + + if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) { + problem(PROB_FMT_STRING, MREQ(1), MRES(1), + (u8*)"response to %dn%dn%dn... different than to %nd%nd%nd...", + req->pivot, 0); + RESP_CHECKS(MREQ(1), MRES(1)); + } + + return 0; +} + +static u8 inject_integer_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u32 orig_state = pivot->state; + + /* Integer overflow bugs - 9 requests. */ + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "-0000012345"); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "-2147483649"); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "-2147483648"); + n->callback = inject_state_manager; + n->user_val = 2; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "0000012345"); + n->callback = inject_state_manager; + n->user_val = 3; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "2147483647"); + n->callback = inject_state_manager; + n->user_val = 4; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "2147483648"); + n->callback = inject_state_manager; + n->user_val = 5; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "4294967295"); + n->callback = inject_state_manager; + n->user_val = 6; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "4294967296"); + n->callback = inject_state_manager; + n->user_val = 7; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + SET_VECTOR(orig_state, n, "0000023456"); + n->callback = inject_state_manager; + n->user_val = 8; + async_request(n); + + return 0; +} + + +static u8 inject_integer_check(struct http_request* req, + struct http_response* res) { + + DEBUG_MISC_CALLBACK(req, res); + + /* Got all data: + + misc[0] = -12345 (baseline) + misc[1] = -(2^31-1) + misc[2] = -2^31 + misc[3] = 12345 (baseline) + misc[4] = 2^31-1 + misc[5] = 2^31 + misc[6] = 2^32-1 + misc[7] = 2^32 + misc[8] = 23456 (validation) + + If misc[3] != misc[8], skip tests - we're likely dealing with a + search field instead. + + If misc[0] != misc[1] or misc[2], probable integer overflow; + ditto for 3 vs 4, 5, 6, 7. + + */ + + if (!same_page(&MRES(3)->sig, &MRES(8)->sig)) + return 0; + + if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) { + problem(PROB_INT_OVER, MREQ(1), MRES(1), + (u8*)"response to -(2^31-1) different than to -12345", + req->pivot, 0); + RESP_CHECKS(MREQ(1), MRES(1)); + } + + if (!same_page(&MRES(0)->sig, &MRES(2)->sig)) { + problem(PROB_INT_OVER, MREQ(2), MRES(2), + (u8*)"response to -2^31 different than to -12345", + req->pivot, 0); + RESP_CHECKS(MREQ(2), MRES(2)); + } + + if (!same_page(&MRES(3)->sig, &MRES(4)->sig)) { + problem(PROB_INT_OVER, MREQ(4), MRES(4), + (u8*)"response to 2^31-1 different than to 12345", + req->pivot, 0); + RESP_CHECKS(MREQ(4), MRES(4)); + } + + if (!same_page(&MRES(3)->sig, &MRES(5)->sig)) { + problem(PROB_INT_OVER, MREQ(5), MRES(5), + (u8*)"response to 2^31 different than to 12345", + req->pivot, 0); + RESP_CHECKS(MREQ(5), MRES(5)); + } + if (!same_page(&MRES(3)->sig, &MRES(6)->sig)) { + problem(PROB_INT_OVER, MREQ(6), MRES(6), + (u8*)"response to 2^32-1 different than to 12345", + req->pivot, 0); + RESP_CHECKS(MREQ(6), MRES(6)); + } + + if (!same_page(&MRES(3)->sig, &MRES(7)->sig)) { + problem(PROB_INT_OVER, MREQ(7), MRES(7), + (u8*)"response to 2^32 different than to 12345", + req->pivot, 0); + RESP_CHECKS(MREQ(7), MRES(7)); + } + + return 0; +} + + +static u8 param_behavior_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u32 i; + + if (pivot->fuzz_par < 0 || !url_allowed(pivot->req) || !param_allowed(pivot->name)) { + pivot->state = PSTATE_DONE; + maybe_delete_payload(pivot); + return 0; + } + + DEBUG_HELPER(pivot); + + + /* Parameter behavior. */ + + for (i=0;ireq, pivot, 1); + ck_free(TPAR(n)); + TPAR(n) = ck_strdup((u8*)BOGUS_PARAM); + n->callback = inject_state_manager; + n->user_val = i; + async_request(n); + } + return 0; +} + + +static u8 param_behavior_check(struct http_request* req, + struct http_response* res) { + + u32 i; + u32 res_diff; + u32 page_diff = 0; + + DEBUG_MISC_CALLBACK(req, res); + + for (i=0; ipivot->misc_cnt; i++) { + + /* Store the biggest response time */ + res_diff = MREQ(i)->end_time - MREQ(i)->start_time; + if(res_diff > req->pivot->res_time_base) + req->pivot->res_time_base = res_diff; + + /* Compare the page responses */ + if (!page_diff && !same_page(&MRES(i)->sig, &RPRES(req)->sig)) + page_diff = i; + } + + /* If the largest response time exceeded our threshold, we'll skip + the timing related tests */ + + if(req->pivot->res_time_base > MAX_RES_DURATION) { + problem(PROB_VARIES, req, res, (u8*)"Responses too slow for time sensitive tests", req->pivot, 0); + req->pivot->res_time_exceeds = 1; + } + + + if (page_diff == req->pivot->misc_cnt) { + DEBUG("* Parameter seems to have no effect.\n"); + req->pivot->bogus_par = 1; + return 0; + } + + DEBUG("* Parameter seems to have some effect:\n"); + debug_same_page(&res->sig, &RPRES(req)->sig); + + if (req->pivot->bogus_par) { + DEBUG("* We already classified it as having no effect, whoops.\n"); + req->pivot->res_varies = 1; + problem(PROB_VARIES, req, res, 0, req->pivot, 0); + return 0; + } + + /* If we do not have a signature yet, record it. Otherwise, make sure + it did not change. */ + + if (!req->pivot->r404_cnt) { + + DEBUG("* New signature, recorded.\n"); + memcpy(&req->pivot->r404[0], &res->sig, sizeof(struct http_sig)); + req->pivot->r404_cnt = 1; + + } else { + + if (!same_page(&res->sig, &req->pivot->r404[0])) { + DEBUG("* Signature does not match previous responses, whoops.\n"); + req->pivot->res_varies = 1; + problem(PROB_VARIES, req, res, 0, req->pivot, 0); + } + return 0; + } + req->pivot->state = PSTATE_PAR_CHECK; + + return 0; +} + +/* Request this same URL with different user agents. Detect if the + response is different so that additional injection tests can be + scheduled. */ + +static u8 agent_behavior_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u32 i, j = 0; + + /* Schedule browser specific requests. */ + for (i=0; ibrowser) + continue; + + n = req_copy(pivot->req, pivot, 1); + n->callback = inject_state_manager; + n->browser = browser_types[i]; + n->user_val = j++; + async_request(n); + } + + return 0; +} + +static u8 agent_behavior_check(struct http_request* req, + struct http_response* res) { + + u32 i; + DEBUG_MISC_CALLBACK(req, res); + + for (i=0; isig, &MRES(i)->sig)) { + req->pivot->browsers |= MREQ(i)->browser; + maybe_add_pivot(MREQ(i),NULL, 2); + } + } + + return 0; +} + + + + +static u8 param_ognl_tests(struct pivot_desc* pivot) { + + struct http_request* n; + u32 ret = 1; + u8* tmp; + + /* All probes failed? Assume bogus parameter, what else to do... */ + + if (!pivot->r404_cnt) + pivot->bogus_par = 1; + + /* If the parameter has an effect, schedule OGNL checks. */ + + if (!pivot->bogus_par && !pivot->res_varies && + pivot->req->par.n[pivot->fuzz_par]) { + + n = req_copy(pivot->req, pivot, 1); + tmp = ck_alloc(strlen((char*)n->par.n[pivot->fuzz_par]) + 8); + sprintf((char*)tmp, "[0]['%s']", n->par.n[pivot->fuzz_par]); + ck_free(n->par.n[pivot->fuzz_par]); + n->par.n[pivot->fuzz_par] = tmp; + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + ck_free(n->par.n[pivot->fuzz_par]); + n->par.n[pivot->fuzz_par] = ck_strdup((u8*)"[0]['sfish']"); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + ret = 0; + } + + /* Injection attacks should be carried out even if we think this + parameter has no visible effect; but injection checks will not proceed + to dictionary fuzzing if bogus_par or res_varies is set. */ + + pivot->state = PSTATE_PAR_INJECT; + + return ret; + +} + +static u8 param_ognl_check(struct http_request* req, + struct http_response* res) { + + DEBUG_MISC_CALLBACK(req, res); + + /* First response is meant to give the same result. Second + is meant to give a different one. */ + + if (same_page(&MREQ(0)->pivot->res->sig, &MRES(0)->sig) && + !same_page(&MREQ(1)->pivot->res->sig, &MRES(1)->sig)) { + + problem(PROB_OGNL, req, res, + (u8*)"response to [0]['name']=... identical to name=...", + req->pivot, 0); + } + + return 0; +} + + +static u8 dir_ips_tests(struct pivot_desc* pivot) { + + struct http_request* n; + + pivot->state = PSTATE_IPS_CHECK; + + n = req_copy(pivot->req, pivot, 1); + tokenize_path((u8*)IPS_TEST, n, 0); + n->callback = inject_state_manager; + n->user_val = 0; + async_request(n); + + n = req_copy(pivot->req, pivot, 1); + tokenize_path((u8*)IPS_SAFE, n, 0); + n->callback = inject_state_manager; + n->user_val = 1; + async_request(n); + + return 0; + +} + +static u8 dir_ips_check(struct http_request* req, + struct http_response* res) { + struct pivot_desc* par; + + DEBUG_MISC_CALLBACK(req, res); + + par = dir_parent(req->pivot); + + if (!par || !par->uses_ips) { + + if (MRES(0)->state != STATE_OK) + problem(PROB_IPS_FILTER, MREQ(0), MRES(0), + (u8*)"request timed out (could also be a flaky server)", + req->pivot, 0); + else if (!same_page(&MRES(0)->sig, &MRES(1)->sig)) + problem(PROB_IPS_FILTER, MREQ(0), MRES(0), NULL, req->pivot, 0); + + } else { + + if (MRES(0)->state == STATE_OK && same_page(&MRES(0)->sig, &MRES(1)->sig)) + problem(PROB_IPS_FILTER_OFF, MREQ(0), MRES(0), NULL, req->pivot, 0); + + } + + destroy_misc_data(req->pivot, req); + req->pivot->state = PSTATE_CHILD_INJECT; + return 0; +} diff -Nru skipfish-2.02b/src/checks.h skipfish-2.10b/src/checks.h --- skipfish-2.02b/src/checks.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/checks.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,118 @@ +#ifndef _HAVE_CHECKS_H +#include "types.h" +#include "http_client.h" +#include "database.h" + +/* The init crawler structure which loads the test/check combos */ + +void init_injection_checks(void); + +/* The crawler structure helper functions */ + +void display_injection_checks(void); +void release_injection_checks(void); +void toggle_injection_checks(u8* str, u32 enable, u8 user); + +extern u8 no_checks; + +/* The inject state manager callback function is used in crawler.c to + direct the flow to the state manager where all the injection tests are + performed. */ + +u8 inject_state_manager(struct http_request* req, struct http_response* res); + +/* Check identifiers which can be used by other parts of code to + see what the current *check* is. One specific location where this is + used is the signature matching code, */ + +#define CHK_GENERIC 0 +#define CHK_XML 1 +#define CHK_XSS 2 +#define CHK_SHELL_DIFF 3 +#define CHK_SHELL_SPEC 4 +#define CHK_SESSION 5 +#define CHK_DIR_LIST 6 +#define CHK_PUT 7 +#define CHK_LFI 8 +#define CHK_RFI 9 +#define CHK_XSSI 10 +#define CHK_PROLOG 11 +#define CHK_REDIR 12 +#define CHK_SQL 13 +#define CHK_FORMAT 14 +#define CHK_INTEGER 15 +#define CHK_OGNL 16 +#define CHK_BEHAVE 17 +#define CHK_IPS 18 +#define CHK_RSPLIT 19 +#define CHK_AGENT 20 + +#ifdef _VIA_CHECKS_C + +/* Time attack knobs */ + +#define MAX_RES_DURATION 3 +#define SLEEP_TEST_ONE 3 +#define SLEEP_TEST_TWO 5 + +/* Browsers for behavior testing */ + +#define BROWSER_TYPE_CNT 4 + +u32 browser_types[BROWSER_TYPE_CNT] = { + BROWSER_FAST, + BROWSER_MSIE, + BROWSER_FFOX, + BROWSER_PHONE +}; + +/* Helper for calculating the request time */ + +#define RTIME(_r) (MREQ(_r)->end_time - MREQ(_r)->start_time) + +/* The test/check struct with pointers to callback functions */ + +struct cb_handle { + u32 res_num; /* Amount of expected responses */ + u32 res_keep; /* Bool for keeping req/res */ + u8 allow_varies; /* Bool to accept pivots with res_varies */ + u8 time_sensitive; /* Bool for time sensitive tests */ + u8 scrape; /* Scrape links, or not.. */ + u32 pv_flag; /* Flag to match pivot type */ + u32 id; /* Flag to match pivot type */ + u8* name; /* Name or title of the check */ + + u8 (*tests)(struct pivot_desc* pivot); + u8 (*checks)(struct http_request*, struct http_response*); + + u32 skip; /* Bool to disable the check */ +}; + +/* Strings for traversal and file disclosure tests. The order should + not be changed */ + + +struct lfi_test { + const char *vectors[10]; + const char *test_string; + const char *description; +}; + +#define MAX_LFI_INDEX 2 +struct lfi_test lfi_tests[] = { + {{"/../../../../../../../../../etc/hosts", + "file:///etc/hosts", 0 + }, "127.0.0.1", "File /etc/hosts was disclosed." }, + + {{"/../../../../../../../../../etc/passwd", + "file:///etc/passwd", 0 + }, "root:x:0:0:root", "File /etc/passwd was disclosed."}, + + {{"..\\..\\..\\..\\..\\..\\..\\..\\boot.ini", + "file:///boot.ini", 0 + }, "[boot loader]", "File boot.ini was disclosed."}, + +}; + +#endif /* _VIA_CHECKS_C */ +#endif /* _HAVE_CHECKS_H */ diff -Nru skipfish-2.02b/src/config.h skipfish-2.10b/src/config.h --- skipfish-2.02b/src/config.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/config.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,281 @@ +/* + skipfish - configurable settings + -------------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#ifndef _HAVE_CONFIG_H +#define _HAVE_CONFIG_H + +#define USE_COLOR 1 /* Use terminal colors */ + +#define SHOW_SPLASH 1 /* Annoy user with a splash screen */ + +/* Default paths to runtime files: */ + +#define ASSETS_DIR "assets" + +/* Default signature file */ +#define SIG_FILE "signatures/signatures.conf" + +/* Various default settings for HTTP client (cmdline override): */ + +#define MAX_CONNECTIONS 40 /* Simultaneous connection cap */ +#define MAX_CONN_HOST 10 /* Per-host connction cap */ +#define MAX_REQUESTS 1e8 /* Total request count cap */ +#define MAX_REQUESTS_SEC 0.0 /* Max requests per second */ +#define MAX_FAIL 100 /* Max consecutive failed requests */ +#define RW_TMOUT 10 /* Individual network R/W timeout */ +#define RESP_TMOUT 20 /* Total request time limit */ +#define IDLE_TMOUT 10 /* Connection tear down threshold */ +#define SIZE_LIMIT 400000 /* Response size cap */ +#define MAX_GUESSES 256 /* Guess-based wordlist size limit */ + +/* HTTP client constants: */ + +#define MAX_URL_LEN 2048 /* Maximum length of an URL */ +#define MAX_DNS_LEN 255 /* Maximum length of a host name */ +#define READ_CHUNK 4096 /* Read buffer size */ + +/* Define this to use FILO, rather than FIFO, scheduling for new requests. + FILO ensures a more uniform distribution of requests when fuzzing multiple + directories at once, but may reduce the odds of spotting some stored + XSSes, and increase memory usage a bit. */ + +// #define QUEUE_FILO 1 + +/* Define this to enable experimental HTTP proxy support, through the -J + option in the command line. This mode will not work as expected for + HTTPS requests at this time. */ + +// #define PROXY_SUPPORT 1 + +/* Dummy file to upload to the server where possible. */ + +#define DUMMY_EXT "gif" +#define DUMMY_FILE "GIF89a,\x01" +#define DUMMY_MIME "image/gif" + +/* Allocator settings: */ + +#define MAX_ALLOC 0x50000000 /* Refuse larger allocations. */ + +/* Detect use-after-free, at the expense of some performance cost: */ + +#ifdef DEBUG_ALLOCATOR +#define CHECK_UAF 1 +#endif /* DEBUG_ALLOCATOR */ + +/* Configurable settings for crawl database (cmdline override): */ + +#define MAX_DEPTH 16 /* Maximum crawl tree depth */ +#define MAX_CHILDREN 512 /* Maximum children per tree node */ +#define MAX_DESCENDANTS 8192 /* Maximum descendants per branch */ +#define MAX_SAMENAME 3 /* Identically named path nodes */ + +/* Crawl / analysis constants: */ + +#define MAX_WORD 64 /* Maximum wordlist item length */ +#define GUESS_PROB 50 /* Guess word addition probability */ +#define WORD_HASH 256 /* Hash table for wordlists */ +#define SNIFF_LEN 1024 /* MIME sniffing buffer size */ +#define MAX_SAMPLES 1024 /* Max issue / MIME samples */ +#define MAX_JS_WHITE 16 /* Maximum JS wspaces before id */ + +/* Page fingerprinting constants: */ + +#define FP_SIZE 10 /* Page fingerprint size */ +#define FP_MAX_LEN 15 /* Maximum word length to count */ +#define FP_T_REL 5 /* Relative matching tolerance (%) */ +#define FP_T_ABS 6 /* Absolute matching tolerance */ +#define FP_B_FAIL 3 /* Max number of failed buckets */ + +#define BH_CHECKS 15 /* Page verification check count */ + +/* Crawler / probe constants: */ + +#define BOGUS_FILE "sfi9876" /* Name that should not exist */ +#define BOGUS_EXT "sfish" /* Nonsensical file extension */ +#define BOGUS_PARAM "9876sfi" /* Meaningless parameter */ +#define MAX_404 4 /* Maximum number of 404 sigs */ +#define PAR_MAX_DIGITS 6 /* Max digits in a fuzzable int */ +#define PAR_INT_FUZZ 100 /* Fuzz by + / - this much */ + +#ifdef QUEUE_FILO +#define DICT_BATCH 100 /* Brute-force queue block */ +#else +#define DICT_BATCH 300 /* Brute-force queue block */ +#endif /* ^QUEUE_FILO */ + +/* Single query for IPS detection - Evil Query of Doom (tm). */ + +#define IPS_TEST \ + "?_test1=c:\\windows\\system32\\cmd.exe" \ + "&_test2=/etc/passwd" \ + "&_test3=|/bin/sh" \ + "&_test4=(SELECT * FROM nonexistent) --" \ + "&_test5=>/no/such/file" \ + "&_test6=" \ + "&_test7=javascript:alert(1)" + +/* A benign query with a similar character set to compare with EQoD. */ + +#define IPS_SAFE \ + "?_test1=ccddeeeimmnossstwwxy.:\\\\\\" \ + "&_test2=acdepsstw//" \ + "&_test3=bhins//" \ + "&_test4=CEEFLMORSTeeinnnosttx-*" \ + "&_test5=cefhilnosu///" \ + "&_test6=acceiilpprrrssttt1)(" \ + "&_test7=aaaceijlprrsttv1):(" + +/* XSRF token detector settings: */ + +#define XSRF_B16_MIN 8 /* Minimum base10/16 token length */ +#define XSRF_B16_MAX 45 /* Maximum base10/16 token length */ +#define XSRF_B16_NUM 2 /* ...minimum digit count */ +#define XSRF_B64_MIN 6 /* Minimum base32/64 token length */ +#define XSRF_B64_MAX 32 /* Maximum base32/64 token length */ +#define XSRF_B64_NUM 1 /* ...minimum digit count && */ +#define XSRF_B64_CASE 2 /* ...minimum uppercase count */ +#define XSRF_B64_NUM2 3 /* ...digit count override */ +#define XSRF_B64_SLASH 2 /* ...maximum slash count */ + +#ifdef _VIA_CHECKS_C + +/* The URL and string we use in the RFI test (disabled by default) */ + +#define RFI_HOST "http://www.google.com/humans.txt#foo=" +#define RFI_STRING "we can shake a stick" + + +#endif /* _VIA_CHECKS_C */ + +#ifdef _VIA_DATABASE_C + +/* Domains we always trust (identical to -B options). These entries do not + generate cross-domain content inclusion warnings. NULL-terminated. */ + +static const char* always_trust_domains[] = { + ".google-analytics.com", + ".googleapis.com", + ".googleadservices.com", + ".googlesyndication.com", + "www.w3.org", + 0 +}; + +#endif /* _VIA_DATABASE_C */ + +#ifdef _VIA_ANALYSIS_C + +/* NULL-terminated list of JSON-like response prefixes we consider to + be sufficiently safe against cross-site script inclusion (courtesy + ratproxy). */ + +static const char* json_safe[] = { + "while(1);", /* Parser looping */ + "while (1);", /* ... */ + "while(true);", /* ... */ + "while (true);", /* ... */ + "&&&", /* Parser breaking */ + "//OK[", /* Line commenting */ + "{\"", /* Serialized object */ + "{{\"", /* Serialized object */ + "throw 1; <", /* Magical combo */ + ")]}'", /* Recommended magic */ + 0 +}; + +/* NULL-terminated list of known valid charsets. Charsets not on the list are + considered dangerous (as they may trigger charset sniffing). + + Note that many common misspellings, such as "utf8", are not valid and NOT + RECOGNIZED by browsers, leading to content sniffing. Do not add them here. + + Also note that SF does not support encoding not compatible with US ASCII + transport (e.g., UTF-16, UTF-32). Lastly, variable-length encodings + other than utf-8 may have character consumption issues that are not + tested for at this point. */ + +static const char* valid_charsets[] = { + "utf-8", /* Valid 8-bit safe Unicode */ + "iso8859-1", /* Western Europe */ + "iso8859-2", /* Central Europe */ + "iso8859-15", /* New flavor of ISO8859-1 */ + "iso8859-16", /* New flavor of ISO8859-2 */ + "iso-8859-1", /* Browser-supported misspellings */ + "iso-8859-2", /* - */ + "iso-8859-15", /* - */ + "iso-8859-16", /* - */ + "windows-1252", /* Microsoft's Western Europe */ + "windows-1250", /* Microsoft's Central Europe */ + "us-ascii", /* Old school but generally safe */ + "koi8-r", /* 8-bit and US ASCII compatible */ + 0 +}; + + +/* Default form auto-fill rules - used to pair up form fields with fun + values! Do not attempt security attacks here, though - this is to maximize + crawl coverage, not to exploit anything. The last item must have a name + of NULL, and the value will be used as a default option when no other + matches found. */ + +static const char* form_suggestion[][2] = { + + { "phone" , "6505550100" }, /* Reserved */ + { "zip" , "94043" }, + { "first" , "John" }, + { "last" , "Smith" }, + { "name" , "Smith" }, + { "mail" , "skipfish@example.com" }, + { "street" , "1600 Amphitheatre Pkwy" }, + { "city" , "Mountain View" }, + { "state" , "CA" }, + { "country" , "US" }, + { "language" , "en" }, + { "company" , "ACME" }, + { "search" , "skipfish" }, + { "login" , "skipfish" }, + { "user" , "skipfish" }, + { "nick" , "skipfish" }, + { "pass" , "skipfish" }, + { "pwd" , "skipfish" }, + { "year" , "2010" }, + { "card" , "4111111111111111" }, /* Reserved */ + { "code" , "000" }, + { "cvv" , "000" }, + { "expir" , "1212" }, + { "ssn" , "987654320" }, /* Reserved */ + { "url" , "http://example.com/?sfish_form_test" }, + { "site" , "http://example.com/?sfish_form_test" }, + { "domain" , "example.com" }, + { "search" , "a" }, + { "comment" , "skipfish" }, + { "desc" , "skipfish" }, + { "title" , "skipfish" }, + { "subject" , "skipfish" }, + { "message" , "skipfish" }, + { NULL , "1" } + +}; + +#endif /* _VIA_ANALYSIS_C */ +#endif /* ! _HAVE_CONFIG_H */ diff -Nru skipfish-2.02b/src/crawler.c skipfish-2.10b/src/crawler.c --- skipfish-2.02b/src/crawler.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/crawler.c 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,2002 @@ +/* + skipfish - crawler state machine + -------------------------------- + + Includes dictionary and security injection logic. + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#define _VIA_CRAWLER_C + +#include "debug.h" +#include "config.h" +#include "types.h" +#include "http_client.h" +#include "database.h" +#include "crawler.h" +#include "checks.h" +#include "analysis.h" + +u32 crawl_prob = 100; /* Crawl probability (1-100%) */ +u8 no_fuzz_ext; /* Don't fuzz extensions for dirs */ +u8 no_500_dir; /* Don't crawl 500 directories */ +u8 delete_bin; /* Don't keep binary responses */ +u8 flush_pivot_data; /* Flush pivot data to disk */ + +/* + + ************************* + **** GENERAL HELPERS **** + ************************* + + Assorted functions used by all the crawl callbacks for manipulating + requests, parsing responses, etc. + + */ + + +/* Dumps request, response (for debugging only). */ + +u8 show_response(struct http_request* req, struct http_response* res) { + + dump_http_request(req); + + if (FETCH_FAIL(res)) { + SAY("^^^ REQUEST SHOWN ABOVE CAUSED ERROR: %d ^^^\n", res->state); + return 0; + } + + dump_http_response(res); + + return 0; /* Do not keep req/res */ + +} + + +/* Strips trailing / from a directory request, optionally replaces it with + a new value. */ + +void replace_slash(struct http_request* req, u8* new_val) { + u32 i; + + for (i=0;ipar.c;i++) + if (req->par.t[i] == PARAM_PATH && !req->par.n[i] && !req->par.v[i][0]) { + if (new_val) { + ck_free(req->par.v[i]); + req->par.v[i] = ck_strdup(new_val); + } else req->par.t[i] = PARAM_NONE; + return; + } + + /* Could not find a slash segment - create a new segment instead. */ + + set_value(PARAM_PATH, 0, new_val, -1, &req->par); + +} + + +/* Releases children for crawling (called once parent node had 404, IPS + probes done, etc). Note that non-directories might have locked + children too. */ + +static void unlock_children(struct pivot_desc* pv) { + u32 i; + + DEBUG_HELPER(pv); + + for (i=0;ichild_cnt;i++) + if (pv->child[i]->state == PSTATE_PENDING) { + + DEBUG_PIVOT("Unlocking", pv->child[i]); + + pv->child[i]->state = PSTATE_FETCH; + + if (!pv->child[i]->res) async_request(pv->child[i]->req); + else switch (pv->child[i]->type) { + + case PIVOT_DIR: dir_retrieve_check(pv->req, pv->res); break; + case PIVOT_PARAM: + case PIVOT_FILE: file_retrieve_check(pv->req, pv->res); break; + case PIVOT_UNKNOWN: unknown_retrieve_check(pv->req, pv->res); break; + default: FATAL("Unknown pivot type '%u'", pv->type); + + } + + } + +} + + +/* Handles response error for callbacks in a generalized manner. If 'stop' is + 1, marks the entire pivot as busted, unlocks children. */ + +void handle_error(struct http_request* req, struct http_response* res, + u8* desc, u8 stop) { + + DEBUG_CALLBACK(req, res); + + if (res->state == STATE_SUPPRESS) { + problem(PROB_LIMITS, req, res, (u8*)"Too many previous fetch failures", + req->pivot, 0); + } else { + problem(PROB_FETCH_FAIL, req, res, desc, req->pivot, 0); + } + + if (stop) { + req->pivot->state = PSTATE_DONE; + unlock_children(req->pivot); + } + +} + + +/* Finds nearest "real" directory parent, so that we can consult it for 404 + signatures, etc. Return NULL also if dir found, but signature-less. */ + +struct pivot_desc* dir_parent(struct pivot_desc* pv) { + struct pivot_desc* ret; + + ret = pv->parent; + + while (ret && ret->type != PIVOT_DIR && ret->type != PIVOT_SERV) + ret = ret->parent; + + if (ret && !ret->r404_cnt) return NULL; + return ret; +} + + +/* Deletes any cached requests and responses stored by injection probes. */ + +void destroy_misc_data(struct pivot_desc* pv, + struct http_request* self) { + u32 i; + + for (i=0;imisc_req[i] != self) { + + if (pv->misc_req[i]) + destroy_request(pv->misc_req[i]); + + if (pv->misc_res[i]) + destroy_response(pv->misc_res[i]); + + } + + pv->misc_req[i] = NULL; + pv->misc_res[i] = NULL; + + } + + pv->misc_cnt = 0; + +} + + + +/* + + *************************************** + **** ASSORTED FORWARD DECLARATIONS **** + *************************************** + + */ + +/* Flow control functions */ +static void inject_start(struct pivot_desc*); +static void param_start(struct pivot_desc*); +static void dir_dict_start(struct pivot_desc*); +static void param_numerical_start(struct pivot_desc*); +static void param_dict_start(struct pivot_desc*); +static u8 dir_case_check(struct http_request* req, struct http_response* res); +static u8 dir_dict_check(struct http_request*, struct http_response*); +static u8 dir_dict_bogus_check(struct http_request*, struct http_response*); +static u8 dir_404_check(struct http_request*, struct http_response*); +static u8 dir_up_behavior_check(struct http_request*, struct http_response*); +static u8 param_numerical_check(struct http_request*, struct http_response*); +static u8 param_dict_check(struct http_request*, struct http_response*); +static u8 param_trylist_check(struct http_request*, struct http_response*); +static u8 unknown_retrieve_check2(struct http_request*, struct http_response*); + +/* + + ****************************** + **** ACTUAL STATE MACHINE **** + ****************************** + + The following is a rough sketch of what's going on here. + + == Pivot creation states == + + Path elements: + + root - PSTATE_DONE, no callback + + server - PSTATE_FETCH, dir_retrieve_check + + dir - PSTATE_FETCH, dir_retrieve_check + PSTATE_PENDING if parent state <= PSTATE_IPS_CHECK + + last seg - PSTATE_FETCH, unknown_retrieve_check + PSTATE_PENDING if parent state <= PSTATE_IPS_CHECK + + file - PSTATE_FETCH, file_retrieve_check + PSTATE_PENDING if parent state <= PSTATE_IPS_CHECK + + If element in name=value format, also add value to pivot's trylist. + Call param_trylist_start if pivot already in PSTATE_DONE. + + Query elements: + + PSTATE_FETCH, file_retrieve_check + PSTATE_PENDING if parent dir state <= PSTATE_IPS_CHECK + + Add value to pivot's trylist. Call param_trylist_start if pivot already + in PSTATE_DONE. + + == Initial fetch actions == + + unknown_retrieve_check: + + Initial retrieval of an unknown path element. + + File not found: unlock_children, -> param_behavior_tests + Otherwise: -> file_retrieve_check or -> unknown_retrieve_check2 + + unknown_retrieve_check2: + + Secondary check to detect dir-like behavior (for unknown_retrieve_check). + + -> dir_retrieve_check or -> file_retrieve_check + + file_retrieve_check: + + Initial retrieval of a file, query parameter, or so. + + -> secondary_ext_start (async) + -> dir_case_start (async) + unlock_children + + Query value pivot: -> param_behavior_tests + Other pivots: PSTATE_CHILD_INJECT, -> inject_start + + dir_retrieve_check: + + Initial retrival of a directory or PATHINFO resource. + + -> secondary_ext_start (async) + PSTATE_404_CHECK, -> dir_404_check + + == Basic directory checks == + + dir_404_check: + + Performs basic 404 signature detection. Calls itself in a loop. + + -> dir_case_start (async) + PSTATE_PARENT_CHECK, -> dir_up_behavior_check + + dir_up_behavior_check: + + Checks if path hierarchy is honored by the server. + + unlock_children + PSTATE_CHILD_INJECT, -> inject_start + + dir_case_start: + + Asynchronous handler to check directory case-sensitivity. + + -> dir_case_check + + dir_case_check: + + Case sensitivity callback. No further branching. + + == Injection tests == + + The injection checks are handled by the inject_state_manager in checks.c + but will return control to inject_done() when done. + + inject_done: + + Injection testing wrap-up. + + Path element: PSTATE_CHILD_DICT, -> dir_dict_start if fuzzable dir + -> inject_state_manager if not dir or no 404 sigs + PSTATE_DONE if not allowed or varies randomly + + Other parametric: -> param_numerical_start + PSTATE_DONE if varies randomly + + == Parameter brute-force (name=val only) == + + param_numerical_start: + + Begin numerical brute-force if applicable. + + Numerical: PSTATE_PAR_NUMBER, -> param_numerical_check + Otherwise: PSTATE_PAR_DICT, -> param_dict_start + + param_numerical_check: + + Numerical brute-force callback. May store results as PIVOT_VALUE / + PSTATE_DONE nodes. + + -> secondary_ext_start (async) + PSTATE_PAR_DICT, -> param_dict_start + + param_dict_start: + + Dictionary brute-force init / resume. + + Out of keywords: -> param_trylist_start + Otherwise: -> param_dict_check + + param_dict_check: + + Dictionary brute-force callback. May store results as PIVOT_VALUE / + PSTATE_DONE nodes. + + -> secondary_ext_start (async) + Loops to -> param_trylist_start if not called via secondary_ext_check + + param_trylist_start: + + Begins trylist fuzzing, or resumes from offset. + + Bad pivot or no more keywords: PSTATE_DONE + Otherwise: PSTATE_PAR_TRYLIST, -> param_trylist_check + + param_trylist_check: + + Trylist dictionary callback. May store results as PIVOT_VALUE / PSTATE_DONE + nodes. + + -> secondary_ext_start (async) + PSTATE_DONE + + == Directory brute-force == + + dir_dict_start: + + Dictionary brute-force init / resume. + + Bad pivot or no more keywords: -> param_behavior_tests + Otherwise: -> dir_dict_bogus_check + + dir_dict_bogus_check: + + Check for good keyword candidates, proceed with extension fuzzing. + -> dir_dict_check + + Loops over to -> dir_dict_start + + dir_dict_check: + + Dictionary brute-force callback. + + Loops over to -> dir_dict_start if not called via secondary_ext_start. + + == Secondary extension brute-force == + + secondary_ext_start: + + Asynchronous secondary extension check + + Query: -> param_dict_check + Path: -> dir_dict_check + + */ + +static void dir_case_start(struct pivot_desc* pv) { + u32 i, len; + s32 last = -1; + struct http_request* n; + + if (pv->parent->c_checked) return; + + DEBUG_HELPER(pv); + + for (i=0;ireq->par.c;i++) + if (PATH_SUBTYPE(pv->req->par.t[i]) && pv->req->par.v[i][0]) last = i; + + if (last < 0) return; + + len = strlen((char*)pv->req->par.v[last]); + + for (i=0;ireq->par.v[last][i])) break; + + if (i == len) return; + + pv->parent->c_checked = 1; + + n = req_copy(pv->req, pv, 1); + n->callback = dir_case_check; + + /* Change case. */ + + n->par.v[last][i] = islower(n->par.v[last][i]) ? toupper(n->par.v[last][i]) : + tolower(n->par.v[last][i]); + + DEBUG("* candidate parameter: %s -> %s\n", pv->req->par.v[last], + n->par.v[last]); + + async_request(n); + +} + + +static u8 dir_case_check(struct http_request* req, + struct http_response* res) { + + DEBUG_CALLBACK(req, res); + + if (FETCH_FAIL(res)) { + RPAR(req)->c_checked = 0; + return 0; + } + + if (!same_page(&res->sig, &RPRES(req)->sig)) + RPAR(req)->csens = 1; + + return 0; + +} + + +static void secondary_ext_start(struct pivot_desc* pv, struct http_request* req, + struct http_response* res, u8 is_param) { + + u8 *base_name, *fpos, *lpos, *ex; + s32 tpar = -1, spar = -1; + u32 i = 0; + + DEBUG_HELPER(req->pivot); + DEBUG_HELPER(pv); + + if (is_param) { + + tpar = pv->fuzz_par; + + } else { + + /* Find last path segment other than NULL-''. */ + for (i=0;ipar.c;i++) + if (PATH_SUBTYPE(req->par.t[i])) { + if ((req->par.t[i] == PARAM_PATH && + !req->par.n[i] && !req->par.v[i][0])) spar = i; else tpar = i; + } + + } + + if (tpar < 0) return; + + base_name = req->par.v[tpar]; + + /* Reject parameters with no '.' (unless in no_fuzz_ext mode), + with too many '.'s, or '.' in an odd location. */ + + fpos = (u8*)strchr((char*)base_name, '.'); + + if (!no_fuzz_ext || fpos) + if (!fpos || fpos == base_name || !fpos[1]) return; + + lpos = (u8*)strrchr((char*)base_name, '.'); + + if (fpos != lpos) return; + + i = 0; + + while ((ex = wordlist_get_extension(i, 0))) { + u8* tmp = ck_alloc(strlen((char*)base_name) + strlen((char*)ex) + 2); + u32 c; + + /* Avoid foo.bar.bar. */ + + if (lpos && !strcasecmp((char*)lpos + 1, (char*)ex)) { + i++; + ck_free(tmp); + continue; + } + + sprintf((char*)tmp, "%s.%s", base_name, ex); + + /* Matching child? If yes, don't bother. */ + + for (c=0;cchild_cnt;c++) + if (!((is_c_sens(pv) ? strcmp : strcasecmp)((char*)tmp, + (char*)pv->child[c]->name))) break; + + /* Matching current node? */ + + if (pv->fuzz_par != -1 && + !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)tmp, + (char*)pv->req->par.v[pv->fuzz_par]))) c = ~pv->child_cnt; + + if (c == pv->child_cnt) { + struct http_request* n = req_copy(req, pv, 1); + + /* Remove trailing slash if present. */ + if (spar >= 0) n->par.t[spar] = PARAM_NONE; + + ck_free(n->par.v[tpar]); + n->par.v[tpar] = tmp; + + n->user_val = 1; + + memcpy(&n->same_sig, &res->sig, sizeof(struct http_sig)); + + n->callback = is_param ? param_dict_check : dir_dict_check; + /* Both handlers recognize user_val == 1 as a special indicator. */ + async_request(n); + + } else ck_free(tmp); + + i++; + } + +} + +static void inject_start(struct pivot_desc* pv) { + + DEBUG_HELPER(pv); + + /* Set the injection check counter to 0. This is important since pivots + loop through the injection tests at least twice */ + + pv->check_idx = -1; + + /* Tell the inject state manager that we like to start with our first + test by putting res = NULL */ + + inject_state_manager(pv->req, NULL); +} + + +static void param_start(struct pivot_desc* pv) { + + if (pv->fuzz_par < 0 || !url_allowed(pv->req) || !param_allowed(pv->name)) { + pv->state = PSTATE_DONE; + maybe_delete_payload(pv); + return; + } + + DEBUG_HELPER(pv); + + pv->state = PSTATE_PAR_CHECK; + pv->check_idx = -1; + inject_state_manager(pv->req, NULL); + +} + + +void inject_done(struct pivot_desc* pv) { + + if (pv->state == PSTATE_CHILD_INJECT) { + + if (url_allowed(pv->req) && !pv->res_varies) { + + if ((pv->type == PIVOT_DIR || pv->type == PIVOT_SERV) + && pv->r404_cnt && !pv->bad_parent) { + pv->state = PSTATE_CHILD_DICT; + pv->cur_key = 0; + dir_dict_start(pv); + } else { + /* Continue with parameter tests */ + param_start(pv); + } + + } else { + + pv->state = PSTATE_DONE; + maybe_delete_payload(pv); + return; + + } + + } else { + + if (pv->bogus_par || pv->res_varies) { + pv->state = PSTATE_DONE; + maybe_delete_payload(pv); + } else { + param_numerical_start(pv); + } + } +} + + +static void param_numerical_start(struct pivot_desc* pv) { + u8 *val = TPAR(pv->req), *out, fmt[16]; + u32 i, dig, tail; + s32 val_i, range_st, range_en; + u8 zero_padded = 0; + + DEBUG_HELPER(pv); + + if (!descendants_ok(pv)) goto schedule_next; + + /* Skip to the first digit, then to first non-digit. */ + + i = 0; + while (val[i] && !isdigit(val[i])) i++; + if (!val[i]) goto schedule_next; + + dig = i; + while (val[i] && isdigit(val[i])) i++; + tail = i; + + /* Too many digits is a no-go. */ + + if (tail - dig > PAR_MAX_DIGITS) goto schedule_next; + + if (val[dig] == '0' && tail - dig > 1) zero_padded = 1; + + val_i = atoi((char*)val + dig); + range_st = val_i - PAR_INT_FUZZ; + range_en = val_i + PAR_INT_FUZZ; + if (range_st < 0) range_st = 0; + + if (zero_padded) sprintf((char*)fmt, "%%.%us%%0%uu%%s", dig, tail - dig); + else sprintf((char*)fmt, "%%.%us%%%uu%%s", dig, tail - dig); + + out = ck_alloc(strlen((char*)val) + 16); + + /* Let's roll! */ + + pv->state = PSTATE_PAR_NUMBER; + + pv->num_pending = range_en - range_st + 1; + + for (i=range_st;i<=range_en;i++) { + struct http_request* n; + + if (i == val_i) { pv->num_pending--; continue; } + + sprintf((char*)out, (char*)fmt, val, i, val + tail); + + n = req_copy(pv->req, pv, 1); + ck_free(TPAR(n)); + TPAR(n) = ck_strdup((u8*)out); + n->callback = param_numerical_check; + async_request(n); + + } + + ck_free(out); + + if (!pv->num_pending) goto schedule_next; + return; + +schedule_next: + + pv->state = PSTATE_PAR_DICT; + param_dict_start(pv); + + /* Pew pew! */ + +} + + +static u8 param_numerical_check(struct http_request* req, + struct http_response* res) { + struct pivot_desc *par, *n = NULL, *orig_pv = req->pivot; + u32 i; + + DEBUG_CALLBACK(req, res); + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during numerical brute-force tests", 0); + goto schedule_next; + } + + /* Looks like parent, or like its 404 signature? */ + + if (same_page(&res->sig, &req->pivot->r404[0]) || + same_page(&res->sig, &req->pivot->res->sig)) + goto schedule_next; + + par = dir_parent(req->pivot); + + /* Check with parent if sigs available, but if not - no biggie. */ + + if (par) + for (i=0;ir404_cnt;i++) + if (same_page(&res->sig, &par->r404[i])) goto schedule_next; + + /* Matching child? If yes, don't bother. */ + + for (i=0;ipivot->child_cnt;i++) + if (req->pivot->child[i]->type == PIVOT_VALUE && + !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)TPAR(req), + (char*)req->pivot->child[i]->name))) goto schedule_next; + + if (!descendants_ok(req->pivot)) goto schedule_next; + + /* Hmm, looks like we're onto something. Let's manually create a dummy + pivot and attach it to current node, without any activity planned. + Attach any response notes to that pivot. */ + + n = ck_alloc(sizeof(struct pivot_desc)); + + n->type = PIVOT_VALUE; + n->state = PSTATE_DONE; + n->name = ck_strdup(TPAR(req)); + n->req = req; + n->res = res; + n->fuzz_par = req->pivot->fuzz_par; + n->parent = req->pivot; + + DEBUG("--- New pivot (value): %s ---\n", n->name); + + req->pivot->child = ck_realloc(req->pivot->child, (req->pivot->child_cnt + 1) + * sizeof(struct pivot_desc*)); + + req->pivot->child[req->pivot->child_cnt++] = n; + + add_descendant(req->pivot); + + req->pivot = n; + + secondary_ext_start(orig_pv, req, res, 1); + + maybe_delete_payload(n); + +schedule_next: + + RESP_CHECKS(req, res); + + if (!(--(orig_pv->num_pending))) { + orig_pv->state = PSTATE_PAR_DICT; + param_dict_start(orig_pv); + } + + /* Copied over to pivot. */ + return n ? 1 : 0; + +} + + +static void param_dict_start(struct pivot_desc* pv) { + static u8 in_dict_init; + struct http_request* n; + u8 *kw, *ex; + u32 i, c; + u8 specific; + + /* Too many requests still pending, or already done? */ + + if (in_dict_init || pv->pdic_pending > DICT_BATCH || + pv->state != PSTATE_PAR_DICT) return; + + DEBUG_HELPER(pv); + +restart_dict: + + if (!descendants_ok(pv)) { + param_trylist_start(pv); + return; + } + + i = 0; + + kw = (pv->pdic_guess ? wordlist_get_guess : wordlist_get_word) + (pv->pdic_cur_key, &specific); + + if (!kw) { + + /* No more keywords. Move to guesswords if not there already, or + advance to try list otherwise. */ + + if (pv->pdic_guess) { param_trylist_start(pv); return; } + + pv->pdic_guess = 1; + pv->pdic_cur_key = 0; + goto restart_dict; + + } + + /* Use crawl_prob/100 dictionary entries. */ + + if (R(100) < crawl_prob) { + + /* Schedule extension-less probe, if the keyword is not + on the child list. */ + + for (c=0;cchild_cnt;c++) + if (pv->type == PIVOT_VALUE && + !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)kw, + (char*)pv->child[c]->name))) break; + + /* ...and does not match the node itself. */ + + if (pv->fuzz_par != -1 && + !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)kw, + (char*)pv->req->par.v[pv->fuzz_par]))) c = ~pv->child_cnt; + + if (c == pv->child_cnt) { + n = req_copy(pv->req, pv, 1); + ck_free(TPAR(n)); + TPAR(n) = ck_strdup(kw); + n->callback = param_dict_check; + n->user_val = 0; + pv->pdic_pending++; + in_dict_init = 1; + async_request(n); + in_dict_init = 0; + } + + /* Schedule probes for all extensions for the current word, but + only if the original parameter contained '.' somewhere, + and only if string is not on the try list. Special handling + for specific keywords with '.' inside. */ + + if (!no_fuzz_ext && strchr((char*)TPAR(pv->req), '.')) + while ((ex = wordlist_get_extension(i, specific))) { + + u8* tmp = ck_alloc(strlen((char*)kw) + strlen((char*)ex) + 2); + + sprintf((char*)tmp, "%s.%s", kw, ex); + + for (c=0;cchild_cnt;c++) + if (pv->type == PIVOT_VALUE && + !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)tmp, + (char*)pv->child[c]->name))) break; + + if (pv->fuzz_par != -1 && + !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)tmp, + (char*)pv->req->par.v[pv->fuzz_par]))) c = ~pv->child_cnt; + + if (c == pv->child_cnt) { + n = req_copy(pv->req, pv, 1); + ck_free(TPAR(n)); + TPAR(n) = tmp; + n->user_val = 0; + n->callback = param_dict_check; + pv->pdic_pending++; + in_dict_init = 1; + async_request(n); + in_dict_init = 0; + } else ck_free(tmp); + + i++; + } + + } + + pv->pdic_cur_key++; + + if (pv->pdic_pending < DICT_BATCH) goto restart_dict; + +} + + +static u8 param_dict_check(struct http_request* req, + struct http_response* res) { + struct pivot_desc *par, *n = NULL, *orig_pv = req->pivot; + u8 keep = 0; + u32 i; + + DEBUG_CALLBACK(req, res); + + if (!req->user_val) + req->pivot->pdic_pending--; + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during parameter brute-force tests", 0); + goto schedule_next; + } + + /* Same as parent or parent's 404? Don't bother. */ + + if (same_page(&res->sig, &req->pivot->r404[0]) || + same_page(&res->sig, &RPRES(req)->sig)) goto schedule_next; + + par = dir_parent(req->pivot); + + if (par) + for (i=0;ir404_cnt;i++) + if (same_page(&res->sig, &par->r404[i])) goto schedule_next; + + /* Matching child? If yes, don't bother. */ + + for (i=0;ipivot->child_cnt;i++) + if (req->pivot->child[i]->type == PIVOT_VALUE && + !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)TPAR(req), + (char*)req->pivot->child[i]->name))) goto schedule_next; + + if (!descendants_ok(req->pivot)) goto schedule_next; + + n = ck_alloc(sizeof(struct pivot_desc)); + + n->type = PIVOT_VALUE; + n->state = PSTATE_DONE; + n->name = ck_strdup(TPAR(req)); + n->req = req; + n->res = res; + n->fuzz_par = req->pivot->fuzz_par; + n->parent = req->pivot; + + DEBUG("--- New pivot (value): %s ---\n", n->name); + + req->pivot->child = ck_realloc(req->pivot->child, (req->pivot->child_cnt + 1) + * sizeof(struct pivot_desc*)); + + req->pivot->child[req->pivot->child_cnt++] = n; + + add_descendant(req->pivot); + + req->pivot = n; + + keep = 1; + + if (!req->user_val) + secondary_ext_start(orig_pv, req, res, 1); + + maybe_delete_payload(n); + +schedule_next: + + RESP_CHECKS(req, res); + + if (!req->user_val) + param_dict_start(orig_pv); + + return keep; + +} + + +void param_trylist_start(struct pivot_desc* pv) { + u32 i; + + /* If the parameter does not seem to be doing anything, there is + no point in going through the try list if restarted. */ + + if (pv->fuzz_par == -1 || pv->bogus_par || pv->res_varies + || !descendants_ok(pv)) { + + pv->state = PSTATE_DONE; + maybe_delete_payload(pv); + + return; + } else + pv->state = PSTATE_PAR_TRYLIST; + + DEBUG_HELPER(pv); + + for (i=pv->try_cur;itry_cnt;i++) { + u32 c; + + /* If we already have a child by this name, don't poke it again. */ + + for (c=0;cchild_cnt;c++) + if (!((is_c_sens(pv) ? strcmp : strcasecmp)((char*)pv->try_list[i], + (char*)pv->child[c]->name))) continue; + + /* Matching current node? Ditto. */ + + if (pv->fuzz_par != -1 && + !((is_c_sens(pv) ? strcmp : strcasecmp)((char*)pv->try_list[i], + (char*)pv->req->par.v[pv->fuzz_par]))) continue; + + if (c == pv->child_cnt) { + + if (R(100) < crawl_prob) { + struct http_request* n; + + pv->try_pending++; + + n = req_copy(pv->req, pv, 1); + ck_free(TPAR(n)); + TPAR(n) = ck_strdup(pv->try_list[i]); + n->callback = param_trylist_check; + async_request(n); + } + + } else { + if (!pv->child[c]->linked) pv->child[c]->linked = 1; + } + + } + + pv->try_cur = i; + + if (!pv->try_pending) { + pv->state = PSTATE_DONE; + maybe_delete_payload(pv); + return; + } + +} + + +static u8 param_trylist_check(struct http_request* req, + struct http_response* res) { + struct pivot_desc *par, *n = NULL; + struct pivot_desc* orig_pv = req->pivot; + u32 i; + + DEBUG_CALLBACK(req, res); + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during try list fetches", 0); + goto schedule_next; + } + + /* Same as parent or parent's 404? Don't bother. */ + + if (same_page(&res->sig, &req->pivot->r404[0]) || + same_page(&res->sig, &RPRES(req)->sig)) goto schedule_next; + + par = dir_parent(req->pivot); + + if (par) + for (i=0;ir404_cnt;i++) + if (same_page(&res->sig, &par->r404[i])) goto schedule_next; + + /* Name matching known child? If yes, don't bother. */ + + for (i=0;ipivot->child_cnt;i++) + if (req->pivot->child[i]->type == PIVOT_VALUE && + !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)TPAR(req), + (char*)req->pivot->child[i]->name))) goto schedule_next; + + if (!descendants_ok(req->pivot)) goto schedule_next; + + n = ck_alloc(sizeof(struct pivot_desc)); + + n->type = PIVOT_VALUE; + n->state = PSTATE_DONE; + n->name = ck_strdup(TPAR(req)); + n->req = req; + n->res = res; + n->fuzz_par = req->pivot->fuzz_par; + n->parent = req->pivot; + + DEBUG("--- New pivot (value): %s ---\n", n->name); + + req->pivot->child = ck_realloc(req->pivot->child, (req->pivot->child_cnt + 1) + * sizeof(struct pivot_desc*)); + + req->pivot->child[req->pivot->child_cnt++] = n; + + add_descendant(req->pivot); + + req->pivot = n; + + secondary_ext_start(orig_pv, req, res, 1); + + maybe_delete_payload(n); + +schedule_next: + + RESP_CHECKS(req, res); + + if (!(--(orig_pv->try_pending))) { + orig_pv->state = PSTATE_DONE; + maybe_delete_payload(orig_pv); + } + + /* Copied over to pivot. */ + return n ? 1 : 0; + +} + + +u8 file_retrieve_check(struct http_request* req, struct http_response* res) { + u32 i = 0; + struct pivot_desc* par; + + RPRES(req) = res; + DEBUG_CALLBACK(req, res); + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during initial file fetch", 1); + return 1; + } + + /* Matches parent's 404? */ + + par = dir_parent(req->pivot); + + if (par) + for (i=0;ir404_cnt;i++) + if (same_page(&res->sig, &par->r404[i])) break; + + /* If no signatures on parents, fall back to a basic 404 check, it's + the least we could do. */ + + if ((!par && res->code == 404) || (par && i != par->r404_cnt)) { + + RESP_CHECKS(req, res); + req->pivot->missing = 1; + + } else { + + if (res->code > 400) + problem(PROB_NO_ACCESS, req, res, NULL, req->pivot, 0); + + /* Do not bother with checks on files or params if + content identical to parent. */ + + if (!RPAR(req)->res || !same_page(&res->sig, &RPAR(req)->res->sig)) { + RESP_CHECKS(req, res); + if (par && req->pivot->type != PIVOT_PARAM) + secondary_ext_start(par, req, res, 0); + } + + if (req->pivot->type == PIVOT_FILE) + dir_case_start(req->pivot); + + } + + /* On non-param nodes, we want to proceed with path-based injection + checks. On param nodes, we want to proceed straght to parametric + testng, instead. */ + + unlock_children(req->pivot); + + if (req->pivot->type == PIVOT_PARAM) { + param_start(req->pivot); + } else { + req->pivot->state = PSTATE_CHILD_INJECT; + inject_start(req->pivot); + } + + + /* This is the initial callback, keep the response. */ + return 1; + +} + + +u8 dir_retrieve_check(struct http_request* req, struct http_response* res) { + struct http_request* n; + struct pivot_desc* par; + RPRES(req) = res; + + DEBUG_CALLBACK(req, res); + + + /* Error at this point means we should give up on other probes in this + directory. */ + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during initial directory fetch", 1); + return 1; + } + + if (req->pivot->type == PIVOT_SERV) + PIVOT_CHECKS(req, res); + + /* The next step is checking 404 responses for all extensions (starting + with an empty one), which would also determine if the directory exists + at all, etc. We make an exception for server pivot, though, which is + presumed to be a directory (so we do PIVOT_CHECKS right away). */ + + req->pivot->state = PSTATE_404_CHECK; + n = req_copy(req, req->pivot, 1); + replace_slash(n, (u8*)BOGUS_FILE); + + n->user_val = 0; + n->callback = dir_404_check; + + req->pivot->r404_pending++; + + async_request(n); + + par = dir_parent(req->pivot); + if (par) secondary_ext_start(par, req, res, 0); + + /* Header, response belong to pivot - keep. */ + return 1; +} + + +static u8 dir_404_check(struct http_request* req, + struct http_response* res) { + + struct http_request* n; + u32 i; + s32 ppval = -1, pval = -1, val = -1; + + DEBUG_CALLBACK(req, res); + + if (req->pivot->r404_skip) goto schedule_next; + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during 404 response checks", 0); + goto schedule_next; + } + + /* If the first 404 probe returned something that looks like the + "root" page for the currently tested directory, panic. But don't + do that check on server pivots, or if valid redirect detected + earlier. */ + + if (!req->user_val && !req->pivot->sure_dir && + req->pivot->type != PIVOT_SERV && RPRES(req) && + same_page(&res->sig, &RPRES(req)->sig)) { + DEBUG("* First 404 probe identical with parent!\n"); + goto schedule_next; + } else if (!req->user_val) { + DEBUG("* First 404 probe differs from parent (%d)\n", + RPRES(req) ? RPRES(req)->code : 0); + } + + /* Check if this is a new signature. */ + + for (i=0;ipivot->r404_cnt;i++) + if (same_page(&res->sig, &req->pivot->r404[i])) break; + + if (i == req->pivot->r404_cnt) { + struct pivot_desc* par; + + DEBUG("* New signature found (%u).\n", req->pivot->r404_cnt); + + /* Need to add a new one. Make sure we're not over the limit. */ + + if (req->pivot->r404_cnt >= MAX_404) { + + req->pivot->r404_skip = 1; + + problem(PROB_404_FAIL, RPREQ(req), RPRES(req), + (u8*)"too many 404 signatures found", req->pivot, 0); + + goto schedule_next; + + } + + memcpy(&req->pivot->r404[i], &res->sig, sizeof(struct http_sig)); + + req->pivot->r404_cnt++; + + /* Is this a new signature not seen on parent? Notify if so, + and check it thoroughly. */ + + par = dir_parent(req->pivot); + + if (par) { + + for (i=0;ir404_cnt;i++) + if (same_page(&res->sig, &par->r404[i])) break; + + } + + if (!par || i == par->r404_cnt) { + problem(PROB_NEW_404, req, res, NULL, req->pivot, 1); + RESP_CHECKS(req, res); + } + + } + +schedule_next: + + /* First probe OK? */ + + if (!req->user_val) { + u8* nk; + u32 cur_ext = 0; + + /* First probe should already yield a 404 signature. */ + + if (!req->pivot->r404_cnt) { + DEBUG("* First probe failed to yield a signature.\n"); + goto bad_404; + } + + DEBUG("* First probe yielded a valid signature.\n"); + + /* At this point, we can be reasonably sure the response is + meaningful. */ + + PIVOT_CHECKS(req->pivot->req, req->pivot->res); + dir_case_start(req->pivot); + + /* Aaand schedule all the remaining probes. Repeat BH_CHECKS + times to also catch random variations. */ + + while ((nk = wordlist_get_extension(cur_ext++, 0))) { + u8* tmp = ck_alloc(strlen(BOGUS_FILE) + strlen((char*)nk) + 2); + + sprintf((char*)tmp, "%s.%s", BOGUS_FILE, nk); + + for (i=0;ipivot, 1); + replace_slash(n, tmp); + n->callback = dir_404_check; + n->user_val = 1; + + /* r404_pending is at least 1 to begin with, so this is safe + even if async_request() has a synchronous effect. */ + + req->pivot->r404_pending++; + async_request(n); + + } + + ck_free(tmp); + + } + + /* Also issue 404 probe for "lpt9", as "con", "prn", "nul", "lpt#", + etc, are handled in a really annoying way by IIS. */ + + n = req_copy(RPREQ(req), req->pivot, 1); + replace_slash(n, (u8*)"lpt9"); + n->callback = dir_404_check; + n->user_val = 1; + req->pivot->r404_pending++; + async_request(n); + + /* ...and for ~user, since this sometimes has a custom response, too. */ + + n = req_copy(RPREQ(req), req->pivot, 1); + replace_slash(n, (u8*)"~" BOGUS_FILE); + n->callback = dir_404_check; + n->user_val = 1; + req->pivot->r404_pending++; + async_request(n); + + /* Lastly, make sure that directory 404 is on file. */ + + n = req_copy(RPREQ(req), req->pivot, 1); + replace_slash(n, (u8*)BOGUS_FILE); + set_value(PARAM_PATH, 0, (u8*)"", -1, &n->par); + n->callback = dir_404_check; + n->user_val = 1; + req->pivot->r404_pending++; + async_request(n); + + } + + if (--(req->pivot->r404_pending)) return 0; + + /* If we're here, all probes completed, and we had no major errors. + If no signatures gathered, try to offer useful advice. */ + +bad_404: + + if (!req->pivot->r404_cnt || req->pivot->r404_skip) { + + DEBUG("* 404 detection failed.\n"); + + if (RPRES(req)->code == 404) { + + req->pivot->missing = 1; + + } else if (RPRES(req)->code >= 400) { + + problem(PROB_NO_ACCESS, RPREQ(req), RPRES(req), NULL, req->pivot, 0); + + /* Additional check for 401, 500 codes, as we're not calling + content_checks() otherwise. */ + + if (RPRES(req)->code == 401) + problem(PROB_AUTH_REQ, RPREQ(req), RPRES(req), NULL, req->pivot, 0); + else if (RPRES(req)->code >= 500) + problem(PROB_SERV_ERR, RPREQ(req), RPRES(req), NULL, req->pivot, 0); + + } else { + + if (req->pivot->type != PIVOT_SERV) { + + req->pivot->type = PIVOT_PATHINFO; + replace_slash(req->pivot->req, NULL); + + /* XXX Update request */ + + } else + problem(PROB_404_FAIL, RPREQ(req), RPRES(req), + (u8*)"no distinctive 404 behavior detected", req->pivot, 0); + } + + req->pivot->r404_cnt = 0; + + /* We can still try parsing the response, if it differs from parent + and is not on parent's 404 list. */ + + if (!RPAR(req)->res) { + PIVOT_CHECKS(req->pivot->req, req->pivot->res); + } else { + + if (!same_page(&RPRES(req)->sig, &RPAR(req)->res->sig)) { + + struct pivot_desc* par; + par = dir_parent(req->pivot); + + if (par) { + + for (i=0;ir404_cnt;i++) + if (same_page(&res->sig, &par->r404[i])) break; + + } + + if (!par || i == par->r404_cnt) + PIVOT_CHECKS(req->pivot->req, req->pivot->res); + + } + + } + + } else DEBUG("* 404 detection successful: %u signatures.\n", req->pivot->r404_cnt); + + /* Note that per-extension 404 probes coupled with a limit on the number of + 404 signatures largely eliminates the need for BH_COUNT identical probes + to confirm sane behavior here. */ + + /* The next probe is checking if /foo/current_path/ returns the same + response as /bar/current_path/. If yes, then the directory probably + should not be fuzzed. */ + + req->pivot->state = PSTATE_PARENT_CHECK; + + n = req_copy(RPREQ(req), req->pivot, 1); + n->callback = dir_up_behavior_check; + n->user_val = 0; + + /* Last path element is /; previous path element is current dir name; + previous previous element is parent dir name. Find and replace it. */ + + for (i=0;ipar.c;i++) { + if (PATH_SUBTYPE(n->par.t[i])) { + ppval = pval; + pval = val; + val = i; + } + } + + if (ppval != -1 && req->pivot->r404_cnt) { + + ck_free(n->par.v[ppval]); + n->par.v[ppval] = ck_strdup((u8*)BOGUS_FILE); + async_request(n); + + } else { + + /* Top-level dir - nothing to replace. Do a dummy call to + dir_up_behavior_check() to proceed directly to IPS checks. */ + + n->user_val = 1; + dir_up_behavior_check(n, res); + destroy_request(n); + + } + + return 0; + +} + + +static u8 dir_up_behavior_check(struct http_request* req, + struct http_response* res) { + + DEBUG_CALLBACK(req, res); + + if (req->user_val || req->pivot->r404_skip) { + DEBUG("* Check not carried out (non-existent / bad parent).\n"); + goto schedule_next; + } + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during parent checks", 0); + goto schedule_next; + } + + if (same_page(&res->sig, &RPRES(req)->sig)) { + problem(PROB_PARENT_FAIL, req, res, 0, req->pivot, 0); + DEBUG("* Parent may be bogus, skipping.\n"); + req->pivot->bad_parent = 1; + } else { + DEBUG("* Parent behaves OK.\n"); + } + +schedule_next: + + unlock_children(req->pivot); + req->pivot->state = PSTATE_CHILD_INJECT; + inject_start(req->pivot); + return 0; +} + + +static void dir_dict_start(struct pivot_desc* pv) { + static u8 in_dict_init; + struct http_request* n; + u8 *kw; + u8 specific; + + /* Too many requests still pending, or already moved on to + parametric tests? */ + + if (in_dict_init || pv->pending > DICT_BATCH || pv->state != PSTATE_CHILD_DICT) + return; + + if (!descendants_ok(pv)) { + param_start(pv); + return; + } + + if (pv->no_fuzz) { + switch(pv->no_fuzz) { + case 1: + problem(PROB_LIMITS, pv->req, pv->res, + (u8*)"Recursion limit reached, not fuzzing", pv, 0); + break; + + case 2: + problem(PROB_LIMITS, pv->req, pv->res, + (u8*)"Directory out of scope, not fuzzing", pv, 0); + param_start(pv); + break; + + case 3: + DEBUG("Skipping directory bruteforce (allows listing)"); + break; + } + return; + } + + DEBUG_HELPER(pv); + +restart_dict: + + kw = (pv->guess ? wordlist_get_guess : wordlist_get_word) + (pv->cur_key, &specific); + + if (!kw) { + + /* No more keywords. Move to guesswords if not there already, or + advance to parametric tests otherwise. */ + + if (pv->guess) { param_start(pv); return; } + + pv->guess = 1; + pv->cur_key = 0; + goto restart_dict; + + } + + /* Only schedule crawl_prob% dictionary entries. */ + + if (R(100) < crawl_prob) { + + /* First, schedule a request for /foo.bogus to see if extension + fuzzing is advisable. */ + + u8* tmp = ck_alloc(strlen((char*)kw) + strlen((char*)BOGUS_EXT) + 2); + + sprintf((char*)tmp, "%s.%s", kw, BOGUS_EXT); + + n = req_copy(pv->req, pv, 1); + replace_slash(n, tmp); + n->callback = dir_dict_bogus_check; + n->trying_key = kw; + n->trying_spec = specific; + pv->pending++; + in_dict_init = 1; + async_request(n); + in_dict_init = 0; + + ck_free(tmp); + + } + + pv->cur_key++; + + /* Grab more keywords until we have a reasonable number of parallel requests + scheduled. */ + + if (pv->pending < DICT_BATCH) goto restart_dict; + +} + + +static u8 dir_dict_bogus_check(struct http_request* req, + struct http_response* res) { + + struct http_request* n; + u32 i, c; + + DEBUG_CALLBACK(req, res); + + if (FETCH_FAIL(res)) { + + handle_error(req, res, (u8*)"during path-based dictionary probes", 0); + i = ~req->pivot->r404_cnt; + + } else { + + if (!req->pivot->r404_cnt) + DEBUG("Bad pivot with no sigs! Pivot name = '%s'\n", + req->pivot->name); + + for (i=0;ipivot->r404_cnt;i++) + if (same_page(&res->sig, &req->pivot->r404[i])) break; + + } + + /* Do not schedule probes for .ht* files if default Apache config spotted. */ + + if (i == req->pivot->r404_cnt && res->code == 403 && + prefix(req->trying_key, ".ht")) goto schedule_next; + + /* New file? Add pivot for the extension. */ + + if (i == req->pivot->r404_cnt) maybe_add_pivot(req, res, 0); + + /* Schedule extension probes only if bogus extension resulted in known 404. */ + + if (i != req->pivot->r404_cnt && !no_fuzz_ext) { + u8* ex; + + i = 0; + + while ((ex = wordlist_get_extension(i, req->trying_spec))) { + + u8* tmp = ck_alloc(strlen((char*)req->trying_key) + + strlen((char*)ex) + 2); + + sprintf((char*)tmp, "%s.%s", req->trying_key, ex); + + /* See if that file is already known... */ + + for (c=0;cpivot->child_cnt;c++) + if (!((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)tmp, + (char*)req->pivot->child[c]->name))) break; + + /* When dealing with name=value pairs, also compare to + currently fuzzed value string. */ + + if (req->pivot->fuzz_par != -1 && + !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)tmp, + (char*)req->pivot->req->par.v[req->pivot->fuzz_par]))) + c = ~req->pivot->child_cnt; + + /* Not found - schedule a probe. */ + + if (c == req->pivot->child_cnt) { + n = req_copy(req->pivot->req, req->pivot, 1); + replace_slash(n, tmp); + + n->callback = dir_dict_check; + n->user_val = 0; + req->pivot->pending++; + async_request(n); + } + + ck_free(tmp); + + i++; + } + + } + + /* Regardless of this, also schedule requests for /$name and /$name/. */ + + for (c=0;cpivot->child_cnt;c++) + if (!((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)req->trying_key, + (char*)req->pivot->child[c]->name))) break; + + if (req->pivot->fuzz_par != -1 && + !((is_c_sens(req->pivot) ? strcmp : strcasecmp)((char*)req->trying_key, + (char*)req->pivot->req->par.v[req->pivot->fuzz_par]))) + c = ~req->pivot->child_cnt; + + if (c == req->pivot->child_cnt) { + n = req_copy(req->pivot->req, req->pivot, 1); + replace_slash(n, req->trying_key); + n->callback = dir_dict_check; + n->user_val = 0; + req->pivot->pending++; + + /* While bruteforcing, we explicitly set a fake Accept value to + force a 406 response from servers that support content negotiation + */ + + set_value(PARAM_HEADER, (u8*)"Accept", (u8*)"skip/fish;", 0, &n->par); + async_request(n); + + if (prefix(req->trying_key, (u8*)".ht")) { + + n = req_copy(req->pivot->req, req->pivot, 1); + replace_slash(n, req->trying_key); + set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->par); + n->user_val = 0; + n->callback = dir_dict_check; + req->pivot->pending++; + async_request(n); + + } + + } + +schedule_next: + + /* Calling dir_dict_start() ensures that, if no new requests were scheduled + earlier on and nothing else is pending, that we will still advance to + parametric checks. */ + + req->pivot->pending--; + dir_dict_start(req->pivot); + + return 0; + +} + + +static u8 dir_dict_check(struct http_request* req, + struct http_response* res) { + u32 i; + + DEBUG_CALLBACK(req, res); + + /* When we get a 406, than our content negotiation trick worked. It + means the body contains alternatives and also that this pivot doesn't + exist. So we scrape the content and return immediately. */ + + if(res->code == 406) { + RESP_CHECKS(req, res); + return 0; + } + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during path-based dictionary probes", 0); + } else { + + /* Check if 404... */ + + for (i=0;ipivot->r404_cnt;i++) + if (same_page(&res->sig, &req->pivot->r404[i])) break; + + /* Special case for secondary extension fuzzing - skip secondary + extensions that seemingly return the same document. */ + + if (req->user_val && same_page(&res->sig, &req->same_sig)) + i = ~req->pivot->r404_cnt; + + /* If not 404, do response, and does not look like + parent's original file signature, add pivot. */ + + if (i == req->pivot->r404_cnt) maybe_add_pivot(req, res, 0); + + } + + /* Try replenishing the queue. */ + + if (!req->user_val) { + req->pivot->pending--; + dir_dict_start(req->pivot); + } + + return 0; + +} + + +u8 unknown_retrieve_check(struct http_request* req, struct http_response* res) { + u32 i = 0 /* bad gcc */; + struct pivot_desc *par; + struct http_request* n; + u8* name = NULL; + + RPRES(req) = res; + DEBUG_CALLBACK(req, res); + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during initial resource fetch", 1); + return 1; + } + + /* Matches parent's 404? */ + + par = dir_parent(req->pivot); + + if (par) + for (i=0;ir404_cnt;i++) + if (same_page(&res->sig, &par->r404[i])) break; + + /* Again, 404 is the least we could do. */ + + if ((!par && res->code == 404) || (par && i != par->r404_cnt)) { + + req->pivot->missing = 1; + unlock_children(req->pivot); + param_start(req->pivot); + return 1; + + } + + /* If the response looks like parent's original unknown_callback() + response, assume file. This is a workaround for some really + quirky architectures. */ + + if (par && res->pay_len && res->code == 200 && + same_page(&par->unk_sig, &res->sig)) { + + req->pivot->type = PIVOT_FILE; + return file_retrieve_check(req, res); + + } + + /* Another workaround for quirky frameworks: identical signature + as parent's both probes, and 3xx code. */ + + if (par && res->code >= 300 && res->code < 400 && + same_page(&par->unk_sig, &res->sig) && + same_page(&par->res->sig, &res->sig)) { + + req->pivot->type = PIVOT_FILE; + return file_retrieve_check(req, res); + + } + + /* Special handling for .ht* */ + + if (req->pivot->type < PIVOT_PARAM) { + u32 i; + + /* Find last path segment. */ + + for (i=0;ipar.c;i++) + if (PATH_SUBTYPE(req->par.t[i])) name = req->par.v[i]; + + if (name && !prefix(name, (u8*)".ht")) { + req->pivot->type = PIVOT_FILE; + return file_retrieve_check(req, res); + } + + } + + /* Schedule a request to settle the type of this pivot point. */ + + n = req_copy(req, req->pivot, 1); + set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->par); + n->callback = unknown_retrieve_check2; + n->user_val = 0; + + if (name) { + u8* ppos = (u8*) strrchr((char*)name, '.'); + if (!ppos || ppos == name) n->user_val = 1; + } + + async_request(n); + + /* This is the initial callback, keep the response. */ + + return 1; + +} + + +static u8 unknown_retrieve_check2(struct http_request* req, + struct http_response* res) { + u8 keep = 0; + + DEBUG_CALLBACK(req, res); + + if (FETCH_FAIL(res)) { + handle_error(req, res, (u8*)"during node type checks", 0); + goto schedule_next; + } + + /* If pivot == res, we are probably dealing with PATH_INFO-style + plot device, which is best approached as a directory anyway + (worst-case scenario, dir handlers will dismiss it as + misbehaving and demote it to PIVOT_PATHINFO after some extra + checks). + + If pivot != res, and res is not a 404 response, assume dir; + and if it is 404, assume file, except if pivot redirected to res. + + We also have a special case if the original request returned a + non-empty 2xx response, but the new one returned 3xx-5xx - this is + likely a file, too. */ + + if (same_page(&RPRES(req)->sig, &res->sig)) goto assume_dir; else { + u32 i = 0; + struct pivot_desc* par = dir_parent(req->pivot); + + if (res->code == 404 && RPRES(req)->code >= 300 && RPRES(req)->code < 400) { + u8 *loc = GET_HDR((u8*)"Location", &RPRES(req)->hdr); + + if (loc) { + u8* path = serialize_path(req, 1, 0); + + if (!strcasecmp((char*)path, (char*)loc)) { + ck_free(path); + req->pivot->sure_dir = 1; + goto assume_dir; + } + + ck_free(path); + + } + + } + + if (par) { + for (i=0;ir404_cnt;i++) + if (same_page(&res->sig, &par->r404[i])) break; + + /* Do not use extension-originating signatures for settling non-extension + cases. */ + + if (i && req->user_val) i = par->r404_cnt; + + } + + if ((!par && res->code == 404) || (par && i != par->r404_cnt) || + (RPRES(req)->code < 300 && res->code >= 300 && RPRES(req)->pay_len)) { + + req->pivot->type = PIVOT_FILE; + + } else { + +assume_dir: + + /* If any of the responses is 500, and the user asked for 500 to + be treated specially to work around quirky frameworks, + assume file right away. */ + + if (no_500_dir && (res->code >= 500 || RPRES(req)->code >= 500)) { + DEBUG("Feels like a directory, but assuming file pivot as per -Z flag.\n"); + req->pivot->type = PIVOT_FILE; + goto schedule_next; + } + + req->pivot->type = PIVOT_DIR; + + /* Perform content checks before discarding the old payload. */ + + if (!same_page(&RPRES(req)->sig, &res->sig)) + content_checks(RPREQ(req), RPRES(req)); + + /* Replace original request, response with new data. */ + + destroy_request(RPREQ(req)); + + if (RPRES(req)) { + memcpy(&req->pivot->unk_sig, &RPRES(req)->sig, sizeof(struct http_sig)); + destroy_response(RPRES(req)); + } + + RPREQ(req) = req; + RPRES(req) = res; + + keep = 1; + + } + + } + +schedule_next: + + /* Well, we need to do something. */ + + if (req->pivot->type == PIVOT_DIR || req->pivot->type == PIVOT_SERV) + dir_retrieve_check(RPREQ(req), RPRES(req)); + else file_retrieve_check(RPREQ(req), RPRES(req)); + + return keep; +} diff -Nru skipfish-2.02b/src/crawler.h skipfish-2.10b/src/crawler.h --- skipfish-2.02b/src/crawler.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/crawler.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,170 @@ +/* + skipfish - crawler state machine + -------------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#ifndef _HAVE_CRAWLER_H + +#include "types.h" +#include "http_client.h" +#include "database.h" + +/* Function called during startup to build the test/check structure */ + +void replace_slash(struct http_request* req, u8* new_val); +void handle_error(struct http_request* req, struct http_response* res, u8* desc, u8 stop); +void inject_done(struct pivot_desc*); +void destroy_misc_data(struct pivot_desc* pv, struct http_request* self); +struct pivot_desc* dir_parent(struct pivot_desc* pv); +void authenticate(); + +/* Internal helper macros: */ + +#define TPAR(_req) ((_req)->par.v[(_req)->pivot->fuzz_par]) + +#define SET_VECTOR(_state, _req, _str) do { \ + if (_state == PSTATE_CHILD_INJECT) { \ + replace_slash((_req), (u8*)_str); \ + } else { \ + ck_free(TPAR(_req)); \ + TPAR(_req) = ck_strdup((u8*)_str); \ + } \ + } while (0) + +#define APPEND_VECTOR(_state, _req, _str) do { \ + if (_state == PSTATE_CHILD_INJECT) { \ + replace_slash((_req), (u8*)_str); \ + } else { \ + u8* _n = ck_alloc(strlen((char*)TPAR(_req)) + strlen((char*)_str) + 1); \ + sprintf((char*)_n, "%s%s", TPAR(_req), _str); \ + ck_free(TPAR(_req)); \ + TPAR(_req) = _n; \ + } \ + } while (0) + +#define PREFIX_STRING(_orig, _str) do { \ + u8* tmp = ck_alloc(strlen((char*)_orig) + strlen((char*)_str) + 1); \ + sprintf((char*)tmp, "%s%s", (char*)_str, (char*)_orig); \ + ck_free(_orig); \ + _orig = tmp; \ + } while (0) + +/* Classifies a response, with a special handling of "unavailable" and + "gateway timeout" codes. */ + +#define FETCH_FAIL(_res) ((_res)->state != STATE_OK || (_res)->code == 503 || \ + (_res)->code == 504) + + +extern u32 crawl_prob; /* Crawl probability (1-100%) */ +extern u8 no_parse, /* Disable HTML link detection */ + warn_mixed, /* Warn on mixed content? */ + no_fuzz_ext, /* Don't fuzz ext in dirs? */ + no_500_dir, /* Don't assume dirs on 500 */ + delete_bin, /* Don't keep binary responses */ + flush_pivot_data, /* Flush pivot data to disk */ + log_ext_urls; /* Log external URLs? */ + +/* Provisional debugging callback. */ + +u8 show_response(struct http_request* req, struct http_response* res); + +/* Asynchronous request callback for the initial PSTATE_FETCH request of + PIVOT_UNKNOWN resources. */ + +u8 unknown_retrieve_check(struct http_request* req, struct http_response* res); + +/* Asynchronous request callback for the initial PSTATE_FETCH request of + PIVOT_FILE resources. */ + +u8 file_retrieve_check(struct http_request* req, struct http_response* res); + +/* Asynchronous request callback for the initial PSTATE_FETCH request of + PIVOT_DIR resources. */ + +u8 dir_retrieve_check(struct http_request* req, struct http_response* res); + +/* Initializes the crawl of try_list items for a pivot point (if any still + not crawled). */ + +void param_trylist_start(struct pivot_desc* pv); + +/* Adds new name=value to form hints list. */ + +void add_form_hint(u8* name, u8* value); + +/* Macros to access various useful pivot points: */ + +#define RPAR(_req) ((_req)->pivot->parent) +#define RPREQ(_req) ((_req)->pivot->req) +#define RPRES(_req) ((_req)->pivot->res) +#define MREQ(_x) (req->pivot->misc_req[_x]) +#define MRES(_x) (req->pivot->misc_res[_x]) + + +/* Debugging instrumentation for callbacks and callback helpers: */ + +#ifdef LOG_STDERR + +#define DEBUG_CALLBACK(_req, _res) do { \ + u8* _url = serialize_path(_req, 1, 1); \ + DEBUG("* %s: URL %s (%u, len %u)\n", __FUNCTION__, _url, (_res) ? \ + (_res)->code : 0, (_res) ? (_res)->pay_len : 0); \ + ck_free(_url); \ + } while (0) + +#define DEBUG_MISC_CALLBACK(_req, _res) do { \ + int i; \ + for (i = 0; i < req->pivot->misc_cnt; i++) \ + DEBUG_CALLBACK(MREQ(i), MRES(i)); \ + } while (0) + +#define DEBUG_PIVOT(_text, _pv) do { \ + u8* _url = serialize_path((_pv)->req, 1, 1); \ + DEBUG("* %s: %s\n", _text, _url); \ + ck_free(_url); \ + } while (0) + +#define DEBUG_STATE_CALLBACK(_req, _state, _type) do { \ + u8* _url = serialize_path(_req, 1, 1); \ + DEBUG("* %s::%s: URL %s (running: %s)\n", __FUNCTION__, _state, _url, \ + _type ? "checks" : "tests"); \ + ck_free(_url); \ + } while (0) + + +#define DEBUG_HELPER(_pv) do { \ + u8* _url = serialize_path((_pv)->req, 1, 1); \ + DEBUG("* %s: URL %s (%u, len %u)\n", __FUNCTION__, _url, (_pv)->res ? \ + (_pv)->res->code : 0, (_pv)->res ? (_pv)->res->pay_len : 0); \ + ck_free(_url); \ + } while (0) + +#else + +#define DEBUG_CALLBACK(_req, _res) +#define DEBUG_MISC_CALLBACK(_req, _res) +#define DEBUG_STATE_CALLBACK(_req, _res, _cb) +#define DEBUG_HELPER(_pv) +#define DEBUG_PIVOT(_text, _pv) + +#endif /* ^LOG_STDERR */ + +#endif /* !_HAVE_CRAWLER_H */ diff -Nru skipfish-2.02b/src/database.c skipfish-2.10b/src/database.c --- skipfish-2.02b/src/database.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/database.c 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,1584 @@ +/* + skipfish - database & crawl management + -------------------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#define _VIA_DATABASE_C + +#include +#include +#include +#include + +#include "debug.h" +#include "config.h" +#include "types.h" +#include "http_client.h" +#include "database.h" +#include "crawler.h" +#include "analysis.h" +#include "string-inl.h" + +struct pivot_desc root_pivot; + +u8 **deny_urls, /* List of banned URL substrings */ + **allow_urls, /* List of required URL substrings */ + **allow_domains, /* List of allowed vhosts */ + **trust_domains, /* List of trusted vhosts */ + **skip_params; /* List of parameters to ignore */ + +u32 num_deny_urls, + num_allow_urls, + num_allow_domains, + num_trust_domains, + num_skip_params; + +u32 max_depth = MAX_DEPTH, + max_children = MAX_CHILDREN, + max_descendants = MAX_DESCENDANTS, + max_guesses = MAX_GUESSES; + +u8 dont_add_words; /* No auto dictionary building */ + +#define KW_SPECIFIC 0 +#define KW_GENERIC 1 +#define KW_GEN_AUTO 2 + +struct kw_entry { + u8* word; /* Keyword itself */ + u32 hit_cnt; /* Number of confirmed sightings */ + u8 is_ext; /* Is an extension? */ + u8 hit_already; /* Had its hit count bumped up? */ + u8 read_only; /* Read-only dictionary? */ + u8 class; /* KW_* */ + u32 total_age; /* Total age (in scan cycles) */ + u32 last_age; /* Age since last hit */ +}; + +static struct kw_entry* + keyword[WORD_HASH]; /* Keyword collection (bucketed) */ + +static u32 keyword_cnt[WORD_HASH]; /* Per-bucket keyword counts */ + +struct ext_entry { + u32 bucket; + u32 index; +}; + +static struct ext_entry *wg_extension, /* Extension list */ + *ws_extension; + +static u8 **guess; /* Keyword candidate list */ + +u32 guess_cnt, /* Number of keyword candidates */ + ws_extension_cnt, /* Number of specific extensions */ + wg_extension_cnt, /* Number of extensions */ + keyword_total_cnt, /* Current keyword count */ + keyword_orig_cnt; /* At-boot keyword count */ + +static u32 cur_xss_id, scan_id; /* Stored XSS manager IDs */ +static struct http_request** xss_req; /* Stored XSS manager req cache */ + + +/* Checks descendant counts. */ + +u8 descendants_ok(struct pivot_desc* pv) { + + if (pv->child_cnt > max_children) return 0; + + while (pv) { + if (pv->desc_cnt > max_descendants) return 0; + pv = pv->parent; + } + + return 1; + +} + + +void add_descendant(struct pivot_desc* pv) { + + while (pv) { + pv->desc_cnt++; + pv = pv->parent; + } + +} + +/* Maps a parsed URL (in req) to the pivot tree, creating or modifying nodes + as necessary, and scheduling them for crawl. This should be called only + on requests that were *not* yet retrieved. */ + +void maybe_add_pivot(struct http_request* req, struct http_response* res, + u8 via_link) { + + struct pivot_desc *cur = NULL; + + u32 i, par_cnt = 0, path_cnt = 0, last_val_cnt = 0, pno; + u8 ends_with_slash = 0; + u8* last_val = 0; + +#ifdef LOG_STDERR + + u8* url = serialize_path(req, 1, 1); + DEBUG("--- New pivot requested: %s (%d,%d)\n", url, via_link, req->browser); + ck_free(url); + +#endif /* LOG_STDERR */ + + if (!req) FATAL("Invalid request data."); + + /* Initialize root pivot if not done already. */ + + if (!root_pivot.type) { + root_pivot.type = PIVOT_ROOT; + root_pivot.state = PSTATE_DONE; + root_pivot.linked = 2; + root_pivot.fuzz_par = -1; + root_pivot.name = ck_strdup((u8*)"[root]"); + } + + if (!url_allowed(req)) { url_scope++; return; } + + /* Count the number of path and query parameters in the request. */ + + for (i=0;ipar.c;i++) { + + if (QUERY_SUBTYPE(req->par.t[i]) || POST_SUBTYPE(req->par.t[i])) par_cnt++; + + if (PATH_SUBTYPE(req->par.t[i])) { + + if (req->par.t[i] == PARAM_PATH && !req->par.n[i] && + req->par.v[i] && !req->par.v[i][0]) + ends_with_slash = 1; + else + ends_with_slash = 0; + + if (req->par.v[i][0]) last_val = req->par.v[i]; + + path_cnt++; + + } + + /* While we're at it, try to learn new keywords. */ + + if (PATH_SUBTYPE(req->par.t[i]) || QUERY_SUBTYPE(req->par.t[i])) { + if (req->par.n[i]) wordlist_confirm_word(req->par.n[i]); + wordlist_confirm_word(req->par.v[i]); + } + + } + + /* Try to find pivot point for the host. */ + + for (i=0;ireq->host, (char*)req->host) && + cur->req->port == req->port && + cur->req->proto == req->proto) break; + } + + if (i == root_pivot.child_cnt) { + + /* No server pivot found, we need to create one. */ + + cur = ck_alloc(sizeof(struct pivot_desc)); + + root_pivot.child = ck_realloc(root_pivot.child, + (root_pivot.child_cnt + 1) * sizeof(struct pivot_desc*)); + + root_pivot.child[root_pivot.child_cnt++] = cur; + + add_descendant(&root_pivot); + + cur->type = PIVOT_SERV; + cur->state = PSTATE_FETCH; + cur->linked = 2; + cur->fuzz_par = -1; + cur->parent = &root_pivot; + + /* Copy the original request, sans path. Create a dummy + root dir entry instead. Derive pivot name by serializing + the URL of the associated stub request. */ + + cur->req = req_copy(req, cur, 0); + set_value(PARAM_PATH, NULL, (u8*)"", -1, &cur->req->par); + cur->name = serialize_path(cur->req, 1, 0); + cur->req->callback = dir_retrieve_check; + + /* If matching response not provided, schedule request. */ + + if (res && !par_cnt && path_cnt == 1) { + cur->res = res_copy(res); + dir_retrieve_check(req, cur->res); + } else async_request(cur->req); + + wordlist_confirm_word(req->host); + + } + + /* One way or the other, 'cur' now points to server pivot. Let's + walk through all path elements, and follow or create sub-pivots + for them. */ + + pno = 0; + + for (i=0;ipar.t[pno])) pno++; + + /* Bail out on the trailing NULL-'' indicator, if present. It is + used to denote a directory, and will always be the last path + element. */ + + if (i == path_cnt - 1 && req->par.t[pno] == PARAM_PATH && + !req->par.n[pno] && !req->par.v[pno][0]) break; + + pname = req->par.n[pno] ? req->par.n[pno] : req->par.v[pno]; + + ccnt = cur->child_cnt; + + /* Try to find a matching node. */ + + for (c=0;cchild[c]->name) && (!req->browser || + req->browser == cur->child[c]->browser)) { + cur = cur->child[c]; + if (cur->linked < via_link) cur->linked = via_link; + break; + } + + if (c == ccnt) { + + /* Node not found. We need to create one. */ + + struct pivot_desc* n; + + /* Enforce user limits. */ + + if ((i + 1) >= max_depth || !descendants_ok(cur)) { + problem(PROB_LIMITS, req, res, (u8*)"Child node limit exceeded", cur, + 0); + return; + } + + /* Enforce duplicate name limits as a last-ditch effort to prevent + endless recursion. */ + + if (last_val && !strcmp((char*)last_val, (char*)req->par.v[pno])) + last_val_cnt++; + + if (last_val_cnt > MAX_SAMENAME) { + problem(PROB_LIMITS, req, res, + (u8*)"Duplicate name recursion limit exceeded", cur, 0); + return; + } + + /* Create and link back to parent. */ + + n = ck_alloc(sizeof(struct pivot_desc)); + + cur->child = ck_realloc(cur->child, (cur->child_cnt + 1) * + sizeof(struct pivot_desc*)); + + cur->child[cur->child_cnt++] = n; + + add_descendant(cur); + + n->parent = cur; + n->linked = via_link; + n->name = ck_strdup(pname); + n->browser = req->browser; + + /* Copy the original request, then copy over path up to the + current point. */ + + n->req = req_copy(req, n, 0); + + for (c=0;c<=pno;c++) + if (PATH_SUBTYPE(req->par.t[c])) + set_value(req->par.t[c], req->par.n[c], req->par.v[c], -1, + &n->req->par); + + /* If name is parametric, indicate which parameter to fuzz. */ + + if (req->par.n[pno]) n->fuzz_par = n->req->par.c - 1; + else n->fuzz_par = -1; + + /* Do not fuzz out-of-scope or limit exceeded dirs... */ + + if ((i + 1) == max_depth - 1) n->no_fuzz = 1; + + if (i != path_cnt - 1) { + + /* This is not the last path segment, so let's assume a "directory" + (hierarchy node, to be more accurate), and schedule directory + tests. */ + + set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->req->par); + n->type = PIVOT_DIR; + n->req->callback = dir_retrieve_check; + + if (!url_allowed(n->req)) n->no_fuzz = 2; + + /* Subdirectory tests require parent directory 404 testing to complete + first. If these are still pending, wait a bit. */ + + if (cur->state > PSTATE_IPS_CHECK) { + + n->state = PSTATE_FETCH; + + /* If this actually *is* the last parameter, taking into account the + early-out hack mentioned above, and we were offered a response - + make use of it and don't schedule a new request. */ + + if (i == path_cnt - 2 && ends_with_slash && res) { + + n->res = res_copy(res); + dir_retrieve_check(n->req, n->res); + + } else async_request(n->req); + + } else n->state = PSTATE_PENDING; + + } else { + + /* Last segment. If no parameters, copy response body, mark type as + "unknown", schedule extra checks. */ + + if (!url_allowed(n->req)) n->no_fuzz = 2; + + if (!par_cnt) { + + n->type = PIVOT_UNKNOWN; + n->res = res_copy(res); + n->req->callback = unknown_retrieve_check; + + if (cur->state > PSTATE_IPS_CHECK) { + + n->state = PSTATE_FETCH; + + /* If we already have a response, call the callback directly + (it will schedule further requests on its own). */ + + if (!res) { + n->state = PSTATE_FETCH; + async_request(n->req); + } else unknown_retrieve_check(n->req, n->res); + + } else n->state = PSTATE_PENDING; + + } else { + + /* Parameters found. Assume file, schedule a fetch. */ + + n->type = PIVOT_FILE; + n->req->callback = file_retrieve_check; + + if (cur->state > PSTATE_IPS_CHECK) { + n->state = PSTATE_FETCH; + async_request(n->req); + } else n->state = PSTATE_PENDING; + + } + + } + + cur = n; + + } + + /* At this point, 'cur' points to a newly created or existing node + for the path element. If this element is parametric, make sure + that its value is on the 'try' list. */ + + if (req->par.n[pno]) { + + for (c=0;ctry_cnt;c++) + if (cur->try_list[c] && !(is_c_sens(cur) ? strcmp : strcasecmp) + ((char*)req->par.v[pno], (char*)cur->try_list[c])) break; + + /* Not found on the list - try adding. */ + + if (c == cur->try_cnt) { + + cur->try_list = ck_realloc(cur->try_list, (cur->try_cnt + 1) * + sizeof(u8*)); + cur->try_list[cur->try_cnt++] = ck_strdup(req->par.v[pno]); + + if (cur->state == PSTATE_DONE) + param_trylist_start(cur); + + } + + } + + pno++; + + } + + /* Phew! At this point, 'cur' points to the final path element, and now, + we just need to take care of parameters. Each parameter has its own + pivot point, and a full copy of the request - unless on the + param_skip list. */ + + pno = 0; + + for (i=0;ipar.t[pno]) && !POST_SUBTYPE(req->par.t[pno])) + pno++; + + pname = req->par.n[pno] ? req->par.n[pno] : (u8*)"[blank]"; + ccnt = cur->child_cnt; + + /* Try to find a matching node. */ + + for (c=0;cchild[c]->name) && (!req->browser || + req->browser == cur->child[c]->browser)) { + cur = cur->child[c]; + if (cur->linked < via_link) cur->linked = via_link; + break; + } + + if (c == ccnt) { + + /* Node not found. We need to create one. */ + + struct pivot_desc* n; + + /* Enforce user limits. */ + + if (!descendants_ok(cur)) { + problem(PROB_LIMITS, req, res, (u8*)"Child node limit exceeded", cur, 0); + return; + } + + /* Create and link back to parent. */ + + n = ck_alloc(sizeof(struct pivot_desc)); + + cur->child = ck_realloc(cur->child, (cur->child_cnt + 1) * + sizeof(struct pivot_desc*)); + + cur->child[cur->child_cnt++] = n; + + add_descendant(cur); + + n->parent = cur; + n->type = PIVOT_PARAM; + n->linked = via_link; + n->name = ck_strdup(pname); + n->browser = req->browser; + + /* Copy the original request, in full. Remember not to fuzz + file inputs. */ + + n->req = req_copy(req, n, 1); + n->fuzz_par = req->par.t[pno] == PARAM_POST_F ? -1 : pno; + n->res = res_copy(res); + + /* File fetcher does everything we need. */ + + n->req->callback = file_retrieve_check; + + if (cur->state > PSTATE_IPS_CHECK) { + n->state = PSTATE_FETCH; + if (res) file_retrieve_check(n->req, n->res); + else async_request(n->req); + } else n->state = PSTATE_PENDING; + + cur = n; + + } + + /* Ok, again, 'cur' is at the appropriate node. Make sure the + current value is on the 'try' list. */ + + for (c=0;ctry_cnt;c++) + if (cur->try_list[c] && !(is_c_sens(cur) ? strcmp : strcasecmp) + ((char*)req->par.v[pno], (char*)cur->try_list[c])) break; + + /* Not found on the list - try adding. */ + + if (c == cur->try_cnt) { + + cur->try_list = ck_realloc(cur->try_list, (cur->try_cnt + 1) * + sizeof(u8*)); + cur->try_list[cur->try_cnt++] = ck_strdup(req->par.v[pno]); + + if (cur->state == PSTATE_DONE) + param_trylist_start(cur); + + } + + /* Parameters are not hierarchical, so go back to the parent node. */ + + cur = cur->parent; + pno++; + + } + + /* Done, at last! */ + +} + + +/* Finds the host-level pivot point for global issues. */ + +struct pivot_desc* host_pivot(struct pivot_desc* pv) { + while (pv->parent && pv->parent->parent) pv = pv->parent; + return pv; +} + + +/* Gets case sensitivity info from the nearest DIR / SERV node. */ + +u8 is_c_sens(struct pivot_desc* pv) { + while (pv->parent && (pv->type != PIVOT_DIR || pv->type != PIVOT_SERV)) + pv = pv->parent; + return pv->csens; +} + +/* Lookup an issue title */ + +u8* lookup_issue_title(u32 id) { + u32 i = 0; + + while(pstructs[i].id && pstructs[i].id != id) + i++; + + return pstructs[i].title; +} + +/* Remove issue(s) of with the specified 'type' */ + +void remove_issue(struct pivot_desc *pv, u32 type) { + + u32 i, cnt = 0; + struct issue_desc *tmp = NULL; + + if (!pv->issue_cnt) return; + + for (i=0; iissue_cnt; i++) { + if (pv->issue[i].type == type) { + DEBUG("* Removing issue of type: %d\n", type); + if (pv->issue[i].req) destroy_request(pv->issue[i].req); + if (pv->issue[i].res) destroy_response(pv->issue[i].res); + } else { + tmp = ck_realloc(tmp, (cnt + 1) * sizeof(struct issue_desc)); + + tmp[cnt].type = pv->issue[i].type; + tmp[cnt].extra = pv->issue[i].extra; + tmp[cnt].req = pv->issue[i].req; + tmp[cnt].res = pv->issue[i].res; + tmp[cnt].sid = pv->issue[i].sid; + cnt++; + } + } + + ck_free(pv->issue); + pv->issue = tmp; + pv->issue_cnt = cnt; +} + + +void problem(u32 type, struct http_request* req, struct http_response* res, + u8* extra, struct pivot_desc* pv, u8 allow_dup) { + + /* Small wrapper for all those problem() calls that do not need to + specify a sid */ + register_problem(type, 0, req, res, extra, pv, allow_dup); + +} + +/* Registers a problem, if not duplicate (res, extra may be NULL): */ +void register_problem(u32 type, u32 sid, struct http_request* req, + struct http_response* res, u8* extra, + struct pivot_desc* pv, u8 allow_dup) { + + u32 i; + + if (pv->type == PIVOT_NONE) FATAL("Uninitialized pivot point"); + if (type == PROB_NONE || !req) FATAL("Invalid issue data"); + +#ifdef LOG_STDERR + DEBUG("--- NEW PROBLEM - type: %u, extra: '%s' ---\n", type, extra); +#endif /* LOG_STDERR */ + + /* Check for duplicates */ + + if (!allow_dup) + for (i=0;iissue_cnt;i++) + if (type == pv->issue[i].type && !strcmp(extra ? (char*)extra : "", + pv->issue[i].extra ? (char*)pv->issue[i].extra : "")) return; + + pv->issue = ck_realloc(pv->issue, (pv->issue_cnt + 1) * + sizeof(struct issue_desc)); + + pv->issue[pv->issue_cnt].type = type; + pv->issue[pv->issue_cnt].extra = extra ? ck_strdup(extra) : NULL; + pv->issue[pv->issue_cnt].req = req_copy(req, pv, 1); + pv->issue[pv->issue_cnt].res = res_copy(res); + pv->issue[pv->issue_cnt].sid = sid; + +#ifndef LOG_STDERR + u8* url = serialize_path(req, 1, 1); + u8* title = lookup_issue_title(type); + DEBUGC(L2, "\n--- NEW PROBLEM\n"); + DEBUGC(L2, " - type: %u, %s\n", type, title); + DEBUGC(L2, " - url: %s\n", url); + DEBUGC(L3, " - extra: %s\n", extra); + ck_free(url); +#endif /* LOG_STDERR */ + + /* Mark copies of half-baked requests as done. */ + + if (res && res->state < STATE_OK) { + pv->issue[pv->issue_cnt].res->state = STATE_OK; + ck_free(pv->issue[pv->issue_cnt].res->payload); + pv->issue[pv->issue_cnt].res->payload = + ck_strdup((u8*)"[...truncated...]\n"); + pv->issue[pv->issue_cnt].res->pay_len = 18; + } + + pv->issue_cnt++; + + /* Propagate parent issue counts. */ + + do { pv->desc_issue_cnt++; } while ((pv = pv->parent)); + +} + + + +/* Three functions to check if the URL is permitted under current rules + (0 = no, 1 = yes): */ + +u8 url_allowed_host(struct http_request* req) { + u32 i; + + for (i=0;ihost, allow_domains[i]); + + if (pos && strlen((char*)req->host) == + strlen((char*)allow_domains[i]) + (pos - req->host)) + return 1; + + } else + if (!strcasecmp((char*)req->host, (char*)allow_domains[i])) + return 1; + + } + + return 0; +} + + +u8 url_trusted_host(struct http_request* req) { + u32 i; + + i = 0; + + while (always_trust_domains[i]) { + + if (always_trust_domains[i][0] == '.') { + + u8* pos = inl_strcasestr(req->host, (u8*)always_trust_domains[i]); + + if (pos && strlen((char*)req->host) == + strlen(always_trust_domains[i]) + (pos - req->host)) + return 1; + } else + if (!strcasecmp((char*)req->host, (char*)always_trust_domains[i])) + return 1; + + i++; + + } + + for (i=0;ihost, trust_domains[i]); + + if (pos && strlen((char*)req->host) == + strlen((char*)trust_domains[i]) + (pos - req->host)) + return 1; + + } + + return 0; +} + + +u8 url_allowed(struct http_request* req) { + u8* url = serialize_path(req, 1, 0); + u32 i; + + /* Check blacklist first */ + + for (i=0;icode != sig2->code) + return 0; + + /* One has text and the other hasnt: different page */ + if (sig1->has_text != sig2->has_text) + return 0; + + for (i=0;idata[i] - sig2->data[i]; + u32 scale = sig1->data[i] + sig2->data[i]; + + if (abs(diff) > 1 + (scale * FP_T_REL / 100)) + if (++bucket_fail > FP_B_FAIL) return 0; + + total_diff += diff; + total_scale += scale; + + } + + if (abs(total_diff) > 1 + (total_scale * FP_T_REL / 100)) + return 0; + + return 1; +} + +/* Dumps signature data: */ + +void dump_signature(struct http_sig* sig) { + u32 i; + + DEBUG("SIG %03d: ", sig->code); + for (i=0;idata[i]); + DEBUG("\n"); + +} + + +/* Debugs signature comparison: */ + +void debug_same_page(struct http_sig* sig1, struct http_sig* sig2) { + +#ifdef LOG_STDERR + + u32 i; + s32 total_diff = 0; + u32 total_scale = 0; + + dump_signature(sig1); + dump_signature(sig2); + + DEBUG(" "); + + for (i=0;idata[i] - sig2->data[i]; + DEBUG("[%04d] ", diff); + } + + DEBUG("(diff)\n "); + + for (i=0;idata[i] - sig2->data[i]; + u32 scale = sig1->data[i] + sig2->data[i]; + + if (abs(diff) > 1 + (scale * FP_T_REL / 100)) + DEBUG("[FAIL] "); else DEBUG("[pass] "); + + total_diff += diff; + total_scale += scale; + } + + DEBUG("\n "); + + for (i=0;idata[i] + sig2->data[i]; + + DEBUG("[%04d] ", (u32)( 1 + (scale * FP_T_REL / 100))); + } + + DEBUG("(allow)\n"); + + DEBUG("Total diff: %d, scale %d, allow %d\n", + abs(total_diff), total_scale, 1 + (u32)(total_scale * FP_T_REL / 100)); + +#endif /* LOG_STDERR */ + +} + + + +/* Keyword management: */ + + +/* Word hashing helper. */ + +static inline u32 hash_word(u8* str) { + register u32 ret = 0; + register u8 cur; + + if (str) + while ((cur=*str)) { + ret = ~ret ^ (cur) ^ + (cur << 5) ^ (~cur >> 5) ^ + (cur << 10) ^ (~cur << 15) ^ + (cur << 20) ^ (~cur << 25) ^ + (cur << 30); + str++; + } + + return ret % WORD_HASH; +} + + +/* Adds a new keyword candidate to the global "guess" list. This + list is case-sensitive. */ + +void wordlist_add_guess(u8* text) { + u32 target, i, kh; + + if (dont_add_words) return; + + /* Check if this is a bad or known guess or keyword. */ + + if (!text || !text[0] || strlen((char*)text) > MAX_WORD) return; + + for (i=0;i= max_guesses) target = R(max_guesses); + else target = guess_cnt++; + + ck_free(guess[target]); + guess[target] = ck_strdup(text); + +} + + +/* Adds a single, sanitized keyword to the list, or increases its hit count. + Keyword list is case-sensitive. */ + +static void wordlist_confirm_single(u8* text, u8 is_ext, u8 class, u8 read_only, + u32 add_hits, u32 total_age, u32 last_age) { + u32 kh, i; + + if (!text || !text[0] || strlen((char*)text) > MAX_WORD) return; + + /* Check if this is a known keyword. */ + + kh = hash_word(text); + + for (i=0;i 4) return; + + if (ppos != -1) { + + /* Period only? Too long? */ + if (tlen == 1 || tlen - ppos > 12) return; + + if (ppos && ppos != tlen - 1 && !isdigit(text[ppos + 1])) { + wordlist_confirm_single(text + ppos + 1, 1, KW_GEN_AUTO, 0, 1, 0, 0); + text[ppos] = 0; + wordlist_confirm_single(text, 0, KW_GEN_AUTO, 0, 1, 0, 0); + text[ppos] = '.'; + return; + } + + } + + wordlist_confirm_single(text, 0, KW_GEN_AUTO, 0, 1, 0, 0); +} + + +/* Returns wordlist item at a specified offset (NULL if no more available). */ + +u8* wordlist_get_word(u32 offset, u8* specific) { + u32 cur_off = 0, kh; + + for (kh=0;kh offset) break; + cur_off += keyword_cnt[kh]; + } + + if (kh == WORD_HASH) return NULL; + + *specific = (keyword[kh][offset - cur_off].is_ext == 0 && + keyword[kh][offset - cur_off].class == KW_SPECIFIC); + + return keyword[kh][offset - cur_off].word; +} + + +/* Returns keyword candidate at a specified offset (or NULL). */ + +u8* wordlist_get_guess(u32 offset, u8* specific) { + if (offset >= guess_cnt) return NULL; + *specific = 0; + return guess[offset]; +} + + +/* Returns extension at a specified offset (or NULL). */ + +u8* wordlist_get_extension(u32 offset, u8 specific) { + + if (!specific) { + if (offset >= wg_extension_cnt) return NULL; + return keyword[wg_extension[offset].bucket][wg_extension[offset].index].word; + } + + if (offset >= ws_extension_cnt) return NULL; + return keyword[ws_extension[offset].bucket][ws_extension[offset].index].word; +} + + +/* Loads keywords from file. */ + +void load_keywords(u8* fname, u8 read_only, u32 purge_age) { + FILE* in; + u32 hits, total_age, last_age, lines = 0; + u8 type[3]; + s32 fields; + u8 kword[MAX_WORD + 1]; + char fmt[32]; + + kword[MAX_WORD] = 0; + + in = fopen((char*)fname, "r"); + + if (!in) { + if (read_only) + PFATAL("Unable to open read-only wordlist '%s'.", fname); + else + PFATAL("Unable to open read-write wordlist '%s' (see doc/dictionaries.txt).", fname); + } + + sprintf(fmt, "%%2s %%u %%u %%u %%%u[^\x01-\x1f]", MAX_WORD); + +wordlist_retry: + + while ((fields = fscanf(in, fmt, type, &hits, &total_age, &last_age, kword)) + == 5) { + + u8 class = KW_GEN_AUTO; + + if (type[0] != 'e' && type[0] != 'w') + FATAL("Wordlist '%s': bad keyword type in line %u.\n", fname, lines + 1); + + if (type[1] == 's') class = KW_SPECIFIC; else + if (type[1] == 'g') class = KW_GENERIC; + + if (!purge_age || last_age < purge_age) + wordlist_confirm_single(kword, (type[0] == 'e'), class, read_only, hits, + total_age + 1, last_age + 1); + lines++; + fgetc(in); /* sink \n */ + } + + if (fields == 1 && !strcmp((char*)type, "#r")) { + DEBUG("Found %s (readonly:%d)\n", type, read_only); + if (!read_only) + FATAL("Attempt to load read-only wordlist '%s' via -W (use -S instead).\n", fname); + + fgetc(in); /* sink \n */ + goto wordlist_retry; + } + + if (fields != -1 && fields != 5) + FATAL("Wordlist '%s': syntax error in line %u.\n", fname, lines); + + if (!lines && (read_only || !keyword_total_cnt)) + WARN("Wordlist '%s' contained no valid entries.", fname); + + DEBUG("* Read %d lines from dictionary '%s' (read-only = %d).\n", lines, + fname, read_only); + + keyword_orig_cnt = keyword_total_cnt; + + fclose(in); + +} + + +/* qsort() callback for sorting keywords in save_keywords(). */ + +static int keyword_sorter(const void* word1, const void* word2) { + if (((struct kw_entry*)word1)->hit_cnt < ((struct kw_entry*)word2)->hit_cnt) + return 1; + else if (((struct kw_entry*)word1)->hit_cnt == + ((struct kw_entry*)word2)->hit_cnt) + return 0; + else return -1; +} + + +/* Saves all keywords to a file. */ + +void save_keywords(u8* fname) { + struct stat st; + FILE* out; + s32 fd; + u32 i, kh; + u8* old; + +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0 +#endif /* !O_NOFOLLOW */ + + /* Don't save keywords for /dev/null and other weird files. */ + + if (stat((char*)fname, &st) || !S_ISREG(st.st_mode)) return; + + /* First, sort the list. */ + + for (kh=0;khtype) { + case PIVOT_SERV: pivot_serv++; /* Fall through */ + case PIVOT_DIR: pivot_dir++; break; + case PIVOT_FILE: pivot_file++; break; + case PIVOT_PATHINFO: pivot_pinfo++; break; + case PIVOT_UNKNOWN: pivot_unknown++; break; + case PIVOT_PARAM: pivot_param++; break; + case PIVOT_VALUE: pivot_value++; break; + } + + if (pv->missing) pivot_missing++; + + switch (pv->state) { + case PSTATE_PENDING: pivot_pending++; break; + case PSTATE_FETCH ... PSTATE_IPS_CHECK: pivot_init++; break; + case PSTATE_CHILD_INJECT: + case PSTATE_PAR_INJECT: pivot_attack++; break; + case PSTATE_DONE: pivot_done++; break; + default: pivot_bf++; + } + + for (i=0;iissue_cnt;i++) + issue_cnt[PSEV(pv->issue[i].type)]++; + + for (i=0;ichild_cnt;i++) + pv_stat_crawl(pv->child[i]); + +} + + +void database_stats() { + + pivot_pending = pivot_init = pivot_attack = pivot_bf = pivot_pinfo = + pivot_done = pivot_serv = pivot_dir = pivot_file = pivot_param = + pivot_value = pivot_missing = pivot_unknown = pivot_cnt = 0; + + memset(issue_cnt, 0, sizeof(issue_cnt)); + + pv_stat_crawl(&root_pivot); + + SAY(cLBL "Database statistics:\n\n" + cGRA " Pivots : " cNOR "%u total, %u done (%.02f%%) \n" + cGRA " In progress : " cNOR "%u pending, %u init, %u attacks, " + "%u dict \n" + cGRA " Missing nodes : " cNOR "%u spotted\n" + cGRA " Node types : " cNOR "%u serv, %u dir, %u file, %u pinfo, " + "%u unkn, %u par, %u val\n" + cGRA " Issues found : " cNOR "%u info, %u warn, %u low, %u medium, " + "%u high impact\n" + cGRA " Dict size : " cNOR "%u words (%u new), %u extensions, " + "%u candidates\n" + cGRA " Signatures : " cNOR "%u total\n", + pivot_cnt, pivot_done, pivot_cnt ? ((100.0 * pivot_done) / (pivot_cnt)) + : 0, pivot_pending, pivot_init, pivot_attack, pivot_bf, pivot_missing, + pivot_serv, pivot_dir, pivot_file, pivot_pinfo, pivot_unknown, + pivot_param, pivot_value, issue_cnt[1], issue_cnt[2], issue_cnt[3], + issue_cnt[4], issue_cnt[5], keyword_total_cnt, keyword_total_cnt - + keyword_orig_cnt, wg_extension_cnt, guess_cnt, slist_cnt); + +} + + +/* Dumps pivot database, for debugging purposes. */ + +void dump_pivots(struct pivot_desc* cur, u8 nest) { + + u8* indent = ck_alloc(nest + 1); + u8* url; + u32 i; + + if (!cur) cur = &root_pivot; + + memset(indent, ' ', nest); + + SAY(cBRI "\n%s== Pivot " cLGN "%s" cBRI " [%d] ==\n", + indent, cur->name, cur->dupe); + SAY(cGRA "%sType : " cNOR, indent); + + switch (cur->type) { + case PIVOT_NONE: SAY(cLRD "PIVOT_NONE (bad!)\n" cNOR); break; + case PIVOT_ROOT: SAY("PIVOT_ROOT\n"); break; + case PIVOT_SERV: SAY("PIVOT_SERV\n"); break; + case PIVOT_DIR: SAY("PIVOT_DIR\n"); break; + case PIVOT_FILE: SAY("PIVOT_FILE\n"); break; + case PIVOT_PATHINFO: SAY("PIVOT_PATHINFO\n"); break; + case PIVOT_VALUE: SAY("PIVOT_VALUE\n"); break; + case PIVOT_UNKNOWN: SAY("PIVOT_UNKNOWN\n"); break; + case PIVOT_PARAM: SAY("PIVOT_PARAM\n"); break; + default: SAY(cLRD " (bad!)\n" cNOR, cur->type); + } + + SAY(cGRA "%sState : " cNOR, indent); + + switch (cur->state) { + case PSTATE_NONE: SAY(cLRD "PSTATE_NONE (bad!)\n" cNOR); break; + case PSTATE_PENDING: SAY("PSTATE_PENDING\n"); break; + case PSTATE_FETCH: SAY("PSTATE_FETCH\n"); break; + case PSTATE_TYPE_CHECK: SAY("PSTATE_TYPE_CHECK\n"); break; + case PSTATE_404_CHECK: SAY("PSTATE_404_CHECK\n"); break; + case PSTATE_PARENT_CHECK: SAY("PSTATE_PARENT_CHECK\n"); break; + case PSTATE_IPS_CHECK: SAY("PSTATE_IPS_CHECK\n"); break; + case PSTATE_CHILD_INJECT: SAY("PSTATE_CHILD_INJECT\n"); break; + case PSTATE_CHILD_DICT: SAY("PSTATE_CHILD_DICT\n"); break; + case PSTATE_PAR_CHECK: SAY("PSTATE_PAR_CHECK\n"); break; + case PSTATE_PAR_INJECT: SAY("PSTATE_PAR_INJECT\n"); break; + case PSTATE_PAR_NUMBER: SAY("PSTATE_PAR_NUMBER\n"); break; + case PSTATE_PAR_DICT: SAY("PSTATE_PAR_DICT\n"); break; + case PSTATE_PAR_TRYLIST: SAY("PSTATE_PAR_TRYLIST\n"); break; + case PSTATE_DONE: SAY("PSTATE_DONE\n"); break; + default: SAY(cLRD " (bad!)\n" cNOR, + cur->state); + } + + if (cur->missing) { + if (cur->linked == 2) + SAY(cGRA "%sMissing : " cMGN "YES\n" cNOR, indent); + else + SAY(cGRA "%sMissing : " cLBL "YES (followed a dodgy link)\n" cNOR, + indent); + } + + SAY(cGRA "%sFlags : " cNOR "linked %u, case %u/%u, fuzz_par %d, ips %u, " + "sigs %u, reqs %u, desc %u/%u\n", indent, cur->linked, cur->csens, cur->c_checked, + cur->fuzz_par, cur->uses_ips, cur->r404_cnt, cur->pending, cur->child_cnt, + cur->desc_cnt); + + if (cur->req) { + url = serialize_path(cur->req, 1, 0); + SAY(cGRA "%sTarget : " cNOR "%s (" cYEL "%d" cNOR ")\n", indent, url, + cur->res ? cur->res->code : 0); + ck_free(url); + + if (cur->res) + SAY(cGRA "%sMIME : " cNOR "%s -> %s [" + "%s:%s]\n", indent, cur->res->header_mime ? cur->res->header_mime : + (u8*)"-", cur->res->sniffed_mime ? cur->res->sniffed_mime : (u8*)"-", + cur->res->header_charset ? cur->res->header_charset : (u8*)"-", + cur->res->meta_charset ? cur->res->meta_charset : (u8*)"-"); + } + + if (cur->try_cnt) { + SAY(cGRA "%sTry : " cNOR, indent); + for (i=0;itry_cnt;i++) + SAY("%s%s", cur->try_list[i], (i == cur->try_cnt - 1) ? "" : ", "); + SAY("\n"); + } + + /* Dump issues. */ + + for (i=0;iissue_cnt;i++) { + if (cur->issue[i].req) url = serialize_path(cur->issue[i].req, 0, 0); + else url = ck_strdup((u8*)"[none]"); + SAY(cGRA "%s-> Issue : " cNOR "type %d, extra '%s', URL: " cLGN "%s" + cNOR " (" cYEL "%u" cNOR ")\n", indent, cur->issue[i].type, + cur->issue[i].extra, url, cur->issue[i].res ? cur->issue[i].res->code + : 0); + ck_free(url); + } + + ck_free(indent); + + for (i=0;ichild_cnt;i++) + dump_pivots(cur->child[i], nest + 1); + +} + + +/* Cleans up pivot structure for memory debugging. */ + +static void dealloc_pivots(struct pivot_desc* cur) { + u32 i; + + if (!cur) cur = &root_pivot; + + if (cur->req) destroy_request(cur->req); + if (cur->res) destroy_response(cur->res); + + ck_free(cur->name); + + if (cur->try_cnt) { + for (i=0;itry_cnt;i++) ck_free(cur->try_list[i]); + ck_free(cur->try_list); + } + + if (cur->issue) { + for (i=0;iissue_cnt;i++) { + ck_free(cur->issue[i].extra); + if (cur->issue[i].req) destroy_request(cur->issue[i].req); + if (cur->issue[i].res) destroy_response(cur->issue[i].res); + } + ck_free(cur->issue); + } + + for (i=0;ichild_cnt;i++) + dealloc_pivots(cur->child[i]); + + ck_free(cur->child); + + if (cur != &root_pivot) ck_free(cur); + +} + + +/* Creates a new XSS location tag. */ + +u8* new_xss_tag(u8* prefix) { + static u8* ret; + + if (ret) __DFL_ck_free(ret); + ret = __DFL_ck_alloc((prefix ? strlen((char*)prefix) : 0) + 32); + + if (!scan_id) scan_id = R(999999) + 1; + + sprintf((char*)ret, "%s-->\">'>'\"", + prefix ? prefix : (u8*)"", cur_xss_id, scan_id); + + return ret; + +} + + +/* Registers last XSS tag along with a completed http_request */ + +void register_xss_tag(struct http_request* req) { + xss_req = ck_realloc(xss_req, (cur_xss_id + 1) * + (sizeof(struct http_request*))); + xss_req[cur_xss_id] = req_copy(req, 0, 1); + cur_xss_id++; +} + + +/* Gets the request that submitted the tag in the first place */ + +struct http_request* get_xss_request(u32 xid, u32 sid) { + if (sid != scan_id || xid >= cur_xss_id) return NULL; + return xss_req[xid]; +} + + +/* Cleans up other database entries, for memory profiling purposes. */ + +void destroy_database() { + u32 i, kh; + + dealloc_pivots(0); + + ck_free(deny_urls); + ck_free(allow_urls); + ck_free(allow_domains); + ck_free(trust_domains); + + ck_free(addl_form_name); + ck_free(addl_form_value); + ck_free(skip_params); + + for (kh=0;kh + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#ifndef _HAVE_DATABASE_H +#define _HAVE_DATABASE_H + +#include "debug.h" +#include "config.h" +#include "types.h" +#include "http_client.h" + +/* Testing pivot points - used to organize the scan: */ + +/* - Pivot types: */ + +#define PIVOT_NONE 0 /* Invalid */ +#define PIVOT_ROOT 1 /* Root pivot */ + +#define PIVOT_SERV 2 /* Top-level host pivot */ +#define PIVOT_DIR 4 /* Directory pivot */ +#define PIVOT_FILE 8 /* File pivot */ +#define PIVOT_PATHINFO 16 /* PATH_INFO script */ + +#define PIVOT_UNKNOWN 32 /* (Currently) unknown type */ + +#define PIVOT_PARAM 64 /* Parameter fuzzing pivot */ +#define PIVOT_VALUE 128 /* Parameter value pivot */ + +/* - Pivot states (initialized to PENDING or FETCH by database.c, then + advanced by crawler.c): */ + +#define PSTATE_NONE 0 /* Invalid */ +#define PSTATE_PENDING 1 /* Pending parent tests */ + +#define PSTATE_FETCH 10 /* Initial data fetch */ + +#define PSTATE_TYPE_CHECK 20 /* Type check (unknown only) */ +#define PSTATE_404_CHECK 22 /* 404 check (dir only) */ +#define PSTATE_PARENT_CHECK 24 /* Parent check (dir only) */ +#define PSTATE_IPS_CHECK 26 /* IPS filtering check */ + +/* For directories only (injecting children nodes): */ + +#define PSTATE_CHILD_INJECT 50 /* Common security attacks */ +#define PSTATE_CHILD_DICT 55 /* Dictionary brute-force */ + +/* For parametric nodes only (replacing parameter value): */ + +#define PSTATE_PAR_CHECK 60 /* Parameter works at all? */ +#define PSTATE_PAR_INJECT 65 /* Common security attacks */ +#define PSTATE_PAR_NUMBER 70 /* Numeric ID traversal */ +#define PSTATE_PAR_DICT 75 /* Dictionary brute-force */ +#define PSTATE_PAR_TRYLIST 99 /* 'Try list' fetches */ + +#define PSTATE_DONE 100 /* Analysis done */ + +/* - Descriptor of a pivot point: */ + +struct pivot_desc { + u8 type; /* PIVOT_* */ + u8 state; /* PSTATE_* */ + u8 linked; /* Linked to? (0/1/2) */ + u8 missing; /* Determined to be missing? */ + + u8 csens; /* Case sensitive names? */ + u8 c_checked; /* csens check done? */ + + u8* name; /* Directory / script name */ + + struct http_request* req; /* Prototype HTTP request */ + + u8 browsers; /* Discovered user-agents */ + u8 browser; /* The used user-agent */ + + s32 fuzz_par; /* Fuzz target parameter */ + u8** try_list; /* Values to try */ + u32 try_cnt; /* Number of values to try */ + u32 try_cur; /* Last tested try list offs */ + + struct pivot_desc* parent; /* Parent pivot, if any */ + struct pivot_desc** child; /* List of children */ + u32 child_cnt; /* Number of children */ + u32 desc_cnt; /* Number of descendants */ + + struct issue_desc* issue; /* List of issues found */ + u32 issue_cnt; /* Number of issues */ + u32 desc_issue_cnt; /* Number of child issues */ + + struct http_response* res; /* HTTP response seen */ + + u8 res_varies; /* Response varies? */ + u8 bad_parent; /* Parent is well-behaved? */ + u8 res_time_exceeds; /* Response time too long? */ + u32 res_time_base; /* Base response time */ + + /* Fuzzer and probe state data: */ + + u8 no_fuzz; /* Do not attepmt fuzzing. */ + u8 sure_dir; /* Very sure it's a dir? */ + + u8 uses_ips; /* Uses IPS filtering? */ + + u32 cur_key; /* Current keyword */ + u32 pdic_cur_key; /* ...for param dict */ + + u8 guess; /* Guess list keywords? */ + u8 pdic_guess; /* ...for param dict */ + + u32 pending; /* Number of pending reqs */ + u32 pdic_pending; /* ...for param dict */ + u32 num_pending; /* ...for numerical enum */ + u32 try_pending; /* ...for try list */ + u32 r404_pending; /* ...for 404 probes */ + u32 ck_pending; /* ...for behavior checks */ + + s32 check_idx; /* Current test index */ + u32 check_state; /* Current test state */ + u32 check_id; /* Current test id */ + + struct http_sig r404[MAX_404]; /* 404 response signatures */ + u32 r404_cnt; /* Number of sigs collected */ + struct http_sig unk_sig; /* Original "unknown" sig. */ + + /* Injection attack logic scratchpad: */ + +#define MISC_ENTRIES 64 + + struct http_request* misc_req[MISC_ENTRIES]; /* Saved requests */ + struct http_response* misc_res[MISC_ENTRIES]; /* Saved responses */ + u8 misc_cnt; /* Request / response count */ + +#define MAX_CHECKS 32 + u8 i_skip[MAX_CHECKS]; /* Injection step skip flags */ + u8 i_skip_add; + u8 r404_skip; + + u8 bogus_par; /* fuzz_par does nothing? */ + + u8 ognl_check; /* OGNL check flags */ + + /* Reporting information: */ + + u32 total_child_cnt; /* All children */ + u32 total_issues[6]; /* Issues by severity */ + u8 dupe; /* Looks like a duplicate? */ + u32 pv_sig; /* Simple pivot signature */ + +}; + +extern struct pivot_desc root_pivot; +extern u32 verbosity; +extern u32 slist_cnt; + +/* Checks child / descendant limits. */ + +u8 descendants_ok(struct pivot_desc* pv); + +/* Increases descendant count. */ + +void add_descendant(struct pivot_desc* pv); + +/* Maps a parsed URL (in req) to the pivot tree, creating or modifying nodes + as necessary, and scheduling them for crawl; via_link should be 1 if the + URL came from an explicit link or user input, 0 if brute-forced. + + Always makes a copy of req, res; they can be destroyed safely; via_link + set to 2 means we're sure it's a valid link; 1 means "probably". */ + +void maybe_add_pivot(struct http_request* req, struct http_response* res, + u8 via_link); + +/* Creates a working copy of a request for use in db and crawl functions. If all + is 0, does not copy path, query parameters, or POST data (but still + copies headers); and forces GET method. */ + +struct http_request* req_copy(struct http_request* req, + struct pivot_desc* pv, u8 all); + +/* Finds the host-level pivot point for global issues. */ + +struct pivot_desc* host_pivot(struct pivot_desc* pv); + +/* Case sensitivity helper. */ + +u8 is_c_sens(struct pivot_desc* pv); + +/* Lookup an issue title */ + +u8* lookup_issue_title(u32 id); + +/* Remove issues from a pivot */ + +void remove_issue(struct pivot_desc *pv, u32 type); + +/* Recorded security issues */ + +/* - Informational data (non-specific security-relevant notes): */ + +#define PROB_NONE 0 /* Invalid */ + +#define PROB_SSL_CERT 10101 /* SSL issuer data */ +#define PROB_SSL_CERT_EXPIRE 10102 /* SSL cert will expire */ + +#define PROB_NEW_COOKIE 10201 /* New cookie added */ +#define PROB_SERVER_CHANGE 10202 /* New Server: value seen */ +#define PROB_VIA_CHANGE 10203 /* New Via: value seen */ +#define PROB_X_CHANGE 10204 /* New X-*: value seen */ +#define PROB_NEW_404 10205 /* New 404 signatures seen */ + +#define PROB_NO_ACCESS 10401 /* Resource not accessible */ +#define PROB_AUTH_REQ 10402 /* Authentication requires */ +#define PROB_SERV_ERR 10403 /* Server error */ +#define PROB_DIR_LIST 10404 /* Directory listing */ +#define PROB_HIDDEN_NODE 10405 /* Hidden resource found */ + +#define PROB_EXT_LINK 10501 /* External link */ +#define PROB_EXT_REDIR 10502 /* External redirector */ +#define PROB_MAIL_ADDR 10503 /* E-mail address seen */ +#define PROB_UNKNOWN_PROTO 10504 /* Unknown protocol in URL */ +#define PROB_UNKNOWN_FIELD 10505 /* Unknown form field */ + +#define PROB_FORM 10601 /* XSRF-safe form */ +#define PROB_PASS_FORM 10602 /* Password form */ +#define PROB_FILE_FORM 10603 /* File upload form */ + +#define PROB_USER_LINK 10701 /* User-supplied A link */ + +#define PROB_BAD_MIME_STAT 10801 /* Bad MIME type, low risk */ +#define PROB_GEN_MIME_STAT 10802 /* Generic MIME, low risk */ +#define PROB_BAD_CSET_STAT 10803 /* Bad charset, low risk */ +#define PROB_CFL_HDRS_STAT 10804 /* Conflicting hdr, low risk */ + +#define PROB_FUZZ_DIGIT 10901 /* Try fuzzing file name */ +#define PROB_OGNL 10902 /* OGNL-like parameter */ + +#define PROB_SIG_DETECT 10909 /* Signature detected info */ + +/* - Internal warnings (scan failures, etc): */ + +#define PROB_FETCH_FAIL 20101 /* Fetch failed. */ +#define PROB_LIMITS 20102 /* Crawl limits exceeded. */ + +#define PROB_404_FAIL 20201 /* Behavior probe failed. */ +#define PROB_PARENT_FAIL 20202 /* Parent behavior problem */ +#define PROB_IPS_FILTER 20203 /* IPS behavior detected. */ +#define PROB_IPS_FILTER_OFF 20204 /* IPS no longer active. */ +#define PROB_VARIES 20205 /* Response varies. */ + +#define PROB_NOT_DIR 20301 /* Node should be a dir. */ + +/* - Low severity issues (limited impact or check specificity): */ + +#define PROB_URL_AUTH 30101 /* HTTP credentials in URL */ + +#define PROB_SSL_CERT_DATE 30201 /* SSL cert date invalid */ +#define PROB_SSL_SELF_CERT 30202 /* Self-signed SSL cert */ +#define PROB_SSL_BAD_HOST 30203 /* Certificate host mismatch */ +#define PROB_SSL_NO_CERT 30204 /* No certificate data? */ +#define PROB_SSL_WEAK_CIPHER 30205 /* Weak cipher negotiated */ +#define PROB_SSL_HOST_LEN 30206 /* Possible \0 in host name */ + +#define PROB_DIR_LIST_BYPASS 30301 /* Dir listing bypass */ + +#define PROB_URL_REDIR 30401 /* URL redirection */ +#define PROB_USER_URL 30402 /* URL content inclusion */ + +#define PROB_EXT_OBJ 30501 /* External obj standalone */ +#define PROB_MIXED_OBJ 30502 /* Mixed content standalone */ +#define PROB_MIXED_FORM 30503 /* HTTPS -> HTTP form */ + +#define PROB_VULN_FORM 30601 /* Form w/o anti-XSRF token */ +#define PROB_JS_XSSI 30602 /* Script with no XSSI prot */ + +#define PROB_CACHE_LOW 30701 /* Cache nit-picking */ + +#define PROB_PROLOGUE 30801 /* User-supplied prologue */ +#define PROB_XSS_VECTOR 30802 /* XSS vector, lower risk */ + +#define PROB_HEADER_INJECT 30901 /* Injected string in header */ + +#define PROB_SIG_DETECT_L 30909 /* Signature detected low */ + +/* - Moderate severity issues (data compromise): */ + +#define PROB_BODY_XSS 40101 /* Document body XSS */ +#define PROB_URL_XSS 40102 /* URL-based XSS */ +#define PROB_HTTP_INJECT 40103 /* Header splitting */ +#define PROB_USER_URL_ACT 40104 /* Active user content */ +#define PROB_TAG_XSS 40105 /* TAG attribute XSS */ + +#define PROB_EXT_SUB 40201 /* External subresource */ +#define PROB_MIXED_SUB 40202 /* Mixed content subresource */ + +#define PROB_BAD_MIME_DYN 40301 /* Bad MIME type, hi risk */ +#define PROB_GEN_MIME_DYN 40302 /* Generic MIME, hi risk */ +#define PROB_BAD_CSET_DYN 40304 /* Bad charset, hi risk */ +#define PROB_CFL_HDRS_DYN 40305 /* Conflicting hdr, hi risk */ + +#define PROB_FILE_POI 40401 /* Interesting file */ +#define PROB_ERROR_POI 40402 /* Interesting error message */ + +#define PROB_DIR_TRAVERSAL 40501 /* Directory traversal */ + +#define PROB_CACHE_HI 40601 /* Serious caching issues */ + +#define PROB_PASS_NOSSL 40701 /* Password form, no HTTPS */ + +#define PROB_SIG_DETECT_M 40909 /* Signature detected moderate*/ + +/* - High severity issues (system compromise): */ + +#define PROB_XML_INJECT 50101 /* Backend XML injection */ +#define PROB_SH_INJECT 50102 /* Shell cmd injection */ +#define PROB_SQL_INJECT 50103 /* SQL injection */ +#define PROB_FMT_STRING 50104 /* Format string attack */ +#define PROB_INT_OVER 50105 /* Integer overflow attack */ +#define PROB_FI_LOCAL 50106 /* Local file inclusion */ +#define PROB_FI_REMOTE 50107 /* Remote file inclusion */ + +#define PROB_SQL_PARAM 50201 /* SQL-like parameter */ + +#define PROB_PUT_DIR 50301 /* HTTP PUT accepted */ + +#define PROB_SIG_DETECT_H 50909 /* Signature detected high */ + + +#ifdef _VIA_DATABASE_C + +/* The definitions below are used to make problems, which are displayed + during runtime, more informational */ + +struct pstruct { + u32 id; + u8* title; +}; + +struct pstruct pstructs[] = { + +/* - Informational data (non-specific security-relevant notes): */ + { PROB_SSL_CERT, (u8*)"SSL certificate issuer information" }, + { PROB_NEW_COOKIE, (u8*)"New HTTP cookie added" }, + { PROB_SERVER_CHANGE, (u8*)"New 'Server' header value seen" }, + { PROB_VIA_CHANGE, (u8*)"New 'Via' header value seen" }, + { PROB_X_CHANGE, (u8*)"New 'X-*' header value seen" }, + { PROB_NEW_404, (u8*)"New 404 signature seen" }, + { PROB_NO_ACCESS, (u8*)"Resource not directly accessible" }, + { PROB_AUTH_REQ, (u8*)"HTTP authentication required" }, + { PROB_SERV_ERR, (u8*)"Server error triggered" }, + { PROB_DIR_LIST, (u8*)"Directory listing found" }, + { PROB_EXT_LINK, (u8*)"All external links" }, + { PROB_EXT_REDIR, (u8*)"External URL redirector" }, + { PROB_MAIL_ADDR, (u8*)"All e-mail addresses" }, + { PROB_UNKNOWN_PROTO, (u8*)"Links to unknown protocols" }, + { PROB_UNKNOWN_FIELD, (u8*)"Unknown form field (can't autocomplete)" }, + { PROB_FORM, (u8*)"HTML form (not classified otherwise)" }, + { PROB_PASS_FORM, (u8*)"Password entry form - consider brute-force" }, + { PROB_FILE_FORM, (u8*)"File upload form" }, + { PROB_USER_LINK, (u8*)"User-supplied link rendered on a page" }, + { PROB_BAD_MIME_STAT, (u8*)"Incorrect or missing MIME type (low risk)" }, + { PROB_GEN_MIME_STAT, (u8*)"Generic MIME used (low risk)" }, + { PROB_BAD_CSET_STAT, (u8*)"Incorrect or missing charset (low risk)" }, + { PROB_CFL_HDRS_STAT, (u8*)"Conflicting MIME / charset info (low risk)" }, + { PROB_FUZZ_DIGIT, (u8*)"Numerical filename - consider enumerating" }, + { PROB_OGNL, (u8*)"OGNL-like parameter behavior" }, +/* - Internal warnings (scan failures, etc): */ + { PROB_FETCH_FAIL, (u8*)"Resource fetch failed" }, + { PROB_LIMITS, (u8*)"Limits exceeded, fetch suppressed" }, + { PROB_404_FAIL, (u8*)"Directory behavior checks failed (no brute force)" }, + { PROB_PARENT_FAIL, (u8*)"Parent behavior checks failed (no brute force)" }, + { PROB_IPS_FILTER, (u8*)"IPS filtering enabled" }, + { PROB_IPS_FILTER_OFF, (u8*)"IPS filtering disabled again" }, + { PROB_VARIES, (u8*)"Response varies randomly, skipping checks" }, + { PROB_NOT_DIR, (u8*)"Node should be a directory, detection error?" }, + +/* - Low severity issues (limited impact or check specificity): */ + { PROB_URL_AUTH, (u8*)"HTTP credentials seen in URLs" }, + { PROB_SSL_CERT_DATE, (u8*)"SSL certificate expired or not yet valid" }, + { PROB_SSL_SELF_CERT, (u8*)"Self-signed SSL certificate" }, + { PROB_SSL_BAD_HOST, (u8*)"SSL certificate host name mismatch" }, + { PROB_SSL_NO_CERT, (u8*)"No SSL certificate data found" }, + { PROB_SSL_WEAK_CIPHER, (u8*)"Weak SSL cipher negotiated" }, + { PROB_DIR_LIST, (u8*)"Directory listing restrictions bypassed" }, + { PROB_URL_REDIR, (u8*)"Redirection to attacker-supplied URLs" }, + { PROB_USER_URL, (u8*)"Attacker-supplied URLs in embedded content (lower risk)" }, + { PROB_EXT_OBJ, (u8*)"External content embedded on a page (lower risk)" }, + { PROB_MIXED_OBJ, (u8*)"Mixed content embedded on a page (lower risk)" }, + { PROB_MIXED_FORM, (u8*)"HTTPS form submitting to a HTTP URL" }, + { PROB_VULN_FORM, (u8*)"HTML form with no apparent XSRF protection" }, + { PROB_JS_XSSI, (u8*)"JSON response with no apparent XSSI protection" }, + { PROB_CACHE_LOW, (u8*)"Incorrect caching directives (lower risk)" }, + { PROB_PROLOGUE, (u8*)"User-controlled response prefix (BOM / plugin attacks)" }, + { PROB_HEADER_INJECT, (u8*)"HTTP header injection vector" }, + +/* - Moderate severity issues (data compromise): */ + { PROB_BODY_XSS, (u8*)"XSS vector in document body" }, + { PROB_URL_XSS, (u8*)"XSS vector via arbitrary URLs" }, + { PROB_HTTP_INJECT, (u8*)"HTTP response header splitting" }, + { PROB_USER_URL_ACT, (u8*)"Attacker-supplied URLs in embedded content (higher risk)" }, + { PROB_EXT_SUB, (u8*)"External content embedded on a page (higher risk)" }, + { PROB_MIXED_SUB, (u8*)"Mixed content embedded on a page (higher risk)" }, + { PROB_BAD_MIME_DYN, (u8*)"Incorrect or missing MIME type (higher risk)" }, + { PROB_GEN_MIME_DYN, (u8*)"Generic MIME type (higher risk)" }, + { PROB_BAD_CSET_DYN, (u8*)"Incorrect or missing charset (higher risk)" }, + { PROB_CFL_HDRS_DYN, (u8*)"Conflicting MIME / charset info (higher risk)" }, + { PROB_FILE_POI, (u8*)"Interesting file" }, + { PROB_ERROR_POI, (u8*)"Interesting server message" }, + { PROB_DIR_TRAVERSAL, (u8*)"Directory traversal / file inclusion possible" }, + { PROB_CACHE_HI, (u8*)"Incorrect caching directives (higher risk)" }, + { PROB_PASS_NOSSL, (u8*)"Password form submits from or to non-HTTPS page" }, + +/* - High severity issues (system compromise): */ + + { PROB_XML_INJECT, (u8*)"Server-side XML injection vector" }, + { PROB_SH_INJECT, (u8*)"Shell injection vector" }, + { PROB_SQL_INJECT, (u8*)"Query injection vector" }, + { PROB_FMT_STRING, (u8*)"Format string vector" }, + { PROB_INT_OVER, (u8*)"Integer overflow vector" }, + { PROB_FI_LOCAL, (u8*)"File inclusion" }, + { PROB_SQL_PARAM, (u8*)"SQL query or similar syntax in parameters" }, + { PROB_PUT_DIR, (u8*)"PUT request accepted" }, + { PROB_NONE, (u8*)"Invalid" } +}; + +#endif /* _VIA_DATABASE_C */ + +/* - Severity macros: */ + +#define PSEV(_x) ((_x) / 10000) +#define PSEV_INFO 1 +#define PSEV_WARN 2 +#define PSEV_LOW 3 +#define PSEV_MED 4 +#define PSEV_HI 5 + +/* Issue descriptor: */ + +struct issue_desc { + u32 type; /* PROB_* */ + u8* extra; /* Problem-specific string */ + u32 sid; /* Source ID, if any */ + struct http_request* req; /* HTTP request sent */ + struct http_response* res; /* HTTP response seen */ +}; + + + +/* Register a problem, if not duplicate (res, extra may be NULL): */ +void register_problem(u32 type, u32 sid, struct http_request* req, + struct http_response* res, u8* extra, + struct pivot_desc* pv, u8 allow_dup); + +/* Wrapper for register_problem */ +void problem(u32 type, struct http_request* req, struct http_response* res, + u8* extra, struct pivot_desc* pv, u8 allow_dup); + +/* Compare the checksums for two responses: */ + +u8 same_page(struct http_sig* sig1, struct http_sig* sig2); + +extern u8 **deny_urls, **allow_urls, **allow_domains, + **trust_domains, **skip_params; + +extern u32 num_deny_urls, + num_allow_urls, + num_allow_domains, + num_trust_domains, + num_skip_params; + +extern u32 max_depth, + max_children, + max_descendants, + max_trylist, + max_guesses; + +extern u32 guess_cnt, + wg_extension_cnt, + keyword_total_cnt, + keyword_orig_cnt; + +/* Check if the URL is permitted under current rules (0 = no, 1 = yes): */ + +u8 url_allowed_host(struct http_request* req); +u8 url_trusted_host(struct http_request* req); +u8 url_allowed(struct http_request* req); +u8 param_allowed(u8* pname); + +/* Keyword management: */ + +extern u8 dont_add_words; + +/* Adds a new keyword candidate to the "guess" list. */ + +void wordlist_add_guess(u8* text); + +/* Adds non-sanitized keywords to the list. */ + +void wordlist_confirm_word(u8* text); + +/* Returns wordlist item at a specified offset (NULL if no more available). */ + +u8* wordlist_get_word(u32 offset, u8* specific); + +/* Returns keyword candidate at a specified offset (or NULL). */ + +u8* wordlist_get_guess(u32 offset, u8* specific); + +/* Returns extension at a specified offset (or NULL). */ + +u8* wordlist_get_extension(u32 offset, u8 specific); + +/* Loads keywords from file. */ + +void load_keywords(u8* fname, u8 read_only, u32 purge_age); + +/* Saves all keywords to a file. */ + +void save_keywords(u8* fname); + +/* Database maintenance: */ + +/* Dumps pivot database, for debugging purposes. */ + +void dump_pivots(struct pivot_desc* cur, u8 nest); + +/* Deallocates all data, for debugging purposes. */ + +void destroy_database(); + +/* Prints DB stats. */ + +void database_stats(); + +/* XSS manager: */ + +/* Creates a new stored XSS id (buffer valid only until next call). */ + +u8* new_xss_tag(u8* prefix); + +/* Registers last XSS tag along with a completed http_request. */ + +void register_xss_tag(struct http_request* req); + +/* Returns request associated with a stored XSS id. */ + +struct http_request* get_xss_request(u32 xid, u32 sid); + +/* Dumps signature data: */ + +void dump_signature(struct http_sig* sig); + +/* Displays debug information for same_page() checks. */ + +void debug_same_page(struct http_sig* sig1, struct http_sig* sig2); + +#endif /* _HAVE_DATABASE_H */ diff -Nru skipfish-2.02b/src/debug.h skipfish-2.10b/src/debug.h --- skipfish-2.02b/src/debug.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/debug.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,121 @@ +/* + + skipfish - debugging and messaging macros + ----------------------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#ifndef _HAVE_DEBUG_H +#define _HAVE_DEBUG_H + +#include +#include "config.h" + +#ifdef USE_COLOR +# define cBLK "\x1b[0;30m" +# define cRED "\x1b[0;31m" +# define cGRN "\x1b[0;32m" +# define cBRN "\x1b[0;33m" +# define cBLU "\x1b[0;34m" +# define cMGN "\x1b[0;35m" +# define cCYA "\x1b[0;36m" +# define cNOR "\x1b[0;37m" +# define cGRA "\x1b[1;30m" +# define cLRD "\x1b[1;31m" +# define cLGN "\x1b[1;32m" +# define cYEL "\x1b[1;33m" +# define cLBL "\x1b[1;34m" +# define cPIN "\x1b[1;35m" +# define cLCY "\x1b[1;36m" +# define cBRI "\x1b[1;37m" +# define cRST "\x1b[0m" +#else +# define cBLK "" +# define cRED "" +# define cGRN "" +# define cBRN "" +# define cBLU "" +# define cMGN "" +# define cCYA "" +# define cNOR "" +# define cGRA "" +# define cLRD "" +# define cLGN "" +# define cYEL "" +# define cLBL "" +# define cPIN "" +# define cLCY "" +# define cBRI "" +# define cRST "" +#endif /* ^USE_COLOR */ + +#ifdef LOG_STDERR +# define DEBUG(x...) fprintf(stderr,x) +#else +# define DEBUG(x...) +#endif /* ^LOG_STDERR */ + +#define F_DEBUG(x...) fprintf(stderr,x) +#define SAY(x...) printf(x) + +#define L1 1 /* Informative, one line messages */ +#define L2 2 /* Expand the above, dump reqs, resps */ +#define L3 3 /* todo(heinenn) do we need this.. */ + +#ifdef LOG_STDERR + #define DEBUGC(_l, x...) DEBUG(x) +#else + #define DEBUGC(_l, x...) do { \ + if(_l <= verbosity) { \ + fprintf(stderr, x); \ + } \ + } while (0) +#endif /* LOG_STDERR */ + + +#define WARN(x...) do { \ + F_DEBUG(cYEL "[!] WARNING: " cBRI x); \ + F_DEBUG(cRST "\n"); \ + } while (0) + +#define FATAL(x...) do { \ + F_DEBUG(cLRD "[-] PROGRAM ABORT : " cBRI x); \ + F_DEBUG(cLRD "\n Stop location : " cNOR "%s(), %s:%u\n" cRST, \ + __FUNCTION__, __FILE__, __LINE__); \ + exit(1); \ + } while (0) + +#define ABORT(x...) do { \ + F_DEBUG(cLRD "[-] PROGRAM ABORT : " cBRI x); \ + F_DEBUG(cLRD "\n Stop location : " cNOR "%s(), %s:%u\n" cRST, \ + __FUNCTION__, __FILE__, __LINE__); \ + abort(); \ + } while (0) + +#define PFATAL(x...) do { \ + F_DEBUG(cLRD "[-] SYSTEM ERROR : " cBRI x); \ + F_DEBUG(cLRD "\n Stop location : " cNOR "%s(), %s:%u\n", \ + __FUNCTION__, __FILE__, __LINE__); \ + perror(cLRD " OS message " cNOR); \ + F_DEBUG(cRST); \ + exit(1); \ + } while (0) + + +#endif /* ! _HAVE_DEBUG_H */ diff -Nru skipfish-2.02b/src/http_client.c skipfish-2.10b/src/http_client.c --- skipfish-2.02b/src/http_client.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/http_client.c 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,2795 @@ +/* + skipfish - high-performance, single-process asynchronous HTTP client + -------------------------------------------------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "types.h" +#include "alloc-inl.h" +#include "string-inl.h" +#include "database.h" + +#include "http_client.h" + +/* Assorted exported settings: */ + +u32 max_connections = MAX_CONNECTIONS, + max_conn_host = MAX_CONN_HOST, + max_requests = MAX_REQUESTS, + max_fail = MAX_FAIL, + idle_tmout = IDLE_TMOUT, + resp_tmout = RESP_TMOUT, + rw_tmout = RW_TMOUT, + size_limit = SIZE_LIMIT; + +u8 browser_type = BROWSER_FAST; +u8 auth_type = AUTH_NONE; + +float max_requests_sec = MAX_REQUESTS_SEC; + +struct param_array global_http_par; + +/* Counters: */ + +float req_sec; + +u32 req_errors_net, + req_errors_http, + req_errors_cur, + req_count, + req_dropped, + queue_cur, + conn_cur, + conn_count, + conn_idle_tmout, + conn_busy_tmout, + conn_failed, + req_retried, + url_scope; + +u64 bytes_sent, + bytes_recv, + bytes_deflated, + bytes_inflated, + iterations_cnt = 0; + +u8 *auth_user, + *auth_pass; + + +#ifdef PROXY_SUPPORT +u8* use_proxy; +u32 use_proxy_addr; +u16 use_proxy_port; +#endif /* PROXY_SUPPORT */ + +u8 ignore_cookies, + idle; + +/* Internal globals for queue management: */ + +static struct queue_entry* queue; +static struct conn_entry* conn; +static struct dns_entry* dns; + +#ifdef QUEUE_FILO +static struct queue_entry* q_tail; +#endif /* QUEUE_FILO */ + +static u8 tear_down_idle; + + +/* Extracts parameter value from param_array. Name is matched if + non-NULL. Returns pointer to value data, not a duplicate string; + NULL if no match found. */ + +u8* get_value(u8 type, u8* name, u32 offset, + struct param_array* par) { + + u32 i, coff = 0; + + for (i=0;ic;i++) { + if (type != par->t[i]) continue; + if (name && (!par->n[i] || strcasecmp((char*)par->n[i], (char*)name))) + continue; + + if (offset != coff) { coff++; continue; } + return par->v[i]; + } + + return NULL; + +} + +/* Inserts or overwrites parameter value in param_array. If offset + == -1, will append parameter to list. Duplicates strings, + name and val can be NULL. */ + +void set_value(u8 type, u8* name, u8* val, + s32 offset, struct param_array* par) { + + u32 i, coff = 0; + s32 matched = -1; + + /* If offset specified, try to find an entry to replace. */ + + if (offset >= 0) + for (i=0;ic;i++) { + if (type != par->t[i]) continue; + if (name && (!par->n[i] || strcasecmp((char*)par->n[i], (char*)name))) + continue; + if (offset != coff) { coff++; continue; } + matched = i; + break; + } + + if (matched == -1) { + + /* No offset or no match - append to the end of list. */ + + par->t = ck_realloc(par->t, (par->c + 1) * sizeof(u8)); + par->n = ck_realloc(par->n, (par->c + 1) * sizeof(u8*)); + par->v = ck_realloc(par->v, (par->c + 1) * sizeof(u8*)); + par->t[par->c] = type; + par->n[par->c] = ck_strdup(name); + par->v[par->c] = ck_strdup(val); + par->c++; + + } else { + + /* Matched - replace name & value. */ + + ck_free(par->n[matched]); + ck_free(par->v[matched]); + par->n[matched] = ck_strdup(name); + par->v[matched] = ck_strdup(val); + + } + +} + + +/* Convert a fully-qualified or relative URL string to a proper http_request + representation. Returns 0 on success, 1 on format error. */ + +u8 parse_url(u8* url, struct http_request* req, struct http_request* ref) { + + u8* cur = url; + u32 maybe_proto = strcspn((char*)url, ":/?#@"); + u8 has_host = 0, add_slash = 1; + + if (strlen((char*)url) > MAX_URL_LEN) return 1; + req->orig_url = ck_strdup(url); + + /* Interpret, skip protocol string if the URL seems to be fully-qualified; + otherwise, copy from referring URL. We could be stricter here, as + browsers bail out on seemingly invalid chars in proto names, but... */ + + if (maybe_proto && url[maybe_proto] == ':') { + + if (!case_prefix(url, "http:")) { + req->proto = PROTO_HTTP; + cur += 5; + } else if (!case_prefix(url, "https:")) { + req->proto = PROTO_HTTPS; + cur += 6; + } else return 1; + + } else { + + if (!ref || !ref->proto) return 1; + req->proto = ref->proto; + + } + + /* Interpret, skip //[login[:pass@](\[ipv4\]|\[ipv6\]|host)[:port] part of the + URL, if present. Note that "http:blarg" is a valid relative URL to most + browsers, and "//example.com/blarg" is a valid non-FQ absolute one. + We need to mimick this, which complicates the code a bit. + + We only accept /, ?, #, and : to mark the end of a host name. Some browsers + also allow \ or ;, but it's unlikely that we need to obey this. */ + + if (cur[0] == '/' && cur[1] == '/') { + + u32 path_st; + u8 *at_sign, *host, *x; + u8 has_utf = 0; + + cur += 2; + + /* Detect, skip login[:pass]@; we only use cmdline-supplied credentials or + wordlists into account. Be sure to report any embedded auth, though. + + Trivia: Firefox takes the rightmost, not the leftmost @ char into + account. Not very important, but amusing. */ + + at_sign = (u8*)strchr((char*)cur, '@'); + path_st = strcspn((char*)cur, "/?#"); + + if (at_sign && path_st > (at_sign - cur)) { + cur = at_sign + 1; + if (!req->pivot) return 1; + problem(PROB_URL_AUTH, ref, 0, url, req->pivot, 0); + } + + path_st = strcspn((char*)cur, ":/?#"); + + /* No support for IPv6 or [ip] notation for now, so let's just refuse to + parse the URL. Also, refuse excessively long domain names for sanity. */ + + if (*cur == '[') return 1; + if (path_st > MAX_DNS_LEN) return 1; + + x = host = ck_memdup(cur, path_st + 1); + host[path_st] = 0; + + /* Scan, normalize extracted host name. */ + + while (*x) { + + switch (*x) { + + case 'A' ... 'Z': + *x = tolower(*x); + break; + + case 'a' ... 'z': + case '0' ... '9': + case '.': + case '-': + case '_': + break; + + case 0x80 ... 0xff: + has_utf = 1; + break; + + default: + /* Uh-oh, invalid characters in a host name - abandon ship. */ + ck_free(host); + return 1; + + } + + x++; + + } + + /* Host names that contained high bits need to be converted to Punycode + in order to resolve properly. */ + + if (has_utf) { + + char* output = 0; + + if (idna_to_ascii_8z((char*)host, &output, 0) != IDNA_SUCCESS || + strlen(output) > MAX_DNS_LEN) { + ck_free(host); + free(output); + return 1; + } + + ck_free(host); + host = ck_strdup((u8*)output); + free(output); + + } + + req->host = host; + cur += path_st; + + /* All right, moving on: if host name is followed by :, let's try to + parse and validate port number; otherwise, assume 80 / 443, depending + on protocol. */ + + if (*cur == ':') { + + u32 digit_cnt = strspn((char*)++cur, "0123456789"); + u32 port = atoi((char*)cur); + if (!digit_cnt || (cur[digit_cnt] && !strchr("/?#", cur[digit_cnt]))) + return 1; + req->port = port; + cur += digit_cnt; + + } else { + + if (req->proto == PROTO_HTTPS) req->port = 443; else req->port = 80; + + } + + has_host = 1; + + } else { + + /* No host name found - copy from referring request instead. */ + + if (!ref || !ref->host) return 1; + + req->host = ck_strdup(ref->host); + req->addr = ref->addr; + req->port = ref->port; + + } + + if (!*cur || *cur == '#') { + u32 i; + + /* No-op path. If the URL does not specify host (e.g., #foo), copy + everything from referring request, call it a day. Otherwise + (e.g., http://example.com#foo), let tokenize_path() run to + add NULL-"" entry to the list. */ + + if (!has_host) { + for (i=0;ipar.c;i++) + if (PATH_SUBTYPE(ref->par.t[i]) || QUERY_SUBTYPE(ref->par.t[i])) + set_value(ref->par.t[i], ref->par.n[i], ref->par.v[i], -1, &req->par); + return 0; + } + + } + + if (!has_host && *cur == '?') { + u32 i; + + /* URL begins with ? and does not specify host (e.g., ?foo=bar). Copy all + path segments, but no query, then fall through to parse the query + string. */ + + for (i=0;ipar.c;i++) + if (PATH_SUBTYPE(ref->par.t[i])) + set_value(ref->par.t[i], ref->par.n[i], ref->par.v[i], -1, &req->par); + + /* In this case, we do not want tokenize_path() to tinker with the path + in any way. */ + + add_slash = 0; + + } else if (!has_host && *cur != '/') { + + /* The URL does not begin with / or ?, and does not specify host (e.g., + foo/bar?baz). Copy path from referrer, but drop the last "proper" + path segment and everything that follows it. This mimicks browser + behavior (for URLs ending with /, it just drops the final NULL-"" + pair). */ + + u32 i; + u32 path_cnt = 0, path_cur = 0; + + for (i=0;ipar.c;i++) + if (ref->par.t[i] == PARAM_PATH) path_cnt++; + + for (i=0;ipar.c;i++) { + if (ref->par.t[i] == PARAM_PATH) path_cur++; + if (path_cur < path_cnt && PATH_SUBTYPE(ref->par.t[i])) + set_value(ref->par.t[i], ref->par.n[i], ref->par.v[i], -1, &req->par); + } + + } + + /* Tokenize the remaining path on top of what we parsed / copied over. */ + + tokenize_path(cur, req, add_slash); + return 0; + +} + + +/* URL-decodes a string. 'Plus' parameter governs the behavior on + + signs (as they have a special meaning only in query params, not in path). */ + +u8* url_decode_token(u8* str, u32 len, u8 plus) { + u8 *ret = ck_alloc(len + 1); + u8 *src = str, *dst = ret; + char *hex_str = "0123456789abcdef"; + + while (len--) { + u8 c = *(src++); + char *f, *s; + + if (plus && c == '+') c = ' '; + + if (c == '%' && len >= 2 && + (f = strchr(hex_str, tolower(src[0]))) && + (s = strchr(hex_str, tolower(src[1])))) { + c = ((f - hex_str) << 4) | (s - hex_str); + src += 2; len -= 2; + } + + /* We can't handle NUL-terminators gracefully when deserializing request + parameters, because param_array values are NUL-terminated themselves. + Let's encode \0 as \xFF instead, and hope nobody notices. */ + + if (!c) c = 0xff; + + *(dst++) = c; + + } + + *(dst++) = 0; + + ret = ck_realloc(ret, dst - ret); + + return ret; +} + + +/* URL-encodes a string according to custom rules. The assumption here is that + the data is already tokenized at "special" boundaries such as ?, =, &, /, + ;, !, $, and , so these characters must always be escaped if present in + tokens. We otherwise let pretty much everything else go through, as it + may help with the exploitation of certain vulnerabilities. */ + +u8* url_encode_token(u8* str, u32 len, u8* enc_set) { + + u8 *ret = ck_alloc(len * 3 + 1); + u8 *src = str, *dst = ret; + + while (len--) { + u8 c = *(src++); + + if (c <= 0x20 || c >= 0x80 || strchr((char*)enc_set, c)) { + if (c == 0xFF) c = 0; + sprintf((char*)dst, "%%%02X", c); + dst += 3; + } else *(dst++) = c; + + } + + *(dst++) = 0; + + ret = ck_realloc(ret, dst - ret); + + return ret; + +} + + +/* Split path at known "special" character boundaries, URL decode values, + then put them in the provided http_request struct. */ + +void tokenize_path(u8* str, struct http_request* req, u8 add_slash) { + + u8* cur; + u8 know_dir = 0; + + while (*str == '/') str++; + cur = str; + + /* Parse path elements first. */ + + while (*cur && !strchr("?#", *cur)) { + + u32 next_seg, next_eq; + + u8 *name = NULL, *value = NULL; + u8 first_el = (str == cur); + + + if (first_el || *cur == '/') { + + /* Optimize out //, /\0, /./, and /.\0. They do indicate + we are looking at a directory, so mark this. */ + + if (!first_el && (cur[1] == '/' || !cur[1])) { + cur++; + know_dir = 1; + continue; + } + + if (cur[0 + !first_el] == '.' && (cur[1 + !first_el] == '/' || + !cur[1 + !first_el])) { + cur += 1 + !first_el; + know_dir = 1; + continue; + } + + /* Also optimize out our own \.\ prefix injected in directory + probes. This is to avoid recursion if it actually worked in some + way. */ + + if (!prefix(cur, "/\\.\\") && (cur[4] == '/' || !cur[4])) { + cur += 4; + continue; + } + + if (!case_prefix(cur, "/%5c.%5c") && + (cur[8] == '/' || !cur[8])) { + cur += 8; + continue; + } + + /* If we encountered /../ or /..\0, remove everything up to and + including the last "true" path element. It's also indicative + of a directory, by the way. */ + + if (cur[0 + !first_el] == '.' && cur[1 + !first_el] == '.' && + (cur[2 + !first_el] == '/' || !cur[2 + !first_el])) { + + u32 i, last_p = req->par.c; + + for (i=0;ipar.c;i++) + if (req->par.t[i] == PARAM_PATH) last_p = i; + + for (i=last_p;ipar.c;i++) { + req->par.t[i] = PARAM_NONE; + } + + cur += 2 + !first_el; + know_dir = 1; + continue; + + } + + } + + /* If we're here, we have an actual item to add; cur points to + the string if it's the first element, or to field separator + if one of the subsequent ones. */ + + next_seg = strcspn((char*)cur + 1, "/;,!$?#") + 1, + next_eq = strcspn((char*)cur + 1, "=/;,!$?#") + 1; + know_dir = 0; + + if (next_eq < next_seg) { + name = url_decode_token(cur + !first_el, next_eq - !first_el, 0); + value = url_decode_token(cur + next_eq + 1, next_seg - next_eq - 1, 0); + } else { + value = url_decode_token(cur + !first_el, next_seg - !first_el, 0); + } + + /* If the extracted segment is just '.' or '..', but is followed by + something else than '/', skip one separator. */ + + if (!name && cur[next_seg] && cur[next_seg] != '/' && + (!strcmp((char*)value, ".") || !strcmp((char*)value, ".."))) { + + next_seg = strcspn((char*)cur + next_seg + 1, "/;,!$?#") + next_seg + 1, + + ck_free(name); + ck_free(value); + + value = url_decode_token(cur + !first_el, next_seg - !first_el, 0); + + } + + + switch (first_el ? '/' : *cur) { + + case ';': set_value(PARAM_PATH_S, name, value, -1, &req->par); break; + case ',': set_value(PARAM_PATH_C, name, value, -1, &req->par); break; + case '!': set_value(PARAM_PATH_E, name, value, -1, &req->par); break; + case '$': set_value(PARAM_PATH_D, name, value, -1, &req->par); break; + default: set_value(PARAM_PATH, name, value, -1, &req->par); + + } + + ck_free(name); + ck_free(value); + + cur += next_seg; + + } + + /* If the last segment was /, /./, or /../, *or* if we never added + anything to the path to begin with, we want to store a NULL-"" + entry to denote it's a directory. */ + + if (know_dir || (add_slash && (!*str || strchr("?#", *str)))) + set_value(PARAM_PATH, NULL, (u8*)"", -1, &req->par); + + /* Deal with regular query parameters now. This is much simpler, + obviously. */ + + while (*cur && !strchr("#", *cur)) { + + u32 next_seg = strcspn((char*)cur + 1, "#&;,!$") + 1; + u32 next_eq = strcspn((char*)cur + 1, "=#&;,!$") + 1; + u8 *name = NULL, *value = NULL; + + /* foo=bar syntax... */ + + if (next_eq < next_seg) { + name = url_decode_token(cur + 1, next_eq - 1, 1); + value = url_decode_token(cur + next_eq + 1, next_seg - next_eq - 1, 1); + } else { + value = url_decode_token(cur + 1, next_seg - 1, 1); + } + + switch (*cur) { + + case ';': set_value(PARAM_QUERY_S, name, value, -1, &req->par); break; + case ',': set_value(PARAM_QUERY_C, name, value, -1, &req->par); break; + case '!': set_value(PARAM_QUERY_E, name, value, -1, &req->par); break; + case '$': set_value(PARAM_QUERY_D, name, value, -1, &req->par); break; + default: set_value(PARAM_QUERY, name, value, -1, &req->par); + + } + + ck_free(name); + ck_free(value); + + cur += next_seg; + + } + +} + + +/* Reconstructs URI from http_request data. Includes protocol and host + if with_host is non-zero. */ + +u8* serialize_path(struct http_request* req, u8 with_host, u8 with_post) { + u32 i, cur_pos; + u8 got_search = 0; + u8* ret; + + NEW_STR(ret, cur_pos); + +#define ASD(_p3) ADD_STR_DATA(ret, cur_pos, _p3) + + /* For human-readable uses... */ + + if (with_host) { + ASD("http"); + if (req->proto == PROTO_HTTPS) ASD("s"); + ASD("://"); + ASD(req->host); + + if ((req->proto == PROTO_HTTP && req->port != 80) || + (req->proto == PROTO_HTTPS && req->port != 443)) { + u8 port[7]; + sprintf((char*)port, ":%u", req->port); + ASD(port); + } + + } + + /* First print path... */ + + for (i=0;ipar.c;i++) { + u8 *enc = (u8*)ENC_PATH; + if(req->pivot && req->fuzz_par_enc && i == req->pivot->fuzz_par) + enc = req->fuzz_par_enc; + + if (PATH_SUBTYPE(req->par.t[i])) { + + switch (req->par.t[i]) { + + case PARAM_PATH_S: ASD(";"); break; + case PARAM_PATH_C: ASD(","); break; + case PARAM_PATH_E: ASD("!"); break; + case PARAM_PATH_D: ASD("$"); break; + default: ASD("/"); + + } + + if (req->par.n[i]) { + u32 len = strlen((char*)req->par.n[i]); + u8* str = url_encode_token(req->par.n[i], len, enc); + ASD(str); ASD("="); + ck_free(str); + } + if (req->par.v[i]) { + u32 len = strlen((char*)req->par.v[i]); + u8* str = url_encode_token(req->par.v[i], len, enc); + ASD(str); + ck_free(str); + } + + } + } + + /* Then actual parameters. */ + + for (i=0;ipar.c;i++) { + u8 *enc = (u8*)ENC_DEFAULT; + if(req->pivot && req->fuzz_par_enc && i == req->pivot->fuzz_par) + enc = req->fuzz_par_enc; + + if (QUERY_SUBTYPE(req->par.t[i])) { + + if (!got_search) { + ASD("?"); + got_search = 1; + } else switch (req->par.t[i]) { + + case PARAM_QUERY_S: ASD(";"); break; + case PARAM_QUERY_C: ASD(","); break; + case PARAM_QUERY_E: ASD("!"); break; + case PARAM_QUERY_D: ASD("$"); break; + default: ASD("&"); + + } + + if (req->par.n[i]) { + u32 len = strlen((char*)req->par.n[i]); + u8* str = url_encode_token(req->par.n[i], len, enc); + ASD(str); ASD("="); + ck_free(str); + } + if (req->par.v[i]) { + u32 len = strlen((char*)req->par.v[i]); + u8* str = url_encode_token(req->par.v[i], len, enc); + ASD(str); + ck_free(str); + } + + } + } + + got_search = 0; + + if (with_post) + for (i=0;ipar.c;i++) { + + u8 *enc = (u8*)ENC_DEFAULT; + if(req->pivot && req->fuzz_par_enc && i == req->pivot->fuzz_par) + enc = req->fuzz_par_enc; + + if (POST_SUBTYPE(req->par.t[i])) { + + if (!got_search) { + ASD(" DATA:"); + got_search = 1; + } else ASD("&"); + + if (req->par.n[i]) { + u32 len = strlen((char*)req->par.n[i]); + u8* str = url_encode_token(req->par.n[i], len, enc); + ASD(str); ASD("="); + ck_free(str); + } + if (req->par.v[i]) { + u32 len = strlen((char*)req->par.v[i]); + u8* str = url_encode_token(req->par.v[i], len, enc); + ASD(str); + ck_free(str); + } + + } + } + +#undef ASD + + TRIM_STR(ret, cur_pos); + return ret; + +} + + +/* Looks up IP for a particular host, returns data in network order. + Uses standard resolver, so it is slow and blocking, but we only + expect to call it a couple of times during a typical assessment. + There are some good async DNS libraries to consider in the long run. */ + +u32 maybe_lookup_host(u8* name) { + struct hostent* h; + struct dns_entry *d = dns, *prev = NULL; + u32 ret_addr = 0; + struct in_addr in; + +#ifdef PROXY_SUPPORT + + /* If configured to use proxy, look up proxy IP once; and return that + address for all host names. */ + + if (use_proxy) { + + if (!use_proxy_addr) { + + /* Don't bother resolving raw IP addresses, naturally. */ + + if (inet_aton((char*)use_proxy, &in)) + return (use_proxy_addr = (u32)in.s_addr); + + h = gethostbyname((char*)use_proxy); + + /* If lookup fails with a transient error, be nice - try again. */ + + if (!h && h_errno == TRY_AGAIN) h = gethostbyname((char*)name); + + if (!h || !(use_proxy_addr = *(u32*)h->h_addr_list[0])) + FATAL("Unable to resolve proxy host name '%s'.", use_proxy); + + } + + return use_proxy_addr; + + } + + /* If no proxy... */ + +#endif /* PROXY_SUPPORT */ + + /* Don't bother resolving raw IP addresses, naturally. */ + + if (inet_aton((char*)name, &in)) + return (u32)in.s_addr; + + while (d) { + if (!strcasecmp((char*)name, (char*)d->name)) return d->addr; + prev = d; + d = d->next; + } + + h = gethostbyname((char*)name); + + /* If lookup fails with a transient error, be nice - try again. */ + + if (!h && h_errno == TRY_AGAIN) h = gethostbyname((char*)name); + + if (h) { + + u32 i = 0; + + /* For each address associated with the host, see if we have any + other hosts that resolved to that same IP. If yes, return + that address; otherwise, just return first. This is for HTTP + performance and bookkeeping reasons. */ + + while (h->h_addr_list[i]) { + d = dns; + while (d) { + if (d->addr == *(u32*)h->h_addr_list[i]) { + ret_addr = d->addr; + goto dns_got_name; + } + d = d->next; + } + i++; + } + + ret_addr = *(u32*)h->h_addr_list[0]; + + } + +dns_got_name: + + if (!prev) d = dns = ck_alloc(sizeof(struct dns_entry)); + else d = prev->next = ck_alloc(sizeof(struct dns_entry)); + + d->name = ck_strdup(name); + d->addr = ret_addr; + + return ret_addr; + +} + + +/* Creates an ad hoc DNS cache entry, to override NS lookups. */ + +void fake_host(u8* name, u32 addr) { + struct dns_entry *d = dns, *prev = dns; + + while (d && d->next) { prev = d ; d = d->next;} + + if (!dns) d = dns = ck_alloc(sizeof(struct dns_entry)); + else d = prev->next = ck_alloc(sizeof(struct dns_entry)); + + d->name = ck_strdup(name); + d->addr = addr; + +} + + +/* Prepares a serialized HTTP buffer to be sent over the network. */ + +u8* build_request_data(struct http_request* req) { + + u8 *ret_buf, *ck_buf, *pay_buf, *path; + u32 ret_pos, ck_pos, pay_pos, i; + u8 req_type = PARAM_NONE; + u8 browser = browser_type; + + if (req->proto == PROTO_NONE) + FATAL("uninitialized http_request"); + + NEW_STR(ret_buf, ret_pos); + + path = serialize_path(req, 0, 0); + +#define ASD(_p3) ADD_STR_DATA(ret_buf, ret_pos, _p3) + + if (req->method) ASD(req->method); else ASD((u8*)"GET"); + ASD(" "); + +#ifdef PROXY_SUPPORT + + /* For non-CONNECT proxy requests, insert http://host[:port] too. */ + + if (use_proxy && req->proto == PROTO_HTTP) { + ASD("http://"); + ASD(req->host); + + if (req->port != 80) { + char port[7]; + sprintf((char*)port, ":%u", req->port); + ASD(port); + } + + } + +#endif /* PROXY_SUPPORT */ + + ASD(path); + ASD(" HTTP/1.1\r\n"); + ck_free(path); + + ASD("Host: "); + ASD(req->host); + + if ((req->proto == PROTO_HTTP && req->port != 80) || + (req->proto == PROTO_HTTPS && req->port != 443)) { + char port[7]; + sprintf((char*)port, ":%u", req->port); + ASD(port); + } + + ASD("\r\n"); + + /* Insert generic browser headers first. If the request is for a specific + browser, we use that. Else we use the pivot browser or, when that + is not set, fall back on the default browser (e.g. set with -b). */ + + if (req->browser) { + browser = req->browser; + } else if (req->pivot->browser) { + browser = req->browser; + } + + if (browser == BROWSER_FAST) { + + ASD("Accept-Encoding: gzip\r\n"); + ASD("Connection: keep-alive\r\n"); + + if (!GET_HDR((u8*)"User-Agent", &req->par)) + ASD("User-Agent: Mozilla/5.0 SF/" VERSION "\r\n"); + + /* Some servers will reject to gzip responses unless "Mozilla/..." + is seen in User-Agent. Bleh. */ + + } else if (browser == BROWSER_FFOX) { + + if (!GET_HDR((u8*)"User-Agent", &req->par)) + ASD("User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; " + "rv:1.9.1.2) Gecko/20090729 Firefox/3.5.2 SF/" VERSION "\r\n"); + + ASD("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;" + "q=0.8\r\n"); + + if (!GET_HDR((u8*)"Accept-Language", &req->par)) + ASD("Accept-Language: en-us,en\r\n"); + + ASD("Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n"); + ASD("Keep-Alive: 300\r\n"); + ASD("Connection: keep-alive\r\n"); + + } else if (browser == BROWSER_MSIE) { + + ASD("Accept: */*\r\n"); + + if (!GET_HDR((u8*)"Accept-Language", &req->par)) + ASD("Accept-Language: en,en-US;q=0.5\r\n"); + + if (!GET_HDR((u8*)"User-Agent", &req->par)) + ASD("User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; " + "Trident/4.0; .NET CLR 1.1.4322; InfoPath.1; .NET CLR " + "2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; SF/" + VERSION ")\r\n"); + + ASD("Accept-Encoding: gzip, deflate\r\n"); + ASD("Connection: Keep-Alive\r\n"); + + } else /* iPhone */ { + + if (!GET_HDR((u8*)"User-Agent", &req->par)) + ASD("User-Agent: Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_1 like Mac OS " + "X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Version/4.0.5 " + "Mobile/8B117 Safari/6531.22.7 SF/" VERSION "\r\n"); + + ASD("Accept: application/xml,application/xhtml+xml,text/html;q=0.9," + "text/plain;q=0.8,image/png,*/*;q=0.5\r\n"); + + if (!GET_HDR((u8*)"Accept-Language", &req->par)) + ASD("Accept-Language: en-us\r\n"); + + ASD("Accept-Encoding: gzip, deflate\r\n"); + ASD("Connection: keep-alive\r\n"); + + } + + /* Request a limited range up front to minimize unwanted traffic. + Note that some Oracle servers apparently fail on certain ranged + requests, so allowing -H override seems like a good idea. */ + + if (!GET_HDR((u8*)"Range", &global_http_par)) { + u8 limit[32]; + sprintf((char*)limit, "Range: bytes=0-%u\r\n", size_limit - 1); + ASD(limit); + } + + /* Include a dummy "Referer" header, to avoid certain XSRF checks. */ + + if (!GET_HDR((u8*)"Referer", &req->par)) { + ASD("Referer: http"); + if (req->proto == PROTO_HTTPS) ASD("s"); + ASD("://"); + ASD(req->host); + ASD("/\r\n"); + } + + /* Take care of HTTP authentication next. */ + + if (auth_type == AUTH_BASIC) { + u8* lp = ck_alloc(strlen((char*)auth_user) + strlen((char*)auth_pass) + 2); + u8* lpb64; + + sprintf((char*)lp, "%s:%s", auth_user, auth_pass); + + lpb64 = b64_encode(lp, strlen((char*)lp)); + + ASD("Authorization: Basic "); + ASD(lpb64); + ASD("\r\n"); + + ck_free(lpb64); + ck_free(lp); + + } + + /* Append any other requested headers and cookies. */ + + NEW_STR(ck_buf, ck_pos); + + for (i=0;ipar.c;i++) { + if (req->par.t[i] == PARAM_HEADER) { + ASD(req->par.n[i]); + ASD(": "); + ASD(req->par.v[i]); + ASD("\r\n"); + } else if (req->par.t[i] == PARAM_COOKIE) { + if (ck_pos) ADD_STR_DATA(ck_buf, ck_pos, ";"); + ADD_STR_DATA(ck_buf, ck_pos, req->par.n[i]); + ADD_STR_DATA(ck_buf, ck_pos, "="); + ADD_STR_DATA(ck_buf, ck_pos, req->par.v[i]); + } + } + + /* Also include extra globals, if any (but avoid dupes). */ + + for (i=0;ipar)) { + ASD(global_http_par.n[i]); + ASD(": "); + ASD(global_http_par.v[i]); + ASD("\r\n"); + } else if (global_http_par.t[i] == PARAM_COOKIE && + !GET_CK(global_http_par.n[i], &req->par)) { + if (ck_pos) ADD_STR_DATA(ck_buf, ck_pos, "; "); + ADD_STR_DATA(ck_buf, ck_pos, global_http_par.n[i]); + ADD_STR_DATA(ck_buf, ck_pos, "="); + ADD_STR_DATA(ck_buf, ck_pos, global_http_par.v[i]); + } + } + + if (ck_pos && !req->no_cookies) { + ASD("Cookie: "); + ASD(ck_buf); + ASD("\r\n"); + } + + ck_free(ck_buf); + + /* Now, let's serialize the payload, if necessary. */ + + for (i=0;ipar.c;i++) { + switch (req->par.t[i]) { + case PARAM_POST_F: + case PARAM_POST_O: + req_type = req->par.t[i]; + break; + case PARAM_POST: + if (req_type == PARAM_NONE) req_type = PARAM_POST; + break; + } + } + + NEW_STR(pay_buf, pay_pos); + + if (req_type == PARAM_POST) { + + /* The default case: application/x-www-form-urlencoded. */ + + for (i=0;ipar.c;i++) { + u8 *enc = (u8*)ENC_DEFAULT; + if(req->pivot && req->fuzz_par_enc && i == req->pivot->fuzz_par) + enc = req->fuzz_par_enc; + + if (req->par.t[i] == PARAM_POST) { + if (pay_pos) ADD_STR_DATA(pay_buf, pay_pos, "&"); + if (req->par.n[i]) { + u32 len = strlen((char*)req->par.n[i]); + u8* str = url_encode_token(req->par.n[i], len, enc); + ADD_STR_DATA(pay_buf, pay_pos, str); + ADD_STR_DATA(pay_buf, pay_pos, "="); + ck_free(str); + } + if (req->par.v[i]) { + u32 len = strlen((char*)req->par.v[i]); + u8* str = url_encode_token(req->par.v[i], len, enc); + ADD_STR_DATA(pay_buf, pay_pos, str); + ck_free(str); + } + } + + } + + ASD("Content-Type: application/x-www-form-urlencoded\r\n"); + + } else if (req_type == PARAM_POST_O) { + + /* Opaque, non-escaped data of some sort. */ + + for (i=0;ipar.c;i++) + if (req->par.t[i] == PARAM_POST_O && req->par.v[i]) + ADD_STR_DATA(pay_buf, pay_pos, req->par.v[i]); + + ASD("Content-Type: text/plain\r\n"); + + } else if (req_type == PARAM_POST_F) { + u8 bound[20]; + + /* MIME envelopes: multipart/form-data */ + + sprintf((char*)bound, "sf%u", R(1000000)); + + for (i=0;ipar.c;i++) + if (req->par.t[i] == PARAM_POST || req->par.t[i] == PARAM_POST_F) { + + ADD_STR_DATA(pay_buf, pay_pos, "--"); + ADD_STR_DATA(pay_buf, pay_pos, bound); + ADD_STR_DATA(pay_buf, pay_pos, "\r\n" + "Content-Disposition: form-data; name=\""); + if (req->par.n[i]) + ADD_STR_DATA(pay_buf, pay_pos, req->par.n[i]); + + if (req->par.t[i] == PARAM_POST_F) { + u8 tmp[64]; + sprintf((char*)tmp, "\"; filename=\"sfish%u." DUMMY_EXT "\"\r\n" + "Content-Type: " DUMMY_MIME "\r\n\r\n", R(16)); + ADD_STR_DATA(pay_buf, pay_pos, tmp); + ADD_STR_DATA(pay_buf, pay_pos, new_xss_tag((u8*)DUMMY_FILE)); + register_xss_tag(req); + } else { + ADD_STR_DATA(pay_buf, pay_pos, "\"\r\n\r\n"); + if (req->par.v[i]) + ADD_STR_DATA(pay_buf, pay_pos, req->par.v[i]); + } + + ADD_STR_DATA(pay_buf, pay_pos, "\r\n"); + } + + ADD_STR_DATA(pay_buf, pay_pos, "--"); + ADD_STR_DATA(pay_buf, pay_pos, bound); + ADD_STR_DATA(pay_buf, pay_pos, "--\r\n"); + + ASD("Content-Type: multipart/form-data; boundary="); + ASD(bound); + ASD("\r\n"); + + } else if (req_type == 0) ASD("\r\n"); + + /* Finalize HTTP payload... */ + + for (i=0;i start_ptr && strchr("\r\n", *(cur_ptr-1))) cur_ptr--; + + ret = ck_alloc(cur_ptr - start_ptr + 1); + memcpy(ret, start_ptr, cur_ptr - start_ptr); + ret[cur_ptr - start_ptr] = 0; + + return ret; + +} + + +/* Builds response fingerprint data. These fingerprints are used to + find "roughly comparable" pages based on their word length + distributions (divided into FP_SIZE buckets). */ + +void fprint_response(struct http_response* res) { + u32 i, c_len = 0, in_space = 0; + + res->sig.code = res->code; + + for (i=0;ipay_len;i++) + + if (res->payload[i] <= 0x20 || strchr("<>\"'&:\\", (char)res->payload[i])) { + + if (!in_space) { + + in_space = 1; + if (c_len && ++c_len <= FP_MAX_LEN) + res->sig.data[c_len % FP_SIZE]++; + c_len = 0; + + } else c_len++; + + if (res->payload[i] == '&') + do { i++; } while (i < res->pay_len && + (isalnum(res->payload[i]) || strchr("#;", (char)res->payload[i]))); + + + } else { + + if (in_space) { + + in_space = 0; + if (c_len && ++c_len <= FP_MAX_LEN) + res->sig.data[c_len % FP_SIZE]++; + c_len = 0; + + } else { + res->sig.has_text = 1; + c_len++; + } + + } + + if (c_len) res->sig.data[c_len % FP_SIZE]++; +} + +/* Parses a network buffer containing raw HTTP response received over the + network ('more' == the socket is still available for reading). Returns 0 + if response parses OK, 1 if more data should be read from the socket, + 2 if the response seems invalid, 3 if response OK but connection must be + closed. */ + +u8 parse_response(struct http_request* req, struct http_response* res, + u8* data, u32 data_len, u8 more) { + u8* cur_line = 0; + s32 pay_len = -1; + u32 cur_data_off = 0, + total_chunk = 0, + http_ver; + u8 chunked = 0, compressed = 0, must_close = 0; + + if (res->code) + FATAL("struct http_response reused! Original code '%u'.", res->code); + +#define NEXT_LINE() do { \ + if (cur_line) ck_free(cur_line); \ + cur_line = grab_line(data, &cur_data_off, data_len); \ + } while (0) + + /* First, let's do a superficial request completeness check. Be + prepared for a premature end at any point. */ + + NEXT_LINE(); /* HTTP/1.x xxx ... */ + + if (!cur_line) return more ? 1 : 2; + + if (strlen((char*)cur_line) < 7 && more) { + ck_free(cur_line); + return 1; + } + + if (prefix(cur_line, "HTTP/1.")) { + ck_free(cur_line); + return 2; + } + + /* Scan headers for Content-Length, Transfer-Encoding, etc. */ + + while (1) { + + NEXT_LINE(); /* Next header or empty line. */ + + /* If headers end prematurely, and more data might arrive, ask for + it; otherwise, just assume end of headers and continue. */ + + if (!cur_line) { + if (more) return 1; + res->warn |= WARN_PARTIAL; + break; + } + + /* Empty line indicates the beginning of a payload. */ + + if (!cur_line[0]) break; + + if (!case_prefix(cur_line, "Content-Length:")) { + + /* The value in Content-Length header would be useful for seeing if we + have all the requested data already. Reject invalid values to avoid + integer overflows, etc, though. */ + + if (sscanf((char*)cur_line + 15, "%d", &pay_len) == 1) { + if (pay_len < 0 || pay_len > 1000000000 /* 1 GB */) { + ck_free(cur_line); + return 2; + } + } else pay_len = -1; + + } else if (!case_prefix(cur_line, "Transfer-Encoding:")) { + + /* Transfer-Encoding: chunked must be accounted for to properly + determine if we received all the data when Content-Length not found. */ + + u8* x = cur_line + 18; + + while (isspace(*x)) x++; + if (!strcasecmp((char*)x, "chunked")) chunked = 1; + + } else if (!case_prefix(cur_line, "Content-Encoding:")) { + + /* Content-Encoding is good to know, too. */ + + u8* x = cur_line + 17; + + while (isspace(*x)) x++; + + if (!strcasecmp((char*)x, "deflate") || !strcasecmp((char*)x, "gzip")) + compressed = 1; + + } else if (!case_prefix(cur_line, "Connection:")) { + + u8* x = cur_line + 11; + + while (isspace(*x)) x++; + + if (!strcasecmp((char*)x, "close")) must_close = 1; + + } + } + + /* We are now at the beginning of the payload. Firstly, how about decoding + 'chunked' to see if we received a complete 0-byte terminator chunk + already? */ + + if (chunked) { + while (1) { + u32 chunk_len; + + NEXT_LINE(); /* Should be chunk size, hex. */ + + if (!cur_line || sscanf((char*)cur_line, "%x", &chunk_len) != 1) { + if (more) { ck_free(cur_line); return 1; } + res->warn |= WARN_PARTIAL; + break; + } + + if (chunk_len > 1000000000 || total_chunk > 1000000000 /* 1 GB */) { + ck_free(cur_line); + return 2; + } + + /* See if we actually enough buffer to skip the chunk. Bail out if + not and more data might be coming; otherwise, adjust chunk size + accordingly. */ + + if (cur_data_off + chunk_len > data_len) { + + if (more) { ck_free(cur_line); return 1; } + chunk_len = data_len - cur_data_off; + total_chunk += chunk_len; + + res->warn |= WARN_PARTIAL; + break; + } + + total_chunk += chunk_len; + + cur_data_off += chunk_len; + NEXT_LINE(); + + /* No newline? */ + if (!cur_line) { + if (more) return 1; + res->warn |= WARN_PARTIAL; + } + + /* All right, so that was the last, complete 0-size chunk? + Exit the loop if so. */ + + if (!chunk_len) break; + + } + + if (cur_data_off != data_len) res->warn |= WARN_TRAIL; + + } else if (pay_len == -1 && more) { + + /* If in a mode other than 'chunked', and C-L not received, but more + data might be available - try to request it. */ + + ck_free(cur_line); + return 1; + + } else if (pay_len != 1) { + + if (cur_data_off + pay_len > data_len) { + + /* If C-L seen, but not nough data in the buffer, try to request more + if possible, otherwise tag the response as partial. */ + + if (more) { ck_free(cur_line); return 1; } + res->warn |= WARN_PARTIAL; + + } else if (cur_data_off + pay_len < data_len) res->warn |= WARN_TRAIL; + + } + + /* Rewind, then properly parse HTTP headers, parsing cookies. */ + + cur_data_off = 0; + + NEXT_LINE(); + + if (strlen((char*)cur_line) < 13 || + sscanf((char*)cur_line, "HTTP/1.%u %u ", &http_ver, &res->code) != 2 || + res->code < 100 || res->code > 999) { + ck_free(cur_line); + return 2; + } + + /* Some servers, when presented with 'Range' header, will return 200 on + some queries for a particular resource, and 206 on other queries (e.g., + with query string), despite returning exactly as much data. As an + ugly workaround... */ + + if (res->code == 206) res->code = 200; + + if (http_ver == 0) must_close = 1; + + res->msg = ck_strdup(cur_line + 13); + + while (1) { + u8* val; + + NEXT_LINE(); /* Next header or empty line. */ + + if (!cur_line) return 2; + if (!cur_line[0]) break; + + /* Split field name and value */ + + val = (u8*) strchr((char*)cur_line, ':'); + if (!val) { ck_free(cur_line); return 2; } + + *val = 0; + while (isspace(*(++val))); + + SET_HDR(cur_line, val, &res->hdr); + if (!strcasecmp((char*)cur_line, "Set-Cookie") || + !strcasecmp((char*)cur_line, "Set-Cookie2")) { + + /* We could bother with a proper tokenizer here, but contrary to "teh + standards", browsers generally don't accept multiple cookies in + Set-Cookie headers, handle quoted-string encoding inconsistently, + etc. So let's just grab the first value naively and move on. */ + + u8* cval; + u8* orig_val; + + cval = (u8*) strchr((char*)val, ';'); + if (cval) *cval = 0; + cval = (u8*) strchr((char*)val, '='); + if (cval) { *cval = 0; cval++; } + + /* If proper value not found, use NULL name and put whatever was + found in the value field. */ + + if (!cval) { cval = val; val = 0; } + + if (cval) SET_CK(val, cval, &res->hdr); + + if (val) { + + /* New or drastically changed cookies are noteworthy. */ + + orig_val = GET_CK(val, &global_http_par); + + if (!orig_val || (strlen((char*)orig_val) != strlen((char*)cval) && + strncmp((char*)cval, (char*)orig_val, 3))) { + res->cookies_set = 1; + problem(PROB_NEW_COOKIE, req, res, val, req->pivot, 0); + + } + + /* Set cookie globally, but ignore obvious attempts to delete + existing ones. */ + + if (!ignore_cookies && val && cval[0]) + SET_CK(val, cval, &global_http_par); + + } + } + + /* Content-Type is worth mining for MIME, charset data at this point. */ + + if (!strcasecmp((char*)cur_line, "Content-Type")) { + + if (res->header_mime) { + + /* Duplicate Content-Type. Fetch previous value, if different, + complain. */ + + u8* tmp = GET_HDR((u8*)"Content-Type", &res->hdr); + if (strcasecmp((char*)tmp, (char*)val)) res->warn |= WARN_CFL_HDR; + + } else { + u8 *tmp = (u8*)strchr((char*)val, ';'), *cset; + + if (tmp) { + *tmp = 0; + if ((cset = (u8*)strchr((char*)tmp + 1, '='))) + res->header_charset = ck_strdup(cset + 1); + } + + res->header_mime = ck_strdup(val); + if (tmp) *tmp = ';'; + } + + } + + } + + /* At the beginning of the payload again! */ + + if (!chunked) { + + /* Identity. Ignore actual C-L data, use just as much as we collected. */ + + res->pay_len = data_len - cur_data_off; + res->payload = ck_alloc(res->pay_len + 1); + res->payload[res->pay_len] = 0; /* NUL-terminate for safer parsing. */ + + memcpy(res->payload, data + cur_data_off, res->pay_len); + + } else { + + u32 chunk_off = 0; + + /* Chunked - we should have the authoritative length of chunk + contents in total_chunk already, and the overall structure + validated, so let's just reparse quickly. */ + + res->pay_len = total_chunk; + res->payload = ck_alloc(total_chunk + 1); + res->payload[res->pay_len] = 0; + + while (1) { + u32 chunk_len; + + NEXT_LINE(); + + if (!cur_line || sscanf((char*)cur_line, "%x", &chunk_len) != 1) break; + + if (cur_data_off + chunk_len > data_len) + chunk_len = data_len - cur_data_off; + + memcpy(res->payload + chunk_off, data + cur_data_off, chunk_len); + + chunk_off += chunk_len; + cur_data_off += chunk_len; + + NEXT_LINE(); + + if (!chunk_len) break; + } + + } + + ck_free(cur_line); + + if (compressed) { + + u8* tmp_buf; + + /* Deflate or gzip - zlib can handle both the same way. We lazily allocate + a size_limit output buffer, then truncate it if necessary. */ + + z_stream d; + s32 err; + + tmp_buf = ck_alloc(size_limit + 1); + + d.zalloc = 0; + d.zfree = 0; + d.opaque = 0; + d.next_in = res->payload; + d.avail_in = res->pay_len; + d.next_out = tmp_buf; + d.avail_out = size_limit; + + /* Say hello to third-party vulnerabilities! */ + + if (inflateInit2(&d, 32 + 15) != Z_OK) { + inflateEnd(&d); + ck_free(tmp_buf); + return 2; + } + + err = inflate(&d, Z_FINISH); + inflateEnd(&d); + + if (err != Z_BUF_ERROR && err != Z_OK && err != Z_STREAM_END) { + ck_free(tmp_buf); + return 2; + } + + ck_free(res->payload); + + bytes_deflated += res->pay_len; + + res->pay_len = size_limit - d.avail_out; + res->payload = ck_realloc(tmp_buf, res->pay_len + 1); + res->payload[res->pay_len] = 0; + + + bytes_inflated += res->pay_len; + + } + +#undef NEXT_LINE + + fprint_response(res); + + return must_close ? 3 : 0; +} + + +/* Performs a deep free() of struct http_request */ + +void destroy_request(struct http_request* req) { + u32 i; + + for (i=0;ipar.c;i++) { + ck_free(req->par.n[i]); + ck_free(req->par.v[i]); + } + + ck_free(req->par.t); + ck_free(req->par.n); + ck_free(req->par.v); + + ck_free(req->method); + ck_free(req->host); + ck_free(req->orig_url); + + if (req->flushed && req->flush_dir) + ck_free(req->flush_dir); + + ck_free(req); + +} + + +/* Performs a deep free() of struct http_response */ + +void destroy_response(struct http_response* res) { + u32 i; + + for (i=0;ihdr.c;i++) { + ck_free(res->hdr.n[i]); + ck_free(res->hdr.v[i]); + } + + ck_free(res->hdr.t); + ck_free(res->hdr.n); + ck_free(res->hdr.v); + + ck_free(res->meta_charset); + ck_free(res->header_charset); + ck_free(res->header_mime); + + ck_free(res->msg); + + /* Payload might have been flushed */ + if (res->payload) + ck_free(res->payload); + + if (res->flushed && res->flush_dir) + ck_free(res->flush_dir); + + ck_free(res); + +} + + +/* Performs a deep free(), unlinking of struct queue_entry, and the + underlying request / response pair. */ + +static void destroy_unlink_queue(struct queue_entry* q, u8 keep) { + if (!keep) { + if (q->req) destroy_request(q->req); + if (q->res) destroy_response(q->res); + } + if (!q->prev) queue = q->next; else q->prev->next = q->next; +#ifdef QUEUE_FILO + if (!q->next) q_tail = q->prev; +#endif /* QUEUE_FILO */ + if (q->next) q->next->prev = q->prev; + ck_free(q); + queue_cur--; +} + + +/* Performs a deep free(), unlinking, network shutdown for struct + conn_entry, as well as the underlying queue entry, request + and response structs. */ + +static void destroy_unlink_conn(struct conn_entry* c, u8 keep) { + if (c->q) destroy_unlink_queue(c->q, keep); + if (!c->prev) conn = c->next; else c->prev->next = c->next; + if (c->next) c->next->prev = c->prev; + if (c->srv_ssl) SSL_free(c->srv_ssl); + if (c->srv_ctx) SSL_CTX_free(c->srv_ctx); + ck_free(c->write_buf); + ck_free(c->read_buf); + close(c->fd); + ck_free(c); + conn_cur--; +} + + +/* Performs struct conn_entry for reuse following a clean shutdown. */ + +static void reuse_conn(struct conn_entry* c, u8 keep) { + + if (c->q) destroy_unlink_queue(c->q, keep); + c->q = 0; + ck_free(c->read_buf); + ck_free(c->write_buf); + c->read_buf = c->write_buf = NULL; + c->read_len = c->write_len = c->write_off = 0; + c->SSL_rd_w_wr = c->SSL_wr_w_rd = 0; +} + + +/* Schedules a new asynchronous request (does not make a copy of the + original http_request struct, may deallocate it immediately or + later on); req->callback() will be invoked when the request is + completed (or fails - maybe right away). */ + +void async_request(struct http_request* req) { + struct queue_entry *qe; + struct http_response *res; + + if (req->proto == PROTO_NONE || !req->callback) + FATAL("uninitialized http_request"); + + res = ck_alloc(sizeof(struct http_response)); + + req->addr = maybe_lookup_host(req->host); + + /* Don't try to issue extra requests if max_fail + consecutive failures exceeded; but still try to + wrap up the (partial) scan. */ + + if (req_errors_cur > max_fail) { + DEBUG("!!! Too many subsequent request failures!\n"); + res->state = STATE_SUPPRESS; + if (!req->callback(req, res)) { + destroy_request(req); + destroy_response(res); + } + req_dropped++; + return; + } + + /* DNS errors mean instant fail. */ + + if (!req->addr) { + DEBUG("!!! DNS error!\n"); + res->state = STATE_DNSERR; + if (!req->callback(req, res)) { + destroy_request(req); + destroy_response(res); + } + req_errors_net++; + conn_count++; + conn_failed++; + return; + } + + /* Enforce user limits. */ + + if (req_count > max_requests) { + DEBUG("!!! Total request limit exceeded!\n"); + res->state = STATE_SUPPRESS; + if (!req->callback(req, res)) { + destroy_request(req); + destroy_response(res); + } + req_dropped++; + return; + } + + /* OK, looks like we're good to go. Insert the request + into the the queue. */ + +#ifdef QUEUE_FILO + + qe = q_tail; + q_tail = ck_alloc(sizeof(struct queue_entry)); + q_tail->req = req; + q_tail->res = res; + q_tail->prev = qe; + + if (q_tail->prev) q_tail->prev->next = q_tail; + + if (!queue) queue = q_tail; + +#else + + qe = queue; + + queue = ck_alloc(sizeof(struct queue_entry)); + queue->req = req; + queue->res = res; + queue->next = qe; + + if (queue->next) queue->next->prev = queue; + +#endif /* ^QUEUE_FILO */ + + queue_cur++; + req_count++; + +} + +/* A helper function to compare the CN / altname with our host name */ + +static u8 match_cert_name(char* req_host, char* host) { + + if (!host) return 0; + + /* For matching, we update our pointer from *.example.org to + .example.org */ + if (host[0] == '*' && host[1] == '.') { + host++; + + if (strlen(req_host) > strlen(host)) { + /* The cert name is a wild card which counts for the first level + * subdomain. We for comparison, strip the first section: + * + * foo.bar.example.org must not match .example.org + * bar.example.org must match .example.org + * + * */ + while(req_host && req_host[0] != '.') + req_host++; + } + } + + + if (host) DEBUG("Comparing: %s %s\n", host, req_host); + + if (!host || strcasecmp(host, req_host)) + return 0; + + return 1; +} + + +/* Check SSL properties, raise security alerts if necessary. We do not perform + a very thorough validation - we do not check for valid root CAs, bad ciphers, + SSLv2 support, etc - as these are covered well by network-level security + assessment tools anyway. + + We might eventually want to check aliases or support TLS SNI. */ + +static void check_ssl(struct conn_entry* c) { + X509 *p; + const SSL_CIPHER *cp; + + /* Test if a weak cipher has been negotiated */ + cp = SSL_get_current_cipher(c->srv_ssl); + if(!(cp->algo_strength & SSL_MEDIUM) && !(cp->algo_strength & SSL_HIGH)) + problem(PROB_SSL_WEAK_CIPHER, c->q->req, 0, + (u8*)SSL_CIPHER_get_name(cp),host_pivot(c->q->req->pivot), 0); + + + p = SSL_get_peer_certificate(c->srv_ssl); + + if (p) { + u32 cur_time = time(0); + u32 i, acnt; + char *issuer, *host, *req_host; + STACK_OF(GENERAL_NAME) *altnames; + char *buf = 0; + u8 found = 0; + + /* Check for certificate expiration... */ + + if (ASN1_UTCTIME_cmp_time_t(p->cert_info->validity->notBefore, cur_time) + != -1 || + ASN1_UTCTIME_cmp_time_t(p->cert_info->validity->notAfter, cur_time) + != 1) + problem(PROB_SSL_CERT_DATE, c->q->req, 0, 0, + host_pivot(c->q->req->pivot), 0); + + /* Check for self-signed certs or no issuer data. */ + + issuer = X509_NAME_oneline(p->cert_info->issuer,NULL,0); + + if (!issuer || !p->name || !strcmp(issuer, p->name)) + problem(PROB_SSL_SELF_CERT, c->q->req, 0, (u8*)issuer, + host_pivot(c->q->req->pivot), 0); + else + problem(PROB_SSL_CERT, c->q->req, 0, (u8*)issuer, + host_pivot(c->q->req->pivot), 0); + + free(issuer); + + /* Extract CN= from certificate name, compare to destination host. If + it doesn't match, step 2 is to look for alternate names and compare + those to the hostname */ + + host = strrchr(p->name, '='); + if (host) host++; /* Strip the = */ + req_host = (char*)c->q->req->host; + + /* Step 1: compare the common name value */ + found = match_cert_name(req_host, host); + + /* Step 2: compare the alternate names */ + if (!found) { + + altnames = X509_get_ext_d2i(p, NID_subject_alt_name, NULL, NULL); + + if (altnames) { + acnt = sk_GENERAL_NAME_num(altnames); + DEBUG("*-- Certificate has %d altnames\n", acnt); + + for (i=0; !found && itype != GEN_DNS) continue; + + buf = (char*)ASN1_STRING_data(name->d.dNSName); + + /* No string, no match */ + if (!buf) continue; + + /* Not falling for the \0 trick so we only compare when the + length matches with the string */ + + if (strlen(buf) != ASN1_STRING_length(name->d.dNSName)) { + problem(PROB_SSL_HOST_LEN, c->q->req, 0, (u8*)host, + host_pivot(c->q->req->pivot), 0); + + } else { + found = match_cert_name(req_host, buf); + } + + } + GENERAL_NAMES_free(altnames); + } + } + + if (!found) + problem(PROB_SSL_BAD_HOST, c->q->req, 0, (u8*)host, + host_pivot(c->q->req->pivot), 0); + + X509_free(p); + + } else problem(PROB_SSL_NO_CERT, c->q->req, 0, 0, + host_pivot(c->q->req->pivot), 0); + + c->ssl_checked = 1; +} + + +/* Associates a queue entry with an existing connection (if 'use_c' is + non-NULL), or creates a new connection to host (if 'use_c' NULL). */ + +static void conn_associate(struct conn_entry* use_c, struct queue_entry* q) { + struct conn_entry* c; + + if (use_c) { + + c = use_c; + c->reused = 1; + + } else { + + struct sockaddr_in sin; + + /* OK, we need to create a new connection list entry and connect + it to a target host. */ + + c = ck_alloc(sizeof(struct conn_entry)); + + conn_count++; + + c->proto = q->req->proto; + c->addr = q->req->addr; + c->port = q->req->port; + + c->fd = socket(PF_INET, SOCK_STREAM, 0); + + if (c->fd < 0) { + +connect_error: + + if (c->fd >=0) close(c->fd); + q->res->state = STATE_LOCALERR; + destroy_unlink_queue(q, q->req->callback(q->req, q->res)); + req_errors_net++; + req_errors_cur++; + + ck_free(c); + conn_failed++; + return; + } + + sin.sin_family = PF_INET; + +#ifdef PROXY_SUPPORT + sin.sin_port = htons(use_proxy ? use_proxy_port : c->port); +#else + sin.sin_port = htons(c->port); +#endif /* ^PROXY_SUPPORT */ + + memcpy(&sin.sin_addr, &q->req->addr, 4); + + fcntl(c->fd, F_SETFL, O_NONBLOCK); + + if (connect(c->fd, (struct sockaddr*) &sin, sizeof(struct sockaddr_in)) && + (errno != EINPROGRESS)) goto connect_error; + + /* HTTPS also requires SSL state to be initialized at this point. */ + + if (c->proto == PROTO_HTTPS) { + + c->srv_ctx = SSL_CTX_new(SSLv23_client_method()); + + if (!c->srv_ctx) goto connect_error; + + SSL_CTX_set_mode(c->srv_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + + c->srv_ssl = SSL_new(c->srv_ctx); + + if (!c->srv_ssl) { + SSL_CTX_free(c->srv_ctx); + goto connect_error; + } + + SSL_set_fd(c->srv_ssl, c->fd); + SSL_set_connect_state(c->srv_ssl); + + } + + /* Make it official. */ + + c->next = conn; + conn = c; + if (c->next) c->next->prev = c; + + conn_cur++; + + } + + c->q = q; + q->c = c; + + q->res->state = STATE_CONNECT; + c->req_start = c->last_rw = time(0); + c->write_buf = build_request_data(q->req); + c->write_len = strlen((char*)c->write_buf); + + /* Time the request */ + q->req->start_time = c->req_start; + +} + + +/* Processes the queue. Returns the number of queue entries remaining, + 0 if none. Will do a blocking select() to wait for socket state changes + (or timeouts) if no data available to process. This is the main + routine for the scanning loop. */ + +u32 next_from_queue(void) { + + u32 cur_time = time(0); + + if (conn_cur) { + static struct pollfd* p; + + struct conn_entry* c = conn; + u32 i = 0; + + /* First, go through all connections, handle connects, SSL handshakes, data + reads and writes, and exceptions. */ + + if (!p) + p = __DFL_ck_alloc(sizeof(struct pollfd) * max_connections); + + while (c) { + p[i].fd = c->fd; + p[i].events = POLLIN | POLLERR | POLLHUP; + if (c->write_len - c->write_off || c->SSL_rd_w_wr) + p[i].events |= POLLOUT; + p[i].revents = 0; + c = c->next; + i++; + } + + poll(p, conn_cur, 100); + + c = conn; + + for (i=0;inext; + + /* Connection closed: see if we have any pending data to write. If yes, + fail. If not, try parse_response() to see if we have all the data. + Clean up. */ + + if (p[i].revents & (POLLERR|POLLHUP)) { + + u8 keep; + +network_error: + + keep = 0; + + /* Retry requests that were sent on old keep-alive connections + and failed instantly with no data read; might be just that + the server got bored. */ + + if (c->q && !c->q->retrying && c->reused && !c->read_len) { + + c->q->res->state = STATE_NOTINIT; + c->q->retrying = 1; + c->q->c = 0; + c->q = 0; + + req_retried++; + + } else if (c->q) { + + if (c->write_len - c->write_off || !c->read_len) { + c->q->res->state = STATE_CONNERR; + keep = c->q->req->callback(c->q->req, c->q->res); + req_errors_net++; + req_errors_cur++; + } else { + if (parse_response(c->q->req, c->q->res, c->read_buf, + c->read_len, 0) != 2) { + c->q->res->state = STATE_OK; + keep = c->q->req->callback(c->q->req, c->q->res); + if (req_errors_cur <= max_fail) + req_errors_cur = 0; + } else { + c->q->res->state = STATE_CONNERR; + keep = c->q->req->callback(c->q->req, c->q->res); + req_errors_net++; + req_errors_cur++; + } + } + + } + + destroy_unlink_conn(c, keep); + + } else + + /* Incoming data (when SSL_write() did not request a read) or + continuation of SSL_read() possible (if SSL_read() wanted to write). + Process data, call parse_response() to see if w have all we wanted. + Update event timers. */ + + if (((p[i].revents & POLLIN) && !c->SSL_wr_w_rd) || + ((p[i].revents & POLLOUT) && c->SSL_rd_w_wr)) { + + if (c->q) { + s32 read_res; + u8 p_ret; + +SSL_read_more: + + c->read_buf = ck_realloc(c->read_buf, c->read_len + READ_CHUNK + 1); + + if (c->proto == PROTO_HTTPS) { + s32 ssl_err; + + c->SSL_rd_w_wr = 0; + + read_res = SSL_read(c->srv_ssl, c->read_buf + c->read_len, + READ_CHUNK); + + if (!read_res) goto network_error; + + if (read_res < 0) { + ssl_err = SSL_get_error(c->srv_ssl, read_res); + if (ssl_err == SSL_ERROR_WANT_WRITE) c->SSL_rd_w_wr = 1; + else if (ssl_err != SSL_ERROR_WANT_READ) goto network_error; + read_res = 0; + } + + } else { + read_res = read(c->fd, c->read_buf + c->read_len, READ_CHUNK); + if (read_res <= 0) goto network_error; + } + + bytes_recv += read_res; + + c->read_len += read_res; + c->read_buf = ck_realloc(c->read_buf, c->read_len + 1); + + /* Retry reading until SSL_ERROR_WANT_READ. */ + + if (c->proto == PROTO_HTTPS && + read_res && c->read_len < size_limit) goto SSL_read_more; + + c->read_buf[c->read_len] = 0; /* NUL-terminate for sanity. */ + + /* We force final parse_response() if response length exceeded + size_limit by more than 4 kB. The assumption here is that + it is less expensive to redo the connection than it is + to continue receiving an unknown amount of extra data. */ + + p_ret = parse_response(c->q->req, c->q->res, c->read_buf, c->read_len, + (c->read_len > (size_limit + READ_CHUNK)) ? 0 : 1); + + c->q->req->end_time = time(0); + + if (!p_ret || p_ret == 3) { + + u8 keep; + + c->q->res->state = STATE_OK; + keep = c->q->req->callback(c->q->req, c->q->res); + + /* If we got all data without hitting the limit, and if + "Connection: close" is not indicated, we might want + to keep the connection for future use. */ + + if (c->read_len > (size_limit + READ_CHUNK) || p_ret) + destroy_unlink_conn(c, keep); else reuse_conn(c, keep); + + if (req_errors_cur <= max_fail) + req_errors_cur = 0; + + } else if (p_ret == 2) { + c->q->res->state = STATE_RESPERR; + destroy_unlink_conn(c, c->q->req->callback(c->q->req, c->q->res)); + req_errors_http++; + req_errors_cur++; + } else { + c->last_rw = cur_time; + c->q->res->state = STATE_RECEIVE; + } + + } else destroy_unlink_conn(c, 0); /* Unsolicited response! */ + + } else + + /* Write possible (if SSL_read() did not request a write), or + continuation of SSL_write() possible (if SSL_write() wanted to + read). Send data, update timers, etc. */ + + if (((p[i].revents & POLLOUT) && !c->SSL_rd_w_wr) || + ((p[i].revents & POLLIN) && c->SSL_wr_w_rd)) { + + if (c->write_len - c->write_off) { + s32 write_res; + + if (c->proto == PROTO_HTTPS) { + s32 ssl_err; + + c->SSL_wr_w_rd = 0; + + write_res = SSL_write(c->srv_ssl, c->write_buf + c->write_off, + c->write_len - c->write_off); + + if (!write_res) goto network_error; + + if (write_res < 0) { + ssl_err = SSL_get_error(c->srv_ssl, write_res); + if (ssl_err == SSL_ERROR_WANT_READ) c->SSL_wr_w_rd = 1; + else if (ssl_err != SSL_ERROR_WANT_WRITE) goto network_error; + write_res = 0; + } else if (!c->ssl_checked) check_ssl(c); + + } else { + write_res = write(c->fd, c->write_buf + c->write_off, + c->write_len - c->write_off); + if (write_res <= 0) goto network_error; + } + + bytes_sent += write_res; + + c->write_off += write_res; + + c->q->res->state = STATE_SEND; + + c->last_rw = cur_time; + + } + + } else + + /* Nothing happened. Check timeouts, kill stale connections. + Active (c->q) connections get checked for total and last I/O + timeouts. Non-active connections must just not exceed + idle_tmout. */ + + if (!p[i].revents) { + + u8 keep = 0; + + if ((c->q && (cur_time - c->last_rw > rw_tmout || + cur_time - c->req_start > resp_tmout)) || + (!c->q && (cur_time - c->last_rw > idle_tmout)) || + (!c->q && tear_down_idle)) { + + if (c->q) { + c->q->res->state = STATE_CONNERR; + keep = c->q->req->callback(c->q->req, c->q->res); + req_errors_net++; + req_errors_cur++; + conn_busy_tmout++; + } else { + conn_idle_tmout++; + tear_down_idle = 0; + } + + destroy_unlink_conn(c, keep); + + } + + } + + c = next; + + } + + } + + /* OK, connection-handling affairs taken care of! Next, let's go through all + queue entries NOT currently associated with a connection, and try to + pair them up with something. */ + + if (queue_cur) { + struct queue_entry *q = queue; + + while (q) { + u32 to_host = 0; + + /* enforce the max requests per seconds requirement */ + if (max_requests_sec && req_sec > max_requests_sec) { + u32 diff = req_sec - max_requests_sec; + + if ((iterations_cnt++)%(diff + 1) != 0) { + idle = 1; + return queue_cur; + } + } + idle = 0; + + struct queue_entry* next = q->next; + + if (!q->c) { + + struct conn_entry* c = conn; + + /* Let's try to find a matching, idle connection first. */ + + while (c) { + struct conn_entry* cnext = c->next; + + if (c->addr == q->req->addr && (++to_host) && + c->port == q->req->port && + c->proto == q->req->proto && !c->q) { + conn_associate(c, q); + goto next_q_entry; + } + + c = cnext; + } + + /* No match. If we are out of slots, request some other idle + connection to be nuked soon. */ + + if (to_host < max_conn_host && conn_cur < max_connections) { + conn_associate(0, q); + goto next_q_entry; + } else tear_down_idle = 1; + + } + +next_q_entry: + + q = next; + + } + + } + + return queue_cur; +} + + +/* Helper function for request / response dumpers: */ +static void dump_params(struct param_array* par) { + u32 i; + + for (i=0;ic;i++) { + + switch (par->t[i]) { + case PARAM_NONE: SAY(" <<<<"); break; + case PARAM_PATH: SAY(" PATH"); break; + case PARAM_PATH_S: SAY(" PT_S"); break; + case PARAM_PATH_C: SAY(" PT_C"); break; + case PARAM_PATH_E: SAY(" PT_E"); break; + case PARAM_PATH_D: SAY(" PT_D"); break; + case PARAM_QUERY: SAY(" QUER"); break; + case PARAM_QUERY_S: SAY(" QR_S"); break; + case PARAM_QUERY_C: SAY(" QR_C"); break; + case PARAM_QUERY_E: SAY(" QR_E"); break; + case PARAM_QUERY_D: SAY(" QR_D"); break; + case PARAM_POST: SAY(" POST"); break; + case PARAM_POST_F: SAY(" FILE"); break; + case PARAM_POST_O: SAY(" OPAQ"); break; + case PARAM_HEADER: SAY(" head"); break; + case PARAM_COOKIE: SAY(" cook"); break; + default: SAY(" ????"); + } + + SAY(":%-20s = '%s'\n", + par->n[i] ? par->n[i] : (u8*)"-", + par->v[i] ? par->v[i] : (u8*)"-"); + + } +} + + +/* Creates a working copy of a request. If all is 0, does not copy + path, query parameters, or POST data (but still copies headers). */ + +struct http_request* req_copy(struct http_request* req, struct pivot_desc* pv, + u8 all) { + struct http_request* ret; + u32 i; + + if (!req) return NULL; + + ret = ck_alloc(sizeof(struct http_request)); + + ret->proto = req->proto; + + if (all) + ret->method = ck_strdup(req->method); + else + ret->method = ck_strdup((u8*)"GET"); + + ret->host = ck_strdup(req->host); + ret->addr = req->addr; + ret->port = req->port; + ret->pivot = pv; + ret->user_val = req->user_val; + + /* Copy all the requested data. */ + + for (i=0;ipar.c;i++) + if (all || HEADER_SUBTYPE(req->par.t[i])) + set_value(req->par.t[i], req->par.n[i], req->par.v[i], -1, + &ret->par); + + memcpy(&ret->same_sig, &req->same_sig, sizeof(struct http_sig)); + + return ret; + +} + + +/* Creates a copy of a response. */ + +struct http_response* res_copy(struct http_response* res) { + struct http_response* ret; + u32 i; + + if (!res) return NULL; + + ret = ck_alloc(sizeof(struct http_response)); + + ret->state = res->state; + ret->code = res->code; + ret->msg = res->msg ? ck_strdup(res->msg) : NULL; + ret->warn = res->warn; + + for (i=0;ihdr.c;i++) + set_value(res->hdr.t[i], res->hdr.n[i], res->hdr.v[i], -1, &ret->hdr); + + ret->pay_len = res->pay_len; + + if (res->pay_len) { + + if (res->flushed && res->flush_dir) { + ret->flushed = 1; + ret->flush_dir = ck_strdup(res->flush_dir); + } else { + ret->payload = ck_alloc(res->pay_len); + memcpy(ret->payload, res->payload, res->pay_len); + } + } + + memcpy(&ret->sig, &res->sig, sizeof(struct http_sig)); + + ret->sniff_mime_id = res->sniff_mime_id; + ret->decl_mime_id = res->decl_mime_id; + ret->doc_type = res->doc_type; + ret->css_type = res->css_type; + ret->js_type = res->js_type; + ret->json_safe = res->json_safe; + ret->stuff_checked = res->stuff_checked; + ret->scraped = res->scraped; + + if (res->meta_charset) + ret->meta_charset = ck_strdup(res->meta_charset); + + if (res->header_charset) + ret->header_charset = ck_strdup(res->header_charset); + + if (res->header_mime) + ret->header_mime = ck_strdup(res->header_mime); + + ret->sniffed_mime = res->sniffed_mime; + + return ret; + +} + +/* Dumps HTTP request data, for diagnostic purposes: */ + +void dump_http_request(struct http_request* r) { + + u8 *new_url, *tmp; + + SAY("\n== HTTP REQUEST %p ==\n\nBasic values:\n", r); + + SAY(" Proto = %u\n", r->proto); + SAY(" Method = %s\n", r->method ? r->method : (u8*)"(GET)"); + SAY(" Host = %s\n", r->host); + SAY(" Addr = %u.%u.%u.%u\n", ((u8*)&r->addr)[0], ((u8*)&r->addr)[1], + ((u8*)&r->addr)[2], ((u8*)&r->addr)[3]); + SAY(" Port = %d\n", r->port); + SAY(" Xrefs = pivot %p, handler %p, user %d\n", r->pivot, + r->callback, r->user_val); + + new_url = serialize_path(r, 1, 0); + + SAY("\nURLs:\n Original = %s\n" + " Synthetic = %s\n", r->orig_url ? r->orig_url : (u8*)"[none]", + new_url); + + ck_free(new_url); + + SAY("\nParameter array:\n"); + + dump_params(&r->par); + + SAY("\nRaw request data:\n\n"); + + tmp = build_request_data(r); + SAY("%s\n",tmp); + ck_free(tmp); + + SAY("\n== END OF REQUEST ==\n"); + +} + + +/* Dumps HTTP response data, likewise: */ + +void dump_http_response(struct http_response* r) { + + SAY("\n== HTTP RESPONSE %p ==\n\nBasic values:\n", r); + + SAY(" State = %u\n", r->state); + SAY(" Response = %u ('%s')\n", r->code, r->msg); + SAY(" Flags = %08x\n", r->warn); + SAY(" Data len = %u\n", r->pay_len); + + SAY("\nParameter array:\n"); + + dump_params(&r->hdr); + + if (r->payload) SAY("\nPayload data (%u):\n\n%s\n", r->pay_len, r->payload); + + SAY("\n== END OF RESPONSE ==\n"); + +} + +/* Destroys http state information, for memory profiling. */ + +void destroy_http() { + u32 i; + struct dns_entry* cur; + + for (i=0;inext; + ck_free(cur->name); + ck_free(cur); + cur = next; + } + +} + + +/* Shows some pretty statistics. */ + +void http_stats(u64 st_time) { + u64 en_time; + struct timeval tv; + + gettimeofday(&tv, NULL); + en_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; + + SAY(cLBL "Scan statistics:\n\n" + cGRA " Scan time : " cNOR "%u:%02u:%02u.%03u\n" + cGRA " HTTP requests : " cNOR "%u (%.01f/s), %llu kB in, " + "%llu kB out (%.01f kB/s) \n" + cGRA " Compression : " cNOR "%llu kB in, %llu kB out " + "(%.01f%% gain) \n" + cGRA " HTTP faults : " cNOR "%u net errors, %u proto errors, " + "%u retried, %u drops\n" + cGRA " TCP handshakes : " cNOR "%u total (%.01f req/conn) \n" + cGRA " TCP faults : " cNOR "%u failures, %u timeouts, %u purged\n" + cGRA " External links : " cNOR "%u skipped\n" + cGRA " Reqs pending : " cNOR "%u \n", + + /* hrs */ (u32)((en_time - st_time) / 1000 / 60 / 60), + /* min */ (u32)((en_time - st_time) / 1000 / 60) % 60, + /* sec */ (u32)((en_time - st_time) / 1000) % 60, + /* ms */ (u32)((en_time - st_time) % 1000), + + req_count - queue_cur, + (float) (req_count - queue_cur / 1.15) * 1000 / (en_time - st_time + 1), + (unsigned long long int) bytes_recv / 1024, + (unsigned long long int) bytes_sent / 1024, + (float) (bytes_recv + bytes_sent) / 1.024 / (en_time - st_time + 1), + + (unsigned long long int) bytes_deflated / 1024, + (unsigned long long int) bytes_inflated / 1024, + ((float) bytes_inflated - bytes_deflated) / (bytes_inflated + + bytes_deflated + 1) * 100, + + req_errors_net, req_errors_http, req_retried, req_dropped, + + conn_count, (float) req_count / conn_count, + conn_failed, conn_busy_tmout, conn_idle_tmout, + url_scope, queue_cur); +} + + +/* Show currently handled requests. */ + +#define SP70 \ + " " + +void http_req_list(void) { + u32 i; + struct conn_entry* c = conn; + + SAY(cLBL "In-flight requests (max 15 shown):\n\n"); + + for (i=0;i<15;i++) { + + SAY(" " cGRA "[" cBLU "%02d" cGRA "] " cBRI, i + 1); + + if (c && c->q) { + u8* p = serialize_path(c->q->req, 1, 0); + u32 l = strlen((char*)p); + + if (l > 70) { + SAY("%.30s" cGRA "..." cBRI "%.37s\n", p, p + l - 37); + } else { + SAY("%s%s\n", p, SP70 + l); + } + + ck_free(p); + + } else SAY(cLGN "%s\n", SP70 + 11); + + if (c) c = c->next; + + } + +} diff -Nru skipfish-2.02b/src/http_client.h skipfish-2.10b/src/http_client.h --- skipfish-2.02b/src/http_client.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/http_client.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,479 @@ +/* + skipfish - high-performance, single-process asynchronous HTTP client + -------------------------------------------------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#ifndef _HAVE_HTTP_CLIENT_H +#define _HAVE_HTTP_CLIENT_H + +#include + +#include "config.h" +#include "types.h" +#include "alloc-inl.h" +#include "string-inl.h" + +/* Generic type-name-value array, used for HTTP headers, etc: */ + +struct param_array { + u8* t; /* Type */ + u8** n; /* Name */ + u8** v; /* Value */ + u8** h; /* Host */ + u32 c; /* Count */ +}; + +/* Flags for http_request protocol: */ + +#define PROTO_NONE 0 /* Illegal value */ +#define PROTO_HTTP 1 /* Plain-text HTTP */ +#define PROTO_HTTPS 2 /* TLS/SSL wrapper */ + +/* Flags for http_request parameter list entries: */ + +#define PARAM_NONE 0 /* Empty parameter slot */ + +#define PARAM_PATH 10 /* Path or parametrized path */ +#define PARAM_PATH_S 11 /* - Semicolon element */ +#define PARAM_PATH_C 12 /* - Comma element */ +#define PARAM_PATH_E 13 /* - Exclamation mark element */ +#define PARAM_PATH_D 14 /* - Dollar sign element */ + +#define PATH_SUBTYPE(_x) ((_x) >= PARAM_PATH && (_x) < PARAM_QUERY) + +#define PARAM_QUERY 20 /* Query parameter */ +#define PARAM_QUERY_S 21 /* - Semicolon element */ +#define PARAM_QUERY_C 22 /* - Comma element */ +#define PARAM_QUERY_E 23 /* - Exclamation mark element */ +#define PARAM_QUERY_D 24 /* - Dollar sign element */ + +#define QUERY_SUBTYPE(_x) ((_x) >= PARAM_QUERY && (_x) < PARAM_POST) + +#define PARAM_POST 50 /* Post parameter */ +#define PARAM_POST_F 51 /* - File field */ +#define PARAM_POST_O 52 /* - Non-standard (e.g., JSON) */ + +#define POST_SUBTYPE(_x) ((_x) >= PARAM_POST && (_x) < PARAM_HEADER) + +#define PARAM_HEADER 100 /* Generic HTTP header */ +#define PARAM_COOKIE 101 /* - HTTP cookie */ + +#define HEADER_SUBTYPE(_x) ((_x) >= PARAM_HEADER) + +/* Different character sets to feed the encoding function */ + +#define ENC_DEFAULT "#&=+;,!$?%" /* Default encoding */ +#define ENC_PATH "#&=+;,!$?%/" /* Path encoding with slash */ +#define ENC_NULL "#&=+;,!$?" /* Encoding without % */ + +/* SSL Cipher strengths */ + +#define SSL_MEDIUM 0x00000040L +#define SSL_HIGH 0x00000080L + + +struct http_response; +struct queue_entry; + +/* HTTP response signature. */ + +struct http_sig { + u32 code; /* HTTP response code */ + u32 data[FP_SIZE]; /* Response fingerprint data */ + u8 has_text; /* Does the page have text */ +}; + +/* HTTP request descriptor: */ + +struct http_request { + + u8 proto; /* Protocol (PROTO_*) */ + u8* method; /* HTTP method (GET, POST, ...) */ + u8* host; /* Host name */ + u32 addr; /* Resolved IP address */ + u16 port; /* Port number to connect to */ + + u8* orig_url; /* Copy of the original URL */ + struct param_array par; /* Parameters, headers, cookies */ + + struct pivot_desc *pivot; /* Pivot descriptor */ + + u32 user_val; /* Can be used freely */ + + u8 (*callback)(struct http_request*, struct http_response*); + /* Callback to invoke when done */ + + struct http_sig same_sig; /* Used by secondary ext fuzz. */ + + /* Used by directory brute-force: */ + + u8* trying_key; /* Current keyword ptr */ + u8 trying_spec; /* Keyword specificity info */ + + u32 check_id; /* Injection test ID */ + u32 check_subid; /* Injection test subid */ + + /* Used by injection tests: */ + + u8* fuzz_par_enc; /* Fuzz target encoding */ + u8 no_cookies; /* Don't send cookies */ + + u8 browser; /* Use specified user-agent */ + + u32 start_time; /* Request start time */ + u32 end_time; /* Request end time */ + + u8 flushed; /* Data is flushed to disk? */ + u8* flush_dir; /* Location of data on disk */ + +}; + +/* Flags for http_response completion state: */ + +#define STATE_NOTINIT 0 /* Request not sent */ +#define STATE_CONNECT 1 /* Connecting... */ +#define STATE_SEND 2 /* Sending request */ +#define STATE_RECEIVE 3 /* Waiting for response */ + +#define STATE_OK 100 /* Proper fetch */ +#define STATE_DNSERR 101 /* DNS error */ +#define STATE_LOCALERR 102 /* Socket or routing error */ +#define STATE_CONNERR 103 /* Connection failed */ +#define STATE_RESPERR 104 /* Response not valid */ +#define STATE_SUPPRESS 200 /* Dropped (limits / errors) */ + +/* Flags for http_response warnings: */ + +#define WARN_NONE 0 /* No warnings */ +#define WARN_PARTIAL 1 /* Incomplete read */ +#define WARN_TRAIL 2 /* Trailing request garbage */ +#define WARN_CFL_HDR 4 /* Conflicting headers */ + +/* HTTP response descriptor: */ + +struct http_response { + + u32 state; /* HTTP convo state (STATE_*) */ + u32 code; /* HTTP response code */ + u8* msg; /* HTTP response message */ + u32 warn; /* Warning flags */ + + u8 cookies_set; /* Sets cookies? */ + + struct param_array hdr; /* Server header, cookie list */ + + u32 pay_len; /* Response payload length */ + u8* payload; /* Response payload data */ + + struct http_sig sig; /* Response signature data */ + + /* Various information populated by content checks: */ + + u8 sniff_mime_id; /* Sniffed MIME (MIME_*) */ + u8 decl_mime_id; /* Declared MIME (MIME_*) */ + + u8* meta_charset; /* META tag charset value */ + u8* header_charset; /* Content-Type charset value */ + u8* header_mime; /* Content-Type MIME type */ + u8* sniffed_mime; /* Detected MIME type (ref) */ + + /* Everything below is of interest to scrape_response() only: */ + + u8 doc_type; /* 0 - tbd, 1 - bin, 2 - ascii */ + u8 css_type; /* 0 - tbd, 1 - other, 2 - css */ + u8 js_type; /* 0 - tbd, 1 - other, 2 - js */ + u8 json_safe; /* 0 - no, 1 - yes */ + u8 stuff_checked; /* check_stuff() called? */ + u8 scraped; /* scrape_response() called? */ + + u8 flushed; /* Data is flushed to disk? */ + u8* flush_dir; /* Location of data on disk */ + +}; + +/* Open keep-alive connection descriptor: */ + +struct conn_entry { + + s32 fd; /* The actual file descriptor */ + + u8 proto; /* Protocol (PROTO_*) */ + u32 addr; /* Destination IP */ + u32 port; /* Destination port */ + + u8 reused; /* Used for earier requests? */ + + u32 req_start; /* Unix time: request start */ + u32 last_rw; /* Unix time: last read / write */ + + SSL_CTX *srv_ctx; /* SSL context */ + SSL *srv_ssl; + u8 SSL_rd_w_wr; /* SSL_read() wants to write? */ + u8 SSL_wr_w_rd; /* SSL_write() wants to read? */ + u8 ssl_checked; /* SSL state checked? */ + + u8* read_buf; /* Current read buffer */ + u32 read_len; + u8* write_buf; /* Pending write buffer */ + u32 write_off; /* Current write offset */ + u32 write_len; + + u8* origin; /* Connection origin */ + + struct queue_entry* q; /* Current queue entry */ + + struct conn_entry* prev; /* Previous connection entry */ + struct conn_entry* next; /* Next connection entry */ + +}; + +/* Request queue descriptor: */ + +struct queue_entry { + struct http_request* req; /* Request descriptor */ + struct http_response* res; /* Response descriptor */ + struct conn_entry* c; /* Connection currently used */ + struct queue_entry* prev; /* Previous queue entry */ + struct queue_entry* next; /* Next queue entry */ + u8 retrying; /* Request being retried? */ +}; + +/* DNS cache item: */ + +struct dns_entry { + u8* name; /* Name requested */ + u32 addr; /* IP address (0 = bad host) */ + struct dns_entry* next; /* Next cache entry */ +}; + + +/* Simplified macros to manipulate param_arrays: */ + +#define ADD(_ar,_t,_n,_v) do { \ + u32 _cur = (_ar)->c++; \ + (_ar)->t = ck_realloc((_ar)->t, (_ar)->c); \ + (_ar)->n = ck_realloc((_ar)->n, (_ar)->c * sizeof(u8*)); \ + (_ar)->v = ck_realloc((_ar)->v, (_ar)->c * sizeof(u8*)); \ + (_ar)->t[cur] = _t; \ + (_ar)->n[cur] = (_n) ? ck_strdup(_n) : 0; \ + (_ar)->v[cur] = (_v) ? ck_strdup(_v) : 0; \ + } while (0) + +#define FREE(_ar) do { \ + while ((_ar)->c--) { \ + ck_free((_ar)->n[(_ar)->c]); \ + ck_free((_ar)->v[(_ar)->c]); \ + } \ + ck_free((_ar)->t); \ + ck_free((_ar)->n); \ + ck_free((_ar)->v); \ + } while (0) + + +/* Extracts parameter value from param_array. Name is matched if + non-NULL. Returns pointer to value data, not a duplicate string; + NULL if no match found. */ + +u8* get_value(u8 type, u8* name, u32 offset, struct param_array* par); + +/* Inserts or overwrites parameter value in param_array. If offset + == -1, will append parameter to list. Duplicates strings, + name and val can be NULL. */ + +void set_value(u8 type, u8* name, u8* val, s32 offset, struct param_array* par); + +/* Simplified macros for value table access: */ + +#define GET_CK(_name, _p) get_value(PARAM_COOKIE, _name, 0, _p) +#define SET_CK(_name, _val, _p) set_value(PARAM_COOKIE, _name, _val, 0, _p) +#define GET_PAR(_name, _p) get_value(PARAM_QUERY, _name, 0, _p) +#define SET_PAR(_name, _val, _p) set_value(PARAM_QUERY, _name, _val, -1, _p) +#define GET_HDR(_name, _p) get_value(PARAM_HEADER, _name, 0, _p) +#define SET_HDR(_name, _val, _p) set_value(PARAM_HEADER, _name, _val, -1, _p) +#define GET_HDR_OFF(_name, _p, _o) get_value(PARAM_HEADER, _name, _o, _p) + +void tokenize_path(u8* str, struct http_request* req, u8 add_slash); + +/* Convert a fully-qualified or relative URL string to a proper http_request + representation. Returns 0 on success, 1 on format error. */ + +u8 parse_url(u8* url, struct http_request* req, struct http_request* ref); + +/* URL-decodes a string. 'Plus' parameter governs the behavior on + + signs (as they have a special meaning only in query params, not in path). */ + +u8* url_decode_token(u8* str, u32 len, u8 plus); + +/* URL-encodes a string according to custom rules. The assumption here is that + the data is already tokenized as "special" boundaries such as ?, =, &, /, + ;, so these characters must always be escaped if present in tokens. We + otherwise let pretty much everything else go through, as it may help with + the exploitation of certain vulnerabilities. */ + +u8* url_encode_token(u8* str, u32 len, u8* enc_set); + +/* Reconstructs URI from http_request data. Includes protocol and host + if with_host is non-zero. */ + +u8* serialize_path(struct http_request* req, u8 with_host, u8 with_post); + +/* Looks up IP for a particular host, returns data in network order. + Uses standard resolver, so it is slow and blocking, but we only + expect to call it a couple of times. */ + +u32 maybe_lookup_host(u8* name); + +/* Creates an ad hoc DNS cache entry, to override NS lookups. */ + +void fake_host(u8* name, u32 addr); + +/* Schedules a new asynchronous request; req->callback() will be invoked when + the request is completed. */ + +void async_request(struct http_request* req); + +/* Prepares a serialized HTTP buffer to be sent over the network. */ + +u8* build_request_data(struct http_request* req); + +/* Parses a network buffer containing raw HTTP response received over the + network ('more' == the socket is still available for reading). Returns 0 + if response parses OK, 1 if more data should be read from the socket, + 2 if the response seems invalid. */ + +u8 parse_response(struct http_request* req, struct http_response* res, u8* data, + u32 data_len, u8 more); + +/* Helper function for grabbing lines when parsing requests and responses */ + +u8* grab_line(u8* data, u32* cur_pos, u32 data_len); + +/* Processes the queue. Returns the number of queue entries remaining, + 0 if none. Will do a blocking select() to wait for socket state changes + (or timeouts) if no data available to process. This is the main + routine for the scanning loop. */ + +u32 next_from_queue(void); + +/* Dumps HTTP request stats, for debugging purposes: */ + +void dump_http_request(struct http_request* r); + +/* Dumps HTTP response stats, for debugging purposes: */ + +void dump_http_response(struct http_response* r); + +/* Fingerprints a response: */ + +void fprint_response(struct http_response* res); + +/* Performs a deep free() of sturct http_request */ + +void destroy_request(struct http_request* req); + +/* Performs a deep free() of sturct http_response */ + +void destroy_response(struct http_response* res); + +/* Creates a working copy of a request. If all is 0, does not copy + path, query parameters, or POST data (but still copies headers). */ + +struct http_request* req_copy(struct http_request* req, struct pivot_desc* pv, + u8 all); + +/* Creates a copy of a response. */ + +struct http_response* res_copy(struct http_response* res); + +/* Various settings and counters exported to other modules: */ + +extern u32 max_connections, + max_conn_host, + max_requests, + max_fail, + idle_tmout, + resp_tmout, + rw_tmout, + size_limit, + req_errors_net, + req_errors_http, + req_errors_cur, + req_count, + req_dropped, + req_retried, + url_scope, + conn_count, + conn_idle_tmout, + conn_busy_tmout, + conn_failed, + queue_cur; + +extern float req_sec, + max_requests_sec; + +extern u64 bytes_sent, + bytes_recv, + bytes_deflated, + bytes_inflated, + iterations_cnt; + +extern u8 ignore_cookies, + idle; + +/* Flags for browser type: */ + +#define BROWSER_FAST 0 /* Minimimal HTTP headers */ +#define BROWSER_MSIE 1 /* Try to mimic MSIE */ +#define BROWSER_FFOX 2 /* Try to mimic Firefox */ +#define BROWSER_PHONE 4 /* Try to mimic iPhone */ + +extern u8 browser_type; + +/* Flags for authentication type: */ + +#define AUTH_NONE 0 /* No authentication */ +#define AUTH_BASIC 1 /* 'Basic' HTTP auth */ +#define AUTH_FORM 2 /* Form HTTP auth */ + +extern u8 auth_type; + +extern u8 *auth_user, + *auth_pass; + +#ifdef PROXY_SUPPORT +extern u8* use_proxy; +extern u32 use_proxy_addr; +extern u16 use_proxy_port; +#endif /* PROXY_SUPPORT */ + +/* Global HTTP cookies, extra headers: */ + +extern struct param_array global_http_par; + +/* Destroys http state information, for memory profiling. */ + +void destroy_http(); + +/* Shows some pretty statistics. */ + +void http_stats(u64 st_time); +void http_req_list(void); + +#endif /* !_HAVE_HTTP_CLIENT_H */ diff -Nru skipfish-2.02b/src/options.c skipfish-2.10b/src/options.c --- skipfish-2.02b/src/options.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/options.c 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,120 @@ +/* + skipfish - Config parsing + ---------------------------------------- + + Author: Niels Heinen , + + Copyright 2012 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#include + +#define _VIA_OPTIONS_C + +#include "options.h" +#include "types.h" +#include "debug.h" +#include "alloc-inl.h" +#include "string-inl.h" + +u8 **fargv; +u32 fargc = 0; + +/* This function reads the configuration file turns them into + flags that are given to getopt_long. */ + +int read_config_file(const char *filename, int *_argc, char ***_argv) { + + FILE *fh; + char line[MAX_LINE_LEN + 1]; + char *val, *ptr; + u8 *tmp; + u32 idx, i; + + APPEND_STRING(fargv, fargc, ck_strdup((u8*)*_argv[0])); + + fh = fopen(filename, "r"); + if (!fh) PFATAL("Unable to read config from: %s", filename); + + while (!feof(fh) && fargc < MAX_ARGS && fgets(line, MAX_LINE_LEN, fh)) { + + /* Skip comments and empty lines */ + if (line[0] == '\n' || line[0] == '\r' || line[0] == '#') + continue; + + /* NULL terminate the key */ + idx = strcspn(line, " \t="); + if (idx == strlen(line)) + FATAL("Config key error at line: %s", line); + line[idx] = '\0'; + + /* Find the beginning of the value. */ + val = line + (idx + 1); + idx = strspn(val, " \t="); + if (idx == strlen(val)) + FATAL("Config value error at line: %s", line); + val = val + idx; + + /* Trim the unwanted characters from the value */ + ptr = val + (strlen(val) - 1); + while(*ptr && *ptr < 0x21) { + *ptr = 0; + ptr--; + } + + /* Done! Now we have a key/value pair. If the flag is set to 'false' + we will disregard this line. If the value is 'true', we will set + the flag without a value. In any other case, we will set the flag + and value */ + + if (val[0] == '\0') + FATAL("Empty value in config line: %s", line); + + if (strcasecmp("false", val) == 0) + continue; + + tmp = ck_alloc(strlen(line) + 3); + sprintf((char*)tmp, "--%s", line); + + APPEND_STRING(fargv, fargc, tmp); + if (strncasecmp("true", val, 3) != 0) + APPEND_STRING(fargv, fargc, ck_strdup((u8*)val)); + + } + + /* Copy arguments from command line into our array */ + for (i=1; i<*_argc && fargc < MAX_ARGS; ++i) + APPEND_STRING(fargv, fargc, ck_strdup((u8*)(*_argv)[i])); + + /* Replace original flags */ + *_argc = fargc; + *_argv = (char **)fargv; + + fclose(fh); + return 0; +} + +/* Helper function to cleanup memory */ + +void destroy_config() { + + if (fargc == 0) return; + + while (fargc-- != 0) + ck_free(fargv[fargc]); + ck_free(fargv); + +} diff -Nru skipfish-2.02b/src/options.h skipfish-2.10b/src/options.h --- skipfish-2.02b/src/options.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/options.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,117 @@ +/* + skipfish - option and config parsing + ------------------------------------ + + Author: Niels Heinen + + Copyright 2012 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +#ifndef __OPTIONS_H +#define __OPTIONS_H + +#include +#include + +/* Config file reader function */ + +int read_config_file(const char *filename, int *_argc, char ***_argv); + +/* Config parsing cleanup function that releases memory */ + +void destroy_config(); + +/* Long flags */ + +#ifdef _VIA_OPTIONS_C + +#define MAX_LINE_LEN 2048 +#define MAX_ARGS 100 + +#else + +/* The option string for getopt_long */ + +#define OPT_STRING "+A:B:C:D:EF:G:H:I:J:K:LMNOPQR:S:T:UW:X:YZ" \ + "b:c:d:ef:g:hi:k:l:m:o:p:q:r:s:t:uvw:x:z:" + +struct option long_options[] = { + {"auth", required_argument, 0, 'A' }, + {"host", required_argument, 0, 'F' }, + {"cookie", required_argument, 0, 'C' }, + {"reject-cookies", no_argument, 0, 'N' }, + {"header", required_argument, 0, 'H' }, + {"user-agent", required_argument, 0, 'b' }, +#ifdef PROXY_SUPPORT + {"proxy", required_argument, 0, 'J' }, +#endif /* PROXY_SUPPORT */ + {"max-crawl-depth", required_argument, 0, 'd' }, + {"max-crawl-child", required_argument, 0, 'c' }, + {"max-crawl-descendants", required_argument, 0, 'x' }, + {"max-request-total", required_argument, 0, 'r' }, + {"max-request-rate", required_argument, 0, 'l'}, + {"crawl-probability", required_argument, 0, 'p' }, + {"seed", required_argument, 0, 'q' }, + {"include-string", required_argument, 0, 'I' }, + {"exclude-string", required_argument, 0, 'X' }, + {"skip-parameter", required_argument, 0, 'K' }, + {"no-form-submits", no_argument, 0, 'O' }, + {"include-domain", required_argument, 0, 'D' }, + {"no-html-parsing", no_argument, 0, 'P' }, + {"no-extension-brute", no_argument, 0, 'Y' }, + {"log-mixed-content", no_argument, 0, 'M' }, + {"skip-error-pages", no_argument, 0, 'Z' }, + {"log-external-urls", no_argument, 0, 'U' }, + {"log-cache-mismatches", no_argument, 0, 'E' }, + {"form-value", required_argument, 0, 'T' }, + {"rw-wordlist", required_argument, 0, 'W' }, + {"no-keyword-learning", no_argument, 0, 'L' }, + {"wordlist", required_argument, 0, 'S'}, + {"trust-domain", required_argument, 0, 'B' }, + {"max-connections", required_argument, 0, 'g' }, + {"max-host-connections", required_argument, 0, 'm' }, + {"max-failed-requests", required_argument, 0, 'f' }, + {"request-timeout", required_argument, 0, 't' }, + {"network-timeout", required_argument, 0, 'w' }, + {"idle-timeout", required_argument, 0, 'i' }, + {"response-size", required_argument, 0, 's' }, + {"discard-binary", no_argument, 0, 'e' }, + {"output", required_argument, 0, 'o' }, + {"help", no_argument, 0, 'h' }, + {"quiet", no_argument, 0, 'u' }, + {"verbose", no_argument, 0, 'v' }, + {"scan-timeout", required_argument, 0, 'k'}, + {"signatures", required_argument, 0, 'z'}, + {"checks", no_argument, 0, 0}, + {"checks-toggle", required_argument, 0, 0}, + {"no-injection-tests", no_argument, 0, 0}, + {"fast", no_argument, 0, 0}, + {"flush-to-disk", no_argument, 0, 0}, + {"config", required_argument, 0, 0}, + {"auth-form", required_argument, 0, 0}, + {"auth-form-target", required_argument, 0, 0}, + {"auth-user", required_argument, 0, 0}, + {"auth-user-field", required_argument, 0, 0}, + {"auth-pass", required_argument, 0, 0}, + {"auth-pass-field", required_argument, 0, 0}, + {"auth-verify-url", required_argument, 0, 0}, + {0, 0, 0, 0 } + +}; + +#endif /* !__VIA_OPTIONS_C */ + +#endif /* __OPTIONS_H */ diff -Nru skipfish-2.02b/src/report.c skipfish-2.10b/src/report.c --- skipfish-2.02b/src/report.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/report.c 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,1038 @@ +/* + skipfish - post-processing and reporting + ---------------------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "config.h" +#include "types.h" +#include "http_client.h" +#include "database.h" +#include "crawler.h" +#include "checks.h" +#include "analysis.h" + +/* Pivot and issue signature data. */ + +struct p_sig_desc { + u8 type; /* Pivot type */ + struct http_sig* res_sig; /* Response signature */ + u32 issue_sig; /* Issues fingerprint */ + u32 child_sig; /* Children fingerprint */ +}; + + +static struct p_sig_desc* p_sig; +static u32 p_sig_cnt; +u8 suppress_dupes; +u8 *output_dir = NULL; +u32 verbosity = 0; + + +/* Response, issue sample data. */ + +struct mime_sample_desc { + u8* det_mime; + struct http_request** req; + struct http_response** res; + u32 sample_cnt; +}; + + +struct issue_sample_desc { + u32 type; + struct issue_desc** i; + u32 sample_cnt; +}; + +static struct mime_sample_desc* m_samp; +static struct issue_sample_desc* i_samp; +static u32 m_samp_cnt, i_samp_cnt; + + +/* qsort() helper for sort_annotate_pivot(). */ + +static int pivot_compar(const void* par1, const void* par2) { + const struct pivot_desc *p1 = *(struct pivot_desc**)par1, + *p2 = *(struct pivot_desc**)par2; + + /* Force directories to appear before files, etc. */ + + if (p1->type < p2->type) return -1; + if (p1->type > p2->type) return 1; + + return strcasecmp((char*)p1->name, (char*)p2->name); +} + +static int issue_compar(const void* par1, const void* par2) { + const struct issue_desc *i1 = par1, *i2 = par2; + return i2->type - i1->type; +} + + +/* Recursively annotates and sorts pivots. */ + +static void sort_annotate_pivot(struct pivot_desc* pv) { + u32 i, path_child = 0; + static u32 proc_cnt; + u8 *q1, *q2; + + /* Add notes to all non-dir nodes with dir or file children... */ + + for (i=0;ichild_cnt;i++) { + if (pv->child[i]->type == PIVOT_FILE || pv->child[i]->type == PIVOT_DIR) path_child = 1; + sort_annotate_pivot(pv->child[i]); + } + + if (pv->type != PIVOT_DIR && pv->type != PIVOT_SERV && + pv->type != PIVOT_ROOT && path_child) + problem(PROB_NOT_DIR, pv->req, pv->res, 0, pv, 0); + + /* Non-parametric nodes with digits in the name were not brute-forced, + but the user might be interested in doing so. Skip images here. */ + + if (pv->fuzz_par == -1 && pv->res && + (pv->res->sniff_mime_id < MIME_IMG_JPEG || + pv->res->sniff_mime_id > MIME_AV_WMEDIA) && + (pv->type == PIVOT_DIR || pv->type == PIVOT_FILE || + pv->type == PIVOT_PATHINFO) && !pv->missing) { + i = strlen((char*)pv->name); + while (i--) + if (isdigit(pv->name[i])) { + problem(PROB_FUZZ_DIGIT, pv->req, pv->res, 0, pv, 0); + break; + } + } + + /* Parametric nodes that seem to contain queries in parameters, and are not + marked as bogus_par, should be marked as dangerous. */ + + if (pv->fuzz_par != -1 && !pv->bogus_par && + (((q1 = (u8*)strchr((char*)pv->req->par.v[pv->fuzz_par], '(')) && + (q2 = (u8*)strchr((char*)pv->req->par.v[pv->fuzz_par], ')')) && q1 < q2 && + !isdigit(q1[1])) || + ((inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)"SELECT ") || + inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)"DELETE ") ) && + inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)" FROM ")) || + (inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)"UPDATE ") || + inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)" WHERE ")) || + inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)"DROP TABLE ") || + inl_strcasestr(pv->req->par.v[pv->fuzz_par], (u8*)" ORDER BY "))) + problem(PROB_SQL_PARAM, pv->req, pv->res, 0, pv, 0); + + /* Sort children nodes and issues as appropriate. */ + + if (pv->child_cnt > 1) + qsort(pv->child, pv->child_cnt, sizeof(struct pivot_desc*), pivot_compar); + + if (pv->issue_cnt > 1) + qsort(pv->issue, pv->issue_cnt, sizeof(struct issue_desc), issue_compar); + + if ((!(proc_cnt++ % 50)) || pv->type == PIVOT_ROOT) { + SAY(cLGN "\r[+] " cNOR "Sorting and annotating crawl nodes: %u", proc_cnt); + fflush(0); + } + +} + + +/* Issue extra hashing helper. */ + +static inline u32 hash_extra(u8* str) { + register u32 ret = 0; + register u8 cur; + + if (str) + while ((cur=*str)) { + ret = ~ret ^ (cur) ^ + (cur << 5) ^ (~cur >> 5) ^ + (cur << 10) ^ (~cur << 15) ^ + (cur << 20) ^ (~cur << 25) ^ + (cur << 30); + str++; + } + + return ret; +} + + + +/* Registers a new pivot signature, or updates an existing one. */ + +static void maybe_add_sig(struct pivot_desc* pv) { + u32 i, issue_sig = ~(pv->issue_cnt | (pv->desc_issue_cnt << 16)), + child_sig = ~(pv->desc_cnt | (pv->child_cnt << 16)); + + if (!pv->res) return; + + /* Compute a rough children node signature based on children types. */ + + for (i=0;ichild_cnt;i++) + child_sig ^= (hash_extra(pv->child[i]->name) ^ + pv->child[i]->type) << (i % 16); + + /* Do the same for all recorded issues. */ + + for (i=0;iissue_cnt;i++) + issue_sig ^= (hash_extra(pv->issue[i].extra) ^ + pv->issue[i].type) << (i % 16); + + /* Assign a simplified signature to the pivot. */ + + pv->pv_sig = (pv->type << 16) ^ ~child_sig ^ issue_sig; + + /* See if a matching signature already exists. */ + + for (i=0;itype && p_sig[i].issue_sig == issue_sig && + p_sig[i].child_sig == child_sig && + same_page(p_sig[i].res_sig, &pv->res->sig)) { + + /* We don't mark parameters as dupes: different parameters for the + same page will get the same signature. If we mark them as duplicate, + some issues, like XSS' will only be reported once while that might + be present in two or more parameters. */ + + if (pv->type != PIVOT_PARAM && !pv->bogus_par) + pv->dupe = 1; + return; + + } + + /* No match - create a new one. */ + + p_sig = ck_realloc(p_sig, (p_sig_cnt + 1) * sizeof(struct p_sig_desc)); + + p_sig[p_sig_cnt].type = pv->type; + p_sig[p_sig_cnt].res_sig = &pv->res->sig; + p_sig[p_sig_cnt].issue_sig = issue_sig; + p_sig[p_sig_cnt].child_sig = child_sig; + p_sig_cnt++; + +} + + + + +/* Recursively collects unique signatures for pivots. */ + +static void collect_signatures(struct pivot_desc* pv) { + u32 i; + static u32 proc_cnt; + + maybe_add_sig(pv); + for (i=0;ichild_cnt;i++) collect_signatures(pv->child[i]); + + if ((!(proc_cnt++ % 50)) || pv->type == PIVOT_ROOT) { + SAY(cLGN "\r[+] " cNOR "Looking for duplicate entries: %u", proc_cnt); + fflush(0); + } + +} + + +/* Destroys signature data (for memory profiling purposes). */ + +void destroy_signatures(void) { + u32 i; + + ck_free(p_sig); + + for (i=0;iparent; + static u32 proc_cnt; + + for (i=0;ichild_cnt;i++) compute_counts(pv->child[i]); + + if (pv->dupe) return; + + while (tmp) { + tmp->total_child_cnt++; + tmp = tmp->parent; + } + + for (i=0;iissue_cnt;i++) { + u8 sev = PSEV(pv->issue[i].type); + tmp = pv; + while (tmp) { + tmp->total_issues[sev]++; + tmp = tmp->parent; + } + } + + if ((!(proc_cnt++ % 50)) || pv->type == PIVOT_ROOT) { + SAY(cLGN "\r[+] " cNOR "Counting unique nodes: %u", proc_cnt); + fflush(0); + } + +} + + +/* Helper to JS-escape data. Static buffer, will be destroyed on + subsequent calls. */ + +static inline u8* js_escape(u8* str, u8 sp) { + u32 len; + static u8* ret; + u8* opos; + + if (!str) return (u8*)"[none]"; + + len = strlen((char*)str); + + if (ret) __DFL_ck_free(ret); + opos = ret = __DFL_ck_alloc(len * 4 + 1); + + while (len--) { + if (*str > (sp ? 0x20 : 0x1f) && *str < 0x80 && !strchr("<>\\'\"", *str)) { + *(opos++) = *(str++); + } else { + sprintf((char*)opos, "\\x%02x", *(str++)); + opos += 4; + } + } + + *opos = 0; + + return ret; + +} + + +static void output_scan_info(u64 scan_time, u32 seed) { + FILE* f; + time_t t = time(NULL); + u8* ct = (u8*)ctime(&t); + + if (isspace(ct[strlen((char*)ct)-1])) + ct[strlen((char*)ct)-1] = 0; + + f = fopen("summary.js", "w"); + if (!f) PFATAL("Cannot open 'summary.js'"); + + fprintf(f, "var sf_version = '%s';\n", VERSION); + fprintf(f, "var scan_date = '%s';\n", js_escape(ct, 0)); + fprintf(f, "var scan_seed = '0x%08x';\n", seed); + fprintf(f, "var scan_ms = %llu;\n", (long long)scan_time); + + fclose(f); + +} + + +/* Helper to save request, response data. */ + +static void describe_res(FILE* f, struct http_response* res) { + + if (!res) { + fprintf(f, "'fetched': false, 'error': 'Content not fetched'"); + return; + } + + switch (res->state) { + + case 0 ... STATE_OK - 1: + fprintf(f, "'fetched': false, 'error': '(Reported while fetch in progress)'"); + break; + + case STATE_OK: + fprintf(f, "'fetched': true, 'code': %u, 'len': %u, 'decl_mime': '%s', ", + res->code, res->pay_len, + js_escape(res->header_mime, 0)); + + fprintf(f, "'sniff_mime': '%s', 'cset': '%s'", + res->sniffed_mime ? res->sniffed_mime : (u8*)"[none]", + js_escape(res->header_charset ? res->header_charset + : res->meta_charset, 0)); + break; + + case STATE_DNSERR: + fprintf(f, "'fetched': false, 'error': 'DNS error'"); + break; + + case STATE_LOCALERR: + fprintf(f, "'fetched': false, 'error': 'Local network error'"); + break; + + case STATE_CONNERR: + fprintf(f, "'fetched': false, 'error': 'Connection error'"); + break; + + case STATE_RESPERR: + fprintf(f, "'fetched': false, 'error': 'Malformed HTTP response'"); + break; + + case STATE_SUPPRESS: + fprintf(f, "'fetched': false, 'error': 'Limits exceeded'"); + break; + + + default: + fprintf(f, "'fetched': false, 'error': 'Unknown error'"); + + } + +} + +/* Helper to save request, response data. */ + +static void create_link(const char *dir, const char *target) { + + if (!dir || !target) return; + + u8 *tmp = ck_alloc(strlen(dir) + strlen(target) + 2); + + sprintf((char*)tmp, "%s/%s", dir, target); + + if (link((char*)tmp, target) == -1) + PFATAL("Unable to create hardlink: %s -> %s\n", dir, target); + + ck_free(tmp); + +} + + +/* Helper function to collects MIME samples. */ + +static void collect_samples(struct http_request *req, struct http_response *res) { + + u32 i; + + /* No data, no samples */ + if (!req || !res) return; + + if (!req->pivot->dupe && res->sniffed_mime) { + + for (i=0;isniffed_mime)) break; + + if (i == m_samp_cnt) { + m_samp = ck_realloc(m_samp, (i + 1) * sizeof(struct mime_sample_desc)); + m_samp[i].det_mime = res->sniffed_mime; + m_samp_cnt++; + } else { + u32 c; + + /* If we already have something that looks very much the same on the + list, don't bother reporting it again. */ + + for (c=0;csig, &res->sig)) return; + } + + m_samp[i].req = ck_realloc(m_samp[i].req, (m_samp[i].sample_cnt + 1) * + sizeof(struct http_request*)); + m_samp[i].res = ck_realloc(m_samp[i].res, (m_samp[i].sample_cnt + 1) * + sizeof(struct http_response*)); + m_samp[i].req[m_samp[i].sample_cnt] = req; + m_samp[i].res[m_samp[i].sample_cnt] = res; + m_samp[i].sample_cnt++; + + } +} + + +static void save_req_res(struct http_request* req, struct http_response* res, u8 sample) { + FILE *f; + u8 *rd, *rs; + u32 i; + + /* No request ? We should probably crash here but that's not nice when + writing down a large report. Will warn in debug mode. */ + if (!req) { + DEBUG("ERROR: save_req_res() was called but req == NULL\n"); + return; + } + + /* First check if the data has already been flushed to disk. If this + is the case, we'll make a copy/hardlink to the data in the CWD */ + + if (res && req->flushed && res->flushed) { + create_link((const char *)req->flush_dir, "request.dat"); + create_link((const char *)req->flush_dir, "request.js"); + create_link((const char *)res->flush_dir, "response.dat"); + create_link((const char *)res->flush_dir, "response.js"); + + /* Collect samples */ + if (sample) collect_samples(req, res); + + /* And done! */ + return; + } + + + /* Getting here means we need to write down the request and + response. This is typically required upon when we flush data from + pivots that are DONE or upon report writing. */ + + rd = build_request_data(req); + f = fopen("request.dat", "w"); + if (!f) PFATAL("Cannot create 'request.dat'"); + fprintf(f, "%s", (char*)rd); + fclose(f); + + /* Write .js file with base64 encoded json data. */ + f = fopen("request.js", "w"); + if (!f) PFATAL("Cannot create 'request.js'"); + + fprintf(f, "var req = {'data':'%s'}", js_escape(rd, 0)); + fclose(f); + + ck_free(rd); + + if (res && res->state == STATE_OK) { + f = fopen("response.dat", "w"); + if (!f) PFATAL("Cannot create 'response.dat'"); + + u64 msg_size = strlen((char*)res->msg); + u64 rs_size = msg_size + strlen("HTTP/1.1 1000 \n") + 1; + rs = ck_alloc(rs_size); + snprintf((char*)rs, rs_size -1, "HTTP/1.1 %u %s\n", res->code, res->msg); + + u32 s = strlen((char*)rs); + for (i=0;ihdr.c;i++) + if (res->hdr.t[i] == PARAM_HEADER) { + ADD_STR_DATA(rs, s, res->hdr.n[i]); + ADD_STR_DATA(rs, s, ": "); + ADD_STR_DATA(rs, s, res->hdr.v[i]); + ADD_STR_DATA(rs, s, "\n"); + } + + + if(res->payload) { + ADD_STR_DATA(rs, s, "\n"); + ADD_STR_DATA(rs, s, res->payload); + } + + fprintf(f, "%s", rs); + fclose(f); + + /* Write .js file with base64 encoded json data. */ + + f = fopen("response.js", "w"); + if (!f) PFATAL("Cannot create 'response.js'"); + fprintf(f, "var res = {'data':'%s'}", js_escape(rs, 0)); + fclose(f); + + ck_free(rs); + + } + + /* Collect samples if asked */ + if (sample) collect_samples(req, res); + +} + +/* Flush payloads to tmp dir so we can free up memory */ + +u32 flush_cnt = 0; +u8 cache_created = 0; + +void flush_payload(struct http_request* req, struct http_response* res) { + + u8 *tmp; + char *cwd; + + /* To simplify: we flush pairs */ + if (!req || !res || res->state != STATE_OK) + return; + + /* Only flush them once */ + if (req->flushed || res->flushed) + return; + + /* Create the flush directory when this function is called for the + first time */ + + if (!cache_created) { + + char *flush_dir = (char*)ck_alloc(strlen((char*)output_dir) + 7); + sprintf(flush_dir, "%s/%s", (char*)output_dir, "cache"); + + if (mkdir(flush_dir, 0755)) + PFATAL("Cannot create cache: %s", flush_dir); + + ck_free(flush_dir); + cache_created = 1; + } + + cwd = getcwd(NULL, 0); + if (!cwd) PFATAL("Unable to get CWD."); + + if (output_dir[0] == '/') { + tmp = ck_alloc(strlen((char*)output_dir) + 33); + sprintf((char*)tmp, "%s/cache/%u", (char*)output_dir, flush_cnt); + } else { + tmp = ck_alloc(strlen(cwd) + strlen((char*)output_dir) + 33); + sprintf((char*)tmp, "%s/%s/cache/%u", cwd, (char*)output_dir, flush_cnt); + } + + if (mkdir((char*)tmp, 0755) == -1) + PFATAL("Cannot create cache subdir: %s", tmp); + + if (chdir((char*)tmp) == -1) + PFATAL("chdir to %s unexpectedly fails!", tmp); + + + /* Record the location */ + req->flush_dir = ck_strdup(tmp); + res->flush_dir = ck_strdup(tmp); + ck_free(tmp); + + save_req_res(req, res, 0); + + req->flushed = 1; + res->flushed = 1; + + /* The big memory saver */ + if (res->payload) { + ck_free(res->payload); + res->payload = NULL; + } + + if (chdir(cwd) == -1) + PFATAL("chdir to %s unexpectedly fails!", cwd); + + free(cwd); + flush_cnt++; + +} + + +/* Dumps the actual crawl data. */ + +static void output_crawl_tree(struct pivot_desc* pv) { + u32 i; + FILE* f; + static u32 proc_cnt; + + /* Save request, response. */ + + save_req_res(pv->req, pv->res, 1); + + /* Write children information. Don't crawl children just yet, + because we could run out of file descriptors on a particularly + deep tree if we keep one open and recurse. */ + + f = fopen("child_index.js", "w"); + if (!f) PFATAL("Cannot create 'child_index.js'."); + + fprintf(f, "var child = [\n"); + + for (i=0;ichild_cnt;i++) { + u8 tmp[32]; + u8* p; + + if (suppress_dupes && pv->child[i]->dupe && + !pv->child[i]->total_child_cnt) continue; + + /* Also completely suppress nodes that seem identical to the + previous one, and have a common prefix (as this implies + a mod_rewrite or htaccess filter). */ + + if (i && pv->child[i-1]->pv_sig == pv->child[i]->pv_sig) { + u8 *pn = pv->child[i-1]->name, *cn = pv->child[i]->name; + u32 pnd = strcspn((char*)pn, "."); + if (!strncasecmp((char*)pn, (char*)cn, pnd)) continue; + } + + sprintf((char*)tmp, "c%u", i); + + fprintf(f, " { 'dupe': %s, 'type': %u, 'name': '%s%s", + pv->child[i]->dupe ? "true" : "false", + pv->child[i]->type, js_escape(pv->child[i]->name, 0), + (pv->child[i]->fuzz_par == -1 || pv->child[i]->type == PIVOT_VALUE) + ? (u8*)"" : (u8*)"="); + + fprintf(f, "%s', 'dir': '%s', 'linked': %d, ", + (pv->child[i]->fuzz_par == -1 || pv->child[i]->type == PIVOT_VALUE) + ? (u8*)"" : + js_escape(pv->child[i]->req->par.v[pv->child[i]->fuzz_par], 0), + tmp, pv->child[i]->linked); + + p = serialize_path(pv->child[i]->req, 1, 1); + fprintf(f, "'url': '%s', ", js_escape(p, 0)); + ck_free(p); + + describe_res(f, pv->child[i]->res); + + fprintf(f,", 'missing': %s, 'csens': %s, 'child_cnt': %u, " + "'issue_cnt': [ %u, %u, %u, %u, %u ], 'sig': 0x%x }%s\n", + pv->child[i]->missing ? "true" : "false", + pv->child[i]->csens ? "true" : "false", + pv->child[i]->total_child_cnt, pv->child[i]->total_issues[1], + pv->child[i]->total_issues[2], pv->child[i]->total_issues[3], + pv->child[i]->total_issues[4], pv->child[i]->total_issues[5], + pv->child[i]->pv_sig, + (i == pv->child_cnt - 1) ? "" : ","); + } + + fprintf(f, "];\n"); + fclose(f); + + /* Write issue index, issue dumps. */ + + f = fopen("issue_index.js", "w"); + if (!f) PFATAL("Cannot create 'issue_index.js'."); + + fprintf(f, "var issue = [\n"); + + for (i=0;iissue_cnt;i++) { + u8 tmp[32]; + sprintf((char*)tmp, "i%u", i); + + fprintf(f, " { 'severity': %u, 'type': %u, 'sid': '%d', 'extra': '%s', ", + PSEV(pv->issue[i].type) - 1, pv->issue[i].type, pv->issue[i].sid, + pv->issue[i].extra ? js_escape(pv->issue[i].extra, 0) : (u8*)""); + + describe_res(f, pv->issue[i].res); + + fprintf(f, ", 'dir': '%s' }%s\n", + tmp, (i == pv->issue_cnt - 1) ? "" : ","); + + if (mkdir((char*)tmp, 0755)) PFATAL("Cannot create '%s'.", tmp); + if (chdir((char*)tmp)) PFATAL("chdir unexpectedly fails!"); + save_req_res(pv->issue[i].req, pv->issue[i].res, 1); + if (chdir((char*)"..")) PFATAL("chdir unexpectedly fails!"); + + /* Issue samples next! */ + + if (!pv->dupe) { + u32 c; + for (c=0;cissue[i].type) break; + + if (c == i_samp_cnt) { + i_samp = ck_realloc(i_samp, (c + 1) * sizeof(struct issue_sample_desc)); + i_samp_cnt++; + i_samp[c].type = pv->issue[i].type; + } + + i_samp[c].i = ck_realloc(i_samp[c].i, (i_samp[c].sample_cnt + 1) * + sizeof(struct issue_desc*)); + i_samp[c].i[i_samp[c].sample_cnt] = &pv->issue[i]; + i_samp[c].sample_cnt++; + } + + } + + fprintf(f, "];\n"); + fclose(f); + + /* Actually crawl children. */ + + for (i=0;ichild_cnt;i++) { + u8 tmp[32]; + sprintf((char*)tmp, "c%u", i); + if (mkdir((char*)tmp, 0755)) PFATAL("Cannot create '%s'.", tmp); + if (chdir((char*)tmp)) PFATAL("chdir unexpectedly fails!"); + output_crawl_tree(pv->child[i]); + if (chdir((char*)"..")) PFATAL("chdir unexpectedly fails!"); + } + + if ((!(proc_cnt++ % 50)) || pv->type == PIVOT_ROOT) { + SAY(cLGN "\r[+] " cNOR "Writing crawl tree: %u", proc_cnt); + fflush(0); + } + +} + + +/* Writes previews of MIME types, issues. */ + +static int m_samp_qsort(const void* ptr1, const void* ptr2) { + const struct mime_sample_desc *p1 = ptr1, *p2 = ptr2; + return strcasecmp((char*)p1->det_mime, (char*)p2->det_mime); +} + +static int i_samp_qsort(const void* ptr1, const void* ptr2) { + const struct issue_sample_desc *p1 = ptr1, *p2 = ptr2; + return p2->type - p1->type; +} + + +static void output_summary_views() { + u32 i; + FILE* f; + + f = fopen("samples.js", "w"); + if (!f) PFATAL("Cannot create 'samples.js'."); + + qsort(m_samp, m_samp_cnt, sizeof(struct mime_sample_desc), m_samp_qsort); + qsort(i_samp, i_samp_cnt, sizeof(struct issue_sample_desc), i_samp_qsort); + + fprintf(f, "var mime_samples = [\n"); + + for (i=0;i MAX_SAMPLES ? MAX_SAMPLES : + m_samp[i].sample_cnt); + + sprintf((char*)tmp, "_m%u", i); + if (mkdir((char*)tmp, 0755)) PFATAL("Cannot create '%s'.", tmp); + if (chdir((char*)tmp)) PFATAL("chdir unexpectedly fails!"); + + fprintf(f, " { 'mime': '%s', 'samples': [\n", m_samp[i].det_mime); + + for (c=0;cpivot->linked, m_samp[i].res[c]->pay_len, + (c == use_samp - 1) ? " ]" : ","); + ck_free(p); + } + + fprintf(f, " }%s\n", (i == m_samp_cnt - 1) ? "" : ","); + if (chdir("..")) PFATAL("chdir unexpectedly fails!"); + } + + fprintf(f, "];\n\n"); + + fprintf(f, "var issue_samples = [\n"); + + for (i=0;i MAX_SAMPLES ? MAX_SAMPLES : + i_samp[i].sample_cnt); + + sprintf((char*)tmp, "_i%u", i); + if (mkdir((char*)tmp, 0755)) PFATAL("Cannot create '%s'.", tmp); + if (chdir((char*)tmp)) PFATAL("chdir unexpectedly fails!"); + + fprintf(f, " { 'severity': %d, 'type': %d, 'samples': [\n", + PSEV(i_samp[i].type) - 1, i_samp[i].type); + + for (c=0;creq, 1, 0); + sprintf((char*)tmp2, "%u", c); + if (mkdir((char*)tmp2, 0755)) PFATAL("Cannot create '%s'.", tmp2); + if (chdir((char*)tmp2)) PFATAL("chdir unexpectedly fails!"); + save_req_res(i_samp[i].i[c]->req, i_samp[i].i[c]->res, 0); + if (chdir("..")) PFATAL("chdir unexpectedly fails!"); + fprintf(f, " { 'url': '%s', ", js_escape(p, 0)); + fprintf(f, "'extra': '%s', 'sid': '%d', 'dir': '%s/%s' }%s\n", + i_samp[i].i[c]->extra ? js_escape(i_samp[i].i[c]->extra, 0) : + (u8*)"", i_samp[i].i[c]->sid, tmp, tmp2, + (c == use_samp - 1) ? " ]" : ","); + ck_free(p); + } + + fprintf(f, " }%s\n", (i == i_samp_cnt - 1) ? "" : ","); + if (chdir("..")) PFATAL("chdir unexpectedly fails!"); + } + + fprintf(f, "];\n\n"); + fclose(f); + +} + + +/* Copies over assets to target directory. */ + +static u8* ca_out_dir; + +static int copy_asset(const struct dirent* d) { + u8 *itmp, *otmp, buf[1024]; + s32 i, o; + + if (d->d_name[0] == '.' || !strcmp(d->d_name, "COPYING")) return 0; + + itmp = ck_alloc(strlen(ASSETS_DIR) + strlen(d->d_name) + 2); + sprintf((char*)itmp, "%s/%s", ASSETS_DIR, d->d_name); + i = open((char*)itmp, O_RDONLY); + + otmp = ck_alloc(strlen((char*)ca_out_dir) + strlen(d->d_name) + 2); + sprintf((char*)otmp, "%s/%s", ca_out_dir, d->d_name); + o = open((char*)otmp, O_WRONLY | O_CREAT | O_EXCL, 0644); + + if (i >= 0 && o >= 0) { + s32 c; + while ((c = read(i, buf, 1024)) > 0) + if (write(o, buf, c) != c) break; + } + + close(i); + close(o); + + ck_free(itmp); + ck_free(otmp); + + return 0; + +} + + +static void copy_static_code(u8* out_dir) { + struct dirent** d; + ca_out_dir = out_dir; + scandir(ASSETS_DIR, &d, copy_asset, NULL); +} + + +/* Saves all pivots for use by third-party tools. */ + +static void save_pivots(FILE* f, struct pivot_desc* cur) { + + u32 i; + + if (cur->req) { + u8* url = serialize_path(cur->req, 1, 1); + + fprintf(f, "%s %s ", cur->req->method ? cur->req->method : (u8*)"GET", + js_escape(url, 0)); + + ck_free(url); + + fprintf(f, "name=%s ", js_escape(cur->name, 1)); + + switch (cur->type) { + case PIVOT_SERV: fprintf(f, "type=serv "); break; + case PIVOT_DIR: fprintf(f, "type=dir "); break; + case PIVOT_FILE: fprintf(f, "type=file "); break; + case PIVOT_PATHINFO: fprintf(f, "type=pathinfo "); break; + case PIVOT_VALUE: fprintf(f, "type=value "); break; + case PIVOT_UNKNOWN: fprintf(f, "type=unknown "); break; + case PIVOT_PARAM: fprintf(f, "type=param "); break; + default: fprintf(f, "type=??? "); + } + + switch (cur->linked) { + case 0: fprintf(f, "linked=no "); break; + case 1: fprintf(f, "linked=maybe "); break; + default: fprintf(f, "linked=yes "); + } + + /* Report items that were not linked in any way. Unless -P is used in which + case crawling is disabled and therefore this will cause many false + alerts. */ + if (!cur->linked && !no_parse && !cur->missing) + problem(PROB_HIDDEN_NODE, cur->req, cur->res, 0, cur, 0); + + /* Record pivots where different agents result in different responses */ + fprintf(f, "browsers=%d ", cur->browsers ? cur->browsers : browser_type); + + /* Print the user agent type used */ + switch (cur->browser) { + case 0: fprintf(f, "browser_used=FAST "); break; + case 1: fprintf(f, "browser_used=MSIE "); break; + case 2: fprintf(f, "browser_used=FFOX "); break; + case 4: fprintf(f, "browser_used=PHONE "); break; + default: fprintf(f, "browser_used=??? "); + } + + if (cur->res) + fprintf(f, "dup=%u %s%scode=%u len=%u notes=%u sig=0x%x\n", cur->dupe, + cur->bogus_par ? "bogus " : "", + cur->missing ? "returns_404 " : "", + cur->res->code, cur->res->pay_len, + cur->issue_cnt, cur->pv_sig); + else + fprintf(f, "not_fetched\n"); + + + + } + + for (i=0;ichild_cnt;i++) save_pivots(f, cur->child[i]); + +} + + +static void save_all_pivots(void) { + FILE *f = fopen("pivots.txt", "w"); + + if (!f) PFATAL("Cannot create 'pivots.txt'."); + + save_pivots(f, &root_pivot); + + fclose(f); +} + + + +/* Writes report to index.html in the current directory. Will create + subdirectories, helper files, etc. */ + +void write_report(u8* out_dir, u64 scan_time, u32 seed) { + + SAY(cLGN "[+] " cNOR "Copying static resources...\n"); + copy_static_code(out_dir); + + if (chdir((char*)out_dir)) PFATAL("Cannot chdir to '%s'", out_dir); + + sort_annotate_pivot(&root_pivot); + SAY("\n"); + + collect_signatures(&root_pivot); + SAY("\n"); + + compute_counts(&root_pivot); + SAY("\n"); + + SAY(cLGN "[+] " cNOR "Saving pivot data for third-party tools...\n"); + save_all_pivots(); + + SAY(cLGN "[+] " cNOR "Writing scan description...\n"); + output_scan_info(scan_time, seed); + + output_crawl_tree(&root_pivot); + SAY("\n"); + + SAY(cLGN "[+] " cNOR "Generating summary views...\n"); + output_summary_views(); + + SAY(cLGN "[+] " cNOR "Report saved to '" cLBL "%s/index.html" cNOR "' [" + cLBL "0x%08x" cNOR "].\n", out_dir, seed); + +} diff -Nru skipfish-2.02b/src/report.h skipfish-2.10b/src/report.h --- skipfish-2.02b/src/report.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/report.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,43 @@ +/* + skipfish - post-processing and reporting + ---------------------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#ifndef _HAVE_REPORT_H + +#include "types.h" + +extern u8 suppress_dupes; +extern u8 *output_dir; + +/* Writes report to index.html in the current directory. Will create + subdirectories, helper files, etc. */ + +void write_report(u8* out_dir, u64 scan_time, u32 seed); + +/* Flushes payload to disk and stores the location in the pivot */ + +void flush_payload(struct http_request* req, struct http_response* res); + +/* Destroys all signatures created for pivot and issue clustering purposes. */ + +void destroy_signatures(void); + +#endif /* !_HAVE_REPORT_H */ diff -Nru skipfish-2.02b/src/same_test.c skipfish-2.10b/src/same_test.c --- skipfish-2.02b/src/same_test.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/same_test.c 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,84 @@ +/* + skipfish - same_page() test utility + ----------------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "alloc-inl.h" +#include "string-inl.h" + +#include "crawler.h" +#include "analysis.h" +#include "database.h" +#include "http_client.h" +#include "report.h" + +#ifdef DEBUG_ALLOCATOR +struct TRK_obj* TRK[ALLOC_BUCKETS]; +u32 TRK_cnt[ALLOC_BUCKETS]; +#endif /* DEBUG_ALLOCATOR */ + +#define MAX_LEN (1024*1024) + +u8 p1[MAX_LEN], p2[MAX_LEN]; + +int main(int argc, char** argv) { + static struct http_response r1, r2; + s32 l1, l2; + + l1 = read(8, p1, MAX_LEN); + l2 = read(9, p2, MAX_LEN); + + if (l1 < 0 || l2 < 0) + FATAL("Usage: ./same_test 8, + Sebastian Roschke + + Copyright 2011, 2012 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + + +#include +#include "pcre.h" + +#define _VIA_SIGNATURE_C + +#include "types.h" +#include "config.h" +#include "debug.h" +#include "alloc-inl.h" +#include "database.h" +#include "signatures.h" + +struct signature** sig_list; +u32 slist_max_cnt = 0, slist_cnt = 0; + +void dump_sig(struct signature *sig); + +/* Helper function to lookup a signature keyword. This makes it easier + switch() signature keywords for, for example, storing their values in + a struct */ + +static s32 lookup(u8* key) { + + u32 i; + for (i=0; lookuptable[i].name; i++) { + if (!strcmp((char*)key, lookuptable[i].name)) + return lookuptable[i].id; + } + return -1; +} + +/* Helper function for parse_sig which takes a string where some + characters might be escaped. It returns a copy with the \'s + removed so \"aa\" becomes "aa" */ + +static u8* unescape_str(u8* str) { + + u32 k = 0, i = 0; + u32 len = strlen((char*)str) + 1; + u8* ret = ck_alloc(len); + + for (i=0; iseverity < 0 || sig->severity > 4) { + WARN("Signature %d has an invalid severity: %d\n", sig->id, sig->severity); + ret = 1; + } + + if (!sig->content_cnt && !sig->mime) { + WARN("Signature %d has no \"content\" nor \"mime\" string\n", sig->id); + ret = 1; + } + + if (!sig->memo) { + WARN("Signature %d has no memo string\n", sig->id); + ret = 1; + } + + return ret; +} + +static u8 compile_content(struct content_struct *content) { + const char* pcre_err; + int pcre_err_offset; + + u32 options = PCRE_MULTILINE; + + if (content->type != TYPE_REGEX) + return 1; + + if (content->nocase) options |= PCRE_CASELESS; + + content->pcre_sig = pcre_compile((char*)content->match_str, options, + &pcre_err, &pcre_err_offset, 0); + + if (!content->pcre_sig) + return 1; + + /* This is extra */ + content->pcre_extra_sig = pcre_study(content->pcre_sig, 0, &pcre_err); + + return 0; + +} + +/* Look up a signature. */ +static struct signature* get_signature(u32 id) { + u32 i; + + for (i=0; iid == id) + return sig_list[i]; + } + + return NULL; +} + +/* Parses the signature string that is given as the first parameter and returns + a signature struct */ + +struct signature* parse_sig(u8* tline) { + + u8 *val, *name, *line; + u8 in_quot = 0; + u8 no = 0; + struct content_struct *lcontent = NULL; + + line = tline = ck_strdup(tline); + + struct signature* sig = ck_alloc(sizeof(struct signature)); + while (line) { + + /* Skip spaces */ + name = line; + no = 0; + + while (name && isspace(*name)) + name++; + + /* Split on the value and, for now, return NULL when there is no + value. We check for keyworks without value, like nocase. */ + + val = name; + while (*val && val++) { + if (*val == ':') { + *val = 0; + val++; + break; + } + + if (*val == ';') break; + } + + + if(!*val) { + ck_free(sig); + ck_free(tline); + return 0; + } + + /* Check if ! is present and set 'not' */ + if (*val == '!') { + no = 1; + val++; + } + + /* Move to value and check if quoted */ + if (*val && (*val == '\'' || *val == '"')) { + in_quot = *val; + val++; + } + + /* Find the end of the value string */ + line = val; + while (*line && (in_quot || *line != ';') && line++) { + if(*line == '\\') { + line++; + continue; + } + + /* End of quotation? */ + if (in_quot && *line == in_quot) { + in_quot = 0; + *line = 0; + line++; + continue; + } + } + *line = 0; + + switch (lookup(name)) { + case SIG_ID: + sig->id = atoi((char*)val); + break; + case SIG_CHK: + sig->check = atoi((char*)val); + break; + case SIG_CONTENT: + + /* If we already have a content struct, try to compile it (when + regex) and create a new content struct */ + + if (lcontent) { + + /* Compile and bail out on failure */ + if (lcontent->type == TYPE_REGEX && + compile_content(lcontent)) + FATAL("Unable to compile regex in: %s", tline); + + } + + if (sig->content_cnt > MAX_CONTENT) + FATAL("Too many content's in line: %s", tline); + + sig->content[sig->content_cnt] = ck_alloc(sizeof(struct content_struct)); + lcontent = sig->content[sig->content_cnt]; + lcontent->match_str = unescape_str(val); + lcontent->match_str_len = strlen((char*)lcontent->match_str); + lcontent->no = no; + sig->content_cnt++; + break; + case SIG_MEMO: + sig->memo = unescape_str(val); + break; + case SIG_PCRE_MATCH: + if (!lcontent) { + WARN("Found 'regex_match' before 'content', skipping.."); + break; + } + + lcontent->cap_match_str = unescape_str(val); + break; + case SIG_TYPE: + if (!lcontent) { + WARN("Found 'type' before 'content', skipping.."); + break; + } + /* 0 is plain to which we default so this only checks if the + rule wants a regex */ + if (!strcmp((char*)val, "regex")) + lcontent->type = (u8)TYPE_REGEX; + break; + case SIG_DIST: + if (!lcontent) { + WARN("Found 'distance' before 'content', skipping.."); + break; + } + lcontent->distance = atoi((char*)val); + break; + case SIG_OFFSET: + if (!lcontent) { + WARN("Found 'offset' before 'content', skipping.."); + break; + } + lcontent->offset = atoi((char*)val); + break; + case SIG_REPORT: + /* TODO(heinenn): support "never" */ + if (!strcmp((char*)val, "once")) { + sig->report = REPORT_ONCE; + } else { + sig->report = REPORT_ALWAYS; + } + break; + case SIG_DEPEND: + /* Chain to another signature */ + sig->depend = get_signature(atoi((char*)val)); + break; + + case SIG_SEV: + sig->severity = atoi((char*)val); + break; + case SIG_PROB: + sig->prob = atoi((char*)val); + break; + case SIG_DEPTH: + if (!lcontent) { + WARN("Found 'depth' before 'content', skipping.."); + break; + } + lcontent->depth = atoi((char*)val); + break; + case SIG_CASE: + if (!lcontent) { + WARN("Found 'case' before 'content', skipping.."); + break; + } + lcontent->nocase = 1; + break; + case SIG_PROTO: + if (!strcmp((char*)val, "https")) { + sig->proto = PROTO_HTTPS; + } else if (!strcmp((char*)val, "http")) { + sig->proto = PROTO_HTTP; + } else { + WARN("Unknown proto specified: %s (skipping)", val); + } + break; + case SIG_MIME: + sig->mime = unescape_str(val); + break; + case SIG_HEADER: + if (sig->header) { + FATAL("Found multiple 'header' keywords, skipping.."); + break; + } + sig->header = unescape_str(val); + break; + case SIG_CODE: + sig->rcode = atoi((char*)val); + break; + + default: + FATAL("Unknown keyword: %s", name); + } + + /* Proceed, or stop when we're at the end of the line. Since 'line' + still points to ; , we'll increase it first */ + if(line) line++; + + /* Now if we're at EOF or EOL, we'll stop */ + if (!line || (*line == '\r' || *line == '\n')) + break; + } + + ck_free(tline); + + /* Compile the last content entry */ + if (lcontent) compile_content(lcontent); + + /* Done parsing! Now validate the signature before returning it */ + if (check_signature(sig)) { + DEBUG("Skipping signature (didn't validate)\n"); + destroy_signature(sig); + return NULL; + } + + /* Dump the signature when debugging */ + +#ifdef LOG_STDERR + dump_sig(sig); +#endif /* !LOG_STDERR */ + return sig; +} + +/* Loads the signature list from file 'fname' and parses each line. Whenever a + * line starts with #include , the file will be parsed as well to make signature + * management really easy. */ + +void load_signatures(u8* fname) { + FILE* in; + u8 tmp[MAX_SIG_LEN + 1]; + u8 include[MAX_SIG_FNAME + 1]; + u32 in_cnt = 0; + u8 fmt[20]; + + struct signature *sig; + + in = fopen((char*)fname, "r"); + if (!in) { + PFATAL("Unable to open signature list '%s'", fname); + return; + } + + /* Create a signature list */ + if (!sig_list) + sig_list = ck_alloc(sizeof(struct signature*) * MAX_SIG_CNT); + + u32 sig_off = 0; + s32 tmp_off = 0; + while (fgets((char*)tmp + sig_off, MAX_SIG_LEN - sig_off, in)) { + + if (tmp[0] == '#' || tmp[0] == '\n' || tmp[0] == '\r') + continue; + + /* We concat signature lines that end with a trailing \ */ + tmp_off = strlen((char*)tmp) - 1; + while (tmp_off && isspace(tmp[tmp_off])) + tmp_off--; + + if (tmp[tmp_off] == '\\') { + sig_off = tmp_off; + continue; + } + + /* When the include directive is present, we'll follow it */ + if (!strncmp((char*)tmp, "include ", 8)) { + + /* Check the amount of files included already. This is mostly to protect + * against include loops */ + + if (in_cnt++ > MAX_SIG_INCS) + FATAL("Too many signature includes (max: %d)\n", MAX_SIG_INCS); + + sprintf((char*)fmt, "%%%u[^\x01-\x1f]", MAX_SIG_FNAME); + sscanf((char*)tmp + 8,(char*)fmt, (char*)include); + + DEBUG("- Including signature file: %s\n", include); + load_signatures(include); + continue; + } + + sig = parse_sig(tmp); + sig_off = 0; + + if(sig == NULL) + continue; + + if (slist_cnt >= MAX_SIG_CNT) + FATAL("* Signature list is too large (max = %d)\n", MAX_SIG_CNT); + + sig_list[slist_cnt++] = sig; + } + + DEBUG("*- Signatures processed: %s (total sigs %d)\n", fname, slist_cnt); + + fclose(in); +} + +/* Helper function to check if a certain signature matched. This is, + for example, useful to chain signatures. */ + +static u8 matched_sig(struct pivot_desc *pv, u32 sid) { + u32 i; + + if (!pv->issue_cnt) return 0; + + /* Will optimise this later by changing the way signature match + information is stored per pivot */ + for (i=0; iissue_cnt; i++) { + if (pv->issue[i].sid == sid) + return 1; + } + + return 0; +} + +u8 match_signatures(struct http_request *req, struct http_response *res) { + + u8 pcre_ret, matches = 0; + u8 *payload, *match = NULL; + u32 ovector[PCRE_VECTOR]; + struct pivot_desc *tpv; + u32 ccnt, pay_len, j = 0, i = 0; + + struct content_struct *content = NULL; + + for ( j = 0; j < slist_cnt; j++ ) { + + /* Check the signature is protocol specific (e.g. SSL-only) */ + if (sig_list[j]->proto && (sig_list[j]->proto != req->proto)) + continue; + + /* Check if the signature is only intended for one of the active tests. */ + if (sig_list[j]->check && (req->pivot->check_id > 0 && + req->pivot->check_id != sig_list[j]->check)) { + continue; + } + + /* Compare response code */ + if (sig_list[j]->rcode && sig_list[j]->rcode != res->code) + continue; + + /* If dependent on another signature, first check if that signature + matches. If it than we're done with this sig */ + if (sig_list[j]->depend) { + tpv = req->pivot; + + /* If report == 1 then we need to look at the host pivot */ + if (sig_list[j]->depend->report == REPORT_ONCE) + tpv = host_pivot(req->pivot); + + /* Do the check */ + if(!matched_sig(tpv, sig_list[j]->depend->id)) + continue; + } + + /* Compare the mime types */ + if (sig_list[j]->mime && res->header_mime) { + /* Skip if the mime doesn't match */ + if (strncmp((char*)res->header_mime, (char*)sig_list[j]->mime, + strlen((char*)sig_list[j]->mime))) continue; + + /* We've got a signature match with the mime is the same and no content + * string exists. This is useful for reporting interesting mime types, + * such as application/x-httpd-php-source */ + + if (!sig_list[j]->content_cnt) { + signature_problem(sig_list[j], req, res); + continue; + } + } + + + /* Nice, so here the matching will start! Unless... there are not content + * strings, or when the response is mainly binary data. */ + if (res->doc_type == 1 || !sig_list[j]->content_cnt) + continue; + + /* If this is a header signature, than this is our payload for + matching. Else, we'll take the response body */ + + if (!sig_list[j]->header) { + payload = res->payload; + pay_len = res->pay_len; + + } else { + + /* Header is the payload */ + payload = GET_HDR(sig_list[j]->header, &res->hdr); + + /* A header might very well not be present which means we can + continue with the next signature */ + if (!payload) continue; + pay_len = strlen((char*)payload); + + } + + matches = 0; + + for (i=0; pay_len > 0 && icontent_cnt; i++) { + + content = sig_list[j]->content[i]; + + /* If there is an offset, we will apply it to the current payload + pointer */ + + if (content->offset) { + if (pay_len < content->offset) break; + + payload = payload + content->offset; + } + + /* Use the specified maximum depth to search the string. If no depth + is specified, we search the entire buffer. Note that this is relative + to the beginning of the buffer _or_ the previous content match */ + + if (content->depth) + pay_len = content->depth; + + if (content->distance && pay_len > content->distance) { + payload += content->distance; + pay_len -= content->distance; + } + + match = 0; + if (content->type == TYPE_PLAIN) { + if (content->nocase) { + match = inl_findstrcase(payload, content->match_str, pay_len); + } else { + match = inl_findstr(payload, content->match_str, pay_len); + } + + if (match && !content->no) { + /* Move the match pointer to allow offset to be applied relative + to the previous content-match */ + + payload = match + content->match_str_len; + pay_len -= content->match_str_len; + matches++; + } else if(!match && content->no) { + matches++; + } else break; + + } else if(content->type == TYPE_REGEX) { + /* Lets do the pcre matching */ + pcre_ret = (pcre_exec(content->pcre_sig, content->pcre_extra_sig, + (char*)payload, pay_len, 0, 0, (int*)ovector, PCRE_VECTOR) >= 0); + + if (!content->no && pcre_ret) { + /* We care about the first match and update the match pointer + to the first byte that follows the matching string */ + + /* Check if a string was captured */ + pcre_fullinfo(content->pcre_sig, NULL, PCRE_INFO_CAPTURECOUNT, &ccnt); + + if (ccnt > 0 && content->cap_match_str) { + + /* In pcre we trust.. We only allow one string to be + captured so while we could loop over ccnt: we just grab + the first string. */ + + u32 cap_size = ovector[3] - ovector[2]; + + if (cap_size > MAX_PCRE_CSTR_SIZE) + cap_size = MAX_PCRE_CSTR_SIZE; + + u8 *pcre_cap_str = ck_alloc(cap_size + 1); + + if (pcre_copy_substring((char*)payload, (int*)ovector, 2, 1, + (char*)pcre_cap_str, cap_size)) { + /* No match? break the loop */ + if (inl_strcasestr(pcre_cap_str, content->cap_match_str)) { + ck_free(pcre_cap_str); + break; + } + } + ck_free(pcre_cap_str); + + } + + /* Move to the first byte after the match */ + payload = payload + ovector[1]; + pay_len -= (ovector[1] - ovector[0]); + /* pay_len is checked in the next match */ + + matches++; + } else if(!pcre_ret && content->no) { + matches++; + } else break; + } + } /* for i loop */ + + if (matches == sig_list[j]->content_cnt) + signature_problem(sig_list[j], req, res); + + } /* for j loop */ + + return 0; +} + + +/* Wrapper for reporting a signature problem */ +void signature_problem(struct signature *sig, + struct http_request *req, + struct http_response *res) { + +#ifdef _SIGNATURE_TEST + DEBUG("signature_problem() called for %d (%s)\n", sig->id, sig->memo); +#else + + /* Register the problem, together with the sid */ + register_problem((sig->prob ? sig->prob : sig_serv[sig->severity]), + sig->id, req, res, (sig->memo ? sig->memo : (u8*)""), + sig->report ? host_pivot(req->pivot) : req->pivot, 0); + +#endif +} + +void destroy_signature(struct signature *sig) { + u32 i; + + if (sig->memo) ck_free(sig->memo); + if (sig->mime) ck_free(sig->mime); + if (sig->header) ck_free(sig->header); + + for (i=0; icontent_cnt; i++) { + ck_free(sig->content[i]->match_str); + + if (sig->content[i]->pcre_sig) + free(sig->content[i]->pcre_sig); + if (sig->content[i]->pcre_extra_sig) + free(sig->content[i]->pcre_extra_sig); + + ck_free(sig->content[i]); + } + + ck_free(sig); + +} +void destroy_signature_lists() { + + u32 i; + for (i = 0; i < slist_cnt; i++) + destroy_signature(sig_list[i]); + + ck_free(sig_list); +} + +/* For debugging: dump a signature */ +void dump_sig(struct signature *sig) { + + u32 i; + + DEBUG("\n=== New signature loaded ===\n"); + DEBUG(" id = %d\n", sig->id); + DEBUG(" severity = %d\n", sig->severity); + DEBUG(" content # = %d\n", sig->content_cnt); + + for (i=0; icontent_cnt; i++) { + DEBUG(" %d. match_str = %s\n", i, sig->content[i]->match_str); + DEBUG(" %d. type = %s\n", i, sig->content[i]->type ? "REGEX" : "STRING"); + DEBUG(" %d. offset = %d\n", i, sig->content[i]->offset); + DEBUG(" %d. depth = %d\n", i, sig->content[i]->depth); + DEBUG(" %d. position = %d\n", i, sig->content[i]->distance); + DEBUG(" %d. nocase = %d\n", i, sig->content[i]->nocase); + DEBUG(" %d. no = %d\n", i, sig->content[i]->no); + + } + /* And now the optional fields */ + if (sig->memo) + DEBUG(" memo = %s\n", sig->memo); + if (sig->mime) + DEBUG(" mime = %s\n", sig->mime); + if (sig->rcode) + DEBUG(" code = %d\n", sig->rcode); + + DEBUG(" depend = %d\n", sig->depend ? sig->depend->id : 0); + DEBUG(" header = %s\n", sig->header ? (char*)sig->header : (char*)""); + + switch (sig->proto) { + case '0': + DEBUG(" proto = HTTP/HTTPS\n"); + break; + case PROTO_HTTP: + DEBUG(" proto = HTTP\n"); + break; + case PROTO_HTTPS: + DEBUG(" proto = HTTPS\n"); + } +} diff -Nru skipfish-2.02b/src/signatures.h skipfish-2.10b/src/signatures.h --- skipfish-2.02b/src/signatures.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/signatures.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,171 @@ + +/* + skipfish - signature matching + ---------------------------------------- + + Author: Niels Heinen + + Copyright 2011 - 2012 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#include "pcre.h" + +#ifndef _SIGNATURE_H +#define _SIGNATURE_H + +#define MAX_CONTENT 10 +#define PCRE_VECTOR 30 +#define MAX_PCRE_CSTR_SIZE 256 + +struct content_struct { + u8* match_str; /* The content string to find */ + u32 match_str_len; /* Length of the content string */ + pcre* pcre_sig; /* Regex: compiled */ + pcre_extra* pcre_extra_sig; /* Regex: extra */ + u8 *cap_match_str; /* To compare with pcre cap string */ + + u8 no; /* 1 = string should not be there */ + u8 nocase; /* 1 = case insensitive matching */ + u8 type; /* regex or static string */ + u32 depth; /* Depth of bytes to search */ + u32 distance; /* Relative distance to search */ + u32 offset; /* Search starts after offset */ +}; + +struct signature { + u32 id; /* Unique ID for documentation */ + u8* memo; /* Message displayed when found */ + u8 severity; /* Severity */ + u32 prob; /* Problem ID from analysis.h */ + u8* mime; /* Match with this mime type */ + u8* header; /* Match with this mime type */ + u32 rcode; /* Match with HTTP resp code */ + u32 content_cnt; /* Amount of contenrt structs */ + u32 check; /* The check ID */ + u8 report; /* 0 = always, 1 = once */ + u8 proto; /* 0, PROTO_HTTP or PROTO_HTTPS */ + struct signature *depend; /* Chain/depend on this sig */ + struct content_struct* content[MAX_CONTENT]; +}; + + +/* The signature matching function */ + +u8 match_signatures(struct http_request *req, struct http_response *res); + + +/* Load the passwords from a file */ + +void load_signatures(u8* fname); + +/* Destroy the wordlists and free all memory */ + +void destroy_signature_lists(void); + +/* Wrapper for reporting a signature problem */ + +void signature_problem(struct signature *sig, struct http_request *req, struct http_response *res); + + +struct signature** sig_list; /* The one and only: signature list */ + +extern u32 slist_max_cnt; /* Allocated space in the signature lists */ +u32 slist_cnt; /* Actual elements in the signature lists */ + +#define TYPE_PLAIN 0 /* Content type: static string */ +#define TYPE_REGEX 1 /* Content type: regular expression */ + +#define MAX_SIG_LEN 2048 /* Signature line length */ +#define MAX_SIG_CNT 1024 /* Max amount of signatures to load */ +#define MAX_SIG_FNAME 512 /* Maximum signature filename */ +#define MAX_SIG_INCS 64 /* Maximum files to include. */ + +#ifdef _VIA_SIGNATURE_C + +u32 sig_serv[] = { + PROB_SIG_DETECT, /* Default: info level */ + PROB_SIG_DETECT_H, /* High risk */ + PROB_SIG_DETECT_M, /* Medium risk */ + PROB_SIG_DETECT_L, /* Low risk */ + PROB_SIG_DETECT /* info risk */ +}; + + +/* Destroy an individual signature */ + +void destroy_signature(struct signature *sig); + +#define SIG_ID 1 +#define SIG_CONTENT 2 +#define SIG_MEMO 3 +#define SIG_TYPE 4 +#define SIG_SEV 5 +#define SIG_CONST 6 +#define SIG_PROB 7 +#define SIG_TAG 8 +#define SIG_MIME 9 +#define SIG_CODE 10 +#define SIG_CASE 11 +#define SIG_DEPTH 12 +#define SIG_OFFSET 13 +#define SIG_DIST 14 +#define SIG_CHK 15 +#define SIG_PROTO 16 +#define SIG_HEADER 17 +#define SIG_REPORT 18 +#define SIG_DEPEND 29 +#define SIG_PCRE_MATCH 30 + +/* The structs below are to for helping the signature parser */ + +struct sig_key { + u32 id; + const char *name; +}; + + +struct sig_key lookuptable[] = { + { SIG_ID, "id" }, + { SIG_CONTENT, "content" }, + { SIG_MEMO, "memo" }, + { SIG_TYPE, "type" }, + { SIG_SEV, "sev" }, + { SIG_PROB, "prob" }, + { SIG_TAG, "tag" }, + { SIG_MIME, "mime" }, + { SIG_CODE, "code" }, + { SIG_CASE, "nocase" }, + { SIG_DEPTH, "depth" }, + { SIG_OFFSET, "offset" }, + { SIG_PCRE_MATCH, "regex_match" }, + { SIG_DIST, "distance" }, + { SIG_CHK, "check" }, + { SIG_PROTO, "proto" }, + { SIG_HEADER, "header" }, + { SIG_REPORT, "report" }, + { SIG_DEPEND, "depend" }, + { 0, 0} +}; + +/* Specified whether and when a match should be reported */ + +#define REPORT_ALWAYS 0 +#define REPORT_ONCE 1 +#define REPORT_NEVER 2 + + +#endif /* !_VIA_SIGNATURE_C */ +#endif /* !_SIGNATURE_H */ diff -Nru skipfish-2.02b/src/skipfish.c skipfish-2.10b/src/skipfish.c --- skipfish-2.02b/src/skipfish.c 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/skipfish.c 2012-12-04 13:52:44.000000000 +0000 @@ -0,0 +1,866 @@ +/* + skipfish - main entry point + --------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "alloc-inl.h" +#include "string-inl.h" + +#include "crawler.h" +#include "checks.h" +#include "analysis.h" +#include "database.h" +#include "http_client.h" +#include "report.h" +#include "options.h" +#include "signatures.h" +#include "auth.h" + +#ifdef DEBUG_ALLOCATOR +struct TRK_obj* TRK[ALLOC_BUCKETS]; +u32 TRK_cnt[ALLOC_BUCKETS]; +#endif /* DEBUG_ALLOCATOR */ + +/* Ctrl-C handler... */ + +static u8 stop_soon, clear_screen; + +static void ctrlc_handler(int sig) { + stop_soon = 1; +} + + +/* Screen resizing handler. */ + +static void resize_handler(int sig) { + clear_screen = 1; +} + + +/* Usage info. */ + +static void usage(char* argv0) { + SAY("Usage: %s [ options ... ] -W wordlist -o output_dir start_url [ start_url2 ... ]\n\n" + + "Authentication and access options:\n\n" + + " -A user:pass - use specified HTTP authentication credentials\n" + " -F host=IP - pretend that 'host' resolves to 'IP'\n" + " -C name=val - append a custom cookie to all requests\n" + " -H name=val - append a custom HTTP header to all requests\n" + " -b (i|f|p) - use headers consistent with MSIE / Firefox / iPhone\n" +#ifdef PROXY_SUPPORT + " -J proxy - use a specified HTTP proxy server\n" +#endif /* PROXY_SUPPORT */ + " -N - do not accept any new cookies\n" + " --auth-form url - form authentication URL\n" + " --auth-user user - form authentication user\n" + " --auth-pass pass - form authentication password\n" + " --auth-verify-url - URL for in-session detection\n\n" + + "Crawl scope options:\n\n" + + " -d max_depth - maximum crawl tree depth (%u)\n" + " -c max_child - maximum children to index per node (%u)\n" + " -x max_desc - maximum descendants to index per branch (%u)\n" + " -r r_limit - max total number of requests to send (%u)\n" + " -p crawl%% - node and link crawl probability (100%%)\n" + " -q hex - repeat probabilistic scan with given seed\n" + " -I string - only follow URLs matching 'string'\n" + " -X string - exclude URLs matching 'string'\n" + " -K string - do not fuzz parameters named 'string'\n" + " -D domain - crawl cross-site links to another domain\n" + " -B domain - trust, but do not crawl, another domain\n" + " -Z - do not descend into 5xx locations\n" + " -O - do not submit any forms\n" + " -P - do not parse HTML, etc, to find new links\n\n" + + "Reporting options:\n\n" + + " -o dir - write output to specified directory (required)\n" + " -M - log warnings about mixed content / non-SSL passwords\n" + " -E - log all HTTP/1.0 / HTTP/1.1 caching intent mismatches\n" + " -U - log all external URLs and e-mails seen\n" + " -Q - completely suppress duplicate nodes in reports\n" + " -u - be quiet, disable realtime progress stats\n" + " -v - enable runtime logging (to stderr)\n\n" + + "Dictionary management options:\n\n" + + " -W wordlist - use a specified read-write wordlist (required)\n" + " -S wordlist - load a supplemental read-only wordlist\n" + " -L - do not auto-learn new keywords for the site\n" + " -Y - do not fuzz extensions in directory brute-force\n" + " -R age - purge words hit more than 'age' scans ago\n" + " -T name=val - add new form auto-fill rule\n" + " -G max_guess - maximum number of keyword guesses to keep (%d)\n\n" + " -z sigfile - load signatures from this file\n\n" + + "Performance settings:\n\n" + + " -g max_conn - max simultaneous TCP connections, global (%u)\n" + " -m host_conn - max simultaneous connections, per target IP (%u)\n" + " -f max_fail - max number of consecutive HTTP errors (%u)\n" + " -t req_tmout - total request response timeout (%u s)\n" + " -w rw_tmout - individual network I/O timeout (%u s)\n" + " -i idle_tmout - timeout on idle HTTP connections (%u s)\n" + " -s s_limit - response size limit (%u B)\n" + " -e - do not keep binary responses for reporting\n\n" + + "Other settings:\n\n" + + " -l max_req - max requests per second (%f)\n" + " -k duration - stop scanning after the given duration h:m:s\n" + " --config file - load the specified configuration file\n\n" + + "Send comments and complaints to .\n", argv0, + max_depth, max_children, max_descendants, max_requests, + MAX_GUESSES, max_connections, max_conn_host, + max_fail, resp_tmout, rw_tmout, idle_tmout, size_limit, max_requests_sec); + + exit(1); +} + + +/* Welcome screen. */ + +#ifdef SHOW_SPLASH +void splash_screen(void) { + char keybuf[8]; + u32 time_cnt = 0; + + SAY("\x1b[H\x1b[J"); + + SAY(cBRI "Welcome to " cYEL "skipfish" cBRI ". Here are some useful tips:\n\n" + + "1) To abort the scan at any time, press " cCYA "Ctrl-C" cBRI ". A partial report will be written\n" + " to the specified location. To view a list of currently scanned URLs, you can\n" + " press " cCYA "space" cBRI " at any time during the scan.\n\n" + + "2) Watch the number requests per second shown on the main screen. If this figure\n" + " drops below 100-200, the scan will likely take a very long time.\n\n" + + "3) The scanner does not auto-limit the scope of the scan; on complex sites, you\n" + " may need to specify locations to exclude, or limit brute-force steps.\n\n" + + "4) There are several new releases of the scanner every month. If you run into\n" + " trouble, check for a newer version first, let the author know next.\n\n" + + "More info: " cYEL "http://code.google.com/p/skipfish/wiki/KnownIssues\n\n" cBRI); + + if (!no_fuzz_ext && (keyword_orig_cnt * wg_extension_cnt) > 1000) { + + SAY(cLRD + + "NOTE: The scanner is currently configured for directory brute-force attacks,\n" + "and will make about " cBRI "%u" cLRD " requests per every fuzzable location. If this is\n" + "not what you wanted, stop now and consult the documentation.\n\n", + keyword_orig_cnt * wg_extension_cnt); + + } + + SAY(cLBL "Press any key to continue (or wait 60 seconds)... "); + + while (!stop_soon && fread(keybuf, 1, sizeof(keybuf), stdin) == 0 && time_cnt++ < 600) + usleep(100000); + +} +#endif /* SHOW_SPLASH */ + + +/* Load URLs from file. */ + +static void read_urls(u8* fn) { + FILE* f = fopen((char*)fn, "r"); + u8 tmp[MAX_URL_LEN]; + u32 loaded = 0; + + if (!f) FATAL("Unable to open '%s'.", fn); + + while (fgets((char*)tmp, MAX_URL_LEN, f)) { + struct http_request *req; + u8* url = tmp; + u8* ptr; + u32 l; + + while (isspace(*url)) url++; + + /* Check if we're reading a pivots.txt file and grab the URL */ + if(!strncmp((char*)url,"GET ", 4) || !strncmp((char*)url,"POST ", 4)) { + url += 4; + + /* If the server response code is a 404, than we'll skip the URL + to avoid wasting time on things that are likely not to exist. */ + if(inl_findstr(url, (u8*)"code=404", MAX_URL_LEN)) + continue; + + ptr = url; + /* Find the next space to terminate the URL */ + while(ptr && !isspace(*ptr)) ptr++; + + if(!ptr) FATAL("URL parsing failed at line: %s", tmp); + + *ptr = 0; + /* Else we expect a simple flat file with URLs */ + } else { + l = strlen((char*)url); + while (l && isspace(url[l-1])) l--; + url[l] = 0; + } + + if (*url == '#' || !*url) continue; + + req = ck_alloc(sizeof(struct http_request)); + + if (parse_url(url, req, NULL)) + FATAL("Scan target '%s' in file '%s' is not a valid absolute URL.", url, fn); + + if (!url_allowed_host(req)) + APPEND_STRING(allow_domains, num_allow_domains, + __DFL_ck_strdup(req->host)); + + if (!url_allowed(req)) + FATAL("URL '%s' in file '%s' explicitly excluded by -I / -X rules.", + url, fn); + + maybe_add_pivot(req, NULL, 2); + destroy_request(req); + loaded++; + + } + + fclose(f); + + if (!loaded) FATAL("No valid URLs found in '%s'.", fn); +} + + +/* Main entry point */ + +int main(int argc, char** argv) { + s32 opt; + u32 loop_cnt = 0, purge_age = 0, seed; + u8 sig_loaded = 0, show_once = 0, no_statistics = 0, display_mode = 0; + s32 oindex = 0; + u8 *wordlist = NULL; + u8 *sig_list_strg = NULL; + u8 *gtimeout_str = NULL; + const char *config_file = NULL; + u32 gtimeout = 0; + +#ifdef PROXY_SUPPORT + /* A bool to track whether a fake Host header is set which doesn't + work with the proxy support. */ + u8 has_fake = 0; +#endif /* PROXY_SUPPORT */ + + struct termios term; + struct timeval tv; + u64 st_time, en_time; + + signal(SIGINT, ctrlc_handler); + signal(SIGWINCH, resize_handler); + signal(SIGPIPE, SIG_IGN); + SSL_library_init(); + + /* Come up with a quasi-decent random seed. */ + + gettimeofday(&tv, NULL); + seed = tv.tv_usec ^ (tv.tv_sec << 16) ^ getpid(); + + SAY("skipfish web application scanner - version " VERSION "\n"); + + /* We either parse command-line arguments or read them from a config + file. First we check if a config file was specified and read it + content into the argc and argv pointers */ + + while ((opt = getopt_long(argc, argv, OPT_STRING, + long_options, &oindex)) >= 0 && !config_file) { + if (!opt && !strcmp("config", long_options[oindex].name )) + config_file = optarg; + } + + /* Reset the index */ + oindex = 0; + optind = 1; + + if (config_file) { + DEBUG("Reading configuration file: %s\n", config_file); + read_config_file(config_file, &argc, &argv); + } + + /* Parse the command-line flags. If a configuration file was specified, + the options loaded from it are now present in argv and will therefore + be parsed here all together with the CMD options. */ + + while ((opt = getopt_long(argc, argv, OPT_STRING, + long_options, &oindex)) >= 0) + + switch (opt) { + + case 'A': { + u8* x = (u8*)strchr(optarg, ':'); + if (!x) FATAL("Credentials must be in 'user:pass' form."); + *(x++) = 0; + auth_user = (u8*)optarg; + auth_pass = x; + auth_type = AUTH_BASIC; + break; + } + +#ifdef PROXY_SUPPORT + case 'J': { + u8* x = (u8*)strchr(optarg, ':'); + if (!x) FATAL("Proxy data must be in 'host:port' form."); + *(x++) = 0; + use_proxy = (u8*)optarg; + use_proxy_port = atoi((char*)x); + if (!use_proxy_port) FATAL("Incorrect proxy port number."); + break; + } +#endif /* PROXY_SUPPORT */ + + case 'F': { + u8* x = (u8*)strchr(optarg, '='); + u32 fake_addr; + if (!x) FATAL("Fake mappings must be in 'host=IP' form."); + *x = 0; + fake_addr = inet_addr((char*)x + 1); + if (fake_addr == (u32)-1) + FATAL("Could not parse IP address '%s'.", x + 1); + fake_host((u8*)optarg, fake_addr); +#ifdef PROXY_SUPPORT + has_fake = 1; +#endif /* PROXY_SUPPORT */ + break; + } + + case 'H': { + u8* x = (u8*)strchr(optarg, '='); + if (!x) FATAL("Extra headers must be in 'name=value' form."); + *x = 0; + if (!strcasecmp(optarg, "Cookie")) + FATAL("Do not use -H to set cookies (try -C instead)."); + SET_HDR((u8*)optarg, x + 1, &global_http_par); + break; + } + + case 'C': { + u8* x = (u8*)strchr(optarg, '='); + if (!x) FATAL("Cookies must be in 'name=value' form."); + if (strchr(optarg, ';')) + FATAL("Split multiple cookies into separate -C options."); + *x = 0; + SET_CK((u8*)optarg, x + 1, &global_http_par); + break; + } + + case 'D': + if (*optarg == '*') optarg++; + APPEND_STRING(allow_domains, num_allow_domains, optarg); + break; + + case 'K': + APPEND_STRING(skip_params, num_skip_params, optarg); + break; + + case 'B': + if (*optarg == '*') optarg++; + APPEND_STRING(trust_domains, num_trust_domains, optarg); + break; + + case 'I': + if (*optarg == '*') optarg++; + APPEND_STRING(allow_urls, num_allow_urls, optarg); + break; + + case 'X': + if (*optarg == '*') optarg++; + APPEND_STRING(deny_urls, num_deny_urls, optarg); + break; + + case 'T': { + u8* x = (u8*)strchr(optarg, '='); + if (!x) FATAL("Rules must be in 'name=value' form."); + *x = 0; + add_form_hint((u8*)optarg, x + 1); + break; + } + + case 'N': + ignore_cookies = 1; + break; + + case 'Y': + no_fuzz_ext = 1; + break; + + case 'q': + if (sscanf(optarg, "0x%08x", &seed) != 1) + FATAL("Invalid seed format."); + srandom(seed); + break; + + case 'Q': + suppress_dupes = 1; + break; + + case 'P': + no_parse = 1; + break; + + case 'M': + warn_mixed = 1; + break; + + case 'U': + log_ext_urls = 1; + break; + + case 'L': + dont_add_words = 1; + break; + + case 'E': + pedantic_cache = 1; + break; + + case 'O': + no_forms = 1; + break; + + case 'R': + purge_age = atoi(optarg); + if (purge_age < 3) FATAL("Purge age invalid or too low (min 3)."); + break; + + case 'd': + max_depth = atoi(optarg); + if (max_depth < 2) FATAL("Invalid value '%s'.", optarg); + break; + + case 'c': + max_children = atoi(optarg); + if (!max_children) FATAL("Invalid value '%s'.", optarg); + break; + + case 'x': + max_descendants = atoi(optarg); + if (!max_descendants) FATAL("Invalid value '%s'.", optarg); + break; + + case 'p': + crawl_prob = atoi(optarg); + if (!crawl_prob) FATAL("Invalid value '%s'.", optarg); + break; + + case 'W': + if (wordlist) + FATAL("Only one -W parameter permitted (use -S to load supplemental dictionaries)."); + + if (!strcmp(optarg, "-")) wordlist = (u8*)"/dev/null"; + else wordlist = (u8*)optarg; + + break; + + case 'S': + load_keywords((u8*)optarg, 1, 0); + break; + + case 'z': + load_signatures((u8*)optarg); + sig_loaded = 1; + break; + + case 'b': + if (optarg[0] == 'i') browser_type = BROWSER_MSIE; else + if (optarg[0] == 'f') browser_type = BROWSER_FFOX; else + if (optarg[0] == 'p') browser_type = BROWSER_PHONE; else + usage(argv[0]); + break; + + case 'g': + max_connections = atoi(optarg); + if (!max_connections) FATAL("Invalid value '%s'.", optarg); + break; + + case 'm': + max_conn_host = atoi(optarg); + if (!max_conn_host) FATAL("Invalid value '%s'.", optarg); + break; + + case 'G': + max_guesses = atoi(optarg); + if (!max_guesses) FATAL("Invalid value '%s'.", optarg); + break; + + case 'r': + max_requests = atoi(optarg); + if (!max_requests) FATAL("Invalid value '%s'.", optarg); + break; + + case 'l': + max_requests_sec = atof(optarg); + if (!max_requests_sec) FATAL("Invalid value '%s'.", optarg); + break; + + case 'f': + max_fail = atoi(optarg); + if (!max_fail) FATAL("Invalid value '%s'.", optarg); + break; + + case 't': + resp_tmout = atoi(optarg); + if (!resp_tmout) FATAL("Invalid value '%s'.", optarg); + break; + + case 'w': + rw_tmout = atoi(optarg); + if (!rw_tmout) FATAL("Invalid value '%s'.", optarg); + break; + + case 'i': + idle_tmout = atoi(optarg); + if (!idle_tmout) FATAL("Invalid value '%s'.", optarg); + break; + + case 's': + size_limit = atoi(optarg); + if (!size_limit) FATAL("Invalid value '%s'.", optarg); + break; + + case 'o': + if (output_dir) FATAL("Multiple -o options not allowed."); + output_dir = (u8*)optarg; + + rmdir(optarg); + + if (mkdir(optarg, 0755)) + PFATAL("Unable to create '%s'.", output_dir); + + break; + + case 'u': + no_statistics = 1; + break; + + case 'v': + verbosity++; + break; + + + case 'e': + delete_bin = 1; + break; + + case 'k': + if (gtimeout_str) FATAL("Multiple -k options not allowed."); + gtimeout_str = (u8*)optarg; + break; + + case 'Z': + no_500_dir = 1; + break; + + case 0: + if (!strcmp("checks", long_options[oindex].name )) { + display_injection_checks(); + } else if (!strcmp("checks-toggle", long_options[oindex].name )) { + toggle_injection_checks((u8*)optarg, 1, 1); + } else if (!strcmp("no-injection-tests", long_options[oindex].name )) { + no_checks = 1; + } else if(!strcmp("flush-to-disk", long_options[oindex].name )) { + flush_pivot_data = 1; + } else if (!strcmp("signatures", long_options[oindex].name )) { + load_signatures((u8*)optarg); + } else if(!strcmp("fast", long_options[oindex].name )) { + toggle_injection_checks((u8*)"2,4,6,15,16,17", 0, 0); + } else if (!strcmp("auth-form", long_options[oindex].name )) { + auth_form = (u8*)optarg; + auth_type = AUTH_FORM; + } else if (!strcmp("auth-user", long_options[oindex].name )) { + auth_user = (u8*)optarg; + } else if (!strcmp("auth-pass", long_options[oindex].name )) { + auth_pass = (u8*)optarg; + } else if (!strcmp("auth-pass-field", long_options[oindex].name )) { + auth_pass_field = (u8*)optarg; + } else if (!strcmp("auth-user-field", long_options[oindex].name )) { + auth_user_field = (u8*)optarg; + } else if (!strcmp("auth-form-target", long_options[oindex].name )) { + auth_form_target = (u8*)optarg; + } else if (!strcmp("auth-verify-url", long_options[oindex].name )) { + auth_verify_url = (u8*)optarg; + } + + break; + + default: + usage(argv[0]); + + } + +#ifdef PROXY_SUPPORT + if (has_fake && use_proxy) + FATAL("-F and -J should not be used together."); +#endif /* PROXY_SUPPORT */ + + if (access(ASSETS_DIR "/index.html", R_OK)) + PFATAL("Unable to access '%s/index.html' - wrong directory?", ASSETS_DIR); + + srandom(seed); + + if (optind == argc) + FATAL("Scan target not specified (try -h for help)."); + + if (!output_dir) + FATAL("Output directory not specified (try -h for help)."); + + if(verbosity && !no_statistics && isatty(2)) + FATAL("Please use -v in combination with the -u flag or, " + "run skipfish while redirecting stderr to a file. "); + + + if (resp_tmout < rw_tmout) + resp_tmout = rw_tmout; + + if (max_connections < max_conn_host) + max_connections = max_conn_host; + + /* Parse the timeout string - format h:m:s */ + if (gtimeout_str) { + int i = 0; + int m[3] = { 3600, 60, 1 }; + + u8* tok = (u8*)strtok((char*)gtimeout_str, ":"); + + while(tok && i <= 2) { + gtimeout += atoi((char*)tok) * m[i]; + tok = (u8*)strtok(NULL, ":"); + i++; + } + + if(!gtimeout) + FATAL("Wrong timeout format, please use h:m:s (hours, minutes, seconds)"); + DEBUG("* Scan timeout is set to %d seconds\n", gtimeout); + } + + + if (!wordlist) { + wordlist = (u8*)"/dev/null"; + DEBUG("* No wordlist specified with -W: defaulting to /dev/null\n"); + } + + /* If no signature files have been specified via command-line: load + the default file */ + if (!sig_loaded) + load_signatures((u8*)SIG_FILE); + + load_keywords(wordlist, 0, purge_age); + + /* Load the signatures list for the matching */ + if (sig_list_strg) load_signatures(sig_list_strg); + + /* Try to authenticate when the auth_user and auth_pass fields are set. */ + if (auth_type == AUTH_FORM) { + if (!auth_user || !auth_pass) + FATAL("Authentication requires a username and password."); + + /* Fire off the requests */ + authenticate(); + + while (next_from_queue()) { + usleep(1000); + } + + switch (auth_state) { + case ASTATE_DONE: + DEBUGC(L1, "*- Authentication succeeded!\n"); + break; + + default: + DEBUG("Auth state: %d\n", auth_state); + FATAL("Authentication failed (use -uv for more info)\n"); + break; + } + DEBUG("Authentication done!\n"); + } + + + /* Schedule all URLs in the command line for scanning. */ + + while (optind < argc) { + struct http_request *req; + + /* Support @ notation for reading URL lists from files. */ + + if (argv[optind][0] == '@') { + read_urls((u8*)argv[optind++] + 1); + continue; + } + + req = ck_alloc(sizeof(struct http_request)); + + if (parse_url((u8*)argv[optind], req, NULL)) + FATAL("Scan target '%s' is not a valid absolute URL.", argv[optind]); + + if (!url_allowed_host(req)) + APPEND_STRING(allow_domains, num_allow_domains, + __DFL_ck_strdup(req->host)); + + if (!url_allowed(req)) + FATAL("URL '%s' explicitly excluded by -I / -X rules.", argv[optind]); + + maybe_add_pivot(req, NULL, 2); + destroy_request(req); + + optind++; + } + + /* Char-by char stdin. */ + + tcgetattr(0, &term); + term.c_lflag &= ~ICANON; + tcsetattr(0, TCSANOW, &term); + fcntl(0, F_SETFL, O_NONBLOCK); + + gettimeofday(&tv, NULL); + st_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; + +#ifdef SHOW_SPLASH + if (!no_statistics) splash_screen(); +#endif /* SHOW_SPLASH */ + + if (!no_statistics) SAY("\x1b[H\x1b[J"); + else SAY(cLGN "[*] " cBRI "Scan in progress, please stay tuned...\n"); + + u64 refresh_time = 0; + + /* Enter the crawler loop */ + while ((next_from_queue() && !stop_soon) || (!show_once++)) { + + u8 keybuf[8]; + + u64 end_time; + u64 run_time; + struct timeval tv_tmp; + + gettimeofday(&tv_tmp, NULL); + end_time = tv_tmp.tv_sec * 1000LL + tv_tmp.tv_usec / 1000; + + run_time = end_time - st_time; + if (gtimeout > 0 && run_time && run_time/1000 > gtimeout) { + DEBUG("* Stopping scan due to timeout\n"); + stop_soon = 1; + } + + req_sec = (req_count - queue_cur / 1.15) * 1000 / (run_time + 1); + + if (no_statistics || ((loop_cnt++ % 100) && !show_once && idle == 0)) + continue; + + if (end_time > refresh_time) { + refresh_time = (end_time + 10); + + if (clear_screen) { + SAY("\x1b[H\x1b[2J"); + clear_screen = 0; + } + + SAY(cYEL "\x1b[H" + "skipfish version " VERSION " by lcamtuf@google.com\n\n" + cBRI " -" cPIN " %s " cBRI "-\n\n" cNOR, + allow_domains[0]); + + + if (!display_mode) { + http_stats(st_time); + SAY("\n"); + database_stats(); + } else { + http_req_list(); + } + + SAY(" \r"); + } + + if (fread(keybuf, 1, sizeof(keybuf), stdin) > 0) { + display_mode ^= 1; + clear_screen = 1; + } + } + + gettimeofday(&tv, NULL); + en_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000; + + SAY("\n"); + + if (stop_soon) + SAY(cYEL "[!] " cBRI "Scan aborted by user, bailing out!" cNOR "\n"); + + term.c_lflag |= ICANON; + tcsetattr(0, TCSANOW, &term); + fcntl(0, F_SETFL, O_SYNC); + + save_keywords((u8*)wordlist); + + write_report(output_dir, en_time - st_time, seed); + +#ifdef LOG_STDERR + SAY("\n== PIVOT DEBUG ==\n"); + dump_pivots(0, 0); + SAY("\n== END OF DUMP ==\n\n"); +#endif /* LOG_STDERR */ + + SAY(cLGN "[+] " cBRI "This was a great day for science!" cRST "\n\n"); + +#ifdef DEBUG_ALLOCATOR + if (!stop_soon) { + destroy_database(); + destroy_signature_lists(); + destroy_http(); + destroy_signatures(); + destroy_config(); + __TRK_report(); + } +#endif /* DEBUG_ALLOCATOR */ + + fflush(0); + + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); + + return 0; + +} diff -Nru skipfish-2.02b/src/string-inl.h skipfish-2.10b/src/string-inl.h --- skipfish-2.02b/src/string-inl.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/string-inl.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,257 @@ +/* + + skipfish - various string manipulation helpers + ---------------------------------------------- + + Some modern operating systems still ship with no strcasestr() or memmem() + implementations in place, for reasons beyond comprehension. This file + includes a simplified version of these routines, copied from NetBSD, plus + several minor, custom string manipulation macros and inline functions. + + The original NetBSD code is licensed under a BSD license, as follows: + + Copyright (c) 1990, 1993 + The Regents of the University of California. All rights reserved. + + This code is derived from software contributed to Berkeley by + Chris Torek. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + */ + +#ifndef _HAVE_STRING_INL_H +#define _HAVE_STRING_INL_H + +#include +#include + +#include "types.h" + + +/* Macros for easy string prefix matching */ + +#define prefix(_long, _short) \ + strncmp((const char*)(_long), (const char*)(_short), \ + strlen((const char*)(_short))) + +#define case_prefix(_long, _short) \ + strncasecmp((const char*)(_long), (const char*)(_short), \ + strlen((const char*)(_short))) + +/* Appends a string to a dynamic array by first extending it. */ + +#define APPEND_STRING(_ptr, _cnt, _val) do { \ + (_ptr) = ck_realloc(_ptr, ((_cnt) + 1) * sizeof(u8*)); \ + (_ptr)[_cnt] = (u8*)(_val); \ + (_cnt)++; \ + } while (0) + +/* Modified NetBSD strcasestr() implementation (rolling strncasecmp). */ + +static inline u8* inl_strcasestr(const u8* haystack, const u8* needle) { + register u8 c, sc; + register u32 len; + + if (!haystack || !needle) return 0; + + if ((c = *needle++)) { + + c = tolower(c); + len = strlen((char*)needle); + + do { + do { + if (!(sc = *haystack++)) return 0; + } while (tolower(sc) != c); + } while (strncasecmp((char*)haystack, (char*)needle, len)); + + haystack--; + + } + + return (u8*)haystack; + +} + +/* Modified NetBSD memmem() implementation (rolling memcmp). */ + +static inline void* inl_memmem(const void* haystack, u32 h_len, + const void* needle, u32 n_len) { + register u8* sp = (u8*)haystack; + register u8* pp = (u8*)needle; + register u8* eos = sp + h_len - n_len; + + if (!(haystack && needle && h_len && n_len)) return 0; + + while (sp <= eos) { + if (*sp == *pp) + if (memcmp(sp, pp, n_len) == 0) return sp; + sp++; + } + + return 0; + +} + + +/* Distance-limited strstr. */ + +static inline u8* inl_findstr(const u8* haystack, const u8* needle, u32 max_len) { + register u8 c, sc; + register u32 len; + + if (!haystack || !needle) return 0; + max_len++; + + if ((c = *needle++)) { + + len = strlen((char*)needle); + + do { + do { + if (!(sc = *haystack++) || !max_len--) return 0; + } while (sc != c); + } while (strncmp((char*)haystack, (char*)needle, len)); + + haystack--; + + } + + return (u8*)haystack; + +} + +/* Distance-limited and case-insensitive strstr. */ + +static inline u8* inl_findstrcase(const u8* haystack, const u8* needle, u32 max_len) { + register u8 c, sc; + register u32 len; + + if (!haystack || !needle) return 0; + max_len++; + + if ((c = *needle++)) { + + len = strlen((char*)needle); + + do { + do { + if (!(sc = *haystack++) || !max_len--) return 0; + } while (tolower(sc) != c); + } while (strncasecmp((char*)haystack, (char*)needle, len)); + + haystack--; + + } + + return (u8*)haystack; + +} + + + + + +/* String manipulation macros for operating on a dynamic buffer. */ + +#define NEW_STR(_buf_ptr, _buf_len) do { \ + (_buf_ptr) = ck_alloc(1024); \ + (_buf_len) = 0; \ + } while (0) + +#define ADD_STR_DATA(_buf_ptr, _buf_len, _str) do { \ + u32 _sl = strlen((char*)_str); \ + if ((_buf_len) + (_sl) + 1 > ALLOC_S(_buf_ptr)) { \ + u32 _nsiz = ((_buf_len) + _sl + 1024) >> 10 << 10; \ + (_buf_ptr) = ck_realloc(_buf_ptr, _nsiz); \ + } \ + memcpy((_buf_ptr) + (_buf_len), _str, _sl + 1); \ + (_buf_len) += _sl; \ + } while (0) + +#define TRIM_STR(_buf_ptr, _buf_len) do { \ + (_buf_ptr) = ck_realloc(_buf_ptr, _buf_len + 1); \ + (_buf_ptr)[_buf_len] = 0; \ + } while (0) + + +/* Simple base64 encoder */ + +static inline u8* b64_encode(u8* str, u32 len) { + + const u8 b64[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + u8 *ret, *cur; + + ret = cur = ck_alloc((len + 3) * 4 / 3 + 1); + + while (len > 0) { + + if (len >= 3) { + u32 comp = (str[0] << 16) | (str[1] << 8) | str[2]; + + *(cur++) = b64[comp >> 18]; + *(cur++) = b64[(comp >> 12) & 0x3F]; + *(cur++) = b64[(comp >> 6) & 0x3F]; + *(cur++) = b64[comp & 0x3F]; + + len -= 3; + str += 3; + + } else if (len == 2) { + u32 comp = (str[0] << 16) | (str[1] << 8); + + *(cur++) = b64[comp >> 18]; + *(cur++) = b64[(comp >> 12) & 0x3F]; + *(cur++) = b64[(comp >> 6) & 0x3D]; + *(cur++) = '='; + + len -= 2; + str += 2; + + } else { + u32 comp = (str[0] << 16);; + + *(cur++) = b64[comp >> 18]; + *(cur++) = b64[(comp >> 12) & 0x3F]; + *(cur++) = '='; + *(cur++) = '='; + + len--; + str++; + + } + + } + + *cur = 0; + return ret; + +} + +#endif /* !_HAVE_STRING_INL_H */ diff -Nru skipfish-2.02b/src/types.h skipfish-2.10b/src/types.h --- skipfish-2.02b/src/types.h 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/src/types.h 2012-12-04 13:27:53.000000000 +0000 @@ -0,0 +1,48 @@ +/* + skipfish - type definitions + --------------------------- + + Author: Michal Zalewski + + Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + */ + +#ifndef _HAVE_TYPES_H +#define _HAVE_TYPES_H + +#include + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +typedef int8_t s8; +typedef int16_t s16; +typedef int32_t s32; +typedef int64_t s64; + +/* PRNG wrapper, of no better place to put it. */ + +#define R(_ceil) ((u32)(random() % (_ceil))) + +#ifndef MIN +# define MIN(_a,_b) ((_a) > (_b) ? (_b) : (_a)) +# define MAX(_a,_b) ((_a) > (_b) ? (_a) : (_b)) +#endif /* !MIN */ + + +#endif /* ! _HAVE_TYPES_H */ diff -Nru skipfish-2.02b/string-inl.h skipfish-2.10b/string-inl.h --- skipfish-2.02b/string-inl.h 2011-07-02 08:54:55.000000000 +0000 +++ skipfish-2.10b/string-inl.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,221 +0,0 @@ -/* - - skipfish - various string manipulation helpers - ---------------------------------------------- - - Some modern operating systems still ship with no strcasestr() or memmem() - implementations in place, for reasons beyond comprehension. This file - includes a simplified version of these routines, copied from NetBSD, plus - several minor, custom string manipulation macros and inline functions. - - The original NetBSD code is licensed under a BSD license, as follows: - - Copyright (c) 1990, 1993 - The Regents of the University of California. All rights reserved. - - This code is derived from software contributed to Berkeley by - Chris Torek. - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. Neither the name of the University nor the names of its contributors - may be used to endorse or promote products derived from this software - without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - SUCH DAMAGE. - - */ - -#ifndef _HAVE_STRING_INL_H -#define _HAVE_STRING_INL_H - -#include -#include - -#include "types.h" - - -/* Macros for easy string prefix matching */ - -#define prefix(_long, _short) \ - strncmp((char*)(_long), (char*)(_short), strlen((char*)(_short))) - -#define case_prefix(_long, _short) \ - strncasecmp((char*)(_long), (char*)(_short), strlen((char*)(_short))) - - -/* Modified NetBSD strcasestr() implementation (rolling strncasecmp). */ - -static inline u8* inl_strcasestr(const u8* haystack, const u8* needle) { - register u8 c, sc; - register u32 len; - - if (!haystack || !needle) return 0; - - if ((c = *needle++)) { - - c = tolower(c); - len = strlen((char*)needle); - - do { - do { - if (!(sc = *haystack++)) return 0; - } while (tolower(sc) != c); - } while (strncasecmp((char*)haystack, (char*)needle, len)); - - haystack--; - - } - - return (u8*)haystack; - -} - - -/* Modified NetBSD memmem() implementation (rolling memcmp). */ - -static inline void* inl_memmem(const void* haystack, u32 h_len, - const void* needle, u32 n_len) { - register u8* sp = (u8*)haystack; - register u8* pp = (u8*)needle; - register u8* eos = sp + h_len - n_len; - - if (!(haystack && needle && h_len && n_len)) return 0; - - while (sp <= eos) { - if (*sp == *pp) - if (memcmp(sp, pp, n_len) == 0) return sp; - sp++; - } - - return 0; - -} - - -/* Distance-limited strstr. */ - -static inline u8* inl_findstr(const u8* haystack, const u8* needle, u32 max_len) { - register u8 c, sc; - register u32 len; - - if (!haystack || !needle) return 0; - max_len++; - - if ((c = *needle++)) { - - len = strlen((char*)needle); - - do { - do { - if (!(sc = *haystack++) || !max_len--) return 0; - } while (sc != c); - } while (strncmp((char*)haystack, (char*)needle, len)); - - haystack--; - - } - - return (u8*)haystack; - -} - - - - -/* String manipulation macros for operating on a dynamic buffer. */ - -#define NEW_STR(_buf_ptr, _buf_len) do { \ - (_buf_ptr) = ck_alloc(1024); \ - (_buf_len) = 0; \ - } while (0) - -#define ADD_STR_DATA(_buf_ptr, _buf_len, _str) do { \ - u32 _sl = strlen((char*)_str); \ - if ((_buf_len) + (_sl) + 1 > ALLOC_S(_buf_ptr)) { \ - u32 _nsiz = ((_buf_len) + _sl + 1024) >> 10 << 10; \ - (_buf_ptr) = ck_realloc(_buf_ptr, _nsiz); \ - } \ - memcpy((_buf_ptr) + (_buf_len), _str, _sl + 1); \ - (_buf_len) += _sl; \ - } while (0) - -#define TRIM_STR(_buf_ptr, _buf_len) do { \ - (_buf_ptr) = ck_realloc(_buf_ptr, _buf_len + 1); \ - (_buf_ptr)[_buf_len] = 0; \ - } while (0) - - -/* Simple base64 encoder */ - -static inline u8* b64_encode(u8* str, u32 len) { - - const u8 b64[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - u8 *ret, *cur; - - ret = cur = ck_alloc((len + 3) * 4 / 3 + 1); - - while (len > 0) { - - if (len >= 3) { - u32 comp = (str[0] << 16) | (str[1] << 8) | str[2]; - - *(cur++) = b64[comp >> 18]; - *(cur++) = b64[(comp >> 12) & 0x3F]; - *(cur++) = b64[(comp >> 6) & 0x3F]; - *(cur++) = b64[comp & 0x3F]; - - len -= 3; - str += 3; - - } else if (len == 2) { - u32 comp = (str[0] << 16) | (str[1] << 8); - - *(cur++) = b64[comp >> 18]; - *(cur++) = b64[(comp >> 12) & 0x3F]; - *(cur++) = b64[(comp >> 6) & 0x3D]; - *(cur++) = '='; - - len -= 2; - str += 2; - - } else { - u32 comp = (str[0] << 16);; - - *(cur++) = b64[comp >> 18]; - *(cur++) = b64[(comp >> 12) & 0x3F]; - *(cur++) = '='; - *(cur++) = '='; - - len--; - str++; - - } - - } - - *cur = 0; - return ret; - -} - -#endif /* !_HAVE_STRING_INL_H */ diff -Nru skipfish-2.02b/tools/sfscandiff skipfish-2.10b/tools/sfscandiff --- skipfish-2.02b/tools/sfscandiff 1970-01-01 00:00:00.000000000 +0000 +++ skipfish-2.10b/tools/sfscandiff 2012-12-04 13:27:54.000000000 +0000 @@ -0,0 +1,107 @@ +#!/bin/bash + +echo "sfscandiff - skipfish scan result comparator (lcamtuf@google.com)" 1>&2 + +if [ ! "$#" = "2" ]; then + echo "Usage: $0 /path/to/old/scan/ /path/to/new/scan/" 1>&2 + exit 1 +fi + +if [ ! -s "$1/summary.js" ]; then + echo "ERROR: First parameter does not point to a valid skipfish scan directory." 1>&2 + exit 1 +fi + +if [ ! -s "$2/summary.js" ]; then + echo "ERROR: Second parameter does not point to a valid skipfish scan directory." 1>&2 + exit 1 +fi + +OLD_SCAN="$1" +NEW_SCAN="$2" + +# Takes two parameters: old scan subdir and new scan subdir + +function check_dir { + + # echo "Comparing: old=[$1] new=[$2]..." + + echo "0" >"$2/.diff_cnt" + + echo "var diff_data = {" >"$2/diff_data.js" + + grep "'dir':" "$2/child_index.js" | awk -F "'dir': " '{print $2}' | \ + sed "s/,.*'sig'://" | sed "s/[,}]*$//" |sed "s/'//g" | \ + while read -r dir sig; do + + # echo " Checking dir=[$dir] sig=[$sig]" + + # Find matching child node first. + + MATCH_DIR=`grep -E "'sig': $sig[, ]" "$1/child_index.js" 2>/dev/null | \ + awk -F "'dir': " '{print $2}' | cut -d"'" -f2 | head -1` + + test "$MATCH_DIR" = "" && MATCH_DIR="not_found" + + # Recurse into children first, to get an accurate count of differences + # for all descendants. + + check_dir "$1/$MATCH_DIR" "$2/$dir" + + # Read difference count from descendands. If node does not appear in + # old scan, add 1 to the count. Store count. + + DIFF_CNT=`cat "$2/$dir/.diff_cnt" 2>/dev/null` + test "$DIFF_CNT" = "" && DIFF_CNT=0 + + test "$MATCH_DIR" = "not_found" && DIFF_CNT=$[DIFF_CNT+1] + + echo " '$dir': $DIFF_CNT," >>"$2/diff_data.js" + + # Update total count for parent node ($2) + + TOTAL_DIFF_CNT=`cat "$2/.diff_cnt" 2>/dev/null` + TOTAL_DIFF_CNT=$[TOTAL_DIFF_CNT+DIFF_CNT] + echo "$TOTAL_DIFF_CNT" >"$2/.diff_cnt" + + done + + # Now, for every issue, see if a matching issue appears in old scan. + # If not, add it to diff_data. + + grep "'severity':" "$2/issue_index.js" | while read -r line; do + + LOOK_FOR=`echo "$line" | awk -F"'fetched':" '{print $1}'` + ISSUE_DIR=`echo "$line" | awk -F"'dir':" '{print $2}'|cut -d"'" -f2` + + # echo " Checking issue=[$ISSUE_DIR]" + + if ! grep -qF "$LOOK_FOR" "$1/issue_index.js" 2>/dev/null; then + echo " '$ISSUE_DIR': 1," >>"$2/diff_data.js" + fi + + done + + echo " '_eof': 0" >>"$2/diff_data.js" + echo "};" >>"$2/diff_data.js" + +} + +echo -n "Finding new results in $NEW_SCAN... " + +check_dir "$OLD_SCAN" "$NEW_SCAN" + +TOTAL=`cat "$NEW_SCAN/.diff_cnt"` + +if [ "$TOTAL" = "0" ]; then + echo "no new findings." +elif [ "$TOTAL" = "1" ]; then + echo "one new or modified node found." +else + echo "$TOTAL new or modified nodes found." +fi + +grep -qF "var diff_mode" "$NEW_SCAN/summary.js" || + echo "var diff_mode = true;" >>"$NEW_SCAN/summary.js" + +exit 0 diff -Nru skipfish-2.02b/types.h skipfish-2.10b/types.h --- skipfish-2.02b/types.h 2011-07-02 06:35:05.000000000 +0000 +++ skipfish-2.10b/types.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* - skipfish - type definitions - --------------------------- - - Author: Michal Zalewski - - Copyright 2009, 2010, 2011 by Google Inc. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - */ - -#ifndef _HAVE_TYPES_H -#define _HAVE_TYPES_H - -#include - -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; - -typedef int8_t s8; -typedef int16_t s16; -typedef int32_t s32; -typedef int64_t s64; - -/* PRNG wrapper, of no better place to put it. */ - -#define R(_ceil) ((u32)(random() % (_ceil))) - -#endif /* ! _HAVE_TYPES_H */