1.71b: better duplicate node detection, new report diff tool and child
signatures in report - Child signatures now exposed in the report, - Improvements to duplicate node detection, - sfscandiff tool added to compare reports.
This commit is contained in:
parent
e5f6c3e1b1
commit
2e4f8fa7a7
|
@ -1,3 +1,12 @@
|
||||||
|
Version 1.71b:
|
||||||
|
--------------
|
||||||
|
|
||||||
|
- Child signatures now exposed in the report,
|
||||||
|
|
||||||
|
- Improvements to duplicate node detection,
|
||||||
|
|
||||||
|
- sfscandiff tool added to compare reports.
|
||||||
|
|
||||||
Version 1.70b:
|
Version 1.70b:
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -20,7 +20,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
PROGNAME = skipfish
|
PROGNAME = skipfish
|
||||||
VERSION = 1.70b
|
VERSION = 1.71b
|
||||||
|
|
||||||
OBJFILES = http_client.c database.c crawler.c analysis.c report.c
|
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 \
|
INCFILES = alloc-inl.h string-inl.h debug.h types.h http_client.h \
|
||||||
|
|
16
README
16
README
|
@ -251,6 +251,12 @@ results over HTTP). The index.html file is static; actual results are stored
|
||||||
as a hierarchy of JSON files, suitable for machine processing or different
|
as a hierarchy of JSON files, suitable for machine processing or different
|
||||||
presentation frontends if needs be.
|
presentation frontends if needs be.
|
||||||
|
|
||||||
|
A simple companion script, sfscandiff, can be used to compute a delta for
|
||||||
|
two scans executed against the same target with the same flags. The newer
|
||||||
|
report will be non-destructively annotated by adding red background to all
|
||||||
|
new or changed nodes; and blue background to all new or changed issues
|
||||||
|
found.
|
||||||
|
|
||||||
Some sites may require authentication; for simple HTTP credentials, you can
|
Some sites may require authentication; for simple HTTP credentials, you can
|
||||||
try:
|
try:
|
||||||
|
|
||||||
|
@ -500,15 +506,15 @@ know:
|
||||||
currently employed by skipfish; but in the long run, should be provided
|
currently employed by skipfish; but in the long run, should be provided
|
||||||
as a last-resort option.
|
as a last-resort option.
|
||||||
|
|
||||||
* Scan resume option.
|
* Scan resume option.
|
||||||
|
|
||||||
* Option to limit document sampling or save samples directly to disk.
|
* Option to limit document sampling or save samples directly to disk.
|
||||||
|
|
||||||
* Standalone installation (make install) support.
|
* Standalone installation (make install) support.
|
||||||
|
|
||||||
* Config file support.
|
* Config file support.
|
||||||
|
|
||||||
* A database for banner / version checks?
|
* A database for banner / version checks?
|
||||||
|
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
9. Oy! Something went horribly wrong!
|
9. Oy! Something went horribly wrong!
|
||||||
|
|
|
@ -93,6 +93,13 @@ td.child_ctr:hover {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.name_diff {
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
background-color: red;
|
||||||
|
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
span.sum_name {
|
span.sum_name {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border: 1px solid white;
|
border: 1px solid white;
|
||||||
|
@ -121,6 +128,13 @@ span.sum_name:hover {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.issue_desc_diff {
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
background-color: blue;
|
||||||
|
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
color: crimson;
|
color: crimson;
|
||||||
font-size: 70%;
|
font-size: 70%;
|
||||||
|
@ -227,9 +241,10 @@ div.req_hdr:hover {
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
var c_count = 0;
|
var c_count = 0;
|
||||||
var ignore_click = false;
|
var ignore_click = false;
|
||||||
var max_samples = 100;
|
var max_samples = 100;
|
||||||
|
var diff_mode = false;
|
||||||
|
|
||||||
/* Descriptions for issues reported by the scanner. */
|
/* Descriptions for issues reported by the scanner. */
|
||||||
|
|
||||||
|
@ -484,6 +499,12 @@ function load_node(dir, tid) {
|
||||||
x.send(null);
|
x.send(null);
|
||||||
eval(x.responseText);
|
eval(x.responseText);
|
||||||
|
|
||||||
|
if (diff_mode) {
|
||||||
|
x.open('GET', dir + 'diff_data.js', false);
|
||||||
|
x.send(null);
|
||||||
|
eval(x.responseText);
|
||||||
|
}
|
||||||
|
|
||||||
delete x;
|
delete x;
|
||||||
|
|
||||||
next_opacity('c_' + tid, 0);
|
next_opacity('c_' + tid, 0);
|
||||||
|
@ -504,7 +525,11 @@ function load_node(dir, tid) {
|
||||||
case 4: add_html += '<img src="i_high.png" title="High risk: system compromise">'; break;
|
case 4: add_html += '<img src="i_high.png" title="High risk: system compromise">'; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_html += '</td>\n<td><div style="issue_desc">' + issue_desc[i.type] + '</div>\n<ol>\n';
|
if (!diff_mode || diff_data[i.dir] == undefined) {
|
||||||
|
add_html += '</td>\n<td><div class="issue_desc">' + issue_desc[i.type] + '</div>\n<ol>\n';
|
||||||
|
} else {
|
||||||
|
add_html += '</td>\n<td><div class="issue_desc_diff">' + issue_desc[i.type] + '</div>\n<ol>\n';
|
||||||
|
}
|
||||||
|
|
||||||
for (var cno2 = cno; cno2 < issue.length; cno2++) {
|
for (var cno2 = cno; cno2 < issue.length; cno2++) {
|
||||||
var i2 = issue[cno2];
|
var i2 = issue[cno2];
|
||||||
|
@ -588,7 +613,15 @@ function load_node(dir, tid) {
|
||||||
|
|
||||||
if (c.dupe) add_html += '<img src="n_clone.png" title="Suspected duplicate">' +
|
if (c.dupe) add_html += '<img src="n_clone.png" title="Suspected duplicate">' +
|
||||||
'<span class="dupe_name" title="' + H(c.url) + '">' + H(TRUNC(c.name)) + '</span>\n';
|
'<span class="dupe_name" title="' + H(c.url) + '">' + H(TRUNC(c.name)) + '</span>\n';
|
||||||
else add_html += '<span class="name" title="' + H(c.url) + '">' + H(TRUNC(c.name)) + '</span>\n';
|
else {
|
||||||
|
if (!diff_mode || diff_data[c.dir] == 0) {
|
||||||
|
add_html += '<span class="name" title="' + H(c.url) + '">' + H(TRUNC(c.name)) + '</span>\n';
|
||||||
|
} else if (diff_data[c.dir] == 1) {
|
||||||
|
add_html += '<span class="name_diff" title="' + H(c.url) + '">' + H(TRUNC(c.name)) + '</span>\n';
|
||||||
|
} else {
|
||||||
|
add_html += '<span class="name_diff" title="' + H(c.url) + '">' + H(TRUNC(c.name)) + ' (' + diff_data[c.dir] + ' more)</span>\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (c.linked == 0)
|
if (c.linked == 0)
|
||||||
add_html += '<img src="n_unlinked.png" title="Not linked (brute-forced)" class="i2">';
|
add_html += '<img src="n_unlinked.png" title="Not linked (brute-forced)" class="i2">';
|
||||||
|
|
|
@ -587,6 +587,10 @@ void problem(u32 type, struct http_request* req, struct http_response* res,
|
||||||
|
|
||||||
pv->issue_cnt++;
|
pv->issue_cnt++;
|
||||||
|
|
||||||
|
/* Propagate parent issue counts. */
|
||||||
|
|
||||||
|
do { pv->desc_issue_cnt++; } while ((pv = pv->parent));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -100,6 +100,7 @@ struct pivot_desc {
|
||||||
|
|
||||||
struct issue_desc* issue; /* List of issues found */
|
struct issue_desc* issue; /* List of issues found */
|
||||||
u32 issue_cnt; /* Number of issues */
|
u32 issue_cnt; /* Number of issues */
|
||||||
|
u32 desc_issue_cnt; /* Number of child issues */
|
||||||
|
|
||||||
struct http_response* res; /* HTTP response seen */
|
struct http_response* res; /* HTTP response seen */
|
||||||
|
|
||||||
|
|
7
report.c
7
report.c
|
@ -181,8 +181,8 @@ static inline u32 hash_extra(u8* str) {
|
||||||
/* Registers a new pivot signature, or updates an existing one. */
|
/* Registers a new pivot signature, or updates an existing one. */
|
||||||
|
|
||||||
static void maybe_add_sig(struct pivot_desc* pv) {
|
static void maybe_add_sig(struct pivot_desc* pv) {
|
||||||
u32 i, issue_sig = ~pv->issue_cnt,
|
u32 i, issue_sig = ~(pv->issue_cnt | (pv->desc_issue_cnt << 16)),
|
||||||
child_sig = ~pv->child_cnt;
|
child_sig = ~(pv->desc_cnt | (pv->child_cnt << 16));
|
||||||
|
|
||||||
if (!pv->res) return;
|
if (!pv->res) return;
|
||||||
|
|
||||||
|
@ -531,12 +531,13 @@ static void output_crawl_tree(struct pivot_desc* pv) {
|
||||||
describe_res(f, pv->child[i]->res);
|
describe_res(f, pv->child[i]->res);
|
||||||
|
|
||||||
fprintf(f,", 'missing': %s, 'csens': %s, 'child_cnt': %u, "
|
fprintf(f,", 'missing': %s, 'csens': %s, 'child_cnt': %u, "
|
||||||
"'issue_cnt': [ %u, %u, %u, %u, %u ] }%s\n",
|
"'issue_cnt': [ %u, %u, %u, %u, %u ], 'sig': 0x%x }%s\n",
|
||||||
pv->child[i]->missing ? "true" : "false",
|
pv->child[i]->missing ? "true" : "false",
|
||||||
pv->child[i]->csens ? "true" : "false",
|
pv->child[i]->csens ? "true" : "false",
|
||||||
pv->child[i]->total_child_cnt, pv->child[i]->total_issues[1],
|
pv->child[i]->total_child_cnt, pv->child[i]->total_issues[1],
|
||||||
pv->child[i]->total_issues[2], pv->child[i]->total_issues[3],
|
pv->child[i]->total_issues[2], pv->child[i]->total_issues[3],
|
||||||
pv->child[i]->total_issues[4], pv->child[i]->total_issues[5],
|
pv->child[i]->total_issues[4], pv->child[i]->total_issues[5],
|
||||||
|
pv->child[i]->pv_sig,
|
||||||
(i == pv->child_cnt - 1) ? "" : ",");
|
(i == pv->child_cnt - 1) ? "" : ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "sfscandiff - skipfish scan result comparator (lcamtuf@google.com)" 1>&2
|
||||||
|
|
||||||
|
if [ ! "$#" = "2" ]; then
|
||||||
|
echo "Usage: $0 /path/to/old/scan/ /path/to/new/scan/" 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -s "$1/summary.js" ]; then
|
||||||
|
echo "ERROR: First parameter does not point to a valid skipfish scan directory." 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -s "$2/summary.js" ]; then
|
||||||
|
echo "ERROR: Second parameter does not point to a valid skipfish scan directory." 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
OLD_SCAN="$1"
|
||||||
|
NEW_SCAN="$2"
|
||||||
|
|
||||||
|
# Takes two parameters: old scan subdir and new scan subdir
|
||||||
|
|
||||||
|
function check_dir {
|
||||||
|
|
||||||
|
# echo "Comparing: old=[$1] new=[$2]..."
|
||||||
|
|
||||||
|
echo "0" >"$2/.diff_cnt"
|
||||||
|
|
||||||
|
echo "var diff_data = {" >"$2/diff_data.js"
|
||||||
|
|
||||||
|
grep "'dir':" "$2/child_index.js" | awk -F "'dir': " '{print $2}' | \
|
||||||
|
sed "s/,.*'sig'://" | sed "s/[,}]*$//" |sed "s/'//g" | \
|
||||||
|
while read -r dir sig; do
|
||||||
|
|
||||||
|
# echo " Checking dir=[$dir] sig=[$sig]"
|
||||||
|
|
||||||
|
# Find matching child node first.
|
||||||
|
|
||||||
|
MATCH_DIR=`grep -E "'sig': $sig[, ]" "$1/child_index.js" 2>/dev/null | \
|
||||||
|
awk -F "'dir': " '{print $2}' | cut -d"'" -f2 | head -1`
|
||||||
|
|
||||||
|
test "$MATCH_DIR" = "" && MATCH_DIR="not_found"
|
||||||
|
|
||||||
|
# Recurse into children first, to get an accurate count of differences
|
||||||
|
# for all descendants.
|
||||||
|
|
||||||
|
check_dir "$1/$MATCH_DIR" "$2/$dir"
|
||||||
|
|
||||||
|
# Read difference count from descendands. If node does not appear in
|
||||||
|
# old scan, add 1 to the count. Store count.
|
||||||
|
|
||||||
|
DIFF_CNT=`cat "$2/$dir/.diff_cnt" 2>/dev/null`
|
||||||
|
test "$DIFF_CNT" = "" && DIFF_CNT=0
|
||||||
|
|
||||||
|
test "$MATCH_DIR" = "not_found" && DIFF_CNT=$[DIFF_CNT+1]
|
||||||
|
|
||||||
|
echo " '$dir': $DIFF_CNT," >>"$2/diff_data.js"
|
||||||
|
|
||||||
|
# Update total count for parent node ($2)
|
||||||
|
|
||||||
|
TOTAL_DIFF_CNT=`cat "$2/.diff_cnt" 2>/dev/null`
|
||||||
|
TOTAL_DIFF_CNT=$[TOTAL_DIFF_CNT+DIFF_CNT]
|
||||||
|
echo "$TOTAL_DIFF_CNT" >"$2/.diff_cnt"
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
# Now, for every issue, see if a matching issue appears in old scan.
|
||||||
|
# If not, add it to diff_data.
|
||||||
|
|
||||||
|
grep "'severity':" "$2/issue_index.js" | while read -r line; do
|
||||||
|
|
||||||
|
LOOK_FOR=`echo "$line" | awk -F"'fetched':" '{print $1}'`
|
||||||
|
ISSUE_DIR=`echo "$line" | awk -F"'dir':" '{print $2}'|cut -d"'" -f2`
|
||||||
|
|
||||||
|
# echo " Checking issue=[$ISSUE_DIR]"
|
||||||
|
|
||||||
|
if ! grep -qF "$LOOK_FOR" "$1/issue_index.js" 2>/dev/null; then
|
||||||
|
echo " '$ISSUE_DIR': 1," >>"$2/diff_data.js"
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
echo " '_eof': 0" >>"$2/diff_data.js"
|
||||||
|
echo "};" >>"$2/diff_data.js"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
echo -n "Finding new results in $NEW_SCAN... "
|
||||||
|
|
||||||
|
check_dir "$OLD_SCAN" "$NEW_SCAN"
|
||||||
|
|
||||||
|
TOTAL=`cat "$NEW_SCAN/.diff_cnt"`
|
||||||
|
|
||||||
|
if [ "$TOTAL" = "0" ]; then
|
||||||
|
echo "no new findings."
|
||||||
|
elif [ "$TOTAL" = "1" ]; then
|
||||||
|
echo "one new or modified node found."
|
||||||
|
else
|
||||||
|
echo "$TOTAL new or modified nodes found."
|
||||||
|
fi
|
||||||
|
|
||||||
|
grep -qF "var diff_mode" "$NEW_SCAN/summary.js" ||
|
||||||
|
echo "var diff_mode = true;" >>"$NEW_SCAN/summary.js"
|
||||||
|
|
||||||
|
exit 0
|
|
@ -537,7 +537,7 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
gettimeofday(&tv, NULL);
|
gettimeofday(&tv, NULL);
|
||||||
en_time = tv.tv_sec * 1000L + tv.tv_usec / 1000L;
|
en_time = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
|
||||||
|
|
||||||
SAY("\n");
|
SAY("\n");
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue