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 +4 -4
- data/README.md +10 -3
- data/Rakefile +3 -1
- data/docker/Dockerfile +6 -4
- data/lib/mihari/analyzers/base.rb +3 -2
- data/lib/mihari/analyzers/censys.rb +11 -7
- data/lib/mihari/analyzers/crtsh.rb +4 -2
- data/lib/mihari/analyzers/onyphe.rb +4 -4
- data/lib/mihari/analyzers/securitytrails.rb +4 -2
- data/lib/mihari/analyzers/securitytrails_domain_feed.rb +4 -2
- data/lib/mihari/analyzers/shodan.rb +4 -2
- data/lib/mihari/analyzers/urlscan.rb +4 -2
- data/lib/mihari/analyzers/virustotal.rb +20 -12
- data/lib/mihari/cli.rb +1 -1
- data/lib/mihari/emitters/base.rb +1 -1
- data/lib/mihari/emitters/misp.rb +1 -1
- data/lib/mihari/status.rb +9 -0
- data/lib/mihari/version.rb +1 -1
- data/mihari.gemspec +1 -1
- data/screenshots/eyecatch.png +0 -0
- metadata +4 -5
- data/examples/vt_passive_dns.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4175ef15648358026415714167bb1b0567076ad01c20ecf172def0272610ed02
|
4
|
+
data.tar.gz: 5b001b4a18c211441a753c5325f355028ae7bf426dbe2c51d676be95f267cf48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 351a8537861e52c4d4f5e3eb159d83ee10a699870198736169631c7353726ca6e22cf79c35db827b48b10fef9fc1e6ef236bbb4b07d42dde5452d3316fc051d8
|
7
|
+
data.tar.gz: 1467f8fbbce999eef16a72e430a70a5e40faeb9597f2b6f533a202efe27cce7022598768557b8459d1859c33252311ec63fcbe0005a4225dc076173d4a2b712c
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/mihari)
|
4
4
|
[](https://travis-ci.org/ninoseki/mihari)
|
5
|
+
[](https://hub.docker.com/r/ninoseki/mihari)
|
5
6
|
[](https://coveralls.io/github/ninoseki/mihari?branch=master)
|
6
7
|
[](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
|
-

|
30
31
|
|
31
32
|
- Slack notification example
|
32
33
|
|
33
|
-

|
34
35
|
|
35
36
|
- MISP event example
|
36
37
|
|
37
|
-

|
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
data/docker/Dockerfile
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
FROM ruby:2.6
|
2
|
-
|
3
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
53
|
-
|
54
|
+
begin
|
55
|
+
res = api.domain.resolutions(indicator)
|
56
|
+
rescue ::VirusTotal::Error => _e
|
57
|
+
return nil
|
58
|
+
end
|
54
59
|
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
-
|
63
|
-
|
67
|
+
begin
|
68
|
+
res = api.ip_address.resolutions(indicator)
|
69
|
+
rescue ::VirusTotal::Error => _e
|
70
|
+
return nil
|
71
|
+
end
|
64
72
|
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
data/lib/mihari/cli.rb
CHANGED
data/lib/mihari/emitters/base.rb
CHANGED
data/lib/mihari/emitters/misp.rb
CHANGED
data/lib/mihari/status.rb
CHANGED
@@ -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
|
data/lib/mihari/version.rb
CHANGED
data/mihari.gemspec
CHANGED
@@ -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
|
53
|
+
spec.add_dependency "virustotalx", "~> 1.0"
|
54
54
|
end
|
data/screenshots/eyecatch.png
CHANGED
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.
|
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-
|
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
|
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
|
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
|
data/examples/vt_passive_dns.rb
DELETED
@@ -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
|