diff --git a/app/api/v1/entity.rb b/app/api/v1/entity.rb index d91457d01..0f8bf4389 100644 --- a/app/api/v1/entity.rb +++ b/app/api/v1/entity.rb @@ -1,11 +1,11 @@ class CoreApp < Sinatra::Base post "/api/v1/entity" do - + content_type "application/json" halt_unless_authenticated! - + # For post requests with a json body, just stick it in params # don't clobber params, stick it in its own object json = get_json_body diff --git a/core.rb b/core.rb index 67e711edf..564590498 100644 --- a/core.rb +++ b/core.rb @@ -43,10 +43,11 @@ require_relative 'lib/initialize/hash' require_relative 'lib/initialize/json_export_file' require_relative 'lib/initialize/queue' +require_relative 'lib/initialize/resolv' require_relative 'lib/initialize/sidekiq_profiler' require_relative 'lib/initialize/string' +require_relative 'lib/initialize/symbol' require_relative 'lib/initialize/typhoeus' -require_relative 'lib/initialize/resolv' # load up our system config require_relative 'lib/system/config' diff --git a/lib/checks/atlassian_confluence_cve_2021_26084.rb b/lib/checks/atlassian_confluence_cve_2021_26084.rb index e03a040be..3f0da43cd 100644 --- a/lib/checks/atlassian_confluence_cve_2021_26084.rb +++ b/lib/checks/atlassian_confluence_cve_2021_26084.rb @@ -38,12 +38,15 @@ def self.check_metadata def check # run a nuclei uri = _get_entity_name + _log "Running on #{uri}" + template = 'cves/2021/CVE-2021-26084' # if this returns truthy value, an issue will be raised # the truthy value will be added as proof to the issue - run_nuclei_template uri, template + run_nuclei_template uri, template end + end end end diff --git a/lib/initialize/symbol.rb b/lib/initialize/symbol.rb new file mode 100644 index 000000000..25199f5a1 --- /dev/null +++ b/lib/initialize/symbol.rb @@ -0,0 +1,6 @@ +## Monkeypatch symbol to handle an encode method (that does nothing) +class Symbol + def encode(a,b) + self + end +end diff --git a/lib/issue_factory.rb b/lib/issue_factory.rb index 61014c75a..4def8683d 100644 --- a/lib/issue_factory.rb +++ b/lib/issue_factory.rb @@ -1,10 +1,10 @@ -# +# # First, a simple factory interface # module Intrigue module Issue class IssueFactory - + # # Register a new handler # @@ -12,27 +12,27 @@ def self.register(klass) @issue_types = [] unless @issue_types @issue_types << klass if klass end - + # # Provide the full list of issues # def self.issues @issue_types end - + # # Provide the full list of issues # def self.issue_by_type(name) x = self.issues.find{|x| x if x.generate({})[:name] == name } - x.generate({}) if x + x.generate({}) if x end - + # # Returns an issue name if the check matches a inference:[CVE_ID] # def self.get_issue_by_cve_identifier(cve_id) - self.issues.each do |h| + self.issues.each do |h| # generate the instances issue_metadata = h.generate({}) next unless issue_metadata[:identifiers] @@ -42,30 +42,30 @@ def self.get_issue_by_cve_identifier(cve_id) end false end - + # - # Provide the full list of issues, given a + # Provide the full list of issues, given a # def self.issues_for_vendor_product(vendor,product) - + ### First, get all issues with their affected software mapped_issues = [] - self.issues.each do |h| + self.issues.each do |h| # generate the instances - hi = h.generate({}); + hi = h.generate({}); # then geet all instaces of affected software with issues names as = (hi[:affected_software] || []) mapped_issues << as.map{|x| x.merge({ :name => hi[:name] }) } end - + mapped_issues.flatten.select{|x| x[:vendor] == vendor && x[:product] == product}.map{|x| x[:name] }.uniq.compact end - + # # Provide the full list of issues, given a vendor, product # def self.checks_for_vendor_product(vendor,product) - + ### First, get all issues with their affected software mapped_issues = [] self.issues.each do |h| @@ -73,30 +73,30 @@ def self.checks_for_vendor_product(vendor,product) hi = h.generate({}); # then get all instaces of affected software with issues names as = (hi[:affected_software] || []) - + # first check to see if there's an explicit task specified in the issue - # and if there's not default to the name of the issue (it's probably a check and - # ... checks are automatically named by their issue thanks to introspection magic) + # and if there's not default to the name of the issue (it's probably a check and + # ... checks are automatically named by their issue thanks to introspection magic) mapped_issues << as.map{|x| x.merge({ check_name: (hi[:task]||hi[:name]) }) } end - ## pull out only items that have an affected software that matches our - ## passed-in vendor / product and only return the name of the task/check to be run + ## pull out only items that have an affected software that matches our + ## passed-in vendor / product and only return the name of the task/check to be run checks = mapped_issues.flatten.select{|x| x[:vendor] == vendor && x[:product] == product}.map{|x| x[:check_name] } - - # return only those checks that actually exist. this is due to the magical - # nature of how we select tasks above - which might mean that we pulled in an - # issue that did not have a corresponding check or task + + # return only those checks that actually exist. this is due to the magical + # nature of how we select tasks above - which might mean that we pulled in an + # issue that did not have a corresponding check or task checks.map{|x| x if Intrigue::TaskFactory.include?(x) }.compact.uniq end - + # # Find issue based on check value # def self.find_issue_by_check(check) - - self.issues.each do |h| + + self.issues.each do |h| # generate the instances hi = h.generate({}); if hi[:check] == check @@ -105,8 +105,8 @@ def self.find_issue_by_check(check) end nil end - - + + # # Check to see if this handler exists (check by type) # @@ -114,7 +114,7 @@ def self.include?(name) @issue_types.each { |h| return true if "#{h.generate({})[:name]}" == "#{name}" } false end - + # # create_by_type(type) # @@ -125,26 +125,27 @@ def self.include?(name) # - A handler, which you can call generate on # def self.create_instance_by_type(requested_type, issue_model_details, instance_specifics={}) - + # first look thorugh our issue types and get the right one issue_type = self.issues.select{ |h| h.generate({})[:name] == requested_type }.first - unless issue_type + unless issue_type raise "Unknown issue type: #{requested_type}" return end - - issue_instance_details = issue_type.generate(instance_specifics) - - # add in the fields we want to use when querying... + + sanitized_details = instance_specifics.sanitize_unicode + issue_instance_details = issue_type.generate(sanitized_details) + + # add in the fields we want to use when querying... issue_model = issue_model_details.merge({ name: issue_instance_details[:name], source: issue_instance_details[:source] }) - + # then create the darn thing issue = Intrigue::Core::Model::Issue.update_or_create(issue_model) - - # save the specifics + + # save the specifics issue.description = issue_type.generate({})[:description] issue.pretty_name = issue_instance_details[:pretty_name] issue.status = issue_instance_details[:status] @@ -154,10 +155,10 @@ def self.create_instance_by_type(requested_type, issue_model_details, instance_s issue.references = issue_type.generate({})[:references] issue.details = issue_instance_details issue.save_changes - + issue end - + end end end \ No newline at end of file diff --git a/lib/tasks/helpers/vuln_check.rb b/lib/tasks/helpers/vuln_check.rb index 4e2cff58b..b62b3690f 100644 --- a/lib/tasks/helpers/vuln_check.rb +++ b/lib/tasks/helpers/vuln_check.rb @@ -30,7 +30,7 @@ def matches_fingerprint_metadata?(fingerprint, value_to_match, type) # function to compare version_a with version_b according to given operator. # will try to parse both parameters with versionomy. if parsing fails, it will compare them as string literals. def compare_versions_by_operator(version_a, version_b, operator) - + # try to parse via versionomy begin parsed_a = Versionomy.parse(version_a.scan(/\d\.?+/).join('')) @@ -68,16 +68,27 @@ def run_nuclei_template(uri, template) _log "Running #{template} against #{uri}" begin ruclei = Ruclei::Ruclei.new - ruclei.load_template("data/nuclei-templates/#{template}.yaml") + ruclei.load_template("#{$intrigue_basedir}/data/nuclei-templates/#{template}.yaml") res = ruclei.run(uri) - return { proof: res.results } unless res.nil? - rescue Errno::ENOENT # cannot find template + + # if we got a result, let's return it! + if res + proof_hash = { # construct a hash with safe details + proof: { + url: res.url, + template_info: res.template_info + } + } + return proof_hash + end + + rescue Ruclei::Exceptions::MissingTemplateError # cannot find template _log_error 'ERROR: Cannot find template at specified path.' - rescue Psych::SyntaxError # non-yaml file passed + rescue Ruclei::Exceptions::InvalidTemplateError _log_error 'ERROR: Specified template does not appear to be in YAML format.' end - - return nil + + nil end def run_nuclei_template_from_string(uri, template_string) @@ -88,11 +99,11 @@ def run_nuclei_template_from_string(uri, template_string) ruclei.parse_template template_string res = ruclei.run(uri) return { proof: res.results } unless res.nil? - rescue Psych::SyntaxError # non-yaml file passed + rescue Ruclei::Exceptions::InvalidTemplateError _log_error 'ERROR: Specified template does not appear to be in YAML format.' end - return nil + nil end @@ -129,13 +140,13 @@ def get_update_for_vendor_product(entity, vendor, product) end def fingerprint_to_inference_issues(fingerprint) - fingerprint.each do |fp| + fingerprint.each do |fp| next unless fp["vulns"] fp["vulns"].each do |vuln| - # get and create the issue here + # get and create the issue here issue_metadata = Intrigue::Issue::IssueFactory.get_issue_by_cve_identifier(vuln["cve"]) next unless issue_metadata - + # if we have an issue who has that cve as an identifiger, run the check task task_name = issue_metadata[:task] || issue_metadata[:name] start_task("task_autoscheduled", @project, @task_result.scan_result_id, task_name, @entity, 1)