version 2.0.0-b5 - fixed READLINE buffer overflow

This commit is contained in:
Gerhard Rieger 2012-04-24 07:35:42 +02:00
parent e3788fa8de
commit 2d24bc3157
4 changed files with 71 additions and 16 deletions

13
CHANGES
View file

@ -1,4 +1,17 @@
####################### V 2.0.0-b5:
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 2.0.0-b4:
security:

View file

@ -1 +1 @@
"2.0.0-b4"
"2.0.0-b5"

46
test.sh
View file

@ -1,6 +1,6 @@
#! /bin/bash
# source: test.sh
# Copyright Gerhard Rieger 2001-2009
# Copyright Gerhard Rieger 2001-2012
# Published under the GNU General Public License V.2, see file COPYING
# perform lots of tests on socat
@ -10528,7 +10528,7 @@ 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/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
CMD0="$SOCAT $opts TCP-CONNECT:$(cat "$td/test$N.dat"):$PORT STDIO"
printf "test $F_n $TEST... " $N
@ -10584,6 +10584,48 @@ PORT=$((PORT+1))
N=$((N+1))
# socat up to 1.7.2.0 and 2.0.0-b4 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))
echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed"

View file

@ -1,5 +1,5 @@
/* source: xio-readline.c */
/* Copyright Gerhard Rieger 2002-2009 */
/* Copyright Gerhard Rieger 2002-2012 */
/* Published under the GNU General Public License V.2, see file COPYING */
/* this file contains the source for opening the readline address */
@ -224,25 +224,26 @@ void xioscan_readline(struct single *pipe, const void *buff, size_t bytes) {
if (pipe->dtype == XIODATA_READLINE && pipe->para.readline.dynprompt) {
/* we save the last part of the output as possible prompt */
const void *ptr = buff;
const void *pcr = memrchr(buff, '\r', bytes);
const void *plf = memrchr(buff, '\n', bytes);
const void *pcr;
const void *plf;
size_t len;
if (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 */
pipe->para.readline.dynend = pipe->para.readline.dynprompt;
len -= (peol+1 - ptr);
/* 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 >
pipe->para.readline.dynbytes) {
memmove(pipe->para.readline.dynprompt,
@ -253,7 +254,6 @@ void xioscan_readline(struct single *pipe, const void *buff, size_t bytes) {
pipe->para.readline.dynprompt + pipe->para.readline.dynbytes - 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;
}
return;