mihari 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
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