mihari 5.4.1 → 5.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/frontend/package-lock.json +145 -146
- data/frontend/package.json +8 -8
- data/frontend/src/swagger.yaml +306 -272
- data/lib/mihari/analyzers/base.rb +0 -4
- data/lib/mihari/analyzers/binaryedge.rb +4 -44
- data/lib/mihari/analyzers/censys.rb +4 -20
- data/lib/mihari/analyzers/circl.rb +2 -26
- data/lib/mihari/analyzers/crtsh.rb +2 -17
- data/lib/mihari/analyzers/dnstwister.rb +1 -3
- data/lib/mihari/analyzers/greynoise.rb +5 -4
- data/lib/mihari/analyzers/hunterhow.rb +8 -23
- data/lib/mihari/analyzers/onyphe.rb +5 -39
- data/lib/mihari/analyzers/otx.rb +2 -38
- data/lib/mihari/analyzers/passivetotal.rb +3 -41
- data/lib/mihari/analyzers/securitytrails.rb +3 -41
- data/lib/mihari/analyzers/shodan.rb +7 -39
- data/lib/mihari/analyzers/urlscan.rb +2 -38
- data/lib/mihari/analyzers/virustotal_intelligence.rb +2 -25
- data/lib/mihari/analyzers/zoomeye.rb +17 -83
- data/lib/mihari/cli/alert.rb +11 -0
- data/lib/mihari/cli/main.rb +6 -1
- data/lib/mihari/clients/base.rb +9 -1
- data/lib/mihari/clients/binaryedge.rb +27 -2
- data/lib/mihari/clients/censys.rb +32 -2
- data/lib/mihari/clients/circl.rb +28 -1
- data/lib/mihari/clients/crtsh.rb +9 -2
- data/lib/mihari/clients/dnstwister.rb +4 -2
- data/lib/mihari/clients/greynoise.rb +31 -4
- data/lib/mihari/clients/hunterhow.rb +41 -3
- data/lib/mihari/clients/onyphe.rb +25 -3
- data/lib/mihari/clients/otx.rb +40 -0
- data/lib/mihari/clients/passivetotal.rb +33 -15
- data/lib/mihari/clients/securitytrails.rb +44 -0
- data/lib/mihari/clients/shodan.rb +30 -2
- data/lib/mihari/clients/urlscan.rb +32 -6
- data/lib/mihari/clients/virustotal.rb +29 -4
- data/lib/mihari/clients/zoomeye.rb +53 -2
- data/lib/mihari/commands/alert.rb +42 -0
- data/lib/mihari/commands/rule.rb +2 -2
- data/lib/mihari/commands/search.rb +20 -59
- data/lib/mihari/commands/web.rb +1 -1
- data/lib/mihari/config.rb +2 -2
- data/lib/mihari/emitters/base.rb +1 -1
- data/lib/mihari/emitters/database.rb +2 -2
- data/lib/mihari/errors.rb +23 -2
- data/lib/mihari/http.rb +7 -1
- data/lib/mihari/schemas/alert.rb +14 -0
- data/lib/mihari/services/alert_proxy.rb +106 -0
- data/lib/mihari/services/alert_runner.rb +22 -0
- data/lib/mihari/services/{rule.rb → rule_proxy.rb} +10 -6
- data/lib/mihari/services/rule_runner.rb +49 -0
- data/lib/mihari/structs/censys.rb +11 -11
- data/lib/mihari/structs/greynoise.rb +17 -8
- data/lib/mihari/structs/onyphe.rb +7 -7
- data/lib/mihari/structs/shodan.rb +5 -5
- data/lib/mihari/structs/urlscan.rb +3 -3
- data/lib/mihari/structs/virustotal_intelligence.rb +3 -3
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/alerts.rb +22 -0
- data/lib/mihari/web/endpoints/rules.rb +8 -8
- data/lib/mihari/web/public/assets/{index-61dc587c.js → index-4d7eda9f.js} +1 -1
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +29 -27
- data/lib/mihari.rb +6 -1
- data/mihari.gemspec +9 -10
- metadata +28 -37
- data/Steepfile +0 -31
@@ -18,16 +18,13 @@ module Mihari
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def artifacts
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
results.map do |result|
|
25
|
-
events = result["events"] || []
|
21
|
+
client.search_with_pagination(query, pagination_limit: pagination_limit).map do |res|
|
22
|
+
events = res["events"] || []
|
26
23
|
events.filter_map do |event|
|
27
24
|
data = event.dig("target", "ip")
|
28
25
|
data.nil? ? nil : Artifact.new(data: data, source: source, metadata: event)
|
29
26
|
end
|
30
|
-
end
|
27
|
+
end
|
31
28
|
end
|
32
29
|
|
33
30
|
def configuration_keys
|
@@ -36,49 +33,12 @@ module Mihari
|
|
36
33
|
|
37
34
|
private
|
38
35
|
|
39
|
-
PAGE_SIZE = 20
|
40
|
-
|
41
|
-
#
|
42
|
-
# Search with pagination
|
43
|
-
#
|
44
|
-
# @param [Integer] page
|
45
|
-
#
|
46
|
-
# @return [Hash]
|
47
|
-
#
|
48
|
-
def search_with_page(page: 1)
|
49
|
-
client.search(query, page: page)
|
50
|
-
rescue StatusCodeError => e
|
51
|
-
raise RetryableError, e if e.message.include?("Request time limit exceeded")
|
52
|
-
|
53
|
-
raise e
|
54
|
-
end
|
55
|
-
|
56
|
-
#
|
57
|
-
# Search
|
58
|
-
#
|
59
|
-
# @return [Array<Hash>]
|
60
|
-
#
|
61
|
-
def search
|
62
|
-
responses = []
|
63
|
-
(1..pagination_limit).each do |page|
|
64
|
-
res = search_with_page(page: page)
|
65
|
-
total = res["total"].to_i
|
66
|
-
|
67
|
-
responses << res
|
68
|
-
break if total <= page * PAGE_SIZE
|
69
|
-
|
70
|
-
# sleep #{interval} seconds to avoid the rate limitation (if it is set)
|
71
|
-
sleep_interval
|
72
|
-
end
|
73
|
-
responses
|
74
|
-
end
|
75
|
-
|
76
36
|
#
|
77
37
|
#
|
78
38
|
# @return [Mihari::Clients::BinaryEdge]
|
79
39
|
#
|
80
40
|
def client
|
81
|
-
@client ||= Clients::BinaryEdge.new(api_key: api_key)
|
41
|
+
@client ||= Clients::BinaryEdge.new(api_key: api_key, interval: interval)
|
82
42
|
end
|
83
43
|
end
|
84
44
|
end
|
@@ -27,25 +27,9 @@ module Mihari
|
|
27
27
|
# @return [Array<Mihari::Artifact>]
|
28
28
|
#
|
29
29
|
def artifacts
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
pagination_limit.times do
|
34
|
-
response = client.search(query, cursor: cursor)
|
35
|
-
artifacts << response.result.to_artifacts
|
36
|
-
cursor = response.result.links.next
|
37
|
-
# NOTE: Censys's search API is unstable recently
|
38
|
-
# it may returns empty links or empty string cursors
|
39
|
-
# - Empty links: "links": {}
|
40
|
-
# - Empty cursors: "links": { "next": "", "prev": "" }
|
41
|
-
# So it needs to check both cases
|
42
|
-
break if cursor.nil? || cursor.empty?
|
43
|
-
|
44
|
-
# sleep #{interval} seconds to avoid the rate limitation (if it is set)
|
45
|
-
sleep_interval
|
46
|
-
end
|
47
|
-
|
48
|
-
artifacts.flatten.uniq(&:data)
|
30
|
+
client.search_with_pagination(query, pagination_limit: pagination_limit).map do |res|
|
31
|
+
res.result.artifacts
|
32
|
+
end.flatten.uniq(&:data)
|
49
33
|
end
|
50
34
|
|
51
35
|
#
|
@@ -68,7 +52,7 @@ module Mihari
|
|
68
52
|
# @return [Mihari::Clients::Censys]
|
69
53
|
#
|
70
54
|
def client
|
71
|
-
@client ||= Clients::Censys.new(id: id, secret: secret)
|
55
|
+
@client ||= Clients::Censys.new(id: id, secret: secret, interval: interval)
|
72
56
|
end
|
73
57
|
|
74
58
|
#
|
@@ -32,9 +32,9 @@ module Mihari
|
|
32
32
|
def artifacts
|
33
33
|
case type
|
34
34
|
when "domain"
|
35
|
-
passive_dns_search
|
35
|
+
client.passive_dns_search query
|
36
36
|
when "hash"
|
37
|
-
passive_ssl_search
|
37
|
+
client.passive_ssl_search query
|
38
38
|
else
|
39
39
|
raise InvalidInputError, "#{@query}(type: #{@type || "unknown"}) is not supported."
|
40
40
|
end
|
@@ -54,30 +54,6 @@ module Mihari
|
|
54
54
|
@client ||= Clients::CIRCL.new(username: username, password: password)
|
55
55
|
end
|
56
56
|
|
57
|
-
#
|
58
|
-
# Passive DNS search
|
59
|
-
#
|
60
|
-
# @return [Array<String>]
|
61
|
-
#
|
62
|
-
def passive_dns_search
|
63
|
-
results = client.dns_query(query)
|
64
|
-
results.filter_map do |result|
|
65
|
-
type = result["rrtype"]
|
66
|
-
(type == "A") ? result["rdata"] : nil
|
67
|
-
end.uniq
|
68
|
-
end
|
69
|
-
|
70
|
-
#
|
71
|
-
# Passive SSL search
|
72
|
-
#
|
73
|
-
# @return [Array<String>]
|
74
|
-
#
|
75
|
-
def passive_ssl_search
|
76
|
-
result = client.ssl_cquery(query)
|
77
|
-
seen = result["seen"] || []
|
78
|
-
seen.uniq
|
79
|
-
end
|
80
|
-
|
81
57
|
def username?
|
82
58
|
!username.nil?
|
83
59
|
end
|
@@ -18,13 +18,8 @@ module Mihari
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def artifacts
|
21
|
-
|
22
|
-
|
23
|
-
values = result["name_value"].to_s.lines.map(&:chomp)
|
24
|
-
values.map do |value|
|
25
|
-
Artifact.new(data: value, source: source, metadata: result)
|
26
|
-
end
|
27
|
-
end.flatten
|
21
|
+
exclude = exclude_expired ? "expired" : nil
|
22
|
+
client.search(query, exclude: exclude)
|
28
23
|
end
|
29
24
|
|
30
25
|
private
|
@@ -35,16 +30,6 @@ module Mihari
|
|
35
30
|
def client
|
36
31
|
@client ||= Mihari::Clients::Crtsh.new
|
37
32
|
end
|
38
|
-
|
39
|
-
#
|
40
|
-
# Search
|
41
|
-
#
|
42
|
-
# @return [Array<Hash>]
|
43
|
-
#
|
44
|
-
def search
|
45
|
-
exclude = exclude_expired ? "expired" : nil
|
46
|
-
client.search(query, exclude: exclude)
|
47
|
-
end
|
48
33
|
end
|
49
34
|
end
|
50
35
|
end
|
@@ -21,9 +21,7 @@ module Mihari
|
|
21
21
|
def artifacts
|
22
22
|
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
23
23
|
|
24
|
-
|
25
|
-
fuzzy_domains = res["fuzzy_domains"] || []
|
26
|
-
domains = fuzzy_domains.map { |domain| domain["domain"] }
|
24
|
+
domains = client.fuzz(query)
|
27
25
|
Parallel.map(domains) do |domain|
|
28
26
|
resolvable?(domain) ? domain : nil
|
29
27
|
end.compact
|
@@ -3,8 +3,6 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Analyzers
|
5
5
|
class GreyNoise < Base
|
6
|
-
PAGE_SIZE = 10_000
|
7
|
-
|
8
6
|
# @return [String, nil]
|
9
7
|
attr_reader :api_key
|
10
8
|
|
@@ -20,7 +18,10 @@ module Mihari
|
|
20
18
|
end
|
21
19
|
|
22
20
|
def artifacts
|
23
|
-
client.
|
21
|
+
client.gnql_search_with_pagination(
|
22
|
+
query,
|
23
|
+
pagination_limit: pagination_limit
|
24
|
+
).map(&:artifacts).flatten
|
24
25
|
end
|
25
26
|
|
26
27
|
def configuration_keys
|
@@ -30,7 +31,7 @@ module Mihari
|
|
30
31
|
private
|
31
32
|
|
32
33
|
def client
|
33
|
-
@client ||= Clients::GreyNoise.new(api_key: api_key)
|
34
|
+
@client ||= Clients::GreyNoise.new(api_key: api_key, interval: interval)
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
@@ -30,25 +30,13 @@ module Mihari
|
|
30
30
|
# @return [Array<Mihari::Artifact>]
|
31
31
|
#
|
32
32
|
def artifacts
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
start_time: start_time.strftime("%Y-%m-%d"),
|
41
|
-
end_time: end_time.strftime("%Y-%m-%d")
|
42
|
-
)
|
43
|
-
|
44
|
-
artifacts << res.data.artifacts
|
45
|
-
|
46
|
-
break if res.data.list.length < PAGE_SIZE
|
47
|
-
|
48
|
-
sleep_interval
|
49
|
-
end
|
50
|
-
|
51
|
-
artifacts.flatten
|
33
|
+
client.search_with_pagination(
|
34
|
+
query,
|
35
|
+
start_time: start_time.strftime("%Y-%m-%d"),
|
36
|
+
end_time: end_time.strftime("%Y-%m-%d")
|
37
|
+
).map do |res|
|
38
|
+
res.data.artifacts
|
39
|
+
end.flatten
|
52
40
|
end
|
53
41
|
|
54
42
|
def configuration_keys
|
@@ -57,11 +45,8 @@ module Mihari
|
|
57
45
|
|
58
46
|
private
|
59
47
|
|
60
|
-
# @return [Integer]
|
61
|
-
PAGE_SIZE = 100
|
62
|
-
|
63
48
|
def client
|
64
|
-
@client ||= Clients::HunterHow.new(api_key: api_key)
|
49
|
+
@client ||= Clients::HunterHow.new(api_key: api_key, interval: interval)
|
65
50
|
end
|
66
51
|
end
|
67
52
|
end
|
@@ -20,10 +20,10 @@ module Mihari
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def artifacts
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
client.datascan_with_pagination(
|
24
|
+
query,
|
25
|
+
pagination_limit: pagination_limit
|
26
|
+
).map(&:artifacts).flatten
|
27
27
|
end
|
28
28
|
|
29
29
|
def configuration_keys
|
@@ -32,42 +32,8 @@ module Mihari
|
|
32
32
|
|
33
33
|
private
|
34
34
|
|
35
|
-
PAGE_SIZE = 10
|
36
|
-
|
37
35
|
def client
|
38
|
-
@client ||= Clients::Onyphe.new(api_key: api_key)
|
39
|
-
end
|
40
|
-
|
41
|
-
#
|
42
|
-
# Search with pagination
|
43
|
-
#
|
44
|
-
# @param [String] query
|
45
|
-
# @param [Integer] page
|
46
|
-
#
|
47
|
-
# @return [Structs::Onyphe::Response]
|
48
|
-
#
|
49
|
-
def search_with_page(query, page: 1)
|
50
|
-
client.datascan(query, page: page)
|
51
|
-
end
|
52
|
-
|
53
|
-
#
|
54
|
-
# Search
|
55
|
-
#
|
56
|
-
# @return [Array<Structs::Onyphe::Response>]
|
57
|
-
#
|
58
|
-
def search
|
59
|
-
responses = []
|
60
|
-
(1..pagination_limit).each do |page|
|
61
|
-
res = search_with_page(query, page: page)
|
62
|
-
responses << res
|
63
|
-
|
64
|
-
total = res.total
|
65
|
-
break if total <= page * PAGE_SIZE
|
66
|
-
|
67
|
-
# sleep #{interval} seconds to avoid the rate limitation (if it is set)
|
68
|
-
sleep_interval
|
69
|
-
end
|
70
|
-
responses
|
36
|
+
@client ||= Clients::Onyphe.new(api_key: api_key, interval: interval)
|
71
37
|
end
|
72
38
|
end
|
73
39
|
end
|
data/lib/mihari/analyzers/otx.rb
CHANGED
@@ -27,9 +27,9 @@ module Mihari
|
|
27
27
|
def artifacts
|
28
28
|
case type
|
29
29
|
when "domain"
|
30
|
-
domain_search
|
30
|
+
client.domain_search(query)
|
31
31
|
when "ip"
|
32
|
-
ip_search
|
32
|
+
client.ip_search(query)
|
33
33
|
else
|
34
34
|
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
35
35
|
end
|
@@ -53,42 +53,6 @@ module Mihari
|
|
53
53
|
def valid_type?
|
54
54
|
%w[ip domain].include? type
|
55
55
|
end
|
56
|
-
|
57
|
-
#
|
58
|
-
# Domain search
|
59
|
-
#
|
60
|
-
# @return [Array<String>]
|
61
|
-
#
|
62
|
-
def domain_search
|
63
|
-
res = client.query_by_domain(query)
|
64
|
-
return [] if res.nil?
|
65
|
-
|
66
|
-
records = res["passive_dns"] || []
|
67
|
-
records.filter_map do |record|
|
68
|
-
record_type = record["record_type"]
|
69
|
-
address = record["address"]
|
70
|
-
|
71
|
-
address if record_type == "A"
|
72
|
-
end.uniq
|
73
|
-
end
|
74
|
-
|
75
|
-
#
|
76
|
-
# IP search
|
77
|
-
#
|
78
|
-
# @return [Array<String>]
|
79
|
-
#
|
80
|
-
def ip_search
|
81
|
-
res = client.query_by_ip(query)
|
82
|
-
return [] if res.nil?
|
83
|
-
|
84
|
-
records = res["passive_dns"] || []
|
85
|
-
records.filter_map do |record|
|
86
|
-
record_type = record["record_type"]
|
87
|
-
hostname = record["hostname"]
|
88
|
-
|
89
|
-
hostname if record_type == "A"
|
90
|
-
end.uniq
|
91
|
-
end
|
92
56
|
end
|
93
57
|
end
|
94
58
|
end
|
@@ -32,11 +32,11 @@ module Mihari
|
|
32
32
|
def artifacts
|
33
33
|
case type
|
34
34
|
when "domain", "ip"
|
35
|
-
passive_dns_search
|
35
|
+
client.passive_dns_search query
|
36
36
|
when "mail"
|
37
|
-
reverse_whois_search
|
37
|
+
client.reverse_whois_search query
|
38
38
|
when "hash"
|
39
|
-
ssl_search
|
39
|
+
client.ssl_search query
|
40
40
|
else
|
41
41
|
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
42
42
|
end
|
@@ -65,44 +65,6 @@ module Mihari
|
|
65
65
|
%w[ip domain mail hash].include? type
|
66
66
|
end
|
67
67
|
|
68
|
-
#
|
69
|
-
# Passive DNS search
|
70
|
-
#
|
71
|
-
# @return [Array<String>]
|
72
|
-
#
|
73
|
-
def passive_dns_search
|
74
|
-
res = client.passive_dns_search(query)
|
75
|
-
res["results"] || []
|
76
|
-
end
|
77
|
-
|
78
|
-
#
|
79
|
-
# Reverse whois search
|
80
|
-
#
|
81
|
-
# @return [Array<Mihari::Artifact>]
|
82
|
-
#
|
83
|
-
def reverse_whois_search
|
84
|
-
res = client.reverse_whois_search(query: query, field: "email")
|
85
|
-
results = res["results"] || []
|
86
|
-
results.map do |result|
|
87
|
-
data = result["domain"]
|
88
|
-
Artifact.new(data: data, source: source, metadata: result)
|
89
|
-
end.flatten
|
90
|
-
end
|
91
|
-
|
92
|
-
#
|
93
|
-
# Passive SSL search
|
94
|
-
#
|
95
|
-
# @return [Array<Mihari::Artifact>]
|
96
|
-
#
|
97
|
-
def ssl_search
|
98
|
-
res = client.ssl_search(query)
|
99
|
-
results = res["results"] || []
|
100
|
-
results.map do |result|
|
101
|
-
data = result["ipAddresses"]
|
102
|
-
data.map { |d| Artifact.new(data: d, source: source, metadata: result) }
|
103
|
-
end.flatten
|
104
|
-
end
|
105
|
-
|
106
68
|
def username?
|
107
69
|
!username.nil?
|
108
70
|
end
|
@@ -30,11 +30,11 @@ module Mihari
|
|
30
30
|
def artifacts
|
31
31
|
case type
|
32
32
|
when "domain"
|
33
|
-
domain_search
|
33
|
+
client.domain_search query
|
34
34
|
when "ip"
|
35
|
-
ip_search
|
35
|
+
client.ip_search query
|
36
36
|
when "mail"
|
37
|
-
mail_search
|
37
|
+
client.mail_search query
|
38
38
|
else
|
39
39
|
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
40
40
|
end
|
@@ -58,44 +58,6 @@ module Mihari
|
|
58
58
|
def valid_type?
|
59
59
|
%w[ip domain mail].include? type
|
60
60
|
end
|
61
|
-
|
62
|
-
#
|
63
|
-
# Domain search
|
64
|
-
#
|
65
|
-
# @return [Array<String>]
|
66
|
-
#
|
67
|
-
def domain_search
|
68
|
-
records = client.get_all_dns_history(query, type: "a")
|
69
|
-
records.map do |record|
|
70
|
-
(record["values"] || []).map { |value| value["ip"] }
|
71
|
-
end.flatten.compact.uniq
|
72
|
-
end
|
73
|
-
|
74
|
-
#
|
75
|
-
# IP search
|
76
|
-
#
|
77
|
-
# @return [Array<Mihari::Artifact>]
|
78
|
-
#
|
79
|
-
def ip_search
|
80
|
-
records = client.search_by_ip(query)
|
81
|
-
records.filter_map do |record|
|
82
|
-
data = record["hostname"]
|
83
|
-
Artifact.new(data: data, source: source, metadata: record)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
#
|
88
|
-
# Mail search
|
89
|
-
#
|
90
|
-
# @return [Array<String>]
|
91
|
-
#
|
92
|
-
def mail_search
|
93
|
-
records = client.search_by_mail(query)
|
94
|
-
records.filter_map do |record|
|
95
|
-
data = record["hostname"]
|
96
|
-
Artifact.new(data: data, source: source, metadata: record)
|
97
|
-
end
|
98
|
-
end
|
99
61
|
end
|
100
62
|
end
|
101
63
|
end
|
@@ -18,10 +18,10 @@ module Mihari
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def artifacts
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
client.search_with_pagination(
|
22
|
+
query,
|
23
|
+
pagination_limit: pagination_limit
|
24
|
+
).map(&:artifacts).flatten.uniq(&:data)
|
25
25
|
end
|
26
26
|
|
27
27
|
def configuration_keys
|
@@ -30,43 +30,11 @@ module Mihari
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
PAGE_SIZE = 100
|
34
|
-
|
35
|
-
def client
|
36
|
-
@client ||= Clients::Shodan.new(api_key: api_key)
|
37
|
-
end
|
38
|
-
|
39
|
-
#
|
40
|
-
# Search with pagination
|
41
|
-
#
|
42
|
-
# @param [Integer] page
|
43
|
-
#
|
44
|
-
# @return [Structs::Shodan::Result]
|
45
|
-
#
|
46
|
-
def search_with_page(page: 1)
|
47
|
-
client.search(query, page: page)
|
48
|
-
end
|
49
|
-
|
50
33
|
#
|
51
|
-
#
|
34
|
+
# @return [Clients::Shodan]
|
52
35
|
#
|
53
|
-
|
54
|
-
|
55
|
-
def search
|
56
|
-
responses = []
|
57
|
-
(1..pagination_limit).each do |page|
|
58
|
-
res = search_with_page(page: page)
|
59
|
-
responses << res
|
60
|
-
break if res.total <= page * PAGE_SIZE
|
61
|
-
|
62
|
-
# sleep #{interval} seconds to avoid the rate limitation (if it is set)
|
63
|
-
sleep_interval
|
64
|
-
rescue JSON::ParserError
|
65
|
-
# ignore JSON::ParserError
|
66
|
-
# ref. https://github.com/ninoseki/mihari/issues/197
|
67
|
-
next
|
68
|
-
end
|
69
|
-
responses
|
36
|
+
def client
|
37
|
+
@client ||= Clients::Shodan.new(api_key: api_key, interval: interval)
|
70
38
|
end
|
71
39
|
end
|
72
40
|
end
|
@@ -4,7 +4,6 @@ module Mihari
|
|
4
4
|
module Analyzers
|
5
5
|
class Urlscan < Base
|
6
6
|
SUPPORTED_DATA_TYPES = %w[url domain ip].freeze
|
7
|
-
SIZE = 1000
|
8
7
|
|
9
8
|
# @return [String, nil]
|
10
9
|
attr_reader :api_key
|
@@ -30,9 +29,8 @@ module Mihari
|
|
30
29
|
end
|
31
30
|
|
32
31
|
def artifacts
|
33
|
-
responses = search
|
34
32
|
# @type [Array<Mihari::Artifact>]
|
35
|
-
artifacts =
|
33
|
+
artifacts = client.search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
|
36
34
|
|
37
35
|
artifacts.select do |artifact|
|
38
36
|
allowed_data_types.include? artifact.data_type
|
@@ -46,41 +44,7 @@ module Mihari
|
|
46
44
|
private
|
47
45
|
|
48
46
|
def client
|
49
|
-
@client ||= Clients::UrlScan.new(api_key: api_key)
|
50
|
-
end
|
51
|
-
|
52
|
-
#
|
53
|
-
# Search with search_after option
|
54
|
-
#
|
55
|
-
# @return [Structs::Urlscan::Response]
|
56
|
-
#
|
57
|
-
def search_with_search_after(search_after: nil)
|
58
|
-
res = client.search(query, size: SIZE, search_after: search_after)
|
59
|
-
Structs::Urlscan::Response.from_dynamic! res
|
60
|
-
end
|
61
|
-
|
62
|
-
#
|
63
|
-
# Search
|
64
|
-
#
|
65
|
-
# @return [Array<Structs::Urlscan::Response>]
|
66
|
-
#
|
67
|
-
def search
|
68
|
-
responses = []
|
69
|
-
|
70
|
-
search_after = nil
|
71
|
-
pagination_limit.times do
|
72
|
-
res = search_with_search_after(search_after: search_after)
|
73
|
-
responses << res
|
74
|
-
|
75
|
-
break if res.results.length < SIZE
|
76
|
-
|
77
|
-
search_after = res.results.last.sort.join(",")
|
78
|
-
|
79
|
-
# sleep #{interval} seconds to avoid the rate limitation (if it is set)
|
80
|
-
sleep_interval
|
81
|
-
end
|
82
|
-
|
83
|
-
responses
|
47
|
+
@client ||= Clients::UrlScan.new(api_key: api_key, interval: interval)
|
84
48
|
end
|
85
49
|
|
86
50
|
#
|
@@ -18,7 +18,7 @@ module Mihari
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def artifacts
|
21
|
-
|
21
|
+
client.intel_search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
|
22
22
|
end
|
23
23
|
|
24
24
|
def configuration_keys
|
@@ -33,30 +33,7 @@ module Mihari
|
|
33
33
|
# @return [::VirusTotal::API]
|
34
34
|
#
|
35
35
|
def client
|
36
|
-
@client = Clients::VirusTotal.new(api_key: api_key)
|
37
|
-
end
|
38
|
-
|
39
|
-
#
|
40
|
-
# Search with cursor
|
41
|
-
#
|
42
|
-
# @return [Array<Structs::VirusTotalIntelligence::Response>]
|
43
|
-
#
|
44
|
-
def search_with_cursor
|
45
|
-
cursor = nil
|
46
|
-
responses = []
|
47
|
-
|
48
|
-
pagination_limit.times do
|
49
|
-
response = Structs::VirusTotalIntelligence::Response.from_dynamic!(client.intel_search(query,
|
50
|
-
cursor: cursor))
|
51
|
-
responses << response
|
52
|
-
break if response.meta.cursor.nil?
|
53
|
-
|
54
|
-
cursor = response.meta.cursor
|
55
|
-
# sleep #{interval} seconds to avoid the rate limitation (if it is set)
|
56
|
-
sleep_interval
|
57
|
-
end
|
58
|
-
|
59
|
-
responses
|
36
|
+
@client = Clients::VirusTotal.new(api_key: api_key, interval: interval)
|
60
37
|
end
|
61
38
|
end
|
62
39
|
end
|