☑️Automation & Mitigation

Automated Scanning

It is essential to understand how file inclusion attacks work and how we can manually craft advanced payloads and use custom techniques to reach remote code execution. This is because in many cases, for us to exploit the vulnerability, it may require a custom payload that matches its specific configurations. Furthermore, when dealing with security measures like a WAF or a firewall, we have to apply our understanding to see how a specific payload/character is being blocked and attempt to craft a custom payload to work around it.

We may not need to manually exploit the LFI vulnerability in many trivial cases. There are many automated methods that can help us quickly identify and exploit trivial LFI vulnerabilities. We can utilize fuzzing tools to test a huge list of common LFI payloads and see if any of them work, or we can utilize specialized LFI tools to test for such vulnerabilities.

Fuzzing Parameters

The HTML forms users can use on the web application front-end tend to be properly tested and well secured against different web attacks. However, in many cases, the page may have other exposed parameters that are not linked to any HTML forms, and hence normal users would never access or unintentionally cause harm through. This is why it may be important to fuzz for exposed parameters, as they tend not to be as secure as public ones.

ffuf -w /opt/useful/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?FUZZ=value'

Check out hacktricks most popular LFI parameters.

LFI Wordlist

We know that manual testing is more reliable and can find LFI vulnerabilities that may not be identified otherwise. However, in many cases, we may want to run a quick test on a parameter to see if it is vulnerable to any common LFI payload, which may save us time in web applications where we need to test for various vulnerabilities.

There are a number of LFI Wordlists we can use for this scan. A good wordlist is LFI-Jhaddix.txt, as it contains various bypasses and common files, so it makes it easy to run several tests at once. We can use this wordlist to fuzz the ?language= parameter we have been testing throughout the module, as follows:

ffuf -w /opt/useful/seclists/Fuzzing/LFI/LFI-Jhaddix.txt:FUZZ -u 'http://<SERVER_IP>:<PORT>/index.php?language=FUZZ'

Fuzzing Server Files

In addition to fuzzing LFI payloads, there are different server files that may be helpful in our LFI exploitation, so it would be helpful to know where such files exist and whether we can read them. Such files include: Server webroot path, server configurations file, and server logs.

Server Webroot

We may need to know the full server webroot path to complete our exploitation in some cases. For example, if we wanted to locate a file we uploaded, but we cannot reach its /uploads directory through relative paths (e.g. ../../uploads). In such cases, we may need to figure out the server webroot path so that we can locate our uploaded files through absolute paths instead of relative paths.

To do so, we can fuzz for the index.php file through common webroot paths, which we can find in this wordlist for Linux or this wordlist for Windows. Depending on our LFI situation, we may need to add a few back directories (e.g. ../../../../), and then add our index.php afterwords.

The following is an example of how we can do all of this with ffuf:

Server Logs/Configurations

As we have seen in the previous section, we need to be able to identify the correct logs directory to be able to perform the log poisoning attacks we discussed. Furthermore, as we just discussed, we may also need to read the server configurations to be able to identify the server webroot path and other important information (like the logs path!).

To do so, we may also use the LFI-Jhaddix.txt wordlist, as it contains many of the server logs and configuration paths we may be interested in. If we wanted a more precise scan, we can use this wordlist for Linux or this wordlist for Windows, though they are not part of seclists, so we need to download them first.

Once we get the locations, we will read (/etc/apache2/apache2.conf), as it is a known path for the apache server configuration:

As we can see, we do get the default webroot path and the log path. However, in this case, the log path is using a global apache variable (APACHE_LOG_DIR), which are found in another file we saw above, which is (/etc/apache2/envvars), and we can read it to find the variable values:

As we can see, the (APACHE_LOG_DIR) variable is set to (/var/log/apache2), and the previous configuration told us that the log files are /access.log and /error.log, which have accessed in the previous section.

LFI Tools

Finally, we can utilize a number of LFI tools to automate much of the process we have been learning, which may save time in some cases, but may also miss many vulnerabilities and files we may otherwise identify through manual testing. The most common LFI tools are LFISuite, LFiFreak, and liffy. We can also search GitHub for various other LFI tools and scripts, but in general, most tools perform the same tasks, with varying levels of success and accuracy.

Unfortunately, most of these tools are not maintained and rely on the outdated python2, so using them may not be a long term solution.

File Inclusion Prevention

One of the first things we need is preventing any user input to be directly passed to any back-end function that leads to file inclusion. The back-end server must be able to laod pages without requiring users to specify the files that need to be loaded.

If this is hard and can break the web application, or is to difficult to implement, we can use whitelists to specify the files that can be loaded by passing the input.

Preventing Directory Traversal

If attackers can control the directory, they can escape the web application and attack something they are more familiar with or use a universal attack chain.

Directory traversal could potentially allow attackers to do any of the following:

  • Read /etc/passwd and potentially find SSH Keys or know valid user names for a password spray attack

  • Find other services on the box such as Tomcat and read the tomcat-users.xml file

  • Discover valid PHP Session Cookies and perform session hijacking

  • Read current web application configuration and source code.

The best way to prevent directory traversal is to use your programming language's (or framework's) built-in tool to pull only the filename. For example, PHP has basename(), which will read the path and only return the filename portion. If only a filename is given, then it will return just the filename. If just the path is given, it will treat whatever is after the final / as the filename. The downside to this method is that if the application needs to enter any directories, it will not be able to do it.

If you create your own function to do this method, it is possible you are not accounting for a weird edge case. For example, in your bash terminal, go into your home directory (cd ~) and run the command cat .?/.*/.?/etc/passwd. You'll see Bash allows for the ? and * wildcards to be used as a .. Now type php -a to enter the PHP Command Line interpreter and run echo file_get_contents('.?/.*/.?/etc/passwd');. You'll see PHP does not have the same behaviour with the wildcards, if you replace ? and * with ., the command will work as expected. This demonstrates there is an edge cases with our above function, if we have PHP execute bash with the system() function, the attacker would be able to bypass our directory traversal prevention. If we use native functions to the framework we are in, there is a chance other users would catch edge cases like this and fix it before it gets exploited in our web application.

Furthermore, we can sanitize the user input to recursively remove any attempts of traversing directories, as follows:

Web Server Configuration

Several configurations may also be utilized to reduce the impact of file inclusion vulnerabilities in case they occur. For example, we should globally disable the inclusion of remote files. In PHP this can be done by setting allow_url_fopen and allow_url_include to Off.

It's also often possible to lock web applications to their web root directory, preventing them from accessing non-web related files. The most common way to do this in today's age is by running the application within Docker. However, if that is not an option, many languages often have a way to prevent accessing files outside of the web directory. In PHP that can be done by adding open_basedir = /var/www in the php.ini file. Furthermore, you should ensure that certain potentially dangerous modules are disabled, like PHP Expect mod_userdir.

If these configurations are applied, it should prevent accessing files outside the web application folder, so even if an LFI vulnerability is identified, its impact would be reduced.

Web Application Firewall (WAF)

The universal way to harden applications is to utilize a Web Application Firewall (WAF), such as ModSecurity. When dealing with WAFs, the most important thing to avoid is false positives and blocking non-malicious requests. ModSecurity minimizes false positives by offering a permissive mode, which will only report things it would have blocked. This lets defenders tune the rules to make sure no legitimate request is blocked. Even if the organization never wants to turn the WAF to "blocking mode", just having it in permissive mode can be an early warning sign that your application is being attacked.

Finally, it is important to remember that the purpose of hardening is to give the application a stronger exterior shell, so when an attack does happen, the defenders have time to defend. According to the FireEye M-Trends Report of 2020, the average time it took a company to detect hackers was 30 days. With proper hardening, attackers will leave many more signs, and the organization will hopefully detect these events even quicker.

It is important to understand the goal of hardening is not to make your system un-hackable, meaning you cannot neglect watching logs over a hardened system because it is "secure". Hardened systems should be continually tested, especially after a zero-day is released for a related application to your system (ex: Apache Struts, RAILS, Django, etc.). In most cases, the zero-day would work, but thanks to hardening, it may generate unique logs, which made it possible to confirm whether the exploit was used against the system or not.

Last updated