Kotlin Path Traversal
Guide: Examples
and Prevention

stackhawk

StackHawk|November 19, 2021

In this article, you will gain a basic understanding of what path traversal attacks are and be capable of implementing proper mitigation strategies.

In this article, we'll explore the concept of path traversal and how it can become a critical vulnerability in your system. In addition, this article will present a brief and comprehensive guide on path traversal attacks. We'll also explore some basic examples and investigate how to moderate their impact in the Kotlin and Spring Boot development stack. 

By the time you finish reading this article, you should have a basic understanding of what path traversal attacks are and be capable of implementing proper mitigation strategies. 

I wrote this article for the Kotlin and Spring development stack. Therefore, I assume that you have experience with the technology. Please check out this Spring Boot guide for some excellent introduction info and samples if you haven't done so.

Find and Fix Application Security Vulnerabilities with Automated Testing

Kickstarting Our Demo Site

Before we jump into the theory of path traversal, let's make sure we're all on the same page. For that purpose, we'll be crafting our demo site in Spring Boot at lightspeed using the start.spring.io site. 

If you already know how to do this and just want to read the theory of the subject, don't hesitate to jump to the "Demystifying Path Traversal" section below. 

Setting Up

All right! So, if you're new to Java, all you'll need in your system is a Java Development Kit (JDK) installed—either Gradle or Maven—and your favorite integrated development environment (IDE). In our case, we'll have VSCode as our IDE and Gradle

As we mentioned above, to expedite the process, we'll start by going to https://start.spring.io and setting up our startup project. Before downloading the Zip file, remember to select Kotlin as your language, Gradle as your dependency manager, and Spring Web as your dependency.

Creating a Class File

So, once you've completed the download, proceed to the src/main/kotlin/com/example/demo folder. Then create a class file called HomeController.kt

In it, just input the following code.

package com.example.blog 

import org.springframework.stereotype.Controller

import org.springframework.ui.Model 
import org.springframework.ui.set 
import org.springframework.web.bind.annotation.GetMapping 

@Controller 
class HomeController { 
    @GetMapping("/")
    fun home(model: Model): String { 
        model["title"] = "Blog" 
        
        return "home" 
    } 
}

Now, continue to the src/main/resources/templates folder. Create header and footer templates.  

I've chosen the mustache template language for our templates. But you can make them in the language your prefer. 

header.mustache

<html> 
    <head> 
        <title>{{title}}</title> 
    </head> 
    
    <body>

footer.mustache

    </body> 
</html>

home.mustache

{{> header}} 

<h1>{{title}}</h1> 

<p>This is a test blog.</p> 

{{> footer}}

And there you go! That's all you need.  

You can now run the code with gradle bootRun in the terminal and see your website on localhost:8080.

Kotlin Path Traversal Guide: Examples and Prevention image

Blog home page 

Simple, right? 

Do you want an extensive explanation of what's going on behind the scenes? If so, please check out the Kotlin community and the Spring Boot tutorial I mentioned earlier. 

Let's move on.

Demystifying Path Traversal

Path traversal attacks take advantage of vulnerable access control settings on a server, particularly file access, to gain control of the OS file system.  

In a typical path traversal attack, an attacker tries to access sensitive files by, for example, injecting invalid or malicious input into your platform. Think of it as an injection attack, but on directories instead of databases. 

Understandably, if the attacker succeeds, that compromises the entirety of the server. Goodbye, security and service.

Further Down the Rabbit Hole

The concept of path traversal is in itself quite simple. However, it's crucial to properly understand the underlying mechanisms that can reduce its impact on our platforms. 

Examples of Path Traversal Attack

OK, you understand the theory now. But what does a path traversal attack look like in the wild? 

Here's an example of a simple CRUL attack. 

curl http://mysecuresite.com/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/.ssh/id_rsa

The objective of this attack is to gain access to folders in your server that shouldn't be accessible to users. 

The evil of path traversal lies, in my opinion, in its simplicity and conciseness. With a basic understanding of paths and the command line, you can access pretty much anywhere in a server this way.  

Let's see some other examples. 

Relative Path Attack

Relative path attacks work by abusing the user input validation, or lack thereof, with malicious string inputs. In our case, the id_rsa file contains sensitive information on our server. 

Mitigation

One approach to mitigate this vulnerability is by implementing proper user input validation.  

This strategy should be obvious to most developers, but I'm explicitly stating it anyway. Unfortunately, there are still cases where some platforms have disregarded these basic validations. Sanitizing user input and using methods like path.normalize() can go a long way in preventing disaster.

Poison Null Bytes Attack

Null bytes, represented as \0, are encoded characters that usually aren't visible and are commonly used for encoding purposes.  

When an attacker appends a \0 character at the end of an URL in an HTTP request, that person can bypass some string validations used to sanitize user input. This approach can lead to granting the attacker access to files and directories on your server. 

OK, but what would that look like in code? 

curl http://mysecuresite.com/public/%2e%2e/%2e%2e/%2e%2e/%2e%2e/%2e%2e/root/passwd%00

See the %00 at the end? Those characters could cause basic validation methods to decode to \0.txt\0. And that opens the door to give access to the passwd file.  

More Mitigation

To mitigate this attack, we need to improve basic user input validations.

val BASE_PATH: String = "/etc/user_content"
    
fun getContent(path: String): String { 
    var normalized_path: String = Paths.get(path).normalize().toString()
    var file: File = File(BASE_PATH, normalized_path)
    
    if (file.getCanonicalPath().startsWith(BASE_PATH)) { 
        val content: String = String(Files.readAllBytes(file.toPath()))

        return content
    } else { 
        return "Access Error"
    }
}

Path traversal attacks, by themselves, usually aren't sophisticated. They rely on poor access control implementations or outdated, vulnerable libraries. Still, it's crucial to understand their potential for damage and do your best to prevent them.  

Obviously, you don't want users accessing sensitive files on your server. Therefore, it's vital to implement proper validations whenever possible.  

Thankfully, doing so isn't really complicated.

Kotlin Path Traversal Guide: Examples and Prevention image

Mitigation Strategies for Path Traversal Attacks

First, let's explore a scenario where our application needs to provide access to files in different folders—profile pictures and essays, for example.  

In this situation, we could implement path validations with some hardcoded prefix values. However, this would only open our platform to prefix path traversal attacks. 

Path Prefix Validation

By inputting dots and slashes in a particular way as the user input in the application, an attacker can bypass your validation and traverse the server filesystem.  

That's quite simple but powerful. 

So, to mitigate this vulnerability, you must account for this specific pattern and ensure that your string contains only valid characters. Later, you can either strip them from your string or return an error.

Kotlin Path Traversal Guide: Examples and Prevention image

Safelists

A safelist, also called whitelist, is a list of valid possible paths that can be accessed reliably. By validating the input provided against the list, you can then weed out potential attacks.  

Also, if you designed your application to produce files in a particular scheme—say, lowercase alphanumeric characters—you can then validate that the user input conforms to this standard.

private val BASE_PATH: String = "/etc/user_content"
private val VALID_PATHS = HashSet<String>(arrayOf("/profile", "/photos", "/music").asList());

fun getContent(path: String): String { 
    var normalized_path: String = Paths.get(path).normalize().toString()

    if (VALID_PATHS.contains(normalized_path)) {
        var file: File = File(BASE_PATH, normalized_path)
        
        if (file.getCanonicalPath().startsWith(BASE_PATH)) { 
            val content: String = String(Files.readAllBytes(file.toPath()))

            return content
        } else { 
            return "Access Error"
        }
    } else { 
        return "Access Error"; 
    }
}

Adding a safelist validation to user inputs can serve as an extra layer of protection against malicious attacks. 

Kotlin Path Traversal Guide: Examples and Prevention image

Summing Up and Learning More

Finally, I want to emphasize that even though you can deliver a fair amount of security with simple validations, the best defense is always to restrict user input. Only allow specific access when absolutely needed. 

Furthermore, development kits, packages, and libraries are pretty robust and have implemented many layers of protection against path traversal. However, there's always the possibility that you introduce a vulnerability in your code unintentionally. Therefore, you can always count on the excellent Kotlin community to expand your options. 

Additionally, consider the security analysis solution StackHawk. Providing secure solutions nowadays is incredibly complex and requires a lot of work. So for developers who just want to focus on delivering quality solutions and staying productive, StackHawk offers a security test suite so that you can go back to focus on your work. You can sign up here

This post was written by Juan Reyes. Juan is an engineer by profession and a dreamer by heart who crossed the seas to reach Japan following the promise of opportunity and challenge. While trying to find himself and build a meaningful life in the east, Juan borrows wisdom from his experiences as an entrepreneur, artist, hustler, father figure, husband, and friend to start writing about passion, meaning, self-development, leadership, relationships, and mental health. His many years of struggle and self-discovery have inspired him and drive to embark on a journey for wisdom.


StackHawk  |  November 19, 2021