Spring4shell in Gin
How Spring4shell Manifests in Gin
Spring4shell (CVE-2022-22965) is a critical remote code execution vulnerability in Spring Framework where attacker-controlled data is evaluated as a Spring Expression Language (SpEL) payload. While Gin (the Go web framework) does not have an equivalent expression evaluation engine, the same fundamental flaw—unsafe evaluation of user-supplied expressions—can manifest in Go applications when developers integrate third-party expression evaluators without proper sandboxing.
In a typical vulnerable Gin pattern, a handler accepts a user-provided string (e.g., via query parameter) and passes it to an expression evaluation library like govaluate. If the evaluation context includes references to functions that execute system commands or access sensitive data, an attacker can achieve RCE. Consider this vulnerable Gin route:
r.GET("/calc", func(c *gin.Context) {
expr := c.Query("expr")
// Unsafe: user-controlled expression evaluated with dangerous functions
expression, _ := govaluate.NewEvaluableExpression(expr)
parameters := map[string]interface{}{
"exec": func(cmd string) string {
// WARNING: This is a simplified example. In real code, this might invoke os/exec.
return "[SIMULATED] " + cmd
},
}
result, _ := expression.Evaluate(parameters)
c.JSON(200, gin.H{"result": result})
})
An attacker can send a request like GET /calc?expr=exec("whoami"). The expression engine interprets exec as the function provided in parameters, leading to command execution. This mirrors Spring4shell’s core issue: user data becomes executable code.
Gin-Specific Detection
Detecting expression injection in Gin APIs requires identifying endpoints that accept free-form expressions and testing for unsafe evaluation. Manually, look for routes that:
- Accept parameters named
expr,evaluate,calc, or similar. - Use libraries like
github.com/Knetic/govaluate,github.com/antonmedv/expr, or custom parsers. - Pass a parameters map containing functions that interact with the OS, filesystem, or network.
Automated scanning with middleBrick catches this via its Input Validation and Code Injection checks. The scanner sends payloads designed to trigger expression evaluation, such as:
GET /calc?expr=exec("id")
GET /calc?expr=os.Open("/etc/passwd")It then analyzes the response for indicators of command execution (e.g., presence of uid= or file contents). middleBrick’s black-box approach tests the live API without credentials, mapping findings to OWASP API Top 10: API10:2023 — Server-Side Request Forgery (SSRF) and API03:2023 — Broken Object Property Authorization (if expression access controls are missing). The scan takes 5–15 seconds and produces a per-category risk score with remediation guidance.
Gin-Specific Remediation
Fix expression injection in Gin by eliminating unsafe evaluation or rigorously sandboxing it. The safest approach is to avoid evaluating user-provided expressions entirely. If expression evaluation is a business requirement (e.g., a calculator API), implement strict controls:
- Use an allowlist of permitted characters and operators—reject any input containing letters, quotes, or function calls if only math is needed.
- Never pass functions that interact with the OS, network, or filesystem into the evaluation context.
- Leverage Gin’s binding validation to enforce strict input formats before processing.
Example fixed code using govaluate with a mathematical allowlist and no dangerous parameters:
r.GET("/calc", func(c *gin.Context) {
var input struct {
Expr string `json:"expr" binding:"required,alphanum"`
}
// Bind and validate: only alphanumeric, +-*/() allowed
if err := c.ShouldBindQuery(&input); err != nil {
c.JSON(400, gin.H{"error": "invalid expression format"})
return
}
// Further restrict with regex to ensure only math operators
if matched, _ := regexp.MatchString(`^[0-9+\-*/() ]+$`, input.Expr); !matched {
c.JSON(400, gin.H{"error": "expression contains invalid characters"})
return
}
expression, _ := govaluate.NewEvaluableExpression(input.Expr)
// Safe: no parameters provided, only built-in math functions
result, _ := expression.Evaluate(nil)
c.JSON(200, gin.H{"result": result})
})
For more complex needs, consider using a library that supports sandboxing (e.g., github.com/antonmedv/expr with restricted Env). Always treat user input as data, never as code. Integrate fixes into your CI/CD pipeline with middleBrick’s GitHub Action to fail builds if expression injection risks reappear.
Frequently Asked Questions
Is the Gin framework itself vulnerable to Spring4shell?
How does middleBrick detect expression injection without seeing my source code?
expr=exec("id")) to your live API and analyzing responses for signs of command execution or data leakage. It maps findings to OWASP categories and provides severity scores, all without requiring credentials or agents.