PDA

Bekijk Volledige Versie : middleman-1.2 and prior off-by-one bug



qitest1
16/01/03, 02:17
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


QITEST1 SECURITY ADVISORY #006

middleman-1.2 and prior off-by-one bug

PROGRAM DESCRIPTION
Middleman is a powerful proxy server with many features designed to make browsing
the Internet a more pleasant experience. It can do much more than just proxying
though; it can be used as a layer between any web server and client to filter HTTP
requests, or act as a portal between an internal network and the Internet. It has
an intuitive Web interface that provides an easy way of accessing and changing the
proxy's configuration, there's no need to dig through any complicated configuration
files.
(quoted from its README.html)

VULNERABILITY DESCRIPTION
The program is affected by an ebp corruption condition in the routine performing the
dns lookup of the hostname provided in the request, because its own implementation of
strncpy goes off by one.
The bug could be easily exploited by a remote attacker, leading to a root shell if
the daemon runs as root (it's really nice that the sample rc init file provided
calls the daemon without dropping privileges, which is one of its features).
If exploitation is not successful, it will result in a denial of service, because the
program will die at all.

DETAILS
In src/misc.c:
/*
strncpy which always NULL terminates
*/
char *s_strncpy(char *d, char *s, size_t len)
{
char *dest = d;

for (; len && (*dest = *s); s++, dest++, len--);
*dest = '\0';

return d;
}

No doubt. It always NULL terminates. But even off by one.

In src/networks.c near line 614 we find:
/*
perform a dns lookup, using cached response from a previous lookup if possible
*/
HOSTENT *net_dns(char *host)
{
time_t t;
char *string, hst[128], buf[24];
HOSTENT *hostent;

Looking to this code we could think the memory layout on the stack for this function
is the following:
hst[128] |
string[4] |
t[4] |
ebp |
eip V

In src/networks.c near line 627 we find:
s_strncpy(hst, host, 128);

We know that s_strncpy puts its final 0x00 off by one. This fact, apparently, should
not be a real problem, because the 0x00 byte should be put on the lsb of string.

Where is the bug?

Due to compilation (maybe optimization), those variables get allocated in a
different way:
(I dumped this from the program)
** net_dns(): &hst 0xbf7ff9f4 &string 0xbf7ff9d4 &t 0xbf7ff9d8

So the real memory layout is:
string[4] |
t[4] |
hst[128] |
ebp |
eip V

So we have ebp corruption.
Let's have a memory dump:
** net_dns(): 0xbf7ff9f4 -> 0xbfffc0d3 (0)
** net_dns(): 0xbf7ff9f8 -> 0xbfffc0d3 (1)
** net_dns(): 0xbf7ff9fc -> 0xbfffc0d3 (2)
[...]
** net_dns(): 0xbf7ffa70 -> 0xbfffc0d3 (31)
** net_dns(): 0xbf7ffa74 -> 0xbf7ffa00 (32)
** net_dns(): 0xbf7ffa78 -> 0x804db07 (33)

At 0xbf7ffa78 we have 0x804db07: that's the eip. Indeed:
(gdb) x 0x804db07
0x804db07 <net_connect+75>: 0xc483c289
net_connect() effectively calls net_dns()

At 0xbf7ffa74 there is the corrupted ebp.
At 0xbf7ffa00 + 4 the execution flow will search an eip:
** net_dns(): 0xbf7ffa04 -> 0xbfffc0d3 (4)
We can control the data contained at that address, which can obviously be a pointer to
our code. But there's no need to explain here how this kind of vulnerability can be
exploited: it's quite trivial.

I think this kind of problems should be seriously valued, because they are
difficult to be detected and prevented. But they can be avoided by simply checking
that the program never goes off by one, even if the buffer is not immediately
before the ebp: indeed there could be a condition like that I described.

CREDITS
h2so4 - support and connectivity

SOLUTION
Apply the following patch.

8<
- --- middleman/src/misc.c 2002-10-19 19:07:24.000000000 +0200
+++ middleman-patched/src/misc.c 2003-01-10 11:29:08.000000000 +0100
@@ -27,17 +27,34 @@
#include <sys/types.h>
#include "proto.h"

- -/*
- -strncpy which always NULL terminates
- -*/
- -char *s_strncpy(char *d, char *s, size_t len)
- -{
- - char *dest = d;
- -
- - for (; len && (*dest = *s); s++, dest++, len--);
- - *dest = '\0';
+ /* Adapted version of OpenBSD strlcpy */
+char *
+s_strncpy(dst, src, siz)
+ char *dst;
+ char *src;
+ size_t siz;
+{
+ register char *d = dst;
+ register const char *s = src;
+ register size_t n = siz;
+
+ /* Copy as many bytes as will fit */
+ if (n != 0 && --n != 0) {
+ do {
+ if ((*d++ = *s++) == 0)
+ break;
+ } while (--n != 0);
+ }
+
+ /* Not enough room in dst, add NUL and traverse rest of src */
+ if (n == 0) {
+ if (siz != 0)
+ *d = '\0'; /* NUL-terminate dst */
+ while (*s++)
+ ;
+ }

- - return d;
+ return dst;
}

/*
8<

-- -------------------------------------------
---- q1-- ----------------------------------------
-- -------------------------------------------
Web: http://bespin.org/~qitest1
GPG public key: http://bespin.org/~qitest1/qitest1.gpg.key
- --------------------------------------------------------

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6 (GNU/Linux)
Comment: For info see http://www.gnupg.org

iD8DBQE+Hq4YIrsshIyVmPkRAiX+AKCjJbe2lK4wIEkSDwk08e KbRqzVEgCfYOrx
wIX35R8SlitiQ82VLe9itVs=
=qTBd
-----END PGP SIGNATURE-----