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:
Steve Pinkham 2011-08-09 16:03:29 -04:00
parent e7485cd346
commit b199943c9d
10 changed files with 207 additions and 97 deletions

View File

@ -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:
--------------

View File

@ -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
View File

@ -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.

View File

@ -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;
}

View File

@ -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"

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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) {