CVE-2015-9097 (published 2017-06-12T20:29Z)

The mail gem before 2.5.5 for Ruby (aka A Really Ruby Mail Library) is vulnerable to SMTP command injection via CRLF sequences in a RCPT TO or MAIL FROM command, as demonstrated by CRLF sequences immediately before and after a DATA substring.

Severity: MEDIUM
CVSS v2 base score: 4.3 (AV:N/AC:M/Au:N/C:N/I:P/A:N)
CVSS v3 base score: 6.1, MEDIUM (CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N)

Weakness information:
cwe_id: CWE-93
cwe_name: Improper Neutralization of CRLF Sequences ('CRLF Injection')
description: The product uses CRLF (carriage return line feeds) as a special element, e.g. to separate lines or records, but it does not neutralize or incorrectly neutralizes CRLF sequences from inputs.
extended_description: 
            
            
         
         

Fix information:
repo_url: https://github.com/mikel/mail
commit_details: repo_url: https://github.com/mikel/mail
author: Conrad Irwin
author_date: 2013-02-01 22:08:54-08:00
msg: Also unfold and split lazily

This has little effect on the specs, but on my header reading example it
makes about a 10x performance difference, finally bringing it within one
order of magnitude of the "fast hacky solution" at
https://gist.github.com/5901bbd810c08ed3d0b1
repository: repo_url: https://github.com/mikel/mail
repo_name: mikel/mail
description: A Really Ruby Mail Library
repo_language: Ruby
owner: mikel
file_changes: filename: field.rb
change_type: ModificationType.MODIFY
diff: @@ -115,7 +115,8 @@ module Mail
       case
       when name =~ /:/                  # Field.new("field-name: field data")
         @charset = value.blank? ? charset : value
-        @name, @value = split(name)
+        @name = name[FIELD_PREFIX]
+        @raw_value = name
       when name !~ /:/ && value.blank?  # Field.new("field-name")
         @name = name
         @value = nil
@@ -125,7 +126,7 @@ module Mail
         @value = value
         @charset = charset
       end
-      return self
+      @name = FIELD_NAME_MAP[@name.to_s.downcase] || @name
     end
 
     def field=(value)
@@ -133,11 +134,12 @@ module Mail
     end
 
     def field
+      _, @value = split(@raw_value) if @raw_value && !@value
       @field ||= create_field(@name, @value, @charset)
     end
 
     def name
-      FIELD_NAME_MAP[@name.to_s.downcase] || @name
+      @name
     end
 
     def value
@@ -198,7 +200,21 @@ module Mail
       STDERR.puts "WARNING: Could not parse (and so ignoring) '#{raw_field}'"
     end
 
+    # 2.2.3. Long Header Fields
+    #
+    #  The process of moving from this folded multiple-line representation
+    #  of a header field to its single line representation is called
+    #  "unfolding". Unfolding is accomplished by simply removing any CRLF
+    #  that is immediately followed by WSP.  Each header field should be
+    #  treated in its unfolded form for further syntactic and semantic
+    #  evaluation.
+    def unfold(string)
+      string.gsub(/[\r\n \t]+/m, ' ')
+    end
+
     def create_field(name, value, charset)
+      value = unfold(value) if value.is_a?(String) || value.is_a?(Mail::Multibyte::Chars)
+
       begin
         new_field(name, value, charset)
       rescue Mail::Field::ParseError => e

programming_language: Ruby
filename: header.rb
change_type: ModificationType.MODIFY
diff: @@ -244,27 +244,10 @@ module Mail
       @raw_source = val
     end
     
-    # 2.2.3. Long Header Fields
-    # 
-    #  The process of moving from this folded multiple-line representation
-    #  of a header field to its single line representation is called
-    #  "unfolding". Unfolding is accomplished by simply removing any CRLF
-    #  that is immediately followed by WSP.  Each header field should be
-    #  treated in its unfolded form for further syntactic and semantic
-    #  evaluation.
-    def unfold(string)
-      string.gsub(/#{CRLF}#{WSP}+/, ' ').gsub(/#{WSP}+/, ' ')
-    end
-    
-    # Returns the header with all the folds removed
-    def unfolded_header
-      @unfolded_header ||= unfold(raw_source)
-    end
-    
     # Splits an unfolded and line break cleaned header into individual field
     # strings.
     def split_header
-      self.fields = unfolded_header.split(CRLF)
+      self.fields = raw_source.split(HEADER_SPLIT)
     end
     
     def select_field_for(name)

programming_language: Ruby
filename: patterns.rb
change_type: ModificationType.MODIFY
diff: @@ -20,10 +20,12 @@ module Mail
     FWS           = /#{CRLF}#{WSP}*/
     TEXT          = /[#{text}]/ # + obs-text
     FIELD_NAME    = /[#{field_name}]+/
-    FIELD_BODY    = /.+/
+    FIELD_PREFIX  = /\A(#{FIELD_NAME})/
+    FIELD_BODY    = /.+/m
     FIELD_LINE    = /^[#{field_name}]+:\s*.+$/
     FIELD_SPLIT   = /^(#{FIELD_NAME})\s*:\s*(#{FIELD_BODY})?$/
     HEADER_LINE   = /^([#{field_name}]+:\s*.+)$/
+    HEADER_SPLIT  = /#{CRLF}(?!#{WSP})/
 
     QP_UNSAFE     = /[^#{qp_safe}]/
     QP_SAFE       = /[#{qp_safe}]/

programming_language: Ruby
filename: message_spec.rb
change_type: ModificationType.MODIFY
diff: @@ -109,7 +109,7 @@ describe Mail::Message do
 
     it "should raise a warning (and keep parsing) on having an incorrectly formatted header" do
       STDERR.should_receive(:puts).with("WARNING: Could not parse (and so ignoring) 'quite Delivered-To: xxx@xxx.xxx'")
-      Mail.read(fixture('emails', 'plain_emails', 'raw_email_incorrect_header.eml'))
+      Mail.read(fixture('emails', 'plain_emails', 'raw_email_incorrect_header.eml')).to_s
     end
 
     it "should read in an email message and basically parse it" do

programming_language: Ruby