HIGH command injectiongin

Command Injection in Gin

How Command Injection Manifests in Gin

Command injection vulnerabilities in Gin applications typically occur when user input is directly passed to system commands without proper sanitization. In Gin, this often happens through HTTP parameters that control file operations, external process execution, or system interactions.

A common pattern is using os/exec with user-controlled input:

func backupHandler(c *gin.Context) {
    filename := c.Query("file")
    cmd := exec.Command("tar", "czf", "/backups/"+filename, "/data")
    cmd.Run()
}

An attacker could exploit this by requesting /backup?file=backup.tar; rm -rf /, causing the shell to execute both commands.

Another Gin-specific pattern involves using os/exec with CombinedOutput() for logging or debugging:

func runScript(c *gin.Context) {
    script := c.Query("script")
    output, _ := exec.Command("sh", "-c", script).CombinedOutput()
    c.JSON(200, gin.H{"output": string(output)})
}

This is particularly dangerous because it gives attackers direct shell access through the API.

Even seemingly safe operations can be vulnerable. Consider file path construction:

func readFile(c *gin.Context) {
    path := c.Query("path")
    content, _ := os.ReadFile("/var/www" + path)
    c.String(200, string(content))
}

An attacker could use ../ sequences to read arbitrary files outside the intended directory.

Gin's middleware system can also introduce command injection if not careful. For example, a logging middleware that executes commands based on request data:

func loggingMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        userAgent := c.GetHeader("User-Agent")
        exec.Command("echo", userAgent, ">>", "/var/log/ua.log").Run()
        c.Next()
    }
}

Here, a malicious User-Agent header could inject arbitrary commands.

Gin-Specific Detection

Detecting command injection in Gin applications requires both static code analysis and dynamic testing. For static analysis, look for these patterns:

// Dangerous: direct string concatenation
cmd := exec.Command("ls", "/tmp/"+userInput)

// Dangerous: using sh -c with user input
shCmd := exec.Command("sh", "-c", userInput)

// Dangerous: using os/exec with unvalidated paths
os.ReadFile(basePath + userInput)

middleBrick's API security scanner can automatically detect these patterns in your Gin endpoints. It tests for command injection by sending payloads like:

payloads := []string{
    "; id",
    "| whoami",
    "&& ls /",
    "; touch /tmp/proof",
    "; cat /etc/passwd",
}

The scanner attempts to execute these through your API endpoints and checks for indicators like unexpected process execution, file creation, or information disclosure.

For Gin-specific detection, middleBrick analyzes:

  • Handler functions that use exec.Command with query parameters
  • Middleware that processes headers or body data into system commands
  • File operations with path traversal possibilities
  • Template rendering that might execute shell commands

middleBrick's LLM security module also checks for AI-specific command injection patterns if your Gin app uses language models, testing for jailbreak prompts that could execute system commands through the LLM interface.

Gin-Specific Remediation

Fixing command injection in Gin requires a defense-in-depth approach. The most effective remediation is avoiding system commands entirely when possible:

// Instead of using tar command
tar := exec.Command("tar", "czf", filepath, sourceDir)

// Use Go's archive/tar package directly
import "archive/tar"

func createTarball(c *gin.Context) {
    filename := sanitizeFilename(c.Query("file"))
    targetPath := filepath.Join("/backups", filename+"tar.gz")
    
    f, err := os.Create(targetPath)
    if err != nil { ... }
    defer f.Close()
    
    tw := tar.NewWriter(f)
    defer tw.Close()
    
    addFileToTar(tw, "/data")
    c.JSON(200, gin.H{"status": "backup created"})
}

When system commands are unavoidable, use argument arrays instead of shell interpretation:

// Dangerous - shell interpretation
cmd := exec.Command("sh", "-c", "ls "+dir)

// Safe - argument array
cmd := exec.Command("ls", dir)

// Even safer - whitelist allowed directories
allowedDirs := map[string]string{
    "tmp": "/tmp",
    "logs": "/var/log",
}

func safeListDir(c *gin.Context) {
    dirKey := c.Query("dir")
    basePath, ok := allowedDirs[dirKey]
    if !ok {
        c.JSON(400, gin.H{"error": "invalid directory"})
        return
    }
    
    cmd := exec.Command("ls", basePath)
    output, _ := cmd.Output()
    c.JSON(200, gin.H{"files": string(output)})
}

For file operations, use path sanitization and validation:

func sanitizeFilename(name string) string {
    return filepath.Base(name)
}

func safeReadFile(c *gin.Context) {
    userPath := c.Query("path")
    cleanPath := filepath.Clean("/var/www" + userPath)
    
    if !strings.HasPrefix(cleanPath, "/var/www") {
        c.JSON(400, gin.H{"error": "invalid path"})
        return
    }
    
    content, err := os.ReadFile(cleanPath)
    if err != nil {
        c.JSON(404, gin.H{"error": "file not found"})
        return
    }
    
    c.String(200, string(content))
}

Gin's context binding can help validate input before it reaches dangerous operations:

type BackupRequest struct {
    File string `json:"file" binding:"required,alphanum"`
    Dir  string `json:"dir" binding:"required"`
}

func backupHandler(c *gin.Context) {
    var req BackupRequest
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": "invalid input"})
        return
    }
    
    // Now safe to use req.File and req.Dir
}

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

How can I test my Gin application for command injection vulnerabilities?
Use middleBrick's API security scanner to automatically test your Gin endpoints. It sends command injection payloads to your API and checks for successful exploitation. You can also manually test by sending payloads like ; id, | whoami, or && ls / to endpoints that use exec.Command or file operations.
Is using <code>exec.CommandContext</code> safer than <code>exec.Command</code>?
No, exec.CommandContext only adds timeout/cancellation capabilities. It doesn't prevent command injection if user input reaches the command. You still need proper input validation, argument arrays instead of shell interpretation, and avoiding system commands when possible.