mihari 7.0.0 → 7.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +3 -2
- data/docker-compose.yml +1 -2
- data/lib/mihari/analyzers/crtsh.rb +7 -2
- data/lib/mihari/clients/crtsh.rb +1 -1
- data/lib/mihari/models/artifact.rb +17 -11
- data/lib/mihari/models/autonomous_system.rb +0 -16
- data/lib/mihari/models/cpe.rb +0 -16
- data/lib/mihari/models/dns.rb +0 -16
- data/lib/mihari/models/geolocation.rb +0 -19
- data/lib/mihari/models/reverse_dns.rb +0 -18
- data/lib/mihari/models/whois.rb +0 -14
- data/lib/mihari/schemas/analyzer.rb +1 -0
- data/lib/mihari/services/builders.rb +133 -16
- data/lib/mihari/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 184d0084b9ab68c359ddaa10436a2a6172fa09e71f0cc4492af57a1afcf924d8
|
4
|
+
data.tar.gz: 035620673b2351bd6c70a837c7e73f36819e320a868298b6e9a07271759bf6ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f16c21d9475313b8d6ef81cd12fe79c18964552f27633aa2c23fd530c92f9eece651af02fc51b3059f4e57acd6a7c5080607f2822488d26d56a0945c97c424b
|
7
|
+
data.tar.gz: 25640e747ea9113d294dcb910574d7958d0883187e03f0fa891f809b1bc997722bc9af48299685d4f67d51562a6a9d6cdbec0eb499d6e1afb38410e38a7c1936
|
data/Dockerfile
CHANGED
@@ -2,10 +2,11 @@ FROM ruby:3.2.2-alpine3.19
|
|
2
2
|
|
3
3
|
ARG MIHARI_VERSION=0.0.0
|
4
4
|
|
5
|
-
RUN apk --no-cache add
|
5
|
+
RUN apk --no-cache add build-base ruby-dev libpq-dev && \
|
6
|
+
echo 'gem: --no-document' >> /usr/local/etc/gemrc && \
|
6
7
|
gem install pg && \
|
7
8
|
gem install mihari -v ${MIHARI_VERSION} && \
|
8
|
-
apk del --purge
|
9
|
+
apk del --purge build-base ruby-dev && \
|
9
10
|
rm -rf /usr/local/bundle/cache/*
|
10
11
|
|
11
12
|
ENTRYPOINT ["mihari"]
|
data/docker-compose.yml
CHANGED
@@ -47,8 +47,7 @@ services:
|
|
47
47
|
image: ghcr.io/ninoseki/mihari:latest
|
48
48
|
environment:
|
49
49
|
- DATABASE_URL=${DATABASE_URL:-postgresql://${POSTGRES_USER:-user}:${POSTGRES_PASSWORD:-password}@database:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-mihari}}
|
50
|
-
-
|
51
|
-
- USE_SIDEKIQ=true
|
50
|
+
- SIDEKIQ_REDIS_URL=${REDIS_URL:-redis://redis:${REDIS_PORT:-6379}}
|
52
51
|
env_file:
|
53
52
|
- .env
|
54
53
|
entrypoint: ["mihari", "sidekiq"]
|
@@ -9,20 +9,25 @@ module Mihari
|
|
9
9
|
# @return [Boolean]
|
10
10
|
attr_reader :exclude_expired
|
11
11
|
|
12
|
+
# @return [String, nil]
|
13
|
+
attr_reader :match
|
14
|
+
|
12
15
|
#
|
13
16
|
# @param [String] query
|
14
17
|
# @param [Hash, nil] options
|
15
18
|
# @param [Bool] exclude_expired
|
19
|
+
# @param [String, nil] match
|
16
20
|
#
|
17
|
-
def initialize(query, options: nil, exclude_expired: true)
|
21
|
+
def initialize(query, options: nil, exclude_expired: true, match: nil)
|
18
22
|
super(query, options: options)
|
19
23
|
|
20
24
|
@exclude_expired = exclude_expired
|
25
|
+
@match = match
|
21
26
|
end
|
22
27
|
|
23
28
|
def artifacts
|
24
29
|
exclude = exclude_expired ? "expired" : nil
|
25
|
-
client.search(query, exclude: exclude).map do |result|
|
30
|
+
client.search(query, exclude: exclude, match: match).map do |result|
|
26
31
|
values = result["name_value"].to_s.lines.map(&:chomp).reject { |value| value.starts_with?("*.") }
|
27
32
|
values.map { |value| Models::Artifact.new(data: value, metadata: result) }
|
28
33
|
end.flatten
|
data/lib/mihari/clients/crtsh.rb
CHANGED
@@ -19,7 +19,7 @@ module Mihari
|
|
19
19
|
# Search crt.sh by a given identity
|
20
20
|
#
|
21
21
|
# @param [String] identity
|
22
|
-
# @param [String, nil] match "=", "ILIKE", "LIKE", "single", "any" or nil
|
22
|
+
# @param [String, nil] match "=", "ILIKE", "LIKE", "single", "any", "FTS" or nil
|
23
23
|
# @param [String, nil] exclude "expired" or nil
|
24
24
|
#
|
25
25
|
# @return [Array<Hash>]
|
@@ -87,7 +87,7 @@ module Mihari
|
|
87
87
|
def enrich_whois(enricher = Enrichers::Whois.new)
|
88
88
|
return unless can_enrich_whois?
|
89
89
|
|
90
|
-
self.whois_record =
|
90
|
+
self.whois_record = Services::WhoisRecordBuilder.call(domain, enricher: enricher)
|
91
91
|
end
|
92
92
|
|
93
93
|
#
|
@@ -98,7 +98,7 @@ module Mihari
|
|
98
98
|
def enrich_dns(enricher = Enrichers::GooglePublicDNS.new)
|
99
99
|
return unless can_enrich_dns?
|
100
100
|
|
101
|
-
self.dns_records =
|
101
|
+
self.dns_records = Services::DnsRecordBuilder.call(domain, enricher: enricher)
|
102
102
|
end
|
103
103
|
|
104
104
|
#
|
@@ -109,7 +109,7 @@ module Mihari
|
|
109
109
|
def enrich_reverse_dns(enricher = Enrichers::Shodan.new)
|
110
110
|
return unless can_enrich_reverse_dns?
|
111
111
|
|
112
|
-
self.reverse_dns_names =
|
112
|
+
self.reverse_dns_names = Services::ReverseDnsNameBuilder.call(data, enricher: enricher)
|
113
113
|
end
|
114
114
|
|
115
115
|
#
|
@@ -120,7 +120,7 @@ module Mihari
|
|
120
120
|
def enrich_geolocation(enricher = Enrichers::MMDB.new)
|
121
121
|
return unless can_enrich_geolocation?
|
122
122
|
|
123
|
-
self.geolocation =
|
123
|
+
self.geolocation = Services::GeolocationBuilder.call(data, enricher: enricher)
|
124
124
|
end
|
125
125
|
|
126
126
|
#
|
@@ -131,7 +131,7 @@ module Mihari
|
|
131
131
|
def enrich_autonomous_system(enricher = Enrichers::MMDB.new)
|
132
132
|
return unless can_enrich_autonomous_system?
|
133
133
|
|
134
|
-
self.autonomous_system =
|
134
|
+
self.autonomous_system = Services::AutonomousSystemBuilder.call(data, enricher: enricher)
|
135
135
|
end
|
136
136
|
|
137
137
|
#
|
@@ -142,7 +142,7 @@ module Mihari
|
|
142
142
|
def enrich_ports(enricher = Enrichers::Shodan.new)
|
143
143
|
return unless can_enrich_ports?
|
144
144
|
|
145
|
-
self.ports =
|
145
|
+
self.ports = Services::PortBuilder.call(data, enricher: enricher)
|
146
146
|
end
|
147
147
|
|
148
148
|
#
|
@@ -153,7 +153,7 @@ module Mihari
|
|
153
153
|
def enrich_cpes(enricher = Enrichers::Shodan.new)
|
154
154
|
return unless can_enrich_cpes?
|
155
155
|
|
156
|
-
self.cpes =
|
156
|
+
self.cpes = Services::CPEBuilder.call(data, enricher: enricher)
|
157
157
|
end
|
158
158
|
|
159
159
|
#
|
@@ -225,10 +225,16 @@ module Mihari
|
|
225
225
|
@shodan ||= Enrichers::Shodan.new
|
226
226
|
end
|
227
227
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
228
|
+
#
|
229
|
+
# @return [String, nil]
|
230
|
+
#
|
231
|
+
def domain
|
232
|
+
case data_type
|
233
|
+
when "domain"
|
234
|
+
data
|
235
|
+
when "url"
|
236
|
+
Addressable::URI.parse(data).host
|
237
|
+
end
|
232
238
|
end
|
233
239
|
|
234
240
|
def can_enrich_whois?
|
@@ -7,22 +7,6 @@ module Mihari
|
|
7
7
|
#
|
8
8
|
class AutonomousSystem < ActiveRecord::Base
|
9
9
|
belongs_to :artifact
|
10
|
-
|
11
|
-
class << self
|
12
|
-
#
|
13
|
-
# Build AS
|
14
|
-
#
|
15
|
-
# @param [String] ip
|
16
|
-
# @param [Mihari::Enrichers::MMDB] enricher
|
17
|
-
#
|
18
|
-
# @return [Mihari::AutonomousSystem, nil]
|
19
|
-
#
|
20
|
-
def build_by_ip(ip, enricher: Enrichers::MMDB.new)
|
21
|
-
enricher.result(ip).fmap do |res|
|
22
|
-
new(asn: res.asn) if res.asn
|
23
|
-
end.value_or nil
|
24
|
-
end
|
25
|
-
end
|
26
10
|
end
|
27
11
|
end
|
28
12
|
end
|
data/lib/mihari/models/cpe.rb
CHANGED
@@ -7,22 +7,6 @@ module Mihari
|
|
7
7
|
#
|
8
8
|
class CPE < ActiveRecord::Base
|
9
9
|
belongs_to :artifact
|
10
|
-
|
11
|
-
class << self
|
12
|
-
#
|
13
|
-
# Build CPEs
|
14
|
-
#
|
15
|
-
# @param [String] ip
|
16
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
17
|
-
#
|
18
|
-
# @return [Array<Mihari::CPE>]
|
19
|
-
#
|
20
|
-
def build_by_ip(ip, enricher: Enrichers::Shodan.new)
|
21
|
-
enricher.result(ip).fmap do |res|
|
22
|
-
(res&.cpes || []).map { |cpe| new(cpe: cpe) }
|
23
|
-
end.value_or []
|
24
|
-
end
|
25
|
-
end
|
26
10
|
end
|
27
11
|
end
|
28
12
|
end
|
data/lib/mihari/models/dns.rb
CHANGED
@@ -7,22 +7,6 @@ module Mihari
|
|
7
7
|
#
|
8
8
|
class DnsRecord < ActiveRecord::Base
|
9
9
|
belongs_to :artifact
|
10
|
-
|
11
|
-
class << self
|
12
|
-
#
|
13
|
-
# Build DNS records
|
14
|
-
#
|
15
|
-
# @param [String] domain
|
16
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
17
|
-
#
|
18
|
-
# @return [Array<Mihari::Models::DnsRecord>]
|
19
|
-
#
|
20
|
-
def build_by_domain(domain, enricher: Enrichers::GooglePublicDNS.new)
|
21
|
-
enricher.result(domain).fmap do |res|
|
22
|
-
res.answers.map { |answer| new(resource: answer.resource_type, value: answer.data) }
|
23
|
-
end.value_or([])
|
24
|
-
end
|
25
|
-
end
|
26
10
|
end
|
27
11
|
end
|
28
12
|
end
|
@@ -9,25 +9,6 @@ module Mihari
|
|
9
9
|
#
|
10
10
|
class Geolocation < ActiveRecord::Base
|
11
11
|
belongs_to :artifact
|
12
|
-
|
13
|
-
class << self
|
14
|
-
#
|
15
|
-
# Build Geolocation
|
16
|
-
#
|
17
|
-
# @param [String] ip
|
18
|
-
# @param [Mihari::Enrichers::MMDB] enricher
|
19
|
-
#
|
20
|
-
# @return [Mihari::Geolocation, nil]
|
21
|
-
#
|
22
|
-
def build_by_ip(ip, enricher: Enrichers::MMDB.new)
|
23
|
-
enricher.result(ip).fmap do |res|
|
24
|
-
if res.country_code
|
25
|
-
new(country: NormalizeCountry(res.country_code, to: :short),
|
26
|
-
country_code: res.country_code)
|
27
|
-
end
|
28
|
-
end.value_or nil
|
29
|
-
end
|
30
|
-
end
|
31
12
|
end
|
32
13
|
end
|
33
14
|
end
|
@@ -7,24 +7,6 @@ module Mihari
|
|
7
7
|
#
|
8
8
|
class ReverseDnsName < ActiveRecord::Base
|
9
9
|
belongs_to :artifact
|
10
|
-
|
11
|
-
class << self
|
12
|
-
include Dry::Monads[:result]
|
13
|
-
|
14
|
-
#
|
15
|
-
# Build reverse DNS names
|
16
|
-
#
|
17
|
-
# @param [String] ip
|
18
|
-
# @param [Mihari::Enrichers::Shodan] enricher
|
19
|
-
#
|
20
|
-
# @return [Array<Mihari::Models::ReverseDnsName>]
|
21
|
-
#
|
22
|
-
def build_by_ip(ip, enricher: Enrichers::Shodan.new)
|
23
|
-
enricher.result(ip).fmap do |res|
|
24
|
-
(res&.hostnames || []).map { |name| new(name: name) }
|
25
|
-
end.value_or []
|
26
|
-
end
|
27
|
-
end
|
28
10
|
end
|
29
11
|
end
|
30
12
|
end
|
data/lib/mihari/models/whois.rb
CHANGED
@@ -7,20 +7,6 @@ module Mihari
|
|
7
7
|
#
|
8
8
|
class WhoisRecord < ActiveRecord::Base
|
9
9
|
belongs_to :artifact
|
10
|
-
|
11
|
-
class << self
|
12
|
-
#
|
13
|
-
# Build whois record
|
14
|
-
#
|
15
|
-
# @param [String] domain
|
16
|
-
# @param [Mihari::Enrichers::Whois] enricher
|
17
|
-
#
|
18
|
-
# @return [WhoisRecord, nil]
|
19
|
-
#
|
20
|
-
def build_by_domain(domain, enricher: Enrichers::Whois.new)
|
21
|
-
enricher.result(domain).value_or nil
|
22
|
-
end
|
23
|
-
end
|
24
10
|
end
|
25
11
|
end
|
26
12
|
end
|
@@ -91,6 +91,7 @@ module Mihari
|
|
91
91
|
required(:analyzer).value(Types::String.enum(*Mihari::Analyzers::Crtsh.class_keys))
|
92
92
|
required(:query).value(:string)
|
93
93
|
optional(:exclude_expired).value(:bool).default(true)
|
94
|
+
optional(:match).value(Types::String.enum("=", "ILIKE", "LIKE", "single", "any", "FTS")).default(nil)
|
94
95
|
optional(:options).hash(AnalyzerOptions)
|
95
96
|
end
|
96
97
|
|
@@ -6,35 +6,152 @@ module Mihari
|
|
6
6
|
# Rule builder
|
7
7
|
#
|
8
8
|
class RuleBuilder < Service
|
9
|
-
# @return [String]
|
10
|
-
attr_reader :path_or_id
|
11
|
-
|
12
9
|
#
|
13
10
|
# @param [String] path_or_id
|
14
11
|
#
|
15
|
-
# @return [
|
12
|
+
# @return [Mihari::Rule]
|
16
13
|
#
|
17
|
-
def
|
18
|
-
|
19
|
-
return
|
14
|
+
def call(path_or_id)
|
15
|
+
res = Try { Rule.from_model Mihari::Models::Rule.find(path_or_id) }
|
16
|
+
return res.value! if res.value?
|
20
17
|
|
21
18
|
raise ArgumentError, "#{path_or_id} not found" unless Pathname(path_or_id).exist?
|
22
19
|
|
23
|
-
|
24
|
-
ERB.new(File.read(path_or_id)).result,
|
25
|
-
permitted_classes: [Date, Symbol]
|
26
|
-
)
|
20
|
+
Rule.from_yaml ERB.new(File.read(path_or_id)).result
|
27
21
|
end
|
22
|
+
end
|
28
23
|
|
24
|
+
#
|
25
|
+
# Autonomous system builder
|
26
|
+
#
|
27
|
+
class AutonomousSystemBuilder < Service
|
29
28
|
#
|
30
|
-
# @param [String]
|
29
|
+
# @param [String] ip
|
30
|
+
# @param [Mihari::Enrichers::MMDB] enricher
|
31
31
|
#
|
32
|
-
# @return [Mihari::
|
32
|
+
# @return [Mihari::Models::AutonomousSystem, nil]
|
33
33
|
#
|
34
|
-
def call(
|
35
|
-
|
34
|
+
def call(ip, enricher: Enrichers::MMDB.new)
|
35
|
+
enricher.result(ip).fmap do |res|
|
36
|
+
Models::AutonomousSystem.new(asn: res.asn) if res.asn
|
37
|
+
end.value_or nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# CPE builder
|
43
|
+
#
|
44
|
+
class CPEBuilder < Service
|
45
|
+
#
|
46
|
+
# Build CPEs
|
47
|
+
#
|
48
|
+
# @param [String] ip
|
49
|
+
# @param [Mihari::Enrichers::Shodan] enricher
|
50
|
+
#
|
51
|
+
# @return [Array<Mihari::Models::CPE>]
|
52
|
+
#
|
53
|
+
def call(ip, enricher: Enrichers::Shodan.new)
|
54
|
+
enricher.result(ip).fmap do |res|
|
55
|
+
(res&.cpes || []).map { |cpe| Models::CPE.new(cpe: cpe) }
|
56
|
+
end.value_or []
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# DNS record builder
|
62
|
+
#
|
63
|
+
class DnsRecordBuilder < Service
|
64
|
+
#
|
65
|
+
# Build DNS records
|
66
|
+
#
|
67
|
+
# @param [String] domain
|
68
|
+
# @param [Mihari::Enrichers::Shodan] enricher
|
69
|
+
#
|
70
|
+
# @return [Array<Mihari::Models::DnsRecord>]
|
71
|
+
#
|
72
|
+
def call(domain, enricher: Enrichers::GooglePublicDNS.new)
|
73
|
+
enricher.result(domain).fmap do |res|
|
74
|
+
res.answers.map { |answer| Models::DnsRecord.new(resource: answer.resource_type, value: answer.data) }
|
75
|
+
end.value_or []
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Geolocation builder
|
81
|
+
#
|
82
|
+
class GeolocationBuilder < Service
|
83
|
+
#
|
84
|
+
# Build Geolocation
|
85
|
+
#
|
86
|
+
# @param [String] ip
|
87
|
+
# @param [Mihari::Enrichers::MMDB] enricher
|
88
|
+
#
|
89
|
+
# @return [Mihari::Models::Geolocation, nil]
|
90
|
+
#
|
91
|
+
def call(ip, enricher: Enrichers::MMDB.new)
|
92
|
+
enricher.result(ip).fmap do |res|
|
93
|
+
if res.country_code
|
94
|
+
Models::Geolocation.new(
|
95
|
+
country: NormalizeCountry(res.country_code, to: :short),
|
96
|
+
country_code: res.country_code
|
97
|
+
)
|
98
|
+
end
|
99
|
+
end.value_or nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Port builder
|
105
|
+
#
|
106
|
+
class PortBuilder < Service
|
107
|
+
#
|
108
|
+
# Build ports
|
109
|
+
#
|
110
|
+
# @param [String] ip
|
111
|
+
# @param [Mihari::Enrichers::Shodan] enricher
|
112
|
+
#
|
113
|
+
# @return [Array<Mihari::Models::Port>]
|
114
|
+
#
|
115
|
+
def call(ip, enricher: Enrichers::Shodan.new)
|
116
|
+
enricher.result(ip).fmap do |res|
|
117
|
+
(res&.ports || []).map { |port| Models::Port.new(port: port) }
|
118
|
+
end.value_or []
|
119
|
+
end
|
120
|
+
end
|
36
121
|
|
37
|
-
|
122
|
+
#
|
123
|
+
# Reverse DNS name builder
|
124
|
+
#
|
125
|
+
class ReverseDnsNameBuilder < Service
|
126
|
+
#
|
127
|
+
# Build reverse DNS names
|
128
|
+
#
|
129
|
+
# @param [String] ip
|
130
|
+
# @param [Mihari::Enrichers::Shodan] enricher
|
131
|
+
#
|
132
|
+
# @return [Array<Mihari::Models::ReverseDnsName>]
|
133
|
+
#
|
134
|
+
def call(ip, enricher: Enrichers::Shodan.new)
|
135
|
+
enricher.result(ip).fmap do |res|
|
136
|
+
(res&.hostnames || []).map { |name| Models::ReverseDnsName.new(name: name) }
|
137
|
+
end.value_or []
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
#
|
142
|
+
# Whois record builder
|
143
|
+
#
|
144
|
+
class WhoisRecordBuilder < Service
|
145
|
+
#
|
146
|
+
# Build whois record
|
147
|
+
#
|
148
|
+
# @param [String] domain
|
149
|
+
# @param [Mihari::Enrichers::Whois] enricher
|
150
|
+
#
|
151
|
+
# @return [Mihari::Models::WhoisRecord, nil]
|
152
|
+
#
|
153
|
+
def call(domain, enricher: Enrichers::Whois.new)
|
154
|
+
enricher.result(domain).value_or nil
|
38
155
|
end
|
39
156
|
end
|
40
157
|
end
|
data/lib/mihari/version.rb
CHANGED
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: 7.0.
|
4
|
+
version: 7.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manabu Niseki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: better_errors
|