mihari 5.1.2 → 5.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/mihari/analyzers/censys.rb +6 -15
- data/lib/mihari/analyzers/circl.rb +8 -17
- data/lib/mihari/analyzers/dnstwister.rb +8 -17
- data/lib/mihari/analyzers/otx.rb +8 -17
- data/lib/mihari/analyzers/passivetotal.rb +10 -19
- data/lib/mihari/analyzers/pulsedive.rb +0 -1
- data/lib/mihari/analyzers/securitytrails.rb +10 -19
- data/lib/mihari/analyzers/shodan.rb +0 -11
- data/lib/mihari/analyzers/urlscan.rb +3 -4
- data/lib/mihari/analyzers/virustotal_intelligence.rb +1 -4
- data/lib/mihari/clients/misp.rb +2 -0
- data/lib/mihari/emitters/misp.rb +4 -4
- data/lib/mihari/http.rb +14 -18
- data/lib/mihari/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 52425b89ab9cc2d25fcfd1391661446b938b35596fb5b3a68647f90038a3f848
|
4
|
+
data.tar.gz: 3ae2917e1de1e89e08089e40629eb8652a173cf03dd790137b55c1eb80d35706
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f06c60e30e815c2634c6309b86b323199393d3c6b02e416244d884c60916584fdbe2250a0366ddf002957b240eadce64c2dc25b740ce80ecce126a78eab29b87
|
7
|
+
data.tar.gz: 2e2e71da88d3ba33b58861b97b3987fb8f64fe581cc7f15cf488227df4621ec03d3c91968e126f787c3b23c65141a81817d4b0785f5aa9065e6fcfa396a99ed5
|
@@ -27,21 +27,6 @@ module Mihari
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def artifacts
|
30
|
-
search
|
31
|
-
end
|
32
|
-
|
33
|
-
def configured?
|
34
|
-
configuration_keys.all? { |key| Mihari.config.send(key) } || (id? && secret?)
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
#
|
40
|
-
# Search
|
41
|
-
#
|
42
|
-
# @return [Array<String>]
|
43
|
-
#
|
44
|
-
def search
|
45
30
|
artifacts = []
|
46
31
|
|
47
32
|
cursor = nil
|
@@ -58,6 +43,12 @@ module Mihari
|
|
58
43
|
artifacts.flatten.uniq(&:data)
|
59
44
|
end
|
60
45
|
|
46
|
+
def configured?
|
47
|
+
configuration_keys.all? { |key| Mihari.config.send(key) } || (id? && secret?)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
61
52
|
def configuration_keys
|
62
53
|
%w[censys_id censys_secret]
|
63
54
|
end
|
@@ -30,7 +30,14 @@ module Mihari
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def artifacts
|
33
|
-
|
33
|
+
case type
|
34
|
+
when "domain"
|
35
|
+
passive_dns_search
|
36
|
+
when "hash"
|
37
|
+
passive_ssl_search
|
38
|
+
else
|
39
|
+
raise InvalidInputError, "#{@query}(type: #{@type || "unknown"}) is not supported."
|
40
|
+
end
|
34
41
|
end
|
35
42
|
|
36
43
|
def configured?
|
@@ -47,22 +54,6 @@ module Mihari
|
|
47
54
|
@client ||= Clients::CIRCL.new(username: username, password: password)
|
48
55
|
end
|
49
56
|
|
50
|
-
#
|
51
|
-
# Passive DNS/SSL search
|
52
|
-
#
|
53
|
-
# @return [Array<String>]
|
54
|
-
#
|
55
|
-
def search
|
56
|
-
case @type
|
57
|
-
when "domain"
|
58
|
-
passive_dns_search
|
59
|
-
when "hash"
|
60
|
-
passive_ssl_search
|
61
|
-
else
|
62
|
-
raise InvalidInputError, "#{@query}(type: #{@type || "unknown"}) is not supported."
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
57
|
#
|
67
58
|
# Passive DNS search
|
68
59
|
#
|
@@ -21,7 +21,14 @@ module Mihari
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def artifacts
|
24
|
-
|
24
|
+
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
25
|
+
|
26
|
+
res = client.fuzz(query)
|
27
|
+
fuzzy_domains = res["fuzzy_domains"] || []
|
28
|
+
domains = fuzzy_domains.map { |domain| domain["domain"] }
|
29
|
+
Parallel.map(domains) do |domain|
|
30
|
+
resolvable?(domain) ? domain : nil
|
31
|
+
end.compact
|
25
32
|
end
|
26
33
|
|
27
34
|
private
|
@@ -52,22 +59,6 @@ module Mihari
|
|
52
59
|
rescue Resolv::ResolvError => _e
|
53
60
|
false
|
54
61
|
end
|
55
|
-
|
56
|
-
#
|
57
|
-
# Search
|
58
|
-
#
|
59
|
-
# @return [Array<String>]
|
60
|
-
#
|
61
|
-
def search
|
62
|
-
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
63
|
-
|
64
|
-
res = client.fuzz(query)
|
65
|
-
fuzzy_domains = res["fuzzy_domains"] || []
|
66
|
-
domains = fuzzy_domains.map { |domain| domain["domain"] }
|
67
|
-
Parallel.map(domains) do |domain|
|
68
|
-
resolvable?(domain) ? domain : nil
|
69
|
-
end.compact
|
70
|
-
end
|
71
62
|
end
|
72
63
|
end
|
73
64
|
end
|
data/lib/mihari/analyzers/otx.rb
CHANGED
@@ -26,7 +26,14 @@ module Mihari
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def artifacts
|
29
|
-
|
29
|
+
case type
|
30
|
+
when "domain"
|
31
|
+
domain_search
|
32
|
+
when "ip"
|
33
|
+
ip_search
|
34
|
+
else
|
35
|
+
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
36
|
+
end
|
30
37
|
end
|
31
38
|
|
32
39
|
private
|
@@ -48,22 +55,6 @@ module Mihari
|
|
48
55
|
%w[ip domain].include? type
|
49
56
|
end
|
50
57
|
|
51
|
-
#
|
52
|
-
# IP/domain search
|
53
|
-
#
|
54
|
-
# @return [Array<String>]
|
55
|
-
#
|
56
|
-
def search
|
57
|
-
case type
|
58
|
-
when "domain"
|
59
|
-
domain_search
|
60
|
-
when "ip"
|
61
|
-
ip_search
|
62
|
-
else
|
63
|
-
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
58
|
#
|
68
59
|
# Domain search
|
69
60
|
#
|
@@ -30,7 +30,16 @@ module Mihari
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def artifacts
|
33
|
-
|
33
|
+
case type
|
34
|
+
when "domain", "ip"
|
35
|
+
passive_dns_search
|
36
|
+
when "mail"
|
37
|
+
reverse_whois_search
|
38
|
+
when "hash"
|
39
|
+
ssl_search
|
40
|
+
else
|
41
|
+
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
42
|
+
end
|
34
43
|
end
|
35
44
|
|
36
45
|
def configured?
|
@@ -56,24 +65,6 @@ module Mihari
|
|
56
65
|
%w[ip domain mail hash].include? type
|
57
66
|
end
|
58
67
|
|
59
|
-
#
|
60
|
-
# Passive DNS/SSL, reverse whois search
|
61
|
-
#
|
62
|
-
# @return [Array<String>]
|
63
|
-
#
|
64
|
-
def search
|
65
|
-
case type
|
66
|
-
when "domain", "ip"
|
67
|
-
passive_dns_search
|
68
|
-
when "mail"
|
69
|
-
reverse_whois_search
|
70
|
-
when "hash"
|
71
|
-
ssl_search
|
72
|
-
else
|
73
|
-
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
68
|
#
|
78
69
|
# Passive DNS search
|
79
70
|
#
|
@@ -26,7 +26,16 @@ module Mihari
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def artifacts
|
29
|
-
|
29
|
+
case type
|
30
|
+
when "domain"
|
31
|
+
domain_search
|
32
|
+
when "ip"
|
33
|
+
ip_search
|
34
|
+
when "mail"
|
35
|
+
mail_search
|
36
|
+
else
|
37
|
+
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
38
|
+
end
|
30
39
|
end
|
31
40
|
|
32
41
|
private
|
@@ -48,24 +57,6 @@ module Mihari
|
|
48
57
|
%w[ip domain mail].include? type
|
49
58
|
end
|
50
59
|
|
51
|
-
#
|
52
|
-
# IP/domain/mail search
|
53
|
-
#
|
54
|
-
# @return [Array<String>, Array<Mihari::Artifact>]
|
55
|
-
#
|
56
|
-
def search
|
57
|
-
case type
|
58
|
-
when "domain"
|
59
|
-
domain_search
|
60
|
-
when "ip"
|
61
|
-
ip_search
|
62
|
-
when "mail"
|
63
|
-
mail_search
|
64
|
-
else
|
65
|
-
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
60
|
#
|
70
61
|
# Domain search
|
71
62
|
#
|
@@ -73,17 +73,6 @@ module Mihari
|
|
73
73
|
end
|
74
74
|
responses
|
75
75
|
end
|
76
|
-
|
77
|
-
#
|
78
|
-
# Build an artifact from a Shodan search API response
|
79
|
-
#
|
80
|
-
# @param [Structs::Shodan::Match] match
|
81
|
-
# @param [Array<Structs::Shodan::Match>] matches
|
82
|
-
#
|
83
|
-
# @return [Artifact]
|
84
|
-
#
|
85
|
-
def build_artifact(match, matches)
|
86
|
-
end
|
87
76
|
end
|
88
77
|
end
|
89
78
|
end
|
@@ -27,9 +27,8 @@ module Mihari
|
|
27
27
|
def initialize(*args, **kwargs)
|
28
28
|
super
|
29
29
|
|
30
|
-
unless
|
31
|
-
raise InvalidInputError,
|
32
|
-
"allowed_data_types should be any of url, domain and ip."
|
30
|
+
unless valid_allowed_data_types?
|
31
|
+
raise InvalidInputError, "allowed_data_types should be any of url, domain and ip."
|
33
32
|
end
|
34
33
|
|
35
34
|
@api_key = kwargs[:api_key] || Mihari.config.urlscan_api_key
|
@@ -97,7 +96,7 @@ module Mihari
|
|
97
96
|
#
|
98
97
|
# @return [Boolean]
|
99
98
|
#
|
100
|
-
def
|
99
|
+
def valid_allowed_data_types?
|
101
100
|
allowed_data_types.all? { |type| SUPPORTED_DATA_TYPES.include? type }
|
102
101
|
end
|
103
102
|
end
|
@@ -58,14 +58,11 @@ module Mihari
|
|
58
58
|
responses = []
|
59
59
|
|
60
60
|
loop do
|
61
|
-
response = Structs::VirusTotalIntelligence::Response.from_dynamic!(client.intel_search(query,
|
62
|
-
cursor: cursor))
|
61
|
+
response = Structs::VirusTotalIntelligence::Response.from_dynamic!(client.intel_search(query, cursor: cursor))
|
63
62
|
responses << response
|
64
|
-
|
65
63
|
break if response.meta.cursor.nil?
|
66
64
|
|
67
65
|
cursor = response.meta.cursor
|
68
|
-
|
69
66
|
# sleep #{interval} seconds to avoid the rate limitation (if it is set)
|
70
67
|
sleep interval
|
71
68
|
end
|
data/lib/mihari/clients/misp.rb
CHANGED
data/lib/mihari/emitters/misp.rb
CHANGED
@@ -45,10 +45,10 @@ module Mihari
|
|
45
45
|
|
46
46
|
client.create_event({
|
47
47
|
Event: {
|
48
|
-
info: rule.title
|
49
|
-
|
50
|
-
|
51
|
-
|
48
|
+
info: rule.title,
|
49
|
+
Attribute: artifacts.map { |artifact| build_attribute(artifact) },
|
50
|
+
Tag: rule.tags.map { |tag| { name: tag } }
|
51
|
+
}
|
52
52
|
})
|
53
53
|
end
|
54
54
|
|
data/lib/mihari/http.rb
CHANGED
@@ -4,7 +4,7 @@ require "insensitive_hash"
|
|
4
4
|
|
5
5
|
module Mihari
|
6
6
|
class HTTP
|
7
|
-
# @return [
|
7
|
+
# @return [URI]
|
8
8
|
attr_reader :url
|
9
9
|
|
10
10
|
# @return [Hash]
|
@@ -26,12 +26,12 @@ module Mihari
|
|
26
26
|
new_url = url.deep_dup
|
27
27
|
new_url.query = Addressable::URI.form_encode(params) unless (params || {}).empty?
|
28
28
|
|
29
|
-
get = Net::HTTP::Get.new(new_url)
|
29
|
+
get = Net::HTTP::Get.new(new_url, headers)
|
30
30
|
request get
|
31
31
|
end
|
32
32
|
|
33
33
|
#
|
34
|
-
# Make a POST
|
34
|
+
# Make a POST request
|
35
35
|
#
|
36
36
|
# @param [Hash, nil] params
|
37
37
|
# @param [Hash, nil] json
|
@@ -43,10 +43,17 @@ module Mihari
|
|
43
43
|
new_url = url.deep_dup
|
44
44
|
new_url.query = Addressable::URI.form_encode(params) unless (params || {}).empty?
|
45
45
|
|
46
|
-
post = Net::HTTP::Post.new(new_url)
|
46
|
+
post = Net::HTTP::Post.new(new_url, headers)
|
47
47
|
|
48
|
-
|
49
|
-
|
48
|
+
if json
|
49
|
+
post.body = JSON.generate(json) if json
|
50
|
+
post.content_type = "application/json"
|
51
|
+
end
|
52
|
+
|
53
|
+
if data
|
54
|
+
post.set_form_data(data) if data
|
55
|
+
post.content_type = "application/x-www-form-urlencoded"
|
56
|
+
end
|
50
57
|
|
51
58
|
request post
|
52
59
|
end
|
@@ -65,10 +72,6 @@ module Mihari
|
|
65
72
|
|
66
73
|
private
|
67
74
|
|
68
|
-
def content_type
|
69
|
-
headers["content-type"] || "application/json"
|
70
|
-
end
|
71
|
-
|
72
75
|
#
|
73
76
|
# Get options for HTTP request
|
74
77
|
#
|
@@ -89,16 +92,9 @@ module Mihari
|
|
89
92
|
#
|
90
93
|
def request(req)
|
91
94
|
Net::HTTP.start(url.host, url.port, https_options) do |http|
|
92
|
-
# set headers
|
93
|
-
headers.each do |k, v|
|
94
|
-
req[k] = v
|
95
|
-
end
|
96
|
-
|
97
95
|
res = http.request(req)
|
98
|
-
|
99
96
|
unless res.is_a?(Net::HTTPSuccess)
|
100
|
-
code
|
101
|
-
raise UnsuccessfulStatusCodeError, "Unsuccessful response code returned: #{code}"
|
97
|
+
raise UnsuccessfulStatusCodeError, "Unsuccessful response code returned: #{res.code}"
|
102
98
|
end
|
103
99
|
|
104
100
|
res
|
data/lib/mihari/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mihari
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.1.
|
4
|
+
version: 5.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manabu Niseki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-03-
|
11
|
+
date: 2023-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -994,7 +994,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
994
994
|
- !ruby/object:Gem::Version
|
995
995
|
version: '0'
|
996
996
|
requirements: []
|
997
|
-
rubygems_version: 3.
|
997
|
+
rubygems_version: 3.4.1
|
998
998
|
signing_key:
|
999
999
|
specification_version: 4
|
1000
1000
|
summary: A framework for continuous OSINT based threat hunting
|