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:
parent
347a8b4b58
commit
822e4f67e1
12
ChangeLog
12
ChangeLog
|
@ -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
6
README
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
7
config.h
7
config.h
|
@ -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" }
|
||||
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
43
database.c
43
database.c
|
@ -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]);
|
||||
|
|
|
@ -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: */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
43
skipfish.c
43
skipfish.c
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue