This post continues our dive into Railo security, this time introducing several post-authentication RCE vulnerabilities discovered in the platform. As stated in part one of this series, like ColdFusion, there is a task scheduler that allows authenticated users the ability to write local files. Whilst the existence of this feature sets it as the standard way to shell a Railo box, sometimes this may not work. For example, in the event of stringent firewall rules, or irregular file permissions, or you’d just prefer not to make remote connections, the techniques explored in this post will aid you in this manner.
PHP has an interesting, ahem, feature, where it writes out session information to a temporary file located in a designated path (more). If accessible to an attacker, this file can be used to inject PHP data into, via multiple different vectors such as a User-Agent or some function of the application itself. Railo does sort of the same thing for its Web and Server interfaces, except these files are always stored in a predictable location. Unlike PHP however, the name of the file is not simply the session ID, but is rather a quasi-unique value generated using a mixture of pseudo-random and predictable/leaked information. I’ll dive into this here in a bit.
When a change to the interface is made, or a new page bookmark is created, Railo writes this information out to a session file located at
/admin/userdata/. The file is then either created, or an existing one is used, and will be named either
server-[value].cfm depending on the interface you’re coming in from. It’s important to note the extension on these files; because of the CFM extension, these files will be parsed by the CFML interpreter looking for CF tags, much like PHP will do. A typical request to add a new bookmark is as follows:
server.request is then written out to a JSON-encoded array object in the session file, as below:
The next question is then obvious: what if we inject something malicious as a favorite?
Our session file will then read:
Whilst our injected data is written to the file, astute readers will note the double # around our Coldfusion variable. This is ColdFusion’s way of escaping a number sign, and will therefore not reflect our command output back into the page. To my knowledge, there is no way to obtain shell output without the use of the variable tags.
We have two options for popping this: inject a command to return a shell or inject a web shell that simply writes output to a file that is then accessible from the web root. I’ll start with the easiest of the two, which is injecting a command to return a shell.
I’ll use PowerSploit’s Invoke-Shellcode script and inject a Meterpreter shell into the Railo process. Because Railo will also quote our single/double quotes, we need to base64 the Invoke-Expression payload:
Once injected, we hit our session page and pop a shell:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Because I’m using Powershell, this method won’t work in Windows XP or Linux systems, but it’s trivial to use the next method for that (net user/useradd).
The second method is to simply write out the result of a command into a file and then retrieve it. This can trivially be done with the following:
Note that we’re writing out to the start of web root and that our output file is a CFM; this is a requirement as the web server won’t serve up flat files or txt’s.
Great, we’ve verfied this works. Now, how to actually figure out what the hell this session file is called? As previously noted, the file is saved as either
server-[VALUE].cfm, the prefix coming from the interface you’re accessing it from. I’m going to step through the code used for this, which happens to be a healthy mix of CFML and Java.
We’ll start by identifying the session file on my local Windows XP machine:
web-a898c2525c001da402234da94f336d55.cfm. This is stored in
www\WEB-INF\railo\context\admin\userdata, of which
admin\userdata is accessible from the web root, that is, we can directly access this file by hitting
railo-context/admin/userdata/[file] from the browser.
When a favorite it saved,
internal.savedata.cfm is invoked and searches through the given list for the function we’re performing:
1 2 3
This calls down into
application.adminfunctions with the specified action and favorite-to-save. Our addfavorite function is as follows:
1 2 3 4 5 6
Tunneling yet deeper into the rabbit hole, we move forwards into setdata:
1 2 3 4 5 6 7 8 9
This function actually reads in our data file, inserts our new favorite into the data array, and writes it back down. Our question is “how do you know the file?”, so naturally we need to head into loadData:
1 2 3
And yet deeper we move, into getDataStoreName:
1 2 3
At last we’ve reached the apparent event horizon of this XML black hole; we see the return will be of form
web-#getrailoid()[web].id#, substituting in web for request.admintype.
I’ll skip some of the digging here, but lets fast forward to Admin.java:
1 2 3 4
Here we return the ID of the caller (our ID, for reference, is what we’re currently tracking down!), which calls down into config.getId:
1 2 3 4 5 6 7
Here we invoke getId which, if null, calls down into an overloaded getId which takes a security key and a security token, along with a boolean (false) and some global securityKey value. Here’s the function in its entirety:
1 2 3 4 5 6 7 8 9 10 11 12
Our ID generation is becoming clear; it’s essentially the MD5 of key + token, the key being returned from
getSecurityKey and the token coming from
getSecurityToken. These functions are simply getters for private global variables in the ConfigImpl class, but tracking down their generation is fairly trivial. All state initialization takes place in ConfigWebFactory.java. Let’s first check out the security key:
1 2 3 4 5 6 7 8 9 10 11 12
Okay, so our key is a randomly generated UUID from the safehaus library. This isn’t very likely to be guessed/brute-forced, but the value is written to a file in a consistent place. We’ll return to this.
The second value we need to calculate is the security token, which is set in ConfigImpl:
1 2 3 4 5 6 7 8 9 10 11
Gah! This is predictable/leaked! The token is simply the MD5 of our configuration directory, which in my case is
C:\Documents and Settings\bryan\My Documents\Downloads\railo-express-4.0.4.001-jre-win32\webapps\www\WEB-INF\railo So let’s see if this works.
We MD5 the directory (
20132193c7031326cab946ef86be8c74), then prefix this with the random UUID (securityKey) to finally get:
Ah-ha! Our session file will then be web-a898c2525c001da402234da94f336d55.cfm, which exactly lines up with what we’re seeing:
I mentioned that the config directory is leaked; default Railo is pretty promiscuous:
As you can see, from this we can derive the base configuration directory and figure out one half of the session filename. We now turn our attention to figuring out exactly what the securityKey is; if we recall, this is a randomly generated UUID that is then written out to a file called
There are two options here; one, guess or predict it, or two, pull the file with an LFI. As alluded to in part one, we can set the error handler to any file on the system we want. As we’re in the mood to discuss post-authentication issues, we can harness this to fetch the required
id file containing this UUID:
When we then access a non-existant page, we trigger the template and the system returns our file:
By combining these specific vectors and inherit weaknesses in the Railo architecture, we can obtain post-authentication RCE without forcing the server to connect back. This can be particularly useful when the Task Scheduler just isn’t an option. This vulnerability has been implemented into clusterd as an auxiliary module, and is available in the latest dev build (0.3.1). A quick example of this:
I mentioned briefly at the start of this post that there were “several” post-authentication RCE vulnerabilities. Yes. Several. The one documented above was fun to find and figure out, but there is another way that’s much cleaner. Railo has a function that allows administrators to set logging information, such as level and type and location. It also allows you to create your own logging handlers:
Here we’re building an HTML layout log file that will append all ERROR logs to the file. And we notice we can configure the path and the title. And the log extension. Easy win. By modifying the path to
/context/my_file.cfm and setting the title to
<cfdump var="#session#"> we can execute arbitrary commands on the file system and obtain shell access. The file is not created once you create the log, but once you select Edit and then Submit for some reason. Here’s the HTML output that’s, by default, stuck into the file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
Note our title contains the injected command. Here’s execution:
Using this method we can, again, inject a shell without requiring the use of any reverse connections, though that option is of course available with the help of the
Another fun post-authentication feature is the use of data sources. In Railo, you can craft a custom data source, which is a user-defined database abstraction that can be used as a filesystem. Here’s the definition of a MySQL data source:
With this defined, we can set all client session data to be stored in the database, allowing us to harvest session ID’s and plaintext credentials (see part one). Once the session storage is set to the created database, a new table will be created (cf_session_data) that will contain all relevant session information, including symmetrically-encrypted passwords.
Part three and four of this series will begin to dive into the good stuff, where we’ll discuss several pre-authentication vulnerabilities that we can use to obtain credentials and remote code execution on a Railo host.