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:
parent
6b2d33edca
commit
62021819e7
16
ChangeLog
16
ChangeLog
|
@ -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:
|
||||
--------------
|
||||
|
||||
|
|
2
Makefile
2
Makefile
|
@ -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 \
|
||||
|
|
16
analysis.c
16
analysis.c
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
|
|
5
config.h
5
config.h
|
@ -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). */
|
||||
|
|
15
crawler.h
15
crawler.h
|
@ -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 */
|
||||
|
||||
|
|
22
database.c
22
database.c
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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): */
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: */
|
||||
|
|
30
string-inl.h
30
string-inl.h
|
@ -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 { \
|
||||
|
|
Loading…
Reference in New Issue