mihari 3.2.0 → 3.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/README.md +7 -2
 - data/config.ru +6 -0
 - data/images/overview.jpg +0 -0
 - data/lib/mihari.rb +2 -0
 - data/lib/mihari/analyzers/rule.rb +27 -0
 - data/lib/mihari/cli/analyzer.rb +4 -0
 - data/lib/mihari/cli/base.rb +0 -5
 - data/lib/mihari/commands/init.rb +4 -4
 - data/lib/mihari/commands/search.rb +20 -8
 - data/lib/mihari/commands/web.rb +5 -0
 - data/lib/mihari/mixins/disallowed_data_value.rb +42 -0
 - data/lib/mihari/mixins/rule.rb +5 -1
 - data/lib/mihari/models/alert.rb +24 -10
 - data/lib/mihari/schemas/configuration.rb +1 -0
 - data/lib/mihari/schemas/rule.rb +14 -0
 - data/lib/mihari/templates/rule.yml.erb +5 -1
 - data/lib/mihari/version.rb +1 -1
 - data/lib/mihari/web/app.rb +3 -0
 - data/lib/mihari/web/controllers/alerts_controller.rb +3 -4
 - data/lib/mihari/web/controllers/artifacts_controller.rb +27 -2
 - data/lib/mihari/web/controllers/ip_address_controller.rb +36 -0
 - data/lib/mihari/web/controllers/tags_controller.rb +3 -1
 - data/lib/mihari/web/public/index.html +1 -1
 - data/lib/mihari/web/public/redoc-static.html +12 -10
 - data/lib/mihari/web/public/static/fonts/fa-brands-400.1a575a41.woff +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-brands-400.513aa607.ttf +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-brands-400.592643a8.eot +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-brands-400.ed311c7a.woff2 +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-regular-400.766913e6.ttf +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-regular-400.b0e2db3b.eot +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-regular-400.b91d376b.woff2 +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-regular-400.d1d7e3b4.woff +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-solid-900.0c6bfc66.eot +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-solid-900.b9625119.ttf +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-solid-900.d745348d.woff +0 -0
 - data/lib/mihari/web/public/static/fonts/fa-solid-900.d824df7e.woff2 +0 -0
 - data/lib/mihari/web/public/static/img/fa-brands-400.1d5619cd.svg +3717 -0
 - data/lib/mihari/web/public/static/img/fa-regular-400.c5d109be.svg +801 -0
 - data/lib/mihari/web/public/static/img/fa-solid-900.37bc7099.svg +5034 -0
 - data/lib/mihari/web/public/static/js/app.b5914c39.js +36 -0
 - data/lib/mihari/web/public/static/js/app.b5914c39.js.map +1 -0
 - data/mihari.gemspec +3 -2
 - metadata +41 -7
 - data/images/overview.png +0 -0
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: a2fe0203f89908abbc21df8d595b7167a561c826b933166e531298b83f67e085
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 03a455ebd71f5d3c228041351b6ee8a6a2d89b74a7fc2f0d7609293bc82da7d6
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: aa4a28142eb109d46109d960c1de935ac4632136bc7696b405bfda3d691de2658764e7f796a8e9b1715973e0fc3cc9887997f3ab0dd054e4caa6baf6da40cb8e
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 057c5df7efd59ebf285b4a8d6a17a8857d8fa5df7b0c61f4ddbaf67d3688aaa4c34187af88acfe151a27a21b1d33b5e659347968ba6881e98e3b81114d113eb9
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -14,11 +14,12 @@ Mihari is a framework for continuous OSINT based threat hunting. 
     | 
|
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
            ## How it works
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
            
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
       19 
19 
     | 
    
         
             
            - Mihari makes a query against Shodan, Censys, VirusTotal, SecurityTrails, etc. and extracts artifacts (IP addresses, domains, URLs or hashes).
         
     | 
| 
       20 
     | 
    
         
            -
            - Mihari checks whether  
     | 
| 
      
 20 
     | 
    
         
            +
            - Mihari checks whether the database (SQLite3, PostgreSQL or MySQL) contains the artifacts or not.
         
     | 
| 
       21 
21 
     | 
    
         
             
              - If it doesn't contain the artifacts:
         
     | 
| 
      
 22 
     | 
    
         
            +
                - Mihari saves artifacts in the database.
         
     | 
| 
       22 
23 
     | 
    
         
             
                - Mihari creates an alert on TheHive.
         
     | 
| 
       23 
24 
     | 
    
         
             
                - Mihari sends a notification to Slack.
         
     | 
| 
       24 
25 
     | 
    
         
             
                - Mihari creates an event on MISP.
         
     | 
| 
         @@ -52,6 +53,10 @@ Mihari supports the following services by default. 
     | 
|
| 
       52 
53 
     | 
    
         | 
| 
       53 
54 
     | 
    
         
             
            - [Mihari Knowledge Base](https://www.notion.so/Mihari-Knowledge-Base-266994ff61204428ba6cfcebe40b0bd1)
         
     | 
| 
       54 
55 
     | 
    
         | 
| 
      
 56 
     | 
    
         
            +
            ## Presentations
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            - [Adversary Infrastructure Tracking with Mihari](https://ninoseki.github.io/presentations/Adversary%20Infrastructure%20Tracking%20with%20Mihari.pdf)
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
       55 
60 
     | 
    
         
             
            ## License
         
     | 
| 
       56 
61 
     | 
    
         | 
| 
       57 
62 
     | 
    
         
             
            The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
         
     | 
    
        data/config.ru
    ADDED
    
    
    
        data/images/overview.jpg
    ADDED
    
    | 
         Binary file 
     | 
    
        data/lib/mihari.rb
    CHANGED
    
    | 
         @@ -9,6 +9,7 @@ require "yaml" 
     | 
|
| 
       9 
9 
     | 
    
         
             
            # Mixins
         
     | 
| 
       10 
10 
     | 
    
         
             
            require "mihari/mixins/configurable"
         
     | 
| 
       11 
11 
     | 
    
         
             
            require "mihari/mixins/configuration"
         
     | 
| 
      
 12 
     | 
    
         
            +
            require "mihari/mixins/disallowed_data_value"
         
     | 
| 
       12 
13 
     | 
    
         
             
            require "mihari/mixins/hash"
         
     | 
| 
       13 
14 
     | 
    
         
             
            require "mihari/mixins/refang"
         
     | 
| 
       14 
15 
     | 
    
         
             
            require "mihari/mixins/retriable"
         
     | 
| 
         @@ -30,6 +31,7 @@ module Mihari 
     | 
|
| 
       30 
31 
     | 
    
         
             
              setting :censys_secret, ENV["CENSYS_SECRET"]
         
     | 
| 
       31 
32 
     | 
    
         
             
              setting :circl_passive_password, ENV["CIRCL_PASSIVE_PASSWORD"]
         
     | 
| 
       32 
33 
     | 
    
         
             
              setting :circl_passive_username, ENV["CIRCL_PASSIVE_USERNAME"]
         
     | 
| 
      
 34 
     | 
    
         
            +
              setting :ipinfo_api_key, ENV["ipinfo_api_key"]
         
     | 
| 
       33 
35 
     | 
    
         
             
              setting :misp_api_endpoint, ENV["MISP_API_ENDPOINT"]
         
     | 
| 
       34 
36 
     | 
    
         
             
              setting :misp_api_key, ENV["MISP_API_KEY"]
         
     | 
| 
       35 
37 
     | 
    
         
             
              setting :onyphe_api_key, ENV["ONYPHE_API_KEY"]
         
     | 
| 
         @@ -5,6 +5,8 @@ require "uuidtools" 
     | 
|
| 
       5 
5 
     | 
    
         
             
            module Mihari
         
     | 
| 
       6 
6 
     | 
    
         
             
              module Analyzers
         
     | 
| 
       7 
7 
     | 
    
         
             
                class Rule < Base
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include Mihari::Mixins::DisallowedDataValue
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       8 
10 
     | 
    
         
             
                  option :title
         
     | 
| 
       9 
11 
     | 
    
         
             
                  option :description
         
     | 
| 
       10 
12 
     | 
    
         
             
                  option :queries
         
     | 
| 
         @@ -12,6 +14,7 @@ module Mihari 
     | 
|
| 
       12 
14 
     | 
    
         
             
                  option :id, default: proc {}
         
     | 
| 
       13 
15 
     | 
    
         
             
                  option :tags, default: proc { [] }
         
     | 
| 
       14 
16 
     | 
    
         
             
                  option :allowed_data_types, default: proc { ALLOWED_DATA_TYPES }
         
     | 
| 
      
 17 
     | 
    
         
            +
                  option :disallowed_data_values, default: proc { [] }
         
     | 
| 
       15 
18 
     | 
    
         | 
| 
       16 
19 
     | 
    
         
             
                  attr_reader :source
         
     | 
| 
       17 
20 
     | 
    
         | 
| 
         @@ -68,12 +71,36 @@ module Mihari 
     | 
|
| 
       68 
71 
     | 
    
         
             
                  # - Uniquefy artifacts by #uniq(&:data)
         
     | 
| 
       69 
72 
     | 
    
         
             
                  # - Reject an invalid artifact (for just in case)
         
     | 
| 
       70 
73 
     | 
    
         
             
                  # - Select artifacts with allowed data types
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # - Reject artifacts with disallowed data values
         
     | 
| 
       71 
75 
     | 
    
         
             
                  #
         
     | 
| 
       72 
76 
     | 
    
         
             
                  # @return [Array<Mihari::Artifact>]
         
     | 
| 
       73 
77 
     | 
    
         
             
                  #
         
     | 
| 
       74 
78 
     | 
    
         
             
                  def normalized_artifacts
         
     | 
| 
       75 
79 
     | 
    
         
             
                    @normalized_artifacts ||= artifacts.uniq(&:data).select(&:valid?).select do |artifact|
         
     | 
| 
       76 
80 
     | 
    
         
             
                      allowed_data_types.include? artifact.data_type
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end.reject do |artifact|
         
     | 
| 
      
 82 
     | 
    
         
            +
                      disallowed_data_value? artifact.data
         
     | 
| 
      
 83 
     | 
    
         
            +
                    end
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  #
         
     | 
| 
      
 87 
     | 
    
         
            +
                  # Normalized disallowed data values
         
     | 
| 
      
 88 
     | 
    
         
            +
                  #
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # @return [Array<Regexp, String>]
         
     | 
| 
      
 90 
     | 
    
         
            +
                  #
         
     | 
| 
      
 91 
     | 
    
         
            +
                  def normalized_disallowed_data_values
         
     | 
| 
      
 92 
     | 
    
         
            +
                    @normalized_disallowed_data_values ||= disallowed_data_values.map { |v| normalize_disallowed_data_value v }
         
     | 
| 
      
 93 
     | 
    
         
            +
                  end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
      
 95 
     | 
    
         
            +
                  #
         
     | 
| 
      
 96 
     | 
    
         
            +
                  # Check whether a value is a disallowed data value or not
         
     | 
| 
      
 97 
     | 
    
         
            +
                  #
         
     | 
| 
      
 98 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 99 
     | 
    
         
            +
                  #
         
     | 
| 
      
 100 
     | 
    
         
            +
                  def disallowed_data_value?(value)
         
     | 
| 
      
 101 
     | 
    
         
            +
                    normalized_disallowed_data_values.any? do |disallowed_data_value|
         
     | 
| 
      
 102 
     | 
    
         
            +
                      return value == disallowed_data_value if disallowed_data_value.is_a?(String)
         
     | 
| 
      
 103 
     | 
    
         
            +
                      return disallowed_data_value.match?(value) if disallowed_data_value.is_a?(Regexp)
         
     | 
| 
       77 
104 
     | 
    
         
             
                    end
         
     | 
| 
       78 
105 
     | 
    
         
             
                  end
         
     | 
| 
       79 
106 
     | 
    
         | 
    
        data/lib/mihari/cli/analyzer.rb
    CHANGED
    
    | 
         @@ -22,6 +22,10 @@ require "mihari/commands/json" 
     | 
|
| 
       22 
22 
     | 
    
         
             
            module Mihari
         
     | 
| 
       23 
23 
     | 
    
         
             
              module CLI
         
     | 
| 
       24 
24 
     | 
    
         
             
                class Analyzer < Base
         
     | 
| 
      
 25 
     | 
    
         
            +
                  class_option :ignore_old_artifacts, type: :boolean, default: false, desc: "Whether to ignore old artifacts from checking or not."
         
     | 
| 
      
 26 
     | 
    
         
            +
                  class_option :ignore_threshold, type: :numeric, default: 0, desc: "Number of days to define whether an artifact is old or not."
         
     | 
| 
      
 27 
     | 
    
         
            +
                  class_option :config, type: :string, desc: "Path to the config file"
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
       25 
29 
     | 
    
         
             
                  include Mihari::Commands::BinaryEdge
         
     | 
| 
       26 
30 
     | 
    
         
             
                  include Mihari::Commands::Censys
         
     | 
| 
       27 
31 
     | 
    
         
             
                  include Mihari::Commands::CIRCL
         
     | 
    
        data/lib/mihari/cli/base.rb
    CHANGED
    
    | 
         @@ -12,11 +12,6 @@ module Mihari 
     | 
|
| 
       12 
12 
     | 
    
         
             
                  include Mihari::Mixins::Hash
         
     | 
| 
       13 
13 
     | 
    
         
             
                  include Mixins::Utils
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                  class_option :config, type: :string, desc: "Path to the config file"
         
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
                  class_option :ignore_old_artifacts, type: :boolean, default: false, desc: "Whether to ignore old artifacts from checking or not. Only affects with analyze commands."
         
     | 
| 
       18 
     | 
    
         
            -
                  class_option :ignore_threshold, type: :numeric, default: 0, desc: "Number of days to define whether an artifact is old or not. Only affects with analyze commands."
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
15 
     | 
    
         
             
                  class << self
         
     | 
| 
       21 
16 
     | 
    
         
             
                    def exit_on_failure?
         
     | 
| 
       22 
17 
     | 
    
         
             
                      true
         
     | 
    
        data/lib/mihari/commands/init.rb
    CHANGED
    
    | 
         @@ -5,10 +5,10 @@ require "colorize" 
     | 
|
| 
       5 
5 
     | 
    
         
             
            module Mihari
         
     | 
| 
       6 
6 
     | 
    
         
             
              module Commands
         
     | 
| 
       7 
7 
     | 
    
         
             
                module Initialization
         
     | 
| 
       8 
     | 
    
         
            -
                   
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
                    include Mixins::Rule
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include Mixins::Configuration
         
     | 
| 
      
 9 
     | 
    
         
            +
                  include Mixins::Rule
         
     | 
| 
       11 
10 
     | 
    
         | 
| 
      
 11 
     | 
    
         
            +
                  def self.included(thor)
         
     | 
| 
       12 
12 
     | 
    
         
             
                    thor.class_eval do
         
     | 
| 
       13 
13 
     | 
    
         
             
                      desc "config", "Create a config file"
         
     | 
| 
       14 
14 
     | 
    
         
             
                      method_option :filename, type: :string, default: "mihari.yml"
         
     | 
| 
         @@ -37,7 +37,7 @@ module Mihari 
     | 
|
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
                        initialize_rule_yaml filename
         
     | 
| 
       39 
39 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
                        puts "The rule file is  
     | 
| 
      
 40 
     | 
    
         
            +
                        puts "The rule file is initialized as #{filename}.".colorize(:blue)
         
     | 
| 
       41 
41 
     | 
    
         
             
                      end
         
     | 
| 
       42 
42 
     | 
    
         
             
                    end
         
     | 
| 
       43 
43 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -8,17 +8,27 @@ module Mihari 
     | 
|
| 
       8 
8 
     | 
    
         
             
                  def self.included(thor)
         
     | 
| 
       9 
9 
     | 
    
         
             
                    thor.class_eval do
         
     | 
| 
       10 
10 
     | 
    
         
             
                      desc "search [RULE]", "Search by a rule"
         
     | 
| 
      
 11 
     | 
    
         
            +
                      method_option :config, type: :string, desc: "Path to the config file"
         
     | 
| 
       11 
12 
     | 
    
         
             
                      def search_by_rule(rule)
         
     | 
| 
       12 
13 
     | 
    
         
             
                        # convert str(YAML) to hash or str(path/YAML file) to hash
         
     | 
| 
       13 
14 
     | 
    
         
             
                        rule = load_rule(rule)
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
       15 
16 
     | 
    
         
             
                        # validate rule schema
         
     | 
| 
       16 
     | 
    
         
            -
                        validate_rule 
     | 
| 
      
 17 
     | 
    
         
            +
                        rule = validate_rule(rule)
         
     | 
| 
       17 
18 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
                        analyzer = build_rule_analyzer( 
     | 
| 
      
 19 
     | 
    
         
            +
                        analyzer = build_rule_analyzer(
         
     | 
| 
      
 20 
     | 
    
         
            +
                          title: rule[:title],
         
     | 
| 
      
 21 
     | 
    
         
            +
                          description: rule[:description],
         
     | 
| 
      
 22 
     | 
    
         
            +
                          queries: rule[:queries],
         
     | 
| 
      
 23 
     | 
    
         
            +
                          tags: rule[:tags],
         
     | 
| 
      
 24 
     | 
    
         
            +
                          allowed_data_types: rule[:allowed_data_types],
         
     | 
| 
      
 25 
     | 
    
         
            +
                          disallowed_data_values: rule[:disallowed_data_values],
         
     | 
| 
      
 26 
     | 
    
         
            +
                          source: rule[:source],
         
     | 
| 
      
 27 
     | 
    
         
            +
                          id: rule[:id]
         
     | 
| 
      
 28 
     | 
    
         
            +
                        )
         
     | 
| 
       19 
29 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                        ignore_old_artifacts =  
     | 
| 
       21 
     | 
    
         
            -
                        ignore_threshold =  
     | 
| 
      
 30 
     | 
    
         
            +
                        ignore_old_artifacts = rule[:ignore_old_artifacts]
         
     | 
| 
      
 31 
     | 
    
         
            +
                        ignore_threshold = rule[:ignore_threshold]
         
     | 
| 
       22 
32 
     | 
    
         | 
| 
       23 
33 
     | 
    
         
             
                        with_error_handling do
         
     | 
| 
       24 
34 
     | 
    
         
             
                          run_rule_analyzer analyzer, ignore_old_artifacts: ignore_old_artifacts, ignore_threshold: ignore_threshold
         
     | 
| 
         @@ -37,13 +47,15 @@ module Mihari 
     | 
|
| 
       37 
47 
     | 
    
         
             
                  # @param [Array<Hash>] queries
         
     | 
| 
       38 
48 
     | 
    
         
             
                  # @param [Array<String>, nil] tags
         
     | 
| 
       39 
49 
     | 
    
         
             
                  # @param [Array<String>, nil] allowed_data_types
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # @param [Array<String>, nil] disallowed_data_values
         
     | 
| 
       40 
51 
     | 
    
         
             
                  # @param [String, nil] source
         
     | 
| 
       41 
52 
     | 
    
         
             
                  #
         
     | 
| 
       42 
53 
     | 
    
         
             
                  # @return [Mihari::Analyzers::Rule]
         
     | 
| 
       43 
54 
     | 
    
         
             
                  #
         
     | 
| 
       44 
     | 
    
         
            -
                  def build_rule_analyzer(title:, description:, queries:, tags: nil, allowed_data_types: nil, source: nil)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  def build_rule_analyzer(title:, description:, queries:, tags: nil, allowed_data_types: nil, disallowed_data_values: nil, source: nil, id: nil)
         
     | 
| 
       45 
56 
     | 
    
         
             
                    tags = [] if tags.nil?
         
     | 
| 
       46 
57 
     | 
    
         
             
                    allowed_data_types = ALLOWED_DATA_TYPES if allowed_data_types.nil?
         
     | 
| 
      
 58 
     | 
    
         
            +
                    disallowed_data_values = [] if disallowed_data_values.nil?
         
     | 
| 
       47 
59 
     | 
    
         | 
| 
       48 
60 
     | 
    
         
             
                    Analyzers::Rule.new(
         
     | 
| 
       49 
61 
     | 
    
         
             
                      title: title,
         
     | 
| 
         @@ -51,7 +63,9 @@ module Mihari 
     | 
|
| 
       51 
63 
     | 
    
         
             
                      tags: tags,
         
     | 
| 
       52 
64 
     | 
    
         
             
                      queries: queries,
         
     | 
| 
       53 
65 
     | 
    
         
             
                      allowed_data_types: allowed_data_types,
         
     | 
| 
       54 
     | 
    
         
            -
                       
     | 
| 
      
 66 
     | 
    
         
            +
                      disallowed_data_values: disallowed_data_values,
         
     | 
| 
      
 67 
     | 
    
         
            +
                      source: source,
         
     | 
| 
      
 68 
     | 
    
         
            +
                      id: id
         
     | 
| 
       55 
69 
     | 
    
         
             
                    )
         
     | 
| 
       56 
70 
     | 
    
         
             
                  end
         
     | 
| 
       57 
71 
     | 
    
         | 
| 
         @@ -59,8 +73,6 @@ module Mihari 
     | 
|
| 
       59 
73 
     | 
    
         
             
                  # Run rule analyzer
         
     | 
| 
       60 
74 
     | 
    
         
             
                  #
         
     | 
| 
       61 
75 
     | 
    
         
             
                  # @param [Mihari::Analyzer::Rule] analyzer
         
     | 
| 
       62 
     | 
    
         
            -
                  # @param [Boolean] ignore_old_artifacts
         
     | 
| 
       63 
     | 
    
         
            -
                  # @param [Integer] ignore_threshold
         
     | 
| 
       64 
76 
     | 
    
         
             
                  #
         
     | 
| 
       65 
77 
     | 
    
         
             
                  # @return [nil]
         
     | 
| 
       66 
78 
     | 
    
         
             
                  #
         
     | 
    
        data/lib/mihari/commands/web.rb
    CHANGED
    
    | 
         @@ -8,11 +8,16 @@ module Mihari 
     | 
|
| 
       8 
8 
     | 
    
         
             
                      desc "web", "Launch the web app"
         
     | 
| 
       9 
9 
     | 
    
         
             
                      method_option :port, type: :numeric, default: 9292
         
     | 
| 
       10 
10 
     | 
    
         
             
                      method_option :host, type: :string, default: "localhost"
         
     | 
| 
      
 11 
     | 
    
         
            +
                      method_option :config, type: :string, desc: "Path to the config file"
         
     | 
| 
       11 
12 
     | 
    
         
             
                      def web
         
     | 
| 
       12 
13 
     | 
    
         
             
                        port = options["port"].to_i || 9292
         
     | 
| 
       13 
14 
     | 
    
         
             
                        host = options["host"] || "localhost"
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
       15 
16 
     | 
    
         
             
                        load_configuration
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                        # set rack env as production
         
     | 
| 
      
 19 
     | 
    
         
            +
                        ENV["RACK_ENV"] ||= "production"
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
       16 
21 
     | 
    
         
             
                        Mihari::App.run!(port: port, host: host)
         
     | 
| 
       17 
22 
     | 
    
         
             
                      end
         
     | 
| 
       18 
23 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "mem"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Mihari
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Mixins
         
     | 
| 
      
 5 
     | 
    
         
            +
                module DisallowedDataValue
         
     | 
| 
      
 6 
     | 
    
         
            +
                  include Mem
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # Normalize a value as a disallowed data value
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # @param [String] value Data value
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # @return [String, Regexp] Normalized value
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  def normalize_disallowed_data_value(value)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    return value if !value.start_with?("/") || !value.end_with?("/")
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    # if a value is surrounded by slashes, take it as a regexp
         
     | 
| 
      
 19 
     | 
    
         
            +
                    value_without_slashes = value[1..-2]
         
     | 
| 
      
 20 
     | 
    
         
            +
                    Regexp.compile value_without_slashes
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  memoize :normalize_disallowed_data_value
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  #
         
     | 
| 
      
 26 
     | 
    
         
            +
                  # Check whetehr a value is valid format as a disallowed data value
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # @param [String] value Data value
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # @return [Boolean] true if it is valid, otherwise false
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #
         
     | 
| 
      
 32 
     | 
    
         
            +
                  def valid_disallowed_data_value?(value)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 34 
     | 
    
         
            +
                      normalize_disallowed_data_value value
         
     | 
| 
      
 35 
     | 
    
         
            +
                    rescue RegexpError
         
     | 
| 
      
 36 
     | 
    
         
            +
                      return false
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                    true
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/mihari/mixins/rule.rb
    CHANGED
    
    | 
         @@ -20,10 +20,12 @@ module Mihari 
     | 
|
| 
       20 
20 
     | 
    
         
             
                  end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                  #
         
     | 
| 
       23 
     | 
    
         
            -
                  # Validate rule schema
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # Validate rule schema and return a normalized rule
         
     | 
| 
       24 
24 
     | 
    
         
             
                  #
         
     | 
| 
       25 
25 
     | 
    
         
             
                  # @param [Hash] rule
         
     | 
| 
       26 
26 
     | 
    
         
             
                  #
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # @return [Hash]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #
         
     | 
| 
       27 
29 
     | 
    
         
             
                  def validate_rule(rule)
         
     | 
| 
       28 
30 
     | 
    
         
             
                    error_message = "Failed to parse the input as a rule!"
         
     | 
| 
       29 
31 
     | 
    
         | 
| 
         @@ -42,6 +44,8 @@ module Mihari 
     | 
|
| 
       42 
44 
     | 
    
         
             
                      puts error_message.colorize(:red)
         
     | 
| 
       43 
45 
     | 
    
         
             
                      raise ArgumentError, "Invalid rule schema"
         
     | 
| 
       44 
46 
     | 
    
         
             
                    end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    result.to_h
         
     | 
| 
       45 
49 
     | 
    
         
             
                  end
         
     | 
| 
       46 
50 
     | 
    
         | 
| 
       47 
51 
     | 
    
         
             
                  #
         
     | 
    
        data/lib/mihari/models/alert.rb
    CHANGED
    
    | 
         @@ -18,8 +18,8 @@ module Mihari 
     | 
|
| 
       18 
18 
     | 
    
         
             
                  # @param [String, nil] source
         
     | 
| 
       19 
19 
     | 
    
         
             
                  # @param [String, nil] tag_name
         
     | 
| 
       20 
20 
     | 
    
         
             
                  # @param [String, nil] title
         
     | 
| 
       21 
     | 
    
         
            -
                  # @param [ 
     | 
| 
       22 
     | 
    
         
            -
                  # @param [ 
     | 
| 
      
 21 
     | 
    
         
            +
                  # @param [DateTime, nil] from_at
         
     | 
| 
      
 22 
     | 
    
         
            +
                  # @param [DateTime, nil] to_at
         
     | 
| 
       23 
23 
     | 
    
         
             
                  # @param [Integer, nil] limit
         
     | 
| 
       24 
24 
     | 
    
         
             
                  # @param [Integer, nil] page
         
     | 
| 
       25 
25 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -34,7 +34,15 @@ module Mihari 
     | 
|
| 
       34 
34 
     | 
    
         | 
| 
       35 
35 
     | 
    
         
             
                    offset = (page - 1) * limit
         
     | 
| 
       36 
36 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
                    relation = build_relation( 
     | 
| 
      
 37 
     | 
    
         
            +
                    relation = build_relation(
         
     | 
| 
      
 38 
     | 
    
         
            +
                      artifact_data: artifact_data,
         
     | 
| 
      
 39 
     | 
    
         
            +
                      title: title,
         
     | 
| 
      
 40 
     | 
    
         
            +
                      description: description,
         
     | 
| 
      
 41 
     | 
    
         
            +
                      source: source,
         
     | 
| 
      
 42 
     | 
    
         
            +
                      tag_name: tag_name,
         
     | 
| 
      
 43 
     | 
    
         
            +
                      from_at: from_at,
         
     | 
| 
      
 44 
     | 
    
         
            +
                      to_at: to_at
         
     | 
| 
      
 45 
     | 
    
         
            +
                    )
         
     | 
| 
       38 
46 
     | 
    
         | 
| 
       39 
47 
     | 
    
         
             
                    alerts = relation.limit(limit).offset(offset).order(id: :desc)
         
     | 
| 
       40 
48 
     | 
    
         | 
| 
         @@ -54,13 +62,21 @@ module Mihari 
     | 
|
| 
       54 
62 
     | 
    
         
             
                  # @param [String, nil] source
         
     | 
| 
       55 
63 
     | 
    
         
             
                  # @param [String, nil] tag_name
         
     | 
| 
       56 
64 
     | 
    
         
             
                  # @param [String, nil] title
         
     | 
| 
       57 
     | 
    
         
            -
                  # @param [ 
     | 
| 
       58 
     | 
    
         
            -
                  # @param [ 
     | 
| 
      
 65 
     | 
    
         
            +
                  # @param [DateTime, nil] from_at
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # @param [DateTime, nil] to_at
         
     | 
| 
       59 
67 
     | 
    
         
             
                  #
         
     | 
| 
       60 
68 
     | 
    
         
             
                  # @return [Integer]
         
     | 
| 
       61 
69 
     | 
    
         
             
                  #
         
     | 
| 
       62 
70 
     | 
    
         
             
                  def count(artifact_data: nil, description: nil, source: nil, tag_name: nil, title: nil, from_at: nil, to_at: nil)
         
     | 
| 
       63 
     | 
    
         
            -
                    relation = build_relation( 
     | 
| 
      
 71 
     | 
    
         
            +
                    relation = build_relation(
         
     | 
| 
      
 72 
     | 
    
         
            +
                      artifact_data: artifact_data,
         
     | 
| 
      
 73 
     | 
    
         
            +
                      title: title,
         
     | 
| 
      
 74 
     | 
    
         
            +
                      description: description,
         
     | 
| 
      
 75 
     | 
    
         
            +
                      source: source,
         
     | 
| 
      
 76 
     | 
    
         
            +
                      tag_name: tag_name,
         
     | 
| 
      
 77 
     | 
    
         
            +
                      from_at: from_at,
         
     | 
| 
      
 78 
     | 
    
         
            +
                      to_at: to_at
         
     | 
| 
      
 79 
     | 
    
         
            +
                    )
         
     | 
| 
       64 
80 
     | 
    
         
             
                    relation.distinct("alerts.id").count
         
     | 
| 
       65 
81 
     | 
    
         
             
                  end
         
     | 
| 
       66 
82 
     | 
    
         | 
| 
         @@ -68,11 +84,9 @@ module Mihari 
     | 
|
| 
       68 
84 
     | 
    
         | 
| 
       69 
85 
     | 
    
         
             
                  def build_relation(artifact_data: nil, title: nil, description: nil, source: nil, tag_name: nil, from_at: nil, to_at: nil)
         
     | 
| 
       70 
86 
     | 
    
         
             
                    relation = self
         
     | 
| 
       71 
     | 
    
         
            -
                    relation = joins(:tags) if tag_name
         
     | 
| 
       72 
     | 
    
         
            -
                    relation = joins(:artifacts) if artifact_data
         
     | 
| 
       73 
87 
     | 
    
         | 
| 
       74 
     | 
    
         
            -
                    relation = relation.where(artifacts: { data: artifact_data }) if artifact_data
         
     | 
| 
       75 
     | 
    
         
            -
                    relation = relation.where(tags: { name: tag_name }) if tag_name
         
     | 
| 
      
 88 
     | 
    
         
            +
                    relation = relation.joins(:artifacts).where(artifacts: { data: artifact_data }) if artifact_data
         
     | 
| 
      
 89 
     | 
    
         
            +
                    relation = relation.joins(:tags).where(tags: { name: tag_name }) if tag_name
         
     | 
| 
       76 
90 
     | 
    
         | 
| 
       77 
91 
     | 
    
         
             
                    relation = relation.where(source: source) if source
         
     | 
| 
       78 
92 
     | 
    
         
             
                    relation = relation.where(title: title) if title
         
     | 
| 
         @@ -13,6 +13,7 @@ module Mihari 
     | 
|
| 
       13 
13 
     | 
    
         
             
                  optional(:censys_secret).value(:string)
         
     | 
| 
       14 
14 
     | 
    
         
             
                  optional(:circl_passive_password).value(:string)
         
     | 
| 
       15 
15 
     | 
    
         
             
                  optional(:circl_passive_username).value(:string)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  optional(:ipinfo_api_key).value(:string)
         
     | 
| 
       16 
17 
     | 
    
         
             
                  optional(:misp_api_endpoint).value(:string)
         
     | 
| 
       17 
18 
     | 
    
         
             
                  optional(:misp_api_key).value(:string)
         
     | 
| 
       18 
19 
     | 
    
         
             
                  optional(:onyphe_api_key).value(:string)
         
     | 
    
        data/lib/mihari/schemas/rule.rb
    CHANGED
    
    | 
         @@ -63,10 +63,24 @@ module Mihari 
     | 
|
| 
       63 
63 
     | 
    
         
             
                  required(:queries).value(:array).each { Analyzer | Spyse | ZoomEye | Urlscan | Crtsh }
         
     | 
| 
       64 
64 
     | 
    
         | 
| 
       65 
65 
     | 
    
         
             
                  optional(:allowed_data_types).value(array[DataTypes]).default(ALLOWED_DATA_TYPES)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  optional(:disallowed_data_values).value(array[:string]).default([])
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  optional(:ignore_old_artifacts).value(:bool).default(false)
         
     | 
| 
      
 69 
     | 
    
         
            +
                  optional(:ignore_threshold).value(:integer).default(0)
         
     | 
| 
       66 
70 
     | 
    
         
             
                end
         
     | 
| 
       67 
71 
     | 
    
         | 
| 
       68 
72 
     | 
    
         
             
                class RuleContract < Dry::Validation::Contract
         
     | 
| 
      
 73 
     | 
    
         
            +
                  include Mihari::Mixins::DisallowedDataValue
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
       69 
75 
     | 
    
         
             
                  params(Rule)
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  rule(:disallowed_data_values) do
         
     | 
| 
      
 78 
     | 
    
         
            +
                    value.each do |v|
         
     | 
| 
      
 79 
     | 
    
         
            +
                      unless valid_disallowed_data_value?(v)
         
     | 
| 
      
 80 
     | 
    
         
            +
                        key.failure("#{v} is not a valid format.")
         
     | 
| 
      
 81 
     | 
    
         
            +
                      end
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
       70 
84 
     | 
    
         
             
                end
         
     | 
| 
       71 
85 
     | 
    
         
             
              end
         
     | 
| 
       72 
86 
     | 
    
         
             
            end
         
     | 
| 
         @@ -2,7 +2,7 @@ title: ... # String (required) 
     | 
|
| 
       2 
2 
     | 
    
         
             
            description: ... # String (required)
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            id: ... # String (optional)
         
     | 
| 
       5 
     | 
    
         
            -
            author:  
     | 
| 
      
 5 
     | 
    
         
            +
            author: ... # String (optional)
         
     | 
| 
       6 
6 
     | 
    
         
             
            created_on: <%= Date.today %> # Date (optional)
         
     | 
| 
       7 
7 
     | 
    
         
             
            updated_on: <%= Date.today %> # Date (optional)
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
         @@ -13,6 +13,10 @@ allowed_data_types: # Array<String> (Optional, defaults to ["hash", "ip", "domai 
     | 
|
| 
       13 
13 
     | 
    
         
             
              - domain
         
     | 
| 
       14 
14 
     | 
    
         
             
              - url
         
     | 
| 
       15 
15 
     | 
    
         
             
              - mail
         
     | 
| 
      
 16 
     | 
    
         
            +
            disallowed_data_values: [] # Array<String> (Optional, defaults to [])
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            ignore_old_artifacts: true # Whether to ignore old artifacts from checking or not (Optional, defaults to true)
         
     | 
| 
      
 19 
     | 
    
         
            +
            ignore_threshold: 0 # Number of days to define whether an artifact is old or not (Optional, defaults to 0)
         
     | 
| 
       16 
20 
     | 
    
         | 
| 
       17 
21 
     | 
    
         
             
            queries: # Array<Hash> (required)
         
     | 
| 
       18 
22 
     | 
    
         
             
              - analyzer: shodan # String (required)
         
     | 
    
        data/lib/mihari/version.rb
    CHANGED