diff --git a/CHANGES b/CHANGES index 0c75106..5a77fbb 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,18 @@ security: + Socat security advisory 8 + A stack overflow in vulnerability was found that can be triggered when + command line arguments (complete address specifications, host names, + file names) are longer than 512 bytes. + Successful exploitation might allow an attacker to execute arbitrary + code with the privileges of the socat process. + This vulnerability can only be exploited when an attacker is able to + inject data into socat's command line. + A vulnerable scenario would be a CGI script that reads data from clients + and uses (parts of) this data as hostname for a Socat invocation. + Test: NESTEDOVFL + Credits to Takumi Akiyama for finding and reporting this issue. + Socat security advisory 7 MSVR-1499 In the OpenSSL address implementation the hard coded 1024 bit DH p diff --git a/nestlex.c b/nestlex.c index f8b7c3c..f7950f0 100644 --- a/nestlex.c +++ b/nestlex.c @@ -1,5 +1,5 @@ /* source: nestlex.c */ -/* Copyright Gerhard Rieger 2006-2010 */ +/* Copyright Gerhard Rieger */ /* Published under the GNU General Public License V.2, see file COPYING */ /* a function for lexical scanning of nested character patterns */ @@ -9,6 +9,18 @@ #include "sysincludes.h" +static int _nestlex(const char **addr, + char **token, + ptrdiff_t *len, + const char *ends[], + const char *hquotes[], + const char *squotes[], + const char *nests[], + bool dropspace, + bool dropquotes, + bool c_esc, + bool html_esc + ); /* sub: scan a string and copy its value to output string end scanning when an unescaped, unnested string from ends array is found @@ -34,6 +46,23 @@ int nestlex(const char **addr, /* input string; aft points to end token */ bool c_esc, /* solve C char escapes: \n \t \0 etc */ bool html_esc /* solve HTML char escapes: %0d %08 etc */ ) { + return + _nestlex(addr, token, (ptrdiff_t *)len, ends, hquotes, squotes, nests, + dropspace, dropquotes, c_esc, html_esc); +} + +static int _nestlex(const char **addr, + char **token, + ptrdiff_t *len, + const char *ends[], + const char *hquotes[], + const char *squotes[], + const char *nests[], + bool dropspace, + bool dropquotes, + bool c_esc, + bool html_esc + ) { const char *in = *addr; /* pointer into input string */ const char **endx; /* loops over end patterns */ const char **quotx; /* loops over quote patterns */ @@ -84,16 +113,18 @@ int nestlex(const char **addr, /* input string; aft points to end token */ if (--*len <= 0) { *addr = in; *token = out; return -1; } } } - /* we call nestlex recursively */ + /* we call _nestlex recursively */ endnest[0] = *quotx; endnest[1] = NULL; result = - nestlex(&in, &out, len, endnest, NULL/*hquotes*/, + _nestlex(&in, &out, len, endnest, NULL/*hquotes*/, NULL/*squotes*/, NULL/*nests*/, false, false, c_esc, html_esc); if (result == 0 && dropquotes) { /* we strip this quote */ in += strlen(*quotx); + } else if (result < 0) { + *addr = in; *token = out; return result; } else { /* we copy the trailing quote */ for (i = strlen(*quotx); i > 0; --i) { @@ -118,7 +149,7 @@ int nestlex(const char **addr, /* input string; aft points to end token */ if (!strncmp(in, *quotx, strlen(*quotx))) { /* this quote pattern matches */ /* we strip this quote */ - /* we call nestlex recursively */ + /* we call _nestlex recursively */ const char *endnest[2]; if (dropquotes) { /* we strip this quote */ @@ -132,13 +163,15 @@ int nestlex(const char **addr, /* input string; aft points to end token */ endnest[0] = *quotx; endnest[1] = NULL; result = - nestlex(&in, &out, len, endnest, hquotes, + _nestlex(&in, &out, len, endnest, hquotes, squotes, nests, false, false, c_esc, html_esc); if (result == 0 && dropquotes) { /* we strip the trailing quote */ in += strlen(*quotx); + } else if (result < 0) { + *addr = in; *token = out; return result; } else { /* we copy the trailing quote */ for (i = strlen(*quotx); i > 0; --i) { @@ -171,7 +204,7 @@ int nestlex(const char **addr, /* input string; aft points to end token */ } result = - nestlex(&in, &out, len, endnest, hquotes, squotes, nests, + _nestlex(&in, &out, len, endnest, hquotes, squotes, nests, false, false, c_esc, html_esc); if (result == 0) { /* copy endnest */ @@ -184,6 +217,8 @@ int nestlex(const char **addr, /* input string; aft points to end token */ } --i; } + } else if (result < 0) { + *addr = in; *token = out; return result; } break; } @@ -221,7 +256,7 @@ int nestlex(const char **addr, /* input string; aft points to end token */ } *out++ = c; --*len; - if (*len == 0) { + if (*len <= 0) { *addr = in; *token = out; return -1; /* output overflow */ @@ -233,7 +268,7 @@ int nestlex(const char **addr, /* input string; aft points to end token */ /* just a simple char */ *out++ = c; --*len; - if (*len == 0) { + if (*len <= 0) { *addr = in; *token = out; return -1; /* output overflow */ diff --git a/nestlex.h b/nestlex.h index 9d08d5e..80501f4 100644 --- a/nestlex.h +++ b/nestlex.h @@ -1,5 +1,5 @@ /* source: nestlex.h */ -/* Copyright Gerhard Rieger 2006-2007 */ +/* Copyright Gerhard Rieger */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __nestlex_h_included diff --git a/test.sh b/test.sh index a855436..2f7345b 100755 --- a/test.sh +++ b/test.sh @@ -11403,6 +11403,42 @@ esac PORT=$((PORT+1)) N=$((N+1)) +# socat up to 1.7.3.0 and to 2.0.0-b8 had a stack overflow vulnerability that occurred when +# command line arguments (whole addresses, host names, file names) were longer +# than 512 bytes and specially crafted. +NAME=NESTEDOVFL +case "$TESTS" in +*%$N%*|*%functions%*|*%bugs%*|*%security%*|*%exec%*|*%$NAME%*) +TEST="$NAME: stack overflow on overly long nested arg" +# provide a long host name to TCP-CONNECT and check socats exit code +if ! eval $NUMCOND; then :; else +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="test$N $(date) $RANDOM" +# prepare long data - perl might not be installed +rm -f "$td/test$N.dat" +i=0; while [ $i -lt 64 ]; do echo -n "AAAAAAAAAAAAAAAA" >>"$td/test$N.dat"; i=$((i+1)); done +CMD0="$TRACE $SOCAT $opts EXEC:[$(cat "$td/test$N.dat")] STDIO" +printf "test $F_n $TEST... " $N +$CMD0 &0 2>"${te}0" +rc0=$? +if [ $rc0 -lt 128 ] || [ $rc0 -eq 255 ]; then + $PRINTF "$OK\n" + numOK=$((numOK+1)) +else + $PRINTF "$FAILED\n" + echo "$CMD0" + cat "${te}0" + numFAIL=$((numFAIL+1)) + listFAIL="$listFAIL $N" +fi +fi # NUMCOND + ;; +esac +PORT=$((PORT+1)) +N=$((N+1)) + # test for a bug in gopen that lead to crash or warning when opening a unix # domain socket with GOPEN