Sunday, May 5, 2024
HomeJavaScriptRemediating CSV Injection Assaults In ColdFusion

Remediating CSV Injection Assaults In ColdFusion


A number of days in the past, I did not know what a CSV Injection Assault was. I really like producing CSV (Comma Separated Worth) information in ColdFusion. And, heretofore, I had all the time considered CSV information as containing nothing greater than inert textual content information. On Wednesday, nevertheless, David Epler – considered one of our senior safety engineers – obtained the outcomes of our ongoing Penetration Take a look at (PenTest); and, lo-and-behold, one of many recognized vulnerabilities was “CSV Injection”. Since this was an unknown assault vector for me, I assume it’s also an unknown difficulty for a few of you. As such, I wished to take a look at remediating CSV Injection assaults in ColdFusion.

As with the entire OWASP (Open Net Software Safety Mission) Injection assaults, CSV Injection permits uncooked, user-provided information to set off unintended actions within the goal system. With CSV Injection, the unintended motion is the analysis of a Components within the context of spreadsheet program (corresponding to MS Excel, Google Sheets, Numbers) on the sufferer’s pc.

For instance, if a CSV information file comprises the encoded discipline:

"=3+2"

… the presence of the equal signal (=) as the primary character could trigger the given spreadsheet program to judge the cell content material as a math equation, yielding the sum, 5.

Maths – whereas difficult for my Caveman mind – is not in-and-of-itself a risk. However, as George Mauer factors out in his article on CSV Injection, some spreadsheet packages have formulation that may execute low-level instructions and even make HTTP requests to distant servers. Yikes!

The remediation of CSV Injection assaults beneficial by OWASP is to:

  • Quote fields.
  • Escape embedded quotes.
  • Prepend the sphere worth with a single-quote (') if the sphere begins with one of many malicious characters that may set off a system analysis:
    • Equals (=)
    • Plus (+)
    • Minus (-)
    • At (@)
    • Tab (0x09)
    • Carriage return (0x0D)

Since I really like writing ColdFusion parts, my remediation for the CSV Injection outlined within the PenTest was to create a part that centralizes the logic for serializing Array information into CSV information. It solely has two public strategies:

  • serializeArray( rows )
  • serializeArrayAsBinary( rows )

The second methodology is only a comfort methodology since I am virtually all the time taking the generated CSV information and piping it into the variable attribute of the CFContent tag. This part builds on my earlier publish, celebrating the thrill of CSV in ColdFusion:

part
	output = false
	trace = "I present helper strategies for SAFELY serializing Array information as CSV content material."
	{

	// These are used internally, however may also be used externally as properly as a way to make
	// the calling code extra apparent (seeing names is simpler than seeing ASCII numbers).
	this.chars = {
		COMMA: ",",
		TAB: chr( 9 ),
		NEWLINE: chr( 10 ),
		CARRIAGE_RETURN: chr( 13 ),
		QUOTES: """",
		ESCAPED_QUOTES: """"""
	};

	/**
	* I initialize the CSV serializer with the given defaults.
	*/
	public void operate init(
		string fieldDelimiter = this.chars.COMMA,
		string rowDelimiter = this.chars.NEWLINE,
		string encoding = "utf-8"
		) {

		variables.defaultFieldDelimiter = fieldDelimiter;
		variables.defaultRowDelimiter = rowDelimiter;
		variables.defaultEncoding = encoding;

	}

	// ---
	// PUBLIC METHODS.
	// ---

	/**
	* I serialize the given array information as a CSV string payload.
	*/
	public string operate serializeArray(
		required array rows,
		string fieldDelimiter = defaultFieldDelimiter,
		string rowDelimiter = defaultRowDelimiter
		) {

		var csvData = rows
			.map(
				( row ) => {

					return( this.serializeArrayRow( row, fieldDelimiter ) );

				}
			)
			.toList( rowDelimiter )
		;

		return( csvData );

	}


	/**
	* I serialize the given array information as a CSV binary payload.
	* 
	* NOTE: This methodology is supplied for comfort - after I generate CSV content material, I'm
	* ALMOST ALWAYS then streaming the content material again to the shopper utilizing CFContent, which
	* accepts a binary `variable` attribute.
	*/
	public string operate serializeArrayAsBinary(
		required array rows,
		string fieldDelimiter = defaultFieldDelimiter,
		string rowDelimiter = defaultRowDelimiter,
		string encoding = defaultEncoding
		) {

		return(
			charsetDecode(
				serializeArray( rows, fieldDelimiter, rowDelimiter ),
				encoding
			)
		);

	}

	// ---
	// PRIVATE METHODS.
	// ---

	/**
	* I return a price through which any KNOWN potential CSV injection vector has been escaped.
	* 
	* CAUTION MODIFIES DATA OUTPUT: This works by checking the primary character within the
	* discipline; and, if it's a probably harmful character, it prepends the sphere worth
	* with a single-quote. Some spreadsheet packages will disguise this single-quote; others
	* will render it. As such, it could seem to the tip consumer that we have altered their information
	* (which we have now). Sadly, there is not any method round this.
	*/
	personal string operate escapeCsvInjection( required string discipline ) {

		swap ( discipline.left( 1 ) ) ":

				return( "'" & discipline );

			break;
			default:

				return( discipline );

			break;
		

	}


	/**
	* I return a discipline worth that's quoted and through which embedded particular characters have
	* been escaped.
	*/
	personal string operate escapeField( required string discipline ) {

		return(
			this.chars.QUOTES &
			discipline.exchange( this.chars.QUOTES, this.chars.ESCAPED_QUOTES, "all" ) &
			this.chars.QUOTES
		);

	}


	/**
	* I serialize the given row, each QUOTING and ESCAPING the content material of every discipline.
	*/
	personal string operate serializeArrayRow(
		required array row,
		required string fieldDelimiter
		) {

		var csvData = row
			.map(
				( discipline ) => {

					// NOTE: Calling the toString() methodology to solid every discipline to a string
					// in case we're coping with non-string easy values. This enables
					// us to name member-methods in the entire subsequent invocations.
					var escapedValue = toString( discipline );
					escapedValue = escapeCsvInjection( escapedValue );
					escapedValue = escapeField( escapedValue );

					return( escapedValue );

				}
			)
			.toList( fieldDelimiter )
		;

		return( csvData );

	}

}

The foundation of the answer right here is that each discipline worth is handed by these three strategies:

  • toString()
  • escapeCsvInjection()
  • escapeField()

What I find yourself with is fields that present up wanting like this:

=cmd | Sarah "Stubs" Smith

… and find yourself getting serialized like this:

"'=cmd | Sarah ""Stubs"" Smith"

Now, let’s attempt to generate a CSV file in ColdFusion that comprises malicious content material. Discover that I am utilizing the comfort methodology, serializeArrayAsBinary(), to pipe the info on to the CFContent tag. This resets the output buffer and halts all processing on the server:

NOTE: This code is operating in Lucee CFML. As such, it doesn’t want the cf prefixes on the CFScript-based tags.

<cfscript>

	serializer = new CsvSerializer();

	rows = [
		[ "ID", "NAME", "EMAIL" ],
		[ 1, "Sarah ""Stubs"" Smith", "sarah.smith@example.com" ],
		[ 2, "John Johnson", "jon.johnson@example.com" ],
		[ 3, "=(3+5)", "#chr( 9 )#dr.evil@example.com" ], // <== CAUTION: Malicious information!
		[ 4, "Jo Jamila", "jo.jamila@example.com" ]
	];

	header
		identify = "content-disposition"
		worth = getContentDisposition( "user-data.csv" )
	;
	content material
		sort = "textual content/csv; charset=utf-8"
		variable = serializer.serializeArrayAsBinary( rows )
	;

	// ------------------------------------------------------------------------------- //
	// ------------------------------------------------------------------------------- //

	/**
	* I get the content-disposition header worth for the given filename.
	*/
	personal string operate getContentDisposition(
		required string filename,
		string disposition = "attachment"
		) {

		var encodedFilename = encodeForUrl( filename );

		return( "#disposition#; filename=""#encodedFilename#""; filename*=UTF-8''#encodedFilename#" );

	}

</cfscript>

If we then run this ColdFusion code, generate the CSV file, and import it into Google Sheets, we get the next output:

A malicious CSV file imported into Google Sheets show remediation applied in ColdFusion.

As you’ll be able to see, the the malicious discipline values have been neutralized with the single-quote prefix. Moreover, Google Sheets would not render the quote within the sheet output; however, should you look within the Components enter, you’ll be able to see it.

I’m regularly impressed with how folks discover new and fascinating methods to misuse and abuse applied sciences. Fortunately, SQL Injection has lengthy since been all however eradicated from ColdFusion because of the CFQueryParam tag. And, many types of persevered XSS (Cross-Facet Scripting) assaults have been neutralized by the varied encodeForXYZ() strategies. Maybe we have to introduce an encodeForCsv() methodology?

Wish to use code from this publish?
Take a look at the license.



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments