Product: Seapine TestTrack Pro Vulnerable versions: 2010.x, 2011.x Vulnerability: predictable session cookies Vendor informed: 2010-09-07 Fix available: no Info: TestTrack Pro is an issue tracking application from Seapine Vulnerability: TestTrack Pro offers a SOAP interface which works as follows: - connect with username and password to retrieve a list of available projects: getProjectList(username, password); - connect with username and passsword to retrieve a session login cookie on a project: projectLogon(project, username, password); - query the system to retrieve project data using the session login cookie to authenticate: getRecordListForTable (cookie, .....); - log off the session: databaseLogoff(cookie). The session login cookies generated by the server are predictable. Below is a log file from the connections showing the date and time of a log entry, and then the cookie used for authentication: "09/07/10","11:18:19","1246111" "09/07/10","11:18:22","1246115" "09/07/10","11:18:44","1246123" "09/07/10","11:18:46","1246127" "09/07/10","11:18:51","1246132" "09/07/10","11:18:53","1246139" "09/07/10","11:19:16","1246144" "09/07/10","11:19:18","1246151" "09/07/10","11:19:33","1246156" "09/07/10","11:19:35","1246163" "09/07/10","11:19:51","1246167" "09/07/10","11:19:53","1246175" The absolute value of the session cookie is related to the server uptime, starting near 0 when the server is just started and increasing monotonic afterwards. History: 2010-09-07 Seapine was informed and assigned case number 121426 2010-09-08 Seapine confirmed the issue as a known issue and scheduled a fix in 'an upcoming 2011.0.x maintenance release'. 2010-12-20 TestTrack 2011.1 was released without a fix. 2010-12-24 Seapine was asked to publish a security bulletin detailing risks and mitigations despite no fix being availale 2011-02-02 Seapine was informed this issue would be publicly disclosed 2011-02-13 Submitted to bugtrack and published on my blog
Posts tagged ‘Security’
As promised my hacks to get past the the quirks in the webservice API for the Adobe forums. I am deliberately not publishing the full application, the lack of local caching in it makes it more of a DOS tool then a forum client.
Authentication
The PermissionsService authenticate method doesn’t work since the Adobe forums do not use the standard Jive login methods, but a custom Adobe SSO login method. To get a login on the forums from AIR replay what a browser does when logging in to the forums. First visit the index page of the forums to GET a few cookies, then POST the credentials to the Adobe authentication server, then GET the index page of the forums again to allow the forums to do a call back to the Adobe authentication server to collect the user profile. So all in all it takes 3 HTTP requests to log on.
I have included the code snippet that my AIR app uses to log in to the forums below for those who wish to experiment with it. Be warned that the full sequence takes on average 15 seconds.
// General variables private const _AdobeAuthURL:String = "http://www.adobe.com/cfusion/entitlement/index.cfm?loc=en&e=ca&returnurl=http%3A%2F%2Fforums%2Eadobe%2Ecom%2Flogin%2Ejspa"; private const _ForumRootURL:String = "http://forums.adobe.com/index.jspa"; private function startLoginSequence():void { writeLog("Starting login "); getForumRoot(preAuthResult); } private function getForumRoot(resultFn:Function):void { // just GET the forum root to collect cookies var forumRootService:HTTPService = new HTTPService(); forumRootService.method = "GET"; forumRootService.url = _ForumRootURL; forumRootService.useProxy = false; forumRootService.resultFormat = "text"; forumRootService.addEventListener(FaultEvent.FAULT, faultHandler); forumRootService.addEventListener(ResultEvent.RESULT, resultFn); forumRootService.send(); } private function preAuthResult(event:Event):void { // We have now collected the forum cookies, log in to the Adobe ID login(); } private function login():void { // login does a login request to the main Adobe site // credentials Object with all name value pairs var credentials:Object = new Object(); credentials['returnURL'] = 'http://forums.adobe.com%2Flogin.jspa'; credentials['up_login'] = 'yes'; credentials['ignore_email_validation'] = 'yes'; credentials['up_username'] = "spam@vandieten.net"; credentials['has_pwd'] = "true"; credentials['up_password'] = "sihtyrt"; // login Service var loginService:HTTPService = new HTTPService(); loginService.method = "POST"; loginService.url = _AdobeAuthURL; loginService.useProxy = false; loginService.resultFormat = "text"; loginService.addEventListener(FaultEvent.FAULT, faultHandler); loginService.addEventListener(ResultEvent.RESULT, loginResult); loginService.send(credentials); } private function loginResult(event:ResultEvent):void { // check if we are really logged in var loggedInTestString:String = '<a href="http://www.adobe.com/cfusion/membership/logout.cfm">Sign out</a>'; if (event.result.indexOf(loggedInTestString) != -1) { // Login success // Do a new HTTP request to the forums to propagate the login from Adobe to Jive getForumRoot(postAuthResult); } else { // Login failure throw("Username password combination incorrect."); } } private function postAuthResult(event:ResultEvent):void { // Fully logged in, ready to use the API writeLog("Login complete"); // extractUserDetails(event.result); } |
Once we are logged in we can use all of the APIs to get the actual useful information from the forums. The FlexBuilder WSDL import tool works quite well with the APIs, all cases where it failed turned out to be mistakes in my programming. To get stared call getRecursiveCommunities of the CommunityService to get a list of all the communities (forums) available. Depending on how busy the forums are, internet bandwidth, traffic and the position of the moon this can take between 25 seconds and 5 minutes. From the list of forums you get you can drill down into the list of threads (ForumService getThreadsByCommunityID: up to 50 seconds to get the thread list of the DreamWeaver forum, the busiest forum with about 50K threads) and then into the list of messages per thread (ForumService getMessagesByThreadID: usually less then a second). When you get the messages you will also get the users and all things you need to display a tree of who responded to who.
Getting your own userID
Apart from the login methods in the API there appears to be another problem in the webservice API (or maybe just something I haven’t figured out). Searching for a user, either by his username or by his emailaddress, has never returned any results for me. (I do in fact get a 500 Internal server error when I try to use the UserService getUserByUsername.) But the user details are needed for all API methods to post messages. The workaround I implemented at last for that is to just do a string lookup in the HTML of the forum start page (the commented out call to extractUserDetails in the previous code snippet):
public function extractUserDetails(pageObj:Object):void { var page:String = pageObj as String; var userIDPreString:String = 'quickuserprofile.getUserProfileTooltip('; var userIDPreStringIndex:int = page.indexOf(userIDPreString) + 39; var userIDPostString:String = ')'; var userIDPostStringIndex:int = page.indexOf(userIDPostString, userIDPreStringIndex); _userID = page.substring(userIDPreStringIndex, userIDPostStringIndex) as int; } |
Obviously depending on visiting some sites in a certain order to pick up cookies along the way and scraping generated HTML is rather fragile. The smallest change in the HTML or the authentication will break any application that uses this. Ideally Adobe would implement some authentication webservices on its own site to facilitate logging in from an application.
With this the basic services to list forums, retrieve threads, read messages and post your own messages can be accessed without further surprises. I intend to continue working on my ForumClient, but it will be a while. I will need a proper design for the application, I am thinking about modules to support different forum software API’s, storing configuration data in XML, message caching in a SQLite database per configured site etc. Then I need to develop the whole thing. And in the mean time real life is catching up and I am going on a training tour of Europe, so I will have little time for the next three weeks. Drop me a line if you are interested in helping out, or don’t wait for me and just get started on your own client
Lately I have posted a number of posts on the state of shared hosting security. Unfortunately we have to conclude that only by taking drastic measures a hoster can begin to protect his customers from eachother. And even when the hoster locks dow everything to the best of his abilities, which disables lots of useful functionality, there are some significant holes left. Therefore I have compiled this wishlist of things I would like to see improved in Centaur to make shared hosting security a bit better and easier to deal with.
Documentation
Setting up shared hosting is largely a process of trial-and-error. There is absolutely no documentation available that tells us which tags will blow up when we Sandbox the temp folder. That cfdocument will error out if the fonts folder is not allowed in the Sanbox. There is nothing to warn us that cfdump will break if we disable createObject(JAVA). What we need is better documentation and preferably a whitepaper that shows us how to configure a secure shared hosting environment step by step.
A locked down application scope
Frameworks are storing more and more data in the application scope. The application scope is currently completely unprotected: everybody can see everything. We need a way to lock down the application scope so we can only access it from just one Sandbox.
Fine grained Java permissions
The biggest useful feature we need to disable in order to protect servers is access to Java objects. We need much more control over which actions a Java class can perform before we can open up access to Java. For starters, we need knobs to disable many actions from the Runtime class. We need to be able to disable the loading of arbitrary classes by users (what is the point in disabling createObject(COM) if users can load their own version of JIntegra?), we need to stop users from killing the server with exitVM, halt etc., we need to stop them from spawning other processes etc. Then we need to have all access to SecurityPermission disabled so that users can’t simply reset the security and we need to have ReflectPermission disabled to keep our private stuff private.
Java provides all the API we need for that, we just don’t have any knobs in ColdFusion to switch them on for our Sandboxes
Sane default settings for Sandboxes
If you create a new Sandbox from scratch it has access to many things it shouldn’t. A secure by default configuration would for instance disable all datasources and leave it up to the administrator to enable them on an as needed basis. Same goes for tags like cfexecute and cfobject.
On the other hand the default Sandboxes do not have access to to for instance the /WEB-INF/cftags/ folder. Without that access cfinterface may not work. What is the harm in giving Sandboxes Read and Execute access to that folder by default? Isn’t the code in those folders written by and trusted by Adobe?
There are many more issues that would help ColdFusion becomming a better hosted platform, and I will address a few more in the future, but these are the 4 most important issues in the area of shared hosting security that Adobe needs to address. All of these have been submitted to Adobe, but I am sure a few more votes and ideas won’t hurt.
So far in our attempts to get access to the templates and data of other hosted customers we have primarily focused on accessing the templates of other users in order to get access to their data through them. A more direct approach is to try to access the database directly.
Registered datasources
To access a registered datasource all you need is its name. More often then not, names are easy to guess. Depending on how the datasource is registered in the CF Administrator you may need a username and password as well. If the username and password to the datasource are registered in the ColdFusion Administrator ColdFusion will write them to neo-datasource.xml. They are encrypted there, but with a reversible algorithm (ColdFusion needs to be able to decrypt them in order to authenticate). The way to decrypt these passwords is well known. So if you have read access to neo-datasource.xml through an incorrect Sandbox configuration you have all usernames and passwords registered there.
If the username and password of the datasource are not registered in the ColdFusion Administrator chances are they are stored in the application scope somewhere and we have already seen that the application scope is insecure.
The first line of defense against this is to configure Sandbox Security to only allow access to specific datasources to each Sandbox. This will effectively lock down access from ColdFusion to the datasource, even if you know the full connection string.
Database to database connections
But as good as the protection for datasources is in ColdFusion, it only protects against connections from ColdFusion’s cfquery and cfstoredproc. Just as the trick to work around a disabled cfexecute is to create Java / COM / .NET objects, the trick to work around a disabled ColdFusion datasource connection is to start the connection from somewhere else. And the way to do that is to connect from one database to another. The protocol to do so, SQL/MED, is not widely implemented in databases, but most databases offer proprietary ways to do so. For instance, in MS SQL Server you would use:
SELECT * FROM OpenDataSource( ’SQLOLEDB‘ , ’Data Source=server.asdf.com;User ID=yourusername;Password=yourpassword‘ ).somedatabase.dbo.sometable; |
In PostgreSQL that would be:
SELECT * FROM dblink( 'dbname=postgres' , 'SELECT ID, value FROM tables' ) AS t1( ID INT , value TEXT ); |
The one thing that makes this more difficult is that you usually need some sort of superuser privilege on the database server to be allowed to do this.
MS Access
MS Access is a file based database that is pretty popular in shared hosting. While people frequently question performance, scalability and security, it is still an attractive offering because it allows users to download the database, edit it offline and then upload an entirely new database with new data.
MS Access offers a very simple feature to access tables in other MS Access databases without the superuser privileges you would need on a more full-featured database server. You just specify the path to the data file you want to access directly in the query:
SELECT * FROM table IN 'h:\sites\database.mdb' |
So far we have seen that to secure our shared hosting customers from each other we need to place them in a Sandbox limiting file system access, datasource access and disabling access to cfexecute, cfobject and CreateObject(JAVA/COM/.NET) . Typically I add cfregistry to the list of disallowed tags as well. While not strictly necessary for security (you are running ColdFusion with a non-privileged account, aren’t you?), the registry is a shared resource that is easily fragmented and has a limited size, so I just want to keep customers out.
cfdump
The problem we have now is that apart from the functionality we intended to disable we have inadvertently disabled some more functionality, including the cfdump tag. In the directory /WEB-INF/cftags/ are a number of .cfm templates that implement tags and one of them is dump.cfm. But since this tag is included much like a customtag would be included, it runs with the same permissions that a normal customtag would be running. And those permissions disable the use of CreateObject(JAVA), causing cfdump to throw an error when some datatypes are dumped.
The workaround (go ask Adobe for a solution) is to replace /WEB-INF/cftags/dump.cfm with a custom dump template. I have made the one I use available as a download. It isn’t pretty, it doesn’t dump Java objects (but shared hosting customers can’t create them anyway), but it does the job.
cfinterface
cfinterface is another one of the tags implemented in CFML in /WEB-INF/cftags/. If you try to use a component implementing an interface you will notice that this throws an error. Apparently we need to grant read and execute permissions on that directory to every Sandbox (you can adapt the script I posted before for that). I just ran into this this weekend when I updated my dump template to show interface information as well, so I guess that shows how much I use shared hosting myself.
The ColdFusion Administrator and Admin API
In ColdFusion 8 Adobe introduced multiple user accounts and permission delegation for the ColdFusion Administrator and the Admin API. I have my doubts about delegating access to the ColdFusion Administrator in a shared hosting environment since the delegation is based on functionality and not on Sandboxes. So that means you can delegate access to see certain features, such as the logfiles, but not certain features for a specific Sandbox, i.e. the logfiles of a specific customer. Since pretty much every feature in the Administrator I can delegate has security implications, I can not delegate anything.
With the Admin API the situation is even worse. Internally the Admin API has been implemented in CFML and it suffers from the same problem as cfdump: it uses cfobject and createobject(JAVA) functionality that we can not enable without giving users the ability to upgrade themselves to root administrator. The only way we can use them is in our own management scripts and as a backend invoked through some facade we need to write first.
Application scope variables
ColdFusion piggybacks its application scope variables on the Java application scope. This allows for an integration in the application scope between for instance ColdFusion and JSP code: variables you write in one are available in the other. The downside is that on the Java side the application scope is considered an instance wide scope and all data is accessible. That means that whatever customer A puts in the application scope is accessible to customer B. Put the following in your Application.cfc to see how that works:
<cfcomponent> <cfset this.name = "" /> <cffunction name="onRequestStart"> <cfdump var="#application#" /> <cfabort /> </cffunction> </cfcomponent> |
This sharing of the application scope is by design and as a result it is unsafe to store data in the application scope on a shared host.
In the first part we have set the stage for this series: the goal is to protect one shared hosting customer from an ‘inside attack’ by another shared hosting customer on the same ColdFusion instance. In the second part we have gone into the reason why we need ColdFusion Enterprise Edition to secure the filesystem from direct access through cffile and cfdirectory. In this part we will see why we need to take additional steps to secure the filesystem against the other languages we can use in / call from ColdFusion.
Java
Since ColdFusion is written in Java the integration with Java is pretty tight and we can easily use Java code from ColdFusion. So what happens if we try not to use a CFML tag to access a file from some other customer, but use Java directly. You don’t need any Java knowledge to do so, if you Google for some code you will find plenty of examples. For this I downloaded the code from Ben Nadel’s Java Exploration in ColdFusion: java.io.LineNumberReader blogpost, changed the ExpandPath(”./data.txt”) to my template name and ran the code. It displayed the sourcecode of the template, so the code was good. Next I changed the code so the file it pointed to was “c:\\test\\secret.txt”, which is outside my Sandbox. And the code promptly returned an error.
So far so good: our Sandbox protects against reading files directly through Java.
CFEXECUTE
So if we can’t read files from elsewhere on the filesystem directly, perhaps we can copy them to our Sandbox first using the native functionality of the operating system, and then read them once they are in our Sandbox. (And if you don’t know which files you need, just use xcopy and copy them all recursively.) For that we turn to the cfexecute tag. In the default form that tag would be used like this:
<cfexecute name="c:\windows\system32\cmd.exe" arguments="/c copy #source# #target#" /> |
But because we are in a Sandbox that will most likely not work because your Sandbox will not allow you to access the c:\windows\system32\cmd.exe file. To circumvent that, simply upload a copy of cmd.exe to your Sandbox and use that one. With that the code to copy files and then read them through cffile becomes:
So by now it should be clear that CFEXECUTE needs to be disabled to maintain security in a shared hosting environment. The bottom line is that with CFEXECUTE you start a process, that process runs as the same user as ColdFusion, the ColdFusion user by definition has access to the templates of other customers, so the process you started has access to them as well. And this means that useful functionality (albeit used by a limited number of users) needs to bes disabled.
COM, .NET and Java again
The problem is that CFEXECUTE is not the only way to start a new process. COM, .NET and Java can do so as well. Below is a simple code example that shows how to dynamically generate a batchfile, write that to the filesystem inside the Sandbox, execute it through Java, and read the result.
<!--- target file outside our Sandbox ---> <cfset secretFile = server.coldfusion.rootdir & "\lib\neo-datasource.xml" /> <!--- batch file inside our Sandbox ---> <cfset batchFile = ExpandPath("./copy.bat") /> <!--- location where we copy the file inside our Sandbox ---> <cfset readableCopyOfSecretFile = ExpandPath("./readableCopyOfSecretFile.xml") /> <!--- copy command for in our batchfile ---> <cfset batchFileContent = 'cmd /c "copy #secretfile# #readableCopyOfSecretFile#"' /> <!--- write the batchfile inside our Sandbox ---> <cffile action="write" file="#batchFile#" output="#batchFileContent#" /> <!--- execute the batchfile ---> <cfset CreateObject("java", "java.lang.Runtime").getRuntime().exec(batchFile) /> <!--- read the copy of file inside the sandbox ---> <cffile action="read" file="#readableCopyOfSecretFile#" variable="fileContent" /> <!--- display the contects of the file ---> <cfoutput>#HTMLCodeFormat(fileContent)#</cfoutput> |
We can prevent this by modifying the Sandbox configuration and disabling the execute permission from the Sandbox, but since ColdFusion itself needs execute permissions to run CFML that will kill ColdFusion as well. Executing processes from COM works the same as from Java. From .NET it works slightly different because the actual .NET service runs in another process and could potentially have different permissions from the ColdFusion account, but the inability to differentiate the permissions between different users remains.
So the hoster is left with a hard choice: disable CFEXECUTE, CFOBJECT, CreateObject(.NET), CreateObject(COM) and CreateObject(JAVA) or accept that there is no security whatsoever in the shared hosting configuration. If you disable these tags a lot of applications and frameworks won’t work anymore. For instance Transfer ORM needs Java access, so any application build on top of it will not work in a secured shared hosting environment.
Warning
A long time ago I had some other code examples online that could be used to extend the functionality of a shared hosting environment. I have taken these examples down not just because with the arrival of the Admin API they are obsolete, but also because I was informed that somebody had wrecked a shared hosting server with them. So please before you start uploading and running these examples on your shared host, please take a moment to consider the following points:
- If you want to look further into this code, why not do that on your own development environment?
- In most jurisdictions accessing other peoples data / code is illegal.
- Some of the code examples (especially if you replace copy with xcopy) can consume lots of resources.
- You will never be able to test whether your hosting account is protected from others. You will only be able to test if others are protected from you. So really, why not on your own development environment?
In the first part we have set the stage for this series: the goal is to protect one shared hosting customer from an ‘inside attack’ by another shared hosting customer on the same ColdFusion instance.
If no precautions have been taken, attacking is a simple job. The starting point is a template that does a directory listing of every drive from “a:\” to “z:\” using cfdirectory. When you find something interesting, like the “coldfusion8″ directory, the “jrun4″ directory or a “customers” directory, just drill down from there. Not elegant, but very effective. The only way to hide from it is to use netwerk shares with hard to guess names, but even then it is just a matter of drilling down from the #server.coldfusion.rootdir# to the logfiles and see what the directory paths in some of the mappings and logged errors are.
Hiding (i.e. obscurity) provides no effective hurdle against an inside job and hence no real security.
To get real security against this reading of directories we need to enable Sandbox Security. Sandbox Security allows us to define a directory on the filesystem as a Sandbox and subject every request that starts from that Sandbox to a set of constraints. These constraints can include which tags are allowed, i.e. forbid cfregistry outright, or which resources can be accessed. Typically each Sandbox is defined at the root of a customers FTP and / or WWW directory and then allows for access of only some directories and datasources. Setting up the allowed resources and tags in a Sandbox can occasionally be a bit counterintuitive, for instance to allow a file to be used in a cfinclude it needs execute permissions and several extra directories need to be accessible for some tags.
The thing with Sandbox Security is that it is a feature that is only available in Enterprse Edition. ColdFusion 8 Standard Edition has the ability to restrict the usage of tags, functions and resources as well, but everybody operates in the same Sandbox. So while we can disallow customer A from reading the files of customer B through cfdirectory, that would also disallow customer B from reading his own files. And disabling tags and functions won’t be foolproof either because there is always a way around that. For instance, even if you can’t read a certain file with cffile that doesn’t stop you from mailing it to yourself using cfmailparam.
So here we see the first issue with shared hosting and security: in order to combine them and get a system that is even remotely securable, the hoster needs to invest significantly in a ColdFusion 8 Enterprise Edition license and needs to figure out how to configure Sandbox Security properly. Obviously (if the hoster even decides to bother with all that in the first place) that expense gets charged to the hosted customers, making ColdFusion hosting more expensive then for instance PHP hosting.
A frequent cause for complaints among ColdFusion developers is that shared hosting for ColdFusion is hard to find, expensive and/or limited in functionality. I am not really affected by that since I don’t develop applications that are intended to be deployed in a shared hosting environment. But I do get to see the other side of the medal: at Prisma IT we offer some shared hosting (it is not an area we focus on) and I am responsible for the architecture of the platform. So in this and some followup articles I will dive into some of the complications of securing a shared hosting platform running Adobe ColdFusion 8 on Windows.
Setting the stage
These articles will specifically be about ‘inside jobs’: securing ColdFusion shared hosting customers from each other, while they run on the same ColdFusion instance. This is not about securing servers from outside threats, the starting point is some customer who can FTP his CFML templates to the server and wants to look at the templates and the database records of another customer on the same system.
For this discussion I will largely ignore the complications of combining ColdFusion with other scripting languages. While it is most certainly an interesting subject, the issues involved are too diverse for some general blogs.
Topics
The topics aren’t set in stone yet, but for now I am writing on the following blogs:
- Filesystem access and Sandbox Security
- Memory access
- ColdFusion internals
- Java, COM and .NET
- Microsoft Access
Feel free to suggest more / other content in your comments.
Adobe just released Security Bulletin APSB08-21 for a ColdFusion privilege escalation issue. The issue described in Security Bulletin APSB08-21 is only applicable to your ColdFusion installation if you are using Sandbox Security. If you have configured a Sandbox to limit access to specific parts of the filesystem it may be possible to access information outside the Sandbox. This issue is particularly important for shared hosting servers because they are the most likely to have Sandbox Security enabled.
There are patches available for ColdFusion 7.0.2, 8.0.0 and 8.0.1. At Prisma IT we have been testing the patch for ColdFusion 8.0.1 for a while now and we have not found any side effects from applying it to our shared hosting servers.
If you are a ColdFusion user and this blog post is the first you read about this issue you really should subscribe to the Adobe Security Notification Service. You will get emails for all the important security updates from Adobe and it is an invaluable tool to staying on top of security.