Frontend Labs
⋯

Day 71 — Path Traversal & Command Injection

Learning Objectives

  • Detect path traversal in all encoded forms: raw, URL-encoded, double-encoded, unicode
  • Detect command injection operators
  • Understand why SSRF is a path traversal variant at the URL level

Exercise: Path Traversal + Command Injection Rules

package waf

import (
    "net/http"
    "net/url"
    "regexp"
    "strings"
)

var (
    // Path traversal in various encodings
    pathTraversalPatterns = []struct{ pattern, name string }{
        {`\.\./`, "dot-dot-slash"},
        {`\.\.\\`, "dot-dot-backslash"},
        {`%2e%2e%2f`, "url-encoded ../"},
        {`%2e%2e/`, "partial url-encoded ../"},
        {`\.%2e/`, "partial url-encoded ../"},
        {`%252e%252e%252f`, "double url-encoded ../"},
        {`%c0%ae%c0%ae/`, "unicode dot ../"},
        {`\x00`, "null byte"},
    }

    // Command injection operators
    cmdInjectionRe = regexp.MustCompile("(?i)(;|\\||&&|\\$\\(|`|\\$\\{|\\bexec\\b|\\bsystem\\b|\\bpassthru\\b|\\bshell_exec\\b|\\bpopen\\b|\\bproc_open\\b)")

    // SSRF patterns — internal IP access via URL parameters
    ssrfInternalIPRe = regexp.MustCompile(`(?:^|[^0-9])(?:127\.0\.0\.1|0\.0\.0\.0|localhost|169\.254\.\d+\.\d+|10\.\d+\.\d+\.\d+|172\.(?:1[6-9]|2\d|3[01])\.\d+\.\d+|192\.168\.\d+\.\d+)(?:[:/]|$)`)
)

type PathTraversalRule struct{}

func (r *PathTraversalRule) ID() string       { return "TRAV-001" }
func (r *PathTraversalRule) Category() string { return "TRAVERSAL" }

func (r *PathTraversalRule) Match(req *http.Request, body []byte) (bool, int, string) {
    // Check URL path + all query parameters + body
    inputs := []string{req.URL.Path, req.URL.RawQuery}
    inputs = append(inputs, extractInputs(req, body)...)

    for _, input := range inputs {
        normalized := normalizeTraversal(input)
        for _, p := range pathTraversalPatterns {
            re := regexp.MustCompile("(?i)" + p.pattern)
            if re.MatchString(normalized) {
                return true, 10, fmt.Sprintf("path traversal: %s in %q", p.name, input[:min(50, len(input))])
            }
        }
    }
    return false, 0, ""
}

func normalizeTraversal(input string) string {
    // Multiple decode passes to catch double-encoding
    result := input
    for i := 0; i < 3; i++ {
        decoded, err := url.PathUnescape(result)
        if err != nil || decoded == result { break }
        result = decoded
    }
    return strings.ToLower(result)
}

type CmdInjectionRule struct{}

func (r *CmdInjectionRule) ID() string       { return "CMDINJ-001" }
func (r *CmdInjectionRule) Category() string { return "CMDINJ" }

func (r *CmdInjectionRule) Match(req *http.Request, body []byte) (bool, int, string) {
    for _, input := range extractInputs(req, body) {
        if m := cmdInjectionRe.FindString(input); m != "" {
            return true, 9, fmt.Sprintf("command injection: %q", m)
        }
    }
    return false, 0, ""
}

type SSRFRule struct{}

func (r *SSRFRule) ID() string       { return "SSRF-001" }
func (r *SSRFRule) Category() string { return "SSRF" }

func (r *SSRFRule) Match(req *http.Request, body []byte) (bool, int, string) {
    // Check URL parameters that might contain target URLs
    for _, param := range []string{"url", "target", "redirect", "src", "href", "link"} {
        val := req.URL.Query().Get(param)
        if val == "" { continue }
        if ssrfInternalIPRe.MatchString(val) {
            return true, 9, fmt.Sprintf("SSRF: internal IP in %q param", param)
        }
    }
    return false, 0, ""
}

Real-World Context

Path traversal (OWASP A01) was used in the 2021 Accellion FTA breach (100+ organizations). Command injection was the mechanism of the 2021 Log4Shell vulnerability (JNDI lookup injection). SSRF (OWASP A10) was used in the Capital One breach — attacker used metadata service (169.254.169.254) to steal AWS credentials.


Generated from week10.md · 8 Oct 2026 16:36