A directory traversal attack aims to access files and directories that are stored outside the intended folder. By manipulating files with "dot-dot-slash (../)" sequences and its variations, or by using absolute file paths, it may be possible to access arbitrary files and directories stored on the filesystem; including application source code, configuration, and other critical system files.
In this lesson, you will learn how directory traversal works and how to mitigate it in your application. You will first use a directory traversal attack to hack a vulnerable web server. We will then explain directory traversal by showing you the backend code of that vulnerable server. Finally, we will teach you how to prevent directory traversal from affecting your code.
Ready to learn? Buckle your seat belts, put on your hacker's hat, and let's get started!
Essentially, the attack is accomplished by adding characters such as ../
into a URL that serves content from a directory structure. The content is usually served from a base directory, such as /public
. An attacker can supply filenames that contain ../
or a URL encoded equivalent %2e%2e%2f
. These URLs allow the attacker to break out of the base directory and view files stored in other folders on the filesystem.
To illustrate this, let's jump into the code. Below you will find the a function, which constructs a filesystem path from the URL. All files and directories returned by the function are served statically by the web server.
Golang’s filepath.Join() will return the specified path generated from the components. To restrict access to the current working directory, we should check that the logo file path is the same as the expected base filesystem path where the image files reside.
Let’s look at how to do this by walking through the remediated code shown below. Firstly, we generate the directory component for the base path separately and assign it to the basePath variable. We then generate the absolute file path for the logo by combining the basePath and query string parameter for the logo filename. To check what the directory for the logo is, we use the filepath.Dir() function, which returns the specified directory path of the logo file without the filename on the end. We can then compare if this directory is the same as the base path and reject any requests where it’s not what we expect. You can see here in the result, our injected path would be converted to a canonical path after joining with our working directory:
If our app’s base path is “/opt/todoapp/images” and the attack payload of “../../../etc/passwd” is injected into the logo filename query string parameter, it will result in an error because the directory “/etc/” is outside the base path directory of “/opt/todoapp/images”.
To learn more about directory traversal, check out some other great content produced by Snyk:
Read our white paper on Zip Slip, a directory traversal vulnerability that results in remote code execution.
If the white paper got you worried, learn how to mitigate Zip Slip in your code-base with our cheat sheet.
Have a look at our YouTube video which further explains directory traversal and digs into a real-world example of directory traversal affecting a well-known open-source library.