mihari 4.4.1 → 4.5.0
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/commands/web.rb +1 -1
- data/lib/mihari/database.rb +22 -1
- data/lib/mihari/enrichers/shodan.rb +36 -0
- data/lib/mihari/entities/artifact.rb +6 -0
- data/lib/mihari/entities/cpe.rb +9 -0
- data/lib/mihari/entities/port.rb +9 -0
- data/lib/mihari/models/alert.rb +3 -1
- data/lib/mihari/models/artifact.rb +31 -1
- data/lib/mihari/models/cpe.rb +23 -0
- data/lib/mihari/models/port.rb +23 -0
- data/lib/mihari/models/reverse_dns.rb +4 -4
- data/lib/mihari/structs/shodan.rb +25 -0
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/api.rb +0 -2
- data/lib/mihari/web/endpoints/artifacts.rb +3 -1
- data/lib/mihari/web/public/index.html +1 -1
- data/lib/mihari/web/public/redoc-static.html +18 -22
- data/lib/mihari/web/public/static/css/{app.de5845d8.css → app.2a5d3d21.css} +1 -1
- data/lib/mihari/web/public/static/js/app-legacy.c3595dce.js +2 -0
- data/lib/mihari/web/public/static/js/app-legacy.c3595dce.js.map +1 -0
- data/lib/mihari/web/public/static/js/app.afd5025f.js +2 -0
- data/lib/mihari/web/public/static/js/app.afd5025f.js.map +1 -0
- data/lib/mihari.rb +5 -1
- data/mihari.gemspec +0 -1
- data/sig/lib/mihari/models/artifact.rbs +2 -0
- data/sig/lib/mihari/models/cpe.rbs +7 -0
- data/sig/lib/mihari/models/port.rbs +7 -0
- metadata +14 -23
- data/lib/mihari/entities/command.rb +0 -14
- data/lib/mihari/web/endpoints/command.rb +0 -33
- data/lib/mihari/web/public/static/js/app-legacy.f550d6ae.js +0 -2
- data/lib/mihari/web/public/static/js/app-legacy.f550d6ae.js.map +0 -1
- data/lib/mihari/web/public/static/js/app.40749592.js +0 -2
- data/lib/mihari/web/public/static/js/app.40749592.js.map +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dfd4bafdc33911546dce3477184426c581b9e3a40a762f32d1944870589ffcc9
|
|
4
|
+
data.tar.gz: 8765422118bcc6fe914439b416e6362551c9de0492397ce90c2c5bf7487d982a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a0788f87ab0ef4838243e25ca71496408187add826900d0428af6f4a8e7f81093e81944d809425b2c716c778c35b725f757b656cec046adc2294b4a07d764b4f
|
|
7
|
+
data.tar.gz: 3c0c001e91aaec0090daeb117a7595a84172a355d527af0dab7f81dfee557b271697a0fbcf75c8a44d5e33b6fb1ff1f8b82e0c057dff91e85b4fb4a0a30d69ca
|
data/lib/mihari/commands/web.rb
CHANGED
|
@@ -8,7 +8,7 @@ module Mihari
|
|
|
8
8
|
desc "web", "Launch the web app"
|
|
9
9
|
method_option :port, type: :numeric, default: 9292, desc: "Hostname to listen on"
|
|
10
10
|
method_option :host, type: :string, default: "localhost", desc: "Port to listen on"
|
|
11
|
-
method_option :threads, type: :string, default: "
|
|
11
|
+
method_option :threads, type: :string, default: "1:1", desc: "min:max threads to use"
|
|
12
12
|
method_option :verbose, type: :boolean, default: true, desc: "Report each request"
|
|
13
13
|
def web
|
|
14
14
|
port = options["port"]
|
data/lib/mihari/database.rb
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# Make possible to use upper case acronyms in class names
|
|
4
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
|
5
|
+
inflect.acronym "CPE"
|
|
6
|
+
end
|
|
7
|
+
|
|
3
8
|
def env
|
|
4
9
|
ENV["APP_ENV"] || ENV["RACK_ENV"]
|
|
5
10
|
end
|
|
@@ -118,6 +123,20 @@ class AddYAMLToRulesSchema < ActiveRecord::Migration[7.0]
|
|
|
118
123
|
end
|
|
119
124
|
end
|
|
120
125
|
|
|
126
|
+
class EnrichmentsV45Schema < ActiveRecord::Migration[7.0]
|
|
127
|
+
def change
|
|
128
|
+
create_table :cpes, if_not_exists: true do |t|
|
|
129
|
+
t.string :cpe, null: false
|
|
130
|
+
t.belongs_to :artifact, foreign_key: true
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
create_table :ports, if_not_exists: true do |t|
|
|
134
|
+
t.integer :port, null: false
|
|
135
|
+
t.belongs_to :artifact, foreign_key: true
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
121
140
|
def adapter
|
|
122
141
|
return "postgresql" if Mihari.config.database.start_with?("postgresql://", "postgres://")
|
|
123
142
|
return "mysql2" if Mihari.config.database.start_with?("mysql2://")
|
|
@@ -147,7 +166,9 @@ module Mihari
|
|
|
147
166
|
RuleSchema,
|
|
148
167
|
AddeMetadataToArtifactSchema,
|
|
149
168
|
# v4.4
|
|
150
|
-
AddYAMLToRulesSchema
|
|
169
|
+
AddYAMLToRulesSchema,
|
|
170
|
+
# v4.5
|
|
171
|
+
EnrichmentsV45Schema
|
|
151
172
|
].each { |schema| schema.migrate direction }
|
|
152
173
|
end
|
|
153
174
|
memoize :migrate unless test_env?
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/https"
|
|
4
|
+
|
|
5
|
+
module Mihari
|
|
6
|
+
module Enrichers
|
|
7
|
+
class Shodan < Base
|
|
8
|
+
# @return [Boolean]
|
|
9
|
+
def valid?
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
include Memist::Memoizable
|
|
15
|
+
|
|
16
|
+
#
|
|
17
|
+
# Query Shodan Internet DB
|
|
18
|
+
#
|
|
19
|
+
# @param [String] ip
|
|
20
|
+
#
|
|
21
|
+
# @return [Mihari::Structs::Shodan::InternetDBResponse, nil]
|
|
22
|
+
#
|
|
23
|
+
def query(ip)
|
|
24
|
+
url = "https://internetdb.shodan.io/#{ip}"
|
|
25
|
+
res = HTTP.get(url)
|
|
26
|
+
data = JSON.parse(res.body.to_s)
|
|
27
|
+
|
|
28
|
+
Structs::Shodan::InternetDBResponse.from_dynamic! data
|
|
29
|
+
rescue HttpError
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
memoize :query
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -21,6 +21,12 @@ module Mihari
|
|
|
21
21
|
expose :dns_records, using: Entities::DnsRecord, documentation: { type: Entities::DnsRecord, is_array: true, required: false }, as: :dnsRecords do |status, _options|
|
|
22
22
|
status.dns_records.empty? ? nil : status.dns_records
|
|
23
23
|
end
|
|
24
|
+
expose :ceps, using: Entities::CPE, documentation: { type: Entities::CPE, is_array: true, required: false }, as: :cpes do |status, _options|
|
|
25
|
+
status.cpes.empty? ? nil : status.cpes
|
|
26
|
+
end
|
|
27
|
+
expose :ports, using: Entities::Port, documentation: { type: Entities::Port, is_array: true, required: false }, as: :ports do |status, _options|
|
|
28
|
+
status.ports.empty? ? nil : status.ports
|
|
29
|
+
end
|
|
24
30
|
end
|
|
25
31
|
end
|
|
26
32
|
end
|
data/lib/mihari/models/alert.rb
CHANGED
|
@@ -16,7 +16,9 @@ module Mihari
|
|
|
16
16
|
has_one :geolocation, dependent: :destroy
|
|
17
17
|
has_one :whois_record, dependent: :destroy
|
|
18
18
|
|
|
19
|
+
has_many :cpes, dependent: :destroy
|
|
19
20
|
has_many :dns_records, dependent: :destroy
|
|
21
|
+
has_many :ports, dependent: :destroy
|
|
20
22
|
has_many :reverse_dns_names, dependent: :destroy
|
|
21
23
|
|
|
22
24
|
include ActiveModel::Validations
|
|
@@ -94,7 +96,7 @@ module Mihari
|
|
|
94
96
|
end
|
|
95
97
|
|
|
96
98
|
#
|
|
97
|
-
# Enrich
|
|
99
|
+
# Enrich AS
|
|
98
100
|
#
|
|
99
101
|
def enrich_autonomous_system
|
|
100
102
|
return unless can_enrich_autonomous_system?
|
|
@@ -102,6 +104,24 @@ module Mihari
|
|
|
102
104
|
self.autonomous_system = AutonomousSystem.build_by_ip(data)
|
|
103
105
|
end
|
|
104
106
|
|
|
107
|
+
#
|
|
108
|
+
# Enrich ports
|
|
109
|
+
#
|
|
110
|
+
def enrich_ports
|
|
111
|
+
return unless can_enrich_ports?
|
|
112
|
+
|
|
113
|
+
self.ports = Port.build_by_ip(data)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
#
|
|
117
|
+
# Enrich CPEs
|
|
118
|
+
#
|
|
119
|
+
def enrich_cpes
|
|
120
|
+
return unless can_enrich_cpes?
|
|
121
|
+
|
|
122
|
+
self.cpes = CPE.build_by_ip(data)
|
|
123
|
+
end
|
|
124
|
+
|
|
105
125
|
#
|
|
106
126
|
# Enrich all the enrichable relationships of the artifact
|
|
107
127
|
#
|
|
@@ -111,6 +131,8 @@ module Mihari
|
|
|
111
131
|
enrich_geolocation
|
|
112
132
|
enrich_reverse_dns
|
|
113
133
|
enrich_whois
|
|
134
|
+
enrich_ports
|
|
135
|
+
enrich_cpes
|
|
114
136
|
end
|
|
115
137
|
|
|
116
138
|
private
|
|
@@ -140,5 +162,13 @@ module Mihari
|
|
|
140
162
|
def can_enrich_autonomous_system?
|
|
141
163
|
data_type == "ip" && autonomous_system.nil?
|
|
142
164
|
end
|
|
165
|
+
|
|
166
|
+
def can_enrich_ports?
|
|
167
|
+
data_type == "ip" && ports.empty?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def can_enrich_cpes?
|
|
171
|
+
data_type == "ip" && cpes.empty?
|
|
172
|
+
end
|
|
143
173
|
end
|
|
144
174
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mihari
|
|
4
|
+
class CPE < ActiveRecord::Base
|
|
5
|
+
belongs_to :artifact
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
#
|
|
9
|
+
# Build CPEs
|
|
10
|
+
#
|
|
11
|
+
# @param [String] ip
|
|
12
|
+
#
|
|
13
|
+
# @return [Array<Mihari::CPE>]
|
|
14
|
+
#
|
|
15
|
+
def build_by_ip(ip)
|
|
16
|
+
res = Enrichers::Shodan.query(ip)
|
|
17
|
+
return if res.nil?
|
|
18
|
+
|
|
19
|
+
res.cpes.map { |cpe| new(cpe: cpe) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Mihari
|
|
4
|
+
class Port < ActiveRecord::Base
|
|
5
|
+
belongs_to :artifact
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
#
|
|
9
|
+
# Build ports
|
|
10
|
+
#
|
|
11
|
+
# @param [String] ip
|
|
12
|
+
#
|
|
13
|
+
# @return [Array<Mihari::Port>]
|
|
14
|
+
#
|
|
15
|
+
def build_by_ip(ip)
|
|
16
|
+
res = Enrichers::Shodan.query(ip)
|
|
17
|
+
return if res.nil?
|
|
18
|
+
|
|
19
|
+
res.ports.map { |port| new(port: port) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -13,10 +13,10 @@ module Mihari
|
|
|
13
13
|
# @return [Array<Mihari::ReverseDnsName>]
|
|
14
14
|
#
|
|
15
15
|
def build_by_ip(ip)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
res = Enrichers::Shodan.query(ip)
|
|
17
|
+
return if res.nil?
|
|
18
|
+
|
|
19
|
+
res.hostnames.map { |name| new(name: name) }
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -57,6 +57,31 @@ module Mihari
|
|
|
57
57
|
)
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
|
+
|
|
61
|
+
class InternetDBResponse < Dry::Struct
|
|
62
|
+
attribute :ip, Types::String
|
|
63
|
+
attribute :ports, Types.Array(Types::Int)
|
|
64
|
+
attribute :cpes, Types.Array(Types::String)
|
|
65
|
+
attribute :hostnames, Types.Array(Types::String)
|
|
66
|
+
attribute :tags, Types.Array(Types::String)
|
|
67
|
+
attribute :vulns, Types.Array(Types::String)
|
|
68
|
+
|
|
69
|
+
def self.from_dynamic!(d)
|
|
70
|
+
d = Types::Hash[d]
|
|
71
|
+
new(
|
|
72
|
+
ip: d.fetch("ip"),
|
|
73
|
+
ports: d.fetch("ports"),
|
|
74
|
+
cpes: d.fetch("cpes"),
|
|
75
|
+
hostnames: d.fetch("hostnames"),
|
|
76
|
+
tags: d.fetch("tags"),
|
|
77
|
+
vulns: d.fetch("vulns")
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def self.from_json!(json)
|
|
82
|
+
from_dynamic!(JSON.parse(json))
|
|
83
|
+
end
|
|
84
|
+
end
|
|
60
85
|
end
|
|
61
86
|
end
|
|
62
87
|
end
|
data/lib/mihari/version.rb
CHANGED
data/lib/mihari/web/api.rb
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
# Endpoints
|
|
4
4
|
require "mihari/web/endpoints/alerts"
|
|
5
5
|
require "mihari/web/endpoints/artifacts"
|
|
6
|
-
require "mihari/web/endpoints/command"
|
|
7
6
|
require "mihari/web/endpoints/configs"
|
|
8
7
|
require "mihari/web/endpoints/ip_addresses"
|
|
9
8
|
require "mihari/web/endpoints/rules"
|
|
@@ -17,7 +16,6 @@ module Mihari
|
|
|
17
16
|
|
|
18
17
|
mount Endpoints::Alerts
|
|
19
18
|
mount Endpoints::Artifacts
|
|
20
|
-
mount Endpoints::Command
|
|
21
19
|
mount Endpoints::Configs
|
|
22
20
|
mount Endpoints::IPAddresses
|
|
23
21
|
mount Endpoints::Rules
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="icon" href="/static/favicon.ico"/><title>Mihari</title><script defer="defer" type="module" src="/static/js/chunk-vendors.3bdbaffb.js"></script><script defer="defer" type="module" src="/static/js/app.
|
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="icon" href="/static/favicon.ico"/><title>Mihari</title><script defer="defer" type="module" src="/static/js/chunk-vendors.3bdbaffb.js"></script><script defer="defer" type="module" src="/static/js/app.afd5025f.js"></script><link href="/static/css/chunk-vendors.da2a7bfc.css" rel="stylesheet"><link href="/static/css/app.2a5d3d21.css" rel="stylesheet"><script defer="defer" src="/static/js/chunk-vendors-legacy.d6b76c57.js" nomodule></script><script defer="defer" src="/static/js/app-legacy.c3595dce.js" nomodule></script></head><body><noscript><strong>We're sorry but Mihari doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
|