Rails 4 path traversal possible? - security

The app I'm working on has a controller that issues templates to the front end (single page app). It's very basic, and simply consists of
#path = params[:path]
render template: "templates/#{#path}", layout: nil
Here my concern however is the direct use of the users input. Everything about this to me feels like it can be attacked with something as simple as path traversal. The route for this is
get "/templates/:path.html" => "templates#file", constraints: { path: /.+/ }, defaults: { format: 'html' }
I've tried multiple things to attempt a path traversal attack, such as
request /templates/path/to/../somewhere/else.html
request /templates?path=/path/to/../../something.rb
request /templates/index.html?path=/path/to/../../config/something.html
request /templates/path/../../../file.html
Fortunately, I haven't had any success with this. The requests that just start with /templates and don't specify anything after it, don't match the route thanks to the constraint so that is good.
It seems as though when that route is matched, rails doesn't allow you to override the path parameter through a url parameter, so I don't seem to be able to inject it there.
The ones that interest are the first and last examples above, where rails seems to internally be changing the requested URL before invoking the routes file. When I request /templates/path/to/../somewhere/else.html, my console output shows a request for /templates/path/somewhere/else.html. When I make a request for /templates/path/../../../file.html, the log shows a request for /file.html.
Am I missing something somewhere that will leave the app open to security issues, or is this just rails being sensible and protecting itself for me?
UPDATE
I've done some more digging, and if I try doing some URL encoding then I can cause the server to simply not respond at all. If I request /templates/%2e%2e%2f%2e%2e%2f%2e%2e%2ffresult.html then I just get an empty response with a connection: close header.
I assume that the parameter parser higher up in the rack is checking all urls for this type of attack? Regardless, my original question still stands. Am I missing something here?

Related

Database context not allowed

We have a cluster with 3 servers with Load Balancer in front (CloudFlare). Things worked well when we had 2 servers (A & B) in the cluster but after we added a 3-rd server (C) we noticed few odd things.
One of them is quite important and I do not understand how it happens at all.
Our web application makes AJAX requests to itself in order to get some JSON data back and if requests hit new server (C) response looks like that:
{
code: 404,
text: "Not Found",
message: "Database context not allowed."
}
Our application does not throw such error and so I searched in google a bit and noticed that it's mentioned on: OpenNTF XPagesExtensionLibrary
However, we do not use XPages at all so I wonder how could it be that our AJAX requests somehow involve that logic.
Any suggestion & tip would be appreciated.
UPDATE
The backend code of my agent is not important (it could be also an empty agent, I checked), because the request does not come to my agent.
The AJAX call is triggered by jQuery
let url = "domain.tld/api/key";
let params = {"a": 1};
$.post(url, params, function (data) {
// some code
},
"json"
).always(function() {
// some code
});
The URL, which I suspect is an issue starts with /api/key and I believe it's an issue (because all other ajax calls where endpoint do not start from /api/ work well).
Thanks.
Figured that our with help from comments (which you can see under my original post).
Apparently there is DAS servlet that handles all requests starting from /api/* and it runs if XPages engine is loaded.
In my case the 2 servers out of 3 have XPages shut down so the issue happened only on 1 server.
The solution would be:
Shut down XPages (or find a way to shut down DAS).
Alternatively change a URL from /api/path to something else (this is what we will do).

How to fill login prompt with Webdriver IO?

I'm working on a CLI with OCLIF. In one of the commands, I need to simulate a couple of clicks on a web page (using the WebdriverIO framework for that). Before you're able to reach the desired page, there is a redirect to a page with a login prompt. When I use WebdriverIO methods related to alerts such as browser.getAlertText(), browser.sendAlertText() or browser.acceptAlert, I always get the error no such alert.
As an alternative, I tried to get the URL when I am on the page that shows the login prompt. With the URL, I wanted to do something like browser.url(https://<username>:<password>#<url>) to circumvent the prompt. However, browser.url() returns chrome-error://chromewebdata/ as URL when I'm on that page. I guess because the focus is on the prompt and that doesn't have an URL. I also don't know the URL before I land on that page. When being redirected, a query string parameter containing a token is added to the URL that I need.
A screenshot of the prompt:
Is it possible to handle this scenario with WebdriverIO? And if so, how?
You are on the right track, probably there are some fine-tunings that you need to address to get it working.
First off, regarding the chrome-error://chromewebdata errors, quoting Chrome DOCs:
If you see errors with a location like chrome-error://chromewebdata/
in the error stack, these errors are not from the extension or from
your app - they are usually a sign that Chrome was not able to load
your app.
When you see these errors, first check whether Chrome was able to load
your app. Does Chrome say "This site can't be reached" or something
similar? You must start your own server to run your app. Double-check
that your server is running, and that the url and port are configured
correctly.
A lot of words that sum up to: Chrome couldn't load the URL you used inside the browser.url() command.
I tried myself on The Internet - Basic Auth page. It worked like a charm.
URL without basic auth credentials:
URL WITH basic auth credentials:
Code used:
it('Bypass HTTP basic auth', () => {
browser.url('https://admin:admin#the-internet.herokuapp.com/basic_auth');
browser.waitForReadyState('complete');
const banner = $('div.example p').getText().trim();
expect(banner).to.equal('Congratulations! You must have the proper credentials.');
});
What I'd do is manually go through each step, trying to emulate the same flow in the script you're using. From history I can tell you, I dealt with some HTTP web-apps that required a refresh after issuing the basic auth browser.url() call.
Another way to tackle this is to make use of some custom browser profiles (Firefox | Chrome) . I know I wrote a tutorial on it somewhere on SO, but I'm too lazy to find it. I reference a similar post here.
Short story, manually complete the basic auth flow (logging in with credentials) in an incognito window (as to isolate the configurations). Open chrome://version/ in another tab of that session and store the contents of the Profile Path. That folder in going to keep all your sessions & preserve cookies and other browser data.
Lastly, in your currentCapabilities, update the browser-specific options to start the sessions with a custom profile, via the '--user-data-dir=/path/to/your/custom/profile. It should look something like this:
'goog:chromeOptions': {
args: [
'--user-data-dir=/Users/iamdanchiv/Desktop/scoped_dir18256_17319',
],
}
Good luck!

instagram feed on website

I'm trying to display an Instagram feed on my website. I am not familiar with server side scripts. The explanations I have read go over my head.
I have managed to get an access key through Instagram but I don't know how to proceed.
I also tried the instructions from a video "How to implement an Instagram Feed in PHP without using the Instagram API"
For that, my php file works, displays the feed but I don't understand how to make it appear on my html page.
I'd be happy to make either method work.
In your HTML you should include a JavaScript 'script' element where you do something called an AJAX Request. The request will access the PHP file that produces the Instagram feed. ECHO the result from the PHP file and it will be returned into the DATA variable in the AJAX request. The request looks roughly like this:
$.ajax({
method: ‘POST or GET, most likely POST’,
url: ‘not always an actual url, this is the path to your PHP file’,
success: function(data) {
//php file returns into variable ‘data’
//display the feed within this function
}
});
The result from the PHP file should be echo’d back in JSON notation, if it isn’t, just add this to end of PHP the file:
$whatever = json_encode($your_result);
echo $whatever; //this is still the feed, just encoded in JSON
And if you need to decode it for some reason, just do
json_decode($variable_to_decode);
There are other parameters you can use in the AJAX request, like if for instance you need access to certain variables in your PHP file, that currently only exist in your HTML document.
this was solved by saving the html as php. i don't know why that is but it worked

Is it possible to rewrite url (with extra parameters) with a Chrome extension

I am trying to append few extra parameters to the url that user typed (before the page gets loaded). Is it possible to do?
For example, if user types www.google.com, I would like to append ?q=query to url (final: www.google.com?q=query.
Thanks
The webRequest API might be what you need. This code goes in your background page:
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if( details.url == "http://www.google.com/" )
return {redirectUrl: "http://www.google.com/?q=defaultquery" };
},
{urls: ["http://www.google.com/*"]},
["blocking"]);
This is an extremely specific rule that redirects visits to http://www.google.com/ with http://www.google.com/?q=defaultquery, but I think you can see how to expand it to include more functionality.
Note that this will reroute all attempts to reach http://www.google.com/, including Ajax requests and iframes.
Per the documentation, you will need to add the webRequest and webRequestBlocking permissions, along with host permissions for every host you plan to intercept:
"permissions": [
"webRequest",
"webRequestBlocking",
"*://*.google.com/",
...
],
This is an old question still I am answering it for future readers.
Modification of query parameters is a little tricky because you can endup in an infinite loop and Chrome/Firefox may detect it and process whatever is the current state of the request URL.
I have faced this situation in my chrome extension Requestly where Users used Replace Rule and replaced www.google.com with www.google.com?q=query or did something similar.
The problem with this approach is browsers intercept the request URL after adding query parameter so the parameter will be added multiple times and corrupt the URL. So you have to ensure either of the following:-
Do not intercept a request once it has been redirected.
Check if the parameter already exists, then do not redirect it.
As correctly pointed out by #apsillers in his answer, you have to use webRequest API to perform any modifications to the URL. Please have a look at his answer
and write your code accordingly.
Just in case, you are looking for an already available solution, consider trying Requestly's Query Parameter Rule. Here is a screenshot of how it looks like:-
For Firefox, you can download Requestly from its home page.

Symfony2 and Uploadify : security token is not kept

I'm trying to make Symfony2 and Uploadify working together in a secured area.
(Uploadify is a flash/javascript component used to upload multiple files)
My uploadify component is working fine when the back-end script route is out of the secure area, but when in this area, I get a 302 HTTP error.
The log message is :
security.INFO: Authentication exception occurred; redirecting to authentication entry point (A Token was not found in the SecurityContext.)
While searching for an answer, I found that passing the PHPSESSID to the back-end script as a post parameter should work on "non-framework php" but with the Symfony Security Component, it seems that this script is not even reached.
Does anyone know if there is a way to send that token to the back-end script while keeping this script in the secure area ?
After having a read around the Symfony website, it's possible that this solution may work (haven't tested it). In your security.yml file, change your access_contol config option to something like this...
access_control:
- { path: ^/path/to/flash_component, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Where /path/to/flash_component is the URL you are uploading to in Uploadify. Let me know if that works.

Resources