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:
- Added Host header XSS testing.

View File

@ -20,7 +20,7 @@
#
PROGNAME = skipfish
VERSION = 2.08b
VERSION = 2.09b
SRCDIR = src
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>';
}
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_win(\'' + i.samples[sno].dir + '\', false)">+</a> ]</span>\n';
if (i.samples[sno].extra && i.samples[sno].extra.length > 0)
add_html += '<div class="comment">Memo: ' + H(i.samples[sno].extra) + '</div>\n';
if (i.samples[sno].extra && i.samples[sno].extra.length > 0) {
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';

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: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";
# Typical crossdomain / access policy files
id:31008; sev:3; content:"<cross-domain-policy>"; depth:512; memo:"Flash crossdomain file";
id:31009; sev:3; content:"<access-policy>"; depth:512; memo:"Silverlight cross-domain policy";
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:4; content:"<access-policy>"; depth:512; content:'<domain uri="*"/>'; depth:512; memo:"Silverlight cross-domain policy with wildcard";
# 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: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
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";

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... */
if (strstr((char*)sniffbuf, "<A HREF=\"?N=D\">") ||
@ -2471,14 +2465,6 @@ static void check_for_stuff(struct http_request* req,
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);
req->pivot->state = PSTATE_PAR_CHECK;
for (i=0; i<req->pivot->misc_cnt; i++) {
/* Store the biggest response time */
@ -1911,7 +1909,9 @@ static u8 param_behavior_check(struct http_request* req,
req->pivot->res_varies = 1;
problem(PROB_VARIES, req, res, 0, req->pivot, 0);
}
return 0;
}
req->pivot->state = PSTATE_PAR_CHECK;
return 0;
}

View File

@ -1399,12 +1399,11 @@ bad_404:
} else {
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;
destroy_request(n);
replace_slash(req->pivot->req, NULL);
/* XXX Update request */
} else
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
for the path element. If this element is parametric, make sure
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! */
}
@ -599,6 +592,7 @@ void remove_issue(struct pivot_desc *pv, u32 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++;
}
}
@ -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,
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");
@ -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].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);

View File

@ -452,13 +452,19 @@ struct pstruct pstructs[] = {
struct issue_desc {
u32 type; /* PROB_* */
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_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,
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];
sprintf((char*)tmp, "i%u", i);
fprintf(f, " { 'severity': %u, 'type': %u, 'extra': '%s', ",
PSEV(pv->issue[i].type) - 1, pv->issue[i].type,
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);
@ -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);
if (chdir("..")) PFATAL("chdir unexpectedly fails!");
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) :
(u8*)"", tmp, tmp2,
(u8*)"", i_samp[i].i[c]->sid, tmp, tmp2,
(c == use_samp - 1) ? " ]" : ",");
ck_free(p);
}

View File

@ -495,20 +495,11 @@ void signature_problem(struct signature *sig,
#ifdef _SIGNATURE_TEST
DEBUG("signature_problem() called for %d (%s)\n", sig->id, sig->memo);
#else
u8* memo = NULL;
/* Each signature is supposed to have a memo: testing just in case */
if (sig->memo) {
u32 len = strlen((char*)sig->memo);
memo = ck_alloc(len + 15); /* of which 5 for the ID */
snprintf((char*)memo, len + 14, "%s (sig: %u)", (char*)sig->memo, sig->id);
}
/* 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*)""), req->pivot, 0);
/* 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
}