lib/contrast/agent/protect/rule/cmd_injection.rb in contrast-agent-6.6.5 vs lib/contrast/agent/protect/rule/cmd_injection.rb in contrast-agent-6.7.0
- old
+ new
@@ -4,45 +4,40 @@
require 'contrast/agent/protect/rule/base_service'
require 'contrast/utils/stack_trace_utils'
require 'contrast/utils/object_share'
require 'contrast/components/logger'
require 'contrast/agent/reporting/input_analysis/input_type'
-require 'contrast/agent/reporting/details/cmd_injection_details'
+require 'contrast/agent/protect/rule/cmdi/cmdi_base_rule'
+require 'contrast/agent/protect/rule/cmdi/cmdi_backdoors'
module Contrast
module Agent
module Protect
module Rule
# The Ruby implementation of the Protect Command Injection rule.
- class CmdInjection < Contrast::Agent::Protect::Rule::BaseService
+ class CmdInjection < Contrast::Agent::Protect::Rule::CmdiBaseRule
include Contrast::Components::Logger::InstanceMethods
include Contrast::Agent::Reporting::InputType
-
NAME = 'cmd-injection'
- CHAINED_COMMAND_CHARS = /[;&|<>]/.cs__freeze
- APPLICABLE_USER_INPUTS = [
- BODY, COOKIE_VALUE, HEADER, PARAMETER_NAME,
- PARAMETER_VALUE, JSON_VALUE, MULTIPART_VALUE,
- MULTIPART_FIELD_NAME, XML_VALUE, DWR_VALUE
- ].cs__freeze
+ SUB_RULES = [Contrast::Agent::Protect::Rule::CmdiBackdoors.new].cs__freeze
- class << self
- # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
- # @return [Hash] the details for this specific rule
- def extract_details attack_sample
- {
- command: attack_sample.cmdi.command,
- startIndex: attack_sample.cmdi.start_idx,
- endIndex: attack_sample.cmdi.end_idx
- }
- end
- end
-
def rule_name
NAME
end
+ def sub_rules
+ SUB_RULES
+ end
+
+ # CMDI infilter:
+ #
+ # @param context [Contrast::Agent::RequestContext] current request context
+ # @param classname [String] Name of the class
+ # @param method [String] name of the method triggering the rule
+ # @param command [String] potential dangerous command executed.
+ # @raise [Contrast::SecurityException] if the rule mode is set
+ # to BLOCK and valid cdmi is detected.
def infilter context, classname, method, command
return unless infilter?(context)
ia_results = gather_ia_results(context)
return if ia_results.empty?
@@ -61,93 +56,11 @@
append_to_activity(context, result)
cef_logging(result, :successful_attack)
return unless blocked?
- raise(Contrast::SecurityException.new(self,
- 'Command Injection rule triggered. '\
- "Call to #{ classname }.#{ method } blocked."))
- end
-
- def build_attack_with_match context, input_analysis_result, result, candidate_string, **kwargs
- if mode == Contrast::Api::Settings::ProtectionRule::Mode::NO_ACTION ||
- mode == Contrast::Api::Settings::ProtectionRule::Mode::PERMIT
-
- return result
- end
-
- result ||= build_attack_result(context)
- update_successful_attack_response(context, input_analysis_result, result, candidate_string)
- append_sample(context, input_analysis_result, result, candidate_string, **kwargs)
- result
- end
-
- protected
-
- # Because results are not necessarily on the context across
- # processes; extract early and pass into the method
- def find_attacker_with_results context, potential_attack_string, ia_results, **kwargs
- logger.trace('Checking vectors for attacks', rule: rule_name, input: potential_attack_string)
- result = super(context, potential_attack_string, ia_results, **kwargs)
- if result.nil? && potential_attack_string
- result = find_probable_attacker(context, potential_attack_string, ia_results, **kwargs)
- end
- result
- end
-
- # Build a subclass of the RaspRuleSample using the query string and the
- # evaluation
- def build_sample context, input_analysis_result, candidate_string, **_kwargs
- sample = build_base_sample(context, input_analysis_result)
- sample.details = Contrast::Agent::Reporting::Details::CmdInjectionDetails.new
-
- command = candidate_string || input_analysis_result.value
- command = Contrast::Utils::StringUtils.protobuf_safe_string(command)
- sample.details.cmd = command
- sample.details.end_idx = command.length
-
- # This is a special case where the user input is UNKNOWN_USER_INPUT but
- # we want to send the attack value
- if input_analysis_result.nil?
- ui = Contrast::Agent::Reporting::UserInput.new
- ui.input_type = :UNKNOWN
- ui.value = command
- sample.user_input = ui
- end
-
- sample
- end
-
- private
-
- def report_command_execution context, command, **kwargs
- return unless report_any_command_execution?
- return if protect_excluded_by_code?
-
- build_attack_with_match(context, nil, nil, command, **kwargs)
- end
-
- def find_probable_attacker context, potential_attack_string, ia_results, **kwargs
- return unless chained_command?(potential_attack_string)
-
- likely_attacker = ia_results.find { |input_analysis_result| chained_command?(input_analysis_result.value) }
- return unless likely_attacker
-
- build_attack_with_match(context, likely_attacker, nil, potential_attack_string, **kwargs)
- end
-
- def chained_command? command
- CHAINED_COMMAND_CHARS.match(command)
- end
-
- # Part of the Hardening for Command Injection detection is the
- # ability to detect and prevent any command execution from within the
- # application. This check determines if that hardening has been
- # enabled.
- # @return [Boolean] if the agent should report all command
- # executions.
- def report_any_command_execution?
- ::Contrast::PROTECT.report_any_command_execution?
+ # Raise cmdi error
+ raise_error(classname, method)
end
end
end
end
end