injection_projection code

Mark Bucciarelli mark at gaiahost.coop
Wed Jul 19 23:13:51 UTC 2006


Following up on the C CGI thread, I settled on the old cgiemail
program from MIT, deleted all the printf and html- and
url-encoding crap, and added code to protect against email header
injection.

If you see any holes in the injection_protection code, please let
me know--it turned out to be harder than I thought, mainly
because of hex encoding.  Once I get a git repository up, this
will available as Free Software.

This only runs for the template variables that are replaced in
the email headers.  I identify the headers by looking for two
linefeeds (or carriage returns, or carriage return+linefeeds) in
a row in the template.

As a little background, cgiemail lets you define a template and
with field names escaped in square brackets.  Then you define the
form vars that match the field names and set the form action to
/cgi-bin/cgiemail/template.txt.  It's another issue, but I don't
like that cgiemail uses PATH_TRANSLATED to lookup the template
file.  PATH_TRANSLATED = DOCUMENT_ROOT + PATH_INFO, so you have
to put the template files in a place that is readable via a web
browser.  Seems like the templates are better off in a cgi-bin
dir that is outside the document root.

/**
 * Sanitize string to protect against email header injection.
 *
 * From my testing, a line feed encoded as "\%6e" is still a line feed
 * according to sendmail.   Get rid of hex encodeing before looking for
 * nasties.
 *
 * This routine modifies the string passed in.
 */
void injection_protection( char *s ) 
{ 
    char *injections[] = { 
        "\\n", 
        "\\r", 
        "\n", 
        "\r", 
        "content-type:", 
        "bcc:", 
        "to:", 
        "cc:" 
    };
    char *cleaned;
    char *p;
    int i;

    hex2char( s );

    // If we find any injection text, drop whatever comes after it.
    for ( i = 0; i < sizeof(injections)/sizeof(char*); i++ )
    {
        // s = "abcde\n"
        //      0123456
        // p = "\n"
        // p - s = 5
        p = strcasestr( s, injections[i] );
        if ( p )
        {
            // MKB: TODO: log injection attempt.
            if ( p - s > 0 )
                strlcpy( s, s, p - s + 1 );
            else
                *s = '\0';
        }
    }
}

/**
 * Convert all hex entries to char's in the given string.
 * 
 * If hex code resolves to a non-printable character, just drop
 * it.
 *
 * Return new string in the arg passed in.
 *
 */
void hex2char( char * s )
{
    char *cleaned;
    char *p;
    char *q;
    char hex_string[3];
    unsigned int hex_int;

    cleaned = (char*) calloc(strlen(s) + 1, sizeof(char));

    // Replace hex values with characters.  If hex code references a
    // non-printable character, drop it and continue with rest of string.
    hex_string[2] = '\0';
    p = s;
    q = cleaned;
    while ( *p )
    {
        if ( *p != '%' )
        {
            *q++ = *p++;
        }
        else
        {
          p++;
          if ( *p )
          {
              hex_string[0] = *p++;
              if ( *p )
                  hex_string[1] = *p++;
              else
                  hex_string[1] = '\0';

              sscanf(hex_string, "%x", &hex_int);
              if( isprint(hex_int) )
              {
                  *q++ = (char)hex_int;
              }

              if ( *p == '\0' )
                  *q = '\0';
          }
          else
          {
              // value terminates in a percentage sign.
             *q++ = '%'; 
             *q = '\0';
          }
        }
    }

    strcpy( s, cleaned );

    free( cleaned );
}


More information about the freebsd-isp mailing list