COMMAND
thhtpd
SYSTEMS AFFECTED
thttpd 2.04 (Suse 6.2 and 6.3 plus others)
PROBLEM
D. J. Bernstein found following. thttpd is a single-process web
server. According to Netcraft, it's used on 1.82% of all HTTP
servers, behind only Apache, IIS, Enterprise, and Rapidsite. The
current version is thttpd 2.04; as far as known and in time of
writing, the comments below apply to all versions back to 1.90a.
The thttpd web page says that thttpd is simple, small, portable,
fast, and secure; it ``goes to great lengths to protect the web
server machine against attacks and breakins from other sites.''
Sounds good, doesn't it? Bernstein glanced at the thttpd 2.04
source code, wondering how seriously thttpd parsed HTTP
If-Modified-Since fields. He was horrified to see that
tdate_parse() scans %[a-zA-Z] into a fixed-size stack buffer. He
tried running thttpd on a throwaway account, and feeding it an
If-Modified-Since line with 1300 x's. It dumped core. This is
something that any attacker on the Internet could do to any thttpd
server, taking down web service until thttpd is restarted.
Presumably, at least on little-endian machines, a careful attacker
can take over the thttpd server---i.e., take over web service, and
anything else running as ``nobody''---by overwriting only two or
three bytes of the return address.
SOLUTION
Fortunately the fix is trivial. The patch applied is below, and
you can find the full tarchive at the usual place
http://www.acme.com/software/thttpd/
Patch:
*** tdate_parse.c 1999/09/15 16:09:36 1.1
--- tdate_parse.c 1999/11/10 01:16:39
***************
*** 211,217 ****
*/
/* DD-mth-YY HH:MM:SS GMT */
! if ( sscanf( cp, "%d-%[a-zA-Z]-%d %d:%d:%d GMT",
&tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
&tm_sec ) == 6 &&
scan_mon( str_mon, &tm_mon ) )
--- 211,217 ----
*/
/* DD-mth-YY HH:MM:SS GMT */
! if ( sscanf( cp, "%d-%400[a-zA-Z]-%d %d:%d:%d GMT",
&tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
&tm_sec ) == 6 &&
scan_mon( str_mon, &tm_mon ) )
***************
*** 225,231 ****
}
/* DD mth YY HH:MM:SS GMT */
! else if ( sscanf( cp, "%d %[a-zA-Z] %d %d:%d:%d GMT",
&tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
&tm_sec) == 6 &&
scan_mon( str_mon, &tm_mon ) )
--- 225,231 ----
}
/* DD mth YY HH:MM:SS GMT */
! else if ( sscanf( cp, "%d %400[a-zA-Z] %d %d:%d:%d GMT",
&tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
&tm_sec) == 6 &&
scan_mon( str_mon, &tm_mon ) )
***************
*** 239,245 ****
}
/* HH:MM:SS GMT DD-mth-YY */
! else if ( sscanf( cp, "%d:%d:%d GMT %d-%[a-zA-Z]-%d",
&tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon,
&tm_year ) == 6 &&
scan_mon( str_mon, &tm_mon ) )
--- 239,245 ----
}
/* HH:MM:SS GMT DD-mth-YY */
! else if ( sscanf( cp, "%d:%d:%d GMT %d-%400[a-zA-Z]-%d",
&tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon,
&tm_year ) == 6 &&
scan_mon( str_mon, &tm_mon ) )
***************
*** 253,259 ****
}
/* HH:MM:SS GMT DD mth YY */
! else if ( sscanf( cp, "%d:%d:%d GMT %d %[a-zA-Z] %d",
&tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon,
&tm_year ) == 6 &&
scan_mon( str_mon, &tm_mon ) )
--- 253,259 ----
}
/* HH:MM:SS GMT DD mth YY */
! else if ( sscanf( cp, "%d:%d:%d GMT %d %400[a-zA-Z] %d",
&tm_hour, &tm_min, &tm_sec, &tm_mday, str_mon,
&tm_year ) == 6 &&
scan_mon( str_mon, &tm_mon ) )
***************
*** 267,273 ****
}
/* wdy, DD-mth-YY HH:MM:SS GMT */
! else if ( sscanf( cp, "%[a-zA-Z], %d-%[a-zA-Z]-%d %d:%d:%d GMT",
str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
&tm_sec ) == 7 &&
scan_wday( str_wday, &tm_wday ) &&
--- 267,273 ----
}
/* wdy, DD-mth-YY HH:MM:SS GMT */
! else if ( sscanf( cp, "%400[a-zA-Z], %d-%400[a-zA-Z]-%d %d:%d:%d GMT",
str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
&tm_sec ) == 7 &&
scan_wday( str_wday, &tm_wday ) &&
***************
*** 283,289 ****
}
/* wdy, DD mth YY HH:MM:SS GMT */
! else if ( sscanf( cp, "%[a-zA-Z], %d %[a-zA-Z] %d %d:%d:%d GMT",
str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
&tm_sec ) == 7 &&
scan_wday( str_wday, &tm_wday ) &&
--- 283,289 ----
}
/* wdy, DD mth YY HH:MM:SS GMT */
! else if ( sscanf( cp, "%400[a-zA-Z], %d %400[a-zA-Z] %d %d:%d:%d GMT",
str_wday, &tm_mday, str_mon, &tm_year, &tm_hour, &tm_min,
&tm_sec ) == 7 &&
scan_wday( str_wday, &tm_wday ) &&
***************
*** 299,305 ****
}
/* wdy mth DD HH:MM:SS GMT YY */
! else if ( sscanf( cp, "%[a-zA-Z] %[a-zA-Z] %d %d:%d:%d GMT %d",
str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec,
&tm_year ) == 7 &&
scan_wday( str_wday, &tm_wday ) &&
--- 299,305 ----
}
/* wdy mth DD HH:MM:SS GMT YY */
! else if ( sscanf( cp, "%400[a-zA-Z] %400[a-zA-Z] %d %d:%d:%d GMT %d",
str_wday, str_mon, &tm_mday, &tm_hour, &tm_min, &tm_sec,
&tm_year ) == 7 &&
scan_wday( str_wday, &tm_wday ) &&
Updated the package from SuSE FTP server:
ftp://ftp.suse.com/pub/suse/i386/update/6.2/n1/thttpd-2.04-31.i386.rpm
ftp://ftp.suse.com/pub/suse/i386/update/6.3/n1/thttpd-2.04-31.i386.rpm