Jeff Macdonald wrote:
Ok, I'll bite.
How can:
log_write(0, LOG_MAIN, (char *)logmsg)
be used to arbitrarily inject code? I understand the concept, but
having % in the logmsg with no parameters to feed it seems harmless to
me.
It depends. The devil is in the details of how log_write() was written.
In general, in C, the '%' character is part of the print formatting
syntax and should be escaped (double %%) if there is no intention to
use it with parameters. The parser with formatting support can read
the % and critically fail in some fashion with the next character and
its argument type does not match or missing.
In general, a function with print-like formatting prototype with
variable arguments would be written like so using the va_xxxx functions:
void logger(char *format, ...)
{
va_list va;
va_start(va, format);
vXXXXXXXXX(output, format, va);
va_end(va);
}
Where vXXXXXXXX() is one of many print-like va formatting functions
under C, C++ and MFC. The output can be a buffer string or a
streaming device.
When using va_list, it means the first string is expected to be a
format string and the rest that follow are parameters for the format
string.
So for Exim's log_write, looking at the public source, its prototype
in local_scan.h has:
void log_write(unsigned int, int, char *format, ...);
That means the 3rd parameter is expected to a format string.
So when you use it like so:
log_write(0, LOG_MAIN, (char *)logmsg)
and logmsg has a non-escaped % character, this is a classic
exploitable thread entry point. When there is no 4th parameter passed
to log_write():
PUFF! Critical exception fault and all kinds of exploits are
possible based on the recovery that might be employed, if any.
This is the essence of the CodeRed-based exploit - Buffer (Stack)
OverFlow exploits where hackers tries to inject a precise amount of
bytes with object code that causes a GPF (General Protection Fault) at
some known spot in the application and he crosses his fingers there is
an OS recovery method enabled which will restart at the injected code
byte offset precisely where the exploit object code is located.
Normally, it a corruption of the function epilog stack rewinding. The
function proloq pushing parameters into the stack, including the final
result, and the function epilog removes (pops) it.
With no recovery, the application simply aborts, the OS catches the
GPF and removes the process from memory.
But when there is a recovery method employed, this is where it can
cause trouble with CodeRed like exploits. However, does not mean you
should have any recovery.
If you have text you wish to log that has no parameters, very common
thing to do, and this text has a '%' as part of the normal text, then
you MUST make sure it is escaped using a double %%.
log_write(0,LOG_MAIN, "There is a 50%% chance of rain.");
But to implement a robust global recovery solution is to add an OS
portable and/or with OS depended compiler pragmas for an *exception
trap* or abort signing.
It could be this using a try/catch()
void
log_write(unsigned int selector, int flags, char *format, ...)
{
va_list ap;
va_start(ap, format);
try {
vfprintf(stderr, format, ap);
} catch(int e) {
fprintf(stderr, "log write exception: %d", e);
}
fprintf(stderr, "\n");
va_end(ap);
selector = selector; /* Keep picky compilers happy */
flags = flags;
}
HOWEVER! This is not a new issue, very common known issue and most
systems have long addressed it. If the vfprintf() doesn't trap and
throw the exception, the above may not be enough.
In pure C, you might have to prepare abort signal function. In C/C++,
for our log functions under Windows, we would use the following for
this log_write() function:
void
log_write(unsigned int selector, int flags, char *format, ...)
{
va_list ap;
va_start(ap, format);
#ifdef WIN32
try {
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
} __except(EXCEPTION_EXECUTE_HANDLER) {
fprintf(stderr, "log-write CRITICAL GPF!\n"); <-- see below
}
#else
// add OS based GPF signal trapping
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
#endif
va_end(ap);
selector = selector; /* Keep picky compilers happy */
flags = flags;
}
In the exception block, you could assume this might happen and just
print the format string:
fprintf(stderr, "%s\n", format);
But EXCEPTION_EXECUTE_HANDLER is a serious critical GPF consideration
and should be monitored because its very possible stack corruption has
occurred and the application is will or has already become unstable.
In modern OSes and compilers, any OS, but especially for Windows, it
has introduced specific Stack Monitoring Overhead and new stack
methods specifically for the CodeRed-based exploits. Its higher
overhead on your code, but with the high speed today, its feasible to
consider.
My long opinion has been that "Recovery" can cripple application
programmers who because of increased complexity, component engineering
and Smarter IDEs, have come to depend on using catch and exception
traps for the simple idea of keeping a program running. It had
lowered the focus of getting it right to begin with, especially at the
interface points. So while using a global exception trap for
log_write(), you need to look at every place it is used and make sure
there is no threat entry point with buffer overflow exploit potentials.
--
Hector Santos, CTO
http://www.santronics.com
http://santronics.blogspot.com
_______________________________________________
dkim-ops mailing list
dkim-ops(_at_)mipassoc(_dot_)org
http://mipassoc.org/mailman/listinfo/dkim-ops