mihari 2.0.0 → 2.3.1

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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/bug_report.md +43 -0
  3. data/.github/ISSUE_TEMPLATE/feature_request.md +15 -0
  4. data/.rubocop.yml +6 -0
  5. data/.standard.yml +4 -0
  6. data/README.md +11 -1
  7. data/bin/console +1 -0
  8. data/docker/Dockerfile +3 -2
  9. data/examples/ipinfo_hosted_domains.rb +1 -1
  10. data/images/{eyecatch.png → overview.png} +0 -0
  11. data/images/tines.png +0 -0
  12. data/images/web_alerts.png +0 -0
  13. data/images/web_config.png +0 -0
  14. data/lib/mihari/analyzers/base.rb +11 -2
  15. data/lib/mihari/analyzers/circl.rb +3 -3
  16. data/lib/mihari/analyzers/onyphe.rb +2 -2
  17. data/lib/mihari/analyzers/securitytrails.rb +2 -2
  18. data/lib/mihari/analyzers/urlscan.rb +1 -6
  19. data/lib/mihari/analyzers/zoomeye.rb +2 -2
  20. data/lib/mihari/cli.rb +72 -289
  21. data/lib/mihari/commands/binaryedge.rb +21 -0
  22. data/lib/mihari/commands/censys.rb +22 -0
  23. data/lib/mihari/commands/circl.rb +21 -0
  24. data/lib/mihari/commands/config.rb +27 -0
  25. data/lib/mihari/commands/crtsh.rb +22 -0
  26. data/lib/mihari/commands/dnpedia.rb +21 -0
  27. data/lib/mihari/commands/dnstwister.rb +21 -0
  28. data/lib/mihari/commands/free_text.rb +21 -0
  29. data/lib/mihari/commands/http_hash.rb +25 -0
  30. data/lib/mihari/commands/json.rb +42 -0
  31. data/lib/mihari/commands/onyphe.rb +21 -0
  32. data/lib/mihari/commands/otx.rb +21 -0
  33. data/lib/mihari/commands/passive_dns.rb +21 -0
  34. data/lib/mihari/commands/passive_ssl.rb +21 -0
  35. data/lib/mihari/commands/passivetotal.rb +21 -0
  36. data/lib/mihari/commands/pulsedive.rb +21 -0
  37. data/lib/mihari/commands/reverse_whois.rb +21 -0
  38. data/lib/mihari/commands/securitytrails.rb +22 -0
  39. data/lib/mihari/commands/securitytrails_domain_feed.rb +23 -0
  40. data/lib/mihari/commands/shodan.rb +21 -0
  41. data/lib/mihari/commands/spyse.rb +22 -0
  42. data/lib/mihari/commands/ssh_fingerprint.rb +21 -0
  43. data/lib/mihari/commands/urlscan.rb +23 -0
  44. data/lib/mihari/commands/virustotal.rb +21 -0
  45. data/lib/mihari/commands/web.rb +22 -0
  46. data/lib/mihari/commands/zoomeye.rb +22 -0
  47. data/lib/mihari/config.rb +14 -3
  48. data/lib/mihari/configurable.rb +1 -1
  49. data/lib/mihari/emitters/slack.rb +4 -4
  50. data/lib/mihari/emitters/the_hive.rb +1 -1
  51. data/lib/mihari/models/alert.rb +5 -5
  52. data/lib/mihari/models/artifact.rb +13 -2
  53. data/lib/mihari/notifiers/exception_notifier.rb +4 -4
  54. data/lib/mihari/status.rb +2 -10
  55. data/lib/mihari/version.rb +1 -1
  56. data/lib/mihari/web/app.rb +25 -100
  57. data/lib/mihari/web/controllers/alerts_controller.rb +75 -0
  58. data/lib/mihari/web/controllers/artifacts_controller.rb +24 -0
  59. data/lib/mihari/web/controllers/base_controller.rb +22 -0
  60. data/lib/mihari/web/controllers/command_controller.rb +26 -0
  61. data/lib/mihari/web/controllers/config_controller.rb +13 -0
  62. data/lib/mihari/web/controllers/sources_controller.rb +12 -0
  63. data/lib/mihari/web/controllers/tags_controller.rb +28 -0
  64. data/lib/mihari/web/helpers/json.rb +53 -0
  65. data/lib/mihari/web/public/index.html +2 -2
  66. data/lib/mihari/web/public/redoc-static.html +519 -0
  67. data/lib/mihari/web/public/static/js/{app.58b32d15.js → app.cccddb2b.js} +4 -4
  68. data/lib/mihari/web/public/static/js/app.cccddb2b.js.map +1 -0
  69. data/mihari.gemspec +9 -3
  70. metadata +146 -23
  71. data/lib/mihari/web/public/static/js/app.58b32d15.js.map +0 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3cf190c27b1be3c0eeb690354361c3bb3295beb8609661a861fcc31e227cb7f0
4
- data.tar.gz: e989209e3668e9342803ed1b91c9e668bcf0519e659f9fb88687cc38460818cb
3
+ metadata.gz: ea3de689646f7be03616ac03315aeca9afb34ba16737ad0f706b508374bdd214
4
+ data.tar.gz: cbd5e02b4e8466c195e8311a4d50668763a3d8b40aefd0ab4a7478979b4725c0
5
5
  SHA512:
6
- metadata.gz: 0a40de96264eaf211240b0020aa3494e6a64bddf8e2aa74461e91122fff29d72a5ab66032e226fa80c19c984fcb7a3e37d1e78aa0da317f6fd1445945b93d200
7
- data.tar.gz: b0b55e75e84037cf7761fd28128504d45fc049de01d0f4db65184b2e8ff0e9bc3948e06b8165e90c19244fb1c8b8af33efa77d260bb323d4084d85e21f72245f
6
+ metadata.gz: 388d1f90bd35a7819418d230703b6d20fe9b8118d64cbd90fea3802b1c80d04337ee8507d26bc4de57e13867f51593be820416eedea52984392ef9e203783707
7
+ data.tar.gz: f3c48426a7bc6c4334870d9de1fc57085094f783e8e1c6aca3ac043b9f42ff738b79457bb9cf93edeecb8d6b9976c3fe4cd5e70a0bdb306c71f767fa20c333c8
@@ -0,0 +1,43 @@
1
+ ---
2
+ name: Bug report
3
+ about: Create a bug report to help us improve
4
+ title: "[BUG]"
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ <!--
11
+ Thank you for taking the time to report a bug.
12
+ Please make sure there is no existing issue about this kind of bug.
13
+ -->
14
+
15
+ ### **Describe the bug**
16
+
17
+ A clear and concise description of what the bug is.
18
+
19
+ ### **Steps to reproduce**
20
+
21
+ - ...
22
+
23
+ ### **Expected behavior**
24
+
25
+ A clear and concise description of what you expected to happen.
26
+
27
+ ### **Actual behavior**
28
+
29
+ A clear and concise description of what actually happened.
30
+
31
+ ### **Screenshots**
32
+
33
+ Add screenshots to help explain your problem.
34
+
35
+ ### **System Information:**
36
+
37
+ - OS: [e.g. Windows10]
38
+ - Ruby version: [e.g. 3.0]
39
+ - Mihari version: [e.g. 2.0.0]
40
+
41
+ ### **Additional context**
42
+
43
+ Add any other context about the problem here.
@@ -0,0 +1,15 @@
1
+ ---
2
+ name: Feature request
3
+ about: Suggest a new Feature for Mihari
4
+ title: "[Feature Request]"
5
+ labels: enhancement
6
+ assignees: ''
7
+
8
+ ---
9
+ <!--
10
+
11
+ 1. Make sure your requested feature makes sense for Mihari.
12
+
13
+ 2. If you want to suggest a new integration of a service, please provide detailed information of it. (e.g. API docs)
14
+
15
+ -->
data/.rubocop.yml CHANGED
@@ -4,6 +4,9 @@
4
4
  require:
5
5
  - rubocop-performance
6
6
 
7
+ AllCops:
8
+ NewCops: enable
9
+
7
10
  Style/Alias:
8
11
  Enabled: false
9
12
  StyleGuide: https://relaxed.ruby.style/#stylealias
@@ -151,5 +154,8 @@ Lint/AssignmentInCondition:
151
154
  Layout/LineLength:
152
155
  Enabled: false
153
156
 
157
+ Style/StringLiteralsInInterpolation:
158
+ Enabled: false
159
+
154
160
  Metrics:
155
161
  Enabled: false
data/.standard.yml ADDED
@@ -0,0 +1,4 @@
1
+ ignore:
2
+ - "**/*":
3
+ - Layout/SpaceInsideHashLiteralBraces
4
+ - Style/RescueStandardError
data/README.md CHANGED
@@ -8,10 +8,14 @@
8
8
 
9
9
  ![img](https://github.com/ninoseki/mihari/raw/master/images/logo.png)
10
10
 
11
+ [![](images/tines.png)](https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki)
12
+
11
13
  Mihari is a framework for continuous OSINT based threat hunting.
12
14
 
13
15
  ## How it works
14
16
 
17
+ ![img](https://github.com/ninoseki/mihari/raw/master/images/overview.png)
18
+
15
19
  - Mihari makes a query against Shodan, Censys, VirusTotal, SecurityTrails, etc. and extracts artifacts (IP addresses, domains, URLs or hashes).
16
20
  - Mihari checks whether a DB (SQLite3, PostgreSQL or MySQL) contains the artifacts or not.
17
21
  - If it doesn't contain the artifacts:
@@ -50,10 +54,16 @@ See [Usage](https://github.com/ninoseki/mihari/wiki/Usage) for more information.
50
54
 
51
55
  - [Requirements & Installation](https://github.com/ninoseki/mihari/wiki/Requirements-&-Installation)
52
56
  - [Usage](https://github.com/ninoseki/mihari/wiki/Usage)
57
+ - [Built-in Web App](https://github.com/ninoseki/mihari/wiki/Built-in-Web-App)
53
58
  - [Configuration](https://github.com/ninoseki/mihari/wiki/Configuration)
54
- - [Custom script](https://github.com/ninoseki/mihari/wiki/Custom-script)
59
+ - [Custom Script](https://github.com/ninoseki/mihari/wiki/Custom-Script)
55
60
  - [Docker](https://github.com/ninoseki/mihari/wiki/Docker)
61
+ - [GitHub Actions](https://github.com/ninoseki/mihari/wiki/GitHub-Actions)
56
62
 
57
63
  ## License
58
64
 
59
65
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
66
+
67
+ ## Acknowledgement
68
+
69
+ Mihari is proudly supported by [Tines.io](https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki), The SOAR Platform for Enterprise Security Teams.
data/bin/console CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  require "bundler/setup"
4
5
  require "mihari"
data/docker/Dockerfile CHANGED
@@ -1,6 +1,7 @@
1
- FROM ruby:3.0.0-alpine3.13
1
+ FROM ruby:3.0.1-alpine3.13
2
2
 
3
- RUN apk --no-cache add git build-base ruby-dev sqlite-dev postgresql-dev \
3
+ RUN apk --no-cache add git build-base ruby-dev sqlite-dev postgresql-dev mysql-client mysql-dev \
4
+ && gem install pg mysql2 \
4
5
  && cd /tmp/ \
5
6
  && git clone https://github.com/ninoseki/mihari.git \
6
7
  && cd mihari \
@@ -34,7 +34,7 @@ module Mihari
34
34
  uri = URI("#{IPINFO_API_ENDPOINT}/domains/#{ip}?token=#{token}")
35
35
  res = uri.read
36
36
  json = JSON.parse(res)
37
- json.dig("domains") || []
37
+ json["domains"] || []
38
38
  end
39
39
  end
40
40
  end
File without changes
data/images/tines.png ADDED
Binary file
Binary file
Binary file
@@ -8,6 +8,13 @@ module Mihari
8
8
  include Configurable
9
9
  include Retriable
10
10
 
11
+ attr_accessor :ignore_old_artifacts, :ignore_threshold
12
+
13
+ def initialize
14
+ @ignore_old_artifacts = false
15
+ @ignore_threshold = 0
16
+ end
17
+
11
18
  # @return [Array<String>, Array<Mihari::Artifact>]
12
19
  def artifacts
13
20
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
@@ -42,7 +49,7 @@ module Mihari
42
49
 
43
50
  def run_emitter(emitter)
44
51
  emitter.run(title: title, description: description, artifacts: unique_artifacts, source: source, tags: tags)
45
- rescue => e
52
+ rescue StandardError => e
46
53
  puts "Emission by #{emitter.class} is failed: #{e}"
47
54
  end
48
55
 
@@ -61,7 +68,9 @@ module Mihari
61
68
 
62
69
  # @return [Array<Mihari::Artifact>]
63
70
  def unique_artifacts
64
- @unique_artifacts ||= normalized_artifacts.select(&:unique?)
71
+ @unique_artifacts ||= normalized_artifacts.select do |artifact|
72
+ artifact.unique?(ignore_old_artifacts: ignore_old_artifacts, ignore_threshold: ignore_threshold)
73
+ end
65
74
  end
66
75
 
67
76
  def set_unique_artifacts
@@ -46,14 +46,14 @@ module Mihari
46
46
  def passive_dns_lookup
47
47
  results = api.dns.query(@query)
48
48
  results.map do |result|
49
- type = result.dig("rrtype")
50
- type == "A" ? result.dig("rdata") : nil
49
+ type = result["rrtype"]
50
+ type == "A" ? result["rdata"] : nil
51
51
  end.compact.uniq
52
52
  end
53
53
 
54
54
  def passive_ssl_lookup
55
55
  result = api.ssl.cquery(@query)
56
- seen = result.dig("seen") || []
56
+ seen = result["seen"] || []
57
57
  seen.uniq
58
58
  end
59
59
  end
@@ -21,10 +21,10 @@ module Mihari
21
21
  return [] unless results
22
22
 
23
23
  flat_results = results.map do |result|
24
- result.dig("results")
24
+ result["results"]
25
25
  end.flatten.compact
26
26
 
27
- flat_results.map { |result| result.dig("ip") }.compact.uniq
27
+ flat_results.map { |result| result["ip"] }.compact.uniq
28
28
  end
29
29
 
30
30
  private
@@ -58,13 +58,13 @@ module Mihari
58
58
  end
59
59
 
60
60
  def ip_lookup
61
- result = api.domains.search(filter: {ipv4: query})
61
+ result = api.domains.search(filter: { ipv4: query })
62
62
  records = result["records"] || []
63
63
  records.map { |record| record["hostname"] }.compact.uniq
64
64
  end
65
65
 
66
66
  def mail_lookup
67
- result = api.domains.search(filter: {whois_email: query})
67
+ result = api.domains.search(filter: { whois_email: query })
68
68
  records = result["records"] || []
69
69
  records.map { |record| record["hostname"] }.compact.uniq
70
70
  end
@@ -5,16 +5,14 @@ require "urlscan"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class Urlscan < Base
8
- attr_reader :title, :description, :query, :tags, :filter, :target_type, :use_pro, :use_similarity
8
+ attr_reader :title, :description, :query, :tags, :target_type, :use_similarity
9
9
 
10
10
  def initialize(
11
11
  query,
12
12
  description: nil,
13
- filter: nil,
14
13
  tags: [],
15
14
  target_type: "url",
16
15
  title: nil,
17
- use_pro: false,
18
16
  use_similarity: false
19
17
  )
20
18
  super()
@@ -24,9 +22,7 @@ module Mihari
24
22
  @description = description || "query = #{query}"
25
23
  @tags = tags
26
24
 
27
- @filter = filter
28
25
  @target_type = target_type
29
- @use_pro = use_pro
30
26
  @use_similarity = use_similarity
31
27
 
32
28
  raise InvalidInputError, "type should be url, domain or ip." unless valid_target_type?
@@ -54,7 +50,6 @@ module Mihari
54
50
 
55
51
  def search
56
52
  return api.pro.similar(query) if use_similarity
57
- return api.pro.search(query: query, filter: filter, size: 10_000) if use_pro
58
53
 
59
54
  api.search(query, size: 10_000)
60
55
  end
@@ -37,11 +37,11 @@ module Mihari
37
37
  end
38
38
 
39
39
  def config_keys
40
- %w[zoomeye_password zoomeye_username]
40
+ %w[zoomeye_api_key]
41
41
  end
42
42
 
43
43
  def api
44
- @api ||= ::ZoomEye::API.new(username: Mihari.config.zoomeye_username, password: Mihari.config.zoomeye_password)
44
+ @api ||= ::ZoomEye::API.new(api_key: Mihari.config.zoomeye_api_key)
45
45
  end
46
46
 
47
47
  def convert_responses(responses)
data/lib/mihari/cli.rb CHANGED
@@ -1,293 +1,76 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
- require "rack/builder"
5
- require "rack/handler/webrick"
6
3
  require "thor"
7
4
 
5
+ require "mihari/commands/binaryedge"
6
+ require "mihari/commands/censys"
7
+ require "mihari/commands/circl"
8
+ require "mihari/commands/crtsh"
9
+ require "mihari/commands/dnpedia"
10
+ require "mihari/commands/dnstwister"
11
+ require "mihari/commands/onyphe"
12
+ require "mihari/commands/otx"
13
+ require "mihari/commands/passivetotal"
14
+ require "mihari/commands/pulsedive"
15
+ require "mihari/commands/securitytrails_domain_feed"
16
+ require "mihari/commands/securitytrails"
17
+ require "mihari/commands/shodan"
18
+ require "mihari/commands/spyse"
19
+ require "mihari/commands/urlscan"
20
+ require "mihari/commands/virustotal"
21
+ require "mihari/commands/zoomeye"
22
+
23
+ require "mihari/commands/free_text"
24
+ require "mihari/commands/http_hash"
25
+ require "mihari/commands/passive_dns"
26
+ require "mihari/commands/passive_ssl"
27
+ require "mihari/commands/reverse_whois"
28
+ require "mihari/commands/ssh_fingerprint"
29
+
30
+ require "mihari/commands/config"
31
+ require "mihari/commands/json"
32
+ require "mihari/commands/web"
33
+
8
34
  module Mihari
9
35
  class CLI < Thor
10
- class_option :config, type: :string, desc: "path to config file"
11
-
12
- def self.exit_on_failure?
13
- true
14
- end
15
-
16
- desc "censys [QUERY]", "Censys IPv4 search by a query"
17
- method_option :title, type: :string, desc: "title"
18
- method_option :description, type: :string, desc: "description"
19
- method_option :tags, type: :array, desc: "tags"
20
- method_option :type, type: :string, desc: "type to search (ipv4 / websites / certificates)", default: "ipv4"
21
- def censys(query)
22
- with_error_handling do
23
- run_analyzer Analyzers::Censys, query: query, options: options
24
- end
25
- end
26
-
27
- desc "shodan [QUERY]", "Shodan host search by a query"
28
- method_option :title, type: :string, desc: "title"
29
- method_option :description, type: :string, desc: "description"
30
- method_option :tags, type: :array, desc: "tags"
31
- def shodan(query)
32
- with_error_handling do
33
- run_analyzer Analyzers::Shodan, query: query, options: options
34
- end
35
- end
36
-
37
- desc "onyphe [QUERY]", "Onyphe datascan search by a query"
38
- method_option :title, type: :string, desc: "title"
39
- method_option :description, type: :string, desc: "description"
40
- method_option :tags, type: :array, desc: "tags"
41
- def onyphe(query)
42
- with_error_handling do
43
- run_analyzer Analyzers::Onyphe, query: query, options: options
44
- end
45
- end
46
-
47
- desc "urlscan [QUERY]", "urlscan search by a given query"
48
- method_option :title, type: :string, desc: "title"
49
- method_option :description, type: :string, desc: "description"
50
- method_option :tags, type: :array, desc: "tags"
51
- method_option :filter, type: :string, desc: "filter for urlscan pro search"
52
- method_option :target_type, type: :string, default: "url", desc: "target type to fetch from lookup results (target type should be 'url', 'domain' or 'ip')"
53
- method_option :use_pro, type: :boolean, default: false, desc: "use pro search API or not"
54
- method_option :use_similarity, type: :boolean, default: false, desc: "use similarity API or not"
55
- def urlscan(query)
56
- with_error_handling do
57
- run_analyzer Analyzers::Urlscan, query: query, options: options
58
- end
59
- end
60
-
61
- desc "virustotal [IP|DOMAIN]", "VirusTotal resolutions lookup by an ip or domain"
62
- method_option :title, type: :string, desc: "title"
63
- method_option :description, type: :string, desc: "description"
64
- method_option :tags, type: :array, desc: "tags"
65
- def virustotal(indiactor)
66
- with_error_handling do
67
- run_analyzer Analyzers::VirusTotal, query: refang(indiactor), options: options
68
- end
69
- end
70
-
71
- desc "securitytrails [IP|DOMAIN|EMAIL]", "SecurityTrails lookup by an ip, domain or email"
72
- method_option :title, type: :string, desc: "title"
73
- method_option :description, type: :string, desc: "description"
74
- method_option :tags, type: :array, desc: "tags"
75
- def securitytrails(indiactor)
76
- with_error_handling do
77
- run_analyzer Analyzers::SecurityTrails, query: refang(indiactor), options: options
78
- end
79
- end
80
- map "st" => :securitytrails
81
-
82
- desc "securitytrails_domain_feed [REGEXP]", "SecurityTrails new domain feed search by a regexp"
83
- method_option :title, type: :string, desc: "title"
84
- method_option :description, type: :string, desc: "description"
85
- method_option :tags, type: :array, desc: "tags"
86
- method_option :type, type: :string, default: "registered", desc: "A type of domain feed ('all', 'new' or 'registered')"
87
- def securitytrails_domain_feed(regexp)
88
- with_error_handling do
89
- run_analyzer Analyzers::SecurityTrailsDomainFeed, query: regexp, options: options
90
- end
91
- end
92
- map "st_domain_feed" => :securitytrails_domain_feed
93
-
94
- desc "crtsh [QUERY]", "crt.sh search by a query"
95
- method_option :title, type: :string, desc: "title"
96
- method_option :description, type: :string, desc: "description"
97
- method_option :tags, type: :array, desc: "tags"
98
- method_option :exclude_expired, type: :boolean, desc: "exclude expired certificates"
99
- def crtsh(query)
100
- with_error_handling do
101
- run_analyzer Analyzers::Crtsh, query: query, options: options
36
+ class_option :config, type: :string, desc: "Path to the config file"
37
+
38
+ class_option :ignore_old_artifacts, type: :boolean, default: false, desc: "Whether to ignore old artifacts from checking or not. Only affects with analyze commands."
39
+ 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."
40
+
41
+ include Mihari::Commands::BinaryEdge
42
+ include Mihari::Commands::Censys
43
+ include Mihari::Commands::CIRCL
44
+ include Mihari::Commands::Config
45
+ include Mihari::Commands::Crtsh
46
+ include Mihari::Commands::DNPedia
47
+ include Mihari::Commands::DNSTwister
48
+ include Mihari::Commands::FreeText
49
+ include Mihari::Commands::HTTPHash
50
+ include Mihari::Commands::JSON
51
+ include Mihari::Commands::Onyphe
52
+ include Mihari::Commands::OTX
53
+ include Mihari::Commands::PassiveDNS
54
+ include Mihari::Commands::PassiveSSL
55
+ include Mihari::Commands::PassiveTotal
56
+ include Mihari::Commands::Pulsedive
57
+ include Mihari::Commands::ReverseWhois
58
+ include Mihari::Commands::SecurityTrails
59
+ include Mihari::Commands::SecurityTrailsDomainFeed
60
+ include Mihari::Commands::Shodan
61
+ include Mihari::Commands::Spyse
62
+ include Mihari::Commands::SSHFingerprint
63
+ include Mihari::Commands::Urlscan
64
+ include Mihari::Commands::VirusTotal
65
+ include Mihari::Commands::Web
66
+ include Mihari::Commands::ZoomEye
67
+
68
+ class << self
69
+ def exit_on_failure?
70
+ true
102
71
  end
103
72
  end
104
73
 
105
- desc "dnpedia [QUERY]", "DNPedia domain search by a query"
106
- method_option :title, type: :string, desc: "title"
107
- method_option :description, type: :string, desc: "description"
108
- method_option :tags, type: :array, desc: "tags"
109
- def dnpedia(query)
110
- with_error_handling do
111
- run_analyzer Analyzers::DNPedia, query: query, options: options
112
- end
113
- end
114
-
115
- desc "circl [DOMAIN|SHA1]", "CIRCL passive DNS/SSL lookup by a domain or SHA1 certificate fingerprint"
116
- method_option :title, type: :string, desc: "title"
117
- method_option :description, type: :string, desc: "description"
118
- method_option :tags, type: :array, desc: "tags"
119
- def circl(query)
120
- with_error_handling do
121
- run_analyzer Analyzers::CIRCL, query: refang(query), options: options
122
- end
123
- end
124
-
125
- desc "passivetotal [IP|DOMAIN|EMAIL|SHA1]", "PassiveTotal lookup by an ip, domain, email or SHA1 certificate fingerprint"
126
- method_option :title, type: :string, desc: "title"
127
- method_option :description, type: :string, desc: "description"
128
- method_option :tags, type: :array, desc: "tags"
129
- def passivetotal(indicator)
130
- with_error_handling do
131
- run_analyzer Analyzers::PassiveTotal, query: refang(indicator), options: options
132
- end
133
- end
134
-
135
- desc "zoomeye [QUERY]", "ZoomEye search by a query"
136
- method_option :title, type: :string, desc: "title"
137
- method_option :description, type: :string, desc: "description"
138
- method_option :tags, type: :array, desc: "tags"
139
- method_option :type, type: :string, desc: "type to search(host / web)", default: "host"
140
- def zoomeye(query)
141
- with_error_handling do
142
- run_analyzer Analyzers::ZoomEye, query: query, options: options
143
- end
144
- end
145
-
146
- desc "binaryedge [QUERY]", "BinaryEdge host search by a query"
147
- method_option :title, type: :string, desc: "title"
148
- method_option :description, type: :string, desc: "description"
149
- method_option :tags, type: :array, desc: "tags"
150
- def binaryedge(query)
151
- with_error_handling do
152
- run_analyzer Analyzers::BinaryEdge, query: query, options: options
153
- end
154
- end
155
-
156
- desc "pulsedive [IP|DOMAIN]", "Pulsedive lookup by an ip or domain"
157
- method_option :title, type: :string, desc: "title"
158
- method_option :description, type: :string, desc: "description"
159
- method_option :tags, type: :array, desc: "tags"
160
- def pulsedive(indiactor)
161
- with_error_handling do
162
- run_analyzer Analyzers::Pulsedive, query: refang(indiactor), options: options
163
- end
164
- end
165
-
166
- desc "dnstwister [DOMAIN]", "dnstwister lookup by a domain"
167
- method_option :title, type: :string, desc: "title"
168
- method_option :description, type: :string, desc: "description"
169
- method_option :tags, type: :array, desc: "tags"
170
- def dnstwister(domain)
171
- with_error_handling do
172
- run_analyzer Analyzers::DNSTwister, query: refang(domain), options: options
173
- end
174
- end
175
-
176
- desc "otx [IP|DOMAIN]", "OTX lookup by an IP or domain"
177
- method_option :title, type: :string, desc: "title"
178
- method_option :description, type: :string, desc: "description"
179
- method_option :tags, type: :array, desc: "tags"
180
- def otx(domain)
181
- with_error_handling do
182
- run_analyzer Analyzers::OTX, query: refang(domain), options: options
183
- end
184
- end
185
-
186
- desc "spyse [QUERY]", "Spyse search by a query"
187
- method_option :title, type: :string, desc: "title"
188
- method_option :description, type: :string, desc: "description"
189
- method_option :tags, type: :array, desc: "tags"
190
- method_option :type, type: :string, desc: "type to search (ip or domain)", default: "doamin"
191
- def spyse(query)
192
- with_error_handling do
193
- run_analyzer Analyzers::Spyse, query: query, options: options
194
- end
195
- end
196
-
197
- desc "passive_dns [IP|DOMAIN]", "Cross search with passive DNS services by an ip or domain"
198
- method_option :title, type: :string, desc: "title"
199
- method_option :description, type: :string, desc: "description"
200
- method_option :tags, type: :array, desc: "tags"
201
- def passive_dns(query)
202
- with_error_handling do
203
- run_analyzer Analyzers::PassiveDNS, query: refang(query), options: options
204
- end
205
- end
206
-
207
- desc "passive_ssl [SHA1]", "Cross search with passive SSL services by an SHA1 certificate fingerprint"
208
- method_option :title, type: :string, desc: "title"
209
- method_option :description, type: :string, desc: "description"
210
- method_option :tags, type: :array, desc: "tags"
211
- def passive_ssl(query)
212
- with_error_handling do
213
- run_analyzer Analyzers::PassiveSSL, query: query, options: options
214
- end
215
- end
216
-
217
- desc "reverse_whois [EMAIL]", "Cross search with reverse whois services by an email"
218
- method_option :title, type: :string, desc: "title"
219
- method_option :description, type: :string, desc: "description"
220
- method_option :tags, type: :array, desc: "tags"
221
- def reverse_whois(query)
222
- with_error_handling do
223
- run_analyzer Analyzers::ReveseWhois, query: refang(query), options: options
224
- end
225
- end
226
-
227
- desc "http_hash", "Cross search with search engines by a hash of an HTTP response (SHA256, MD5 and MurmurHash3)"
228
- method_option :title, type: :string, desc: "title"
229
- method_option :description, type: :string, desc: "description"
230
- method_option :tags, type: :array, desc: "tags"
231
- method_option :md5, type: :string, desc: "MD5 hash"
232
- method_option :sha256, type: :string, desc: "SHA256 hash"
233
- method_option :mmh3, type: :numeric, desc: "MurmurHash3 hash"
234
- method_option :html, type: :string, desc: "path to an HTML file"
235
- def http_hash
236
- with_error_handling do
237
- run_analyzer Analyzers::HTTPHash, query: nil, options: options
238
- end
239
- end
240
-
241
- desc "free_text [TEXT]", "Cross search with search engines by a free text"
242
- method_option :title, type: :string, desc: "title"
243
- method_option :description, type: :string, desc: "description"
244
- method_option :tags, type: :array, desc: "tags"
245
- def free_text(text)
246
- with_error_handling do
247
- run_analyzer Analyzers::FreeText, query: text, options: options
248
- end
249
- end
250
-
251
- desc "ssh_fingerprint [FINGERPRINT]", "Cross search with search engines by an SSH fingerprint (e.g. dc:14:de:8e:d7:c1:15:43:23:82:25:81:d2:59:e8:c0)"
252
- method_option :title, type: :string, desc: "title"
253
- method_option :description, type: :string, desc: "description"
254
- method_option :tags, type: :array, desc: "tags"
255
- def ssh_fingerprint(fingerprint)
256
- with_error_handling do
257
- run_analyzer Analyzers::SSHFingerprint, query: fingerprint, options: options
258
- end
259
- end
260
-
261
- desc "import_from_json", "Give a JSON input via STDIN"
262
- def import_from_json(input = nil)
263
- with_error_handling do
264
- json = input || $stdin.gets.chomp
265
- raise ArgumentError, "Input not found: please give an input in a JSON format" unless json
266
-
267
- json = parse_as_json(json)
268
- raise ArgumentError, "Invalid input format: an input JSON data should have title, description and artifacts key" unless valid_json?(json)
269
-
270
- title = json["title"]
271
- description = json["description"]
272
- artifacts = json["artifacts"]
273
- tags = json["tags"] || []
274
-
275
- basic = Analyzers::Basic.new(title: title, description: description, artifacts: artifacts, source: "json", tags: tags)
276
- basic.run
277
- end
278
- end
279
-
280
- desc "web", "Launch the web app"
281
- method_option :port, type: :numeric, default: 9292
282
- method_option :host, type: :string, default: "localhost"
283
- def web
284
- port = options["port"].to_i || 9292
285
- host = options["host"] || "localhost"
286
-
287
- load_configuration
288
- Mihari::App.run!(port: port, host: host)
289
- end
290
-
291
74
  no_commands do
292
75
  def with_error_handling
293
76
  yield
@@ -296,12 +79,6 @@ module Mihari
296
79
  notifier.notify e
297
80
  end
298
81
 
299
- def parse_as_json(input)
300
- JSON.parse input
301
- rescue JSON::ParserError => _e
302
- nil
303
- end
304
-
305
82
  # @return [true, false]
306
83
  def valid_json?(json)
307
84
  %w[title description artifacts].all? { |key| json.key? key }
@@ -322,16 +99,22 @@ module Mihari
322
99
  options = normalize_options(options)
323
100
 
324
101
  analyzer = analyzer_class.new(query, **options)
102
+
103
+ analyzer.ignore_old_artifacts = options[:ignore_old_artifacts] || false
104
+ analyzer.ignore_threshold = options[:ignore_threshold] || 0
105
+
325
106
  analyzer.run
326
107
  end
327
108
 
328
109
  def symbolize_hash_keys(hash)
329
- hash.map { |k, v| [k.to_sym, v] }.to_h
110
+ hash.transform_keys(&:to_sym)
330
111
  end
331
112
 
332
113
  def normalize_options(options)
333
114
  # Delete :config because it is not intended to use for running an analyzer
334
- options.delete(:config)
115
+ [:config, :ignore_old_artifacts, :ignore_threshold].each do |ignore_key|
116
+ options.delete(ignore_key)
117
+ end
335
118
  options
336
119
  end
337
120