mihari 5.4.2 → 5.4.4
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 +2399 -1504
- data/frontend/package.json +22 -22
- data/lib/mihari/analyzers/base.rb +25 -14
- data/lib/mihari/analyzers/binaryedge.rb +2 -48
- data/lib/mihari/analyzers/censys.rb +4 -20
- data/lib/mihari/analyzers/circl.rb +3 -27
- data/lib/mihari/analyzers/crtsh.rb +2 -17
- data/lib/mihari/analyzers/dnstwister.rb +2 -4
- 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 +3 -39
- data/lib/mihari/analyzers/passivetotal.rb +4 -42
- data/lib/mihari/analyzers/pulsedive.rb +1 -1
- data/lib/mihari/analyzers/rule.rb +18 -13
- data/lib/mihari/analyzers/securitytrails.rb +4 -42
- data/lib/mihari/analyzers/shodan.rb +7 -39
- data/lib/mihari/analyzers/urlscan.rb +3 -39
- data/lib/mihari/analyzers/virustotal.rb +1 -1
- data/lib/mihari/analyzers/virustotal_intelligence.rb +2 -25
- data/lib/mihari/analyzers/zoomeye.rb +18 -84
- data/lib/mihari/clients/base.rb +9 -1
- data/lib/mihari/clients/binaryedge.rb +26 -4
- data/lib/mihari/clients/censys.rb +32 -2
- data/lib/mihari/clients/circl.rb +28 -1
- data/lib/mihari/clients/crtsh.rb +7 -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/publsedive.rb +1 -1
- data/lib/mihari/clients/securitytrails.rb +44 -0
- data/lib/mihari/clients/shodan.rb +31 -3
- 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 -13
- data/lib/mihari/commands/rule.rb +11 -7
- data/lib/mihari/commands/search.rb +54 -22
- data/lib/mihari/commands/web.rb +1 -1
- data/lib/mihari/config.rb +6 -1
- data/lib/mihari/emitters/base.rb +9 -3
- data/lib/mihari/emitters/slack.rb +1 -1
- data/lib/mihari/enrichers/base.rb +13 -0
- data/lib/mihari/enrichers/google_public_dns.rb +16 -1
- data/lib/mihari/enrichers/ipinfo.rb +9 -13
- data/lib/mihari/enrichers/shodan.rb +1 -2
- data/lib/mihari/enrichers/whois.rb +2 -2
- data/lib/mihari/errors.rb +16 -10
- data/lib/mihari/feed/parser.rb +2 -2
- data/lib/mihari/models/artifact.rb +1 -1
- data/lib/mihari/models/autonomous_system.rb +11 -5
- data/lib/mihari/models/cpe.rb +10 -4
- data/lib/mihari/models/dns.rb +11 -16
- data/lib/mihari/models/geolocation.rb +11 -5
- data/lib/mihari/models/port.rb +10 -4
- data/lib/mihari/models/reverse_dns.rb +10 -4
- data/lib/mihari/models/whois.rb +4 -1
- data/lib/mihari/schemas/analyzer.rb +1 -0
- data/lib/mihari/services/alert_builder.rb +43 -0
- data/lib/mihari/services/alert_proxy.rb +7 -25
- data/lib/mihari/services/alert_runner.rb +9 -0
- data/lib/mihari/services/rule_builder.rb +47 -0
- data/lib/mihari/services/rule_proxy.rb +5 -61
- data/lib/mihari/services/rule_runner.rb +9 -4
- data/lib/mihari/structs/binaryedge.rb +89 -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 +7 -6
- data/lib/mihari/structs/urlscan.rb +4 -6
- data/lib/mihari/structs/virustotal_intelligence.rb +4 -6
- data/lib/mihari/type_checker.rb +1 -1
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/endpoints/alerts.rb +33 -15
- data/lib/mihari/web/endpoints/artifacts.rb +53 -25
- data/lib/mihari/web/endpoints/configs.rb +2 -2
- data/lib/mihari/web/endpoints/ip_addresses.rb +3 -5
- data/lib/mihari/web/endpoints/rules.rb +97 -71
- data/lib/mihari/web/endpoints/tags.rb +15 -5
- data/lib/mihari/web/public/assets/index-ef33a6cd.js +1738 -0
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +419 -382
- data/lib/mihari.rb +4 -0
- data/mihari.gemspec +10 -9
- metadata +38 -21
- data/lib/mihari/web/public/assets/index-4d7eda9f.js +0 -1738
data/frontend/package.json
CHANGED
@@ -17,15 +17,15 @@
|
|
17
17
|
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
18
18
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
19
19
|
"@fortawesome/vue-fontawesome": "^3.0.3",
|
20
|
-
"@vueuse/core": "^10.
|
21
|
-
"@vueuse/router": "^10.
|
22
|
-
"ace-builds": "^1.
|
23
|
-
"axios": "^1.
|
20
|
+
"@vueuse/core": "^10.4.1",
|
21
|
+
"@vueuse/router": "^10.4.1",
|
22
|
+
"ace-builds": "^1.28.0",
|
23
|
+
"axios": "^1.5.0",
|
24
24
|
"bulma": "^0.9.4",
|
25
25
|
"bulma-helpers": "^0.4.3",
|
26
|
-
"dayjs": "^1.11.
|
26
|
+
"dayjs": "^1.11.10",
|
27
27
|
"font-awesome-animation": "^1.1.1",
|
28
|
-
"js-sha256": "^0.
|
28
|
+
"js-sha256": "^0.10.1",
|
29
29
|
"truncate": "^3.0.0",
|
30
30
|
"ts-dedent": "^2.2.0",
|
31
31
|
"url-parse": "^1.5.10",
|
@@ -33,24 +33,24 @@
|
|
33
33
|
"vue": "^3.3.4",
|
34
34
|
"vue-concurrency": "4.0.1",
|
35
35
|
"vue-json-pretty": "^2.2.4",
|
36
|
-
"vue-router": "^4.2.
|
36
|
+
"vue-router": "^4.2.5",
|
37
37
|
"vue3-ace-editor": "^2.2.3"
|
38
38
|
},
|
39
39
|
"devDependencies": {
|
40
|
-
"@redocly/cli": "1.0
|
41
|
-
"@rushstack/eslint-patch": "^1.
|
42
|
-
"@tsconfig/node20": "^20.1.
|
43
|
-
"@types/jsdom": "^21.1.
|
44
|
-
"@types/node": "^20.
|
45
|
-
"@types/url-parse": "^1.4.
|
46
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
47
|
-
"@typescript-eslint/parser": "^6.
|
48
|
-
"@vitejs/plugin-vue": "^4.3.
|
40
|
+
"@redocly/cli": "1.2.0",
|
41
|
+
"@rushstack/eslint-patch": "^1.4.0",
|
42
|
+
"@tsconfig/node20": "^20.1.2",
|
43
|
+
"@types/jsdom": "^21.1.3",
|
44
|
+
"@types/node": "^20.6.4",
|
45
|
+
"@types/url-parse": "^1.4.9",
|
46
|
+
"@typescript-eslint/eslint-plugin": "^6.7.2",
|
47
|
+
"@typescript-eslint/parser": "^6.7.2",
|
48
|
+
"@vitejs/plugin-vue": "^4.3.4",
|
49
49
|
"@vue/eslint-config-prettier": "^8.0.0",
|
50
|
-
"@vue/eslint-config-typescript": "^
|
50
|
+
"@vue/eslint-config-typescript": "^12.0.0",
|
51
51
|
"@vue/test-utils": "2.4.1",
|
52
52
|
"@vue/tsconfig": "^0.4.0",
|
53
|
-
"eslint": "^8.
|
53
|
+
"eslint": "^8.50.0",
|
54
54
|
"eslint-config-prettier": "^9.0.0",
|
55
55
|
"eslint-plugin-prettier": "^5.0.0",
|
56
56
|
"eslint-plugin-simple-import-sort": "^10.0.0",
|
@@ -58,10 +58,10 @@
|
|
58
58
|
"husky": "^8.0.3",
|
59
59
|
"jsdom": "^22.1.0",
|
60
60
|
"npm-run-all": "^4.1.5",
|
61
|
-
"prettier": "^3.0.
|
62
|
-
"typescript": "~5.
|
61
|
+
"prettier": "^3.0.3",
|
62
|
+
"typescript": "~5.2.2",
|
63
63
|
"vite": "^4.4.9",
|
64
|
-
"vitest": "^0.34.
|
65
|
-
"vue-tsc": "^1.8.
|
64
|
+
"vitest": "^0.34.5",
|
65
|
+
"vue-tsc": "^1.8.13"
|
66
66
|
}
|
67
67
|
}
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module Mihari
|
4
4
|
module Analyzers
|
5
5
|
class Base
|
6
|
+
include Dry::Monads[:result, :try]
|
7
|
+
|
6
8
|
include Mixins::Configurable
|
7
9
|
include Mixins::Retriable
|
8
10
|
|
@@ -25,28 +27,38 @@ module Mihari
|
|
25
27
|
# @return [Integer, nil]
|
26
28
|
#
|
27
29
|
def interval
|
28
|
-
|
30
|
+
options[:interval]
|
29
31
|
end
|
30
32
|
|
31
33
|
#
|
32
34
|
# @return [Integer]
|
33
35
|
#
|
34
36
|
def retry_interval
|
35
|
-
|
37
|
+
options[:retry_interval] || Mihari.config.retry_interval
|
36
38
|
end
|
37
39
|
|
38
40
|
#
|
39
41
|
# @return [Integer]
|
40
42
|
#
|
41
43
|
def retry_times
|
42
|
-
|
44
|
+
options[:retry_times] || Mihari.config.retry_times
|
43
45
|
end
|
44
46
|
|
45
47
|
#
|
46
48
|
# @return [Integer]
|
47
49
|
#
|
48
50
|
def pagination_limit
|
49
|
-
|
51
|
+
options[:pagination_limit] || Mihari.config.pagination_limit
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# @return [Boolean]
|
56
|
+
#
|
57
|
+
def ignore_error?
|
58
|
+
ignore_error = options[:ignore_error]
|
59
|
+
return ignore_error unless ignore_error.nil?
|
60
|
+
|
61
|
+
Mihari.config.ignore_error
|
50
62
|
end
|
51
63
|
|
52
64
|
# @return [Array<String>, Array<Mihari::Artifact>]
|
@@ -63,7 +75,7 @@ module Mihari
|
|
63
75
|
#
|
64
76
|
def normalized_artifacts
|
65
77
|
retry_on_error(times: retry_times, interval: retry_interval) do
|
66
|
-
|
78
|
+
artifacts.compact.sort.map do |artifact|
|
67
79
|
# No need to set data_type manually
|
68
80
|
# It is set automatically in #initialize
|
69
81
|
artifact = artifact.is_a?(Artifact) ? artifact : Artifact.new(data: artifact)
|
@@ -73,6 +85,13 @@ module Mihari
|
|
73
85
|
end
|
74
86
|
end
|
75
87
|
|
88
|
+
#
|
89
|
+
# @return [Dry::Monads::Result::Success<Array<Mihari::Artifact>>, Dry::Monads::Result::Failure]
|
90
|
+
#
|
91
|
+
def result
|
92
|
+
Try[StandardError] { normalized_artifacts }.to_result
|
93
|
+
end
|
94
|
+
|
76
95
|
# @return [String]
|
77
96
|
def class_name
|
78
97
|
self.class.to_s.split("::").last
|
@@ -80,12 +99,6 @@ module Mihari
|
|
80
99
|
|
81
100
|
alias_method :source, :class_name
|
82
101
|
|
83
|
-
private
|
84
|
-
|
85
|
-
def sleep_interval
|
86
|
-
sleep(interval) if interval
|
87
|
-
end
|
88
|
-
|
89
102
|
class << self
|
90
103
|
#
|
91
104
|
# Initialize an analyzer by query params
|
@@ -101,9 +114,7 @@ module Mihari
|
|
101
114
|
query = copied[:query]
|
102
115
|
|
103
116
|
# delete analyzer and query
|
104
|
-
%i[analyzer query].each
|
105
|
-
copied.delete key
|
106
|
-
end
|
117
|
+
%i[analyzer query].each { |key| copied.delete key }
|
107
118
|
|
108
119
|
copied[:options] = copied[:options] || nil
|
109
120
|
|
@@ -18,16 +18,7 @@ module Mihari
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def artifacts
|
21
|
-
|
22
|
-
return [] unless results || results.empty?
|
23
|
-
|
24
|
-
results.map do |result|
|
25
|
-
events = result["events"] || []
|
26
|
-
events.filter_map do |event|
|
27
|
-
data = event.dig("target", "ip")
|
28
|
-
data.nil? ? nil : Artifact.new(data: data, source: source, metadata: event)
|
29
|
-
end
|
30
|
-
end.flatten
|
21
|
+
client.search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
|
31
22
|
end
|
32
23
|
|
33
24
|
def configuration_keys
|
@@ -36,49 +27,12 @@ module Mihari
|
|
36
27
|
|
37
28
|
private
|
38
29
|
|
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
30
|
#
|
77
31
|
#
|
78
32
|
# @return [Mihari::Clients::BinaryEdge]
|
79
33
|
#
|
80
34
|
def client
|
81
|
-
@client ||= Clients::BinaryEdge.new(api_key: api_key)
|
35
|
+
@client ||= Clients::BinaryEdge.new(api_key: api_key, interval: interval)
|
82
36
|
end
|
83
37
|
end
|
84
38
|
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,11 +32,11 @@ 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
|
-
raise
|
39
|
+
raise ValueError, "#{@query}(type: #{@type || "unknown"}) is not supported."
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
@@ -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
|
@@ -19,11 +19,9 @@ module Mihari
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def artifacts
|
22
|
-
raise
|
22
|
+
raise ValueError, "#{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,11 +27,11 @@ 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
|
-
raise
|
34
|
+
raise ValueError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -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,13 +32,13 @@ 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
|
-
raise
|
41
|
+
raise ValueError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -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
|
@@ -25,7 +25,7 @@ module Mihari
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def artifacts
|
28
|
-
raise
|
28
|
+
raise ValueError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
29
29
|
|
30
30
|
indicator = client.get_indicator(query)
|
31
31
|
iid = indicator["iid"]
|