mihari 6.3.0 → 7.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -10
- data/.rubocop.yml +2 -0
- data/Dockerfile +14 -0
- data/config.ru +5 -3
- data/docker-compose.yml +61 -0
- data/exe/mihari +2 -1
- data/lefthook.yml +8 -0
- data/lib/mihari/actor.rb +4 -4
- data/lib/mihari/analyzers/base.rb +16 -0
- data/lib/mihari/analyzers/binaryedge.rb +4 -2
- data/lib/mihari/analyzers/censys.rb +7 -5
- data/lib/mihari/analyzers/circl.rb +5 -3
- data/lib/mihari/analyzers/crtsh.rb +10 -2
- data/lib/mihari/analyzers/dnstwister.rb +1 -1
- data/lib/mihari/analyzers/feed.rb +12 -20
- data/lib/mihari/analyzers/fofa.rb +6 -8
- data/lib/mihari/analyzers/greynoise.rb +4 -2
- data/lib/mihari/analyzers/hunterhow.rb +4 -2
- data/lib/mihari/analyzers/onyphe.rb +4 -2
- data/lib/mihari/analyzers/otx.rb +5 -3
- data/lib/mihari/analyzers/passivetotal.rb +29 -12
- data/lib/mihari/analyzers/pulsedive.rb +5 -3
- data/lib/mihari/analyzers/securitytrails.rb +32 -8
- data/lib/mihari/analyzers/shodan.rb +4 -2
- data/lib/mihari/analyzers/urlscan.rb +4 -2
- data/lib/mihari/analyzers/virustotal.rb +5 -5
- data/lib/mihari/analyzers/virustotal_intelligence.rb +4 -2
- data/lib/mihari/analyzers/zoomeye.rb +4 -2
- data/lib/mihari/cli/{main.rb → application.rb} +17 -5
- data/lib/mihari/cli/artifact.rb +14 -0
- data/lib/mihari/cli/config.rb +14 -0
- data/lib/mihari/cli/rule.rb +1 -0
- data/lib/mihari/cli/tag.rb +14 -0
- data/lib/mihari/clients/base.rb +2 -2
- data/lib/mihari/clients/binaryedge.rb +2 -2
- data/lib/mihari/clients/crtsh.rb +3 -10
- data/lib/mihari/clients/fofa.rb +1 -1
- data/lib/mihari/clients/hunterhow.rb +1 -1
- data/lib/mihari/clients/mmdb.rb +28 -0
- data/lib/mihari/clients/passivetotal.rb +7 -20
- data/lib/mihari/clients/securitytrails.rb +19 -43
- data/lib/mihari/clients/shodan_internet_db.rb +28 -0
- data/lib/mihari/clients/the_hive.rb +7 -5
- data/lib/mihari/commands/alert.rb +53 -11
- data/lib/mihari/commands/artifact.rb +66 -0
- data/lib/mihari/commands/config.rb +23 -0
- data/lib/mihari/commands/database.rb +1 -1
- data/lib/mihari/commands/rule.rb +40 -27
- data/lib/mihari/commands/search.rb +10 -11
- data/lib/mihari/commands/sidekiq.rb +31 -0
- data/lib/mihari/commands/tag.rb +46 -0
- data/lib/mihari/commands/web.rb +6 -7
- data/lib/mihari/{mixins/autonomous_system.rb → concerns/autonomous_system_normalizable.rb} +5 -3
- data/lib/mihari/concerns/configurable.rb +72 -0
- data/lib/mihari/concerns/database_connectable.rb +16 -0
- data/lib/mihari/{mixins/unwrap_error.rb → concerns/error_unwrappable.rb} +5 -3
- data/lib/mihari/{mixins/falsepositive.rb → concerns/falsepositive_validatable.rb} +5 -3
- data/lib/mihari/{mixins/refang.rb → concerns/refangable.rb} +5 -3
- data/lib/mihari/{mixins → concerns}/retriable.rb +4 -2
- data/lib/mihari/config.rb +13 -12
- data/lib/mihari/database.rb +30 -42
- data/lib/mihari/emitters/database.rb +5 -6
- data/lib/mihari/emitters/misp.rb +4 -11
- data/lib/mihari/emitters/slack.rb +7 -5
- data/lib/mihari/emitters/the_hive.rb +8 -58
- data/lib/mihari/emitters/webhook.rb +6 -6
- data/lib/mihari/enrichers/google_public_dns.rb +1 -1
- data/lib/mihari/enrichers/mmdb.rb +28 -0
- data/lib/mihari/enrichers/shodan.rb +3 -5
- data/lib/mihari/enrichers/whois.rb +3 -3
- data/lib/mihari/entities/alert.rb +3 -10
- data/lib/mihari/entities/artifact.rb +6 -14
- data/lib/mihari/entities/config.rb +2 -2
- data/lib/mihari/entities/cpe.rb +1 -0
- data/lib/mihari/entities/dns.rb +1 -0
- data/lib/mihari/entities/geolocation.rb +1 -0
- data/lib/mihari/entities/ip_address.rb +1 -3
- data/lib/mihari/entities/messages.rb +17 -0
- data/lib/mihari/entities/pagination.rb +11 -0
- data/lib/mihari/entities/port.rb +1 -0
- data/lib/mihari/entities/reverse_dns.rb +1 -0
- data/lib/mihari/entities/rule.rb +2 -20
- data/lib/mihari/entities/tag.rb +2 -2
- data/lib/mihari/entities/whois.rb +1 -0
- data/lib/mihari/errors.rb +2 -4
- data/lib/mihari/http.rb +4 -0
- data/lib/mihari/models/alert.rb +21 -53
- data/lib/mihari/models/artifact.rb +61 -97
- data/lib/mihari/models/autonomous_system.rb +0 -24
- data/lib/mihari/models/concerns/searchable.rb +50 -0
- data/lib/mihari/models/cpe.rb +0 -23
- data/lib/mihari/models/dns.rb +0 -20
- data/lib/mihari/models/geolocation.rb +0 -24
- data/lib/mihari/models/port.rb +3 -10
- data/lib/mihari/models/reverse_dns.rb +0 -23
- data/lib/mihari/models/rule.rb +16 -57
- data/lib/mihari/models/tag.rb +17 -1
- data/lib/mihari/models/tagging.rb +1 -1
- data/lib/mihari/models/whois.rb +0 -17
- data/lib/mihari/rule.rb +35 -24
- data/lib/mihari/schemas/alert.rb +1 -0
- data/lib/mihari/schemas/analyzer.rb +3 -2
- data/lib/mihari/schemas/concerns/orrable.rb +24 -0
- data/lib/mihari/schemas/emitter.rb +1 -2
- data/lib/mihari/schemas/enricher.rb +3 -4
- data/lib/mihari/schemas/macros.rb +1 -1
- data/lib/mihari/schemas/options.rb +0 -2
- data/lib/mihari/schemas/rule.rb +1 -2
- data/lib/mihari/services/builders.rb +158 -0
- data/lib/mihari/services/creators.rb +22 -0
- data/lib/mihari/services/destroyers.rb +41 -0
- data/lib/mihari/services/enrichers.rb +25 -0
- data/lib/mihari/services/feed.rb +107 -0
- data/lib/mihari/services/getters.rb +58 -0
- data/lib/mihari/services/initializers.rb +22 -0
- data/lib/mihari/services/{alert_builder.rb → proxies.rb} +10 -40
- data/lib/mihari/services/searchers.rb +91 -0
- data/lib/mihari/sidekiq/application.rb +13 -0
- data/lib/mihari/sidekiq/jobs.rb +36 -0
- data/lib/mihari/structs/censys.rb +1 -1
- data/lib/mihari/structs/config.rb +10 -10
- data/lib/mihari/structs/filters.rb +12 -130
- data/lib/mihari/structs/google_public_dns.rb +1 -1
- data/lib/mihari/structs/greynoise.rb +1 -1
- data/lib/mihari/structs/mmdb.rb +115 -0
- data/lib/mihari/structs/onyphe.rb +1 -1
- data/lib/mihari/structs/shodan.rb +2 -2
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/{app.rb → application.rb} +28 -15
- data/lib/mihari/web/endpoints/alerts.rb +34 -73
- data/lib/mihari/web/endpoints/artifacts.rb +27 -111
- data/lib/mihari/web/endpoints/configs.rb +3 -5
- data/lib/mihari/web/endpoints/ip_addresses.rb +14 -15
- data/lib/mihari/web/endpoints/rules.rb +58 -130
- data/lib/mihari/web/endpoints/tags.rb +21 -17
- data/lib/mihari/web/middleware/capture_exceptions.rb +25 -0
- data/lib/mihari/web/middleware/{connection_adapter.rb → connection.rb} +4 -2
- data/lib/mihari/web/public/assets/index-cQUcyII5.js +1766 -0
- data/lib/mihari/web/public/assets/index-dVaNxqTC.css +1 -0
- data/lib/mihari/web/public/index.html +2 -2
- data/lib/mihari/web/public/redoc-static.html +385 -385
- data/lib/mihari.rb +56 -28
- data/mihari.gemspec +12 -4
- data/mkdocs.yml +5 -2
- data/requirements.txt +1 -1
- metadata +164 -34
- data/lib/mihari/commands/mixins.rb +0 -11
- data/lib/mihari/enrichers/ipinfo.rb +0 -52
- data/lib/mihari/entities/message.rb +0 -9
- data/lib/mihari/feed/parser.rb +0 -38
- data/lib/mihari/feed/reader.rb +0 -111
- data/lib/mihari/mixins/configurable.rb +0 -68
- data/lib/mihari/schemas/mixins.rb +0 -20
- data/lib/mihari/services/alert_runner.rb +0 -20
- data/lib/mihari/services/rule_builder.rb +0 -46
- data/lib/mihari/structs/ipinfo.rb +0 -53
- data/lib/mihari/web/endpoints/exports.rb +0 -0
- data/lib/mihari/web/middleware/error_notification_adapter.rb +0 -35
- data/lib/mihari/web/public/assets/index-81613_nX.js +0 -1763
- data/lib/mihari/web/public/assets/index-Wv6xUrTI.css +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bc368e612c78350f6b66f4fdcf815a63f4a292e185a2348dce8110b7d17e1692
|
4
|
+
data.tar.gz: b3e9939de4ee9488bb6b48793cf5ebe196f50e92eb8f6c1eadb3ed0b201fd2c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 217918561a441c15d4c55e12fa90792230dee66186ad12db8629c30ec848a2a4711eb154d1a008f153abadb3d9cc97ad43ce574d175d167873f3b7c44f0de011
|
7
|
+
data.tar.gz: 2d16db3598a3d5918499ce7119f7e69a548f0891dd6f9c5ee5764508244486c6ba255e190f65cca79559db888936b7975db86eadefb7e548b476fb3925316f32
|
data/.gitignore
CHANGED
@@ -52,20 +52,14 @@ Gemfile.lock
|
|
52
52
|
# rspec
|
53
53
|
.rspec_status
|
54
54
|
|
55
|
-
# solargraph
|
56
|
-
.solargraph.yml
|
57
|
-
|
58
55
|
# SQLite
|
59
56
|
*.db
|
60
57
|
*.db-shm
|
61
58
|
*.db-wal
|
62
59
|
|
63
|
-
# Config
|
64
|
-
mihari.yml
|
65
|
-
|
66
|
-
# Rule
|
67
|
-
rule.yml
|
68
|
-
!lib/mihari/templates/rule.yml
|
69
|
-
|
70
60
|
# Frontend assets
|
71
61
|
lib/mihari/web/public/
|
62
|
+
|
63
|
+
# Rules & Alerts
|
64
|
+
rules/
|
65
|
+
alerts/
|
data/.rubocop.yml
CHANGED
data/Dockerfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
FROM ruby:3.2.2-alpine3.19
|
2
|
+
|
3
|
+
ARG MIHARI_VERSION=0.0.0
|
4
|
+
|
5
|
+
RUN apk --no-cache add build-base ruby-dev libpq-dev && \
|
6
|
+
echo 'gem: --no-document' >> /usr/local/etc/gemrc && \
|
7
|
+
gem install pg && \
|
8
|
+
gem install mihari -v ${MIHARI_VERSION} && \
|
9
|
+
apk del --purge build-base ruby-dev && \
|
10
|
+
rm -rf /usr/local/bundle/cache/*
|
11
|
+
|
12
|
+
ENTRYPOINT ["mihari"]
|
13
|
+
|
14
|
+
CMD ["--help"]
|
data/config.ru
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift("#{__dir__}/../lib")
|
2
|
+
|
3
|
+
require "mihari"
|
4
|
+
require "mihari/web/application"
|
2
5
|
|
3
6
|
require "better_errors"
|
4
7
|
|
5
|
-
|
6
|
-
ENV["RACK_ENV"] ||= "development"
|
8
|
+
ENV["APP_ENV"] ||= "development"
|
7
9
|
|
8
10
|
run Mihari::Web::App.instance
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
version: "3"
|
2
|
+
services:
|
3
|
+
database:
|
4
|
+
image: postgres:16
|
5
|
+
volumes:
|
6
|
+
- postgresql:/var/lib/postgresql/data
|
7
|
+
ports:
|
8
|
+
- ${POSTGRES_PORT:-5432}:5432
|
9
|
+
environment:
|
10
|
+
- POSTGRES_USER=${POSTGRES_USER:-user}
|
11
|
+
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
|
12
|
+
- POSTGRES_DB=${POSTGRES_DB:-mihari}
|
13
|
+
restart: always
|
14
|
+
|
15
|
+
redis:
|
16
|
+
image: "redis/redis-stack:6.2.6-v10"
|
17
|
+
restart: always
|
18
|
+
ports:
|
19
|
+
- ${REDIS_PORT:-6379}:6379
|
20
|
+
- ${REDIS_INSIGHT_PORT:-8001}:8001
|
21
|
+
volumes:
|
22
|
+
- redis:/data
|
23
|
+
|
24
|
+
mihari-init:
|
25
|
+
image: ghcr.io/ninoseki/mihari:latest
|
26
|
+
environment:
|
27
|
+
- DATABASE_URL=${DATABASE_URL:-postgresql://${POSTGRES_USER:-user}:${POSTGRES_PASSWORD:-password}@database:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-mihari}}
|
28
|
+
entrypoint: ["mihari", "db", "migrate"]
|
29
|
+
depends_on:
|
30
|
+
- database
|
31
|
+
|
32
|
+
mihari:
|
33
|
+
image: ghcr.io/ninoseki/mihari:latest
|
34
|
+
ports:
|
35
|
+
- ${MIHARI_PORT:-9292}:9292
|
36
|
+
environment:
|
37
|
+
- DATABASE_URL=${DATABASE_URL:-postgresql://${POSTGRES_USER:-user}:${POSTGRES_PASSWORD:-password}@database:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-mihari}}
|
38
|
+
- REDIS_URL=${REDIS_URL:-redis://redis:${REDIS_PORT:-6379}}
|
39
|
+
env_file:
|
40
|
+
- .env
|
41
|
+
entrypoint: ["mihari", "web", "--host", "0.0.0.0"]
|
42
|
+
restart: always
|
43
|
+
depends_on:
|
44
|
+
- mihari-init
|
45
|
+
|
46
|
+
sidekiq:
|
47
|
+
image: ghcr.io/ninoseki/mihari:latest
|
48
|
+
environment:
|
49
|
+
- DATABASE_URL=${DATABASE_URL:-postgresql://${POSTGRES_USER:-user}:${POSTGRES_PASSWORD:-password}@database:${POSTGRES_PORT:-5432}/${POSTGRES_DB:-mihari}}
|
50
|
+
- SIDEKIQ_REDIS_URL=${REDIS_URL:-redis://redis:${REDIS_PORT:-6379}}
|
51
|
+
env_file:
|
52
|
+
- .env
|
53
|
+
entrypoint: ["mihari", "sidekiq"]
|
54
|
+
restart: always
|
55
|
+
depends_on:
|
56
|
+
- mihari-init
|
57
|
+
- redis
|
58
|
+
|
59
|
+
volumes:
|
60
|
+
postgresql:
|
61
|
+
redis:
|
data/exe/mihari
CHANGED
data/lefthook.yml
CHANGED
@@ -10,3 +10,11 @@ pre-commit:
|
|
10
10
|
glob: "*.{js,ts,vue}"
|
11
11
|
run: npx eslint --fix {staged_files}
|
12
12
|
stage_fixed: true
|
13
|
+
prettier:
|
14
|
+
root: "frontend/"
|
15
|
+
glob: "*.{js,ts,vue}"
|
16
|
+
run: npx prettier --write {staged_files}
|
17
|
+
stage_fixed: true
|
18
|
+
actionlint:
|
19
|
+
glob: ".github/workflows/*.yaml"
|
20
|
+
run: actionlint
|
data/lib/mihari/actor.rb
CHANGED
@@ -7,8 +7,8 @@ module Mihari
|
|
7
7
|
class Actor
|
8
8
|
include Dry::Monads[:result, :try]
|
9
9
|
|
10
|
-
include
|
11
|
-
include
|
10
|
+
include Concerns::Configurable
|
11
|
+
include Concerns::Retriable
|
12
12
|
|
13
13
|
# @return [Hash]
|
14
14
|
attr_reader :options
|
@@ -53,8 +53,8 @@ module Mihari
|
|
53
53
|
def validate_configuration!
|
54
54
|
return if configured?
|
55
55
|
|
56
|
-
joined = configuration_keys.join(", ")
|
57
|
-
be = (configuration_keys.length > 1) ? "are" : "is"
|
56
|
+
joined = self.class.configuration_keys.join(", ")
|
57
|
+
be = (self.class.configuration_keys.length > 1) ? "are" : "is"
|
58
58
|
message = "#{self.class.class_key} is not configured correctly. #{joined} #{be} missing."
|
59
59
|
raise ConfigurationError, message
|
60
60
|
end
|
@@ -79,6 +79,22 @@ module Mihari
|
|
79
79
|
normalized_artifacts
|
80
80
|
end
|
81
81
|
|
82
|
+
def result(...)
|
83
|
+
res = Try[StandardError] do
|
84
|
+
retry_on_error(
|
85
|
+
times: retry_times,
|
86
|
+
interval: retry_interval,
|
87
|
+
exponential_backoff: retry_exponential_backoff
|
88
|
+
) do
|
89
|
+
call(...)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
return res.recover { [] } if ignore_error?
|
94
|
+
|
95
|
+
res.to_result
|
96
|
+
end
|
97
|
+
|
82
98
|
class << self
|
83
99
|
#
|
84
100
|
# Initialize an analyzer by query params
|
@@ -24,8 +24,10 @@ module Mihari
|
|
24
24
|
client.search_with_pagination(query, pagination_limit: pagination_limit).map(&:artifacts).flatten
|
25
25
|
end
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
class << self
|
28
|
+
def configuration_keys
|
29
|
+
%w[binaryedge_api_key]
|
30
|
+
end
|
29
31
|
end
|
30
32
|
|
31
33
|
private
|
@@ -41,11 +41,13 @@ module Mihari
|
|
41
41
|
configuration_keys? || (id? && secret?)
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
class << self
|
45
|
+
#
|
46
|
+
# @return [Array<String>]
|
47
|
+
#
|
48
|
+
def configuration_keys
|
49
|
+
%w[censys_id censys_secret]
|
50
|
+
end
|
49
51
|
end
|
50
52
|
|
51
53
|
private
|
@@ -6,7 +6,7 @@ module Mihari
|
|
6
6
|
# CIRCL passive DNS/SSL analyzer
|
7
7
|
#
|
8
8
|
class CIRCL < Base
|
9
|
-
include
|
9
|
+
include Concerns::Refangable
|
10
10
|
|
11
11
|
# @return [String, nil]
|
12
12
|
attr_reader :type
|
@@ -47,8 +47,10 @@ module Mihari
|
|
47
47
|
configuration_keys? || (username? && password?)
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
|
50
|
+
class << self
|
51
|
+
def configuration_keys
|
52
|
+
%w[circl_passive_password circl_passive_username]
|
53
|
+
end
|
52
54
|
end
|
53
55
|
|
54
56
|
private
|
@@ -9,20 +9,28 @@ module Mihari
|
|
9
9
|
# @return [Boolean]
|
10
10
|
attr_reader :exclude_expired
|
11
11
|
|
12
|
+
# @return [String, nil]
|
13
|
+
attr_reader :match
|
14
|
+
|
12
15
|
#
|
13
16
|
# @param [String] query
|
14
17
|
# @param [Hash, nil] options
|
15
18
|
# @param [Bool] exclude_expired
|
19
|
+
# @param [String, nil] match
|
16
20
|
#
|
17
|
-
def initialize(query, options: nil, exclude_expired: true)
|
21
|
+
def initialize(query, options: nil, exclude_expired: true, match: nil)
|
18
22
|
super(query, options: options)
|
19
23
|
|
20
24
|
@exclude_expired = exclude_expired
|
25
|
+
@match = match
|
21
26
|
end
|
22
27
|
|
23
28
|
def artifacts
|
24
29
|
exclude = exclude_expired ? "expired" : nil
|
25
|
-
client.search(query, exclude: exclude)
|
30
|
+
client.search(query, exclude: exclude, match: match).map do |result|
|
31
|
+
values = result["name_value"].to_s.lines.map(&:chomp).reject { |value| value.starts_with?("*.") }
|
32
|
+
values.map { |value| Models::Artifact.new(data: value, metadata: result) }
|
33
|
+
end.flatten
|
26
34
|
end
|
27
35
|
|
28
36
|
private
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "mihari/feed/reader"
|
4
|
-
require "mihari/feed/parser"
|
5
|
-
|
6
3
|
module Mihari
|
7
4
|
module Analyzers
|
8
5
|
#
|
@@ -10,7 +7,7 @@ module Mihari
|
|
10
7
|
#
|
11
8
|
class Feed < Base
|
12
9
|
# @return [Hash, nil]
|
13
|
-
attr_reader :
|
10
|
+
attr_reader :form
|
14
11
|
|
15
12
|
# @return [Hash, nil]
|
16
13
|
attr_reader :json
|
@@ -37,37 +34,32 @@ module Mihari
|
|
37
34
|
# @param [Hash, nil] headers
|
38
35
|
# @param [Hash, nil] params
|
39
36
|
# @param [Hash, nil] json
|
40
|
-
# @param [
|
37
|
+
# @param [form, nil] form
|
41
38
|
# @param [String] selector
|
42
39
|
#
|
43
|
-
|
44
|
-
|
40
|
+
# @param [Object] url
|
41
|
+
def initialize(url, options: nil, method: "GET", headers: nil, params: nil, json: nil, form: nil, selector: "")
|
42
|
+
super(url, options: options)
|
45
43
|
|
46
44
|
@method = method
|
47
45
|
@headers = headers || {}
|
48
46
|
@params = params
|
49
47
|
@json = json
|
50
|
-
@
|
48
|
+
@form = form
|
51
49
|
@selector = selector
|
52
50
|
end
|
53
51
|
|
54
52
|
def artifacts
|
55
|
-
|
53
|
+
data = Services::FeedReader.call(
|
54
|
+
url, headers: headers, method: method, params: params, json: json, form: form, timeout: timeout
|
55
|
+
)
|
56
|
+
Services::FeedParser.call(data, selector)
|
56
57
|
end
|
57
58
|
|
58
59
|
private
|
59
60
|
|
60
|
-
def
|
61
|
-
|
62
|
-
query,
|
63
|
-
method: method,
|
64
|
-
headers: headers,
|
65
|
-
timeout: timeout,
|
66
|
-
params: params,
|
67
|
-
json: json,
|
68
|
-
data: data
|
69
|
-
)
|
70
|
-
reader.read
|
61
|
+
def url
|
62
|
+
query
|
71
63
|
end
|
72
64
|
end
|
73
65
|
end
|
@@ -31,20 +31,18 @@ module Mihari
|
|
31
31
|
end.flatten.compact
|
32
32
|
end
|
33
33
|
|
34
|
-
def configuration_keys
|
35
|
-
%w[fofa_api_key fofa_email]
|
36
|
-
end
|
37
|
-
|
38
34
|
def configured?
|
39
35
|
api_key? && email?
|
40
36
|
end
|
41
37
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
38
|
+
class << self
|
39
|
+
def configuration_keys
|
40
|
+
%w[fofa_api_key fofa_email]
|
41
|
+
end
|
46
42
|
end
|
47
43
|
|
44
|
+
private
|
45
|
+
|
48
46
|
def email?
|
49
47
|
!email.nil?
|
50
48
|
end
|
data/lib/mihari/analyzers/otx.rb
CHANGED
@@ -6,7 +6,7 @@ module Mihari
|
|
6
6
|
# OTX analyzer
|
7
7
|
#
|
8
8
|
class OTX < Base
|
9
|
-
include
|
9
|
+
include Concerns::Refangable
|
10
10
|
|
11
11
|
# @return [String, nil]
|
12
12
|
attr_reader :type
|
@@ -38,8 +38,10 @@ module Mihari
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
41
|
+
class << self
|
42
|
+
def configuration_keys
|
43
|
+
%w[otx_api_key]
|
44
|
+
end
|
43
45
|
end
|
44
46
|
|
45
47
|
private
|
@@ -6,7 +6,7 @@ module Mihari
|
|
6
6
|
# PassiveTotal analyzer
|
7
7
|
#
|
8
8
|
class PassiveTotal < Base
|
9
|
-
include
|
9
|
+
include Concerns::Refangable
|
10
10
|
|
11
11
|
# @return [String, nil]
|
12
12
|
attr_reader :type
|
@@ -35,11 +35,11 @@ module Mihari
|
|
35
35
|
def artifacts
|
36
36
|
case type
|
37
37
|
when "domain", "ip"
|
38
|
-
|
38
|
+
passive_dns_search
|
39
39
|
when "mail"
|
40
|
-
|
40
|
+
reverse_whois_search
|
41
41
|
when "hash"
|
42
|
-
|
42
|
+
ssl_search
|
43
43
|
else
|
44
44
|
raise ValueError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
45
45
|
end
|
@@ -49,11 +49,11 @@ module Mihari
|
|
49
49
|
configuration_keys? || (username? && api_key?)
|
50
50
|
end
|
51
51
|
|
52
|
-
def configuration_keys
|
53
|
-
%w[passivetotal_username passivetotal_api_key]
|
54
|
-
end
|
55
|
-
|
56
52
|
class << self
|
53
|
+
def configuration_keys
|
54
|
+
%w[passivetotal_username passivetotal_api_key]
|
55
|
+
end
|
56
|
+
|
57
57
|
#
|
58
58
|
# @return [Array<String>, nil]
|
59
59
|
#
|
@@ -64,6 +64,27 @@ module Mihari
|
|
64
64
|
|
65
65
|
private
|
66
66
|
|
67
|
+
def passive_dns_search
|
68
|
+
res = client.passive_dns_search(query)
|
69
|
+
res["results"] || []
|
70
|
+
end
|
71
|
+
|
72
|
+
def reverse_whois_search
|
73
|
+
res = client.reverse_whois_search(query)
|
74
|
+
(res["results"] || []).map do |result|
|
75
|
+
data = result["domain"]
|
76
|
+
Models::Artifact.new(data: data, metadata: result)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def ssl_search
|
81
|
+
res = client.ssl_search(query)
|
82
|
+
(res["results"] || []).map do |result|
|
83
|
+
data = result["ipAddresses"]
|
84
|
+
data.map { |d| Models::Artifact.new(data: d, metadata: result) }
|
85
|
+
end.flatten
|
86
|
+
end
|
87
|
+
|
67
88
|
def client
|
68
89
|
Clients::PassiveTotal.new(username: username, api_key: api_key, timeout: timeout)
|
69
90
|
end
|
@@ -80,10 +101,6 @@ module Mihari
|
|
80
101
|
def username?
|
81
102
|
!username.nil?
|
82
103
|
end
|
83
|
-
|
84
|
-
def api_key?
|
85
|
-
!api_key.nil?
|
86
|
-
end
|
87
104
|
end
|
88
105
|
end
|
89
106
|
end
|
@@ -6,7 +6,7 @@ module Mihari
|
|
6
6
|
# Pulsedive analyzer
|
7
7
|
#
|
8
8
|
class Pulsedive < Base
|
9
|
-
include
|
9
|
+
include Concerns::Refangable
|
10
10
|
|
11
11
|
# @return [String, nil]
|
12
12
|
attr_reader :type
|
@@ -43,8 +43,10 @@ module Mihari
|
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
|
47
|
-
|
46
|
+
class << self
|
47
|
+
def configuration_keys
|
48
|
+
%w[pulsedive_api_key]
|
49
|
+
end
|
48
50
|
end
|
49
51
|
|
50
52
|
private
|
@@ -6,7 +6,7 @@ module Mihari
|
|
6
6
|
# SecurityTrails
|
7
7
|
#
|
8
8
|
class SecurityTrails < Base
|
9
|
-
include
|
9
|
+
include Concerns::Refangable
|
10
10
|
|
11
11
|
# @return [String, nil]
|
12
12
|
attr_reader :type
|
@@ -33,21 +33,21 @@ module Mihari
|
|
33
33
|
def artifacts
|
34
34
|
case type
|
35
35
|
when "domain"
|
36
|
-
|
36
|
+
domain_search
|
37
37
|
when "ip"
|
38
|
-
|
38
|
+
ip_search
|
39
39
|
when "mail"
|
40
|
-
|
40
|
+
mail_search
|
41
41
|
else
|
42
42
|
raise ValueError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
|
-
def configuration_keys
|
47
|
-
%w[securitytrails_api_key]
|
48
|
-
end
|
49
|
-
|
50
46
|
class << self
|
47
|
+
def configuration_keys
|
48
|
+
%w[securitytrails_api_key]
|
49
|
+
end
|
50
|
+
|
51
51
|
#
|
52
52
|
# @return [Array<String>, nil]
|
53
53
|
#
|
@@ -58,6 +58,30 @@ module Mihari
|
|
58
58
|
|
59
59
|
private
|
60
60
|
|
61
|
+
def domain_search
|
62
|
+
client.get_all_dns_history(query, type: "a").map do |res|
|
63
|
+
(res["records"] || []).map do |record|
|
64
|
+
(record["values"] || []).map { |value| value["ip"] }
|
65
|
+
end.flatten.compact.uniq
|
66
|
+
end.flatten
|
67
|
+
end
|
68
|
+
|
69
|
+
def ip_search
|
70
|
+
res = client.ip_search(query)
|
71
|
+
(res["records"] || []).filter_map do |record|
|
72
|
+
data = record["hostname"]
|
73
|
+
Models::Artifact.new(data: data, metadata: record)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def mail_search
|
78
|
+
res = client.mail_search(query)
|
79
|
+
(res["records"] || []).filter_map do |record|
|
80
|
+
data = record["hostname"]
|
81
|
+
Models::Artifact.new(data: data, metadata: record)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
61
85
|
def client
|
62
86
|
Clients::SecurityTrails.new(api_key: api_key, timeout: timeout)
|
63
87
|
end
|