Here is the combined list of vulnerabilities, formatted as markdown with descriptions, impacts, ranks, mitigations, preconditions, source code analysis, and security test cases.
-
Vulnerability Name: Path Traversal in BasePathFs
-
Description:
- An attacker can bypass the base path restriction imposed by
BasePathFs
and access files outside the intended directory. - This can be achieved by crafting paths with ".." sequences that, after initial cleaning by
filepath.Clean
, still allow traversal out of the base path when combined with the internal base path handling withinBasePathFs
. - The vulnerability arises because the
RealPath
function inBasePathFs
only checks if the final joined path starts with the cleaned base path, but doesn't prevent path traversal within the name itself before joining.
- An attacker can bypass the base path restriction imposed by
-
Impact:
- High
- Unauthorized file system access: An attacker can read, and potentially modify or delete files outside the intended base path if the underlying filesystem and permissions allow it.
- Information disclosure: Attackers can read sensitive files located outside the restricted directory.
- Data integrity compromise: Attackers might be able to modify or delete files outside the base path.
-
Vulnerability Rank: High
-
Currently Implemented Mitigations:
- None. The code attempts to restrict access using
BasePathFs
, but the current implementation is flawed and bypassable.
- None. The code attempts to restrict access using
-
Missing Mitigations:
- Stronger path validation in
RealPath
to prevent traversal beyond the intended base directory. - Implement checks to disallow ".." sequences within the input
name
before path joining. - Consider using secure path manipulation functions that prevent path traversal vulnerabilities.
- Stronger path validation in
-
Preconditions:
- An application uses
BasePathFs
to restrict file system access to a specific directory. - The application allows user-controlled input to be used as file paths within the
BasePathFs
.
- An application uses
-
Source Code Analysis:
- File:
/code/basepath.go
- Function:
RealPath(name string) (path string, err error)
func (b *BasePathFs) RealPath(name string) (path string, err error) { if err := validateBasePathName(name); err != nil { // [1] Validate base path name return name, err } bpath := filepath.Clean(b.path) // [2] Clean base path path = filepath.Clean(filepath.Join(bpath, name)) // [3] Join base path and name, and clean again if !strings.HasPrefix(path, bpath) { // [4] Check if the final path starts with the base path return name, os.ErrNotExist } return path, nil }
-
Step-by-step vulnerability analysis:
validateBasePathName(name)
[1]: This function performs basic validation, but it does not prevent path traversal sequences like "..". For non-windows OS it returns nil always.filepath.Clean(b.path)
[2]: The base path is cleaned, which is good practice.filepath.Clean(filepath.Join(bpath, name))
[3]: The inputname
is joined with the base path and cleaned. However, if thename
itself contains enough ".." sequences to traverse out of thebpath
before the join and clean operation, this step can be bypassed.strings.HasPrefix(path, bpath)
[4]: This check verifies if the final path starts with the base path. But if the traversal happens withinname
before joining, this check might still pass even if the effective path is outside the intended base directory.
-
Visualization: Imagine
b.path
is/restricted/base
and attacker providesname
asbase/../sensitive/file.txt
.bpath
becomes/restricted/base
(cleaned).path
becomesfilepath.Clean(filepath.Join("/restricted/base", "base/../sensitive/file.txt"))
which simplifies to/restricted/sensitive/file.txt
.strings.HasPrefix("/restricted/sensitive/file.txt", "/restricted/base")
is TRUE. In this case, the vulnerability IS triggered and attacker can access file outside the base directory/restricted/base
.
The vulnerability occurs when the attacker can craft a path that, after joining with the base path and cleaning, still starts with the base path prefix but effectively points outside the intended base directory.
- File:
-
Security Test Case:
- Step 1: Set up a MemMapFs as the base filesystem.
- Step 2: Create a base directory
/restricted/base
within the MemMapFs. - Step 3: Create a sensitive file
/sensitive/outside.txt
outside the base directory in the MemMapFs with content "Sensitive Data". - Step 4: Create a
BasePathFs
instance with/restricted/base
as the base path, using the MemMapFs as the source. - Step 5: Attempt to open and read the sensitive file using the
BasePathFs
with the pathbase/../sensitive/outside.txt
. - Step 6: Verify that the content read from the opened file is "Sensitive Data", confirming path traversal vulnerability.
package afero_test import ( "bytes" "os" "testing" "github.com/spf13/afero" ) func TestBasePathFsPathTraversal(t *testing.T) { baseFs := afero.NewMemMapFs() basePath := "/restricted/base" sensitiveFilePath := "/sensitive/outside.txt" sensitiveData := "Sensitive Data" // Setup base filesystem baseFs.MkdirAll(basePath, 0o777) baseFs.MkdirAll("/sensitive", 0o777) afero.WriteFile(baseFs, sensitiveFilePath, []byte(sensitiveData), 0o644) // Create BasePathFs bpFs := afero.NewBasePathFs(baseFs, basePath) // Attempt to open sensitive file using path traversal traversalPath := "base/../sensitive/outside.txt" f, err := bpFs.Open(traversalPath) if err != nil { t.Fatalf("Failed to open file with traversal path: %v", err) } defer f.Close() // Read the content of the opened file buf := new(bytes.Buffer) _, err = buf.ReadFrom(f) if err != nil { t.Fatalf("Failed to read file content: %v", err) } // Verify if sensitive data is accessed, indicating path traversal if buf.String() != sensitiveData { t.Errorf("Path traversal vulnerability not detected. Expected content: '%s', Got: '%s'", sensitiveData, buf.String()) } else { t.Logf("Path traversal vulnerability successfully detected!") } }
- Vulnerability Name: Predictable Temporary File Name Generation
- Description:
- The functions
TempFile
andTempDir
(located in/code/util.go
) derive a “random” suffix using an LCG (linear congruential generator) seeded with the current time and process ID. - Because this method is not cryptographically secure, an external attacker who can write to the system’s temporary directory may predict the temporary file or directory name before it is created.
- By pre-creating (or pre-linking) the file under that name, the attacker can force file–hijacking or race–conditions.
- The functions
- Impact:
- High
- An attacker who successfully predicts the temporary file name may hijack sensitive file contents, force failures in file creation, or even influence application logic when temporary files are used in security‐sensitive contexts.
- Vulnerability Rank: High
- Currently Implemented Mitigations:
- The code uses an exclusive creation flag (
os.O_CREATE|os.O_EXCL
) when opening temporary files.
- The code uses an exclusive creation flag (
- Missing Mitigations:
- A cryptographically secure random number generator is not used to produce the temporary suffix.
- No atomic “check‐and‐create” mechanism is employed to hide the predictable randomness.
- Preconditions:
- The attacker must be able to write to (or influence) the temporary directory (typically the one returned by
os.TempDir()
). - The application uses these functions in a multiuser or publicly accessible environment.
- The attacker must be able to write to (or influence) the temporary directory (typically the one returned by
- Source Code Analysis:
- In
/code/util.go
, the file–creation loop extracts a prefix and suffix from a pattern and then composes the file name by appending the result ofnextRandom()
. - The helper function
nextRandom()
relies on a linear congruential update that makes future values predictable from the current seed. - Because the flag
os.O_CREATE|os.O_EXCL
only prevents overwrite of an already–existing file (and cannot stop an attacker from pre–creating the file), the predictable name leads to a security risk.
- In
- Security Test Case:
- In an environment where the temporary directory is writable by an attacker, call
TempFile
(orTempDir
) with a known naming pattern. - Record the current system time and process ID (or otherwise replicate the known seed conditions).
- Compute the expected “random” suffix using the same LCG algorithm as in
nextRandom()
. - Pre-create a file (or symbolic link) at the computed temporary name.
- Invoke the application function that creates a temporary file/directory and verify that file creation fails or that the new file’s content/path can be controlled by the attacker.
- Confirm that the vulnerability is exploitable by repeating the test under race conditions.
- In an environment where the temporary directory is writable by an attacker, call
- Vulnerability Name: Time‐of‐Check-to-Time‐of-Use (TOCTOU) Race in SafeWriteReader
- Description:
- The function
SafeWriteReader
(in/code/util.go
) first checks for file existence by callingExists()
and then creates the file viafs.Create()
if no file is found. - This two–step “check then act” process is not atomic.
- An attacker who can write to the target directory may create the file (or substitute a symbolic link) between the check and the creation call.
- The safe write operation can thus be hijacked or misdirected.
- The function
- Impact:
- High
- An attacker may force the safe write operation to write data into a file under attacker control or simply cause the operation to fail.
- In scenarios where the data is sensitive or the file is accessed by another privileged process, this may lead to data corruption or unauthorized data injection.
- Vulnerability Rank: High
- Currently Implemented Mitigations:
- The code uses an explicit existence check (
Exists(fs, path)
) before file creation; however, the check and creation remain separate.
- The code uses an explicit existence check (
- Missing Mitigations:
- An atomic “create‐if–not–exists” operation (or exclusive file–creation flag) is not used to remove the race window.
- There is no additional synchronization between the check and the create steps.
- Preconditions:
- The destination directory must be writable by an attacker.
- The attacker must be able to perform a file creation (or symlink insertion) between the existence check and the subsequent write operation.
- Source Code Analysis:
- In
/code/util.go
,SafeWriteReader
callsExists()
to verify that a file does not exist. - If the file is absent, it then continues to call
fs.Create(path)
and writes data. - The lack of atomicity between checking and creation permits an attacker to intervene and create the file (or a symlink) in the interim, thus hijacking or disrupting the write operation.
- In
- Security Test Case:
- Set up a controlled file system instance where the attacker can simulate concurrent file creation.
- Begin the invocation of
SafeWriteReader
with a given file path and a known input stream. - Immediately after the existence check, initiate a parallel process (or goroutine) that creates the file at the same path.
- Observe that
SafeWriteReader
either fails with an “already exists” error or writes data to a file controlled by the attacker. - Repeat the test several times to demonstrate the race condition.
- Vulnerability Name: TOCTOU Race in GCS File Creation via OpenFile
- Description:
- In the GCS–backed filesystem implementation (in
/code/gcsfs/fs.go
), theOpenFile
method handles theos.O_CREATE
flag by first callingfile.Stat()
to check whether an object exists and then, if no file is found, proceeds to create the file by callingfile.WriteString("")
. - Because these are executed as two separate operations (a “check‐then‐act” sequence), an attacker who is able to race the file creation may pre–create an object (or a malicious symbolic link) with the same name in the time gap between the existence check and the write.
- In the GCS–backed filesystem implementation (in
- Impact:
- High
- An attacker exploiting this race condition could influence the outcome of the file creation.
- This may lead to the file being created under the attacker’s control or result in unexpected file content – ultimately affecting the integrity and security of the data stored in the GCS bucket.
- Vulnerability Rank: High
- Currently Implemented Mitigations:
- The code in
OpenFile
callsfile.Stat()
before attempting creation and checks for existence; however, it does not enforce an atomic check‐and‐create operation.
- The code in
- Missing Mitigations:
- An atomic “create–if–not–exists” operation using GCS’s pre–conditions or similar measures is missing.
- There is no synchronization to bridge the gap between the separate stat and write operations.
- Preconditions:
- The attacker must have—or be able to simulate having—write access to the target GCS bucket (or be in a position to trigger a race condition by concurrently creating objects).
- The file creation request must be susceptible to interference by a concurrent process.
- Source Code Analysis:
- Within
/code/gcsfs/fs.go
, the relevant code begins by normalizing the file name and then (ifflag & os.O_CREATE
is set) performing a call tofile.Stat()
. - If
Stat()
returns no error (meaning the object exists), the function returns an error (syscall.EPERM
). - Otherwise, the code proceeds with
file.WriteString("")
to create an empty object. - The non–atomic nature of the two–step process (first checking with
Stat()
and then writing) introduces a window in which an attacker can act.
- Within
- Security Test Case:
- In a test setup (for example, using a mocked or staging GCS bucket), invoke
OpenFile
with theos.O_CREATE
flag for a chosen object name. - Immediately after the
Stat()
check but before theWriteString("")
call has been completed, simulate an attacker’s concurrent process that creates an object (or a symlink) with the same name in the bucket. - Observe that the subsequent file creation logic does not reliably prevent the creation (or overwriting) of the object by the attacker.
- Verify that the function either returns an unexpected successful creation (allowing attacker–controlled file content) or returns an error that indicates the race condition was exposed.
- Repeat this test multiple times to confirm the intermittent nature of the race condition.
- In a test setup (for example, using a mocked or staging GCS bucket), invoke
- Vulnerability Name: Path Traversal in HttpFs
- Description:
- An attacker sends a crafted HTTP request to an application using
HttpFs
. - The request path contains path traversal sequences like
../../../
. - The
httpDir.Open
function processes the path. path.Clean("/"+name)
is called, which can resolve to an absolute path outside the intended base directory. For example,../../../sensitive_file
becomes/sensitive_file
.filepath.Join
then concatenates the base path with the cleaned path. However, if the cleaned path is absolute,filepath.Join
effectively ignores the base path and uses the absolute path.HttpFs.Open
opens the file at the escaped path, allowing access outside the intended directory.
- An attacker sends a crafted HTTP request to an application using
- Impact:
- High
- An attacker can read arbitrary files from the server's filesystem that are accessible to the application process.
- This can lead to information disclosure of sensitive data, source code, configuration files, etc.
- Vulnerability Rank: High
- Currently Implemented Mitigations:
- None. The current implementation in
httpDir.Open
does not prevent path traversal.
- None. The current implementation in
- Missing Mitigations:
- Implement path validation in
httpDir.Open
after cleaning the path and before opening the file. - Verify that the resolved path remains within the intended base directory. A simple check would be to ensure the resolved path still has the base path as a prefix.
- Implement path validation in
- Preconditions:
- An application uses
afero.HttpFs
to serve static files. - The application does not implement additional path validation before using
HttpFs
. - An attacker has network access to the application.
- An application uses
- Source Code Analysis:
- File:
/code/httpFs.go
func (d httpDir) Open(name string) (http.File, error) { if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) || strings.Contains(name, "\x00") { return nil, errors.New("http: invalid character in file path") } dir := string(d.basePath) // Base path is stored in d.basePath if dir == "" { dir = "." } // Vulnerable line: path.Clean might resolve to absolute path, escaping basePath f, err := d.fs.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))) if err != nil { return nil, err } return f, nil }
- Step-by-step vulnerability analysis:
The vulnerability is in the line
f, err := d.fs.Open(filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))))
.path.Clean("/"+name)
can return an absolute path, and when joined withdir
usingfilepath.Join
, it can bypass the intended base path restriction.
- File:
- Security Test Case:
- Create a directory
/tmp/afero_httpfs_test_base
as the base directory forHttpFs
. - Inside
/tmp/afero_httpfs_test_base
, create a fileindex.html
with content "Hello from base directory". - Outside
/tmp/afero_httpfs_test_base
, create a sensitive file/tmp/sensitive.txt
with content "This is sensitive data.". - Write a Go program that uses
afero.HttpFs
to serve files from/tmp/afero_httpfs_test_base
usinghttp.FileServer
.
package main import ( "log" "net/http" "github.com/spf13/afero" ) func main() { baseFs := afero.NewOsFs() httpFs := afero.NewHttpFs(baseFs) fs := http.FileServer(httpFs.Dir("/tmp/afero_httpfs_test_base")) http.Handle("/", fs) log.Println("Server listening on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) }
- Run the Go program.
- Send an HTTP GET request to
http://localhost:8080/index.html
. Verify that you receive "Hello from base directory". - Send a crafted HTTP GET request to
http://localhost:8080/../../../tmp/sensitive.txt
. - Check the HTTP response body. If the response body contains "This is sensitive data.", then path traversal vulnerability exists.
- Create a directory