go.lang.security.audit.dangerous-exec-command.dangerous-exec-command

profile photo of semgrepsemgrep
Author
6,586
Download Count*

Detected non-static command inside Command. Audit the input to 'exec.Command'. If unverified user data can reach this call site, this is a code injection vulnerability. A malicious actor can inject a malicious script to execute arbitrary code.

Run Locally

Run in CI

Defintion

rules:
  - id: dangerous-exec-command
    patterns:
      - pattern-either:
          - patterns:
              - pattern-either:
                  - pattern: |
                      exec.Command($CMD,...)
                  - pattern: |
                      exec.CommandContext($CTX,$CMD,...)
              - pattern-not: |
                  exec.Command("...",...)
              - pattern-not: |
                  exec.CommandContext($CTX,"...",...)
          - patterns:
              - pattern-either:
                  - pattern: >
                      exec.Command("=~/(sh|bash|ksh|csh|tcsh|zsh)/","-c",$CMD,...)
                  - pattern: >
                      exec.CommandContext($CTX,"=~/(sh|bash|ksh|csh|tcsh|zsh)/","-c",$CMD,...)
              - pattern-not: |
                  exec.Command("...","...","...",...)
              - pattern-not: |
                  exec.CommandContext($CTX,"...","...","...",...)
          - pattern-either:
              - pattern: >
                  exec.Command("=~/\/bin\/env/","=~/(sh|bash|ksh|csh|tcsh|zsh)/","-c",$CMD,...)
              - pattern: >
                  exec.CommandContext($CTX,"=~/\/bin\/env/","=~/(sh|bash|ksh|csh|tcsh|zsh)/","-c",$CMD,...)
      - pattern-inside: |
          import "os/exec"
          ...
      - pattern-not-inside: |
          $CMD,$ERR := exec.LookPath("...");
          ...
      - pattern-not-inside: |
          $CMD = "...";
          ...
    message: Detected non-static command inside Command. Audit the input to
      'exec.Command'. If unverified user data can reach this call site, this is
      a code injection vulnerability. A malicious actor can inject a malicious
      script to execute arbitrary code.
    metadata:
      cwe:
        - "CWE-94: Improper Control of Generation of Code ('Code Injection')"
      owasp:
        - A03:2021 - Injection
      category: security
      technology:
        - go
      confidence: LOW
      references:
        - https://owasp.org/Top10/A03_2021-Injection
      cwe2022-top25: true
      subcategory:
        - audit
      likelihood: LOW
      impact: HIGH
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Code Injection
    severity: ERROR
    languages:
      - go

Examples

dangerous-exec-command.go

package main

import (
	"context"
	"fmt"
	"os"
	"os/exec"
	"time"
)

func runCommand1(userInput string) {
	// ruleid:dangerous-exec-command
	cmd := exec.Command(userInput, "foobar")

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stdout

	if err := cmd.Run(); err != nil {
		fmt.Println("Error:", err)
	}

}

func runCommand2(userInput string) {

	execPath, _ := exec.LookPath(userInput)

	// ruleid:dangerous-exec-command
	cmd := exec.Command(execPath, "foobar")

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stdout

	if err := cmd.Run(); err != nil {
		fmt.Println("Error:", err)
	}

}

func runCommand3(userInput string) {
	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	defer cancel()

	// ruleid:dangerous-exec-command
	if err := exec.CommandContext(ctx, userInput, "5").Run(); err != nil {
		fmt.Println("Error:", err)
	}

}

func runCommand4(userInput string) {

	// ruleid:dangerous-exec-command
	cmd := exec.Command("bash", "-c", userInput)

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stdout

	if err := cmd.Run(); err != nil {
		fmt.Println("Error:", err)
	}

}

func runcommand5(s string) (string, error) {

	// ruleid:dangerous-exec-command
	cmd := exec.Command("/usr/bin/env", "bash", "-c", s)
	stdoutStderr, err := cmd.CombinedOutput()

	if err != nil {
		return "", fmt.Errorf("shellCommand: unexpected error: out = %s, error = %v", stdoutStderr, err)
	}

	return string(stdoutStderr), nil
}

func runcommand6(s string) (string, error) {
	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	// might not have user context
	// ruleid:dangerous-exec-command
	cmd := exec.CommandContext(ctx, "/bin/env", "bash", "-c", s)
	stdoutStderr, err := cmd.CombinedOutput()

	if err != nil {
		return "", fmt.Errorf("shellCommand: unexpected error: out = %s, error = %v", stdoutStderr, err)
	}

	return string(stdoutStderr), nil
}

func okCommand1(userInput string) {

	goExec, _ := exec.LookPath("go")

	// ok:dangerous-exec-command
	cmd := exec.Command(goExec, "version")

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stdout

	if err := cmd.Run(); err != nil {
		fmt.Println("Error:", err)
	}

}

func okCommand2(userInput string) {
	// ok:dangerous-exec-command
	cmd := exec.Command("go", "version")

	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stdout

	if err := cmd.Run(); err != nil {
		fmt.Println("Error:", err)
	}

}

func okCommand3(s string) (string, error) {

	someCommand := "w"
	// ok:dangerous-exec-command
	cmd := exec.Command("/usr/bin/env", "bash", "-c", someCommand)
	stdoutStderr, err := cmd.CombinedOutput()

	if err != nil {
		return "", fmt.Errorf("shellCommand: unexpected error: out = %s, error = %v", stdoutStderr, err)
	}

	return string(stdoutStderr), nil
}