Golang Path Traversal Guide:
Examples and Prevention

stackhawk

StackHawk|September 20, 2021

Brief introduction to what a path traversal is, with examples, and how to fix and prevent them in Golang.

Golang consistently features among the top 10 programming languages in use across developer communities. This popularity also makes Go applications prone to all the vulnerabilities on OWASP's prevalent web application exploits list. Although not on the list, Golang path traversal is a vulnerability worth getting to know and patching applications against before it becomes their ruin.

Golang Path Traversal Guide - Picture 1 image

This post is an exploration of the path traversal attack in the context of Golang applications. Expect an examination of a few examples to highlight the logic and severity of Golang path traversal attacks. When that's established, we'll close out with a discussion of traversal attack prevention methods. We'll use the Golang proverb "Don't just check errors, handle them gracefully" as inspiration.

Let's start by laying out a clear definition of the problem at hand.

What Is Path Traversal?

Also known as a "dot-dot-dash" attack, path traversal attacks take advantage of file path logic to access and even plant files on the server machine's directories. And a Golang path traversal attack is simply when this kind of attack happens in Golang. The short version of it all is how the inclusion of "../" components in file path declarations leaves entire directory trees open for attackers to traverse outside the application's root.

Imagine leaving your entire file storage open to hackers to use as they please. The authorization that's granted by being a user of a single Golang application on your network is enough for an attacker to grant themselves higher privileges (like adding themselves to admin ranks). At the extreme, your application could get taken over, or overwritten, to hold you at ransom when you least expect it.

Golang path traversal attacks generally fall into one of two subcategories. Both of them damage a Golang application on availability, access, and integrity levels.

Snooping

This happens when attackers make efforts to learn as much as they can about your file storage directories in search of sensitive information in files. This often means more than just application file access, extending into operating system control to eventually access every other file on your network.

Such access is possible when the following command is passed from user input:

yourapplication/alloweddir/../../../../../../root/.ssh/authorized_keys

The above example escapes the allowed directories, climbing the file storage hierarchy all the way to the root folder. Being it's an absolute URL, it returns the contents of the keys file.

Insertion and Overwriting

The deletion of files in discovered paths is another goal attained by Golang path traversal efforts. When a file is removed, chances are the application will crash. Especially if it contains code required (called) across the application. What fun is crashing applications when you can overthrow the administration, right?

In the same way that we discovered the authorized keys in the snooping category, it's possible to change them. You can do this by introducing a file that's named the same yet contains different keys altogether.

With the definition out of the way, let's now give practical examples of both Golang path traversal attack categories.

Examples of Golang Path Traversal Attacks

The first example we'll look at is one often inherited from the use of Golang frameworks to create web applications. Consider the case where Golang aah was unknowingly providing access to files and information (relative and absolute) when an attacker implemented the dot-dot-dash command on the following lines of code:

.....
t.Log("Directory Listing - /assets")
resp, err = httpClient.Get(ts.URL + "/assets")
assert.Nil(t, err)
assert.Equal(t, 200, resp.StatusCode)
body := responseBody(resp)
assert.True(t, strings.Contains(body, "<title>Listing of /assets/</title>"))
assert.True(t, strings.Contains(body, "<h1>Listing of /assets/</h1><hr>"))
assert.True(t, strings.Contains(body, `<a href="robots.txt">robots.txt</a>`))
assert.Equal(t, "", resp.Header.Get(ahttp.HeaderCacheControl))
......

To start with, the above code lacks any checks for the access level of the user on the httpClient. This is the same source from which the server gets the URL components. In addition to "/assets", the URL can be laced with sequences of dashes and dots such that the operating system jumps into other directories as with the snooping example.

Where an error would be expected, you'd get access to the entire document tree (folders) by phishing a request that lands in the root directory.

Golang Path Traversal Guide - Picture 2 image

This case study is a flag from a contributor to the aah framework and raises an issue as shown in the report above. We'll get into the fix shortly.

The second example applies to web applications as well as Golang OS native apps. Dubbed the Zip-Slip, this is a clever way through which hackers can implement path traversal hits on your Golang applications. Here's how it works:

  1. The attackers hide files inside a .zip archive folder.

  2. Any number of system commands can hide as file names. For example, ../../../../../root/.ssh/privatized_keys will be a file name.

  3. The attacker extracts the archive folder inside your server through any file import form options.

  4. The attack springs into action as soon as a file with its command appears anywhere along the flow of the program.

Go ahead and scan your file storage for such files. If any appear, chances are the attack has long been active. Now for some remediation strategies.

Preventing Golang Path Traversal Attacks

It's never too late to patch your applications with the following protections against Golang path traversal attacks. Keep in mind how the use of open-source Go packages as part of your main source code can add vulnerabilities along with the desired features. This means you must keep watch and test new packages before committing them to your main source code.

Validate User Input

As your first line of defense, make sure every user input has validation against an array of rules that include how the path should look. Using the aah framework code snippet, validation can be as simple as placing a filepath.Clean() function before joining the URL and "/assets". The function comes as part of the filepath Golang package.

The resulting URL call is clean, and it inherits the following functional characteristics:

  1. All multiple separators are cut out, with just a single separator remaining.

  2. No reference to the original directory will be necessary since ./ is the default location throughout an application.

  3. All attempts to call the operating system up a level fail. Users only access specific app directories.

The moment all dot-dot-dash attempts go on mute, the input attains a clean status and can safely be passed with predictable returns. The clean function is how aah framework managers resolved their loopholes. It's also exciting to note that the same patch can prevent other user-input-related Golang application vulnerabilities such as command injections.

Another clever way to render Golang path traversal attacks useless is by using different storage options for the code and documents. While it could reduce latency as network calls will need to run for each request, they also place APIs that require authentication in between hackers and information. Better still would be a case where you reference files by links and not actual location paths on the same machine serving the application.

Add Automated Security Testing to Your Pipeline

How to Prevent Golang Path Traversal Attacks Before Production

It'd be more impressive to prevent attacks before they've evolved into active issues. The best line of thinking here would be to include checks during every commit. Never trust the end user too much to assume your use cases are all-encompassing of their intentions. To be realistic, such checks will only slow your CI/CD process down (albeit by a few seconds). Worse is if you handle checks manually or with a single test script, which is naive considering how wide the attack angle is with path traversal attacks.

It, therefore, makes sense to include automation checks in your workflow as developers append new lines. This is where StackHawk comes in handy. Knowing what parts of your code need patching before it goes live will save you a lot of costs. Let alone the embarrassment of sensitive documents leaking to the public as was the case with the WikiLeaks saga.

This post was written by Taurai Mutimutema. Taurai is a systems analyst with a knack for writing, which was probably sparked by the need to document technical processes during code and implementation sessions. He enjoys learning new technology and talks about tech even more than he writes.


StackHawk  |  September 20, 2021