VYPR
High severityNVD Advisory· Published Sep 28, 2022· Updated May 21, 2025

CVE-2022-40082

CVE-2022-40082

Description

Hertz v0.3.0 ws discovered to contain a path traversal vulnerability via the normalizePath function.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Hertz v0.3.0 contains a path traversal vulnerability in normalizePath due to improper backslash handling, allowing attackers to read arbitrary files.

CVE-2022-40082 is a path traversal vulnerability in the Hertz Go HTTP framework (version 0.3.0). The root cause lies in the normalizePath function, which failed to restrict backslash characters (\). This allowed path traversal sequences such as ..\ to bypass the intended normalization logic, enabling access to files outside the web root directory [1].

An attacker can exploit this by sending a crafted HTTP request containing backslash-based traversal patterns (e.g., ..\..\etc\passwd) to a vulnerable Hertz endpoint that serves static files or processes URL paths. No authentication is required if the endpoint is publicly accessible, making the attack surface broad for any application using the affected version [1][4].

Successful exploitation permits reading arbitrary files from the server's filesystem, leading to information disclosure of sensitive data such as configuration files, credentials, or source code. The vulnerability has been assigned a CVSS score (as recorded in the NVD) reflecting its potential for high impact [3].

The issue was addressed in pull request #229, which replaced all backslashes with forward slashes before normalization, effectively neutralizing traversal attempts. The fix was included in Hertz version 0.3.1 and later. Users are strongly advised to upgrade to the latest version to mitigate the risk [1][2].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/cloudwego/hertzGo
< 0.3.10.3.1

Affected products

3

Patches

1
dcb0b5a1861a

fix: fix path-traversal bug (#229)

https://github.com/cloudwego/hertzruokeqxSep 8, 2022via ghsa-ref
6 files changed · +102 38
  • internal/bytestr/bytes.go+1 0 modified
    @@ -28,6 +28,7 @@ var (
     )
     
     var (
    +	StrBackSlash        = []byte("\\")
     	StrSlash            = []byte("/")
     	StrSlashSlash       = []byte("//")
     	StrSlashDotDot      = []byte("/..")
    
  • internal/bytestr/bytes.go+1 0 modified
    @@ -28,6 +28,7 @@ var (
     )
     
     var (
    +	StrBackSlash        = []byte("\\")
     	StrSlash            = []byte("/")
     	StrSlashSlash       = []byte("//")
     	StrSlashDotDot      = []byte("/..")
    
  • pkg/protocol/uri.go+32 19 modified
    @@ -43,6 +43,7 @@ package protocol
     
     import (
     	"bytes"
    +	"path/filepath"
     	"sync"
     
     	"github.com/cloudwego/hertz/internal/bytesconv"
    @@ -318,9 +319,9 @@ func (u *URI) SetHostBytes(host []byte) {
     //
     // Examples:
     //
    -//    * For /foo/bar/baz.html path returns baz.html.
    -//    * For /foo/bar/ returns empty byte slice.
    -//    * For /foobar.js returns foobar.js.
    +//   - For /foo/bar/baz.html path returns baz.html.
    +//   - For /foo/bar/ returns empty byte slice.
    +//   - For /foobar.js returns foobar.js.
     func (u *URI) LastPathSegment() []byte {
     	path := u.Path()
     	n := bytes.LastIndexByte(path, '/')
    @@ -334,14 +335,14 @@ func (u *URI) LastPathSegment() []byte {
     //
     // The following newURI types are accepted:
     //
    -//     * Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
    -//       uri is replaced by newURI.
    -//     * Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
    -//       the original scheme is preserved.
    -//     * Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
    -//       of the original uri is replaced.
    -//     * Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
    -//       is updated according to the new relative path.
    +//   - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
    +//     uri is replaced by newURI.
    +//   - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
    +//     the original scheme is preserved.
    +//   - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
    +//     of the original uri is replaced.
    +//   - Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
    +//     is updated according to the new relative path.
     func (u *URI) Update(newURI string) {
     	u.UpdateBytes(bytesconv.S2b(newURI))
     }
    @@ -350,14 +351,14 @@ func (u *URI) Update(newURI string) {
     //
     // The following newURI types are accepted:
     //
    -//     * Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
    -//       uri is replaced by newURI.
    -//     * Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
    -//       the original scheme is preserved.
    -//     * Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
    -//       of the original uri is replaced.
    -//     * Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
    -//       is updated according to the new relative path.
    +//   - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
    +//     uri is replaced by newURI.
    +//   - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
    +//     the original scheme is preserved.
    +//   - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
    +//     of the original uri is replaced.
    +//   - Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
    +//     is updated according to the new relative path.
     func (u *URI) UpdateBytes(newURI []byte) {
     	u.requestURI = u.updateBytes(newURI, u.requestURI)
     }
    @@ -484,6 +485,18 @@ func normalizePath(dst, src []byte) []byte {
     	dst = addLeadingSlash(dst, src)
     	dst = decodeArgAppendNoPlus(dst, src)
     
    +	// Windows server need to replace all backslashes with
    +	// forward slashes to avoid path traversal attacks.
    +	if filepath.Separator == '\\' {
    +		for {
    +			n := bytes.IndexByte(dst, '\\')
    +			if n < 0 {
    +				break
    +			}
    +			dst[n] = '/'
    +		}
    +	}
    +
     	// remove duplicate slashes
     	b := dst
     	bSize := len(b)
    
  • pkg/protocol/uri.go+32 19 modified
    @@ -43,6 +43,7 @@ package protocol
     
     import (
     	"bytes"
    +	"path/filepath"
     	"sync"
     
     	"github.com/cloudwego/hertz/internal/bytesconv"
    @@ -318,9 +319,9 @@ func (u *URI) SetHostBytes(host []byte) {
     //
     // Examples:
     //
    -//    * For /foo/bar/baz.html path returns baz.html.
    -//    * For /foo/bar/ returns empty byte slice.
    -//    * For /foobar.js returns foobar.js.
    +//   - For /foo/bar/baz.html path returns baz.html.
    +//   - For /foo/bar/ returns empty byte slice.
    +//   - For /foobar.js returns foobar.js.
     func (u *URI) LastPathSegment() []byte {
     	path := u.Path()
     	n := bytes.LastIndexByte(path, '/')
    @@ -334,14 +335,14 @@ func (u *URI) LastPathSegment() []byte {
     //
     // The following newURI types are accepted:
     //
    -//     * Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
    -//       uri is replaced by newURI.
    -//     * Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
    -//       the original scheme is preserved.
    -//     * Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
    -//       of the original uri is replaced.
    -//     * Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
    -//       is updated according to the new relative path.
    +//   - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
    +//     uri is replaced by newURI.
    +//   - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
    +//     the original scheme is preserved.
    +//   - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
    +//     of the original uri is replaced.
    +//   - Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
    +//     is updated according to the new relative path.
     func (u *URI) Update(newURI string) {
     	u.UpdateBytes(bytesconv.S2b(newURI))
     }
    @@ -350,14 +351,14 @@ func (u *URI) Update(newURI string) {
     //
     // The following newURI types are accepted:
     //
    -//     * Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
    -//       uri is replaced by newURI.
    -//     * Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
    -//       the original scheme is preserved.
    -//     * Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
    -//       of the original uri is replaced.
    -//     * Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
    -//       is updated according to the new relative path.
    +//   - Absolute, i.e. http://foobar.com/aaa/bb?cc . In this case the original
    +//     uri is replaced by newURI.
    +//   - Absolute without scheme, i.e. //foobar.com/aaa/bb?cc. In this case
    +//     the original scheme is preserved.
    +//   - Missing host, i.e. /aaa/bb?cc . In this case only RequestURI part
    +//     of the original uri is replaced.
    +//   - Relative path, i.e.  xx?yy=abc . In this case the original RequestURI
    +//     is updated according to the new relative path.
     func (u *URI) UpdateBytes(newURI []byte) {
     	u.requestURI = u.updateBytes(newURI, u.requestURI)
     }
    @@ -484,6 +485,18 @@ func normalizePath(dst, src []byte) []byte {
     	dst = addLeadingSlash(dst, src)
     	dst = decodeArgAppendNoPlus(dst, src)
     
    +	// Windows server need to replace all backslashes with
    +	// forward slashes to avoid path traversal attacks.
    +	if filepath.Separator == '\\' {
    +		for {
    +			n := bytes.IndexByte(dst, '\\')
    +			if n < 0 {
    +				break
    +			}
    +			dst[n] = '/'
    +		}
    +	}
    +
     	// remove duplicate slashes
     	b := dst
     	bSize := len(b)
    
  • pkg/protocol/uri_test.go+18 0 modified
    @@ -43,6 +43,7 @@ package protocol
     
     import (
     	"fmt"
    +	"path/filepath"
     	"reflect"
     	"testing"
     
    @@ -228,3 +229,20 @@ func testURIFullURI(t *testing.T, scheme, host, path, hash string, args *Args, e
     		t.Fatalf("Unexpected URI: %q. Expected %q", uri, expectedURI)
     	}
     }
    +
    +func TestParsePathWindows(t *testing.T) {
    +	t.Parallel()
    +
    +	testParsePathWindows(t, "/../../../../../foo", "/foo")
    +	testParsePathWindows(t, "/..\\..\\..\\..\\..\\foo", "/foo")
    +	testParsePathWindows(t, "/..%5c..%5cfoo", "/foo")
    +}
    +
    +func testParsePathWindows(t *testing.T, path, expectedPath string) {
    +	var u URI
    +	u.Parse(nil, []byte(path))
    +	parsedPath := u.Path()
    +	if filepath.Separator == '\\' && string(parsedPath) != expectedPath {
    +		t.Fatalf("Unexpected Path: %q. Expected %q", parsedPath, expectedPath)
    +	}
    +}
    
  • pkg/protocol/uri_test.go+18 0 modified
    @@ -43,6 +43,7 @@ package protocol
     
     import (
     	"fmt"
    +	"path/filepath"
     	"reflect"
     	"testing"
     
    @@ -228,3 +229,20 @@ func testURIFullURI(t *testing.T, scheme, host, path, hash string, args *Args, e
     		t.Fatalf("Unexpected URI: %q. Expected %q", uri, expectedURI)
     	}
     }
    +
    +func TestParsePathWindows(t *testing.T) {
    +	t.Parallel()
    +
    +	testParsePathWindows(t, "/../../../../../foo", "/foo")
    +	testParsePathWindows(t, "/..\\..\\..\\..\\..\\foo", "/foo")
    +	testParsePathWindows(t, "/..%5c..%5cfoo", "/foo")
    +}
    +
    +func testParsePathWindows(t *testing.T, path, expectedPath string) {
    +	var u URI
    +	u.Parse(nil, []byte(path))
    +	parsedPath := u.Path()
    +	if filepath.Separator == '\\' && string(parsedPath) != expectedPath {
    +		t.Fatalf("Unexpected Path: %q. Expected %q", parsedPath, expectedPath)
    +	}
    +}
    

Vulnerability mechanics

Root cause

"The normalizePath function in Hertz v0.3.0 does not replace backslash characters with forward slashes on Windows, allowing path traversal sequences containing backslashes to bypass sanitization."

Attack vector

An attacker sends an HTTP request with a crafted URI path containing backslash-based path traversal sequences (e.g., `/..\..\..\..\..\foo` or URL-encoded variants like `/..%5c..%5cfoo`). On Windows servers, the `normalizePath` function [patch_id=1641302] does not convert backslashes to forward slashes before processing, so the traversal payload is not neutralized. This allows the attacker to escape the intended directory and access or disclose arbitrary files on the server.

Affected code

The vulnerable code is in `pkg/protocol/uri.go` within the `normalizePath` function [patch_id=1641302]. The function decodes and normalizes URI paths but lacked any handling of backslash characters, which are treated as path separators on Windows. The patch also adds a `StrBackSlash` constant in `internal/bytestr/bytes.go` and new test cases in `pkg/protocol/uri_test.go`.

What the fix does

The patch adds a loop in `normalizePath` [patch_id=1641302] that replaces every backslash (`\`) with a forward slash (`/`) when running on Windows (`filepath.Separator == '\\'`). It also adds the `StrBackSlash` constant to `internal/bytestr/bytes.go` for reuse. By normalizing backslashes before the duplicate-slash removal and path-cleaning logic runs, the fix ensures that traversal sequences like `..\..` are treated identically to `../../` and are properly collapsed.

Preconditions

  • configThe Hertz server must be running on a Windows operating system where filepath.Separator is '\'.
  • networkThe attacker must be able to send HTTP requests with arbitrary URI paths to the server.

Generated on May 23, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.