Version 2.09b: bugfixes and policy improvements

- 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.
This commit is contained in:
Steve Pinkham 2012-09-12 17:09:00 -04:00
parent c9d5b74896
commit e48969d827
12 changed files with 76 additions and 54 deletions

View File

@ -1,3 +1,16 @@
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: Version 2.08b:
- Added Host header XSS testing. - Added Host header XSS testing.

View File

@ -20,7 +20,7 @@
# #
PROGNAME = skipfish PROGNAME = skipfish
VERSION = 2.08b VERSION = 2.09b
SRCDIR = src SRCDIR = src
SFILES = http_client.c database.c crawler.c analysis.c report.c \ SFILES = http_client.c database.c crawler.c analysis.c report.c \

View File

@ -707,7 +707,13 @@ function load_issues() {
'Fetch result: ' + i2.error + '</div>'; 'Fetch result: ' + i2.error + '</div>';
} }
if (i2.extra.length > 0) add_html += '<div class="comment">Memo: ' + H(i2.extra) + '</div>\n'; if (i2.extra.length > 0) {
add_html += '<div class="comment">Memo: ' + H(i2.extra);
if (i2.sid.length > 0 && i2.sid > 0) {
add_html += ' (sig: ' + i2.sid + ')';
}
add_html += '</div>\n';
}
} }
@ -922,9 +928,14 @@ function load_issue_summaries() {
'[ <a href="#" onclick="return show_dat(\'' + i.samples[sno].dir + '\', false)">show trace</a> ' + '[ <a href="#" onclick="return show_dat(\'' + i.samples[sno].dir + '\', false)">show trace</a> ' +
'<a href="#" onclick="return show_win(\'' + i.samples[sno].dir + '\', false)">+</a> ]</span>\n'; '<a href="#" onclick="return show_win(\'' + i.samples[sno].dir + '\', false)">+</a> ]</span>\n';
if (i.samples[sno].extra && i.samples[sno].extra.length > 0) if (i.samples[sno].extra && i.samples[sno].extra.length > 0) {
add_html += '<div class="comment">Memo: ' + H(i.samples[sno].extra) + '</div>\n'; add_html += '<div class="comment">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 += '</div>\n';
}
} }
add_html += '</ol></tr></td></table>\n'; add_html += '</ol></tr></td></table>\n';

View File

@ -11,3 +11,8 @@ id:11001; sev:3; content:"<title>phpinfo()</title><meta name="; depth:2048; memo
id:11002; sev:3; content:'<title>phpMyAdmin </title>'; depth:1024; content:'<a href="http://www.phpmyadmin.net" target="_blank" class="logo">'; depth:2048; memo:"phpMyAdmin"; id:11002; sev:3; content:'<title>phpMyAdmin </title>'; depth:1024; content:'<a href="http://www.phpmyadmin.net" target="_blank" class="logo">'; depth:2048; memo:"phpMyAdmin";
id:11003; sev:3; content:"<title>Parallels Plesk Panel"; depth:1024; content:'action="/login_up.php3" method="post"'; memo:"Plesk administrative interface"; id:11003; sev:3; content:"<title>Parallels Plesk Panel"; depth:1024; content:'action="/login_up.php3" method="post"'; memo:"Plesk administrative interface";
# Reference: http://httpd.apache.org/docs/2.2/mod/mod_status.html
id:11004; sev:3; mime:"text/html"; content:"<title>Apache Status</title>"; depth:100; content:"<h1>Apache Server Status for"; depth:25; memo:"Apache mod_status page";
id:11005; sev:3; mime:"text/html"; content:"<title>Server Information</title>"; depth:200; content:"Apache Server Information</h1>"; depth:50; memo:"Apache mod_status page";

View File

@ -13,8 +13,8 @@ id:31006; sev:3; content:"Provider="; content:";Password="; depth:512; memo:"ODB
id:31007; sev:3; content:"Driver="; content:";Pwd="; depth:512; memo:"ODBC connect string"; id:31007; sev:3; content:"Driver="; content:";Pwd="; depth:512; memo:"ODBC connect string";
# Typical crossdomain / access policy files # Typical crossdomain / access policy files
id:31008; sev:3; content:"<cross-domain-policy>"; depth:512; memo:"Flash crossdomain file"; id:31008; sev:2; content:"<cross-domain-policy>"; depth:512; content:'<allow-access-from domain="*"'; depth:50; memo:"Flash cross-domain policy with wildcard";
id:31009; sev:3; content:"<access-policy>"; depth:512; memo:"Silverlight cross-domain policy"; id:31009; sev:4; content:"<access-policy>"; depth:512; content:'<domain uri="*"/>'; depth:512; memo:"Silverlight cross-domain policy with wildcard";
# Web.xml config file # Web.xml config file
id:31010; sev:3; content:"<web-app"; depth:512; memo:"web.xml config file"; id:31010; sev:3; content:"<web-app"; depth:512; memo:"web.xml config file";
@ -28,6 +28,13 @@ id:31013; sev:3; content:"0] \"GET /"; depth:1024; memo:"Apache access log";
id:31014; sev:3; content:"[error] [client "; depth:1024; memo:"Apache error log"; id:31014; sev:3; content:"[error] [client "; depth:1024; memo:"Apache error log";
id:31015; sev:3; content:"0, GET, /"; depth:1024; memo:"Microsoft IIS access log"; id:31015; sev:3; content:"0, GET, /"; depth:1024; memo:"Microsoft IIS access log";
# Generic robots.txt file
id:31016; sev:4; content:"User-agent:"; depth:100; content:"Disallow: /"; memo:"robots.txt file";
# Signatures to detect SQL dumps
id:31101; sev:2; mime:"text/plain"; content:"-- MySQL dump"; depth:1; content:"-- Host"; depth:256; content:"-- Server version"; memo:"MySQL dump database file";
id:31103; sev:2; mime:"text/plain"; content:" phpMyAdmin SQL Dump"; depth:3; content:" version"; content:"CREATE TABLE"; memo:"phpMyAdmin database dump file";
# Source code and scripts # Source code and scripts
id:32001; sev:3; content:"\nimport java."; depth:512; memo:"Java source"; id:32001; sev:3; content:"\nimport java."; depth:512; memo:"Java source";
id:32002; sev:3; content:"\n#include"; depth:512; memo:"C/C++ source"; id:32002; sev:3; content:"\n#include"; depth:512; memo:"C/C++ source";

View File

@ -2379,12 +2379,6 @@ static void check_for_stuff(struct http_request* req,
} }
} }
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;
}
/* Add more directory signatures here... */ /* Add more directory signatures here... */
if (strstr((char*)sniffbuf, "<A HREF=\"?N=D\">") || if (strstr((char*)sniffbuf, "<A HREF=\"?N=D\">") ||
@ -2471,14 +2465,6 @@ static void check_for_stuff(struct http_request* req,
return; 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;
}
} }

View File

@ -1856,8 +1856,6 @@ static u8 param_behavior_check(struct http_request* req,
DEBUG_MISC_CALLBACK(req, res); DEBUG_MISC_CALLBACK(req, res);
req->pivot->state = PSTATE_PAR_CHECK;
for (i=0; i<req->pivot->misc_cnt; i++) { for (i=0; i<req->pivot->misc_cnt; i++) {
/* Store the biggest response time */ /* Store the biggest response time */
@ -1911,7 +1909,9 @@ static u8 param_behavior_check(struct http_request* req,
req->pivot->res_varies = 1; req->pivot->res_varies = 1;
problem(PROB_VARIES, req, res, 0, req->pivot, 0); problem(PROB_VARIES, req, res, 0, req->pivot, 0);
} }
return 0;
} }
req->pivot->state = PSTATE_PAR_CHECK;
return 0; return 0;
} }

View File

@ -1399,12 +1399,11 @@ bad_404:
} else { } else {
if (req->pivot->type != PIVOT_SERV) { if (req->pivot->type != PIVOT_SERV) {
/* todo(niels) improve behavior by adding a new pivot */
n = req_copy(RPREQ(req), req->pivot, 1);
replace_slash(n, NULL);
maybe_add_pivot(n, NULL, 2);
req->pivot->type = PIVOT_PATHINFO; req->pivot->type = PIVOT_PATHINFO;
destroy_request(n); replace_slash(req->pivot->req, NULL);
/* XXX Update request */
} else } else
problem(PROB_404_FAIL, RPREQ(req), RPRES(req), problem(PROB_404_FAIL, RPREQ(req), RPRES(req),

View File

@ -406,9 +406,6 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
} }
/* Store a reference in our the callers request struct. */
req->pivot = cur;
/* At this point, 'cur' points to a newly created or existing node /* At this point, 'cur' points to a newly created or existing node
for the path element. If this element is parametric, make sure for the path element. If this element is parametric, make sure
that its value is on the 'try' list. */ that its value is on the 'try' list. */
@ -542,10 +539,6 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
} }
/* Set the request pivot, if not set already */
if(!req->pivot)
req->pivot = cur;
/* Done, at last! */ /* Done, at last! */
} }
@ -599,6 +592,7 @@ void remove_issue(struct pivot_desc *pv, u32 type) {
tmp[cnt].extra = pv->issue[i].extra; tmp[cnt].extra = pv->issue[i].extra;
tmp[cnt].req = pv->issue[i].req; tmp[cnt].req = pv->issue[i].req;
tmp[cnt].res = pv->issue[i].res; tmp[cnt].res = pv->issue[i].res;
tmp[cnt].sid = pv->issue[i].sid;
cnt++; cnt++;
} }
} }
@ -609,11 +603,20 @@ void remove_issue(struct pivot_desc *pv, u32 type) {
} }
/* Registers a problem, if not duplicate (res, extra may be NULL): */
void problem(u32 type, struct http_request* req, struct http_response* res, void problem(u32 type, struct http_request* req, struct http_response* res,
u8* extra, struct pivot_desc* pv, u8 allow_dup) { 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; u32 i;
if (pv->type == PIVOT_NONE) FATAL("Uninitialized pivot point"); if (pv->type == PIVOT_NONE) FATAL("Uninitialized pivot point");
@ -637,6 +640,7 @@ void problem(u32 type, struct http_request* req, struct http_response* res,
pv->issue[pv->issue_cnt].extra = extra ? ck_strdup(extra) : NULL; 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].req = req_copy(req, pv, 1);
pv->issue[pv->issue_cnt].res = res_copy(res); pv->issue[pv->issue_cnt].res = res_copy(res);
pv->issue[pv->issue_cnt].sid = sid;
#ifndef LOG_STDERR #ifndef LOG_STDERR
u8* url = serialize_path(req, 1, 1); u8* url = serialize_path(req, 1, 1);

View File

@ -452,13 +452,19 @@ struct pstruct pstructs[] = {
struct issue_desc { struct issue_desc {
u32 type; /* PROB_* */ u32 type; /* PROB_* */
u8* extra; /* Problem-specific string */ u8* extra; /* Problem-specific string */
u32 sid; /* Signature ID, if any */ u32 sid; /* Source ID, if any */
struct http_request* req; /* HTTP request sent */ struct http_request* req; /* HTTP request sent */
struct http_response* res; /* HTTP response seen */ struct http_response* res; /* HTTP response seen */
}; };
/* Register a problem, if not duplicate (res, extra may be NULL): */
/* 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, void problem(u32 type, struct http_request* req, struct http_response* res,
u8* extra, struct pivot_desc* pv, u8 allow_dup); u8* extra, struct pivot_desc* pv, u8 allow_dup);

View File

@ -605,8 +605,8 @@ static void output_crawl_tree(struct pivot_desc* pv) {
u8 tmp[32]; u8 tmp[32];
sprintf((char*)tmp, "i%u", i); sprintf((char*)tmp, "i%u", i);
fprintf(f, " { 'severity': %u, 'type': %u, 'extra': '%s', ", fprintf(f, " { 'severity': %u, 'type': %u, 'sid': '%d', 'extra': '%s', ",
PSEV(pv->issue[i].type) - 1, pv->issue[i].type, 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*)""); pv->issue[i].extra ? js_escape(pv->issue[i].extra, 0) : (u8*)"");
describe_res(f, pv->issue[i].res); describe_res(f, pv->issue[i].res);
@ -744,9 +744,9 @@ static void output_summary_views() {
save_req_res(i_samp[i].i[c]->req, i_samp[i].i[c]->res, 0); save_req_res(i_samp[i].i[c]->req, i_samp[i].i[c]->res, 0);
if (chdir("..")) PFATAL("chdir unexpectedly fails!"); if (chdir("..")) PFATAL("chdir unexpectedly fails!");
fprintf(f, " { 'url': '%s', ", js_escape(p, 0)); fprintf(f, " { 'url': '%s', ", js_escape(p, 0));
fprintf(f, "'extra': '%s', 'dir': '%s/%s' }%s\n", 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) : i_samp[i].i[c]->extra ? js_escape(i_samp[i].i[c]->extra, 0) :
(u8*)"", tmp, tmp2, (u8*)"", i_samp[i].i[c]->sid, tmp, tmp2,
(c == use_samp - 1) ? " ]" : ","); (c == use_samp - 1) ? " ]" : ",");
ck_free(p); ck_free(p);
} }

View File

@ -495,20 +495,11 @@ void signature_problem(struct signature *sig,
#ifdef _SIGNATURE_TEST #ifdef _SIGNATURE_TEST
DEBUG("signature_problem() called for %d (%s)\n", sig->id, sig->memo); DEBUG("signature_problem() called for %d (%s)\n", sig->id, sig->memo);
#else #else
u8* memo = NULL;
/* Each signature is supposed to have a memo: testing just in case */ /* Register the problem, together with the sid */
if (sig->memo) { register_problem((sig->prob ? sig->prob : sig_serv[sig->severity]), sig->id,
u32 len = strlen((char*)sig->memo); req, res, (sig->memo ? sig->memo : (u8*)""), req->pivot, 0);
memo = ck_alloc(len + 15); /* of which 5 for the ID */
snprintf((char*)memo, len + 14, "%s (sig: %u)", (char*)sig->memo, sig->id);
}
/* Todo: update issue_desc and add the ID in it */
problem((sig->prob ? sig->prob : sig_serv[sig->severity]), req, res,
(memo ? memo : (u8*)""), req->pivot, 0);
if (memo) ck_free(memo);
#endif #endif
} }