mihari 0.9.0 → 0.9.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a1801d9d520fb6de9a4a27d17145b775b074f87e7e1ff88fcb7c50d5630d19e4
4
- data.tar.gz: 886708647767e1d5d5a7aaaf7bfd9aca8d25bc714f7f2faf72e0c2c2c4b3073f
3
+ metadata.gz: 4175ef15648358026415714167bb1b0567076ad01c20ecf172def0272610ed02
4
+ data.tar.gz: 5b001b4a18c211441a753c5325f355028ae7bf426dbe2c51d676be95f267cf48
5
5
  SHA512:
6
- metadata.gz: 727703b118506f0b310ed1422a757f669748057bd6b4bdb091c638d716209c5ff220088c1396c91924ca275c6fb959e2014f80cb5b9ecbdf72eee23025cf2a7d
7
- data.tar.gz: 150c88bac7b129001b109b1966dc527eda5a7e734438507028e5e2a79f74d1884d6cf98d63a4142a8c45f37f501093a7214187aac88265d9a15a9e6f3d24d03e
6
+ metadata.gz: 351a8537861e52c4d4f5e3eb159d83ee10a699870198736169631c7353726ca6e22cf79c35db827b48b10fef9fc1e6ef236bbb4b07d42dde5452d3316fc051d8
7
+ data.tar.gz: 1467f8fbbce999eef16a72e430a70a5e40faeb9597f2b6f533a202efe27cce7022598768557b8459d1859c33252311ec63fcbe0005a4225dc076173d4a2b712c
data/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/mihari.svg)](https://badge.fury.io/rb/mihari)
4
4
  [![Build Status](https://travis-ci.org/ninoseki/mihari.svg?branch=master)](https://travis-ci.org/ninoseki/mihari)
5
+ [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/ninoseki/mihari)](https://hub.docker.com/r/ninoseki/mihari)
5
6
  [![Coverage Status](https://coveralls.io/repos/github/ninoseki/mihari/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/mihari?branch=master)
6
7
  [![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/mihari/badge)](https://www.codefactor.io/repository/github/ninoseki/mihari)
7
8
 
@@ -26,15 +27,15 @@ You can use mihari without TheHive. But note that mihari depends on TheHive to m
26
27
 
27
28
  - TheHive alert example
28
29
 
29
- ![img](./screenshots/alert.png)
30
+ ![img](https://github.com/ninoseki/mihari/raw/master/screenshots/alert.png)
30
31
 
31
32
  - Slack notification example
32
33
 
33
- ![img](./screenshots/slack.png)
34
+ ![img](https://github.com/ninoseki/mihari/raw/master/screenshots/slack.png)
34
35
 
35
36
  - MISP event example
36
37
 
37
- ![img](./screenshots/misp.png)
38
+ ![img](https://github.com/ninoseki/mihari/raw/master/screenshots/misp.png)
38
39
 
39
40
  ## Installation
40
41
 
@@ -159,6 +160,12 @@ All configuration is done via ENV variables.
159
160
  | SHODAN_API_KEY | Shodan API key | Optional |
160
161
  | VIRUSTOTAL_API_KEY | VirusTotal API key | Optional |
161
162
 
163
+ You can check the configuration status via `status` command.
164
+
165
+ ```bash
166
+ mihari status
167
+ ```
168
+
162
169
  ## How to create a custom script
163
170
 
164
171
  Create a class which extends `Mihari::Analyzers::Base` and implements the following methods.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "bundler/gem_tasks"
2
4
  require "rspec/core/rake_task"
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
@@ -1,10 +1,12 @@
1
- FROM ruby:2.6
2
-
3
- RUN cd /tmp/ \
1
+ FROM ruby:2.6-alpine3.10
2
+ RUN apk --no-cache add git build-base ruby-dev \
3
+ && cd /tmp/ \
4
4
  && git clone https://github.com/ninoseki/mihari.git \
5
5
  && cd mihari \
6
6
  && gem build mihari.gemspec -o mihari.gem \
7
- && gem install mihari.gem
7
+ && gem install mihari.gem \
8
+ && rm -rf /tmp/mihari \
9
+ && apk del --purge git build-base ruby-dev
8
10
 
9
11
  ENTRYPOINT ["mihari"]
10
12
 
@@ -69,10 +69,11 @@ module Mihari
69
69
  @unique_artifacts ||= @the_hive.artifact.find_non_existing_artifacts(uncached_artifacts)
70
70
  end
71
71
 
72
- private
73
-
74
72
  def set_unique_artifacts
75
73
  unique_artifacts
74
+ rescue ArgumentError => _e
75
+ klass = self.class.to_s.split("::").last.to_s
76
+ raise Error, "Please configure #{klass} API settings properly"
76
77
  end
77
78
  end
78
79
  end
@@ -5,7 +5,6 @@ require "censu"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class Censys < Base
8
- attr_reader :api
9
8
  attr_reader :title
10
9
  attr_reader :description
11
10
  attr_reader :query
@@ -17,9 +16,6 @@ module Mihari
17
16
  def initialize(query, title: nil, description: nil, tags: [])
18
17
  super()
19
18
 
20
- raise ArgumentError, "#{CENSYS_ID_KEY} and #{CENSYS_SECRET_KEY} are required" unless valid?
21
-
22
- @api = ::Censys::API.new
23
19
  @query = query
24
20
  @title = title || "Censys lookup"
25
21
  @description = description || "query = #{query}"
@@ -36,6 +32,13 @@ module Mihari
36
32
  ipv4s
37
33
  end
38
34
 
35
+ # @return [true, false]
36
+ def valid?
37
+ censys_id? && censys_secret?
38
+ end
39
+
40
+ private
41
+
39
42
  # @return [true, false]
40
43
  def censys_id?
41
44
  ENV.key? CENSYS_ID_KEY
@@ -46,9 +49,10 @@ module Mihari
46
49
  ENV.key? CENSYS_SECRET_KEY
47
50
  end
48
51
 
49
- # @return [true, false]
50
- def valid?
51
- censys_id? && censys_secret?
52
+ def api
53
+ raise ArgumentError, "#{CENSYS_ID_KEY} and #{CENSYS_SECRET_KEY} are required" unless valid?
54
+
55
+ @api ||= ::Censys::API.new
52
56
  end
53
57
  end
54
58
  end
@@ -5,7 +5,6 @@ require "crtsh"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class Crtsh < Base
8
- attr_reader :api
9
8
  attr_reader :title
10
9
  attr_reader :description
11
10
  attr_reader :query
@@ -14,7 +13,6 @@ module Mihari
14
13
  def initialize(query, title: nil, description: nil, tags: [])
15
14
  super()
16
15
 
17
- @api = ::Crtsh::API.new
18
16
  @query = query
19
17
  @title = title || "crt.sh lookup"
20
18
  @description = description || "query = #{query}"
@@ -28,6 +26,10 @@ module Mihari
28
26
 
29
27
  private
30
28
 
29
+ def api
30
+ @api ||= ::Crtsh::API.new
31
+ end
32
+
31
33
  def search
32
34
  api.search(query)
33
35
  rescue ::Crtsh::Error => _e
@@ -5,7 +5,6 @@ require "onyphe"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class Onyphe < Base
8
- attr_reader :api
9
8
  attr_reader :title
10
9
  attr_reader :description
11
10
  attr_reader :query
@@ -14,7 +13,6 @@ module Mihari
14
13
  def initialize(query, title: nil, description: nil, tags: [])
15
14
  super()
16
15
 
17
- @api = ::Onyphe::API.new
18
16
  @query = query
19
17
  @title = title || "Onyphe lookup"
20
18
  @description = description || "query = #{query}"
@@ -31,10 +29,12 @@ module Mihari
31
29
 
32
30
  private
33
31
 
32
+ def api
33
+ @api ||= ::Onyphe::API.new
34
+ end
35
+
34
36
  def search
35
37
  api.datascan(query)
36
- rescue ::Onyphe::Error => _e
37
- nil
38
38
  end
39
39
  end
40
40
  end
@@ -5,7 +5,6 @@ require "securitytrails"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class SecurityTrails < Base
8
- attr_reader :api
9
8
  attr_reader :indicator
10
9
  attr_reader :type
11
10
 
@@ -16,7 +15,6 @@ module Mihari
16
15
  def initialize(indicator, title: nil, description: nil, tags: [])
17
16
  super()
18
17
 
19
- @api = ::SecurityTrails::API.new
20
18
  @indicator = indicator
21
19
  @type = TypeChecker.type(indicator)
22
20
 
@@ -31,6 +29,10 @@ module Mihari
31
29
 
32
30
  private
33
31
 
32
+ def api
33
+ @api ||= ::SecurityTrails::API.new
34
+ end
35
+
34
36
  def valid_type?
35
37
  %w(ip domain).include? type
36
38
  end
@@ -5,7 +5,6 @@ require "securitytrails"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class SecurityTrailsDomainFeed < Base
8
- attr_reader :api
9
8
  attr_reader :type
10
9
 
11
10
  attr_reader :title
@@ -15,7 +14,6 @@ module Mihari
15
14
  def initialize(regexp, type: "registered", title: nil, description: nil, tags: [])
16
15
  super()
17
16
 
18
- @api = ::SecurityTrails::API.new
19
17
  @_regexp = regexp
20
18
  @type = type
21
19
 
@@ -33,6 +31,10 @@ module Mihari
33
31
 
34
32
  private
35
33
 
34
+ def api
35
+ @api ||= ::SecurityTrails::API.new
36
+ end
37
+
36
38
  def valid_type?
37
39
  %w(all new registered).include? type
38
40
  end
@@ -5,7 +5,6 @@ require "shodan"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class Shodan < Base
8
- attr_reader :api
9
8
  attr_reader :title
10
9
  attr_reader :description
11
10
  attr_reader :query
@@ -14,7 +13,6 @@ module Mihari
14
13
  def initialize(query, title: nil, description: nil, tags: [])
15
14
  super()
16
15
 
17
- @api = ::Shodan::API.new
18
16
  @query = query
19
17
  @title = title || "Shodan lookup"
20
18
  @description = description || "query = #{query}"
@@ -33,6 +31,10 @@ module Mihari
33
31
 
34
32
  private
35
33
 
34
+ def api
35
+ @api ||= ::Shodan::API.new
36
+ end
37
+
36
38
  def search
37
39
  api.host.search(query)
38
40
  rescue ::Shodan::Error => _e
@@ -5,7 +5,6 @@ require "urlscan"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class Urlscan < Base
8
- attr_reader :api
9
8
  attr_reader :title
10
9
  attr_reader :description
11
10
  attr_reader :query
@@ -15,7 +14,6 @@ module Mihari
15
14
  def initialize(query, title: nil, description: nil, tags: [], target_type: "url")
16
15
  super()
17
16
 
18
- @api = ::UrlScan::API.new
19
17
  @query = query
20
18
  @title = title || "urlscan lookup"
21
19
  @description = description || "query = #{query}"
@@ -37,6 +35,10 @@ module Mihari
37
35
 
38
36
  private
39
37
 
38
+ def api
39
+ @api ||= ::UrlScan::API.new
40
+ end
41
+
40
42
  def search
41
43
  api.search(query)
42
44
  rescue ::UrlScan::ResponseError => _e
@@ -5,7 +5,6 @@ require "virustotal"
5
5
  module Mihari
6
6
  module Analyzers
7
7
  class VirusTotal < Base
8
- attr_reader :api
9
8
  attr_reader :indicator
10
9
  attr_reader :type
11
10
 
@@ -16,7 +15,6 @@ module Mihari
16
15
  def initialize(indicator, title: nil, description: nil, tags: [])
17
16
  super()
18
17
 
19
- @api = ::VirusTotal::API.new
20
18
  @indicator = indicator
21
19
  @type = TypeChecker.type(indicator)
22
20
 
@@ -31,6 +29,10 @@ module Mihari
31
29
 
32
30
  private
33
31
 
32
+ def api
33
+ @api = ::VirusTotal::API.new
34
+ end
35
+
34
36
  def valid_type?
35
37
  %w(ip domain).include? type
36
38
  end
@@ -49,22 +51,28 @@ module Mihari
49
51
  end
50
52
 
51
53
  def domain_lookup
52
- report = api.domain.report(indicator)
53
- return nil unless report
54
+ begin
55
+ res = api.domain.resolutions(indicator)
56
+ rescue ::VirusTotal::Error => _e
57
+ return nil
58
+ end
54
59
 
55
- resolutions = report.dig("resolutions") || []
56
- resolutions.map do |resolution|
57
- resolution.dig("ip_address")
60
+ data = res.dig("data") || []
61
+ data.map do |item|
62
+ item.dig("attributes", "ip_address")
58
63
  end.compact.uniq
59
64
  end
60
65
 
61
66
  def ip_lookup
62
- report = api.ip_address.report(indicator)
63
- return nil unless report
67
+ begin
68
+ res = api.ip_address.resolutions(indicator)
69
+ rescue ::VirusTotal::Error => _e
70
+ return nil
71
+ end
64
72
 
65
- resolutions = report.dig("resolutions") || []
66
- resolutions.map do |resolution|
67
- resolution.dig("hostname")
73
+ data = res.dig("data") || []
74
+ data.map do |item|
75
+ item.dig("attributes", "host_name")
68
76
  end.compact.uniq
69
77
  end
70
78
  end
@@ -135,7 +135,7 @@ module Mihari
135
135
 
136
136
  def parse_as_json(input)
137
137
  JSON.parse input
138
- rescue JSON::ParserError => e
138
+ rescue JSON::ParserError => _e
139
139
  nil
140
140
  end
141
141
 
@@ -12,7 +12,7 @@ module Mihari
12
12
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
13
13
  end
14
14
 
15
- def emit(title:, description:, artifacts:)
15
+ def emit(*)
16
16
  raise NotImplementedError, "You must implement #{self.class}##{__method__}"
17
17
  end
18
18
  end
@@ -11,7 +11,7 @@ module Mihari
11
11
  api_endpoint? && api_key? && ping?
12
12
  end
13
13
 
14
- def emit(title:, description:, artifacts:, tags: [])
14
+ def emit(title:, artifacts:, tags: [], **_options)
15
15
  event = ::MISP::Event.new(info: title)
16
16
 
17
17
  artifacts.each do |artifact|
@@ -5,6 +5,7 @@ module Mihari
5
5
  def check
6
6
  {
7
7
  censys: { status: censys?, message: censys },
8
+ misp: { status: misp?, message: misp },
8
9
  onyphe: { status: onyphe?, message: onyphe },
9
10
  securitytrails: { status: securitytrails?, message: securitytrails },
10
11
  shodan: { status: shodan?, message: shodan },
@@ -84,5 +85,13 @@ module Mihari
84
85
  def the_hive
85
86
  the_hive? ? "THEHIVE_API_ENDPOINT and THEHIVE_API_KEY are found" : "THEHIVE_API_ENDPOINT and THEHIVE_API_KEY are are missing"
86
87
  end
88
+
89
+ def misp?
90
+ ENV.key?("MISP_API_ENDPOINT") && ENV.key?("MISP_API_KEY")
91
+ end
92
+
93
+ def misp
94
+ misp? ? "MISP_API_ENDPOINT and MISP_API_KEY are found" : "MISP_API_ENDPOINT and MISP_API_KEY are are missing"
95
+ end
87
96
  end
88
97
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mihari
4
- VERSION = "0.9.0"
4
+ VERSION = "0.9.1"
5
5
  end
@@ -50,5 +50,5 @@ Gem::Specification.new do |spec|
50
50
  spec.add_dependency "slack-notifier", "~> 2.3"
51
51
  spec.add_dependency "thor", "~> 0.20"
52
52
  spec.add_dependency "urlscan", "~> 0.4"
53
- spec.add_dependency "virustotalx", "~> 0.1"
53
+ spec.add_dependency "virustotalx", "~> 1.0"
54
54
  end
Binary file
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.9.0
4
+ version: 0.9.1
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-09-24 00:00:00.000000000 Z
11
+ date: 2019-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -366,14 +366,14 @@ dependencies:
366
366
  requirements:
367
367
  - - "~>"
368
368
  - !ruby/object:Gem::Version
369
- version: '0.1'
369
+ version: '1.0'
370
370
  type: :runtime
371
371
  prerelease: false
372
372
  version_requirements: !ruby/object:Gem::Requirement
373
373
  requirements:
374
374
  - - "~>"
375
375
  - !ruby/object:Gem::Version
376
- version: '0.1'
376
+ version: '1.0'
377
377
  description: A framework for continuous malicious hosts monitoring.
378
378
  email:
379
379
  - manabu.niseki@gmail.com
@@ -393,7 +393,6 @@ files:
393
393
  - bin/setup
394
394
  - docker/Dockerfile
395
395
  - examples/ipinfo_hosted_domains.rb
396
- - examples/vt_passive_dns.rb
397
396
  - exe/mihari
398
397
  - lib/mihari.rb
399
398
  - lib/mihari/alert_viewer.rb
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $LOAD_PATH.unshift("#{__dir__}/../lib")
4
-
5
- require "mihari"
6
-
7
- require "virustotal_api"
8
-
9
- module Mihari
10
- module Analyzers
11
- class VTPassiveDNS < Base
12
- attr_reader :ip
13
-
14
- def initialize(ip, api_key: nil)
15
- @ip = ip
16
- @api_key = api_key
17
- end
18
-
19
- def title
20
- "VT passive DNS"
21
- end
22
-
23
- def description
24
- "VT passive DNS: #{ip}"
25
- end
26
-
27
- def api_key
28
- ENV["VT_API_KEY"] || @api_key
29
- end
30
-
31
- def artifacts
32
- ip_report = VirustotalAPI::IPReport.find(ip, api_key)
33
- return [] unless ip_report.exists?
34
-
35
- report = ip_report.report
36
- report.dig("resolutions")&.map do |resolution|
37
- resolution.dig("hostname")
38
- end&.compact
39
- end
40
- end
41
- end
42
- end
43
-
44
- ip = "TARGET_IP"
45
- analyzer = Mihari::Analyzers::VTPassiveDNS.new(ip)
46
- analyzer.run