Asp.net

ASP.NET 中的 SMTP 標頭注入?

  • August 17, 2014

我的 ASP.NET 網站有一個全域錯誤處理程序,當 Web 應用程序出現任何類型的錯誤時,它會向我(和另一個開發人員)發送電子郵件。我們最近收到一個錯誤,其中包含對我們從未聽說過的電子郵件地址的抄送。可怕的是,錯誤電子郵件發送給的開發人員列表是硬編碼在已編譯的 ASP.NET 程式碼中的。我們看不到如何添加 CC。

我們也非常懷疑犯規行為,因為導致錯誤的請求是試圖使用我們的一種形式發送垃圾郵件。發送請求的 IP 地址也列在http://www.projecthoneypot.org/上。

我們現在最好的猜測是該請求以某種方式格式錯誤,它在電子郵件中註入了一個 CC 標頭。問題是我們無法弄清楚如何做到這一點。我們正在使用 System.Net.Mail 發送電子郵件,它似乎可以防止此類事情發生。MailMessage 對象的主題只接受單行,因此您不會使用抄送行創建多行主題。在 MailMessage 中設置 to 和 cc 地址似乎相當健壯。而且我看不到您如何在郵件正文中添加 CC 標頭。我找不到有關此的任何資訊,我很想知道這是否是一個真正的問題。

編輯:有人要求提供程式碼。它有點長,但這裡是:

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.
       }
   }
}

我可以看到這是一個非常有創意的攻擊的目標……您正在將使用者控制的數據填充到您的消息正文中……此時,巧妙地使用二進制數據可能會導致一個在發送期間發送正確數據的 BODY SMTP 會話以將其格式化為正確…如果可以的話,我建議將正文轉換為所有 ASCII 文本,或者在您的字元串建構期間,編寫一個僅允許 RFC 字元進入的字元串清理程序。(過濾 URL, REFERRER、遠端地址和 UserAgent)。這些是你更有可能的攻擊點。

第二個想法可能是用程式碼建構一個基本的電子郵件,並將您建構的正文附加為文本、HTML 或 PDF 文件。

請記住,SMTP 信封數據郵件數據不同……如果他們繼續發送數據,他們可以發送整個 MAIL FROM: RCPT TO:, DATA, etc…(當然,這是不太可能的情況…)…

我很想看到你收到的電子郵件的原始來源……(如實際 SMTP 事務的十六進制轉儲中,不是 Outlook 希望你看到的,或其他)。

您也可以在發送消息之前嘗試使用 QP 或 B64 對您的身體進行編碼……這可能會解決您的問題……

這是一個有趣的問題,我很期待它的結果。

引用自:https://stackoverflow.com/questions/639217