mihari 5.2.2 → 5.2.3
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/lib/mihari/analyzers/binaryedge.rb +0 -1
- data/lib/mihari/analyzers/censys.rb +7 -2
- data/lib/mihari/analyzers/circl.rb +1 -1
- data/lib/mihari/analyzers/passivetotal.rb +1 -1
- data/lib/mihari/analyzers/rule.rb +63 -72
- data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -2
- data/lib/mihari/clients/base.rb +1 -1
- data/lib/mihari/commands/database.rb +12 -11
- data/lib/mihari/commands/rule.rb +47 -45
- data/lib/mihari/commands/search.rb +66 -47
- data/lib/mihari/commands/version.rb +8 -6
- data/lib/mihari/commands/web.rb +26 -23
- data/lib/mihari/emitters/base.rb +14 -1
- data/lib/mihari/emitters/database.rb +3 -10
- data/lib/mihari/emitters/misp.rb +16 -5
- data/lib/mihari/emitters/slack.rb +13 -15
- data/lib/mihari/emitters/the_hive.rb +17 -19
- data/lib/mihari/emitters/webhook.rb +23 -23
- data/lib/mihari/enrichers/whois.rb +1 -0
- data/lib/mihari/feed/parser.rb +1 -0
- data/lib/mihari/feed/reader.rb +29 -14
- data/lib/mihari/mixins/configurable.rb +13 -4
- data/lib/mihari/structs/censys.rb +96 -82
- data/lib/mihari/structs/config.rb +23 -21
- data/lib/mihari/structs/google_public_dns.rb +27 -23
- data/lib/mihari/structs/greynoise.rb +44 -38
- data/lib/mihari/structs/onyphe.rb +34 -30
- data/lib/mihari/structs/shodan.rb +77 -69
- data/lib/mihari/structs/urlscan.rb +42 -36
- data/lib/mihari/structs/virustotal_intelligence.rb +57 -49
- data/lib/mihari/type_checker.rb +10 -8
- data/lib/mihari/version.rb +1 -1
- data/mihari.gemspec +3 -3
- metadata +8 -8
@@ -36,32 +36,34 @@ module Mihari
|
|
36
36
|
attributes[:values]
|
37
37
|
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
class << self
|
40
|
+
#
|
41
|
+
# @param [Class<Mihari::Analyzers::Base>, Class<Mihari::Emitters::Base>] klass
|
42
|
+
#
|
43
|
+
# @return [Mihari::Structs::Config, nil] config
|
44
|
+
#
|
45
|
+
def from_class(klass)
|
46
|
+
return nil if klass == Mihari::Analyzers::Rule
|
46
47
|
|
47
|
-
|
48
|
+
name = klass.to_s.split("::").last.to_s
|
48
49
|
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
is_analyzer = klass.ancestors.include?(Mihari::Analyzers::Base)
|
51
|
+
is_emitter = klass.ancestors.include?(Mihari::Emitters::Base)
|
52
|
+
is_enricher = klass.ancestors.include?(Mihari::Enrichers::Base)
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
type = "Analyzer"
|
55
|
+
type = "Emitter" if is_emitter
|
56
|
+
type = "Enricher" if is_enricher
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
begin
|
59
|
+
instance = is_analyzer ? klass.new("dummy") : klass.new(artifacts: [], rule: nil)
|
60
|
+
is_configured = instance.configured?
|
61
|
+
values = instance.configuration_values
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
63
|
+
new(name: name, values: values, is_configured: is_configured, type: type)
|
64
|
+
rescue ArgumentError => _e
|
65
|
+
nil
|
66
|
+
end
|
65
67
|
end
|
66
68
|
end
|
67
69
|
end
|
@@ -37,19 +37,21 @@ module Mihari
|
|
37
37
|
attributes[:resource_type]
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
40
|
+
class << self
|
41
|
+
#
|
42
|
+
# @param [Hash] d
|
43
|
+
#
|
44
|
+
# @return [Answer]
|
45
|
+
#
|
46
|
+
def from_dynamic!(d)
|
47
|
+
d = Types::Hash[d]
|
48
|
+
resource_type = INT_TYPE_TO_TYPE[d.fetch("type")]
|
49
|
+
new(
|
50
|
+
name: d.fetch("name"),
|
51
|
+
data: d.fetch("data"),
|
52
|
+
resource_type: resource_type
|
53
|
+
)
|
54
|
+
end
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
@@ -63,16 +65,18 @@ module Mihari
|
|
63
65
|
attributes[:answers]
|
64
66
|
end
|
65
67
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
68
|
+
class << self
|
69
|
+
#
|
70
|
+
# @param [Hash] d
|
71
|
+
#
|
72
|
+
# @return [Response]
|
73
|
+
#
|
74
|
+
def from_dynamic!(d)
|
75
|
+
d = Types::Hash[d]
|
76
|
+
new(
|
77
|
+
answers: d.fetch("Answer", []).map { |x| Answer.from_dynamic!(x) }
|
78
|
+
)
|
79
|
+
end
|
76
80
|
end
|
77
81
|
end
|
78
82
|
end
|
@@ -48,18 +48,20 @@ module Mihari
|
|
48
48
|
)
|
49
49
|
end
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
51
|
+
class << self
|
52
|
+
#
|
53
|
+
# @param [Hash] d
|
54
|
+
#
|
55
|
+
# @return [Metadata]
|
56
|
+
#
|
57
|
+
def from_dynamic!(d)
|
58
|
+
d = Types::Hash[d]
|
59
|
+
new(
|
60
|
+
country: d.fetch("country"),
|
61
|
+
country_code: d.fetch("country_code"),
|
62
|
+
asn: d.fetch("asn")
|
63
|
+
)
|
64
|
+
end
|
63
65
|
end
|
64
66
|
end
|
65
67
|
|
@@ -101,18 +103,20 @@ module Mihari
|
|
101
103
|
)
|
102
104
|
end
|
103
105
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
106
|
+
class << self
|
107
|
+
#
|
108
|
+
# @param [Hash] d
|
109
|
+
#
|
110
|
+
# @return [Datum]
|
111
|
+
#
|
112
|
+
def from_dynamic!(d)
|
113
|
+
d = Types::Hash[d]
|
114
|
+
new(
|
115
|
+
ip: d.fetch("ip"),
|
116
|
+
metadata: Metadata.from_dynamic!(d.fetch("metadata")),
|
117
|
+
metadata_: d
|
118
|
+
)
|
119
|
+
end
|
116
120
|
end
|
117
121
|
end
|
118
122
|
|
@@ -165,20 +169,22 @@ module Mihari
|
|
165
169
|
data.map { |datum| datum.to_artifact }
|
166
170
|
end
|
167
171
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
172
|
+
class << self
|
173
|
+
#
|
174
|
+
# @param [Hash] d
|
175
|
+
#
|
176
|
+
# @return [Response]
|
177
|
+
#
|
178
|
+
def from_dynamic!(d)
|
179
|
+
d = Types::Hash[d]
|
180
|
+
new(
|
181
|
+
complete: d.fetch("complete"),
|
182
|
+
count: d.fetch("count"),
|
183
|
+
data: d.fetch("data").map { |x| Datum.from_dynamic!(x) },
|
184
|
+
message: d.fetch("message"),
|
185
|
+
query: d.fetch("query")
|
186
|
+
)
|
187
|
+
end
|
182
188
|
end
|
183
189
|
end
|
184
190
|
end
|
@@ -70,20 +70,22 @@ module Mihari
|
|
70
70
|
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
71
71
|
end
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
73
|
+
class << self
|
74
|
+
#
|
75
|
+
# @param [Hash] d
|
76
|
+
#
|
77
|
+
# @return [Result]
|
78
|
+
#
|
79
|
+
def from_dynamic!(d)
|
80
|
+
d = Types::Hash[d]
|
81
|
+
new(
|
82
|
+
asn: d.fetch("asn"),
|
83
|
+
ip: d.fetch("ip"),
|
84
|
+
# Onyphe's country = 2-letter country code
|
85
|
+
country_code: d["country"],
|
86
|
+
metadata: d
|
87
|
+
)
|
88
|
+
end
|
87
89
|
end
|
88
90
|
end
|
89
91
|
|
@@ -152,22 +154,24 @@ module Mihari
|
|
152
154
|
results.map(&:to_artifact)
|
153
155
|
end
|
154
156
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
157
|
+
class << self
|
158
|
+
#
|
159
|
+
# @param [Hash] d
|
160
|
+
#
|
161
|
+
# @return [Response]
|
162
|
+
#
|
163
|
+
def from_dynamic!(d)
|
164
|
+
d = Types::Hash[d]
|
165
|
+
new(
|
166
|
+
count: d.fetch("count"),
|
167
|
+
error: d.fetch("error"),
|
168
|
+
max_page: d.fetch("max_page"),
|
169
|
+
page: d.fetch("page").to_i,
|
170
|
+
results: d.fetch("results").map { |x| Result.from_dynamic!(x) },
|
171
|
+
status: d.fetch("status"),
|
172
|
+
total: d.fetch("total")
|
173
|
+
)
|
174
|
+
end
|
171
175
|
end
|
172
176
|
end
|
173
177
|
end
|
@@ -33,17 +33,19 @@ module Mihari
|
|
33
33
|
)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
36
|
+
class << self
|
37
|
+
#
|
38
|
+
# @param [Hash] d
|
39
|
+
#
|
40
|
+
# @return [Location]
|
41
|
+
#
|
42
|
+
def from_dynamic!(d)
|
43
|
+
d = Types::Hash[d]
|
44
|
+
new(
|
45
|
+
country_code: d["country_code"],
|
46
|
+
country_name: d["country_name"]
|
47
|
+
)
|
48
|
+
end
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
@@ -109,30 +111,32 @@ module Mihari
|
|
109
111
|
Mihari::AutonomousSystem.new(asn: normalize_asn(asn))
|
110
112
|
end
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
114
|
+
class << self
|
115
|
+
#
|
116
|
+
# @param [Hash] d
|
117
|
+
#
|
118
|
+
# @return [Match]
|
119
|
+
#
|
120
|
+
def from_dynamic!(d)
|
121
|
+
d = Types::Hash[d]
|
122
|
+
|
123
|
+
# hostnames should be an array of string but sometimes Shodan returns a string
|
124
|
+
# e.g. "hostnames": "set(['149.28.146.131.vultr.com', 'rebs.ga'])",
|
125
|
+
# https://github.com/ninoseki/mihari/issues/424
|
126
|
+
# so use an empty array if hostnames is a string
|
127
|
+
hostnames = d.fetch("hostnames")
|
128
|
+
hostnames = [] if hostnames.is_a?(String)
|
129
|
+
|
130
|
+
new(
|
131
|
+
asn: d["asn"],
|
132
|
+
hostnames: hostnames,
|
133
|
+
location: Location.from_dynamic!(d.fetch("location")),
|
134
|
+
domains: d.fetch("domains"),
|
135
|
+
ip_str: d.fetch("ip_str"),
|
136
|
+
port: d.fetch("port"),
|
137
|
+
metadata: d
|
138
|
+
)
|
139
|
+
end
|
136
140
|
end
|
137
141
|
end
|
138
142
|
|
@@ -211,17 +215,19 @@ module Mihari
|
|
211
215
|
end
|
212
216
|
end
|
213
217
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
218
|
+
class << self
|
219
|
+
#
|
220
|
+
# @param [Hash] d
|
221
|
+
#
|
222
|
+
# @return [Result]
|
223
|
+
#
|
224
|
+
def from_dynamic!(d)
|
225
|
+
d = Types::Hash[d]
|
226
|
+
new(
|
227
|
+
matches: d.fetch("matches", []).map { |x| Match.from_dynamic!(x) },
|
228
|
+
total: d.fetch("total")
|
229
|
+
)
|
230
|
+
end
|
225
231
|
end
|
226
232
|
end
|
227
233
|
|
@@ -275,30 +281,32 @@ module Mihari
|
|
275
281
|
attributes[:vulns]
|
276
282
|
end
|
277
283
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
284
|
+
class << self
|
285
|
+
#
|
286
|
+
# @param [Hash] d
|
287
|
+
#
|
288
|
+
# @return [InternetDBResponse]
|
289
|
+
#
|
290
|
+
def from_dynamic!(d)
|
291
|
+
d = Types::Hash[d]
|
292
|
+
new(
|
293
|
+
ip: d.fetch("ip"),
|
294
|
+
ports: d.fetch("ports"),
|
295
|
+
cpes: d.fetch("cpes"),
|
296
|
+
hostnames: d.fetch("hostnames"),
|
297
|
+
tags: d.fetch("tags"),
|
298
|
+
vulns: d.fetch("vulns")
|
299
|
+
)
|
300
|
+
end
|
294
301
|
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
+
#
|
303
|
+
# @param [String] json
|
304
|
+
#
|
305
|
+
# @return [InternetDBResponse]
|
306
|
+
#
|
307
|
+
def from_json!(json)
|
308
|
+
from_dynamic!(JSON.parse(json))
|
309
|
+
end
|
302
310
|
end
|
303
311
|
end
|
304
312
|
end
|
@@ -29,18 +29,20 @@ module Mihari
|
|
29
29
|
attributes[:url]
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
32
|
+
class << self
|
33
|
+
#
|
34
|
+
# @param [Hash] d
|
35
|
+
#
|
36
|
+
# @return [Page]
|
37
|
+
#
|
38
|
+
def from_dynamic!(d)
|
39
|
+
d = Types::Hash[d]
|
40
|
+
new(
|
41
|
+
domain: d["domain"],
|
42
|
+
ip: d["ip"],
|
43
|
+
url: d.fetch("url")
|
44
|
+
)
|
45
|
+
end
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
@@ -88,19 +90,21 @@ module Mihari
|
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
93
|
+
class << self
|
94
|
+
#
|
95
|
+
# @param [Hash] d
|
96
|
+
#
|
97
|
+
# @return [Result]
|
98
|
+
#
|
99
|
+
def from_dynamic!(d)
|
100
|
+
d = Types::Hash[d]
|
101
|
+
new(
|
102
|
+
page: Page.from_dynamic!(d.fetch("page")),
|
103
|
+
id: d.fetch("_id"),
|
104
|
+
sort: d.fetch("sort"),
|
105
|
+
metadata: d
|
106
|
+
)
|
107
|
+
end
|
104
108
|
end
|
105
109
|
end
|
106
110
|
|
@@ -129,17 +133,19 @@ module Mihari
|
|
129
133
|
results.map(&:to_artifacts).flatten
|
130
134
|
end
|
131
135
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
136
|
+
class << self
|
137
|
+
#
|
138
|
+
# @param [Hash] d
|
139
|
+
#
|
140
|
+
# @return [Response]
|
141
|
+
#
|
142
|
+
def from_dynamic!(d)
|
143
|
+
d = Types::Hash[d]
|
144
|
+
new(
|
145
|
+
results: d.fetch("results").map { |x| Result.from_dynamic!(x) },
|
146
|
+
has_more: d.fetch("has_more")
|
147
|
+
)
|
148
|
+
end
|
143
149
|
end
|
144
150
|
end
|
145
151
|
end
|