Skip to content

os-command-injection

Ensure secure usage of os commands

Using functions that allow invoking OS system commands is insecure when arbitrary user input is allowed to modify the intended system command execution. In many cases, there is an alternative to invoking a system command directly such as using an abstraction for accessing the file system. Whenever it is strictly required that user input influences the system command, it's always best to use a whitelist of safe commands (or arguments).

Examples

Insecure Example

import os
from flask import request

@app.route('/')
def index():
    directory_name = request.args.get("directory") # User input
    return os.system("ls -lah %s" % directory_name) # User can append any other shell command
class SomeController < ApplicationController
    def index
        directory_name = params[:directory] # User input
        # In Ruby ``, %x(...), system(), exec() are all insecure with user input
        command_injection_1 = `ls -lah #{directory_name}`
        command_injection_2 = system("ls -lah #{directory_name}")
        command_injection_3 = exec("ls -lah #{directory_name}")
        command_injection_4 = %(ls -lah #{directory_name})
        render :text => command_injection_1
    end
end
package main

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

func main() {
    action := os.Args[1]

    out, err := exec.Command("bash", "-c", "git "+action).Output()

    if err != nil {
        fmt.Println("Execution failed")
    } else {
        fmt.Println(string(out))
    }
}

Secure Example

import os
from flask import request

@app.route('/')
def index():
    directory_name = request.args.get("directory") # User input
    if directory_name == "documents" or directory_name == "images":
        return os.system("ls -lah %s" % directory_name)
    else:
        return "Unauthorized directory"
class SomeController < ApplicationController
    def index
        directory_name = params[:directory] # User input
        # Only allow a whitelist of directories
        if directory_name == "documents" || directory_name == "images"
            render :text => `ls -lah #{directory_name}`
        else
            render :text => "Unauthorized directory"
        end
    end
end
package main

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

func main() {
    actions := map[string]string{
        "status":"status",
        "diff": "diff",
        // ...
    }

    if len(os.Args) != 2 {
        fmt.Println("Please provide a command")
        os.Exit(1)
    }
    if action, ok := actions[os.Args[1]]; ok {
        out, err := exec.Command("bash", "-c", "git "+action).Output()

        if err == nil {
            fmt.Println(string(out))
        } else {
            fmt.Println("Boom")
        }
    } else {
        fmt.Println("Unauthorized command provided")
    }
}