1.94b: Proxy support and bugfixes
- Proxy support! Currently only works for HTTP, put behind #ifdef PROXY_SUPPORT. - Change to prefix() and change_prefix() macros to limit the risk of bugs.
This commit is contained in:
parent
e7485cd346
commit
b199943c9d
|
@ -1,3 +1,11 @@
|
|||
Version 1.94b:
|
||||
--------------
|
||||
|
||||
- Proxy support! Currently only works for HTTP, put behind #ifdef
|
||||
PROXY_SUPPORT.
|
||||
|
||||
- Change to prefix() and change_prefix() macros to limit the risk of bugs.
|
||||
|
||||
Version 1.93b:
|
||||
--------------
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -20,7 +20,7 @@
|
|||
#
|
||||
|
||||
PROGNAME = skipfish
|
||||
VERSION = 1.93b
|
||||
VERSION = 1.94b
|
||||
|
||||
OBJFILES = http_client.c database.c crawler.c analysis.c report.c
|
||||
INCFILES = alloc-inl.h string-inl.h debug.h types.h http_client.h \
|
||||
|
|
26
README
26
README
|
@ -475,24 +475,24 @@ Below is a list of features currently missing in skipfish. If you wish to
|
|||
improve the tool by contributing code in one of these areas, please let me
|
||||
know:
|
||||
|
||||
* Buffer overflow checks: after careful consideration, I suspect there is
|
||||
no reliable way to test for buffer overflows remotely. Much like the actual
|
||||
* Buffer overflow checks: after careful consideration, I suspect there is
|
||||
no reliable way to test for buffer overflows remotely. Much like the actual
|
||||
fault condition we are looking for, proper buffer size checks may also
|
||||
result in uncaught exceptions, 500 messages, etc. I would love to be proved
|
||||
wrong, though.
|
||||
|
||||
* Fully-fledged JavaScript XSS detection: several rudimentary checks are
|
||||
present in the code, but there is no proper script engine to evaluate
|
||||
expressions and DOM access built in.
|
||||
* Fully-fledged JavaScript XSS detection: several rudimentary checks are
|
||||
present in the code, but there is no proper script engine to evaluate
|
||||
expressions and DOM access built in.
|
||||
|
||||
* Variable length encoding character consumption / injection bugs: these
|
||||
* Variable length encoding character consumption / injection bugs: these
|
||||
problems seem to be largely addressed on browser level at this point, so
|
||||
they were much lower priority at the time of this writing.
|
||||
they were much lower priority at the time of this writing.
|
||||
|
||||
* Security checks and link extraction for third-party, plugin-based
|
||||
* Security checks and link extraction for third-party, plugin-based
|
||||
content (Flash, Java, PDF, etc).
|
||||
|
||||
* Password brute-force and numerical filename brute-force probes.
|
||||
* Password brute-force and numerical filename brute-force probes.
|
||||
|
||||
* Search engine integration (vhosts, starting paths).
|
||||
|
||||
|
@ -500,11 +500,11 @@ know:
|
|||
|
||||
* NTLM and digest authentication.
|
||||
|
||||
* More specific PHP tests (eval injection, RFI).
|
||||
* More specific PHP tests (eval injection, RFI).
|
||||
|
||||
* Proxy support: somewhat incompatible with performance control features
|
||||
currently employed by skipfish; but in the long run, should be provided
|
||||
as a last-resort option.
|
||||
* Proxy support: an experimental HTTP proxy support is available through
|
||||
a #define directive in config.h. Adding support for HTTPS proxying is
|
||||
more complicated, and still in the works.
|
||||
|
||||
* Scan resume option.
|
||||
|
||||
|
|
124
analysis.c
124
analysis.c
|
@ -80,7 +80,7 @@ void pivot_header_checks(struct http_request* req,
|
|||
for (i=0;i<res->hdr.c;i++) {
|
||||
|
||||
if (res->hdr.t[i] != PARAM_HEADER ||
|
||||
strncasecmp((char*)res->hdr.n[i], "X-", 2)) continue;
|
||||
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);
|
||||
|
@ -96,7 +96,7 @@ void pivot_header_checks(struct http_request* req,
|
|||
for (i=0;i<RPAR(req)->res->hdr.c;i++) {
|
||||
|
||||
if (RPAR(req)->res->hdr.t[i] != PARAM_HEADER ||
|
||||
strncasecmp((char*)RPAR(req)->res->hdr.n[i], "X-", 2)) continue;
|
||||
case_prefix(RPAR(req)->res->hdr.n[i], "X-")) continue;
|
||||
|
||||
cur_hdr = GET_HDR(RPAR(req)->res->hdr.n[i], &res->hdr);
|
||||
|
||||
|
@ -122,10 +122,10 @@ static void test_add_link(u8* str, struct http_request* ref,
|
|||
|
||||
/* Don't add injected links. */
|
||||
|
||||
if (!strncasecmp((char*)str, "skipfish:", 9) ||
|
||||
!strncasecmp((char*)str, "//skipfish.invalid/", 19) ||
|
||||
if (!case_prefix(str, "skipfish:") ||
|
||||
!case_prefix(str, "//skipfish.invalid/") ||
|
||||
inl_strcasestr(str, (u8*) "/" BOGUS_FILE) ||
|
||||
!strncasecmp((char*)str, "http://skipfish.invalid/", 24)) return;
|
||||
!case_prefix(str, "http://skipfish.invalid/")) return;
|
||||
|
||||
/* Don't add links that look like they came from JS code with fragmented HTML
|
||||
snippets, etc. */
|
||||
|
@ -136,7 +136,7 @@ static void test_add_link(u8* str, struct http_request* ref,
|
|||
if ((str[0] == '\'' || str[0] == '"') && (str[1] == '+' || str[1] == ' '))
|
||||
return;
|
||||
|
||||
if (!strncasecmp((char*)str, "mailto:", 7)) {
|
||||
if (!case_prefix(str, "mailto:")) {
|
||||
|
||||
if (log_ext_urls) {
|
||||
u8* qmark = (u8*)strchr((char*)str, '?');
|
||||
|
@ -262,19 +262,19 @@ static u8* html_decode_param(u8* url, u8 also_js) {
|
|||
|
||||
if (url[i] == '&') {
|
||||
|
||||
if (!strncasecmp((char*)url + i + 1, "amp;", 4)) {
|
||||
if (!case_prefix(url + i + 1, "amp;")) {
|
||||
ret[pos++] = '&';
|
||||
i += 4;
|
||||
continue;
|
||||
} else if (!strncasecmp((char*)url + i + 1, "quot;", 5)) {
|
||||
} else if (!case_prefix(url + i + 1, "quot;")) {
|
||||
ret[pos++] = '\'';
|
||||
i += 5;
|
||||
continue;
|
||||
} else if (!strncasecmp((char*)url + i + 1, "lt;", 3)) {
|
||||
} else if (!case_prefix(url + i + 1, "lt;")) {
|
||||
ret[pos++] = '<';
|
||||
i += 3;
|
||||
continue;
|
||||
} else if (!strncasecmp((char*)url + i + 1, "gt;", 3)) {
|
||||
} else if (!case_prefix(url + i + 1, "gt;")) {
|
||||
ret[pos++] = '>';
|
||||
i += 3;
|
||||
continue;
|
||||
|
@ -398,8 +398,7 @@ static u8 maybe_xsrf(u8* token) {
|
|||
|
||||
/* Unix time is not a valid token. */
|
||||
|
||||
if (!strncasecmp((char*)token, (char*)tm_prefix, strlen((char*)tm_prefix)))
|
||||
return 0;
|
||||
if (!case_prefix(token, tm_prefix)) return 0;
|
||||
|
||||
tmp = token;
|
||||
while (*tmp && (isdigit(*tmp) || strchr("abcdef", tolower(*tmp)))) {
|
||||
|
@ -464,7 +463,7 @@ static void collect_form_data(struct http_request* req,
|
|||
cur_str++;
|
||||
*tag_end = 0;
|
||||
|
||||
if (!strncasecmp((char*)cur_str, "/form", 5)) {
|
||||
if (!case_prefix(cur_str, "/form")) {
|
||||
*tag_end = '>';
|
||||
goto final_checks;
|
||||
}
|
||||
|
@ -909,8 +908,7 @@ next_tag:
|
|||
We do not make assumptins about syntax such as /foo/, though, as
|
||||
it could very well be a regex in a JS block. */
|
||||
|
||||
if (!strncmp((char*)clean_url, "./", 2) || !strncmp((char*)clean_url,
|
||||
"../", 3)) {
|
||||
if (!prefix(clean_url, "./") || !prefix(clean_url, "../")) {
|
||||
add_link:
|
||||
test_add_link(clean_url, base ? base : req, res, 0, 0);
|
||||
goto url_done;
|
||||
|
@ -921,7 +919,7 @@ add_link:
|
|||
|
||||
while (clean_url[lead] && (isalnum(clean_url[lead]))) lead++;
|
||||
|
||||
if (lead && !strncmp((char*)clean_url + lead, "://", 3) &&
|
||||
if (lead && !prefix(clean_url + lead, "://") &&
|
||||
clean_url[lead + 3]) goto add_link;
|
||||
|
||||
/* URL CHECK 3: If the result ends with <str>.<known_ext>,
|
||||
|
@ -1047,7 +1045,7 @@ static u8 is_css(struct http_response* res) {
|
|||
|
||||
/* Skip HTML, CSS comments. */
|
||||
|
||||
if (!strncmp((char*)text, "<!--", 4)) {
|
||||
if (!prefix(text, "<!--")) {
|
||||
text += 4;
|
||||
continue;
|
||||
}
|
||||
|
@ -1072,9 +1070,9 @@ static u8 is_css(struct http_response* res) {
|
|||
|
||||
/* @import, @media, or @charset is a clear indicator of CSS. */
|
||||
|
||||
if (*text == '@' && (!strncasecmp((char*)text + 1, "import", 6) ||
|
||||
!strncasecmp((char*)text + 1, "media", 5) ||
|
||||
!strncasecmp((char*)text + 1, "charset", 7))) {
|
||||
if (*text == '@' && (!case_prefix(text + 1, "import") ||
|
||||
!case_prefix(text + 1, "media") ||
|
||||
!case_prefix(text + 1, "charset"))) {
|
||||
res->css_type = 2;
|
||||
return 1;
|
||||
}
|
||||
|
@ -1157,11 +1155,11 @@ static u8 is_javascript(struct http_response* res) {
|
|||
|
||||
/* Skip HTML, JS comments. Special case for MOTW. */
|
||||
|
||||
if (!strncmp((char*)text, "<!--", 4)) {
|
||||
if (!prefix(text, "<!--")) {
|
||||
|
||||
text += 4;
|
||||
|
||||
if (!strncmp((char*)text, " saved from url=", 16)) {
|
||||
if (!prefix(text, " saved from url=")) {
|
||||
res->js_type = 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -1194,7 +1192,7 @@ static u8 is_javascript(struct http_response* res) {
|
|||
|
||||
if (!first)
|
||||
while (json_safe[i]) {
|
||||
if (!strncasecmp((char*)text, json_safe[i], strlen(json_safe[i]))) {
|
||||
if (!case_prefix(text, json_safe[i])) {
|
||||
res->js_type = 2;
|
||||
res->json_safe = 1;
|
||||
return 1;
|
||||
|
@ -1270,20 +1268,20 @@ static void check_js_xss(struct http_request* req, struct http_response* res,
|
|||
and current string starts with //skipfishy thingees,
|
||||
complain. */
|
||||
|
||||
if ((!strncmp((char*)last_word, "innerHTML", 9) ||
|
||||
!strncmp((char*)last_word, "open", 4) ||
|
||||
!strncmp((char*)last_word, "url", 3) ||
|
||||
!strncmp((char*)last_word, "href", 4) ||
|
||||
!strncmp((char*)last_word, "write", 5)) &&
|
||||
(!strncasecmp((char*)text + 1,"//skipfish.invalid/", 19) ||
|
||||
!strncasecmp((char*)text + 1,"http://skipfish.invalid/", 24) ||
|
||||
!strncasecmp((char*)text + 1,"skipfish:", 9)))
|
||||
if ((!prefix(last_word, "innerHTML") ||
|
||||
!prefix(last_word, "open") ||
|
||||
!prefix(last_word, "url") ||
|
||||
!prefix(last_word, "href") ||
|
||||
!prefix(last_word, "write")) &&
|
||||
(!case_prefix(text + 1,"//skipfish.invalid/") ||
|
||||
!case_prefix(text + 1,"http://skipfish.invalid/") ||
|
||||
!case_prefix(text + 1,"skipfish:")))
|
||||
problem(PROB_URL_XSS, req, res,
|
||||
(u8*)"injected URL in JS/CSS code", req->pivot, 0);
|
||||
|
||||
} else if (in_quot && *text == in_quot) in_quot = 0;
|
||||
|
||||
else if (!in_quot && !strncasecmp((char*)text, "sfi", 3) &&
|
||||
else if (!in_quot && !case_prefix(text, "sfi") &&
|
||||
sscanf((char*)text, "sfi%06uv%06u", &tag_id, &scan_id) == 2) {
|
||||
struct http_request* orig = get_xss_request(tag_id, scan_id);
|
||||
|
||||
|
@ -1576,7 +1574,7 @@ void content_checks(struct http_request* req, struct http_response* res) {
|
|||
|
||||
/* Skip comments where possible. */
|
||||
|
||||
if (!strncmp((char*)tmp, "!--", 3)) {
|
||||
if (!prefix(tmp, "!--")) {
|
||||
u8* next = (u8*)strstr((char*)tmp + 3, "-->");
|
||||
if (next) {
|
||||
tmp = next + 3;
|
||||
|
@ -1644,7 +1642,7 @@ void content_checks(struct http_request* req, struct http_response* res) {
|
|||
strcasecmp((char*)tag_name, "input")) ||
|
||||
!strcasecmp((char*)param_name, "codebase")) && clean_val) {
|
||||
|
||||
if (!strncasecmp((char*)clean_val, "skipfish://", 11))
|
||||
if (!case_prefix(clean_val, "skipfish:"))
|
||||
problem(PROB_URL_XSS, req, res, tag_name, req->pivot, 0);
|
||||
|
||||
/* A bit hairy, but in essence, links to attacker-supplied
|
||||
|
@ -1652,8 +1650,8 @@ void content_checks(struct http_request* req, struct http_response* res) {
|
|||
are sorta noteworthy, depending on context; and A links
|
||||
are usually of little relevance. */
|
||||
|
||||
if (!strncasecmp((char*)clean_val, "http://skipfish.invalid/", 24) ||
|
||||
!strncasecmp((char*)clean_val, "//skipfish.invalid/", 19)) {
|
||||
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"))
|
||||
|
@ -1679,14 +1677,14 @@ void content_checks(struct http_request* req, struct http_response* res) {
|
|||
url += 4;
|
||||
if (*url == '\'' || *url == '"') { url++; semi_safe = 1; }
|
||||
|
||||
if (!strncasecmp((char*)url, "http://skipfish.invalid/", 24) ||
|
||||
!strncasecmp((char*)url, "//skipfish.invalid/", 19))
|
||||
if (!case_prefix(url, "http://skipfish.invalid/") ||
|
||||
!case_prefix(url, "//skipfish.invalid/"))
|
||||
problem(PROB_URL_REDIR, req, res, (u8*)"injected URL in META refresh",
|
||||
req->pivot, 0);
|
||||
|
||||
/* Unescaped semicolon in Refresh headers is unsafe with MSIE6. */
|
||||
|
||||
if (!strncasecmp((char*)url, "skipfish://", 11) ||
|
||||
if (!case_prefix(url, "skipfish:") ||
|
||||
(!semi_safe && strchr((char*)url, ';')))
|
||||
problem(PROB_URL_XSS, req, res, (u8*)"injected URL in META refresh",
|
||||
req->pivot, 0);
|
||||
|
@ -1705,7 +1703,7 @@ void content_checks(struct http_request* req, struct http_response* res) {
|
|||
|
||||
/* CHECK 3.3: JavaScript on*=, CSS style= parameters. */
|
||||
|
||||
if ((!strncasecmp((char*)param_name, "on", 2) ||
|
||||
if ((!case_prefix(param_name, "on") ||
|
||||
!strcasecmp((char*)param_name, "style")) && clean_val)
|
||||
check_js_xss(req, res, clean_val);
|
||||
|
||||
|
@ -1941,19 +1939,19 @@ static void detect_mime(struct http_request* req, struct http_response* res) {
|
|||
}
|
||||
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "%!PS", 4)) {
|
||||
if (!prefix(sniffbuf, "%!PS")) {
|
||||
res->sniff_mime_id = MIME_ASC_POSTSCRIPT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "{\\rtf", 5)) {
|
||||
if (!prefix(sniffbuf, "{\\rtf")) {
|
||||
res->sniff_mime_id = MIME_ASC_RTF;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Adobe PDF (may be mostly ASCII in some cases). */
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "%PDF", 4)) {
|
||||
if (!prefix(sniffbuf, "%PDF")) {
|
||||
res->sniff_mime_id = MIME_EXT_PDF;
|
||||
return;
|
||||
}
|
||||
|
@ -2058,29 +2056,29 @@ static void detect_mime(struct http_request* req, struct http_response* res) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "GIF8", 4)) {
|
||||
if (!prefix(sniffbuf, "GIF8")) {
|
||||
res->sniff_mime_id = MIME_IMG_GIF;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sniffbuf[0] == 0x89 && !strncmp((char*)sniffbuf + 1, "PNG", 3)) {
|
||||
if (sniffbuf[0] == 0x89 && !prefix(sniffbuf + 1, "PNG")) {
|
||||
res->sniff_mime_id = MIME_IMG_PNG;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "BM", 2)) {
|
||||
if (!prefix(sniffbuf, "BM")) {
|
||||
res->sniff_mime_id = MIME_IMG_BMP;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "II", 2) && sniffbuf[2] == 42 /* dec */) {
|
||||
if (!prefix(sniffbuf, "II") && sniffbuf[2] == 42 /* dec */) {
|
||||
res->sniff_mime_id = MIME_IMG_TIFF;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Next: RIFF containers (AVI, ANI, WAV). */
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "RIFF", 4)) {
|
||||
if (!prefix(sniffbuf, "RIFF")) {
|
||||
|
||||
if (sniffbuf[8] == 'A') {
|
||||
if (sniffbuf[9] == 'C')
|
||||
|
@ -2123,27 +2121,27 @@ static void detect_mime(struct http_request* req, struct http_response* res) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!strncasecmp((char*)sniffbuf, "OggS", 4)) {
|
||||
if (!prefix(sniffbuf, "OggS")) {
|
||||
res->sniff_mime_id = MIME_AV_OGG;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sniffbuf[0] == 0x28 && !strncasecmp((char*)sniffbuf + 1, "RMF", 3)) {
|
||||
if (sniffbuf[0] == 0x28 && !prefix(sniffbuf + 1, "RMF")) {
|
||||
res->sniff_mime_id = MIME_AV_RA;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sniffbuf[0] == 0x2E && !strncasecmp((char*)sniffbuf + 1, "RMF", 3)) {
|
||||
if (sniffbuf[0] == 0x2E && !prefix(sniffbuf + 1, "RMF")) {
|
||||
res->sniff_mime_id = MIME_AV_RV;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp((char*)sniffbuf + 4, "free", 4) ||
|
||||
!strncmp((char*)sniffbuf + 4, "mdat", 4) ||
|
||||
!strncmp((char*)sniffbuf + 4, "wide", 4) ||
|
||||
!strncmp((char*)sniffbuf + 4, "pnot", 4) ||
|
||||
!strncmp((char*)sniffbuf + 4, "skip", 4) ||
|
||||
!strncmp((char*)sniffbuf + 4, "moov", 4)) {
|
||||
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;
|
||||
|
@ -2151,20 +2149,20 @@ static void detect_mime(struct http_request* req, struct http_response* res) {
|
|||
|
||||
/* Flash and FLV. */
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "FLV", 3)) {
|
||||
if (!prefix(sniffbuf, "FLV")) {
|
||||
res->sniff_mime_id = MIME_AV_FLV;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "FCWS", 4) ||
|
||||
!strncmp((char*)sniffbuf, "CWS", 3)) {
|
||||
if (!prefix(sniffbuf, "FCWS") ||
|
||||
!prefix(sniffbuf, "CWS")) {
|
||||
res->sniff_mime_id = MIME_EXT_FLASH;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Adobe PDF. */
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "%PDF", 4)) {
|
||||
if (!prefix(sniffbuf, "%PDF")) {
|
||||
res->sniff_mime_id = MIME_EXT_PDF;
|
||||
return;
|
||||
}
|
||||
|
@ -2172,7 +2170,7 @@ static void detect_mime(struct http_request* req, struct http_response* res) {
|
|||
/* JAR versus ZIP. A bit tricky, because, well, they are both just
|
||||
ZIP archives. */
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "PK", 2) &&
|
||||
if (!prefix(sniffbuf, "PK") &&
|
||||
sniffbuf[2] < 6 && sniffbuf[3] < 7) {
|
||||
|
||||
if (inl_memmem(res->payload, res->pay_len, "META-INF/", 9))
|
||||
|
@ -2429,7 +2427,7 @@ static void check_for_stuff(struct http_request* req,
|
|||
|
||||
/* This should also cover most cases of Perl, Python, etc. */
|
||||
|
||||
if (!strncmp((char*)sniffbuf, "#!/", 3)) {
|
||||
if (!prefix(sniffbuf, "#!/")) {
|
||||
problem(PROB_FILE_POI, req, res, (u8*)"shell script", req->pivot, 0);
|
||||
return;
|
||||
}
|
||||
|
|
6
config.h
6
config.h
|
@ -27,6 +27,12 @@
|
|||
|
||||
#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 point. */
|
||||
|
||||
// #define PROXY_SUPPORT 1
|
||||
|
||||
/* Default paths to runtime files: */
|
||||
|
||||
#define ASSETS_DIR "assets"
|
||||
|
|
14
crawler.c
14
crawler.c
|
@ -980,12 +980,12 @@ static u8 inject_check5_callback(struct http_request* req,
|
|||
|
||||
if (val) {
|
||||
|
||||
if (!strncasecmp((char*)val, "http://skipfish.invalid/", 24) ||
|
||||
!strncasecmp((char*)val, "//skipfish.invalid/", 19))
|
||||
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 (!strncasecmp((char*)val, "skipfish://", 11))
|
||||
if (!case_prefix(val, "skipfish:"))
|
||||
problem(PROB_URL_XSS, req, res, (u8*)"injected URL in 'Location' header",
|
||||
req->pivot, 0);
|
||||
|
||||
|
@ -998,14 +998,14 @@ static u8 inject_check5_callback(struct http_request* req,
|
|||
|
||||
if (*val == '\'' || *val == '"') { val++; semi_safe++; }
|
||||
|
||||
if (!strncasecmp((char*)val, "http://skipfish.invalid/", 24) ||
|
||||
!strncasecmp((char*)val, "//skipfish.invalid/", 19))
|
||||
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 (!strncasecmp((char*)val, "skipfish://", 11) ||
|
||||
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);
|
||||
|
@ -2821,7 +2821,7 @@ static u8 dir_dict_callback(struct http_request* req,
|
|||
/* Do not add 403 responses to .ht* requests - workaround for
|
||||
Apache filtering to keep reports clean. */
|
||||
|
||||
if (lp && !strncmp((char*)lp,".ht",3))
|
||||
if (lp && !prefix(lp, ".ht"))
|
||||
i = ~req->pivot->r404_cnt;
|
||||
|
||||
/* If not 404, do response, and does not look like
|
||||
|
|
|
@ -86,6 +86,12 @@ u64 bytes_sent,
|
|||
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: */
|
||||
|
@ -187,10 +193,10 @@ u8 parse_url(u8* url, struct http_request* req, struct http_request* ref) {
|
|||
|
||||
if (maybe_proto && url[maybe_proto] == ':') {
|
||||
|
||||
if (!strncasecmp((char*)url, "http:", 5)) {
|
||||
if (!case_prefix(url, "http:")) {
|
||||
req->proto = PROTO_HTTP;
|
||||
cur += 5;
|
||||
} else if (!strncasecmp((char*)url, "https:", 6)) {
|
||||
} else if (!case_prefix(url, "https:")) {
|
||||
req->proto = PROTO_HTTPS;
|
||||
cur += 6;
|
||||
} else return 1;
|
||||
|
@ -509,12 +515,12 @@ void tokenize_path(u8* str, struct http_request* req, u8 add_slash) {
|
|||
probes. This is to avoid recursion if it actually worked in some
|
||||
way. */
|
||||
|
||||
if (!strncmp((char*)cur, "/\\.\\", 4) && (cur[4] == '/' || !cur[4])) {
|
||||
if (!prefix(cur, "/\\.\\") && (cur[4] == '/' || !cur[4])) {
|
||||
cur += 4;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!strncasecmp((char*)cur, "/%5c.%5c", 8) &&
|
||||
if (!case_prefix(cur, "/%5c.%5c") &&
|
||||
(cur[8] == '/' || !cur[8])) {
|
||||
cur += 8;
|
||||
continue;
|
||||
|
@ -758,6 +764,39 @@ u32 maybe_lookup_host(u8* name) {
|
|||
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))
|
||||
|
@ -848,6 +887,25 @@ u8* build_request_data(struct http_request* req) {
|
|||
|
||||
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);
|
||||
|
@ -927,7 +985,6 @@ u8* build_request_data(struct http_request* req) {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/* 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. */
|
||||
|
@ -1224,7 +1281,7 @@ u8 parse_response(struct http_request* req, struct http_response* res,
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (strncmp((char*)cur_line, "HTTP/1.", 7)) {
|
||||
if (prefix(cur_line, "HTTP/1.")) {
|
||||
ck_free(cur_line);
|
||||
return 2;
|
||||
}
|
||||
|
@ -1248,7 +1305,7 @@ u8 parse_response(struct http_request* req, struct http_response* res,
|
|||
|
||||
if (!cur_line[0]) break;
|
||||
|
||||
if (!strncasecmp((char*)cur_line, "Content-Length:", 15)) {
|
||||
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
|
||||
|
@ -1261,7 +1318,7 @@ u8 parse_response(struct http_request* req, struct http_response* res,
|
|||
}
|
||||
} else pay_len = -1;
|
||||
|
||||
} else if (!strncasecmp((char*)cur_line, "Transfer-Encoding:", 18)) {
|
||||
} 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. */
|
||||
|
@ -1271,7 +1328,7 @@ u8 parse_response(struct http_request* req, struct http_response* res,
|
|||
while (isspace(*x)) x++;
|
||||
if (!strcasecmp((char*)x, "chunked")) chunked = 1;
|
||||
|
||||
} else if (!strncasecmp((char*)cur_line, "Content-Encoding:", 17)) {
|
||||
} else if (!case_prefix(cur_line, "Content-Encoding:")) {
|
||||
|
||||
/* Content-Encoding is good to know, too. */
|
||||
|
||||
|
@ -1282,7 +1339,7 @@ u8 parse_response(struct http_request* req, struct http_response* res,
|
|||
if (!strcasecmp((char*)x, "deflate") || !strcasecmp((char*)x, "gzip"))
|
||||
compressed = 1;
|
||||
|
||||
} else if (!strncasecmp((char*)cur_line, "Connection:", 11)) {
|
||||
} else if (!case_prefix(cur_line, "Connection:")) {
|
||||
|
||||
u8* x = cur_line + 11;
|
||||
|
||||
|
@ -1887,7 +1944,12 @@ connect_error:
|
|||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -2053,7 +2115,6 @@ SSL_read_more:
|
|||
|
||||
c->SSL_rd_w_wr = 0;
|
||||
|
||||
|
||||
read_res = SSL_read(c->srv_ssl, c->read_buf + c->read_len,
|
||||
READ_CHUNK);
|
||||
|
||||
|
|
|
@ -405,6 +405,12 @@ 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;
|
||||
|
|
26
skipfish.c
26
skipfish.c
|
@ -77,6 +77,9 @@ static void usage(char* argv0) {
|
|||
" -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"
|
||||
|
@ -234,7 +237,8 @@ static void read_urls(u8* fn) {
|
|||
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;
|
||||
u8 dont_save_words = 0, show_once = 0, be_quiet = 0, display_mode = 0,
|
||||
has_fake = 0;
|
||||
u8 *wordlist = (u8*)DEF_WORDLIST, *output_dir = NULL;
|
||||
|
||||
struct termios term;
|
||||
|
@ -254,7 +258,7 @@ int main(int argc, char** argv) {
|
|||
SAY("skipfish version " VERSION " by <lcamtuf@google.com>\n");
|
||||
|
||||
while ((opt = getopt(argc, argv,
|
||||
"+A:F:C:H:b:Nd:c:x:r:p:I:X:D:POYQMZUEK:W:LVT:G:R:B:q:g:m:f:t:w:i:s:o:hue")) > 0)
|
||||
"+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) {
|
||||
|
||||
|
@ -268,6 +272,18 @@ int main(int argc, char** argv) {
|
|||
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;
|
||||
|
@ -277,6 +293,7 @@ int main(int argc, char** argv) {
|
|||
if (fake_addr == (u32)-1)
|
||||
FATAL("Could not parse IP address '%s'.", x + 1);
|
||||
fake_host((u8*)optarg, fake_addr);
|
||||
has_fake = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -487,6 +504,11 @@ int main(int argc, char** argv) {
|
|||
|
||||
}
|
||||
|
||||
#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);
|
||||
|
||||
|
|
|
@ -51,6 +51,15 @@
|
|||
#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) {
|
||||
|
|
Loading…
Reference in New Issue