TL;DR: Several ColdFusion/CFML tags and functions can process URLs as file path arguments -- including some tags and and functions that you might not expect. This can lead to Server-Side Request Forgery (SSRF) vulnerabilities in your code. Developers should be sure to validate any user input passed to the affected tags and functions.
Overview
I recently observed some CFML tags and functions that could be used to perform Server-Side Request Forgery (SSRF), if they processed user-controlled input. Based on this, I decided to do some fuzzing to identify all of the tags and functions that were potentially impacted by this type of attack. There are many legitimate cases where applications need to process URLs and file paths. And the security pitfalls of a few “dangerous” CFML tags and functions are well-known and well-documented. However, there are other instances where the underlying functionality that leads to SSRF is unexpected, and user input is incorrectly assumed to be safe.
Since I haven’t seen anything written about SSRF in CFML, I wanted to share some of my findings to help CFML developers secure their applications. Additionally, since there isn't a ColdFusion equivalent to something like PHP's allow_url_fopen (to prevent some functions from treating a URL as a valid file path) [1] [2], it's up to the developer to ensure that safe, validated input is passed to these tags and functions.
Some CFML Background
Maybe you’re not familiar with ColdFusion and CFML. (If you
are, just skip ahead to the next section.) ColdFusion Markup Language (CFML) is
a web application development language, first released in 1995. Adobe now owns and maintains the original
ColdFusion implementation, and there have been other commercial and open source
implementations, including Lucee, Railo, and BlueDragon. CFML use remains popular for both legacy applications
and new development in organizations across healthcare, education, government, and
various commercial industries. Just ask Google and take a look at the 89 million+ results.
Server-side request forgery (SSRF)
Server-Side Request Forgery (SSRF) is a web application security vulnerability where an attacker is able to abuse functionality and make the application server request an arbitrary URL. Some of the specifics can be application and language/platform dependent, but requests can typically be made for all supported URL schemes, such as http://, https://, ftp://, file:// and more. An attacker can leverage SSRF to:
- Make requests back to the server, including localhost-only services
- Access internal hosts and services, including things like cloud metadata services
- Access external hosts and services
- Potentially send raw network requests
The techniques to turn an SSRF vulnerability into part of an exploit chain for a high-impact compromise are beyond the scope of this post, and will often depend on details in the affected application and target environment. As a very simple example, consider an internal service that isn’t accessible from the public Internet. An SSRF vulnerability within that environment may let an external attacker make requests to that internal service, breaking the security assumption that it should be inaccessible. While this is only a high-level overview of SSRF, there’s lots more in-depth material available elsewhere -- such as here and here. And for a ridiculously awesome look at some novel SSRF exploitation techniques, have a look at this presentation from Orange Tsai.
SSRF and CFML
Some CFML tags and functions, by design, perform actions
that could be dangerous or have security implications. For example, most developers are aware that
if you let a user specify the arguments to <cfexecute> or <filedelete>
tags, this could have disastrous consequences.
<cfhttp url="#url.requestURL#">
However, there are other tags and functions that will
process and request URLs passed in parameters, where this functionality may be
less obvious. If any of these tags and
functions consume user-controlled input in the affected parameters, an attacker
will be able to perform SSRF. Consider
the code below:
<cfscript>
/* Some
file processing stuff */
[...]
mimeType =
fileGetMimeType(form.file);
/* Do more
stuff to validate the MIME type and process the file */
[...]
</cfscript>
Testing Results - Affected Tags and Functions
The following tags and functions can be vulnerable to SSRF,
if they pass unvalidated user input into affected parameters. These results are based on testing against
Lucee and Adobe ColdFusion 2018.
Tags
cfcache |
cfcontent |
cfdocument |
cfdocumentsection |
cfdump* |
cfexecute* |
cfhttp |
cfpdf* |
cfvideoplayer* |
cfzip |
|
|
Functions
callstackdump* |
contractpath* |
directorycreate |
directorydelete |
directoryexists |
directorylist |
fileappend* |
filecopy* |
filedelete |
fileexists |
filegetmimetype |
fileinfo* |
filemove |
fileopen |
fileread |
filereadbinary |
filesetaccessmode* |
filesetattribute* |
filesetlastmodified |
filetouch* |
filewrite |
filewriteline* |
getcanonicalpath* |
getfileinfo |
getfreespace* |
getprofilesections* |
getprofilestring* |
gettempfile* |
gettotalspace* |
imagegetblob* |
imageinfo* |
imagenew |
imageread |
isimagefile |
ispdfobject* |
isvideofile |
iszipfile* |
manifestread* |
objectload |
setprofilestring* |
storeaddacl* |
storegetacl* |
storegetmetadata* |
storesetacl* |
xmlchildpos |
xmlelemnew* |
xmlgetnodetype* |
xmlparse |
xmlsearch |
xmlvalidate |
|
Developers should make sure to validate any user-controlled input
before they’re passed to any affected
tags and functions. If a URL is not
expected input, or if following URLs is not intended behavior, additional
validation logic should be added to prevent bad data or malicious
activity. For example, some functions
will process both URLs/file paths and
file objects as function arguments.
Validation in this case might enforce that only file objects are treated
as valid input. The specific techniques and
logic to validate the user input may depend on the tag/function and necessary
application functionality, and are beyond the scope of this post.
Examples of user-controlled data can be any of the
following:
- Variables in the URL Scope
- Variables in the FORM Scope
- Some Variables in the CGI Scope
- Cookies (Variables in the COOKIE Scope)
- Secondary variables derived from URL, FORM, CGI, and Cookie Scopes
Bottom line -- make sure that you validate any user-controlled input passed to the tags and functions above.
[1] Update - Thanks to feedback from Brad Wood and Zac Spitzer, adding a note that various Resource providers (http, https, etc.) can be disabled in Lucee by commenting out the appropriate lines in lucee-server.xml. I haven't tested this exhaustively, but it looks like this will prevent URLs with the disabled schemes (e.g., http://...) from being processed in some of these functions, but may still allow them in other functions.
[2] Update - Adobe PSIRT has provided the following response:
"Thank you for the opportunity to review and respond to your blog post. Our ColdFusion engineering team has confirmed they leverage Apache Commons VFS in these tags/functions. This API provides a way to disable schemes like [[http://%5Dhttp:/]http://]http://, [[ftp://%5Dftp:/]ftp://]ftp://, ram:// etc by editing the file "org/apache/commons/vfs2/impl/providers.xml" within the commons-vfs jar file. It is strongly recommended for the ColdFusion developer to incorporate input validation in the supported schemes to prevent a risk of SSRF, even if certain schemes are disabled.
However, thanks to your research, our engineering team has determined it would be advantageous to make it easier for ColdFusion developers to disable schemes in an easier and intuitive way. Please keep an eye out for this change in a future release of ColdFusion."
Excellent write up. It's worth noting that basically any function that accepts a file path will allow a URL. In Lucee, there is an abstraction where a Resource interface can be a file path, and zip file, a URL, etc. It's also worth noting that not all the functions in your list are capable of being exploited. For instance callStackDump('https://www.google.com') just returns an error:
ReplyDelete"this is a read-only resource, can't write to it [https://www.google.com/]"
Thanks for the feedback, Brad!
DeleteBut in terms of exploitability, all of the tags and functions above can be used in an SSRF attack. SSRF vulnerabilities can be basic/non-blind (where the attacker can see the response) or blind (where the attacker can make the request but doesn't see the response).
While callStackDump() returns an error and doesn't show the URL contents, an attacker is still able to request the URL from the application server. Blind SSRF could, for example, be used to exploit the recent TestBox remote code execution vulnerability (CVE-2020-15929) against an internal server running TestBox that was only reachable from an Internet-facing application server with a blind SSRF vulnerability.
An additional note I just got from Zac Spitzer is that Lucee Server allows you to disable any of the Resource providers by commenting out that provider in the XML config inside the "" block. Lucee supports the following resource providers:
ReplyDelete* File (default provider)
* FTP
* Zip
* Tar
* TGZ
* HTTP
* HTTPS
* SMB (disabled by default)
* S3
That would, in theory, disable the default behavior of any tag or function that accepts a "Resource" in Lucee as being capable of interpreting it as anything other than a file (if you removed all providers except the file one).
Thanks Brad (and Zac)! This is a great tip, and I wasn't aware of it. I've updated the post with a note. I haven't tested this exhaustively, but it looks like "disabled" resources/schemes are still fetched by some functions. For example, after disabling http, callStackDump() didn't fetch the URL, but cfhttp() still did.
DeleteYes to be clear, cfhttp doesn't follow URLs because of the "Resource" abstraction, but simply because hitting URLs is literally what what function does! So disabling the HTTP resource wouldn't have any affect on the cfhttp tag, whose sole purpose is... to hit HTTP endpoints. Unlike the other tags and and functions in the language where this behavior is generally unexpected, a developer should be aware of what cfhttp does.
Delete