diff --git a/lib/header.c b/lib/header.c index 5daf0d84..b2d78288 100644 --- a/lib/header.c +++ b/lib/header.c @@ -24,9 +24,6 @@ const unsigned char *lws_token_to_string(enum lws_token_indexes token) { - if (token == WSI_TOKEN_HTTP_URI_ARGS) - return (unsigned char *)"Uri-Args:"; - if ((unsigned int)token >= ARRAY_SIZE(set)) return NULL; diff --git a/lib/parsers.c b/lib/parsers.c index d40eeb68..736aeed9 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -323,8 +323,24 @@ int lws_parse(struct lws *wsi, unsigned char c) switch (wsi->u.hdr.ups) { case URIPS_IDLE: + /* genuine delimiter */ + if (c == '&' && !enc) { + issue_char(wsi, c); + /* swallow the terminator */ + ah->frags[ah->nfrag].len--; + /* link to next fragment */ + ah->frags[ah->nfrag].nfrag = ah->nfrag + 1; + ah->nfrag++; + if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + goto excessive; + /* start next fragment after the & */ + ah->frags[ah->nfrag].offset = ah->pos; + ah->frags[ah->nfrag].len = 0; + ah->frags[ah->nfrag].nfrag = 0; + goto swallow; + } /* issue the first / always */ - if (c == '/') + if (c == '/' && !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) wsi->u.hdr.ups = URIPS_SEEN_SLASH; break; case URIPS_SEEN_SLASH: @@ -365,7 +381,8 @@ int lws_parse(struct lws *wsi, unsigned char c) } /* it was like /.dir ... regurgitate the . */ wsi->u.hdr.ups = URIPS_IDLE; - issue_char(wsi, '.'); + if (issue_char(wsi, '.') < 0) + return -1; break; case URIPS_SEEN_SLASH_DOT_DOT: @@ -380,19 +397,20 @@ int lws_parse(struct lws *wsi, unsigned char c) break; } - if (c == '?' && !enc) { /* start of URI arguments */ + if (c == '?' && !enc && + !ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS]) { /* start of URI arguments */ /* seal off uri header */ ah->data[ah->pos++] = '\0'; /* move to using WSI_TOKEN_HTTP_URI_ARGS */ ah->nfrag++; + if (ah->nfrag >= ARRAY_SIZE(ah->frags)) + goto excessive; ah->frags[ah->nfrag].offset = ah->pos; ah->frags[ah->nfrag].len = 0; ah->frags[ah->nfrag].nfrag = 0; ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = ah->nfrag; - - /* defeat normal uri path processing */ wsi->u.hdr.ups = URIPS_IDLE; goto swallow; } @@ -501,6 +519,7 @@ swallow: start_fragment: ah->nfrag++; +excessive: if (ah->nfrag == ARRAY_SIZE(ah->frags)) { lwsl_warn("More hdr frags than we can deal with\n"); return -1; diff --git a/test-server/attack.sh b/test-server/attack.sh index d9defec2..dab8be77 100755 --- a/test-server/attack.sh +++ b/test-server/attack.sh @@ -35,14 +35,32 @@ function check { fi fi - if [ "$1" == "args" ] ; then - a="`dd if=$LOG bs=1 skip=$LEN 2>/dev/null |grep Uri.Args\: | tr -s ' ' | cut -d' ' -f4-`" + if [ "$1" == "1" ] ; then + a="`dd if=$LOG bs=1 skip=$LEN 2>/dev/null |grep URI\ Arg\ 1\: | tr -s ' ' | cut -d' ' -f5-`" if [ "$a" != "$2" ] ; then - echo "Args '$a' not $2" + echo "Arg 1 '$a' not $2" exit 1 fi fi - LEN=`stat $LOG -c %s` + + if [ "$1" == "2" ] ; then + a="`dd if=$LOG bs=1 skip=$LEN 2>/dev/null |grep URI\ Arg\ 2\: | tr -s ' ' | cut -d' ' -f5-`" + if [ "$a" != "$2" ] ; then + echo "Arg 2 '$a' not $2" + exit 1 + fi + fi + if [ "$1" == "3" ] ; then + a="`dd if=$LOG bs=1 skip=$LEN 2>/dev/null |grep URI\ Arg\ 3\: | tr -s ' ' | cut -d' ' -f5-`" + if [ "$a" != "$2" ] ; then + echo "Arg 3 '$a' not $2" + exit 1 + fi + fi + + if [ -z "$1" ] ; then + LEN=`stat $LOG -c %s` + fi } @@ -60,22 +78,24 @@ echo echo "---- /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F" rm -f /tmp/lwscap echo -e "GET /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap -check args "UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=?&Root_Channels_1_Channel_location_http_post=?" - - +check 1 "UPDATE_SETTINGS=1" +check 2 "Root_Channels_1_Channel_name_http_post=?" +check 3 "Root_Channels_1_Channel_location_http_post=?" +check echo echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=1)" rm -f /tmp/lwscap echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap -check args "arg=1" +check 1 "arg=1" +check echo echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=/../.)" rm -f /tmp/lwscap echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap -check args "arg=/../." - +check 1 "arg=/../." +check echo echo "---- spam enough crap to not be GET" @@ -172,55 +192,63 @@ echo -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD................. "......................................................................................................................." \ | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap check default +check 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 echo echo "---- directory attack 2 (/../ should be /)" rm -f /tmp/lwscap echo -e "GET /../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap check default +check echo echo "---- directory attack 3 (/./ should be /)" rm -f /tmp/lwscap echo -e "GET /./ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap check default +check echo echo "---- directory attack 4 (/blah/.. should be /)" rm -f /tmp/lwscap echo -e "GET /blah/.. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap check default +check echo echo "---- directory attack 5 (/blah/../ should be /)" rm -f /tmp/lwscap echo -e "GET /blah/../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap check default +check echo echo "---- directory attack 6 (/blah/../. should be /)" rm -f /tmp/lwscap echo -e "GET /blah/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap check default +check 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 echo echo "---- directory attack 7 (%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 echo echo "--- survived OK ---" diff --git a/test-server/test-server-http.c b/test-server/test-server-http.c index 1100d071..52b0b0f2 100644 --- a/test-server/test-server-http.c +++ b/test-server/test-server-http.c @@ -137,6 +137,18 @@ int callback_http(struct lws_context *context, struct lws *wsi, dump_handshake_info(wsi); + /* dump the individual URI Arg parameters */ + m = 1; + n = 0; + while (m > 0) { + m = lws_hdr_copy_fragment(wsi, buf, sizeof(buf), + WSI_TOKEN_HTTP_URI_ARGS, n); + if (m < 0) + continue; + n++; + lwsl_info("URI Arg %d: %s\n", n, buf); + } + if (len < 1) { lws_return_http_status(context, wsi, HTTP_STATUS_BAD_REQUEST, NULL);