mihari 5.2.0 → 5.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/lib/mihari/analyzers/base.rb +20 -115
- data/lib/mihari/analyzers/censys.rb +20 -2
- data/lib/mihari/analyzers/onyphe.rb +1 -1
- data/lib/mihari/analyzers/rule.rb +116 -60
- data/lib/mihari/analyzers/shodan.rb +1 -1
- data/lib/mihari/analyzers/urlscan.rb +6 -9
- data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -5
- data/lib/mihari/cli/main.rb +2 -2
- data/lib/mihari/commands/search.rb +69 -0
- data/lib/mihari/commands/web.rb +9 -2
- data/lib/mihari/mixins/error_notification.rb +0 -2
- data/lib/mihari/models/artifact.rb +1 -1
- data/lib/mihari/schemas/rule.rb +2 -17
- data/lib/mihari/structs/censys.rb +167 -11
- data/lib/mihari/structs/config.rb +28 -0
- data/lib/mihari/structs/google_public_dns.rb +39 -1
- data/lib/mihari/structs/greynoise.rb +93 -6
- data/lib/mihari/structs/ipinfo.rb +40 -0
- data/lib/mihari/structs/onyphe.rb +88 -6
- data/lib/mihari/structs/rule.rb +4 -2
- data/lib/mihari/structs/shodan.rb +138 -4
- data/lib/mihari/structs/urlscan.rb +98 -1
- data/lib/mihari/structs/virustotal_intelligence.rb +96 -1
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/app.rb +2 -2
- data/lib/mihari/web/public/assets/index-cbe1734c.js +50 -0
- data/lib/mihari/web/public/assets/index-eed1bcd8.css +5 -0
- data/lib/mihari/web/public/index.html +2 -2
- data/lib/mihari.rb +1 -0
- data/mihari.gemspec +13 -12
- metadata +41 -42
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -43
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -15
- data/.github/workflows/test.yml +0 -90
- data/config/pre_commit.yml +0 -3
- data/docker/Dockerfile +0 -14
- data/examples/ipinfo_hosted_domains.rb +0 -45
- data/images/Tines-Full_Logo-Tines_Black.png +0 -0
- data/images/alert.png +0 -0
- data/images/logo.png +0 -0
- data/images/misp.png +0 -0
- data/images/overview.jpg +0 -0
- data/images/slack.png +0 -0
- data/images/tines.png +0 -0
- data/images/web_alerts.png +0 -0
- data/images/web_config.png +0 -0
- data/lib/mihari/commands/searcher.rb +0 -61
- data/lib/mihari/web/public/assets/index-9948ee35.js +0 -50
- data/lib/mihari/web/public/assets/index-d88cc3f1.css +0 -5
data/lib/mihari/structs/rule.rb
CHANGED
@@ -10,6 +10,8 @@ require "yaml"
|
|
10
10
|
module Mihari
|
11
11
|
module Structs
|
12
12
|
class Rule
|
13
|
+
include Mixins::FalsePositive
|
14
|
+
|
13
15
|
# @return [Hash]
|
14
16
|
attr_reader :data
|
15
17
|
|
@@ -109,10 +111,10 @@ module Mihari
|
|
109
111
|
end
|
110
112
|
|
111
113
|
#
|
112
|
-
# @return [Array<String>]
|
114
|
+
# @return [Array<String, RegExp>]
|
113
115
|
#
|
114
116
|
def falsepositives
|
115
|
-
@falsepositives ||= data[:falsepositives]
|
117
|
+
@falsepositives ||= data[:falsepositives].map { |fp| normalize_falsepositive fp }
|
116
118
|
end
|
117
119
|
|
118
120
|
#
|
@@ -7,6 +7,20 @@ module Mihari
|
|
7
7
|
attribute :country_code, Types::String.optional
|
8
8
|
attribute :country_name, Types::String.optional
|
9
9
|
|
10
|
+
#
|
11
|
+
# @return [String, nil]
|
12
|
+
#
|
13
|
+
def country_code
|
14
|
+
attributes[:country_code]
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# @return [String, nil]
|
19
|
+
#
|
20
|
+
def country_name
|
21
|
+
attributes[:country_name]
|
22
|
+
end
|
23
|
+
|
10
24
|
#
|
11
25
|
# @return [Mihari::Geolocation, nil]
|
12
26
|
#
|
@@ -19,6 +33,11 @@ module Mihari
|
|
19
33
|
)
|
20
34
|
end
|
21
35
|
|
36
|
+
#
|
37
|
+
# @param [Hash] d
|
38
|
+
#
|
39
|
+
# @return [Location]
|
40
|
+
#
|
22
41
|
def self.from_dynamic!(d)
|
23
42
|
d = Types::Hash[d]
|
24
43
|
new(
|
@@ -39,6 +58,48 @@ module Mihari
|
|
39
58
|
attribute :port, Types::Integer
|
40
59
|
attribute :metadata, Types::Hash
|
41
60
|
|
61
|
+
#
|
62
|
+
# @return [String, nil]
|
63
|
+
#
|
64
|
+
def asn
|
65
|
+
attributes[:asn]
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# @return [Array<String>]
|
70
|
+
#
|
71
|
+
def hostnames
|
72
|
+
attributes[:hostnames]
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
76
|
+
# @return [Location]
|
77
|
+
#
|
78
|
+
def location
|
79
|
+
attributes[:location]
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# @return [String]
|
84
|
+
#
|
85
|
+
def ip_str
|
86
|
+
attributes[:ip_str]
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# @return [Integer]
|
91
|
+
#
|
92
|
+
def port
|
93
|
+
attributes[:port]
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# @return [Hash]
|
98
|
+
#
|
99
|
+
def metadata
|
100
|
+
attributes[:metadata]
|
101
|
+
end
|
102
|
+
|
42
103
|
#
|
43
104
|
# @return [Mihari::AutonomousSystem, nil]
|
44
105
|
#
|
@@ -48,6 +109,11 @@ module Mihari
|
|
48
109
|
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
49
110
|
end
|
50
111
|
|
112
|
+
#
|
113
|
+
# @param [Hash] d
|
114
|
+
#
|
115
|
+
# @return [Match]
|
116
|
+
#
|
51
117
|
def self.from_dynamic!(d)
|
52
118
|
d = Types::Hash[d]
|
53
119
|
|
@@ -74,6 +140,20 @@ module Mihari
|
|
74
140
|
attribute :matches, Types.Array(Match)
|
75
141
|
attribute :total, Types::Int
|
76
142
|
|
143
|
+
#
|
144
|
+
# @return [Array<Match>]
|
145
|
+
#
|
146
|
+
def matches
|
147
|
+
attributes[:matches]
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# @return [Integer]
|
152
|
+
#
|
153
|
+
def total
|
154
|
+
attributes[:total]
|
155
|
+
end
|
156
|
+
|
77
157
|
#
|
78
158
|
# Collect metadata from matches
|
79
159
|
#
|
@@ -107,12 +187,10 @@ module Mihari
|
|
107
187
|
matches.select { |match| match.ip_str == ip }.map(&:hostnames).flatten.uniq
|
108
188
|
end
|
109
189
|
|
110
|
-
#
|
111
|
-
# @param [Source] source
|
112
190
|
#
|
113
191
|
# @return [Array<Mihari::Artifact>]
|
114
192
|
#
|
115
|
-
def to_artifacts
|
193
|
+
def to_artifacts
|
116
194
|
matches.map do |match|
|
117
195
|
metadata = collect_metadata_by_ip(match.ip_str)
|
118
196
|
ports = collect_ports_by_ip(match.ip_str).map do |port|
|
@@ -124,7 +202,6 @@ module Mihari
|
|
124
202
|
|
125
203
|
Mihari::Artifact.new(
|
126
204
|
data: match.ip_str,
|
127
|
-
source: source,
|
128
205
|
metadata: metadata,
|
129
206
|
autonomous_system: match.to_asn,
|
130
207
|
geolocation: match.location.to_geolocation,
|
@@ -134,6 +211,11 @@ module Mihari
|
|
134
211
|
end
|
135
212
|
end
|
136
213
|
|
214
|
+
#
|
215
|
+
# @param [Hash] d
|
216
|
+
#
|
217
|
+
# @return [Result]
|
218
|
+
#
|
137
219
|
def self.from_dynamic!(d)
|
138
220
|
d = Types::Hash[d]
|
139
221
|
new(
|
@@ -151,6 +233,53 @@ module Mihari
|
|
151
233
|
attribute :tags, Types.Array(Types::String)
|
152
234
|
attribute :vulns, Types.Array(Types::String)
|
153
235
|
|
236
|
+
#
|
237
|
+
# @return [String]
|
238
|
+
#
|
239
|
+
def ip
|
240
|
+
attributes[:ip]
|
241
|
+
end
|
242
|
+
|
243
|
+
#
|
244
|
+
# @return [Array<Integer>]
|
245
|
+
#
|
246
|
+
def ports
|
247
|
+
attributes[:ports]
|
248
|
+
end
|
249
|
+
|
250
|
+
#
|
251
|
+
# @return [Array<String>]
|
252
|
+
#
|
253
|
+
def cpes
|
254
|
+
attributes[:cpes]
|
255
|
+
end
|
256
|
+
|
257
|
+
#
|
258
|
+
# @return [Array<String>]
|
259
|
+
#
|
260
|
+
def hostnames
|
261
|
+
attributes[:hostnames]
|
262
|
+
end
|
263
|
+
|
264
|
+
#
|
265
|
+
# @return [Array<String>]
|
266
|
+
#
|
267
|
+
def tags
|
268
|
+
attributes[:tags]
|
269
|
+
end
|
270
|
+
|
271
|
+
#
|
272
|
+
# @return [Array<String>]
|
273
|
+
#
|
274
|
+
def vulns
|
275
|
+
attributes[:vulns]
|
276
|
+
end
|
277
|
+
|
278
|
+
#
|
279
|
+
# @param [Hash] d
|
280
|
+
#
|
281
|
+
# @return [InternetDBResponse]
|
282
|
+
#
|
154
283
|
def self.from_dynamic!(d)
|
155
284
|
d = Types::Hash[d]
|
156
285
|
new(
|
@@ -163,6 +292,11 @@ module Mihari
|
|
163
292
|
)
|
164
293
|
end
|
165
294
|
|
295
|
+
#
|
296
|
+
# @param [String] json
|
297
|
+
#
|
298
|
+
# @return [InternetDBResponse]
|
299
|
+
#
|
166
300
|
def self.from_json!(json)
|
167
301
|
from_dynamic!(JSON.parse(json))
|
168
302
|
end
|
@@ -8,6 +8,32 @@ module Mihari
|
|
8
8
|
attribute :ip, Types::String.optional
|
9
9
|
attribute :url, Types::String
|
10
10
|
|
11
|
+
#
|
12
|
+
# @return [String, nil]
|
13
|
+
#
|
14
|
+
def domain
|
15
|
+
attributes[:domain]
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# @return [String, nil]
|
20
|
+
#
|
21
|
+
def ip
|
22
|
+
attributes[:ip]
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
#
|
28
|
+
def url
|
29
|
+
attributes[:url]
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# @param [Hash] d
|
34
|
+
#
|
35
|
+
# @return [Page]
|
36
|
+
#
|
11
37
|
def self.from_dynamic!(d)
|
12
38
|
d = Types::Hash[d]
|
13
39
|
new(
|
@@ -22,13 +48,58 @@ module Mihari
|
|
22
48
|
attribute :page, Page
|
23
49
|
attribute :id, Types::String
|
24
50
|
attribute :sort, Types.Array(Types::String | Types::Integer)
|
51
|
+
attribute :metadata, Types::Hash
|
52
|
+
|
53
|
+
#
|
54
|
+
# @return [Page]
|
55
|
+
#
|
56
|
+
def page
|
57
|
+
attributes[:page]
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
#
|
63
|
+
def id
|
64
|
+
attributes[:id]
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# @return [Array<String, Integer>]
|
69
|
+
#
|
70
|
+
def sort
|
71
|
+
attributes[:sort]
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# @return [Array<String, Integer>]
|
76
|
+
#
|
77
|
+
def metadata
|
78
|
+
attributes[:metadata]
|
79
|
+
end
|
25
80
|
|
81
|
+
#
|
82
|
+
# @return [Array<Mihari::Artifact>]
|
83
|
+
#
|
84
|
+
def to_artifacts
|
85
|
+
values = [page.url, page.domain, page.ip].compact
|
86
|
+
values.map do |value|
|
87
|
+
Mihari::Artifact.new(data: value, metadata: metadata)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# @param [Hash] d
|
93
|
+
#
|
94
|
+
# @return [Result]
|
95
|
+
#
|
26
96
|
def self.from_dynamic!(d)
|
27
97
|
d = Types::Hash[d]
|
28
98
|
new(
|
29
99
|
page: Page.from_dynamic!(d.fetch("page")),
|
30
100
|
id: d.fetch("_id"),
|
31
|
-
sort: d.fetch("sort")
|
101
|
+
sort: d.fetch("sort"),
|
102
|
+
metadata: d
|
32
103
|
)
|
33
104
|
end
|
34
105
|
end
|
@@ -37,6 +108,32 @@ module Mihari
|
|
37
108
|
attribute :results, Types.Array(Result)
|
38
109
|
attribute :has_more, Types::Bool
|
39
110
|
|
111
|
+
#
|
112
|
+
# @return [Array<Result>]
|
113
|
+
#
|
114
|
+
def results
|
115
|
+
attributes[:results]
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# @return [Boolean]
|
120
|
+
#
|
121
|
+
def has_more
|
122
|
+
attributes[:has_more]
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# @return [Array<Mihari::Artifact>]
|
127
|
+
#
|
128
|
+
def to_artifacts
|
129
|
+
results.map(&:to_artifacts).flatten
|
130
|
+
end
|
131
|
+
|
132
|
+
#
|
133
|
+
# @param [Hash] d
|
134
|
+
#
|
135
|
+
# @return [Response]
|
136
|
+
#
|
40
137
|
def self.from_dynamic!(d)
|
41
138
|
d = Types::Hash[d]
|
42
139
|
new(
|
@@ -6,6 +6,18 @@ module Mihari
|
|
6
6
|
class ContextAttributes < Dry::Struct
|
7
7
|
attribute :url, Types::String.optional
|
8
8
|
|
9
|
+
#
|
10
|
+
# @return [String, nil]
|
11
|
+
#
|
12
|
+
def url
|
13
|
+
attributes[:url]
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# @param [Hash] d
|
18
|
+
#
|
19
|
+
# @return [ContextAttributes]
|
20
|
+
#
|
9
21
|
def self.from_dynamic!(d)
|
10
22
|
d = Types::Hash[d]
|
11
23
|
new(
|
@@ -20,6 +32,37 @@ module Mihari
|
|
20
32
|
attribute :context_attributes, ContextAttributes.optional
|
21
33
|
attribute :metadata, Types::Hash
|
22
34
|
|
35
|
+
#
|
36
|
+
# @return [String]
|
37
|
+
#
|
38
|
+
def type
|
39
|
+
attributes[:type]
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# @return [String]
|
44
|
+
#
|
45
|
+
def id
|
46
|
+
attributes[:id]
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# @return [ContextAttributes, nil]
|
51
|
+
#
|
52
|
+
def context_attributes
|
53
|
+
attributes[:context_attributes]
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# @return [Hash, nil]
|
58
|
+
#
|
59
|
+
def metadata
|
60
|
+
attributes[:metadata]
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# @return [String, nil]
|
65
|
+
#
|
23
66
|
def value
|
24
67
|
case type
|
25
68
|
when "file"
|
@@ -33,11 +76,25 @@ module Mihari
|
|
33
76
|
end
|
34
77
|
end
|
35
78
|
|
79
|
+
#
|
80
|
+
# @return [Mihari::Artifact]
|
81
|
+
#
|
82
|
+
def to_artifact
|
83
|
+
Artifact.new(data: value, metadata: metadata)
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# @param [Hash] d
|
88
|
+
#
|
89
|
+
# @return [Datum]
|
90
|
+
#
|
36
91
|
def self.from_dynamic!(d)
|
37
92
|
d = Types::Hash[d]
|
38
93
|
|
39
94
|
context_attributes = nil
|
40
|
-
|
95
|
+
if d.key?("context_attributes")
|
96
|
+
context_attributes = ContextAttributes.from_dynamic!(d.fetch("context_attributes"))
|
97
|
+
end
|
41
98
|
|
42
99
|
new(
|
43
100
|
type: d.fetch("type"),
|
@@ -51,6 +108,18 @@ module Mihari
|
|
51
108
|
class Meta < Dry::Struct
|
52
109
|
attribute :cursor, Types::String.optional
|
53
110
|
|
111
|
+
#
|
112
|
+
# @return [String, nil]
|
113
|
+
#
|
114
|
+
def cursor
|
115
|
+
attributes[:cursor]
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# @param [Hash] d
|
120
|
+
#
|
121
|
+
# @return [Meta]
|
122
|
+
#
|
54
123
|
def self.from_dynamic!(d)
|
55
124
|
d = Types::Hash[d]
|
56
125
|
new(
|
@@ -63,6 +132,32 @@ module Mihari
|
|
63
132
|
attribute :meta, Meta
|
64
133
|
attribute :data, Types.Array(Datum)
|
65
134
|
|
135
|
+
#
|
136
|
+
# @return [Meta]
|
137
|
+
#
|
138
|
+
def meta
|
139
|
+
attributes[:meta]
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# @return [Array<Datum>]
|
144
|
+
#
|
145
|
+
def data
|
146
|
+
attributes[:data]
|
147
|
+
end
|
148
|
+
|
149
|
+
#
|
150
|
+
# @return [Array<Mihari::Artifact>]
|
151
|
+
#
|
152
|
+
def to_artifacts
|
153
|
+
data.map(&:to_artifact)
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# @param [Hash] d
|
158
|
+
#
|
159
|
+
# @return [Response]
|
160
|
+
#
|
66
161
|
def self.from_dynamic!(d)
|
67
162
|
d = Types::Hash[d]
|
68
163
|
new(
|
data/lib/mihari/version.rb
CHANGED
data/lib/mihari/web/app.rb
CHANGED
@@ -44,7 +44,7 @@ module Mihari
|
|
44
44
|
end.to_app
|
45
45
|
end
|
46
46
|
|
47
|
-
def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60)
|
47
|
+
def run!(port: 9292, host: "localhost", threads: "0:5", verbose: false, worker_timeout: 60, open: true)
|
48
48
|
url = "http://#{host}:#{port}"
|
49
49
|
|
50
50
|
# set maximum number of threads to use as PARALLEL_PROCESSOR_COUNT (if it is not set)
|
@@ -60,7 +60,7 @@ module Mihari
|
|
60
60
|
Verbose: verbose,
|
61
61
|
worker_timeout: worker_timeout
|
62
62
|
) do |_|
|
63
|
-
Launchy.open(url) if ENV["RACK_ENV"] != "development"
|
63
|
+
Launchy.open(url) if ENV["RACK_ENV"] != "development" && open
|
64
64
|
rescue Launchy::CommandNotFoundError
|
65
65
|
# ref. https://github.com/ninoseki/mihari/issues/477
|
66
66
|
# do nothing
|