1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

urldecode forbid malformed

And update attack.sh to confirm the new test cases

Signed-off-by: Andy Green <andy@warmcat.com>
This commit is contained in:
Andy Green 2016-04-07 10:08:35 +08:00
parent fd12fc2781
commit 150233d61f
4 changed files with 535 additions and 50 deletions

View file

@ -586,6 +586,23 @@ lws_parse(struct lws *wsi, unsigned char c)
if (issue_char(wsi, '/') < 0)
return -1;
if (wsi->u.hdr.ups == URIPS_SEEN_SLASH_DOT_DOT) {
/*
* back up one dir level if possible
* safe against header fragmentation because
* the method URI can only be in 1 fragment
*/
if (ah->frags[ah->nfrag].len > 2) {
ah->pos--;
ah->frags[ah->nfrag].len--;
do {
ah->pos--;
ah->frags[ah->nfrag].len--;
} while (ah->frags[ah->nfrag].len > 1 &&
ah->data[ah->pos] != '/');
}
}
/* begin parsing HTTP version: */
if (issue_char(wsi, '\0') < 0)
return -1;
@ -593,7 +610,10 @@ lws_parse(struct lws *wsi, unsigned char c)
goto start_fragment;
}
/* special URI processing... convert %xx */
/*
* PRIORITY 1
* special URI processing... convert %xx
*/
switch (wsi->u.hdr.ues) {
case URIES_IDLE:
@ -603,30 +623,19 @@ lws_parse(struct lws *wsi, unsigned char c)
}
break;
case URIES_SEEN_PERCENT:
if (char_to_hex(c) < 0) {
/* regurgitate */
if (issue_char(wsi, '%') < 0)
return -1;
wsi->u.hdr.ues = URIES_IDLE;
/* continue on to assess c */
break;
}
if (char_to_hex(c) < 0)
/* illegal post-% char */
goto forbid;
wsi->u.hdr.esc_stash = c;
wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1;
goto swallow;
case URIES_SEEN_PERCENT_H1:
if (char_to_hex(c) < 0) {
/* regurgitate */
if (issue_char(wsi, '%') < 0)
return -1;
wsi->u.hdr.ues = URIES_IDLE;
/* regurgitate + assess */
if (lws_parse(wsi, wsi->u.hdr.esc_stash) < 0)
return -1;
/* continue on to assess c */
break;
}
if (char_to_hex(c) < 0)
/* illegal post-% char */
goto forbid;
c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) |
char_to_hex(c);
enc = 1;
@ -635,6 +644,7 @@ lws_parse(struct lws *wsi, unsigned char c)
}
/*
* PRIORITY 2
* special URI processing...
* convert /.. or /... or /../ etc to /
* convert /./ to /
@ -693,20 +703,6 @@ lws_parse(struct lws *wsi, unsigned char c)
case URIPS_SEEN_SLASH_DOT:
/* swallow second . */
if (c == '.') {
/*
* back up one dir level if possible
* safe against header fragmentation because
* the method URI can only be in 1 fragment
*/
if (ah->frags[ah->nfrag].len > 2) {
ah->pos--;
ah->frags[ah->nfrag].len--;
do {
ah->pos--;
ah->frags[ah->nfrag].len--;
} while (ah->frags[ah->nfrag].len > 1 &&
ah->data[ah->pos] != '/');
}
wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT;
goto swallow;
}
@ -722,19 +718,44 @@ lws_parse(struct lws *wsi, unsigned char c)
break;
case URIPS_SEEN_SLASH_DOT_DOT:
/* swallow prior .. chars and any subsequent . */
if (c == '.')
/* /../ or /..[End of URI] --> backup to last / */
if (c == '/' || c == '?') {
/*
* back up one dir level if possible
* safe against header fragmentation because
* the method URI can only be in 1 fragment
*/
if (ah->frags[ah->nfrag].len > 2) {
ah->pos--;
ah->frags[ah->nfrag].len--;
do {
ah->pos--;
ah->frags[ah->nfrag].len--;
} while (ah->frags[ah->nfrag].len > 1 &&
ah->data[ah->pos] != '/');
}
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
if (ah->frags[ah->nfrag].len > 1)
break;
goto swallow;
/* last issued was /, so another / == // */
if (c == '/')
goto swallow;
/* last we issued was / so SEEN_SLASH */
wsi->u.hdr.ups = URIPS_SEEN_SLASH;
}
/* /..[^/] ... regurgitate and allow */
if (issue_char(wsi, '.') < 0)
return -1;
if (issue_char(wsi, '.') < 0)
return -1;
wsi->u.hdr.ups = URIPS_IDLE;
break;
}
if (c == '?' && !enc &&
!ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */
if (wsi->u.hdr.ues != URIES_IDLE)
goto forbid;
/* seal off uri header */
if (issue_char(wsi, '\0') < 0)
return -1;
@ -754,10 +775,12 @@ lws_parse(struct lws *wsi, unsigned char c)
}
check_eol:
/* bail at EOL */
if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE &&
c == '\x0d') {
if (wsi->u.hdr.ues != URIES_IDLE)
goto forbid;
c = '\0';
wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR;
lwsl_parser("*\n");
@ -803,7 +826,7 @@ swallow:
*/
if (m == ARRAY_SIZE(methods)) {
lwsl_info("Unknown method - dropping\n");
return -1;
goto forbid;
}
break;
}
@ -891,6 +914,8 @@ excessive:
case WSI_TOKEN_SKIPPING_SAW_CR:
lwsl_parser("WSI_TOKEN_SKIPPING_SAW_CR '%c'\n", c);
if (wsi->u.hdr.ues != URIES_IDLE)
goto forbid;
if (c == '\x0a') {
wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART;
wsi->u.hdr.lextable_pos = 0;
@ -907,7 +932,8 @@ excessive:
return 0;
set_parsing_complete:
if (wsi->u.hdr.ues != URIES_IDLE)
goto forbid;
if (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {
if (lws_hdr_total_length(wsi, WSI_TOKEN_VERSION))
wsi->ietf_spec_revision =
@ -919,6 +945,11 @@ set_parsing_complete:
wsi->hdr_parsing_completed = 1;
return 0;
forbid:
lwsl_notice(" forbidding on uri sanitation\n");
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
return -1;
}

View file

@ -376,7 +376,7 @@ lws_http_action(struct lws *wsi)
goto bail_nuke_ah;
if (lws_add_http_header_status(wsi, 301, &p, end))
goto bail_nuke_ah;
n = sprintf((char *)end, "htt struct lws_http_mount *hm;ps://%s/",
n = sprintf((char *)end, "https://%s/",
lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));
if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION,
end, n, &p, end))

View file

@ -35,6 +35,14 @@ function check {
fi
fi
if [ "$1" = "rejected" ] ; then
if [ -z "`grep '<h1>406</h1>' /tmp/lwscap`" ] ; then
echo "FAIL: should have told forbidden (test server has no dirs)"
exit 1
fi
fi
if [ "$1" = "media" ] ; then
if [ -z "`grep '<h1>415</h1>' /tmp/lwscap`" ] ; then
echo "FAIL: should have told unknown media type"
@ -73,7 +81,7 @@ function check {
rm -rf $LOG
killall libwebsockets-test-server 2>/dev/null
libwebsockets-test-server -d31 2>> $LOG &
libwebsockets-test-server -d15 2>> $LOG &
CPID=$!
while [ -z "`grep Listening $LOG`" ] ; do
@ -233,7 +241,7 @@ echo
echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)"
rm -f /tmp/lwscap
echo -e "GET /../../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check forbidden
check rejected
check
echo
@ -275,14 +283,14 @@ echo
echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/passswd)"
rm -f /tmp/lwscap
echo -e "GET /%2e%2e%2f../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check forbidden
check rejected
check
echo
echo "---- directory attack 8 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be /etc/passswd)"
rm -f /tmp/lwscap
echo -e "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap
check forbidden
check rejected
check
echo
@ -295,6 +303,449 @@ if [ "$good" != "`md5sum /tmp/lwsdump | cut -d' ' -f 1`" ] ; then
exit 1
fi
echo
echo "---- mass testing uri variations"
rm -f /tmp/results
for i in \
/..../ \
/.../. \
/...// \
/.../a \
/.../w \
/.../? \
/.../% \
/../.. \
/.././ \
/../.a \
/../.w \
/../.. \
/../.% \
/..//. \
/../// \
/..//a \
/..//w \
/..//? \
/..//% \
/../a. \
/../a/ \
/../aa \
/../aw \
/../a? \
/../a% \
/../w. \
/../w/ \
/../wa \
/../ww \
/../w? \
/../w% \
/../?. \
/../?/ \
/../?a \
/../?w \
/../?? \
/../?% \
/../%. \
/../%/ \
/../%a \
/../%w \
/../%? \
/../%% \
/./... \
/./../ \
/./..a \
/./..w \
/./..? \
/./..% \
/.//.. \
/.a../ \
/.a/.. \
/.w../ \
/.w/.. \
/.?../ \
/../.. \
/.%../ \
/.%/.. \
//.... \
//.../ \
//...a \
//...w \
//...? \
//...% \
//../. \
//..// \
//../a \
//../w \
//../? \
//../% \
//..a. \
//..a/ \
//..aa \
//..aw \
//..a? \
//..a% \
//..w. \
//..w/ \
//..wa \
//..ww \
//..w? \
//..w% \
//..?. \
//..?/ \
//..?a \
//..?w \
//..?? \
//..?% \
//..%. \
//..%/ \
//..%a \
//..%w \
//..%? \
//..%% \
//./.. \
///... \
///../ \
///..a \
///..w \
///..? \
///..% \
////.. \
//a../ \
//a/.. \
//w../ \
//w/.. \
//?../ \
//?/.. \
//%../ \
//%/.. \
/a.../ \
/a../. \
/a..// \
/a../a \
/a../w \
/a../? \
/a../% \
/a./.. \
/a/... \
/a/../ \
/a/..a \
/a/..w \
/a/..? \
/a/..% \
/a//.. \
/aa../ \
/aa/.. \
/aw../ \
/aw/.. \
/a?../ \
/a?/.. \
/a%../ \
/a%/.. \
/w.../ \
/w../. \
/w..// \
/w../a \
/w../w \
/w../? \
/w../% \
/w./.. \
/w/... \
/w/../ \
/w/..a \
/w/..w \
/w/..? \
/w/..% \
/w//.. \
/wa../ \
/wa/.. \
/ww../ \
/ww/.. \
/w?../ \
/w?/.. \
/w%../ \
/w%/.. \
/?.../ \
/?../. \
/?..// \
/?../a \
/?../w \
/?../? \
/?../% \
/?./.. \
/?/... \
/?/../ \
/?/..a \
/?/..w \
/?/..? \
/?/..% \
/?//.. \
/?a../ \
/?a/.. \
/?w../ \
/?w/.. \
/??../ \
/??/.. \
/?%../ \
/?%/.. \
/%.../ \
/%../. \
/%..// \
/%../a \
/%../w \
/%../? \
/%../% \
/%./.. \
/%/... \
/%/../ \
/%/..a \
/%/..w \
/%/..? \
/%/..% \
/%//.. \
/%a../ \
/%a/.. \
/%w../ \
/%w/.. \
/%?../ \
/%?/.. \
/%%../ \
/%%/.. \
/a/w/../a \
/path/to/dir/../other/dir \
; do
R=`rm -f /tmp/lwscap ; echo -n -e "GET $i HTTP/1.0\r\n\r\n" | nc localhost 7681 2>/dev/null >/tmp/lwscap; head -n1 /tmp/lwscap| cut -d' ' -f2`
cat /tmp/lwscap | head -n1
echo ==== $R
if [ "$R" != "403" ]; then
U=`cat $LOG | grep lws_http_serve | tail -n 1 | cut -d':' -f3 | cut -d' ' -f2`
echo $U
echo "- \"$i\" -> $R \"$U\"" >>/tmp/results
else
echo "- \"$i\" -> $R" >>/tmp/results
fi
done
cat <<EOF >/tmp/lwsresult1
- "/..../" -> 406 "/..../"
- "/.../." -> 406 "/.../"
- "/...//" -> 406 "/.../"
- "/.../a" -> 406 "/.../a"
- "/.../w" -> 406 "/.../w"
- "/.../?" -> 406 "/.../"
- "/.../%" -> 403
- "/../.." -> 200 "/"
- "/.././" -> 200 "/"
- "/../.a" -> 415 "/.a"
- "/../.w" -> 415 "/.w"
- "/../.." -> 200 "/"
- "/../.%" -> 403
- "/..//." -> 200 "/"
- "/..///" -> 200 "/"
- "/..//a" -> 415 "/a"
- "/..//w" -> 415 "/w"
- "/..//?" -> 200 "/"
- "/..//%" -> 403
- "/../a." -> 415 "/a."
- "/../a/" -> 406 "/a/"
- "/../aa" -> 415 "/aa"
- "/../aw" -> 415 "/aw"
- "/../a?" -> 415 "/a"
- "/../a%" -> 403
- "/../w." -> 415 "/w."
- "/../w/" -> 406 "/w/"
- "/../wa" -> 415 "/wa"
- "/../ww" -> 415 "/ww"
- "/../w?" -> 415 "/w"
- "/../w%" -> 403
- "/../?." -> 200 "/"
- "/../?/" -> 200 "/"
- "/../?a" -> 200 "/"
- "/../?w" -> 200 "/"
- "/../??" -> 200 "/"
- "/../?%" -> 403
- "/../%." -> 403
- "/../%/" -> 403
- "/../%a" -> 403
- "/../%w" -> 403
- "/../%?" -> 403
- "/../%%" -> 403
- "/./..." -> 415 "/..."
- "/./../" -> 200 "/"
- "/./..a" -> 415 "/..a"
- "/./..w" -> 415 "/..w"
- "/./..?" -> 200 "/"
- "/./..%" -> 403
- "/.//.." -> 200 "/"
- "/.a../" -> 406 "/.a../"
- "/.a/.." -> 200 "/"
- "/.w../" -> 406 "/.w../"
- "/.w/.." -> 200 "/"
- "/.?../" -> 415 "/."
- "/../.." -> 200 "/"
- "/.%../" -> 403
- "/.%/.." -> 403
- "//...." -> 415 "/...."
- "//.../" -> 406 "/.../"
- "//...a" -> 415 "/...a"
- "//...w" -> 415 "/...w"
- "//...?" -> 415 "/..."
- "//...%" -> 403
- "//../." -> 200 "/"
- "//..//" -> 200 "/"
- "//../a" -> 415 "/a"
- "//../w" -> 415 "/w"
- "//../?" -> 200 "/"
- "//../%" -> 403
- "//..a." -> 415 "/..a."
- "//..a/" -> 406 "/..a/"
- "//..aa" -> 415 "/..aa"
- "//..aw" -> 415 "/..aw"
- "//..a?" -> 415 "/..a"
- "//..a%" -> 403
- "//..w." -> 415 "/..w."
- "//..w/" -> 406 "/..w/"
- "//..wa" -> 415 "/..wa"
- "//..ww" -> 415 "/..ww"
- "//..w?" -> 415 "/..w"
- "//..w%" -> 403
- "//..?." -> 200 "/"
- "//..?/" -> 200 "/"
- "//..?a" -> 415 "/a"
- "//..?w" -> 415 "/w"
- "//..??" -> 200 "/"
- "//..?%" -> 403
- "//..%." -> 403
- "//..%/" -> 403
- "//..%a" -> 403
- "//..%w" -> 403
- "//..%?" -> 403
- "//..%%" -> 403
- "//./.." -> 200 "/"
- "///..." -> 415 "/..."
- "///../" -> 200 "/"
- "///..a" -> 415 "/..a"
- "///..w" -> 415 "/..w"
- "///..?" -> 200 "/"
- "///..%" -> 403
- "////.." -> 200 "/"
- "//a../" -> 406 "/a../"
- "//a/.." -> 200 "/"
- "//w../" -> 406 "/w../"
- "//w/.." -> 200 "/"
- "//?../" -> 200 "/"
- "//?/.." -> 200 "/"
- "//%../" -> 403
- "//%/.." -> 403
- "/a.../" -> 406 "/a.../"
- "/a../." -> 406 "/a../"
- "/a..//" -> 406 "/a../"
- "/a../a" -> 406 "/a../a"
- "/a../w" -> 406 "/a../w"
- "/a../?" -> 406 "/a../"
- "/a../%" -> 403
- "/a./.." -> 200 "/"
- "/a/..." -> 406 "/a/..."
- "/a/../" -> 200 "/"
- "/a/..a" -> 406 "/a/..a"
- "/a/..w" -> 406 "/a/..w"
- "/a/..?" -> 200 "/"
- "/a/..%" -> 403
- "/a//.." -> 200 "/"
- "/aa../" -> 406 "/aa../"
- "/aa/.." -> 200 "/"
- "/aw../" -> 406 "/aw../"
- "/aw/.." -> 200 "/"
- "/a?../" -> 415 "/a"
- "/a?/.." -> 415 "/a"
- "/a%../" -> 403
- "/a%/.." -> 403
- "/w.../" -> 406 "/w.../"
- "/w../." -> 406 "/w../"
- "/w..//" -> 406 "/w../"
- "/w../a" -> 406 "/w../a"
- "/w../w" -> 406 "/w../w"
- "/w../?" -> 406 "/w../"
- "/w../%" -> 403
- "/w./.." -> 200 "/"
- "/w/..." -> 406 "/w/..."
- "/w/../" -> 200 "/"
- "/w/..a" -> 406 "/w/..a"
- "/w/..w" -> 406 "/w/..w"
- "/w/..?" -> 200 "/"
- "/w/..%" -> 403
- "/w//.." -> 200 "/"
- "/wa../" -> 406 "/wa../"
- "/wa/.." -> 200 "/"
- "/ww../" -> 406 "/ww../"
- "/ww/.." -> 200 "/"
- "/w?../" -> 415 "/w"
- "/w?/.." -> 415 "/w"
- "/w%../" -> 403
- "/w%/.." -> 403
- "/?.../" -> 200 "/"
- "/?../." -> 200 "/"
- "/?..//" -> 200 "/"
- "/?../a" -> 200 "/"
- "/?../w" -> 200 "/"
- "/?../?" -> 200 "/"
- "/?../%" -> 403
- "/?./.." -> 200 "/"
- "/?/..." -> 200 "/"
- "/?/../" -> 200 "/"
- "/?/..a" -> 200 "/"
- "/?/..w" -> 200 "/"
- "/?/..?" -> 200 "/"
- "/?/..%" -> 403
- "/?//.." -> 200 "/"
- "/?a../" -> 200 "/"
- "/?a/.." -> 200 "/"
- "/?w../" -> 200 "/"
- "/?w/.." -> 200 "/"
- "/??../" -> 200 "/"
- "/??/.." -> 200 "/"
- "/?%../" -> 403
- "/?%/.." -> 403
- "/%.../" -> 403
- "/%../." -> 403
- "/%..//" -> 403
- "/%../a" -> 403
- "/%../w" -> 403
- "/%../?" -> 403
- "/%../%" -> 403
- "/%./.." -> 403
- "/%/..." -> 403
- "/%/../" -> 403
- "/%/..a" -> 403
- "/%/..w" -> 403
- "/%/..?" -> 403
- "/%/..%" -> 403
- "/%//.." -> 403
- "/%a../" -> 403
- "/%a/.." -> 403
- "/%w../" -> 403
- "/%w/.." -> 403
- "/%?../" -> 403
- "/%?/.." -> 403
- "/%%../" -> 403
- "/%%/.." -> 403
- "/a/w/../a" -> 406 "/a/a"
- "/path/to/dir/../other/dir" -> 406 "/path/to/other/dir"
EOF
if [ "`md5sum /tmp/results | cut -d' ' -f 1`" != "`md5sum /tmp/lwsresult1 | cut -d' ' -f1`" ] ; then
echo "Differences..."
diff -urN /tmp/results /tmp/lwsresult1
exit 1
else
echo "OK"
fi
echo
echo "--- survived OK ---"
kill -2 $CPID

View file

@ -140,9 +140,12 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
struct lws_pollargs *pa = (struct lws_pollargs *)in;
#endif
switch (reason) {
case LWS_CALLBACK_HTTP:
lwsl_notice("lws_http_serve: %s\n",in);
if (debug_level & LLL_INFO) {
dump_handshake_info(wsi);
@ -207,7 +210,7 @@ int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
#if 1
/* this example server has no concept of directories */
if (strchr((const char *)in + 1, '/')) {
lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);
lws_return_http_status(wsi, HTTP_STATUS_NOT_ACCEPTABLE, NULL);
goto try_to_reuse;
}
#endif