mihari 0.3.0 → 0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '00748afea4184f8ea3595e822be84acf14a97f78d7d91390013c7bc49d3792bc'
4
- data.tar.gz: fb9f40550592648a4c4c9095aadf714efbea3f35de82f26f1fd2648c9c054409
3
+ metadata.gz: afc816e7fe81735a8e8b007fe32884b1188f09b9fa905fe1603584b4a6d6fbfb
4
+ data.tar.gz: c7f689b44a4964e982744d67861c6f93f567e4c01ae0d6c43c6419dba063aa73
5
5
  SHA512:
6
- metadata.gz: bd032efa642aa96133776a8e1483f7e98485dd5c86395b2eec41c9c8806ebf300131c2a2cafa91c9a0027b08136679f4d6926016ad5d0504514bb657f2440de1
7
- data.tar.gz: d77bebb5f059bc55e0a467f3547b5cd3148551d3101037cad0667a1568c8e2cec1f69ea8bee7bf86f71805909ed695073450a19296d39ae0c1c679762a8232f6
6
+ metadata.gz: 3dfe252ff2f2572328a2101fd50a97cb4ff236430d60d5985f861b885f78daef780f93f67975e78be5debe6ee6fab88777bacc2f7e338681becf2251e755513f
7
+ data.tar.gz: 8ec54176b1f19bcb1b7542e376d7a8a87aef0fb076270408bdea60a08bbb6efe787b9824436c39129a61d0e99a953154d31277a9ebb67dd644fcbcaaf91650be
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # mihari
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/mihari.svg)](https://badge.fury.io/rb/mihari)
3
4
  [![Build Status](https://travis-ci.org/ninoseki/mihari.svg?branch=master)](https://travis-ci.org/ninoseki/mihari)
4
5
  [![Coverage Status](https://coveralls.io/repos/github/ninoseki/mihari/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/mihari?branch=master)
5
6
  [![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/mihari/badge)](https://www.codefactor.io/repository/github/ninoseki/mihari)
6
7
 
7
- mihari(`見張り`) is a framework for continuous malicious hosts (C2 / landing page / phishing, etc.) monitoring backended with [TheHive](https://github.com/TheHive-Project/TheHive).
8
+ mihari(`見張り`) is a sidekick tool for [TheHive](https://github.com/TheHive-Project/TheHive) to monitor malicious hosts (C2 / landing page / phishing, etc.) continuously.
8
9
 
9
10
  ## How it works
10
11
 
@@ -33,41 +34,18 @@ gem install mihari
33
34
 
34
35
  ## Basic usage
35
36
 
36
- mihari supports Censys, Shodan and Onyphe by default.
37
+ mihari supports Censys, Shodan, Onyphe, urlscan and VirusTotal by default.
37
38
 
38
39
  ```bash
39
40
  $ mihari
40
41
  Commands:
41
- mihari censys [QUERY] # Censys IPv4 lookup by a given query
42
- mihari help [COMMAND] # Describe available commands or one specific command
43
- mihari import_from_json # Give a JSON input via STDIN
44
- mihari onyphe [QUERY] # Onyphe datascan lookup by a given query
45
- mihari shodan [QUERY] # Shodan host lookup by a given query
46
- mihari urlscan [QUERY] # urlscan lookup by a given query
47
- ```
48
-
49
- ### Censys
50
-
51
- ```bash
52
- mihari censys "YOUR_QUERY"
53
- ```
54
-
55
- ### Shodan
56
-
57
- ```bash
58
- mihari shodan "YOUR QUERY"
59
- ```
60
-
61
- ### Onyphe
62
-
63
- ```bash
64
- mihari onyphe "YOUR QUERY"
65
- ```
66
-
67
- ### urlscan.io
68
-
69
- ```bash
70
- mihari urlscan "YOUR QUERY"
42
+ mihari censys [QUERY] # Censys IPv4 lookup by a given query
43
+ mihari help [COMMAND] # Describe available commands or one specific command
44
+ mihari import_from_json # Give a JSON input via STDIN
45
+ mihari onyphe [QUERY] # Onyphe datascan lookup by a given query
46
+ mihari shodan [QUERY] # Shodan host lookup by a given query
47
+ mihari urlscan [QUERY] # urlscan lookup by a given query
48
+ mihari virustotal [IP|DOMAIN] # VirusTotal resolutions lookup by a given ip or domain
71
49
  ```
72
50
 
73
51
  ### Import from JSON
@@ -108,6 +86,7 @@ All configuration is done via ENV variables.
108
86
  | CENSYS_SECRET | Censys secret | Optional |
109
87
  | SHODAN_API_KEY | Shodan API key | Optional |
110
88
  | ONYPHE_API_KEY | Onyphe API key | Optional |
89
+ | VIRUSTOTAL_API_KEY | VirusTotal API key | Optional |
111
90
 
112
91
  ## How to create a custom analyzer
113
92
 
@@ -30,18 +30,16 @@ module Mihari
30
30
  end
31
31
 
32
32
  def run(reject_exists_ones: true)
33
- unique_artifacts = normalized_artifacts.reject do |artifact|
34
- reject_exists_ones & the_hive.valid? && the_hive.exists?(data: artifact.data, data_type: artifact.data_type)
35
- end
33
+ artifacts = reject_exists_ones ? unique_artifacts : normalized_artifacts
36
34
 
37
- Mihari.notifiers.each do |notifier_class|
38
- notifier = notifier_class.new
39
- next unless notifier.valid?
35
+ Mihari.emitters.each do |emitter_class|
36
+ emitter = emitter_class.new
37
+ next unless emitter.valid?
40
38
 
41
39
  begin
42
- notifier.notify(title: title, description: description, artifacts: unique_artifacts, tags: tags)
40
+ emitter.emit(title: title, description: description, artifacts: artifacts, tags: tags)
43
41
  rescue StandardError => e
44
- puts "Sending notification by #{notifier.class} is failed: #{e}"
42
+ puts "Sending notification by #{emitter.class} is failed: #{e}"
45
43
  end
46
44
  end
47
45
  end
@@ -54,6 +52,13 @@ module Mihari
54
52
  artifact.is_a?(Artifact) ? artifact : Artifact.new(artifact)
55
53
  end.select(&:valid?)
56
54
  end
55
+
56
+ # @return [Array<Mihari::Artifact>]
57
+ def unique_artifacts
58
+ normalized_artifacts.reject do |artifact|
59
+ the_hive.valid? && the_hive.exists?(data: artifact.data, data_type: artifact.data_type)
60
+ end
61
+ end
57
62
  end
58
63
  end
59
64
  end
@@ -28,7 +28,7 @@ module Mihari
28
28
  results = result.dig("results") || []
29
29
  results.map do |match|
30
30
  match.dig "task", "url"
31
- end.compact
31
+ end.compact.uniq
32
32
  end
33
33
 
34
34
  private
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "virustotal"
4
+
5
+ module Mihari
6
+ module Analyzers
7
+ class VirusTotal < Base
8
+ attr_reader :api
9
+ attr_reader :indicator
10
+ attr_reader :type
11
+
12
+ attr_reader :title
13
+ attr_reader :description
14
+ attr_reader :tags
15
+
16
+ def initialize(indicator, tags: [])
17
+ super()
18
+
19
+ @api = ::VirusTotal::API.new
20
+ @indicator = indicator
21
+ @type = TypeChecker.type(indicator)
22
+
23
+ @title = "VirusTotal lookup"
24
+ @description = "indicator = #{indicator}"
25
+ @tags = tags
26
+ end
27
+
28
+ def artifacts
29
+ lookup || []
30
+ end
31
+
32
+ private
33
+
34
+ def valid_type?
35
+ %w(ip domain).include? type
36
+ end
37
+
38
+ def lookup
39
+ case type
40
+ when "domain"
41
+ domain_lookup
42
+ when "ip"
43
+ ip_lookup
44
+ else
45
+ raise ArgumentError, "#{indicator}(type: #{type || 'unknown'}) is not supported." unless valid_type?
46
+ end
47
+ rescue ::VirusTotal::Error => _e
48
+ nil
49
+ end
50
+
51
+ def domain_lookup
52
+ report = api.domain.report(indicator)
53
+ return nil unless report
54
+
55
+ resolutions = report.dig("resolutions") || []
56
+ resolutions.map do |resolution|
57
+ resolution.dig("ip_address")
58
+ end.compact.uniq
59
+ end
60
+
61
+ def ip_lookup
62
+ report = api.ip_address.report(indicator)
63
+ return nil unless report
64
+
65
+ resolutions = report.dig("resolutions") || []
66
+ resolutions.map do |resolution|
67
+ resolution.dig("hostname")
68
+ end.compact.uniq
69
+ end
70
+ end
71
+ end
72
+ end
data/lib/mihari/cli.rb CHANGED
@@ -37,6 +37,14 @@ module Mihari
37
37
  run_analyzer urlscan
38
38
  end
39
39
 
40
+ desc "virustotal [IP|DOMAIN]", "VirusTotal resolutions lookup by a given ip or domain"
41
+ method_option :tags, type: :array, desc: "tags"
42
+ def virustotal(indiactor)
43
+ tags = options.dig("tags") || []
44
+ virustotal = Analyzers::VirusTotal.new(indiactor, tags: tags)
45
+ run_analyzer virustotal
46
+ end
47
+
40
48
  desc "import_from_json", "Give a JSON input via STDIN"
41
49
  def import_from_json(input = nil)
42
50
  json = input || STDIN.gets.chomp
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- module Notifiers
4
+ module Emitters
5
5
  class Base
6
6
  def self.inherited(child)
7
- Mihari.notifiers << child
7
+ Mihari.emitters << child
8
8
  end
9
9
 
10
10
  # @return [true, false]
@@ -12,7 +12,7 @@ module Mihari
12
12
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
13
13
  end
14
14
 
15
- def notify(title:, description:, artifacts:)
15
+ def emit(title:, description:, artifacts:)
16
16
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
17
17
  end
18
18
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "slack/incoming/webhooks"
3
+ require "slack-notifier"
4
4
  require "digest/sha2"
5
5
  require "mem"
6
6
 
7
7
  module Mihari
8
- module Notifiers
8
+ module Emitters
9
9
  class Attachment
10
10
  include Mem
11
11
 
@@ -125,14 +125,14 @@ module Mihari
125
125
  end.flatten
126
126
  end
127
127
 
128
- def notify(title:, description:, artifacts:, tags:)
128
+ def emit(title:, description:, artifacts:, tags:)
129
129
  return if artifacts.empty?
130
130
 
131
131
  attachments = to_attachments(artifacts)
132
132
  tags << ["N/A"] if tags.empty?
133
133
 
134
- slack = ::Slack::Incoming::Webhooks.new(slack_webhook_url, channel: slack_channel)
135
- slack.post("#{title} (desc.: #{description} / tags: #{tags.join(', ')})", attachments: attachments)
134
+ notifier = ::Slack::Notifier.new(slack_webhook_url, channel: slack_channel)
135
+ notifier.post(text: "#{title} (desc.: #{description} / tags: #{tags.join(', ')})", attachments: attachments)
136
136
  end
137
137
  end
138
138
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Mihari
6
+ module Emitters
7
+ class StandardOutput < Base
8
+ def valid?
9
+ true
10
+ end
11
+
12
+ def emit(title:, description:, artifacts:, tags:)
13
+ h = {
14
+ title: title,
15
+ description: description,
16
+ artifacts: artifacts.map(&:data),
17
+ tags: tags
18
+ }
19
+ puts JSON.pretty_generate(h)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- module Notifiers
4
+ module Emitters
5
5
  class TheHive < Base
6
6
  attr_reader :api
7
7
 
@@ -14,17 +14,15 @@ module Mihari
14
14
  api.valid?
15
15
  end
16
16
 
17
- def notify(title:, description:, artifacts:, tags: [])
17
+ def emit(title:, description:, artifacts:, tags: [])
18
18
  return if artifacts.empty?
19
19
 
20
- res = api.create_alert(
20
+ api.create_alert(
21
21
  title: title,
22
22
  description: description,
23
23
  artifacts: artifacts.map(&:to_h),
24
24
  tags: tags
25
25
  )
26
- id = res.dig("id")
27
- puts "A new alret is created. (id: #{id})"
28
26
  end
29
27
  end
30
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
data/lib/mihari.rb CHANGED
@@ -6,10 +6,10 @@ module Mihari
6
6
  class << self
7
7
  include Mem
8
8
 
9
- def notifiers
9
+ def emitters
10
10
  []
11
11
  end
12
- memoize :notifiers
12
+ memoize :emitters
13
13
  end
14
14
  end
15
15
 
@@ -28,9 +28,11 @@ require "mihari/analyzers/censys"
28
28
  require "mihari/analyzers/onyphe"
29
29
  require "mihari/analyzers/shodan"
30
30
  require "mihari/analyzers/urlscan"
31
+ require "mihari/analyzers/virustotal"
31
32
 
32
- require "mihari/notifiers/base"
33
- require "mihari/notifiers/slack"
34
- require "mihari/notifiers/the_hive"
33
+ require "mihari/emitters/base"
34
+ require "mihari/emitters/slack"
35
+ require "mihari/emitters/stdout"
36
+ require "mihari/emitters/the_hive"
35
37
 
36
38
  require "mihari/cli"
data/mihari.gemspec CHANGED
@@ -36,11 +36,12 @@ Gem::Specification.new do |spec|
36
36
  spec.add_dependency "email_address", "~> 0.1"
37
37
  spec.add_dependency "hachi", "~> 0.1"
38
38
  spec.add_dependency "mem", "~> 0.1"
39
- spec.add_dependency "net-ping"
39
+ spec.add_dependency "net-ping", "~> 2.0"
40
40
  spec.add_dependency "onyphe", "~> 0.2"
41
41
  spec.add_dependency "public_suffix", "~> 3.1"
42
42
  spec.add_dependency "shodanx", "~> 0.1"
43
- spec.add_dependency "slack-incoming-webhooks", "~> 0.2"
43
+ spec.add_dependency "slack-notifier", "~> 2.3"
44
44
  spec.add_dependency "thor", "~> 0.19"
45
45
  spec.add_dependency "urlscan", "~> 0.2"
46
+ spec.add_dependency "virustotalx", "~> 0.1"
46
47
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mihari
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Manabu Niseki
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-07-26 00:00:00.000000000 Z
11
+ date: 2019-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -168,16 +168,16 @@ dependencies:
168
168
  name: net-ping
169
169
  requirement: !ruby/object:Gem::Requirement
170
170
  requirements:
171
- - - ">="
171
+ - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: '0'
173
+ version: '2.0'
174
174
  type: :runtime
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
- - - ">="
178
+ - - "~>"
179
179
  - !ruby/object:Gem::Version
180
- version: '0'
180
+ version: '2.0'
181
181
  - !ruby/object:Gem::Dependency
182
182
  name: onyphe
183
183
  requirement: !ruby/object:Gem::Requirement
@@ -221,19 +221,19 @@ dependencies:
221
221
  - !ruby/object:Gem::Version
222
222
  version: '0.1'
223
223
  - !ruby/object:Gem::Dependency
224
- name: slack-incoming-webhooks
224
+ name: slack-notifier
225
225
  requirement: !ruby/object:Gem::Requirement
226
226
  requirements:
227
227
  - - "~>"
228
228
  - !ruby/object:Gem::Version
229
- version: '0.2'
229
+ version: '2.3'
230
230
  type: :runtime
231
231
  prerelease: false
232
232
  version_requirements: !ruby/object:Gem::Requirement
233
233
  requirements:
234
234
  - - "~>"
235
235
  - !ruby/object:Gem::Version
236
- version: '0.2'
236
+ version: '2.3'
237
237
  - !ruby/object:Gem::Dependency
238
238
  name: thor
239
239
  requirement: !ruby/object:Gem::Requirement
@@ -262,6 +262,20 @@ dependencies:
262
262
  - - "~>"
263
263
  - !ruby/object:Gem::Version
264
264
  version: '0.2'
265
+ - !ruby/object:Gem::Dependency
266
+ name: virustotalx
267
+ requirement: !ruby/object:Gem::Requirement
268
+ requirements:
269
+ - - "~>"
270
+ - !ruby/object:Gem::Version
271
+ version: '0.1'
272
+ type: :runtime
273
+ prerelease: false
274
+ version_requirements: !ruby/object:Gem::Requirement
275
+ requirements:
276
+ - - "~>"
277
+ - !ruby/object:Gem::Version
278
+ version: '0.1'
265
279
  description: A framework for continuous malicious hosts monitoring.
266
280
  email:
267
281
  - manabu.niseki@gmail.com
@@ -289,12 +303,14 @@ files:
289
303
  - lib/mihari/analyzers/onyphe.rb
290
304
  - lib/mihari/analyzers/shodan.rb
291
305
  - lib/mihari/analyzers/urlscan.rb
306
+ - lib/mihari/analyzers/virustotal.rb
292
307
  - lib/mihari/artifact.rb
293
308
  - lib/mihari/cli.rb
309
+ - lib/mihari/emitters/base.rb
310
+ - lib/mihari/emitters/slack.rb
311
+ - lib/mihari/emitters/stdout.rb
312
+ - lib/mihari/emitters/the_hive.rb
294
313
  - lib/mihari/errors.rb
295
- - lib/mihari/notifiers/base.rb
296
- - lib/mihari/notifiers/slack.rb
297
- - lib/mihari/notifiers/the_hive.rb
298
314
  - lib/mihari/the_hive.rb
299
315
  - lib/mihari/type_checker.rb
300
316
  - lib/mihari/version.rb