{"record":{"id":"7e40e499eecdea27","repo":"projectdiscovery/nuclei","slug":"share-path-escapes-share-root-q","errorCode":null,"errorMessage":"share path escapes share root: %q","messagePattern":"share path escapes share root: %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"pkg/js/libs/smbsession/path.go","lineNumber":42,"sourceCode":"\treturn \"\", user\n}\n\n// NormalizeSharePath converts an SMB share-relative path to a clean form\n// (forward slashes, no leading slash, \".\" for share root). Rejects \"..\" escapes.\nfunc NormalizeSharePath(p string) (string, error) {\n\tp = strings.TrimSpace(p)\n\tp = strings.ReplaceAll(p, `\\`, `/`)\n\tp = strings.Trim(p, `/`)\n\tif p == \"\" || p == \".\" {\n\t\treturn \".\", nil\n\t}\n\tif strings.ContainsRune(p, 0) {\n\t\treturn \"\", fmt.Errorf(\"share path contains NUL\")\n\t}\n\tclean := path.Clean(p)\n\tclean = strings.TrimPrefix(clean, \"/\")\n\tif clean == \"..\" || strings.HasPrefix(clean, \"../\") {\n\t\treturn \"\", fmt.Errorf(\"share path escapes share root: %q\", p)\n\t}\n\tif clean == \".\" {\n\t\treturn \".\", nil\n\t}\n\treturn clean, nil\n}\n\n// RequireShareName validates a share name (no path separators).\nfunc RequireShareName(share string) error {\n\tshare = strings.TrimSpace(share)\n\tif share == \"\" {\n\t\treturn fmt.Errorf(\"share name cannot be empty\")\n\t}\n\tif strings.ContainsAny(share, `/\\`) {\n\t\treturn fmt.Errorf(\"share name must not contain path separators: %q\", share)\n\t}\n\tif strings.ContainsRune(share, 0) {\n\t\treturn fmt.Errorf(\"share name contains NUL\")","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/projectdiscovery/nuclei/blob/265b3a3dec374741614e342f813c10f8b38d2bb7/pkg/js/libs/smbsession/path.go#L24-L60","documentation":"After NormalizeSharePath cleans the input, the result must stay inside the share: if it is '..' or starts with '../' the path would traverse out of the share root and is rejected. SMB paths in this library are always share-relative, so traversal has no valid use — it signals a wrong path or hostile input.","triggerScenarios":"Calling s.ReadFile('C$', '../../../etc/passwd', 0); passing Windows backslash traversal '..\\\\..\\\\x' (normalized to forward slashes first); building paths by concatenating untrusted directory names.","commonSituations":"Templates that try Unix-style absolute paths against an SMB share; path arguments copied from URL traversal payloads; walking up from a subdirectory with a computed prefix of '..'.","solutions":["Express the location as a share name plus a share-relative path: s.ReadFile('C$', 'Windows/system32/config/sam', 0)","If the target lives under a different share, change the share argument, not the path depth","Strip or reject '..' segments from untrusted input before passing it"],"exampleFix":"// before\ns.ReadFile('C$', '../../../Windows/system32/config/sam', 0) // escapes share root\n\n// after\ns.ReadFile('C$', 'Windows/system32/config/sam', 0)","handlingStrategy":"validation","validationCode":"// Collapse traversal before calling\nfor _, seg := range strings.Split(p, \"/\") {\n    if seg == \"..\" { return errors.New(\"path escapes share root\") }\n}","typeGuard":"func isShareRelative(p string) bool {\n    c := path.Clean(strings.ReplaceAll(strings.Trim(p, \"/\"), \"\\\\\", \"/\"))\n    return c != \"..\" && !strings.HasPrefix(c, \"../\")\n}","tryCatchPattern":"normalized, err := smbsession.NormalizeSharePath(p)\nif err != nil && strings.Contains(err.Error(), \"escapes share root\") {\n    // re-anchor the path inside the share instead of retrying as-is\n    return nil, err\n}","preventionTips":["Build paths from trusted constants plus sanitized relative segments","Never pass raw traversal payloads from HTTP parameters into SMB paths","Remember paths are share-relative; change the share argument to reach elsewhere"],"tags":["validation","path-traversal","smb","input"],"backgroundTag":null,"analyzedSha":"265b3a3dec374741614e342f813c10f8b38d2bb7","analyzedAt":"2026-08-15T20:05:51.855Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}