version 1.7.2.1 - fixed READLINE buffer overflow

This commit is contained in:
Gerhard Rieger 2012-04-24 07:30:01 +02:00
parent 42ef3f4645
commit 464d23a34f
4 changed files with 70 additions and 16 deletions

13
CHANGES
View file

@ -1,4 +1,17 @@
####################### V 1.7.2.1:
security:
fixed a possible heap buffer overflow in the readline address. This bug
could be exploited when all of the following conditions were met:
1) one of the addresses is READLINE without the noprompt and without the
prompt options.
2) the other (almost arbitrary address) reads malicious data (which is
then transferred by socat to READLINE).
Workaround: when using the READLINE address apply option prompt or
noprompt.
Full credits to Johan Thillemann for finding and reporting this issue.
####################### V 1.7.2.0: ####################### V 1.7.2.0:
corrections: corrections:

View file

@ -1 +1 @@
"1.7.2.0" "1.7.2.1"

45
test.sh
View file

@ -1,6 +1,6 @@
#! /bin/bash #! /bin/bash
# source: test.sh # source: test.sh
# Copyright Gerhard Rieger 2001-2011 # Copyright Gerhard Rieger 2001-2012
# Published under the GNU General Public License V.2, see file COPYING # Published under the GNU General Public License V.2, see file COPYING
# perform lots of tests on socat # perform lots of tests on socat
@ -10465,7 +10465,7 @@ te="$td/test$N.stderr"
tdiff="$td/test$N.diff" tdiff="$td/test$N.diff"
da="test$N $(date) $RANDOM" da="test$N $(date) $RANDOM"
# prepare long data - perl might not be installed # prepare long data - perl might not be installed
rm -f "$td/terst$N.dat" rm -f "$td/test$N.dat"
i=0; while [ $i -lt 64 ]; do echo -n "AAAAAAAAAAAAAAAA" >>"$td/test$N.dat"; i=$((i+1)); done i=0; while [ $i -lt 64 ]; do echo -n "AAAAAAAAAAAAAAAA" >>"$td/test$N.dat"; i=$((i+1)); done
CMD0="$SOCAT $opts TCP-CONNECT:$(cat "$td/test$N.dat"):$PORT STDIO" CMD0="$SOCAT $opts TCP-CONNECT:$(cat "$td/test$N.dat"):$PORT STDIO"
printf "test $F_n $TEST... " $N printf "test $F_n $TEST... " $N
@ -10776,6 +10776,47 @@ esac
N=$((N+1)) N=$((N+1))
# socat up to 1.7.2.0 had a bug in xioscan_readline() that could be exploited
# to overflow a heap based buffer (socat security advisory 3)
# problem reported by Johan Thillemann
NAME=READLINE_OVFL
case "$TESTS" in
*%functions%*|*%bugs%*|*%security%*|*%$NAME%*)
TEST="$NAME: test for buffer overflow in readline prompt handling"
# address 1 is the readline where write data was handled erroneous
# address 2 provides data to trigger the buffer overflow
# when no SIGSEGV or so occurs the test succeeded (bug fixed)
if ! eval $NUMCOND; then :; else
tf="$td/test$N.stdout"
te="$td/test$N.stderr"
ti="$td/test$N.data"
CMD0="$SOCAT $opts READLINE $ti"
printf "test $F_n $TEST... " $N
# prepare long data - perl might not be installed
#perl -e 'print "\r","Z"x513' >"$ti"
echo $E -n "\rA" >"$ti"
i=0; while [ $i -lt 32 ]; do echo -n "AAAAAAAAAAAAAAAA" >>"$ti"; let i=i+1; done
$SOCAT - system:"$CMD0; echo rc=\$? >&2",pty >/dev/null 2>"${te}0"
rc=$?
rc0="$(grep ^rc= "${te}0" |sed 's/.*=//')"
if [ $rc -ne 0 ]; then
$PRINTF "${YELLOW}framework failed${NORMAL}\n"
elif [ $rc0 -eq 0 ]; then
$PRINTF "$OK\n"
numOK=$((numOK+1))
else
$PRINTF "$FAILED\n"
echo "$CMD0"
grep -v ^rc= "${te}0"
numFAIL=$((numFAIL+1))
fi
fi # NUMCOND
;;
esac
PORT=$((PORT+1))
N=$((N+1))
############################################################################### ###############################################################################
# here come tests that might affect your systems integrity. Put normal tests # here come tests that might affect your systems integrity. Put normal tests
# before this paragraph. # before this paragraph.

View file

@ -1,5 +1,5 @@
/* source: xio-readline.c */ /* source: xio-readline.c */
/* Copyright Gerhard Rieger 2002-2011 */ /* Copyright Gerhard Rieger 2002-2012 */
/* Published under the GNU General Public License V.2, see file COPYING */ /* Published under the GNU General Public License V.2, see file COPYING */
/* this file contains the source for opening the readline address */ /* this file contains the source for opening the readline address */
@ -214,25 +214,26 @@ void xioscan_readline(struct single *pipe, const void *buff, size_t bytes) {
if (pipe->dtype == XIODATA_READLINE && pipe->para.readline.dynprompt) { if (pipe->dtype == XIODATA_READLINE && pipe->para.readline.dynprompt) {
/* we save the last part of the output as possible prompt */ /* we save the last part of the output as possible prompt */
const void *ptr = buff; const void *ptr = buff;
const void *pcr = memrchr(buff, '\r', bytes); const void *pcr;
const void *plf = memrchr(buff, '\n', bytes); const void *plf;
size_t len; size_t len;
if (bytes > pipe->para.readline.dynbytes) { if (bytes > pipe->para.readline.dynbytes) {
ptr = (const char *)buff + bytes - pipe->para.readline.dynbytes; ptr = (const char *)buff + bytes - pipe->para.readline.dynbytes;
len = pipe->para.readline.dynbytes;
} else {
len = bytes;
} }
if (pcr) { pcr = memrchr(ptr, '\r', len);
plf = memrchr(ptr, '\n', len);
if (pcr != NULL || plf != NULL) {
const void *peol = Max(pcr, plf);
/* forget old prompt */ /* forget old prompt */
pipe->para.readline.dynend = pipe->para.readline.dynprompt; pipe->para.readline.dynend = pipe->para.readline.dynprompt;
len -= (peol+1 - ptr);
/* new prompt starts here */ /* new prompt starts here */
ptr = (const char *)pcr+1; ptr = (const char *)peol+1;
} }
if (plf && plf >= ptr) {
/* forget old prompt */
pipe->para.readline.dynend = pipe->para.readline.dynprompt;
/* new prompt starts here */
ptr = (const char *)plf+1;
}
len = (const char *)buff-(const char *)ptr+bytes;
if (pipe->para.readline.dynend - pipe->para.readline.dynprompt + len > if (pipe->para.readline.dynend - pipe->para.readline.dynprompt + len >
pipe->para.readline.dynbytes) { pipe->para.readline.dynbytes) {
memmove(pipe->para.readline.dynprompt, memmove(pipe->para.readline.dynprompt,
@ -243,7 +244,6 @@ void xioscan_readline(struct single *pipe, const void *buff, size_t bytes) {
pipe->para.readline.dynprompt + pipe->para.readline.dynbytes - len; pipe->para.readline.dynprompt + pipe->para.readline.dynbytes - len;
} }
memcpy(pipe->para.readline.dynend, ptr, len); memcpy(pipe->para.readline.dynend, ptr, len);
/*pipe->para.readline.dynend = pipe->para.readline.dynprompt + len;*/
pipe->para.readline.dynend = pipe->para.readline.dynend + len; pipe->para.readline.dynend = pipe->para.readline.dynend + len;
} }
return; return;