lib/brakeman/checks/check_execute.rb in brakeman-lib-4.6.1 vs lib/brakeman/checks/check_execute.rb in brakeman-lib-4.7.0
- old
+ new
@@ -19,10 +19,14 @@
s(:call, s(:const, :Process), :pid)]
SHELL_ESCAPE_MODULE_METHODS = Set[:escape, :join, :shellescape, :shelljoin]
SHELL_ESCAPE_MIXIN_METHODS = Set[:shellescape, :shelljoin]
+ # These are common shells that are known to allow the execution of commands
+ # via a -c flag. See dash_c_shell_command? for more info.
+ KNOWN_SHELL_COMMANDS = Set["sh", "bash", "ksh", "csh", "tcsh", "zsh"]
+
SHELLWORDS = s(:const, :Shellwords)
#Check models, controllers, and views for command injection.
def run_check
Brakeman.debug "Finding system calls using ``"
@@ -40,10 +44,12 @@
calls.each do |result|
process_result result
end
end
+ private
+
#Processes results from Tracker#find_call.
def process_result result
call = result[:call]
args = call.arglist
first_arg = call.first_arg
@@ -52,11 +58,21 @@
when :popen
unless array? first_arg
failure = include_user_input?(args) || dangerous_interp?(args)
end
when :system, :exec
- failure = include_user_input?(first_arg) || dangerous_interp?(first_arg)
+ # Normally, if we're in a `system` or `exec` call, we only are worried
+ # about shell injection when there's a single argument, because comma-
+ # separated arguments are always escaped by Ruby. However, an exception is
+ # when the first two arguments are something like "bash -c" because then
+ # the third argument is effectively the command being run and might be
+ # a malicious executable if it comes (partially or fully) from user input.
+ if dash_c_shell_command?(first_arg, call.second_arg)
+ failure = include_user_input?(args[3]) || dangerous_interp?(args[3])
+ else
+ failure = include_user_input?(first_arg) || dangerous_interp?(first_arg)
+ end
else
failure = include_user_input?(args) || dangerous_interp?(args)
end
if failure and original? result
@@ -73,9 +89,18 @@
:message => "Possible command injection",
:code => call,
:user_input => failure,
:confidence => confidence
end
+ end
+
+ # @return [Boolean] true iff the command given by `first_arg`, `second_arg`
+ # invokes a new shell process via `<shell_command> -c` (like `bash -c`)
+ def dash_c_shell_command?(first_arg, second_arg)
+ string?(first_arg) &&
+ KNOWN_SHELL_COMMANDS.include?(first_arg.value) &&
+ string?(second_arg) &&
+ second_arg.value == "-c"
end
def check_open_calls
tracker.find_call(:targets => [nil, :Kernel], :method => :open).each do |result|
if match = dangerous_open_arg?(result[:call].first_arg)