mihari 3.12.0 → 4.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +5 -0
  3. data/config.ru +0 -1
  4. data/lib/mihari/analyzers/base.rb +6 -6
  5. data/lib/mihari/analyzers/binaryedge.rb +3 -5
  6. data/lib/mihari/analyzers/censys.rb +1 -3
  7. data/lib/mihari/analyzers/circl.rb +0 -3
  8. data/lib/mihari/analyzers/crtsh.rb +7 -5
  9. data/lib/mihari/analyzers/dnpedia.rb +4 -4
  10. data/lib/mihari/analyzers/dnstwister.rb +1 -4
  11. data/lib/mihari/analyzers/feed.rb +0 -3
  12. data/lib/mihari/analyzers/greynoise.rb +1 -3
  13. data/lib/mihari/analyzers/onyphe.rb +1 -3
  14. data/lib/mihari/analyzers/otx.rb +0 -3
  15. data/lib/mihari/analyzers/passivetotal.rb +8 -9
  16. data/lib/mihari/analyzers/pulsedive.rb +7 -5
  17. data/lib/mihari/analyzers/rule.rb +5 -6
  18. data/lib/mihari/analyzers/securitytrails.rb +10 -7
  19. data/lib/mihari/analyzers/shodan.rb +19 -6
  20. data/lib/mihari/analyzers/spyse.rb +10 -11
  21. data/lib/mihari/analyzers/urlscan.rb +5 -6
  22. data/lib/mihari/analyzers/virustotal.rb +8 -9
  23. data/lib/mihari/analyzers/virustotal_intelligence.rb +4 -5
  24. data/lib/mihari/analyzers/zoomeye.rb +4 -5
  25. data/lib/mihari/cli/base.rb +0 -5
  26. data/lib/mihari/cli/init.rb +0 -2
  27. data/lib/mihari/cli/main.rb +4 -6
  28. data/lib/mihari/cli/mixins/utils.rb +2 -18
  29. data/lib/mihari/commands/init.rb +0 -18
  30. data/lib/mihari/commands/search.rb +20 -15
  31. data/lib/mihari/commands/validator.rb +7 -19
  32. data/lib/mihari/commands/web.rb +0 -3
  33. data/lib/mihari/database.rb +66 -14
  34. data/lib/mihari/emitters/misp.rb +0 -1
  35. data/lib/mihari/emitters/slack.rb +3 -4
  36. data/lib/mihari/emitters/stdout.rb +0 -2
  37. data/lib/mihari/emitters/the_hive.rb +0 -1
  38. data/lib/mihari/emitters/webhook.rb +1 -5
  39. data/lib/mihari/enrichers/ipinfo.rb +0 -2
  40. data/lib/mihari/errors.rb +2 -0
  41. data/lib/mihari/feed/reader.rb +22 -8
  42. data/lib/mihari/mixins/database.rb +14 -0
  43. data/lib/mihari/mixins/disallowed_data_value.rb +1 -4
  44. data/lib/mihari/mixins/rule.rb +34 -31
  45. data/lib/mihari/models/alert.rb +3 -3
  46. data/lib/mihari/models/artifact.rb +0 -5
  47. data/lib/mihari/models/autonomous_system.rb +0 -2
  48. data/lib/mihari/models/dns.rb +0 -3
  49. data/lib/mihari/models/geolocation.rb +0 -1
  50. data/lib/mihari/models/reverse_dns.rb +0 -3
  51. data/lib/mihari/models/rule.rb +73 -0
  52. data/lib/mihari/models/tag.rb +0 -2
  53. data/lib/mihari/models/tagging.rb +0 -2
  54. data/lib/mihari/models/whois.rb +0 -2
  55. data/lib/mihari/notifiers/exception_notifier.rb +0 -2
  56. data/lib/mihari/schemas/analyzer.rb +0 -5
  57. data/lib/mihari/schemas/macros.rb +0 -2
  58. data/lib/mihari/schemas/rule.rb +0 -5
  59. data/lib/mihari/structs/alert.rb +0 -3
  60. data/lib/mihari/structs/censys.rb +3 -4
  61. data/lib/mihari/structs/greynoise.rb +3 -4
  62. data/lib/mihari/structs/ipinfo.rb +0 -3
  63. data/lib/mihari/structs/onyphe.rb +5 -6
  64. data/lib/mihari/structs/rule.rb +127 -0
  65. data/lib/mihari/structs/shodan.rb +3 -4
  66. data/lib/mihari/structs/urlscan.rb +0 -3
  67. data/lib/mihari/structs/virustotal_intelligence.rb +3 -4
  68. data/lib/mihari/type_checker.rb +2 -6
  69. data/lib/mihari/types.rb +0 -2
  70. data/lib/mihari/version.rb +1 -1
  71. data/lib/mihari/web/api.rb +4 -0
  72. data/lib/mihari/web/app.rb +6 -8
  73. data/lib/mihari/web/endpoints/alerts.rb +7 -3
  74. data/lib/mihari/web/endpoints/artifacts.rb +6 -3
  75. data/lib/mihari/web/endpoints/command.rb +2 -1
  76. data/lib/mihari/web/endpoints/configs.rb +2 -1
  77. data/lib/mihari/web/endpoints/ip_addresses.rb +2 -1
  78. data/lib/mihari/web/endpoints/rules.rb +175 -0
  79. data/lib/mihari/web/endpoints/sources.rb +2 -1
  80. data/lib/mihari/web/endpoints/tags.rb +4 -2
  81. data/lib/mihari/web/entities/artifact.rb +2 -0
  82. data/lib/mihari/web/entities/rule.rb +35 -0
  83. data/lib/mihari/web/middleware/connection_adapter.rb +19 -0
  84. data/lib/mihari/web/public/index.html +1 -1
  85. data/lib/mihari/web/public/redoc-static.html +39 -22
  86. data/lib/mihari/web/public/static/js/app.49ab738a.js +21 -0
  87. data/lib/mihari/web/public/static/js/app.49ab738a.js.map +1 -0
  88. data/lib/mihari/web/public/static/js/app.b88ce341.js +35 -0
  89. data/lib/mihari/web/public/static/js/app.b88ce341.js.map +1 -0
  90. data/lib/mihari/web/public/static/js/app.cb1fa7be.js +30 -0
  91. data/lib/mihari/web/public/static/js/app.cb1fa7be.js.map +1 -0
  92. data/lib/mihari/web/public/static/js/app.eb95cfc9.js +30 -0
  93. data/lib/mihari/web/public/static/js/app.eb95cfc9.js.map +1 -0
  94. data/lib/mihari.rb +40 -35
  95. data/mihari.gemspec +11 -13
  96. data/sig/lib/mihari/analyzers/binaryedge.rbs +0 -3
  97. data/sig/lib/mihari/analyzers/censys.rbs +0 -3
  98. data/sig/lib/mihari/analyzers/circl.rbs +1 -3
  99. data/sig/lib/mihari/analyzers/crtsh.rbs +1 -3
  100. data/sig/lib/mihari/analyzers/dnpedia.rbs +1 -4
  101. data/sig/lib/mihari/analyzers/dnstwister.rbs +1 -3
  102. data/sig/lib/mihari/analyzers/feed.rbs +0 -3
  103. data/sig/lib/mihari/analyzers/onyphe.rbs +0 -3
  104. data/sig/lib/mihari/analyzers/otx.rbs +1 -3
  105. data/sig/lib/mihari/analyzers/passivetotal.rbs +3 -5
  106. data/sig/lib/mihari/analyzers/pulsedive.rbs +2 -4
  107. data/sig/lib/mihari/analyzers/securitytrails.rbs +3 -5
  108. data/sig/lib/mihari/analyzers/shodan.rbs +2 -3
  109. data/sig/lib/mihari/analyzers/spyse.rbs +4 -6
  110. data/sig/lib/mihari/analyzers/urlscan.rbs +1 -3
  111. data/sig/lib/mihari/analyzers/virustotal.rbs +4 -6
  112. data/sig/lib/mihari/analyzers/virustotal_intelligence.rbs +0 -3
  113. data/sig/lib/mihari/analyzers/zoomeye.rbs +2 -4
  114. data/sig/lib/mihari/commands/init.rbs +0 -2
  115. data/sig/lib/mihari/commands/validator.rbs +0 -2
  116. data/sig/lib/mihari/emitters/slack.rbs +0 -1
  117. data/sig/lib/mihari/feed/reader.rbs +1 -1
  118. data/sig/lib/mihari/mixins/disallowed_data_value.rbs +0 -2
  119. data/sig/lib/mihari/mixins/rule.rbs +5 -12
  120. data/sig/lib/mihari/models/alert.rbs +1 -1
  121. data/sig/lib/mihari/models/artifact.rbs +2 -0
  122. data/sig/lib/mihari/models/rule.rbs +14 -0
  123. data/sig/lib/mihari/structs/rule.rbs +56 -0
  124. data/sig/lib/mihari.rbs +0 -2
  125. metadata +40 -97
  126. data/lib/mihari/analyzers/basic.rb +0 -19
  127. data/lib/mihari/cli/analyzer.rb +0 -55
  128. data/lib/mihari/commands/binaryedge.rb +0 -21
  129. data/lib/mihari/commands/censys.rb +0 -22
  130. data/lib/mihari/commands/circl.rb +0 -21
  131. data/lib/mihari/commands/crtsh.rb +0 -22
  132. data/lib/mihari/commands/dnpedia.rb +0 -21
  133. data/lib/mihari/commands/dnstwister.rb +0 -21
  134. data/lib/mihari/commands/feed.rb +0 -26
  135. data/lib/mihari/commands/greynoise.rb +0 -21
  136. data/lib/mihari/commands/json.rb +0 -42
  137. data/lib/mihari/commands/onyphe.rb +0 -21
  138. data/lib/mihari/commands/otx.rb +0 -21
  139. data/lib/mihari/commands/passivetotal.rb +0 -22
  140. data/lib/mihari/commands/pulsedive.rb +0 -21
  141. data/lib/mihari/commands/securitytrails.rb +0 -22
  142. data/lib/mihari/commands/shodan.rb +0 -21
  143. data/lib/mihari/commands/spyse.rb +0 -22
  144. data/lib/mihari/commands/urlscan.rb +0 -22
  145. data/lib/mihari/commands/virustotal.rb +0 -22
  146. data/lib/mihari/commands/virustotal_intelligence.rb +0 -22
  147. data/lib/mihari/commands/zoomeye.rb +0 -22
  148. data/lib/mihari/mixins/configuration.rb +0 -100
  149. data/lib/mihari/mixins/hash.rb +0 -20
  150. data/lib/mihari/schemas/configuration.rb +0 -44
  151. data/lib/mihari/web/public/grape.rb +0 -73
  152. data/sig/lib/mihari/analyzers/basic.rbs +0 -17
  153. data/sig/lib/mihari/cli/analyzer.rbs +0 -43
  154. data/sig/lib/mihari/commands/binaryedge.rbs +0 -7
  155. data/sig/lib/mihari/commands/censys.rbs +0 -7
  156. data/sig/lib/mihari/commands/circl.rbs +0 -7
  157. data/sig/lib/mihari/commands/crtsh.rbs +0 -7
  158. data/sig/lib/mihari/commands/dnpedia.rbs +0 -7
  159. data/sig/lib/mihari/commands/dnstwister.rbs +0 -7
  160. data/sig/lib/mihari/commands/feed.rbs +0 -7
  161. data/sig/lib/mihari/commands/onyphe.rbs +0 -7
  162. data/sig/lib/mihari/commands/otx.rbs +0 -7
  163. data/sig/lib/mihari/commands/passivetotal.rbs +0 -7
  164. data/sig/lib/mihari/commands/pulsedive.rbs +0 -7
  165. data/sig/lib/mihari/commands/securitytrails.rbs +0 -7
  166. data/sig/lib/mihari/commands/shodan.rbs +0 -7
  167. data/sig/lib/mihari/commands/spyse.rbs +0 -7
  168. data/sig/lib/mihari/commands/urlscan.rbs +0 -7
  169. data/sig/lib/mihari/commands/virustotal.rbs +0 -7
  170. data/sig/lib/mihari/commands/zoomeye.rbs +0 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a17c14dffe68b2f82316858615371036996a3a2b6d31d3c6a5c431294c38634
4
- data.tar.gz: 97afaf2f8b20985b0908cc1fe5778fe75ab7d0e1bf13b990a7b4165c92843604
3
+ metadata.gz: c9d5f66d8c2bc30eeb644d6841662778e5457db184e21a35d9bc689cbf653d25
4
+ data.tar.gz: 64edb36cf057854f4216ac51158dfa3e2b2876bb89a81c9441e38a4015c9bc78
5
5
  SHA512:
6
- metadata.gz: 418d7f278536e245196723d2ebbe0d99934b0db3278e8f8178470241c67f25e05d0aacf7bab2b766ce69d0589a79e87eca554a91fe4cfae5cfa12cd52d97fb61
7
- data.tar.gz: 9e90193273ee40c9956a138c2c73b713eb610a3ebdc1c9def73a6d354c4124a97e6842e438004956b3edc3206dfee8bae5771a86e964b59a2a7065a1f910891f
6
+ metadata.gz: 54eb1b87c28c9af6a2e045ecc1c4bacb575353fe7a7eaa7f9c5ba2a4cea0a15b46342707b444b3a65f9c14317c6b19c58c8085feb653f6167a57c7f3231ce284
7
+ data.tar.gz: b67ece0bb87d24ca83773a1c50b80560b46505a069f26a9c4c0ffe09105f2fe05ddafb4cfd5d0e1e7b1603ae5f1a11ea1d7b161971c37207c07869acb49e9b6c
data/Rakefile CHANGED
@@ -7,3 +7,8 @@ require "standard/rake"
7
7
  RSpec::Core::RakeTask.new(:spec)
8
8
 
9
9
  task default: :spec
10
+
11
+ desc "run rackup (via rerun)"
12
+ task :rackup do
13
+ sh "rerun --pattern '{Gemfile.lock,lib/**/*.rb,lib/*.rb}' -- rackup config.ru"
14
+ end
data/config.ru CHANGED
@@ -1,4 +1,3 @@
1
- # bundle exec rerun -- rackup config.ru
2
1
  require "./lib/mihari"
3
2
 
4
3
  # set rack env as development
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "dry-initializer"
4
- require "parallel"
5
-
6
3
  module Mihari
7
4
  module Analyzers
8
5
  class Base
@@ -10,6 +7,7 @@ module Mihari
10
7
 
11
8
  include Mixins::AutonomousSystem
12
9
  include Mixins::Configurable
10
+ include Mixins::Database
13
11
  include Mixins::Retriable
14
12
 
15
13
  attr_accessor :ignore_old_artifacts, :ignore_threshold
@@ -52,10 +50,12 @@ module Mihari
52
50
  # @return [nil]
53
51
  #
54
52
  def run
55
- set_enriched_artifacts
53
+ with_db_connection do
54
+ set_enriched_artifacts
56
55
 
57
- Parallel.each(valid_emitters) do |emitter|
58
- run_emitter emitter
56
+ Parallel.each(valid_emitters) do |emitter|
57
+ run_emitter emitter
58
+ end
59
59
  end
60
60
  end
61
61
 
@@ -6,9 +6,6 @@ module Mihari
6
6
  module Analyzers
7
7
  class BinaryEdge < Base
8
8
  param :query
9
- option :title, default: proc { "BinaryEdge search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
12
9
 
13
10
  option :interval, default: proc { 0 }
14
11
 
@@ -19,9 +16,10 @@ module Mihari
19
16
  results.map do |result|
20
17
  events = result["events"] || []
21
18
  events.filter_map do |event|
22
- event.dig "target", "ip"
19
+ data = event.dig("target", "ip")
20
+ data.nil? ? nil : Artifact.new(data: data, source: source, metadata: event)
23
21
  end
24
- end.flatten.compact.uniq
22
+ end.flatten
25
23
  end
26
24
 
27
25
  private
@@ -6,9 +6,6 @@ module Mihari
6
6
  module Analyzers
7
7
  class Censys < Base
8
8
  param :query
9
- option :title, default: proc { "Censys search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
12
9
 
13
10
  option :interval, default: proc { 0 }
14
11
 
@@ -77,6 +74,7 @@ module Mihari
77
74
  Artifact.new(
78
75
  data: hit.ip,
79
76
  source: source,
77
+ metadata: hit.metadata,
80
78
  autonomous_system: as,
81
79
  geolocation: geolocation
82
80
  )
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "CIRCL passive DNS/SSL search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -6,15 +6,17 @@ module Mihari
6
6
  module Analyzers
7
7
  class Crtsh < Base
8
8
  param :query
9
- option :title, default: proc { "crt.sh search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
9
+
12
10
  option :exclude_expired, default: proc { true }
13
11
 
14
12
  def artifacts
15
13
  results = search
16
- name_values = results.filter_map { |result| result["name_value"] }
17
- name_values.map(&:lines).flatten.uniq.map(&:chomp)
14
+ results.map do |result|
15
+ values = result["name_value"].to_s.lines.map(&:chomp)
16
+ values.map do |value|
17
+ Artifact.new(data: value, source: source, metadata: result)
18
+ end
19
+ end.flatten
18
20
  end
19
21
 
20
22
  private
@@ -6,8 +6,7 @@ module Mihari
6
6
  module Analyzers
7
7
  class DNPedia < Base
8
8
  param :query
9
- option :title, default: proc { "DNPedia domain search" }
10
- option :description, default: proc { "query = #{query}" }
9
+
11
10
  option :tags, default: proc { [] }
12
11
 
13
12
  def artifacts
@@ -23,13 +22,14 @@ module Mihari
23
22
  #
24
23
  # Search
25
24
  #
26
- # @return [Array<String>]
25
+ # @return [Array<Mihari::Artifact>]
27
26
  #
28
27
  def search
29
28
  res = api.search(query)
30
29
  rows = res["rows"] || []
31
30
  rows.map do |row|
32
- [row["name"], row["zoneid"]].join(".")
31
+ data = [row["name"], row["zoneid"]].join(".")
32
+ Artifact.new(data: data, source: source, metadata: row)
33
33
  end
34
34
  end
35
35
  end
@@ -1,8 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dnstwister"
4
- require "resolv"
5
- require "parallel"
6
4
 
7
5
  module Mihari
8
6
  module Analyzers
@@ -10,8 +8,7 @@ module Mihari
10
8
  include Mixins::Refang
11
9
 
12
10
  param :query
13
- option :title, default: proc { "dnstwister domain search" }
14
- option :description, default: proc { "query = #{query}" }
11
+
15
12
  option :tags, default: proc { [] }
16
13
 
17
14
  attr_reader :type
@@ -7,9 +7,6 @@ module Mihari
7
7
  module Analyzers
8
8
  class Feed < Base
9
9
  param :query
10
- option :title, default: proc { "Feed" }
11
- option :description, default: proc { "query = #{query}" }
12
- option :tags, default: proc { [] }
13
10
 
14
11
  option :http_request_method, default: proc { "GET" }
15
12
  option :http_request_headers, default: proc { {} }
@@ -6,9 +6,6 @@ module Mihari
6
6
  module Analyzers
7
7
  class GreyNoise < Base
8
8
  param :query
9
- option :title, default: proc { "GreyNoise search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
12
9
 
13
10
  def artifacts
14
11
  res = Structs::GreyNoise::Response.from_dynamic!(search)
@@ -56,6 +53,7 @@ module Mihari
56
53
  Artifact.new(
57
54
  data: datum.ip,
58
55
  source: source,
56
+ metadata: datum.metadata_,
59
57
  autonomous_system: as,
60
58
  geolocation: geolocation
61
59
  )
@@ -7,9 +7,6 @@ module Mihari
7
7
  module Analyzers
8
8
  class Onyphe < Base
9
9
  param :query
10
- option :title, default: proc { "Onyphe search" }
11
- option :description, default: proc { "query = #{query}" }
12
- option :tags, default: proc { [] }
13
10
 
14
11
  option :interval, default: proc { 0 }
15
12
 
@@ -89,6 +86,7 @@ module Mihari
89
86
  Artifact.new(
90
87
  data: result.ip,
91
88
  source: source,
89
+ metadata: result.metadata,
92
90
  autonomous_system: as,
93
91
  geolocation: geolocation
94
92
  )
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "OTX search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "PassiveTotal search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -75,27 +72,29 @@ module Mihari
75
72
  #
76
73
  # Reverse whois search
77
74
  #
78
- # @return [Array<String>]
75
+ # @return [Array<Mihari::Artifact>]
79
76
  #
80
77
  def reverse_whois_search
81
78
  res = api.whois.search(query: query, field: "email")
82
79
  results = res["results"] || []
83
80
  results.map do |result|
84
- result["domain"]
85
- end.flatten.compact.uniq
81
+ data = result["domain"]
82
+ Artifact.new(data: data, source: source, metadata: result)
83
+ end.flatten
86
84
  end
87
85
 
88
86
  #
89
87
  # Passive SSL search
90
88
  #
91
- # @return [Array<String>]
89
+ # @return [Array<Mihari::Artifact>]
92
90
  #
93
91
  def ssl_search
94
92
  res = api.ssl.history(query)
95
93
  results = res["results"] || []
96
94
  results.map do |result|
97
- result["ipAddresses"]
98
- end.flatten.compact.uniq
95
+ data = result["ipAddresses"]
96
+ Artifact.new(data: data, source: source, metadata: result)
97
+ end.flatten
99
98
  end
100
99
  end
101
100
  end
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "Pulsedive search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -47,7 +44,7 @@ module Mihari
47
44
  #
48
45
  # Search
49
46
  #
50
- # @return [Array<String>]
47
+ # @return [Array<Mihari::Artifact>]
51
48
  #
52
49
  def search
53
50
  raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
@@ -57,7 +54,12 @@ module Mihari
57
54
 
58
55
  properties = api.indicator.get_properties_by_id(iid)
59
56
  (properties["dns"] || []).filter_map do |property|
60
- property["value"] if ["A", "PTR"].include?(property["name"])
57
+ if ["A", "PTR"].include?(property["name"])
58
+ nil
59
+ else
60
+ data = property["value"]
61
+ Artifact.new(data: data, source: source, metadata: property)
62
+ end
61
63
  end
62
64
  end
63
65
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "uuidtools"
4
-
5
3
  module Mihari
6
4
  module Analyzers
7
5
  ANALYZER_TO_CLASS = {
@@ -31,13 +29,14 @@ module Mihari
31
29
  }.freeze
32
30
 
33
31
  class Rule < Base
34
- include Mihari::Mixins::DisallowedDataValue
32
+ include Mixins::DisallowedDataValue
33
+ include Mixins::Rule
35
34
 
36
35
  option :title
37
36
  option :description
38
37
  option :queries
39
38
 
40
- option :id, default: proc {}
39
+ option :id, default: proc { "" }
41
40
  option :tags, default: proc { [] }
42
41
  option :allowed_data_types, default: proc { ALLOWED_DATA_TYPES }
43
42
  option :disallowed_data_values, default: proc { [] }
@@ -47,7 +46,7 @@ module Mihari
47
46
  def initialize(**kwargs)
48
47
  super(**kwargs)
49
48
 
50
- @source = id || UUIDTools::UUID.md5_create(UUIDTools::UUID_URL_NAMESPACE, title + description).to_s
49
+ @source = id
51
50
 
52
51
  validate_analyzer_configurations
53
52
  end
@@ -69,7 +68,7 @@ module Mihari
69
68
  # set interval in the top level
70
69
  options = params[:options] || {}
71
70
  interval = options[:interval]
72
- params[:interval] = interval
71
+ params[:interval] = interval if interval
73
72
 
74
73
  analyzer = klass.new(query, **params)
75
74
 
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "SecurityTrails search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -47,7 +44,7 @@ module Mihari
47
44
  #
48
45
  # IP/domain/mail search
49
46
  #
50
- # @return [Array<String>]
47
+ # @return [Array<String>, Array<Mihari::Artifact>]
51
48
  #
52
49
  def search
53
50
  case type
@@ -78,12 +75,15 @@ module Mihari
78
75
  #
79
76
  # IP search
80
77
  #
81
- # @return [Array<String>]
78
+ # @return [Array<Mihari::Artifact>]
82
79
  #
83
80
  def ip_search
84
81
  result = api.domains.search(filter: { ipv4: query })
85
82
  records = result["records"] || []
86
- records.filter_map { |record| record["hostname"] }.uniq
83
+ records.filter_map do |record|
84
+ data = record["hostname"]
85
+ Artifact.new(data: data, source: source, metadata: record)
86
+ end
87
87
  end
88
88
 
89
89
  #
@@ -94,7 +94,10 @@ module Mihari
94
94
  def mail_search
95
95
  result = api.domains.search(filter: { whois_email: query })
96
96
  records = result["records"] || []
97
- records.filter_map { |record| record["hostname"] }.uniq
97
+ records.filter_map do |record|
98
+ data = record["hostname"]
99
+ Artifact.new(data: data, source: source, metadata: record)
100
+ end
98
101
  end
99
102
  end
100
103
  end
@@ -6,9 +6,6 @@ module Mihari
6
6
  module Analyzers
7
7
  class Shodan < Base
8
8
  param :query
9
- option :title, default: proc { "Shodan search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
12
9
 
13
10
  option :interval, default: proc { 0 }
14
11
 
@@ -19,8 +16,8 @@ module Mihari
19
16
  results = results.map { |result| Structs::Shodan::Result.from_dynamic!(result) }
20
17
  results.map do |result|
21
18
  matches = result.matches || []
22
- matches.map { |match| build_artifact match }
23
- end.flatten.compact.uniq(&:data)
19
+ matches.map { |match| build_artifact(match, matches) }
20
+ end.flatten.uniq(&:data)
24
21
  end
25
22
 
26
23
  private
@@ -76,14 +73,27 @@ module Mihari
76
73
  responses
77
74
  end
78
75
 
76
+ #
77
+ # Collect metadata from matches
78
+ #
79
+ # @param [Array<Structs::Shodan::Match>] matches
80
+ # @param [String] ip
81
+ #
82
+ # @return [Array<Hash>]
83
+ #
84
+ def collect_metadata_by_ip(matches, ip)
85
+ matches.select { |match| match.ip_str == ip }.map(&:metadata)
86
+ end
87
+
79
88
  #
80
89
  # Build an artifact from a Shodan search API response
81
90
  #
82
91
  # @param [Structs::Shodan::Match] match
92
+ # @param [Array<Structs::Shodan::Match>] matches
83
93
  #
84
94
  # @return [Artifact]
85
95
  #
86
- def build_artifact(match)
96
+ def build_artifact(match, matches)
87
97
  as = nil
88
98
  as = AutonomousSystem.new(asn: normalize_asn(match.asn)) unless match.asn.nil?
89
99
 
@@ -95,9 +105,12 @@ module Mihari
95
105
  )
96
106
  end
97
107
 
108
+ metadata = collect_metadata_by_ip(matches, match.ip_str)
109
+
98
110
  Artifact.new(
99
111
  data: match.ip_str,
100
112
  source: source,
113
+ metadata: metadata,
101
114
  autonomous_system: as,
102
115
  geolocation: geolocation
103
116
  )
@@ -1,16 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "spyse"
4
- require "json"
5
4
 
6
5
  module Mihari
7
6
  module Analyzers
8
7
  class Spyse < Base
9
8
  param :query
10
- option :title, default: proc { "Spyse search" }
11
- option :description, default: proc { "query = #{query}" }
9
+
12
10
  option :type, default: proc { "domain" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  def artifacts
16
13
  search || []
@@ -42,33 +39,35 @@ module Mihari
42
39
  #
43
40
  # Domain search
44
41
  #
45
- # @return [Array<String>]
42
+ # @return [Array<Mihari::Artifact>]
46
43
  #
47
44
  def domain_search
48
45
  res = api.domain.search(search_params, limit: 100)
49
46
  items = res.dig("data", "items") || []
50
47
  items.map do |item|
51
- item["name"]
52
- end.uniq.compact
48
+ data = item["name"]
49
+ Artifact.new(data: data, source: source, metadata: item)
50
+ end
53
51
  end
54
52
 
55
53
  #
56
54
  # IP search
57
55
  #
58
- # @return [Array<String>]
56
+ # @return [Array<Mihari::Artifact>]
59
57
  #
60
58
  def ip_search
61
59
  res = api.ip.search(search_params, limit: 100)
62
60
  items = res.dig("data", "items") || []
63
61
  items.map do |item|
64
- item["ip"]
65
- end.uniq.compact
62
+ data = item["ip"]
63
+ Artifact.new(data: data, source: source, metadata: item)
64
+ end
66
65
  end
67
66
 
68
67
  #
69
68
  # IP/domain search
70
69
  #
71
- # @return [Array<String>]
70
+ # @return [Array<Mihari::Artifact>]
72
71
  #
73
72
  def search
74
73
  case type
@@ -6,9 +6,7 @@ module Mihari
6
6
  module Analyzers
7
7
  class Urlscan < Base
8
8
  param :query
9
- option :title, default: proc { "urlscan search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
9
+
12
10
  option :allowed_data_types, default: proc { SUPPORTED_DATA_TYPES }
13
11
 
14
12
  option :interval, default: proc { 0 }
@@ -29,9 +27,10 @@ module Mihari
29
27
  allowed_data_types.map do |type|
30
28
  results.filter_map do |result|
31
29
  page = result.page
32
- page.send(type.to_sym)
33
- end.uniq
34
- end.flatten.compact
30
+ data = page.send(type.to_sym)
31
+ data.nil? ? nil : Artifact.new(data: data, source: source, metadata: result)
32
+ end
33
+ end.flatten
35
34
  end
36
35
 
37
36
  private
@@ -8,9 +8,6 @@ module Mihari
8
8
  include Mixins::Refang
9
9
 
10
10
  param :query
11
- option :title, default: proc { "VirusTotal search" }
12
- option :description, default: proc { "query = #{query}" }
13
- option :tags, default: proc { [] }
14
11
 
15
12
  attr_reader :type
16
13
 
@@ -47,7 +44,7 @@ module Mihari
47
44
  #
48
45
  # Search
49
46
  #
50
- # @return [Array<String>]
47
+ # @return [Array<Mihari::Artifact>]
51
48
  #
52
49
  def search
53
50
  case type
@@ -63,28 +60,30 @@ module Mihari
63
60
  #
64
61
  # Domain search
65
62
  #
66
- # @return [Array<String>]
63
+ # @return [Array<Mihari::Artifact>]
67
64
  #
68
65
  def domain_search
69
66
  res = api.domain.resolutions(query)
70
67
 
71
68
  data = res["data"] || []
72
69
  data.filter_map do |item|
73
- item.dig("attributes", "ip_address")
74
- end.uniq
70
+ data = item.dig("attributes", "ip_address")
71
+ data.nil? ? nil : Artifact.new(data: data, source: source, metadata: item)
72
+ end
75
73
  end
76
74
 
77
75
  #
78
76
  # IP search
79
77
  #
80
- # @return [Array<String>]
78
+ # @return [Array<Mihari::Artifact>]
81
79
  #
82
80
  def ip_search
83
81
  res = api.ip_address.resolutions(query)
84
82
 
85
83
  data = res["data"] || []
86
84
  data.filter_map do |item|
87
- item.dig("attributes", "host_name")
85
+ data = item.dig("attributes", "host_name")
86
+ Artifact.new(data: data, source: source, metadata: item)
88
87
  end.uniq
89
88
  end
90
89
  end
@@ -6,9 +6,6 @@ module Mihari
6
6
  module Analyzers
7
7
  class VirusTotalIntelligence < Base
8
8
  param :query
9
- option :title, default: proc { "VirusTotal Intelligence search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
12
9
 
13
10
  option :interval, default: proc { 0 }
14
11
 
@@ -21,8 +18,10 @@ module Mihari
21
18
  def artifacts
22
19
  responses = search_witgh_cursor
23
20
  responses.map do |response|
24
- response.data.map(&:value)
25
- end.flatten.compact.uniq
21
+ response.data.map do |datum|
22
+ Artifact.new(data: datum.value, source: source, metadata: datum.metadata)
23
+ end
24
+ end.flatten
26
25
  end
27
26
 
28
27
  private
@@ -6,9 +6,7 @@ module Mihari
6
6
  module Analyzers
7
7
  class ZoomEye < Base
8
8
  param :query
9
- option :title, default: proc { "ZoomEye search" }
10
- option :description, default: proc { "query = #{query}" }
11
- option :tags, default: proc { [] }
9
+
12
10
  option :type, default: proc { "host" }
13
11
 
14
12
  option :interval, default: proc { 0 }
@@ -50,13 +48,14 @@ module Mihari
50
48
  #
51
49
  # @param [Array<Hash>] responses
52
50
  #
53
- # @return [Array<String>]
51
+ # @return [Array<Mihari::Artifact>]
54
52
  #
55
53
  def convert_responses(responses)
56
54
  responses.map do |res|
57
55
  matches = res["matches"] || []
58
56
  matches.map do |match|
59
- match["ip"]
57
+ data = match["ip"]
58
+ Artifact.new(data: data, source: source, metadata: match)
60
59
  end
61
60
  end.flatten.compact.uniq
62
61
  end