mihari 3.0.1 → 3.4.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/build_frontend.sh +5 -0
 - data/config.ru +6 -0
 - data/docker/Dockerfile +1 -1
 - data/images/overview.jpg +0 -0
 - data/lib/mihari.rb +2 -0
 - data/lib/mihari/analyzers/rule.rb +27 -2
 - data/lib/mihari/cli/analyzer.rb +3 -0
 - data/lib/mihari/cli/base.rb +0 -3
 - data/lib/mihari/commands/search.rb +19 -8
 - data/lib/mihari/commands/web.rb +4 -0
 - data/lib/mihari/mixins/disallowed_data_value.rb +42 -0
 - data/lib/mihari/mixins/rule.rb +5 -1
 - data/lib/mihari/models/artifact.rb +1 -1
 - data/lib/mihari/schemas/analyzer.rb +25 -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 +2 -0
 - data/lib/mihari/web/controllers/analyzers_controller.rb +38 -0
 - data/lib/mihari/web/public/index.html +1 -1
 - data/lib/mihari/web/public/redoc-static.html +338 -461
 - data/lib/mihari/web/public/static/js/app.365f1907.js +13 -0
 - data/lib/mihari/web/public/static/js/app.365f1907.js.map +1 -0
 - data/mihari.gemspec +4 -4
 - metadata +17 -11
 - 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: 142df147927aee93e6c653b2eb29cca50f4ba11606d68f1302af21780d6f0dc5
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 594f762c94e361cc53cab08b39abad5e3503555b51d93add3cbce744fcbff711
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 8483d2d125e30f04bdaf74243877dd9a261511d7d857f6dba42c6ea54af5de95f63c23759d1937badfdb5ae75819b99e70270edb0fb2f466601b8eaf6912dc8e
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 8ffca15aadc0bc783086dd11df9fd051933af31e6778fcd4a67ddf51af5532b452603526a0303eb7c1dfdb530636a6e91df8440210189af3dc0f2806a4e64218
         
     | 
    
        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/build_frontend.sh
    CHANGED
    
    | 
         @@ -2,6 +2,7 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            CURRENT_DIR=${PWD}
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            # build the frontend app
         
     | 
| 
       5 
6 
     | 
    
         
             
            mkdir -p tmp
         
     | 
| 
       6 
7 
     | 
    
         
             
            cd tmp
         
     | 
| 
       7 
8 
     | 
    
         
             
            git clone https://github.com/ninoseki/mihari-frontend.git
         
     | 
| 
         @@ -11,4 +12,8 @@ npm run build 
     | 
|
| 
       11 
12 
     | 
    
         | 
| 
       12 
13 
     | 
    
         
             
            cp -r dist/* ${CURRENT_DIR}/lib/mihari/web/public
         
     | 
| 
       13 
14 
     | 
    
         | 
| 
      
 15 
     | 
    
         
            +
            # replace favicon path
         
     | 
| 
      
 16 
     | 
    
         
            +
            sed -i "" 's/href="\/favicon.ico"/href="\/static\/favicon.ico"/' ${CURRENT_DIR}/lib/mihari/web/public/index.html
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            # remove tmp dir
         
     | 
| 
       14 
19 
     | 
    
         
             
            rm -rf ${CURRENT_DIR}/tmp/mihari-frontend
         
     | 
    
        data/config.ru
    ADDED
    
    
    
        data/docker/Dockerfile
    CHANGED
    
    
    
        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"
         
     | 
| 
         @@ -94,6 +95,7 @@ require "mihari/type_checker" 
     | 
|
| 
       94 
95 
     | 
    
         
             
            require "mihari/constraints"
         
     | 
| 
       95 
96 
     | 
    
         | 
| 
       96 
97 
     | 
    
         
             
            # Schemas
         
     | 
| 
      
 98 
     | 
    
         
            +
            require "mihari/schemas/analyzer"
         
     | 
| 
       97 
99 
     | 
    
         
             
            require "mihari/schemas/configuration"
         
     | 
| 
       98 
100 
     | 
    
         
             
            require "mihari/schemas/rule"
         
     | 
| 
       99 
101 
     | 
    
         | 
| 
         @@ -2,11 +2,11 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require "uuidtools"
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            NIL = nil
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
5 
     | 
    
         
             
            module Mihari
         
     | 
| 
       8 
6 
     | 
    
         
             
              module Analyzers
         
     | 
| 
       9 
7 
     | 
    
         
             
                class Rule < Base
         
     | 
| 
      
 8 
     | 
    
         
            +
                  include Mihari::Mixins::DisallowedDataValue
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
       10 
10 
     | 
    
         
             
                  option :title
         
     | 
| 
       11 
11 
     | 
    
         
             
                  option :description
         
     | 
| 
       12 
12 
     | 
    
         
             
                  option :queries
         
     | 
| 
         @@ -14,6 +14,7 @@ module Mihari 
     | 
|
| 
       14 
14 
     | 
    
         
             
                  option :id, default: proc {}
         
     | 
| 
       15 
15 
     | 
    
         
             
                  option :tags, default: proc { [] }
         
     | 
| 
       16 
16 
     | 
    
         
             
                  option :allowed_data_types, default: proc { ALLOWED_DATA_TYPES }
         
     | 
| 
      
 17 
     | 
    
         
            +
                  option :disallowed_data_values, default: proc { [] }
         
     | 
| 
       17 
18 
     | 
    
         | 
| 
       18 
19 
     | 
    
         
             
                  attr_reader :source
         
     | 
| 
       19 
20 
     | 
    
         | 
| 
         @@ -70,12 +71,36 @@ module Mihari 
     | 
|
| 
       70 
71 
     | 
    
         
             
                  # - Uniquefy artifacts by #uniq(&:data)
         
     | 
| 
       71 
72 
     | 
    
         
             
                  # - Reject an invalid artifact (for just in case)
         
     | 
| 
       72 
73 
     | 
    
         
             
                  # - Select artifacts with allowed data types
         
     | 
| 
      
 74 
     | 
    
         
            +
                  # - Reject artifacts with disallowed data values
         
     | 
| 
       73 
75 
     | 
    
         
             
                  #
         
     | 
| 
       74 
76 
     | 
    
         
             
                  # @return [Array<Mihari::Artifact>]
         
     | 
| 
       75 
77 
     | 
    
         
             
                  #
         
     | 
| 
       76 
78 
     | 
    
         
             
                  def normalized_artifacts
         
     | 
| 
       77 
79 
     | 
    
         
             
                    @normalized_artifacts ||= artifacts.uniq(&:data).select(&:valid?).select do |artifact|
         
     | 
| 
       78 
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)
         
     | 
| 
       79 
104 
     | 
    
         
             
                    end
         
     | 
| 
       80 
105 
     | 
    
         
             
                  end
         
     | 
| 
       81 
106 
     | 
    
         | 
    
        data/lib/mihari/cli/analyzer.rb
    CHANGED
    
    | 
         @@ -22,6 +22,9 @@ 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 
     | 
    
         
            +
             
     | 
| 
       25 
28 
     | 
    
         
             
                  include Mihari::Commands::BinaryEdge
         
     | 
| 
       26 
29 
     | 
    
         
             
                  include Mihari::Commands::Censys
         
     | 
| 
       27 
30 
     | 
    
         
             
                  include Mihari::Commands::CIRCL
         
     | 
    
        data/lib/mihari/cli/base.rb
    CHANGED
    
    | 
         @@ -14,9 +14,6 @@ module Mihari 
     | 
|
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
                  class_option :config, type: :string, desc: "Path to the config file"
         
     | 
| 
       16 
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 
17 
     | 
    
         
             
                  class << self
         
     | 
| 
       21 
18 
     | 
    
         
             
                    def exit_on_failure?
         
     | 
| 
       22 
19 
     | 
    
         
             
                      true
         
     | 
| 
         @@ -13,12 +13,21 @@ module Mihari 
     | 
|
| 
       13 
13 
     | 
    
         
             
                        rule = load_rule(rule)
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
                        # validate rule schema
         
     | 
| 
       16 
     | 
    
         
            -
                        validate_rule 
     | 
| 
      
 16 
     | 
    
         
            +
                        rule = validate_rule(rule)
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
                        analyzer = build_rule_analyzer( 
     | 
| 
      
 18 
     | 
    
         
            +
                        analyzer = build_rule_analyzer(
         
     | 
| 
      
 19 
     | 
    
         
            +
                          title: rule[:title],
         
     | 
| 
      
 20 
     | 
    
         
            +
                          description: rule[:description],
         
     | 
| 
      
 21 
     | 
    
         
            +
                          queries: rule[:queries],
         
     | 
| 
      
 22 
     | 
    
         
            +
                          tags: rule[:tags],
         
     | 
| 
      
 23 
     | 
    
         
            +
                          allowed_data_types: rule[:allowed_data_types],
         
     | 
| 
      
 24 
     | 
    
         
            +
                          disallowed_data_values: rule[:disallowed_data_values],
         
     | 
| 
      
 25 
     | 
    
         
            +
                          source: rule[:source],
         
     | 
| 
      
 26 
     | 
    
         
            +
                          id: rule[:id]
         
     | 
| 
      
 27 
     | 
    
         
            +
                        )
         
     | 
| 
       19 
28 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                        ignore_old_artifacts =  
     | 
| 
       21 
     | 
    
         
            -
                        ignore_threshold =  
     | 
| 
      
 29 
     | 
    
         
            +
                        ignore_old_artifacts = rule[:ignore_old_artifacts]
         
     | 
| 
      
 30 
     | 
    
         
            +
                        ignore_threshold = rule[:ignore_threshold]
         
     | 
| 
       22 
31 
     | 
    
         | 
| 
       23 
32 
     | 
    
         
             
                        with_error_handling do
         
     | 
| 
       24 
33 
     | 
    
         
             
                          run_rule_analyzer analyzer, ignore_old_artifacts: ignore_old_artifacts, ignore_threshold: ignore_threshold
         
     | 
| 
         @@ -37,13 +46,15 @@ module Mihari 
     | 
|
| 
       37 
46 
     | 
    
         
             
                  # @param [Array<Hash>] queries
         
     | 
| 
       38 
47 
     | 
    
         
             
                  # @param [Array<String>, nil] tags
         
     | 
| 
       39 
48 
     | 
    
         
             
                  # @param [Array<String>, nil] allowed_data_types
         
     | 
| 
      
 49 
     | 
    
         
            +
                  # @param [Array<String>, nil] disallowed_data_values
         
     | 
| 
       40 
50 
     | 
    
         
             
                  # @param [String, nil] source
         
     | 
| 
       41 
51 
     | 
    
         
             
                  #
         
     | 
| 
       42 
52 
     | 
    
         
             
                  # @return [Mihari::Analyzers::Rule]
         
     | 
| 
       43 
53 
     | 
    
         
             
                  #
         
     | 
| 
       44 
     | 
    
         
            -
                  def build_rule_analyzer(title:, description:, queries:, tags: nil, allowed_data_types: nil, source: nil)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  def build_rule_analyzer(title:, description:, queries:, tags: nil, allowed_data_types: nil, disallowed_data_values: nil, source: nil, id: nil)
         
     | 
| 
       45 
55 
     | 
    
         
             
                    tags = [] if tags.nil?
         
     | 
| 
       46 
56 
     | 
    
         
             
                    allowed_data_types = ALLOWED_DATA_TYPES if allowed_data_types.nil?
         
     | 
| 
      
 57 
     | 
    
         
            +
                    disallowed_data_values = [] if disallowed_data_values.nil?
         
     | 
| 
       47 
58 
     | 
    
         | 
| 
       48 
59 
     | 
    
         
             
                    Analyzers::Rule.new(
         
     | 
| 
       49 
60 
     | 
    
         
             
                      title: title,
         
     | 
| 
         @@ -51,7 +62,9 @@ module Mihari 
     | 
|
| 
       51 
62 
     | 
    
         
             
                      tags: tags,
         
     | 
| 
       52 
63 
     | 
    
         
             
                      queries: queries,
         
     | 
| 
       53 
64 
     | 
    
         
             
                      allowed_data_types: allowed_data_types,
         
     | 
| 
       54 
     | 
    
         
            -
                       
     | 
| 
      
 65 
     | 
    
         
            +
                      disallowed_data_values: disallowed_data_values,
         
     | 
| 
      
 66 
     | 
    
         
            +
                      source: source,
         
     | 
| 
      
 67 
     | 
    
         
            +
                      id: id
         
     | 
| 
       55 
68 
     | 
    
         
             
                    )
         
     | 
| 
       56 
69 
     | 
    
         
             
                  end
         
     | 
| 
       57 
70 
     | 
    
         | 
| 
         @@ -59,8 +72,6 @@ module Mihari 
     | 
|
| 
       59 
72 
     | 
    
         
             
                  # Run rule analyzer
         
     | 
| 
       60 
73 
     | 
    
         
             
                  #
         
     | 
| 
       61 
74 
     | 
    
         
             
                  # @param [Mihari::Analyzer::Rule] analyzer
         
     | 
| 
       62 
     | 
    
         
            -
                  # @param [Boolean] ignore_old_artifacts
         
     | 
| 
       63 
     | 
    
         
            -
                  # @param [Integer] ignore_threshold
         
     | 
| 
       64 
75 
     | 
    
         
             
                  #
         
     | 
| 
       65 
76 
     | 
    
         
             
                  # @return [nil]
         
     | 
| 
       66 
77 
     | 
    
         
             
                  #
         
     | 
    
        data/lib/mihari/commands/web.rb
    CHANGED
    
    
| 
         @@ -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 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -39,7 +39,7 @@ module Mihari 
     | 
|
| 
       39 
39 
     | 
    
         | 
| 
       40 
40 
     | 
    
         
             
                  return false unless ignore_old_artifacts
         
     | 
| 
       41 
41 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                  days_before = (-ignore_threshold).days.from_now
         
     | 
| 
      
 42 
     | 
    
         
            +
                  days_before = (-ignore_threshold).days.from_now.utc
         
     | 
| 
       43 
43 
     | 
    
         
             
                  # if an artifact is created before {ignore_threshold} days, ignore it
         
     | 
| 
       44 
44 
     | 
    
         
             
                  #                           within {ignore_threshold} days, do not ignore it
         
     | 
| 
       45 
45 
     | 
    
         
             
                  artifact.created_at < days_before
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "dry/schema"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "dry/validation"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            require "mihari/schemas/macros"
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            module Mihari
         
     | 
| 
      
 9 
     | 
    
         
            +
              module Schemas
         
     | 
| 
      
 10 
     | 
    
         
            +
                AnalyzerRun = Dry::Schema.Params do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  required(:title).value(:string)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  required(:description).value(:string)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  required(:source).value(:string)
         
     | 
| 
      
 14 
     | 
    
         
            +
                  required(:artifacts).value(array[:string])
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  optional(:tags).value(array[:string]).default([])
         
     | 
| 
      
 17 
     | 
    
         
            +
                  optional(:ignoreOldArtifacts).value(:bool).default(false)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  optional(:ignoreThreshold).value(:integer).default(0)
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                class AnalyzerRunContract < Dry::Validation::Contract
         
     | 
| 
      
 22 
     | 
    
         
            +
                  params(AnalyzerRun)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
    
        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
    
    
    
        data/lib/mihari/web/app.rb
    CHANGED
    
    | 
         @@ -10,6 +10,7 @@ require "mihari/web/helpers/json" 
     | 
|
| 
       10 
10 
     | 
    
         
             
            require "mihari/web/controllers/base_controller"
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
            require "mihari/web/controllers/alerts_controller"
         
     | 
| 
      
 13 
     | 
    
         
            +
            require "mihari/web/controllers/analyzers_controller"
         
     | 
| 
       13 
14 
     | 
    
         
             
            require "mihari/web/controllers/artifacts_controller"
         
     | 
| 
       14 
15 
     | 
    
         
             
            require "mihari/web/controllers/command_controller"
         
     | 
| 
       15 
16 
     | 
    
         
             
            require "mihari/web/controllers/config_controller"
         
     | 
| 
         @@ -26,6 +27,7 @@ module Mihari 
     | 
|
| 
       26 
27 
     | 
    
         
             
                end
         
     | 
| 
       27 
28 
     | 
    
         | 
| 
       28 
29 
     | 
    
         
             
                use Mihari::Controllers::AlertsController
         
     | 
| 
      
 30 
     | 
    
         
            +
                use Mihari::Controllers::AnalyzersController
         
     | 
| 
       29 
31 
     | 
    
         
             
                use Mihari::Controllers::ArtifactsController
         
     | 
| 
       30 
32 
     | 
    
         
             
                use Mihari::Controllers::CommandController
         
     | 
| 
       31 
33 
     | 
    
         
             
                use Mihari::Controllers::ConfigController
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Mihari
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Controllers
         
     | 
| 
      
 5 
     | 
    
         
            +
                class AnalyzersController < BaseController
         
     | 
| 
      
 6 
     | 
    
         
            +
                  post "/api/analyzer" do
         
     | 
| 
      
 7 
     | 
    
         
            +
                    contract = Mihari::Schemas::AnalyzerRunContract.new
         
     | 
| 
      
 8 
     | 
    
         
            +
                    result = contract.call(params)
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    unless result.errors.empty?
         
     | 
| 
      
 11 
     | 
    
         
            +
                      status 400
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                      return json(result.errors.to_h)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    args = result.to_h
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                    ignore_old_artifacts = args[:ignoreOldArtifacts]
         
     | 
| 
      
 19 
     | 
    
         
            +
                    ignore_threshold = args[:ignoreThreshold]
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    analyzer = Mihari::Analyzers::Basic.new(
         
     | 
| 
      
 22 
     | 
    
         
            +
                      title: args[:title],
         
     | 
| 
      
 23 
     | 
    
         
            +
                      description: args[:description],
         
     | 
| 
      
 24 
     | 
    
         
            +
                      source: args[:source],
         
     | 
| 
      
 25 
     | 
    
         
            +
                      artifacts: args[:artifacts],
         
     | 
| 
      
 26 
     | 
    
         
            +
                      tags: args[:tags]
         
     | 
| 
      
 27 
     | 
    
         
            +
                    )
         
     | 
| 
      
 28 
     | 
    
         
            +
                    analyzer.ignore_old_artifacts = ignore_old_artifacts
         
     | 
| 
      
 29 
     | 
    
         
            +
                    analyzer.ignore_threshold = ignore_threshold
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                    analyzer.run
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                    status 201
         
     | 
| 
      
 34 
     | 
    
         
            +
                    body ""
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     |