Arbitrary File Write
Being able to create or overwrite files on a server, often causing Remote Code Execution (RCE)
Last updated
Being able to create or overwrite files on a server, often causing Remote Code Execution (RCE)
Last updated
Using techniques similar to Local File Disclosure, it may be possible to write files at arbitrary locations on a system. An easy way to confirm if this is the case in a blind scenario is to try to write a file to special locations and observe any errors or responses:
/tmp
should always work because every user has all permissions here
/root
likely won't work if you are not the root
user, and may result in "Permission denied"
/nonexistant
or any random name may give an error saying the directory wasn't found
Then, depending on the system, you need to decide what file to create or overwrite. Many ways exist to obtain Remote Code Execution but there often isn't one silver bullet that always works, so this requires some experimenting and knowledge of the server's backend.
This page collects some known ways to achieve RCE or other privileged access on the server. When a method does not require completely controlling the full content of the file, it is considered a 'dirty' write because random data may come before or after it.
One simple and often consistent way to execute arbitrary commands is to write your own code in a file that the server executes. This may be direct source code or other files that include code like templates which will be executed.
Writing source code can go in multiple ways. If the directory where you are writing files to, like /uploads/
, already allows executing files with the correct extension as source code, you can just upload it here and execute it once you visit the location. Most often, however, these directories are protected from code execution and you have to find a place where the original source code of the web application lives, as these will always be executable. See for some common locations.
Note: Even if you overwrite source code, it might not be directly executed when you visit the page because it is compiled and won't be reloaded until the server is restarted. You may be able to trigger this by crashing the server, or just be patient until this happens naturally.
.php
, .php7
, .phtml
, .phar
, etc.) - dirtyBypass <?php
filter with alternative prefixes:
.py
, .pyc
)You can also create a compiled .pyc
file which can be executed just like any other source code file:
.js
, .mjs
).asp
, .aspx
) - dirtyNot only user-created code can be overwritten, sometimes a program does not reload its source code while running. For those situations, another trick that may work is to overwrite libraries that are loaded. If you have permissions to overwrite a Python .py
file inside the packages folder, or can overwrite a JAR file for Java applications, it could grant code execution again.
Specific to Python, one trick shared in this article involves a .pth
file stored in ~/.local/lib/pythonX.Y/site-packages
. These files are automatically parsed when starting a new Python process to load the package paths, but has one interesting behaviour that we can exploit:
The above shows that if any line starts with import
, that whole line is executed. By using ;
semicolons we can add arbitrary statements to this single line and execute any code we like:
The above will execute when the correct Python version is launched even when the "ANYTHING" part is invalid syntax, it only needs to be valid UTF-8.
If source code is not writable or isn't reloaded, another simple method is overwriting templates that can execute code. There are many different templating engines that all use their own syntax and context, some more restricted than others. But most of them have ways to execute arbitrary code or at least read some secrets. Read the full Server-Side Template Injection page to see if your case fits:
Here are a few easy examples:
If you cannot directly write or execute source code, the configuration of an application or server can often also have large exploitable areas. You may be able to set shell commands directly in here, or change the configuration in some way to aid another method.
.ssh/authorized_keys
- dirtyWhen SSH is set up on a server, every shell user can have an .ssh/
directory inside their home directory containing their public and private key, as well as an authorized_keys
file that contains all the public keys allowed to log in as this user, separated by newlines.
When you have access to SSH port 22 on a server, this is often a very clean way to execute code as the target user. You can grab your own public key from ~/.ssh/id_rsa.pub
or generate one with ssh-keygen
if you haven't already, then write its contents to the /home/$USER/.ssh/authorized_keys
file on the server and log in:
Default installations of SSH don't allow logging in as root
. To check this look at the PermitRootLogin
option in /etc/ssh/sshd_config
:
SSH only splits this file by \n
newline characters and parse all sections as possible public keys. That means a dirty write where random data is before and/or after our payload is possible to exploit by adding newlines before and after our public key. Create a valid PNG that is also a backdoored authorized_keys
file, for example:
.htaccess
When uploading files, rules are often set on the upload directory to prevent .php
files from executing, or these extensions are simply blocked by a filter. In such cases, a file named .htaccess
could configure an Apache server to change the behaviour of a directory.
The main idea is to add another file extension that you are allowed to upload to be able to execute PHP code, and you can even specify an encoding like UTF-7 to bypass filters. See the following writeup for an example of exploiting this from start to finish:
The repository below shows some more techniques using .htaccess
file to get RCE:
Using the @()
syntax, you can define uWSGI configuration anywhere in a file that executes system commands when loaded. Similar to the authorized keys, this can be put into PNG metadata, for example, which will include it in a valid PNG image:
Payloads can be locally tested using the following command:
When written to the server, it may take some time before the payload is executed. If the server is configured to auto-reload using the py-auto-reload =
configuration variable it may happen automatically, but otherwise, you need to either force a restart by crashing the application or just wait until a server admin does it for you.
Overwriting the settings of an application can have a significant effect on security. As the above showed, there are often many sensitive options and you just have to find them in the documentation or with some educated guessing.
Some formats like YAML may even be so complex that they allow arbitrary instantiation of classes, resulting in Insecure Deserialization. Keep this in mind when evaluating overwriting such a config file, that you don't necessarily need to exploit an option if you can exploit the format itself.
Some databases like SQLite store all their data in local files. While this makes it simple, it also allows you to overwrite these files with any data of your choice. Suddenly, you control every bit of data, expanding the attack surface greatly as developers might not expect some generated data to be user-controlled.
One common example is through Deserialization exploits like session data. Other applications might also store custom bits of data that are included in shell commands. After locally creating the same database structure with your injected data, write the file to the location. Often a reload is not required as databases change all the time, so it should instantly have an effect.
/etc/passwd
The root user may edit /etc/passwd
to add another root-level user with your own password. Then you can log in as that user and get root privileges:
Then if you somehow append the following line to the /etc/passwd
file, you will be able to log in as root using the password "hacker":
Shell scripts inherently execute shell commands, often being the end goal of exploiting an arbitrary file write vulnerability. Therefore, they should be large targets and are often easy to exploit.
Some scripts execute on a schedule to automatically exploit, others are triggered by some action on the application or server, and you could even backdoor profile scripts that run when an admin interactively logs in.
Cron Jobs are scheduled tasks on a Linux system that are automatically triggered by the daemon. Here, you can create a file that executes every minute, for example. The next time this minute is triggered your payload will execute. The syntax for such a file looks something like this:
There are multiple places for such files, but often these are only writable by the root
user.
/etc/crontab
: Cron syntax for global use by any user.
/etc/cron.d
: Directory containing files with cron syntax often separated per application.
/var/spool/cron/crontabs/$USER
: File per user with cron syntax, often edited manually with crontab -e
. The filename is the username it executes as.
/etc/cron.hourly
, .daily
, etc.: Bash scripts that cron will also execute every hour, day, week or month. These may be dirty too as they are only bash scripts and don't require special syntax.
Another way is creating a backdoor in the user's home directory. For Bash, the ~/.bashrc
file is most common as it executes in any non-login interactive shell. However, for login shells like SSH, a few more are executed in the following order. The first readable file is the only one that executes:
~/.bash_profile
~/.bash_login
~/.profile
Often the above files execute ~/.bashrc
as well to make sure login shells work similarly to non-login ones, so this is often your best bet. Here is a table that shows what Bash by itself:
bash -c [command]
NO
NO
bash [command]
NO
NO
echo [command] | bash
NO
NO
[command]
NO
NO
ssh ... [command]
YES
YES
login
, bash -l
NO
YES
bash
YES
NO
See this article to get a full understanding, as well as the source code.
As this only requires writing a bash script at the location, it may include any other garbage data before/after your payload if only it is separated by newlines. Bash will ignore syntax errors and keep executing commands until it exits or the end of the file is reached:
Common places to find such data are session files, as these often map a session ID to an object with all properties of a user. If you can overwrite these you may be able to invoke an Insecure Deserialization the next time you use that session ID and the application tries to load its data.
In PHP, these are stored by default in the /var/lib/php/sessions/
directory with names like sess_[PHPSESSID]
where your PHPSESSID
cookie is inserted into the path. That means you can write a file like /var/lib/php/sessions/sess_exploit
with a malicious serialized payload, and when you visit the page with a PHPSESSID=exploit
cookie you will trigger the deserialization payload when session_start();
is called.
Here's an example where we set the x
property of $_SESSION
to a custom deserialization gadget:
If the image is transformed in some way, metadata comments like these may not survive. We can still put our raw data into a BMP file because it isn't compressed (see ).
One example is the .env
file which often replaces environment variables for an application. If a Flask webserver uses this file to get its SECRET_KEY
variable, for example, you will be able to forge any session as explained in with your known key.
Many programming languages have ways of serializing and deserializing complex classes into bytes and back. This can sometimes be dangerous when arbitrary classes can be instantiated, called 'Insecure Deserialization'. The level of complexity varies a lot depending on the programming language and library used. Python is very easy, for example, while PHP or Java often require gadgets in well-known libraries.