Version 1.35 and 1.36 - various changes

Version 1.36b:
  - Command-line support for parameters that should not be fuzzed.
  - In-flight URLs can be previewed by hitting 'return'.

Version 1.35b:
  - Several new form autocomplete rules.
This commit is contained in:
Steve Pinkham 2010-06-14 21:31:24 -04:00
parent 347a8b4b58
commit 822e4f67e1
11 changed files with 156 additions and 40 deletions

View File

@ -1,3 +1,15 @@
Version 1.36b:
--------------
- Command-line support for parameters that should not be fuzzed.
- In-flight URLs can be previewed by hitting 'return'.
Version 1.35b:
--------------
- Several new form autocomplete rules.
Version 1.34b:
--------------

6
README
View File

@ -232,6 +232,9 @@ $ ./skipfish -o output_dir http://www.example.com/some/starting/path.txt
Note that you can provide more than one starting URL if so desired; all of them
will be crawled.
The tool will display some helpful stats while the scan is in progress. You
can also switch to a list of in-flight HTTP requests by pressing return.
In the example above, skipfish will scan the entire www.example.com (including
services on other ports, if linked to from the main page), and write a report
to output_dir/index.html. You can then view this report with your favorite
@ -267,6 +270,9 @@ protocol and port:
$ ./skipfish -I http://example.com:1234/ ...other parameters...
A related function, -K, allows you to specify parameter names not to fuzz
(useful for applications that put session IDs in the URL, to minimize noise).
Another useful scoping option is -D - allowing you to specify additional hosts
or domains to consider in-scope for the test. By default, all hosts appearing
in the command-line URLs are added to the list - but you can use -D to broaden

View File

@ -39,9 +39,9 @@ u8 no_parse, /* Disable HTML link detection */
/* Form autofill hints: */
static u8** addl_form_name;
static u8** addl_form_value;
static u32 addl_form_cnt;
u8** addl_form_name;
u8** addl_form_value;
u32 addl_form_cnt;
/* Runs some rudimentary checks on top-level pivot HTTP responses. */

View File

@ -48,6 +48,12 @@ extern u8 no_parse, /* Disable HTML link detection */
scrape_response(_req, _res); \
} while (0)
/* Form autofill hints: */
extern u8** addl_form_name;
extern u8** addl_form_value;
extern u32 addl_form_cnt;
/* Runs some rudimentary checks on top-level pivot HTTP responses. */

View File

@ -23,7 +23,7 @@
#ifndef _HAVE_CONFIG_H
#define _HAVE_CONFIG_H
#define VERSION "1.34b"
#define VERSION "1.36b"
#define USE_COLOR 1 /* Use terminal colors */
@ -227,6 +227,7 @@ static const char* form_suggestion[][2] = {
{ "search" , "skipfish" },
{ "login" , "skipfish" },
{ "user" , "skipfish" },
{ "nick" , "skipfish" },
{ "pass" , "skipfish" },
{ "year" , "2010" },
{ "card" , "4111111111111111" }, /* Reserved */
@ -238,6 +239,10 @@ static const char* form_suggestion[][2] = {
{ "site" , "http://example.com/?sfish_form_test" },
{ "domain" , "example.com" },
{ "search" , "a" },
{ "comment" , "jellyfish" },
{ "desc" , "jellyfish" },
{ "title" , "jellyfish" },
{ "subject" , "jellyfish" },
{ NULL , "1" }
};

View File

@ -1460,7 +1460,7 @@ static void crawl_parametric_init(struct pivot_desc* pv) {
struct http_request* n;
u32 i;
if (pv->fuzz_par < 0 || !url_allowed(pv->req)) {
if (pv->fuzz_par < 0 || !url_allowed(pv->req) || !param_allowed(pv->name)) {
pv->state = PSTATE_DONE;
return;
}

View File

@ -33,6 +33,7 @@
#include "http_client.h"
#include "database.h"
#include "crawler.h"
#include "analysis.h"
#include "string-inl.h"
struct pivot_desc root_pivot;
@ -41,13 +42,15 @@ u8 **deny_urls, /* List of banned URL substrings */
**deny_strings, /* List of banned page substrings */
**allow_urls, /* List of required URL substrings */
**allow_domains, /* List of allowed vhosts */
**trust_domains; /* List of trusted vhosts */
**trust_domains, /* List of trusted vhosts */
**skip_params; /* List of parameters to ignore */
u32 num_deny_urls,
num_deny_strings,
num_allow_urls,
num_allow_domains,
num_trust_domains;
num_trust_domains,
num_skip_params;
u32 max_depth = MAX_DEPTH,
max_children = MAX_CHILDREN,
@ -81,8 +84,6 @@ static u32 cur_xss_id, scan_id; /* Stored XSS manager IDs */
static struct http_request** xss_req; /* Stored XSS manager req cache */
/* Maps a parsed URL (in req) to the pivot tree, creating or modifying nodes
as necessary, and scheduling them for crawl. This should be called only
on requests that were *not* yet retrieved. */
@ -393,7 +394,8 @@ void maybe_add_pivot(struct http_request* req, struct http_response* res,
/* Phew! At this point, 'cur' points to the final path element, and now,
we just need to take care of parameters. Each parameter has its own
pivot point, and a full copy of the request. */
pivot point, and a full copy of the request - unless on the
param_skip list. */
pno = 0;
@ -618,6 +620,7 @@ u8 url_trusted_host(struct http_request* req) {
return 0;
}
u8 url_allowed(struct http_request* req) {
u8* url = serialize_path(req, 1, 0);
u32 i;
@ -651,6 +654,17 @@ u8 url_allowed(struct http_request* req) {
}
u8 param_allowed(u8* pname) {
u32 i;
for (i=0;i<num_skip_params;i++)
if (!strcmp((char*)pname, (char*)skip_params[i])) return 0;
return 1;
}
/* Compares the checksums for two responses: */
u8 same_page(struct http_sig* sig1, struct http_sig* sig2) {
@ -1143,17 +1157,16 @@ void database_stats() {
pv_stat_crawl(&root_pivot);
SAY("Database statistics\n"
"-------------------\n\n"
cGRA " Pivots : " cNOR "%u total, %u done (%.02f%%) \n"
cGRA " In progress : " cNOR "%u pending, %u init, %u attacks, "
SAY(cLBL "Database statistics:\n\n"
cGRA " Pivots : " cNOR "%u total, %u done (%.02f%%) \n"
cGRA " In progress : " cNOR "%u pending, %u init, %u attacks, "
"%u dict \n"
cGRA " Missing nodes : " cNOR "%u spotted\n"
cGRA " Node types : " cNOR "%u serv, %u dir, %u file, %u pinfo, "
cGRA " Missing nodes : " cNOR "%u spotted\n"
cGRA " Node types : " cNOR "%u serv, %u dir, %u file, %u pinfo, "
"%u unkn, %u par, %u val\n"
cGRA " Issues found : " cNOR "%u info, %u warn, %u low, %u medium, "
cGRA " Issues found : " cNOR "%u info, %u warn, %u low, %u medium, "
"%u high impact\n"
cGRA " Dict size : " cNOR "%u words (%u new), %u extensions, "
cGRA " Dict size : " cNOR "%u words (%u new), %u extensions, "
"%u candidates\n",
pivot_cnt, pivot_done, pivot_cnt ? ((100.0 * pivot_done) / (pivot_cnt))
: 0, pivot_pending, pivot_init, pivot_attack, pivot_bf, pivot_missing,
@ -1354,6 +1367,10 @@ void destroy_database() {
ck_free(allow_domains);
ck_free(trust_domains);
ck_free(addl_form_name);
ck_free(addl_form_value);
ck_free(skip_params);
for (kh=0;kh<WORD_HASH;kh++) {
for (i=0;i<keyword_cnt[kh];i++) ck_free(keyword[kh][i].word);
ck_free(keyword[kh]);

View File

@ -318,13 +318,14 @@ u8 same_page(struct http_sig* sig1, struct http_sig* sig2);
} while (0)
extern u8 **deny_urls, **deny_strings, **allow_urls, **allow_domains,
**trust_domains;
**trust_domains, **skip_params;
extern u32 num_deny_urls,
num_deny_strings,
num_allow_urls,
num_allow_domains,
num_trust_domains;
num_trust_domains,
num_skip_params;
extern u32 max_depth,
max_children,
@ -336,6 +337,7 @@ extern u32 max_depth,
u8 url_allowed_host(struct http_request* req);
u8 url_trusted_host(struct http_request* req);
u8 url_allowed(struct http_request* req);
u8 param_allowed(u8* pname);
/* Keyword management: */

View File

@ -2444,19 +2444,18 @@ void http_stats(u64 st_time) {
gettimeofday(&tv, NULL);
en_time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
SAY("Scan statistics\n"
"---------------\n\n"
cGRA " Scan time : " cNOR "%u:%02u:%02u.%04u\n"
cGRA " HTTP requests : " cNOR "%u sent (%.02f/s), %.02f kB in, "
"%.02f kB out (%.02f kB/s) \n"
cGRA " Compression : " cNOR "%.02f kB in, %.02f kB out "
"(%.02f%% gain) \n"
cGRA " HTTP exceptions : " cNOR "%u net errors, %u proto errors, "
SAY(cLBL "Scan statistics:\n\n"
cGRA " Scan time : " cNOR "%u:%02u:%02u.%04u\n"
cGRA " HTTP requests : " cNOR "%u (%.01f/s), %llu kB in, "
"%llu kB out (%.01f kB/s) \n"
cGRA " Compression : " cNOR "%llu kB in, %llu kB out "
"(%.01f%% gain) \n"
cGRA " HTTP faults : " cNOR "%u net errors, %u proto errors, "
"%u retried, %u drops\n"
cGRA " TCP connections : " cNOR "%u total (%.02f req/conn) \n"
cGRA " TCP exceptions : " cNOR "%u failures, %u timeouts, %u purged\n"
cGRA " External links : " cNOR "%u skipped\n"
cGRA " Reqs pending : " cNOR "%u \n",
cGRA " TCP handshakes : " cNOR "%u total (%.01f req/conn) \n"
cGRA " TCP faults : " cNOR "%u failures, %u timeouts, %u purged\n"
cGRA " External links : " cNOR "%u skipped\n"
cGRA " Reqs pending : " cNOR "%u \n",
/* hrs */ (u32)((en_time - st_time) / 1000 / 60 / 60),
/* min */ (u32)((en_time - st_time) / 1000 / 60) % 60,
@ -2465,10 +2464,10 @@ void http_stats(u64 st_time) {
req_count - queue_cur,
(float) (req_count - queue_cur / 1.15) * 1000 / (en_time - st_time + 1),
(float) bytes_recv / 1024, (float) bytes_sent / 1024,
bytes_recv / 1024, bytes_sent / 1024,
(float) (bytes_recv + bytes_sent) / 1.024 / (en_time - st_time + 1),
(float) bytes_deflated / 1024, (float) bytes_inflated / 1024,
bytes_deflated / 1024, bytes_inflated / 1024,
((float) bytes_inflated - bytes_deflated) / (bytes_inflated +
bytes_deflated + 1) * 100,
@ -2478,3 +2477,40 @@ void http_stats(u64 st_time) {
conn_failed, conn_busy_tmout, conn_idle_tmout,
url_scope, queue_cur);
}
/* Show currently handled requests. */
#define SP70 \
" "
void http_req_list(void) {
u32 i;
struct conn_entry* c = conn;
SAY(cLBL "In-flight requests (max 15 shown):\n\n");
for (i=0;i<15;i++) {
SAY(" " cGRA "[" cBLU "%02d" cGRA "] " cBRI, i + 1);
if (c && c->q) {
u8* p = serialize_path(c->q->req, 1, 0);
u32 l = strlen((char*)p);
if (l > 70) {
SAY("%.30s" cGRA "..." cBRI "%.37s\n", p, p + l - 37);
} else {
SAY("%s%s\n", p, SP70 + l);
}
ck_free(p);
} else SAY(cLGN "<slot idle>%s\n", SP70 + 11);
if (c) c = c->next;
}
}

View File

@ -414,5 +414,6 @@ void destroy_http();
/* Shows some pretty statistics. */
void http_stats(u64 st_time);
void http_req_list(void);
#endif /* !_HAVE_HTTP_CLIENT_H */

View File

@ -31,6 +31,8 @@
#include <sys/time.h>
#include <time.h>
#include <sys/stat.h>
#include <termios.h>
#include <fcntl.h>
#include "types.h"
#include "alloc-inl.h"
@ -75,6 +77,7 @@ void usage(char* argv0) {
" -I string - only follow URLs matching 'string'\n"
" -X string - exclude URLs matching 'string'\n"
" -S string - exclude pages containing 'string'\n"
" -K string - do not fuzz parameters named 'string'\n"
" -D domain - crawl cross-site links to another domain\n"
" -B domain - trust, but do not crawl, another domain\n"
" -O - do not submit any forms\n"
@ -140,9 +143,10 @@ static void resize_handler(int sig) {
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;
u8 dont_save_words = 0, show_once = 0, be_quiet = 0, display_mode = 0;
u8 *wordlist = (u8*)DEF_WORDLIST, *output_dir = NULL;
struct termios term;
struct timeval tv;
u64 st_time, en_time;
@ -161,7 +165,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:r:p:I:X:S:D:PJOYQMUEW:LVT:G:R:B:q:g:m:f:t:w:i:s:o:hu")) > 0)
"+A:F:C:H:b:Nd:c:r:p:I:X:S:D:PJOYQMUEK:W:LVT:G:R:B:q:g:m:f:t:w:i:s:o:hu")) > 0)
switch (opt) {
@ -212,6 +216,10 @@ int main(int argc, char** argv) {
APPEND_FILTER(allow_domains, num_allow_domains, optarg);
break;
case 'K':
APPEND_FILTER(skip_params, num_skip_params, optarg);
break;
case 'B':
if (*optarg == '*') optarg++;
APPEND_FILTER(trust_domains, num_trust_domains, optarg);
@ -426,6 +434,13 @@ int main(int argc, char** argv) {
optind++;
}
/* Char-by char stdio. */
tcgetattr(0, &term);
term.c_lflag &= ~ICANON;
tcsetattr(0, TCSAFLUSH, &term);
fcntl(0, F_SETFL, O_NONBLOCK);
gettimeofday(&tv, NULL);
st_time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
@ -434,6 +449,8 @@ int main(int argc, char** argv) {
while ((next_from_queue() && !stop_soon) || (!show_once++)) {
u8 keybuf[8];
if (be_quiet || ((loop_cnt++ % 20) && !show_once)) continue;
if (clear_screen) {
@ -446,10 +463,21 @@ int main(int argc, char** argv) {
cBRI " -" cPIN " %s " cBRI "-\n\n" cNOR,
allow_domains[0]);
http_stats(st_time);
SAY("\n");
database_stats();
SAY("\n \r");
if (!display_mode) {
http_stats(st_time);
SAY("\n");
database_stats();
} else {
http_req_list();
}
SAY(" \r");
if (read(0, keybuf, sizeof(keybuf)) > 0) {
display_mode ^= 1;
clear_screen = 1;
}
}
@ -459,6 +487,9 @@ int main(int argc, char** argv) {
if (stop_soon)
SAY(cYEL "[!] " cBRI "Scan aborted by user, bailing out!" cNOR "\n");
term.c_lflag |= ICANON;
tcsetattr(0, TCSAFLUSH, &term);
if (!dont_save_words) save_keywords((u8*)wordlist);
write_report(output_dir, en_time - st_time, seed);