ColdFusion: CFQUERY and Evaluate()

Been going back and forth with a friend who does CF development for some company. Anyways, he rings me up to talk about some SQL injection attacks that have been reported on an application he maintains. I take a look at the code and, sure enough, there are. Problem is they’re buried a bit and not easily found. Furthermore, one of the two exploits I’m about to cover is something not many CF developers will ever notice, even if they are aware of the dangers of SQL injection. This is because ColdFusion, on some levels, sucks.

Case 1: Evaluate

Here’s the code:

<cfset session.isadmin = false>

<cfparam name="url.x" default="1">

<cfset variables.msg1 = "Welcome To Our Site">
<cfset variables.msg2 = "Site Administration">

<h1><cfoutput>#Evaluate( "variables.msg#x#" )#</cfoutput></h1>

<cfif session.isadmin is true>
  <p>You may now administer the site as you see fit.</p>
<cfelse>
  <p>Buy our product.</p>
</cfif>

This is now how you write CF, this is just a very simplified example to show you the problem that was discovered; in this case, it’s the Evaluate() line. Variable X is being passed on the URL in this example for simplification. In reality, there are any number of ways user-provided data could find it’s way into an Evaluate() statement. I’m just making it easy and obvious here.

Evaluate() will evaluate (and execute) the contents of the string that is passed to the function and return the result. The intention of the example above is to return the relevant message associated with the user’s level of access. Since X is supplied by the user it is possible to inject something into that Evaluate() line. But what?

Long story short, calling this script with the following URL will alter the isadmin session variable to change the user’s access level.
vulnerable.cfm?x=2+eq+'a'+or+SetVariable(session.isadmin,true)

This creates the following Evaluate() statement:
Evaluate( "variables.msg2 eq 'a' or SetVariable(session.isadmin,true)" )

The string now represents a boolean statement. Instead of returning the value of the string variables.msg2 it now will return TRUE or FALSE. And before it does that it will execute every expression in the statement, including a call to the function SetVariable() which alters the isadmin session variable. With that, the user is now an admin and will have full control of the application. What’s worse, more function calls could could be passed to the application which essentially give the user the same level of access to the machine as whatever user ColdFusion is running under. Especially under ColdFusion MX where CreateObject() can be used to create JAVA objects that provide system-level access.

Case 2: Evaluate in a CFQUERY

<cfsetting showdebugoutput="Yes">
<cfset variables.hack = "' OR 1=1 OR Name='">
<cfquery datasource="misc" name="test">
  SELECT *
  FROM Test
  WHERE Name = '#Evaluate( "variables.hack" )#'
</cfquery>

Again, I’m oversiplifying this a bit, but I’ll show you where this would happen in the real world once you understand what’s going on here.

variables.hack represents the point of injection. We assume this variable has a legit purpose but, at this point in the code execution, it’s value has been altered by a malicious user to what it’s set as in the code. The relevant portion of the SQL injection is OR 1=1 OR which will trigger the database to return all records instead of just the one record intended. There are many other things you can do when you’ve got a SQL injection point like this, but this isn’t about SQL injection it’s about how Evaluate() creates problems.

In ColdFusion, any variable in a CFQUERY block that is wrapped in single quotes ('#variables.string#') will be automatically escaped. In other languages like PHP you have to do this maually, but CF does it automatically. Escaping these strings prevents malicious users from injecting SQL commands into the query. It makes the database treat the string as just a string and not as a series of commands that’s part of the SQL statement.

If you were to run this script on a CF server you would discover that the string in variables.hack is NOT escaped. This allows SQL to be injected into the query. Why is this? The Evaluate() statement is wrapped in single quotes, so what gives?

Well.. actually, ColdFusion is working as intended. There are several passes of the SQL statement made by ColdFusion. The first pass handles the escaping of variables. The second pass handles executing functions and after that the SQL is passed on to the database. If you had something like:


<cfset variables.msg="hack'd">
<cfquery datasource="misc" name="test">
  SELECT *
  FROM Test
  WHERE Name = '#Evaluate( "variables.#msg#" )#'
</cfquery>

You would find the Evaluate() statement becomes:
Evaluate( "variables.hack''d" )

Two single-quotes is the escape sequence for a single quot e in SQL. The contents of the msg variable are escaped. After this escape, Evaluate() is executed. In this case it’d return an error since variable names can’t contain single-quotes.

So you see, the escaping of variables occurs before Evaluate() is processed. This means the contents of whatever Evaluate() will not be escaped and provides a point of injection for an attacker.

The solution is to set the results of the Evaluate() call to a temp variable and use that temp variable inside the CFQUERY.


<cfset variables.test = Evaluate( "variables.hack" )>
<cfquery datasource="misc" name="test">
  SELECT *
  FROM Test
  WHERE Name = '#variables.test#'
</cfquery>

Where is this used in the real world? Arrays. Something where you loop through a number set, say 1 to 20, where you insert the Nth value in an array into the database. Something like SET data = '#Evaluate( "userdata[#X#]" )#' where userdata is an array that stores data provided by the user (form data, data pulled from a database that was set through another application, etc.)

This problem exists for other functions used in a CFQUERY.

That is, any function that performs an evaluation of a string will have the same problem. IIF() is one such function. I’m not sure what others there are off the top of my head, but it’s definately something to think about.

So the point to remember is to never pass any data that, at any time could be set by the user, through Evaluate() and IIF().

And there you go. A couple exploit vectors (as the security guys say) in ColdFusion that you need to be aware of if you’re a CF developer.