mihari 3.4.1 → 3.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitmodules +3 -0
- data/README.md +2 -0
- data/Steepfile +32 -0
- data/config.ru +1 -0
- data/lib/mihari/analyzers/base.rb +24 -11
- data/lib/mihari/analyzers/binaryedge.rb +13 -0
- data/lib/mihari/analyzers/censys.rb +42 -9
- data/lib/mihari/analyzers/circl.rb +15 -0
- data/lib/mihari/analyzers/crtsh.rb +5 -0
- data/lib/mihari/analyzers/dnpedia.rb +5 -0
- data/lib/mihari/analyzers/dnstwister.rb +17 -0
- data/lib/mihari/analyzers/onyphe.rb +50 -9
- data/lib/mihari/analyzers/otx.rb +20 -0
- data/lib/mihari/analyzers/passivetotal.rb +25 -0
- data/lib/mihari/analyzers/pulsedive.rb +10 -0
- data/lib/mihari/analyzers/rule.rb +18 -0
- data/lib/mihari/analyzers/securitytrails.rb +25 -0
- data/lib/mihari/analyzers/shodan.rb +39 -5
- data/lib/mihari/analyzers/spyse.rb +20 -0
- data/lib/mihari/analyzers/urlscan.rb +10 -0
- data/lib/mihari/analyzers/virustotal.rb +20 -0
- data/lib/mihari/analyzers/zoomeye.rb +38 -0
- data/lib/mihari/cli/analyzer.rb +1 -0
- data/lib/mihari/cli/base.rb +0 -2
- data/lib/mihari/commands/init.rb +1 -1
- data/lib/mihari/commands/search.rb +1 -0
- data/lib/mihari/commands/web.rb +1 -0
- data/lib/mihari/{constraints.rb → constants.rb} +0 -0
- data/lib/mihari/database.rb +55 -3
- data/lib/mihari/emitters/base.rb +1 -1
- data/lib/mihari/emitters/misp.rb +38 -5
- data/lib/mihari/emitters/slack.rb +20 -2
- data/lib/mihari/emitters/the_hive.rb +16 -3
- data/lib/mihari/emitters/webhook.rb +18 -3
- data/lib/mihari/enrichers/ipinfo.rb +38 -0
- data/lib/mihari/mixins/autonomous_system.rb +19 -0
- data/lib/mihari/mixins/disallowed_data_value.rb +1 -1
- data/lib/mihari/models/alert.rb +28 -10
- data/lib/mihari/models/artifact.rb +94 -0
- data/lib/mihari/models/autonomous_system.rb +28 -0
- data/lib/mihari/models/dns.rb +55 -0
- data/lib/mihari/models/geolocation.rb +29 -0
- data/lib/mihari/models/reverse_dns.rb +26 -0
- data/lib/mihari/models/whois.rb +119 -0
- data/lib/mihari/schemas/configuration.rb +1 -0
- data/lib/mihari/schemas/rule.rb +2 -15
- data/lib/mihari/serializers/alert.rb +6 -4
- data/lib/mihari/serializers/artifact.rb +11 -2
- data/lib/mihari/serializers/autonomous_system.rb +9 -0
- data/lib/mihari/serializers/dns.rb +11 -0
- data/lib/mihari/serializers/geolocation.rb +11 -0
- data/lib/mihari/serializers/reverse_dns.rb +11 -0
- data/lib/mihari/serializers/tag.rb +4 -2
- data/lib/mihari/serializers/whois.rb +11 -0
- data/lib/mihari/structs/censys.rb +92 -0
- data/lib/mihari/structs/ipinfo.rb +36 -0
- data/lib/mihari/structs/onyphe.rb +47 -0
- data/lib/mihari/structs/shodan.rb +53 -0
- data/lib/mihari/type_checker.rb +9 -9
- data/lib/mihari/types.rb +21 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/app.rb +2 -0
- data/lib/mihari/web/controllers/alerts_controller.rb +3 -4
- data/lib/mihari/web/controllers/artifacts_controller.rb +73 -3
- data/lib/mihari/web/controllers/ip_address_controller.rb +21 -0
- data/lib/mihari/web/controllers/sources_controller.rb +2 -2
- data/lib/mihari/web/controllers/tags_controller.rb +3 -1
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +14 -11
- data/lib/mihari/web/public/static/fonts/fa-brands-400.1a575a41.woff +0 -0
- data/lib/mihari/web/public/static/fonts/fa-brands-400.513aa607.ttf +0 -0
- data/lib/mihari/web/public/static/fonts/fa-brands-400.592643a8.eot +0 -0
- data/lib/mihari/web/public/static/fonts/fa-brands-400.ed311c7a.woff2 +0 -0
- data/lib/mihari/web/public/static/fonts/fa-regular-400.766913e6.ttf +0 -0
- data/lib/mihari/web/public/static/fonts/fa-regular-400.b0e2db3b.eot +0 -0
- data/lib/mihari/web/public/static/fonts/fa-regular-400.b91d376b.woff2 +0 -0
- data/lib/mihari/web/public/static/fonts/fa-regular-400.d1d7e3b4.woff +0 -0
- data/lib/mihari/web/public/static/fonts/fa-solid-900.0c6bfc66.eot +0 -0
- data/lib/mihari/web/public/static/fonts/fa-solid-900.b9625119.ttf +0 -0
- data/lib/mihari/web/public/static/fonts/fa-solid-900.d745348d.woff +0 -0
- data/lib/mihari/web/public/static/fonts/fa-solid-900.d824df7e.woff2 +0 -0
- data/lib/mihari/web/public/static/img/fa-brands-400.1d5619cd.svg +3717 -0
- data/lib/mihari/web/public/static/img/fa-regular-400.c5d109be.svg +801 -0
- data/lib/mihari/web/public/static/img/fa-solid-900.37bc7099.svg +5034 -0
- data/lib/mihari/web/public/static/js/app.06d5cf1c.js +36 -0
- data/lib/mihari/web/public/static/js/app.06d5cf1c.js.map +1 -0
- data/lib/mihari/web/public/static/js/app.8e3e5150.js +36 -0
- data/lib/mihari/web/public/static/js/app.8e3e5150.js.map +1 -0
- data/lib/mihari/web/public/static/js/app.b5914c39.js +36 -0
- data/lib/mihari/web/public/static/js/app.b5914c39.js.map +1 -0
- data/lib/mihari.rb +30 -4
- data/mihari.gemspec +10 -1
- data/sig/lib/mihari/analyzers/base.rbs +90 -0
- data/sig/lib/mihari/analyzers/basic.rbs +17 -0
- data/sig/lib/mihari/analyzers/binaryedge.rbs +25 -0
- data/sig/lib/mihari/analyzers/censys.rbs +38 -0
- data/sig/lib/mihari/analyzers/circl.rbs +29 -0
- data/sig/lib/mihari/analyzers/crtsh.rbs +19 -0
- data/sig/lib/mihari/analyzers/dnpedia.rbs +18 -0
- data/sig/lib/mihari/analyzers/dnstwister.rbs +27 -0
- data/sig/lib/mihari/analyzers/onyphe.rbs +33 -0
- data/sig/lib/mihari/analyzers/otx.rbs +33 -0
- data/sig/lib/mihari/analyzers/passivetotal.rbs +33 -0
- data/sig/lib/mihari/analyzers/pulsedive.rbs +27 -0
- data/sig/lib/mihari/analyzers/rule.rbs +68 -0
- data/sig/lib/mihari/analyzers/securitytrails.rbs +33 -0
- data/sig/lib/mihari/analyzers/shodan.rbs +33 -0
- data/sig/lib/mihari/analyzers/spyse.rbs +29 -0
- data/sig/lib/mihari/analyzers/urlscan.rbs +28 -0
- data/sig/lib/mihari/analyzers/virustotal.rbs +31 -0
- data/sig/lib/mihari/analyzers/zoomeye.rbs +33 -0
- data/sig/lib/mihari/cli/analyzer.rbs +39 -0
- data/sig/lib/mihari/cli/base.rbs +11 -0
- data/sig/lib/mihari/cli/init.rbs +7 -0
- data/sig/lib/mihari/cli/main.rbs +9 -0
- data/sig/lib/mihari/cli/mixins/utils.rbs +50 -0
- data/sig/lib/mihari/cli/validator.rbs +7 -0
- data/sig/lib/mihari/commands/binaryedge.rbs +7 -0
- data/sig/lib/mihari/commands/censys.rbs +7 -0
- data/sig/lib/mihari/commands/circl.rbs +7 -0
- data/sig/lib/mihari/commands/crtsh.rbs +7 -0
- data/sig/lib/mihari/commands/dnpedia.rbs +7 -0
- data/sig/lib/mihari/commands/dnstwister.rbs +7 -0
- data/sig/lib/mihari/commands/init.rbs +11 -0
- data/sig/lib/mihari/commands/json.rbs +7 -0
- data/sig/lib/mihari/commands/onyphe.rbs +7 -0
- data/sig/lib/mihari/commands/otx.rbs +7 -0
- data/sig/lib/mihari/commands/passivetotal.rbs +7 -0
- data/sig/lib/mihari/commands/pulsedive.rbs +7 -0
- data/sig/lib/mihari/commands/search.rbs +35 -0
- data/sig/lib/mihari/commands/securitytrails.rbs +7 -0
- data/sig/lib/mihari/commands/shodan.rbs +7 -0
- data/sig/lib/mihari/commands/spyse.rbs +7 -0
- data/sig/lib/mihari/commands/urlscan.rbs +7 -0
- data/sig/lib/mihari/commands/validator.rbs +11 -0
- data/sig/lib/mihari/commands/virustotal.rbs +7 -0
- data/sig/lib/mihari/commands/web.rbs +7 -0
- data/sig/lib/mihari/commands/zoomeye.rbs +7 -0
- data/sig/lib/mihari/constants.rbs +3 -0
- data/sig/lib/mihari/database.rbs +25 -0
- data/sig/lib/mihari/emitters/base.rbs +18 -0
- data/sig/lib/mihari/emitters/database.rbs +9 -0
- data/sig/lib/mihari/emitters/misp.rbs +28 -0
- data/sig/lib/mihari/emitters/slack.rbs +58 -0
- data/sig/lib/mihari/emitters/stdout.rbs +9 -0
- data/sig/lib/mihari/emitters/the_hive.rbs +24 -0
- data/sig/lib/mihari/emitters/webhook.rbs +20 -0
- data/sig/lib/mihari/enrichers/ipinfo.rbs +14 -0
- data/sig/lib/mihari/errors.rbs +10 -0
- data/sig/lib/mihari/mixins/autonomous_system.rbs +14 -0
- data/sig/lib/mihari/mixins/configurable.rbs +26 -0
- data/sig/lib/mihari/mixins/configuration.rbs +45 -0
- data/sig/lib/mihari/mixins/disallowed_data_value.rbs +25 -0
- data/sig/lib/mihari/mixins/hash.rbs +14 -0
- data/sig/lib/mihari/mixins/refang.rbs +14 -0
- data/sig/lib/mihari/mixins/retriable.rbs +15 -0
- data/sig/lib/mihari/mixins/rule.rbs +41 -0
- data/sig/lib/mihari/models/alert.rbs +46 -0
- data/sig/lib/mihari/models/artifact.rbs +65 -0
- data/sig/lib/mihari/models/autonomous_system.rbs +14 -0
- data/sig/lib/mihari/models/dns.rbs +19 -0
- data/sig/lib/mihari/models/geolocation.rbs +15 -0
- data/sig/lib/mihari/models/reverse_dns.rbs +14 -0
- data/sig/lib/mihari/models/tag.rbs +5 -0
- data/sig/lib/mihari/models/tagging.rbs +4 -0
- data/sig/lib/mihari/models/whois.rbs +66 -0
- data/sig/lib/mihari/notifiers/base.rbs +18 -0
- data/sig/lib/mihari/notifiers/exception_notifier.rbs +75 -0
- data/sig/lib/mihari/notifiers/slack.rbs +50 -0
- data/sig/lib/mihari/status.rbs +25 -0
- data/sig/lib/mihari/structs/censys.rbs +50 -0
- data/sig/lib/mihari/structs/ipinfo.rbs +17 -0
- data/sig/lib/mihari/structs/onyphe.rbs +25 -0
- data/sig/lib/mihari/structs/shodan.rbs +28 -0
- data/sig/lib/mihari/type_checker.rbs +48 -0
- data/sig/lib/mihari/types.rbs +17 -0
- data/sig/lib/mihari/version.rbs +3 -0
- data/sig/lib/mihari/web/app.rbs +5 -0
- data/sig/lib/mihari.rbs +57 -0
- metadata +259 -5
data/lib/mihari/models/alert.rb
CHANGED
@@ -18,8 +18,8 @@ module Mihari
|
|
18
18
|
# @param [String, nil] source
|
19
19
|
# @param [String, nil] tag_name
|
20
20
|
# @param [String, nil] title
|
21
|
-
# @param [
|
22
|
-
# @param [
|
21
|
+
# @param [DateTime, nil] from_at
|
22
|
+
# @param [DateTime, nil] to_at
|
23
23
|
# @param [Integer, nil] limit
|
24
24
|
# @param [Integer, nil] page
|
25
25
|
#
|
@@ -34,12 +34,22 @@ module Mihari
|
|
34
34
|
|
35
35
|
offset = (page - 1) * limit
|
36
36
|
|
37
|
-
relation = build_relation(
|
37
|
+
relation = build_relation(
|
38
|
+
artifact_data: artifact_data,
|
39
|
+
title: title,
|
40
|
+
description: description,
|
41
|
+
source: source,
|
42
|
+
tag_name: tag_name,
|
43
|
+
from_at: from_at,
|
44
|
+
to_at: to_at
|
45
|
+
)
|
38
46
|
|
39
|
-
|
47
|
+
# TODO: improve queires
|
48
|
+
alert_ids = relation.limit(limit).offset(offset).order(id: :desc).pluck(:id).uniq
|
49
|
+
alerts = includes(:artifacts, :tags).where(id: [alert_ids]).order(id: :desc)
|
40
50
|
|
41
51
|
alerts.map do |alert|
|
42
|
-
json = AlertSerializer.new(alert).as_json
|
52
|
+
json = Serializers::AlertSerializer.new(alert).as_json
|
43
53
|
json[:artifacts] = json[:artifacts] || []
|
44
54
|
json[:tags] = json[:tags] || []
|
45
55
|
json
|
@@ -54,13 +64,21 @@ module Mihari
|
|
54
64
|
# @param [String, nil] source
|
55
65
|
# @param [String, nil] tag_name
|
56
66
|
# @param [String, nil] title
|
57
|
-
# @param [
|
58
|
-
# @param [
|
67
|
+
# @param [DateTime, nil] from_at
|
68
|
+
# @param [DateTime, nil] to_at
|
59
69
|
#
|
60
70
|
# @return [Integer]
|
61
71
|
#
|
62
72
|
def count(artifact_data: nil, description: nil, source: nil, tag_name: nil, title: nil, from_at: nil, to_at: nil)
|
63
|
-
relation = build_relation(
|
73
|
+
relation = build_relation(
|
74
|
+
artifact_data: artifact_data,
|
75
|
+
title: title,
|
76
|
+
description: description,
|
77
|
+
source: source,
|
78
|
+
tag_name: tag_name,
|
79
|
+
from_at: from_at,
|
80
|
+
to_at: to_at
|
81
|
+
)
|
64
82
|
relation.distinct("alerts.id").count
|
65
83
|
end
|
66
84
|
|
@@ -68,8 +86,8 @@ module Mihari
|
|
68
86
|
|
69
87
|
def build_relation(artifact_data: nil, title: nil, description: nil, source: nil, tag_name: nil, from_at: nil, to_at: nil)
|
70
88
|
relation = self
|
71
|
-
|
72
|
-
relation =
|
89
|
+
|
90
|
+
relation = relation.includes(:artifacts, :tags)
|
73
91
|
|
74
92
|
relation = relation.where(artifacts: { data: artifact_data }) if artifact_data
|
75
93
|
relation = relation.where(tags: { name: tag_name }) if tag_name
|
@@ -4,6 +4,7 @@ require "active_record"
|
|
4
4
|
require "active_record/filter"
|
5
5
|
require "active_support/core_ext/integer/time"
|
6
6
|
require "active_support/core_ext/numeric/time"
|
7
|
+
require "uri"
|
7
8
|
|
8
9
|
class ArtifactValidator < ActiveModel::Validator
|
9
10
|
def validate(record)
|
@@ -15,6 +16,15 @@ end
|
|
15
16
|
|
16
17
|
module Mihari
|
17
18
|
class Artifact < ActiveRecord::Base
|
19
|
+
belongs_to :alert
|
20
|
+
|
21
|
+
has_one :autonomous_system, dependent: :destroy
|
22
|
+
has_one :geolocation, dependent: :destroy
|
23
|
+
has_one :whois_record, dependent: :destroy
|
24
|
+
|
25
|
+
has_many :dns_records, dependent: :destroy
|
26
|
+
has_many :reverse_dns_names, dependent: :destroy
|
27
|
+
|
18
28
|
include ActiveModel::Validations
|
19
29
|
|
20
30
|
validates_with ArtifactValidator
|
@@ -44,5 +54,89 @@ module Mihari
|
|
44
54
|
# within {ignore_threshold} days, do not ignore it
|
45
55
|
artifact.created_at < days_before
|
46
56
|
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# Enrich(add) whois record
|
60
|
+
#
|
61
|
+
def enrich_whois
|
62
|
+
return unless can_enrich_whois?
|
63
|
+
|
64
|
+
self.whois_record = WhoisRecord.build_by_domain(normalize_as_domain(data))
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Enrich(add) DNS records
|
69
|
+
#
|
70
|
+
def enrich_dns
|
71
|
+
return unless can_enrich_dns?
|
72
|
+
|
73
|
+
self.dns_records = DnsRecord.build_by_domain(normalize_as_domain(data))
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Enrich(add) reverse DNS names
|
78
|
+
#
|
79
|
+
def enrich_reverse_dns
|
80
|
+
return unless can_enrich_revese_dns?
|
81
|
+
|
82
|
+
self.reverse_dns_names = ReverseDnsName.build_by_ip(data)
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Enrich(add) geolocation
|
87
|
+
#
|
88
|
+
def enrich_geolocation
|
89
|
+
return unless can_enrich_geolocation?
|
90
|
+
|
91
|
+
self.geolocation = Geolocation.build_by_ip(data)
|
92
|
+
end
|
93
|
+
|
94
|
+
#
|
95
|
+
# Enrich(add) geolocation
|
96
|
+
#
|
97
|
+
def enrich_autonomous_system
|
98
|
+
return unless can_enrich_autonomous_system?
|
99
|
+
|
100
|
+
self.autonomous_system = AutonomousSystem.build_by_ip(data)
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# Enrich all the enrichable relationships of the artifact
|
105
|
+
#
|
106
|
+
def enrich_all
|
107
|
+
enrich_autonomous_system
|
108
|
+
enrich_dns
|
109
|
+
enrich_geolocation
|
110
|
+
enrich_reverse_dns
|
111
|
+
enrich_whois
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def normalize_as_domain(url_or_domain)
|
117
|
+
return url_or_domain if data_type == "domain"
|
118
|
+
|
119
|
+
URI.parse(url_or_domain).host
|
120
|
+
end
|
121
|
+
|
122
|
+
def can_enrich_whois?
|
123
|
+
%w[domain url].include?(data_type) && whois_record.nil?
|
124
|
+
end
|
125
|
+
|
126
|
+
def can_enrich_dns?
|
127
|
+
%w[domain url].include?(data_type) && dns_records.empty?
|
128
|
+
end
|
129
|
+
|
130
|
+
def can_enrich_revese_dns?
|
131
|
+
data_type == "ip" && reverse_dns_names.empty?
|
132
|
+
end
|
133
|
+
|
134
|
+
def can_enrich_geolocation?
|
135
|
+
data_type == "ip" && geolocation.nil?
|
136
|
+
end
|
137
|
+
|
138
|
+
def can_enrich_autonomous_system?
|
139
|
+
data_type == "ip" && autonomous_system.nil?
|
140
|
+
end
|
47
141
|
end
|
48
142
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
|
5
|
+
module Mihari
|
6
|
+
class AutonomousSystem < ActiveRecord::Base
|
7
|
+
belongs_to :artifact
|
8
|
+
|
9
|
+
class << self
|
10
|
+
#
|
11
|
+
# Build AS
|
12
|
+
#
|
13
|
+
# @param [String] ip
|
14
|
+
#
|
15
|
+
# @return [Mihari::AutonomousSystem, nil]
|
16
|
+
#
|
17
|
+
def build_by_ip(ip)
|
18
|
+
res = Enrichers::IPInfo.query(ip)
|
19
|
+
|
20
|
+
unless res.nil?
|
21
|
+
return new(asn: res.asn)
|
22
|
+
end
|
23
|
+
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "resolv"
|
5
|
+
|
6
|
+
module Mihari
|
7
|
+
class DnsRecord < ActiveRecord::Base
|
8
|
+
belongs_to :artifact
|
9
|
+
|
10
|
+
class << self
|
11
|
+
#
|
12
|
+
# Build DNS records
|
13
|
+
#
|
14
|
+
# @param [String] domain
|
15
|
+
#
|
16
|
+
# @return [Array<Mihari::DnsRecord>]
|
17
|
+
#
|
18
|
+
def build_by_domain(domain)
|
19
|
+
resource_types = [
|
20
|
+
Resolv::DNS::Resource::IN::A,
|
21
|
+
Resolv::DNS::Resource::IN::AAAA,
|
22
|
+
Resolv::DNS::Resource::IN::CNAME,
|
23
|
+
Resolv::DNS::Resource::IN::TXT,
|
24
|
+
Resolv::DNS::Resource::IN::NS
|
25
|
+
]
|
26
|
+
|
27
|
+
resource_types.map do |resource_type|
|
28
|
+
get_values domain, resource_type
|
29
|
+
rescue Resolv::ResolvError
|
30
|
+
nil
|
31
|
+
end.flatten.compact
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def get_values(domain, resource_type)
|
37
|
+
resources = Resolv::DNS.new.getresources(domain, resource_type)
|
38
|
+
resource_name = resource_type.to_s.split("::").last
|
39
|
+
|
40
|
+
resources.map do |resource|
|
41
|
+
# A, AAAA
|
42
|
+
if resource.respond_to?(:address)
|
43
|
+
new(resource: resource_name, value: resource.address.to_s)
|
44
|
+
# CNAME, NS
|
45
|
+
elsif resource.respond_to?(:name)
|
46
|
+
new(resource: resource_name, value: resource.name.to_s)
|
47
|
+
# TXT
|
48
|
+
elsif resource.respond_to?(:data)
|
49
|
+
new(resource: resource_name, value: resource.data.to_s)
|
50
|
+
end
|
51
|
+
end.compact
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "normalize_country"
|
5
|
+
|
6
|
+
module Mihari
|
7
|
+
class Geolocation < ActiveRecord::Base
|
8
|
+
belongs_to :artifact
|
9
|
+
|
10
|
+
class << self
|
11
|
+
#
|
12
|
+
# Build Geolocation
|
13
|
+
#
|
14
|
+
# @param [String] ip
|
15
|
+
#
|
16
|
+
# @return [Mihari::Geolocation, nil]
|
17
|
+
#
|
18
|
+
def build_by_ip(ip)
|
19
|
+
res = Enrichers::IPInfo.query(ip)
|
20
|
+
|
21
|
+
unless res.nil?
|
22
|
+
return new(country: NormalizeCountry(res.country_code, to: :short), country_code: res.country_code)
|
23
|
+
end
|
24
|
+
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "resolv"
|
5
|
+
|
6
|
+
module Mihari
|
7
|
+
class ReverseDnsName < ActiveRecord::Base
|
8
|
+
belongs_to :artifact
|
9
|
+
|
10
|
+
class << self
|
11
|
+
#
|
12
|
+
# Build reverse DNS names
|
13
|
+
#
|
14
|
+
# @param [String] ip
|
15
|
+
#
|
16
|
+
# @return [Array<Mihari::ReverseDnsName>]
|
17
|
+
#
|
18
|
+
def build_by_ip(ip)
|
19
|
+
names = Resolv.getnames(ip)
|
20
|
+
names.map { |name| new(name: name) }
|
21
|
+
rescue Resolv::ResolvError
|
22
|
+
[]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "whois-parser"
|
5
|
+
require "public_suffix"
|
6
|
+
|
7
|
+
module Mihari
|
8
|
+
class WhoisRecord < ActiveRecord::Base
|
9
|
+
belongs_to :artifact
|
10
|
+
|
11
|
+
@memo = {}
|
12
|
+
|
13
|
+
class << self
|
14
|
+
#
|
15
|
+
# Build whois record
|
16
|
+
#
|
17
|
+
# @param [Stinrg] domain
|
18
|
+
#
|
19
|
+
# @return [WhoisRecord, nil]
|
20
|
+
#
|
21
|
+
def build_by_domain(domain)
|
22
|
+
domain = PublicSuffix.domain(domain)
|
23
|
+
|
24
|
+
# check memo
|
25
|
+
if @memo.key?(domain)
|
26
|
+
whois_record = @memo[domain]
|
27
|
+
# return clone of the record
|
28
|
+
return whois_record.dup
|
29
|
+
end
|
30
|
+
|
31
|
+
record = Whois.whois(domain)
|
32
|
+
parser = record.parser
|
33
|
+
|
34
|
+
return nil if parser.available?
|
35
|
+
|
36
|
+
whois_record = new(
|
37
|
+
domain: domain,
|
38
|
+
created_on: get_created_on(parser),
|
39
|
+
updated_on: get_updated_on(parser),
|
40
|
+
expires_on: get_expires_on(parser),
|
41
|
+
registrar: get_registrar(parser),
|
42
|
+
contacts: get_contacts(parser)
|
43
|
+
)
|
44
|
+
# set memo
|
45
|
+
@memo[domain] = whois_record
|
46
|
+
whois_record
|
47
|
+
rescue Whois::Error, Whois::ParserError, Timeout::Error
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
#
|
54
|
+
# Get created_on
|
55
|
+
#
|
56
|
+
# @param [::Whois::Parser:] parser
|
57
|
+
#
|
58
|
+
# @return [Date, nil]
|
59
|
+
#
|
60
|
+
def get_created_on(parser)
|
61
|
+
parser.created_on
|
62
|
+
rescue ::Whois::AttributeNotImplemented
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Get updated_on
|
68
|
+
#
|
69
|
+
# @param [::Whois::Parser:] parser
|
70
|
+
#
|
71
|
+
# @return [Date, nil]
|
72
|
+
#
|
73
|
+
def get_updated_on(parser)
|
74
|
+
parser.updated_on
|
75
|
+
rescue ::Whois::AttributeNotImplemented
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
#
|
80
|
+
# Get expires_on
|
81
|
+
#
|
82
|
+
# @param [::Whois::Parser:] parser
|
83
|
+
#
|
84
|
+
# @return [Date, nil]
|
85
|
+
#
|
86
|
+
def get_expires_on(parser)
|
87
|
+
parser.expires_on
|
88
|
+
rescue ::Whois::AttributeNotImplemented
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Get registrar
|
94
|
+
#
|
95
|
+
# @param [::Whois::Parser:] parser
|
96
|
+
#
|
97
|
+
# @return [Hash, nil]
|
98
|
+
#
|
99
|
+
def get_registrar(parser)
|
100
|
+
parser.registrar&.to_h
|
101
|
+
rescue ::Whois::AttributeNotImplemented
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Get contacts
|
107
|
+
#
|
108
|
+
# @param [::Whois::Parser:] parser
|
109
|
+
#
|
110
|
+
# @return [Array[Hash], nil]
|
111
|
+
#
|
112
|
+
def get_contacts(parser)
|
113
|
+
parser.contacts.map(&:to_h)
|
114
|
+
rescue ::Whois::AttributeNotImplemented
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|