2.01b: A number of improvements

- Substantial improvement to SQL injection checks.
- Improvements to directory traversal checks (courtesy of Niels Heinen).
- Fix to numerical brute-force logic.
- Major improvement to directory brute force: much better duplicate elimination in some webserver configurations.
- Added a check for attacker-controlled prefixes on inline responses. This currently leads to UTF-7 BOM XSS, Flash, Java attacks (thanks to Niels Heinen).
This commit is contained in:
Steve Pinkham 2011-08-09 16:06:35 -04:00
parent 6b2d33edca
commit 62021819e7
14 changed files with 935 additions and 526 deletions

View File

@ -1,3 +1,19 @@
Version 2.01b:
--------------
- Substantial improvement to SQL injection checks.
- Improvements to directory traversal checks (courtesy of Niels Heinen).
- Fix to numerical brute-force logic.
- Major improvement to directory brute force: much better
duplicate elimination in some webserver configurations.
- Added a check for attacker-controlled prefixes on inline responses.
This currently leads to UTF-7 BOM XSS, Flash, Java attacks (thanks to
Niels Heinen).
Version 2.00b:
--------------

View File

@ -20,7 +20,7 @@
#
PROGNAME = skipfish
VERSION = 2.00b
VERSION = 2.01b
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 \

View File

@ -1553,14 +1553,14 @@ void content_checks(struct http_request* req, struct http_response* res) {
if (is_javascript(res) && !res->json_safe &&
(!req->method || !strcmp((char*)req->method, "GET")) &&
!strstr((char*)res->payload, "if (") &&
!strstr((char*)res->payload, "if(") &&
!strstr((char*)res->payload, "for (") &&
!strstr((char*)res->payload, "for(") &&
!strstr((char*)res->payload, "while (") &&
!strstr((char*)res->payload, "while(") &&
!strstr((char*)res->payload, "function ") &&
!strstr((char*)res->payload, "function("))
!inl_findstr(res->payload, (u8*)"if (", 1024) &&
!inl_findstr(res->payload, (u8*)"if(", 1024) &&
!inl_findstr(res->payload, (u8*)"for (", 1024) &&
!inl_findstr(res->payload, (u8*)"for(", 1024) &&
!inl_findstr(res->payload, (u8*)"while (", 1024) &&
!inl_findstr(res->payload, (u8*)"while(", 1024) &&
!inl_findstr(res->payload, (u8*)"function ", 1024) &&
!inl_findstr(res->payload, (u8*)"function(", 1024))
problem(PROB_JS_XSSI, req, res, NULL, req->pivot, 0);
tmp = res->payload;

View File

@ -297,6 +297,7 @@ var issue_desc= {
"30601": "HTML form with no apparent XSRF protection",
"30602": "JSON response with no apparent XSSI protection",
"30701": "Incorrect caching directives (lower risk)",
"30801": "User-controlled response prefix (BOM / plugin attacks)",
"40101": "XSS vector in document body",
"40102": "XSS vector via arbitrary URLs",

View File

@ -102,15 +102,16 @@
/* 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 200 /* Brute-force queue block */
#define DICT_BATCH 100 /* Brute-force queue block */
#else
#define DICT_BATCH 1000 /* Brute-force queue block */
#define DICT_BATCH 600 /* Brute-force queue block */
#endif /* ^QUEUE_FILO */
/* Single query for IPS detection - Evil Query of Doom (tm). */

1320
crawler.c

File diff suppressed because it is too large Load Diff

View File

@ -41,22 +41,22 @@ 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 fetch_unknown_callback(struct http_request* req, struct http_response* res);
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 fetch_file_callback(struct http_request* req, struct http_response* res);
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 fetch_dir_callback(struct http_request* req, struct http_response* res);
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 crawl_par_trylist_init(struct pivot_desc* pv);
void param_trylist_start(struct pivot_desc* pv);
/* Adds new name=value to form hints list. */
@ -81,6 +81,12 @@ void add_form_hint(u8* name, u8* value);
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 ? \
@ -92,6 +98,7 @@ void add_form_hint(u8* name, u8* value);
#define DEBUG_CALLBACK(_req, _res)
#define DEBUG_HELPER(_pv)
#define DEBUG_PIVOT(_text, _pv)
#endif /* ^LOG_STDERR */

View File

@ -220,13 +220,13 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
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 = fetch_dir_callback;
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);
fetch_dir_callback(req, cur->res);
dir_retrieve_check(req, cur->res);
} else async_request(cur->req);
wordlist_confirm_word(req->host);
@ -334,7 +334,7 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
set_value(PARAM_PATH, NULL, (u8*)"", -1, &n->req->par);
n->type = PIVOT_DIR;
n->req->callback = fetch_dir_callback;
n->req->callback = dir_retrieve_check;
if (!url_allowed(n->req)) n->no_fuzz = 2;
@ -352,7 +352,7 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
if (i == path_cnt - 2 && ends_with_slash && res) {
n->res = res_copy(res);
fetch_dir_callback(n->req, n->res);
dir_retrieve_check(n->req, n->res);
} else async_request(n->req);
@ -369,7 +369,7 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
n->type = PIVOT_UNKNOWN;
n->res = res_copy(res);
n->req->callback = fetch_unknown_callback;
n->req->callback = unknown_retrieve_check;
if (cur->state > PSTATE_IPS_CHECK) {
@ -381,7 +381,7 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
if (!res) {
n->state = PSTATE_FETCH;
async_request(n->req);
} else fetch_unknown_callback(n->req, n->res);
} else unknown_retrieve_check(n->req, n->res);
} else n->state = PSTATE_PENDING;
@ -390,7 +390,7 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
/* Parameters found. Assume file, schedule a fetch. */
n->type = PIVOT_FILE;
n->req->callback = fetch_file_callback;
n->req->callback = file_retrieve_check;
if (cur->state > PSTATE_IPS_CHECK) {
n->state = PSTATE_FETCH;
@ -424,7 +424,7 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
cur->try_list[cur->try_cnt++] = ck_strdup(req->par.v[pno]);
if (cur->state == PSTATE_DONE)
crawl_par_trylist_init(cur);
param_trylist_start(cur);
}
@ -499,11 +499,11 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
/* File fetcher does everything we need. */
n->req->callback = fetch_file_callback;
n->req->callback = file_retrieve_check;
if (cur->state > PSTATE_IPS_CHECK) {
n->state = PSTATE_FETCH;
if (res) fetch_file_callback(n->req, n->res);
if (res) file_retrieve_check(n->req, n->res);
else async_request(n->req);
} else n->state = PSTATE_PENDING;
@ -527,7 +527,7 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
cur->try_list[cur->try_cnt++] = ck_strdup(req->par.v[pno]);
if (cur->state == PSTATE_DONE)
crawl_par_trylist_init(cur);
param_trylist_start(cur);
}

View File

@ -133,8 +133,10 @@ struct pivot_desc {
/* Injection attack logic scratchpad: */
struct http_request* misc_req[10]; /* Saved requests */
struct http_response* misc_res[10]; /* Saved responses */
#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 */
@ -262,6 +264,7 @@ u8 is_c_sens(struct pivot_desc* pv);
#define PROB_CACHE_LOW 30701 /* Cache nit-picking */
#define PROB_PROLOGUE 30801 /* User-supplied prologue */
/* - Moderate severity issues (data compromise): */

View File

@ -455,6 +455,13 @@ 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

View File

@ -424,6 +424,13 @@ 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

View File

@ -387,6 +387,13 @@ 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

View File

@ -102,13 +102,17 @@ struct http_request {
struct pivot_desc *pivot; /* Pivot descriptor */
u32 user_val; /* Can be used freely */
u8 with_ext; /* Extension-based probe? */
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: */

View File

@ -109,6 +109,36 @@ static inline void* inl_memmem(const void* haystack, u32 h_len,
}
/* 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 { \