mihari 5.4.2 → 5.4.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/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/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/web.rb +1 -1
- data/lib/mihari/config.rb +1 -1
- 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/mihari.gemspec +7 -7
- metadata +16 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 53a27dd7a778ef0678eddbf4f48f2ba19c476873a46a2337432d55b178321c70
|
|
4
|
+
data.tar.gz: 2518b10896081dcdb329f0f3caec21093d6dd980efcec545ed5f56a1bc569e49
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '085565b584d7e0c888fca6d4ba9ddb1c2f462201243df060e157ab1f5ec70c12261475af8c9304ffe21ead8cc39398dbb37e7e438ab3bad9ab28bb99aa4d760b'
|
|
7
|
+
data.tar.gz: e4964d0298b5dc0c49402cd0b04762b3c17e4b34d028ead1c3076b7ed7adfc7b4c9c40591559e1b2822f3c69e7e8e9e8d35b861ddfd670cf9bb071c103b607a1
|
|
@@ -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
|