Wednesday, November 15, 2023

Critical Variable Mass Assignment Vulnerability in Adobe ColdFusion (CVE-2023-44350)



Background

Adobe ColdFusion is vulnerable to a Mass Assignment vulnerability that can result in an attacker being able to modify the value of any variable in any scope within the context of remote CFC methods.  A mass assignment vulnerability occurs when application code allows a user to set or modify arbitrary objects or values without verifying that the user is authorized to do so.  Modifying values related to authorization checks, security controls, or other important functions may permit a malicious user to access sensitive data or perform other unexpected actions.  Mass assignment vulnerabilities are not unique to ColdFusion and have affected other languages including ASP.NET, PHP, and Ruby on Rails


Vulnerability Summary

CVE-2023-44350 - Critical Variable Mass Assignment Vulnerability in Adobe ColdFusion

Impact: An attacker is able to set the value for any variables/variable scope within the context of the CFC method, which may allow him to control program flow and application logic.  The actual impact will depend on the specifics of the target remote CFC method.

Fixed Version(s): Adobe ColdFusion 2023 Update 6; Adobe ColdFusion 2021 Update 12

Vendor Advisory: Adobe Product Security Bulletin APSB23-52


Technical Details


Some Background on ColdFusion Variable Scopes and Remote CFC Methods

Scopes are the context in which a variable exists in ColdFusion.  For example, the "URL" scope contains all of the URL parameters and values passed in a request.  The "Server" scope contains variables associated with the ColdFusion server.  And the "Application" scope contains the variables associated with a specific, named ColdFusion application.  More information about variable scopes is available here.  Some variable scopes, such as URL, FORM, COOKIE, and CGI (partially), are known sources of user-controlled data and would typically be validated (or avoided) before use in sensitive actions.  However, many other variable scopes are expected not to be directly modifiable by the user.  

ColdFusion Components (CFCs) are files with a .cfc extension that contain CFML methods (functions) and data.  CFC methods have an "access" attribute that controls how they can be called.  Setting a method to access="remote" effectively turns it into an API endpoint or webservice that can be called remotely via a request URL.

For example, consider the following CFC code:

<cfcomponent>
  <cffunction name="xyzzy" output=yes access="remote">
    <cfargument required="true" name="one"> 
    <cfargument required="true" name="two"> 
    <cfset output = arguments.one & arguments.two>
    <cfreturn output>
  </cffunction>
</cfcomponent>


Or if you prefer, in modern script syntax:

<cfscript>
component {
   remote string function xyzzy(required one, required two) { 
      output = arguments.one & arguments.two;
      return output;
      }
}
</cfscript>


If we make a call to this method with URL like this one:

/some.cfc?method=xyzzy&one=foo&two=bar

it will return foobar.  (And bonus points if you spotted the potential for Cross Site Scripting in the example above.  That's not the point of this article, but input validation and output encoding of user-controlled data should always be top-of-mind.)

We passed our function arguments to the CFC method in individual parameters (one and two) in the URL above. But we can also pass in all of our parameters in a variable named argumentCollection, in a WDDX packet or a JSON object, shown below:

(wddx)

POST /some.cfc?method=xyzzy HTTP/1.1
Host: the-target-host
Content-Type: application/x-www-form-urlencoded
Content-Length: 48

argumentCollection=<wddxPacket%20version='1.0'><header/><data><struct><var%20name='one'><string>foo</string></var><var%20name='two'><string>bar</string></var></struct></data></wddxPacket>


(json)

POST /some.cfc?method=xyzzy HTTP/1.1
Host: the-target-host
Content-Type: application/x-www-form-urlencoded
Content-Length: 48

argumentCollection={"one":"foo","two":"bar"}


In a previous article, I covered how coldfusion.filter.FilterUtils.class gets the argumentCollection value from a URL parameter or POST data.  

I'm not going to walk through the entire code flow, but we can see the the ArgumentCollectionFilter in coldfusion.runtime.UDFMethod takes the values in the argumentCollection structure and maps them to scopes and variable locations:



Variables in application code then get resolved by calls to various methods in coldfusion.runtime.DotResolver , coldfusion.runtime.CfJspPage, and coldfusion.runtime.NeoPageContext.


The Mass Assignment Vulnerability 

A mass assignment vulnerability occurs when application code allows a user to set or modify arbitrary objects or values without verifying that the user is authorized to do so.   

This vulnerability lets an attacker control the value of any variable, in any scope, within the context of the code in a CFC method.  It does not extend to variables within the context of other places, such as code within included files in the CFC method, called functions that live outside of the CFC method, or methods in application-wide event handlers such as onRequestStart() and onRequestEnd()

If an attacker is able to control values related to sensitive actions such as access control, authentication, and authorization, he may be able to break the assumed logic and program flow with devastating results.  The actual impact and exploitation will depend on the specifics of the remote CFC method being targeted.  But if you are using any remote CFC methods (including just allowing access to built-in or default remote CFC methods) you will want to review your code, understand the vulnerability, and remediate any impacted components.  


Our Test Environment

Let's look at our proof-of-concept test environment to demonstrate an example of this vulnerability.  

We have three files: Applicaiton.cfc (which contains our application-wide components and configurations), scopeTest.cfc (our component file with a method named "test" that we'll be exploiting), and functions.cfm (a file with an external function that we're embedding in scopeTest.cfc via an include() call).  

Application.cfc:

<cfscript>

component {
    this.name = "AppName";
    this.sessionManagement = true;
    this.searchImplicitScopes = false;

    function onApplicationStart() {
        application.config="xyz";
        application.env = "prod";
        this.searchImplicitScopes = false;
   }
      function onRequestStart() {
                writeoutput("<br><hr>in onRequestStart");
writeDump(var="#Application#", label="Application - onRequestStart");
}
      function onRequestEnd() {
                writeoutput("in onRequestEnd");
writeDump(var="#Application#", label="Application - onRequestEnd");
        }
 }
</cfscript>


Note that we're setting this.searchImplicitScopes = false in our Application.cfc to make our test environment as secure as possible and protect against Scope Injection -- although by design (I believe), the CFML protections against scope injection do not apply variables within CFCs.


scopeTest.cfc:

<cfcomponent>

<cfinclude template="/functions.cfm">
<cffunction name="test" output="true" returnFormat="json" access="remote" >
  <!--- debug stuff to track the values for our variables and scopes --->
  <cfscript>
  writedump("in CFC function");
  writedump(var = "#local#", label = "Local - in CFC function");
  writedump(var = "#Variables#", label = "Variables - in CFC function");
  writedump(var = "#Application#", label = "Application - in CFC function");
  writeoutput("Application.env in CFC function - #Application.env#<br>");
  writeoutput("Application.ApplicationName in CFC function - #Application.ApplicationName#<br><br>");
  myIncludedFunction();
  // here is our vulnerable code 
  if (structKeyExists(Application, "env") && Application.env eq "prod") { 
     retval = 1;
   } else { 
     retval = 0;  
  }
  </cfscript>
  <cfreturn retval>
</cffunction>
</cfcomponent>


Our remote CFC method named test displays the values of various scopes and variables at multiple points throughout program flow.  It will return 1 if Application.env equals prod and will return 0 if Application.env equals any other value.


functions.cfm:

<cfscript>

function myIncludedFunction() {
  writeoutput("in included functions.cfm");
  writedump(var="#Application#", label="Application - in function.cfm");
}
</cfscript>


Test Cases to Demonstrate the Vulnerability

We'll now go through three test cases to show how the user calling the remote CFC method can impact program flow and control how all variable scopes are evaluated within the method.

All of these examples have a WDDX packet as the argumentCollection value, but a JSON object such as {"Application":{"env":"prod"}} will work too.

Test Case #1

In the first test case, we pass in an argumentCollection WDDX packet where Application.env is set to prod. We can see that our test method in scopeTest.cfc returns true, since the conditional checking if Application.env eq prod is evaluated as true.

REQUEST:
POST /scopeTest.cfc?method=test HTTP/1.1
Host: the-target-host
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 197

argumentCollection=<wddxPacket%20version='1.0'><header/><data><struct><var%20name='Application'><struct><var%20name='env'><string>prod</string></var></struct></var></struct></data></wddxPacket>

RESPONSE:


Test Case #2

Our second test case demonstrates the underlying vulnerability and shows how it could be exploited.  We pass in an argumentCollection struct where Application.env is set to development. We can see that our test method in scopeTest.cfc returns false, since Application.env does not equal prod within the context of our method.

REQUEST:

POST /scopeTest.cfc?method=test HTTP/1.1
Host: the-target-host
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 197

argumentCollection=<wddxPacket%20version='1.0'><header/><data><struct><var%20name='Application'><struct><var%20name='env'><string>development</string></var></struct></var></struct></data></wddxPacket>

RESPONSE:

Test Case #3

In the third test case, we pass in an argumentCollection struct with an xxApplication scope name, so there is no impact to the real Application scope. We can see that our test method in scopeTest.cfc returns true, since Application.env eq prod is evaluated as true.

REQUEST :

POST /scopeTest.cfc?method=test HTTP/1.1
Host: the-target-host
Connection: keep-alive
Content-Length: 197

argumentCollection=<wddxPacket%20version='1.0'><header/><data><struct><var%20name='xxApplication'><struct><var%20name='env'><string>prod</string></var></struct></var></struct></data></wddxPacket>
Content-Type: application/x-www-form-urlencoded

RESPONSE:


Looking at the Patch

The patched versions of ColdFusion include an update to coldfusion.filter.FilterUtils.class that checks to see if the scopes for any variables passed in the argumentCollection structure are in a defined list:  APPLICATION, CFFILE, CFHTTP, CGI,CLIENT, COOKIE, FILE, LOCAL, REQUEST, SERVER, SESSION, THIS, THISTAG, THREAD, and VARIABLES:




If a request does match this condition, the request will trigger an exception and ColdFusion will log an error similar to:

"Error","ajp-nio-127.0.0.1-8120-exec-6","11/14/23","18:34:29","AppName","Invalid key in argumentCollection: APPlication.The following scopes are disabled: [FILE, CLIENT, COOKIE, CGI, SERVER, APPLICATION, SESSION, REQUEST, CFHTTP, CFFILE, LOCAL, THIS, THISTAG, THREAD, VARIABLES] . If you wish to allow scopes in the argumentCollection, please set the JVM flag 'coldfusion.argumentcollection.allowscopes' to true. The specific sequence of files included or processed is: /var/www/html/scopeTest.cfc''"

I have not tested the -Dcoldfusion.argumentcollection.allowscopes JVM flag, but it looks like that will disable the new checks added by the patch, if you want the old functionality for some reason.

So - the patch will protect against attempts to modify variables in these defined, built-in scopes.  But if your remote CFC method uses variables in other scopes to control program flow or other sensitive actions, it's possible that they could still be exploitable and you will want to take other precautions to prevent misuse.  Refer to the Remediation Recommendations section below for more details.

Remediation Recommendations

  • Upgrade ColdFusion to a version that is not affected by this vulnerability, and follow the other solutions in APSB23-52
  • Consider blocking all remote HTTP access to .cfc files.  (This will not impact the ability to directly call public/private/package methods in CFCs from within CFML code.)
  • If you do permit access to remote CFC methods, consider blocking the argumentCollection parameter in all requests.  Note that argumentCollection can be a URL parameter or a POST data parameter.  This will require that CFC arguments get passed in individual parameters.
  • If you do permit access to remote CFC methods, review and refactor the code such that modified variable values cannot impact the expected program flow, particularly around sensitive actions such as those related to security, authentication, authorization, and data access.  As shown in the examples above, moving code to "included" files and non-remote CFC methods may prevent mass assignment of variable values.  
  • Keep in mind that only variables in the following scopes are protected in the patched versions: APPLICATION, CFFILE, CFHTTP, CGI,CLIENT, COOKIE, FILE, LOCAL, REQUEST, SERVER, SESSION, THIS, THISTAG, THREAD, and VARIABLES.  The values of variables in other scopes could still be modified within the context of CFC remote method code.  

[ NB: Lucee is not affected by this vulnerability. ]

Timeline

2023-08-17 - Reported the vulnerability to Adobe PSIRT

2023-11-14 - Adobe Security Bulletin APSB23-52 released

2023-11-15 - Blog post published

No comments:

Post a Comment