Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SMTP header injection in ASP.NET?

My ASP.NET website has a global error handler that sends an email to me (and another developer) when there is any kind of error in the web app. We recently received an error which contained a CC to an email address that we'd never heard of. The scary thing is that the list of developers that the error email is sent to is hard coded in compiled ASP.NET code. We don't see how the CC could have been added.

We're also very suspicious of foul play because the request that caused the error was an attempt to use one of our forms to send spam. The IP address that sent the request is also listed on http://www.projecthoneypot.org/.

Our best guess right now is that the request was malformed in some way that it injected a CC header into the email. The problem is that we can't figure out how this could be done. We're using System.Net.Mail to send the emails and it seems to protect against this sort of thing. The subject of the MailMessage object only accepts a single line so that you don't create a multiline subject with a CC line. Setting the to and cc addresses in the MailMessage seems pretty robust. And I can't see how you could add a CC header in the body of the message. I can't find any information on this and I'd love to know if this is a real problem.

EDIT: Someone requested the code. It's a little long, but here it is:

public class Global : System.Web.HttpApplication
{
    protected void Application_Error(Object sender, EventArgs e)
    {
        // Get the last exception.
        Exception objException = Server.GetLastError();

        // Work out the error details based on the exception.
        string ErrorType = "";
        string ErrorDescription = "";
        string ErrorHtml = "";

        if (objException == null)
        {
            // This should never occur.
            ErrorType = "Unknown Error";
            ErrorDescription = "Unknown Error";
        }
        else if (objException.GetType() == typeof(HttpException))
        {
            // This will occur when the ASP.NET engine throws a HttpException.
            HttpException objHttpException = objException as HttpException;
            if (objHttpException.GetHttpCode() == 404)
            {
                string Resource = Globals.GetFullUrl(this.Context);
                Server.ClearError();
                Response.Redirect("/ResourceNotFound.aspx?BadUrl=" + Server.UrlEncode(Resource));
                return;
            }
            else
            {
                ErrorType = objHttpException.GetHttpCode().ToString();
                ErrorDescription = objHttpException.Message;
            }
        }
        else if (objException.GetType() == typeof(HttpUnhandledException) && objException.InnerException != null && objException.InnerException.GetType() == typeof(HttpException))
        {
            // This will occur when the code throws a HttpException (e.g. a fake 404).
            HttpException objHttpException = objException.InnerException as HttpException;
            if (objHttpException.GetHttpCode() == 404)
            {
                string Resource = Globals.GetFullUrl(this.Context);
                Server.ClearError();
                Response.Redirect("/ResourceNotFound.aspx?BadUrl=" + Server.UrlEncode(Resource));
                return;
            }
            else
            {
                ErrorType = objHttpException.GetHttpCode().ToString();
                ErrorDescription = objHttpException.Message;
            }
        }
        else if (objException.GetType() == typeof(HttpUnhandledException))
        {
            // This will occur when a page throws an error.
            HttpUnhandledException objHttpUnhandledException = (HttpUnhandledException) objException;
            ErrorType = objHttpUnhandledException.GetHttpCode().ToString();
            if (objHttpUnhandledException.InnerException != null)
                ErrorDescription = objHttpUnhandledException.InnerException.Message;
            else
                ErrorDescription = objHttpUnhandledException.Message;
            if (objHttpUnhandledException.GetHtmlErrorMessage() != null)
            {
                ErrorHtml = objHttpUnhandledException.GetHtmlErrorMessage();
            }
        }
        else if (objException.GetType() == typeof(HttpRequestValidationException) && !Globals.IsTtiUser(this.Context))
        {
            // Do nothing.  This is mostly just spider junk and we don't want to know about it.
        }
        else
        {
            // This will occur when the ASP.NET engine throws any error other than a HttpException.
            ErrorType = objException.GetType().Name;
            ErrorDescription = objException.Message;
        }

        // Send an email if there's an error to report.
        if (ErrorType != "" || ErrorDescription != "")
        {
            Globals.SendErrorEmail(this.Context, ErrorType, ErrorDescription, ErrorHtml);
        }
    }

    public static void SendErrorEmail (HttpContext context, string errorType, string errorDescription, string errorHtml)
    {
        // Build the email subject.
        string Subject = "EM: " + errorType + ": " + context.Request.ServerVariables["SCRIPT_NAME"];

        // Build the email body.
        string Body;

        StringBuilder sb = new StringBuilder("");
        sb.Append("Server:\r\n");
        sb.Append(Globals.Server.ToString() + "\r\n");
        sb.Append("\r\n");
        sb.Append("URL:\r\n");
        sb.Append(Globals.GetFullUrl(context) + "\r\n");
        sb.Append("\r\n");
        sb.Append("Error Type" + ":\r\n");
        sb.Append(errorType + "\r\n");
        sb.Append("\r\n");
        sb.Append("Error Description" + ":\r\n");
        sb.Append(errorDescription + "\r\n");
        sb.Append("\r\n");
        sb.Append("Referring Page:\r\n");
        sb.Append(context.Request.ServerVariables["HTTP_REFERER"] + "\r\n");
        sb.Append("\r\n");
        sb.Append("Date/Time:\r\n");
        sb.Append(DateTime.Now.ToString() + "\r\n");
        sb.Append("\r\n");
        sb.Append("Remote IP:\r\n");
        sb.Append(context.Request.ServerVariables["REMOTE_ADDR"] + "\r\n");
        sb.Append("\r\n");
        sb.Append("User Agent:\r\n");
        sb.Append(context.Request.ServerVariables["HTTP_USER_AGENT"] + "\r\n");
        sb.Append("\r\n");
        sb.Append("Crawler:\r\n");
        sb.Append(context.Request.Browser.Crawler.ToString() + "\r\n");
        sb.Append("\r\n");
        sb.Append("Admin User:\r\n");
        sb.Append(context.User.Identity.Name + "\r\n");
        sb.Append("\r\n");
        sb.Append("\r\n");
        Body = sb.ToString();

        // If there's HTML to represent the error (usually from HttpUnhandledException),
        // then stuff the body text into the HTML (if possible).
        bool HtmlMessage = false;

        if (errorHtml != "")
        {
            Regex r = new Regex("(?<thebodytext><body.*?>)", RegexOptions.IgnoreCase);
            if (r.IsMatch(errorHtml))
            {
                Body = Body.Replace("\r\n", "<br>");
                Body = r.Replace(errorHtml, "${thebodytext}" + Body, 1);
                HtmlMessage = true;
            }
        }

        // Send an email to the TTI developers.
        MailMessage objMail;
        objMail = new MailMessage();
        objMail.From = new MailAddress("from-address");
        objMail.To.Add(new MailAddress("to-address"));
        objMail.CC.Add(new MailAddress("cc-address"));
        objMail.CC.Add(new MailAddress("another-cc-address"));
        if (HtmlMessage)
            objMail.IsBodyHtml = true;
        else
            objMail.IsBodyHtml = false;
        if (errorType == "404")
            objMail.Priority = MailPriority.Low;
        else
            objMail.Priority = MailPriority.High;
        objMail.Subject = Subject;
        objMail.Body = Body;

        try
        {
            SmtpClient objSmtpClient = new SmtpClient();
            objSmtpClient.Send(objMail);
        }
        finally
        {
            // Do nothing.
        }
    }
}
like image 468
Ben Mills Avatar asked Mar 12 '09 15:03

Ben Mills


People also ask

What is SMTP header injection?

SMTP header injection is a technique that is used by attacker to exploit the mail and web servers of the application when the input is not sanitized carefully, it allows the attacker to send emails to other user, the attacker may attach phishing emails, or any dangerous script.

What are email injection attacks?

Email injection is a security vulnerability that can occur in Internet applications that are used to send email messages. It is the email equivalent of HTTP Header Injection.

What type of injection attacks exploit mail server vulnerabilities?

SMTP header injection vulnerabilities arise when user input is placed into email headers without adequate sanitization, allowing an attacker to inject additional headers with arbitrary values.


2 Answers

I could see this being the target of a VERY creative attack.... You are stuffing user controlled data into your message body... At which point, crafty use of binary data COULD result in a BODY that sends the proper data during the SMTP session to get it formatted JUST RIGHT... If I may, I'd suggest either converting the body to all ASCII text, or during your string building, write a string sanitizer that only allows RFC chars in. (Filters the URL's, the REFERRER, Remote Address, and UserAgent). Those are your more likely points of attack.

A second thought might be to construct a basic email in code, and ATTACH the body that you have constructed as a text, or HTML, or PDF file.

Keep in mind, SMTP ENVELOPE data is NOT the same as message data.... If someone was crafty enough to send the correct body that caused a CRLFCRLF.CRLFCRLF to be sent during the body part, that would terminate the sending, and then if they kept sending data, they could send the whole MAIL FROM: RCPT TO:, DATA, etc... (Granted, this is an unlikely scenario...)...

I'd LOVE to see the RAW source of the email you got... (As in the hex dump of the actual SMTP transaction, not what Outlook wants you to see, or whatever).

You may also try encoding your body using QP, or B64 before sending the message.... That might solve your problem...

This is an interesting one, and I'm looking forward to the outcome of it.

like image 127
LarryF Avatar answered Oct 06 '22 01:10

LarryF


Your code looks very secure, so I don't think the problem is on your side.

IMO, either someone intercepted the SMTP message while it was being sent to the mailserver and injected the extra CC: line; or the mailserver has been compromised.

If you can't find an answer I suggest contacting Microsoft directly - you may have uncovered an exploit in the .NET Framework.

like image 31
Ian Kemp Avatar answered Oct 06 '22 00:10

Ian Kemp