dkim-ops
[Top] [All Lists]

Re: [dkim-ops] FW: how can use the DKIM the function

2011-05-17 18:20:02
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