12th Jan 2003 [SBWID-5926]
COMMAND
middleman off-by-one bug
SYSTEMS AFFECTED
middleman-1.2 and prior
PROBLEM
qitest1 [http://bespin.org/~qitest1] found, credits to h2so4 :
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
=============
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.
SOLUTION
Check freshmeat, apply the following patch :
- --- 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;
}
/*
TUCoPS is optimized to look best in Firefox® on a widescreen monitor (1440x900 or better).
Site design & layout copyright © 1986-2025 AOH