mihari 2.0.0 → 2.3.1
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/.github/ISSUE_TEMPLATE/bug_report.md +43 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +15 -0
- data/.rubocop.yml +6 -0
- data/.standard.yml +4 -0
- data/README.md +11 -1
- data/bin/console +1 -0
- data/docker/Dockerfile +3 -2
- data/examples/ipinfo_hosted_domains.rb +1 -1
- data/images/{eyecatch.png → overview.png} +0 -0
- data/images/tines.png +0 -0
- data/images/web_alerts.png +0 -0
- data/images/web_config.png +0 -0
- data/lib/mihari/analyzers/base.rb +11 -2
- data/lib/mihari/analyzers/circl.rb +3 -3
- data/lib/mihari/analyzers/onyphe.rb +2 -2
- data/lib/mihari/analyzers/securitytrails.rb +2 -2
- data/lib/mihari/analyzers/urlscan.rb +1 -6
- data/lib/mihari/analyzers/zoomeye.rb +2 -2
- data/lib/mihari/cli.rb +72 -289
- data/lib/mihari/commands/binaryedge.rb +21 -0
- data/lib/mihari/commands/censys.rb +22 -0
- data/lib/mihari/commands/circl.rb +21 -0
- data/lib/mihari/commands/config.rb +27 -0
- data/lib/mihari/commands/crtsh.rb +22 -0
- data/lib/mihari/commands/dnpedia.rb +21 -0
- data/lib/mihari/commands/dnstwister.rb +21 -0
- data/lib/mihari/commands/free_text.rb +21 -0
- data/lib/mihari/commands/http_hash.rb +25 -0
- data/lib/mihari/commands/json.rb +42 -0
- data/lib/mihari/commands/onyphe.rb +21 -0
- data/lib/mihari/commands/otx.rb +21 -0
- data/lib/mihari/commands/passive_dns.rb +21 -0
- data/lib/mihari/commands/passive_ssl.rb +21 -0
- data/lib/mihari/commands/passivetotal.rb +21 -0
- data/lib/mihari/commands/pulsedive.rb +21 -0
- data/lib/mihari/commands/reverse_whois.rb +21 -0
- data/lib/mihari/commands/securitytrails.rb +22 -0
- data/lib/mihari/commands/securitytrails_domain_feed.rb +23 -0
- data/lib/mihari/commands/shodan.rb +21 -0
- data/lib/mihari/commands/spyse.rb +22 -0
- data/lib/mihari/commands/ssh_fingerprint.rb +21 -0
- data/lib/mihari/commands/urlscan.rb +23 -0
- data/lib/mihari/commands/virustotal.rb +21 -0
- data/lib/mihari/commands/web.rb +22 -0
- data/lib/mihari/commands/zoomeye.rb +22 -0
- data/lib/mihari/config.rb +14 -3
- data/lib/mihari/configurable.rb +1 -1
- data/lib/mihari/emitters/slack.rb +4 -4
- data/lib/mihari/emitters/the_hive.rb +1 -1
- data/lib/mihari/models/alert.rb +5 -5
- data/lib/mihari/models/artifact.rb +13 -2
- data/lib/mihari/notifiers/exception_notifier.rb +4 -4
- data/lib/mihari/status.rb +2 -10
- data/lib/mihari/version.rb +1 -1
- data/lib/mihari/web/app.rb +25 -100
- data/lib/mihari/web/controllers/alerts_controller.rb +75 -0
- data/lib/mihari/web/controllers/artifacts_controller.rb +24 -0
- data/lib/mihari/web/controllers/base_controller.rb +22 -0
- data/lib/mihari/web/controllers/command_controller.rb +26 -0
- data/lib/mihari/web/controllers/config_controller.rb +13 -0
- data/lib/mihari/web/controllers/sources_controller.rb +12 -0
- data/lib/mihari/web/controllers/tags_controller.rb +28 -0
- data/lib/mihari/web/helpers/json.rb +53 -0
- data/lib/mihari/web/public/index.html +2 -2
- data/lib/mihari/web/public/redoc-static.html +519 -0
- data/lib/mihari/web/public/static/js/{app.58b32d15.js → app.cccddb2b.js} +4 -4
- data/lib/mihari/web/public/static/js/app.cccddb2b.js.map +1 -0
- data/mihari.gemspec +9 -3
- metadata +146 -23
- data/lib/mihari/web/public/static/js/app.58b32d15.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: ea3de689646f7be03616ac03315aeca9afb34ba16737ad0f706b508374bdd214
|
4
|
+
data.tar.gz: cbd5e02b4e8466c195e8311a4d50668763a3d8b40aefd0ab4a7478979b4725c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 388d1f90bd35a7819418d230703b6d20fe9b8118d64cbd90fea3802b1c80d04337ee8507d26bc4de57e13867f51593be820416eedea52984392ef9e203783707
|
7
|
+
data.tar.gz: f3c48426a7bc6c4334870d9de1fc57085094f783e8e1c6aca3ac043b9f42ff738b79457bb9cf93edeecb8d6b9976c3fe4cd5e70a0bdb306c71f767fa20c333c8
|
@@ -0,0 +1,43 @@
|
|
1
|
+
---
|
2
|
+
name: Bug report
|
3
|
+
about: Create a bug report to help us improve
|
4
|
+
title: "[BUG]"
|
5
|
+
labels: bug
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
<!--
|
11
|
+
Thank you for taking the time to report a bug.
|
12
|
+
Please make sure there is no existing issue about this kind of bug.
|
13
|
+
-->
|
14
|
+
|
15
|
+
### **Describe the bug**
|
16
|
+
|
17
|
+
A clear and concise description of what the bug is.
|
18
|
+
|
19
|
+
### **Steps to reproduce**
|
20
|
+
|
21
|
+
- ...
|
22
|
+
|
23
|
+
### **Expected behavior**
|
24
|
+
|
25
|
+
A clear and concise description of what you expected to happen.
|
26
|
+
|
27
|
+
### **Actual behavior**
|
28
|
+
|
29
|
+
A clear and concise description of what actually happened.
|
30
|
+
|
31
|
+
### **Screenshots**
|
32
|
+
|
33
|
+
Add screenshots to help explain your problem.
|
34
|
+
|
35
|
+
### **System Information:**
|
36
|
+
|
37
|
+
- OS: [e.g. Windows10]
|
38
|
+
- Ruby version: [e.g. 3.0]
|
39
|
+
- Mihari version: [e.g. 2.0.0]
|
40
|
+
|
41
|
+
### **Additional context**
|
42
|
+
|
43
|
+
Add any other context about the problem here.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
name: Feature request
|
3
|
+
about: Suggest a new Feature for Mihari
|
4
|
+
title: "[Feature Request]"
|
5
|
+
labels: enhancement
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
<!--
|
10
|
+
|
11
|
+
1. Make sure your requested feature makes sense for Mihari.
|
12
|
+
|
13
|
+
2. If you want to suggest a new integration of a service, please provide detailed information of it. (e.g. API docs)
|
14
|
+
|
15
|
+
-->
|
data/.rubocop.yml
CHANGED
@@ -4,6 +4,9 @@
|
|
4
4
|
require:
|
5
5
|
- rubocop-performance
|
6
6
|
|
7
|
+
AllCops:
|
8
|
+
NewCops: enable
|
9
|
+
|
7
10
|
Style/Alias:
|
8
11
|
Enabled: false
|
9
12
|
StyleGuide: https://relaxed.ruby.style/#stylealias
|
@@ -151,5 +154,8 @@ Lint/AssignmentInCondition:
|
|
151
154
|
Layout/LineLength:
|
152
155
|
Enabled: false
|
153
156
|
|
157
|
+
Style/StringLiteralsInInterpolation:
|
158
|
+
Enabled: false
|
159
|
+
|
154
160
|
Metrics:
|
155
161
|
Enabled: false
|
data/.standard.yml
ADDED
data/README.md
CHANGED
@@ -8,10 +8,14 @@
|
|
8
8
|
|
9
9
|

|
10
10
|
|
11
|
+
[](https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki)
|
12
|
+
|
11
13
|
Mihari is a framework for continuous OSINT based threat hunting.
|
12
14
|
|
13
15
|
## How it works
|
14
16
|
|
17
|
+

|
18
|
+
|
15
19
|
- Mihari makes a query against Shodan, Censys, VirusTotal, SecurityTrails, etc. and extracts artifacts (IP addresses, domains, URLs or hashes).
|
16
20
|
- Mihari checks whether a DB (SQLite3, PostgreSQL or MySQL) contains the artifacts or not.
|
17
21
|
- If it doesn't contain the artifacts:
|
@@ -50,10 +54,16 @@ See [Usage](https://github.com/ninoseki/mihari/wiki/Usage) for more information.
|
|
50
54
|
|
51
55
|
- [Requirements & Installation](https://github.com/ninoseki/mihari/wiki/Requirements-&-Installation)
|
52
56
|
- [Usage](https://github.com/ninoseki/mihari/wiki/Usage)
|
57
|
+
- [Built-in Web App](https://github.com/ninoseki/mihari/wiki/Built-in-Web-App)
|
53
58
|
- [Configuration](https://github.com/ninoseki/mihari/wiki/Configuration)
|
54
|
-
- [Custom
|
59
|
+
- [Custom Script](https://github.com/ninoseki/mihari/wiki/Custom-Script)
|
55
60
|
- [Docker](https://github.com/ninoseki/mihari/wiki/Docker)
|
61
|
+
- [GitHub Actions](https://github.com/ninoseki/mihari/wiki/GitHub-Actions)
|
56
62
|
|
57
63
|
## License
|
58
64
|
|
59
65
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
66
|
+
|
67
|
+
## Acknowledgement
|
68
|
+
|
69
|
+
Mihari is proudly supported by [Tines.io](https://tines.io?utm_source=github&utm_medium=sponsorship&utm_campaign=ninoseki), The SOAR Platform for Enterprise Security Teams.
|
data/bin/console
CHANGED
data/docker/Dockerfile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
-
FROM ruby:3.0.
|
1
|
+
FROM ruby:3.0.1-alpine3.13
|
2
2
|
|
3
|
-
RUN apk --no-cache add git build-base ruby-dev sqlite-dev postgresql-dev \
|
3
|
+
RUN apk --no-cache add git build-base ruby-dev sqlite-dev postgresql-dev mysql-client mysql-dev \
|
4
|
+
&& gem install pg mysql2 \
|
4
5
|
&& cd /tmp/ \
|
5
6
|
&& git clone https://github.com/ninoseki/mihari.git \
|
6
7
|
&& cd mihari \
|
File without changes
|
data/images/tines.png
ADDED
Binary file
|
data/images/web_alerts.png
CHANGED
Binary file
|
data/images/web_config.png
CHANGED
Binary file
|
@@ -8,6 +8,13 @@ module Mihari
|
|
8
8
|
include Configurable
|
9
9
|
include Retriable
|
10
10
|
|
11
|
+
attr_accessor :ignore_old_artifacts, :ignore_threshold
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@ignore_old_artifacts = false
|
15
|
+
@ignore_threshold = 0
|
16
|
+
end
|
17
|
+
|
11
18
|
# @return [Array<String>, Array<Mihari::Artifact>]
|
12
19
|
def artifacts
|
13
20
|
raise NotImplementedError, "You must implement #{self.class}##{__method__}"
|
@@ -42,7 +49,7 @@ module Mihari
|
|
42
49
|
|
43
50
|
def run_emitter(emitter)
|
44
51
|
emitter.run(title: title, description: description, artifacts: unique_artifacts, source: source, tags: tags)
|
45
|
-
rescue => e
|
52
|
+
rescue StandardError => e
|
46
53
|
puts "Emission by #{emitter.class} is failed: #{e}"
|
47
54
|
end
|
48
55
|
|
@@ -61,7 +68,9 @@ module Mihari
|
|
61
68
|
|
62
69
|
# @return [Array<Mihari::Artifact>]
|
63
70
|
def unique_artifacts
|
64
|
-
@unique_artifacts ||= normalized_artifacts.select
|
71
|
+
@unique_artifacts ||= normalized_artifacts.select do |artifact|
|
72
|
+
artifact.unique?(ignore_old_artifacts: ignore_old_artifacts, ignore_threshold: ignore_threshold)
|
73
|
+
end
|
65
74
|
end
|
66
75
|
|
67
76
|
def set_unique_artifacts
|
@@ -46,14 +46,14 @@ module Mihari
|
|
46
46
|
def passive_dns_lookup
|
47
47
|
results = api.dns.query(@query)
|
48
48
|
results.map do |result|
|
49
|
-
type = result
|
50
|
-
type == "A" ? result
|
49
|
+
type = result["rrtype"]
|
50
|
+
type == "A" ? result["rdata"] : nil
|
51
51
|
end.compact.uniq
|
52
52
|
end
|
53
53
|
|
54
54
|
def passive_ssl_lookup
|
55
55
|
result = api.ssl.cquery(@query)
|
56
|
-
seen = result
|
56
|
+
seen = result["seen"] || []
|
57
57
|
seen.uniq
|
58
58
|
end
|
59
59
|
end
|
@@ -21,10 +21,10 @@ module Mihari
|
|
21
21
|
return [] unless results
|
22
22
|
|
23
23
|
flat_results = results.map do |result|
|
24
|
-
result
|
24
|
+
result["results"]
|
25
25
|
end.flatten.compact
|
26
26
|
|
27
|
-
flat_results.map { |result| result
|
27
|
+
flat_results.map { |result| result["ip"] }.compact.uniq
|
28
28
|
end
|
29
29
|
|
30
30
|
private
|
@@ -58,13 +58,13 @@ module Mihari
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def ip_lookup
|
61
|
-
result = api.domains.search(filter: {ipv4: query})
|
61
|
+
result = api.domains.search(filter: { ipv4: query })
|
62
62
|
records = result["records"] || []
|
63
63
|
records.map { |record| record["hostname"] }.compact.uniq
|
64
64
|
end
|
65
65
|
|
66
66
|
def mail_lookup
|
67
|
-
result = api.domains.search(filter: {whois_email: query})
|
67
|
+
result = api.domains.search(filter: { whois_email: query })
|
68
68
|
records = result["records"] || []
|
69
69
|
records.map { |record| record["hostname"] }.compact.uniq
|
70
70
|
end
|
@@ -5,16 +5,14 @@ require "urlscan"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class Urlscan < Base
|
8
|
-
attr_reader :title, :description, :query, :tags, :
|
8
|
+
attr_reader :title, :description, :query, :tags, :target_type, :use_similarity
|
9
9
|
|
10
10
|
def initialize(
|
11
11
|
query,
|
12
12
|
description: nil,
|
13
|
-
filter: nil,
|
14
13
|
tags: [],
|
15
14
|
target_type: "url",
|
16
15
|
title: nil,
|
17
|
-
use_pro: false,
|
18
16
|
use_similarity: false
|
19
17
|
)
|
20
18
|
super()
|
@@ -24,9 +22,7 @@ module Mihari
|
|
24
22
|
@description = description || "query = #{query}"
|
25
23
|
@tags = tags
|
26
24
|
|
27
|
-
@filter = filter
|
28
25
|
@target_type = target_type
|
29
|
-
@use_pro = use_pro
|
30
26
|
@use_similarity = use_similarity
|
31
27
|
|
32
28
|
raise InvalidInputError, "type should be url, domain or ip." unless valid_target_type?
|
@@ -54,7 +50,6 @@ module Mihari
|
|
54
50
|
|
55
51
|
def search
|
56
52
|
return api.pro.similar(query) if use_similarity
|
57
|
-
return api.pro.search(query: query, filter: filter, size: 10_000) if use_pro
|
58
53
|
|
59
54
|
api.search(query, size: 10_000)
|
60
55
|
end
|
@@ -37,11 +37,11 @@ module Mihari
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def config_keys
|
40
|
-
%w[
|
40
|
+
%w[zoomeye_api_key]
|
41
41
|
end
|
42
42
|
|
43
43
|
def api
|
44
|
-
@api ||= ::ZoomEye::API.new(
|
44
|
+
@api ||= ::ZoomEye::API.new(api_key: Mihari.config.zoomeye_api_key)
|
45
45
|
end
|
46
46
|
|
47
47
|
def convert_responses(responses)
|
data/lib/mihari/cli.rb
CHANGED
@@ -1,293 +1,76 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "json"
|
4
|
-
require "rack/builder"
|
5
|
-
require "rack/handler/webrick"
|
6
3
|
require "thor"
|
7
4
|
|
5
|
+
require "mihari/commands/binaryedge"
|
6
|
+
require "mihari/commands/censys"
|
7
|
+
require "mihari/commands/circl"
|
8
|
+
require "mihari/commands/crtsh"
|
9
|
+
require "mihari/commands/dnpedia"
|
10
|
+
require "mihari/commands/dnstwister"
|
11
|
+
require "mihari/commands/onyphe"
|
12
|
+
require "mihari/commands/otx"
|
13
|
+
require "mihari/commands/passivetotal"
|
14
|
+
require "mihari/commands/pulsedive"
|
15
|
+
require "mihari/commands/securitytrails_domain_feed"
|
16
|
+
require "mihari/commands/securitytrails"
|
17
|
+
require "mihari/commands/shodan"
|
18
|
+
require "mihari/commands/spyse"
|
19
|
+
require "mihari/commands/urlscan"
|
20
|
+
require "mihari/commands/virustotal"
|
21
|
+
require "mihari/commands/zoomeye"
|
22
|
+
|
23
|
+
require "mihari/commands/free_text"
|
24
|
+
require "mihari/commands/http_hash"
|
25
|
+
require "mihari/commands/passive_dns"
|
26
|
+
require "mihari/commands/passive_ssl"
|
27
|
+
require "mihari/commands/reverse_whois"
|
28
|
+
require "mihari/commands/ssh_fingerprint"
|
29
|
+
|
30
|
+
require "mihari/commands/config"
|
31
|
+
require "mihari/commands/json"
|
32
|
+
require "mihari/commands/web"
|
33
|
+
|
8
34
|
module Mihari
|
9
35
|
class CLI < Thor
|
10
|
-
class_option :config, type: :string, desc: "
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
|
47
|
-
desc "urlscan [QUERY]", "urlscan search by a given query"
|
48
|
-
method_option :title, type: :string, desc: "title"
|
49
|
-
method_option :description, type: :string, desc: "description"
|
50
|
-
method_option :tags, type: :array, desc: "tags"
|
51
|
-
method_option :filter, type: :string, desc: "filter for urlscan pro search"
|
52
|
-
method_option :target_type, type: :string, default: "url", desc: "target type to fetch from lookup results (target type should be 'url', 'domain' or 'ip')"
|
53
|
-
method_option :use_pro, type: :boolean, default: false, desc: "use pro search API or not"
|
54
|
-
method_option :use_similarity, type: :boolean, default: false, desc: "use similarity API or not"
|
55
|
-
def urlscan(query)
|
56
|
-
with_error_handling do
|
57
|
-
run_analyzer Analyzers::Urlscan, query: query, options: options
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
desc "virustotal [IP|DOMAIN]", "VirusTotal resolutions lookup by an ip or domain"
|
62
|
-
method_option :title, type: :string, desc: "title"
|
63
|
-
method_option :description, type: :string, desc: "description"
|
64
|
-
method_option :tags, type: :array, desc: "tags"
|
65
|
-
def virustotal(indiactor)
|
66
|
-
with_error_handling do
|
67
|
-
run_analyzer Analyzers::VirusTotal, query: refang(indiactor), options: options
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
desc "securitytrails [IP|DOMAIN|EMAIL]", "SecurityTrails lookup by an ip, domain or email"
|
72
|
-
method_option :title, type: :string, desc: "title"
|
73
|
-
method_option :description, type: :string, desc: "description"
|
74
|
-
method_option :tags, type: :array, desc: "tags"
|
75
|
-
def securitytrails(indiactor)
|
76
|
-
with_error_handling do
|
77
|
-
run_analyzer Analyzers::SecurityTrails, query: refang(indiactor), options: options
|
78
|
-
end
|
79
|
-
end
|
80
|
-
map "st" => :securitytrails
|
81
|
-
|
82
|
-
desc "securitytrails_domain_feed [REGEXP]", "SecurityTrails new domain feed search by a regexp"
|
83
|
-
method_option :title, type: :string, desc: "title"
|
84
|
-
method_option :description, type: :string, desc: "description"
|
85
|
-
method_option :tags, type: :array, desc: "tags"
|
86
|
-
method_option :type, type: :string, default: "registered", desc: "A type of domain feed ('all', 'new' or 'registered')"
|
87
|
-
def securitytrails_domain_feed(regexp)
|
88
|
-
with_error_handling do
|
89
|
-
run_analyzer Analyzers::SecurityTrailsDomainFeed, query: regexp, options: options
|
90
|
-
end
|
91
|
-
end
|
92
|
-
map "st_domain_feed" => :securitytrails_domain_feed
|
93
|
-
|
94
|
-
desc "crtsh [QUERY]", "crt.sh search by a query"
|
95
|
-
method_option :title, type: :string, desc: "title"
|
96
|
-
method_option :description, type: :string, desc: "description"
|
97
|
-
method_option :tags, type: :array, desc: "tags"
|
98
|
-
method_option :exclude_expired, type: :boolean, desc: "exclude expired certificates"
|
99
|
-
def crtsh(query)
|
100
|
-
with_error_handling do
|
101
|
-
run_analyzer Analyzers::Crtsh, query: query, options: options
|
36
|
+
class_option :config, type: :string, desc: "Path to the config file"
|
37
|
+
|
38
|
+
class_option :ignore_old_artifacts, type: :boolean, default: false, desc: "Whether to ignore old artifacts from checking or not. Only affects with analyze commands."
|
39
|
+
class_option :ignore_threshold, type: :numeric, default: 0, desc: "Number of days to define whether an artifact is old or not. Only affects with analyze commands."
|
40
|
+
|
41
|
+
include Mihari::Commands::BinaryEdge
|
42
|
+
include Mihari::Commands::Censys
|
43
|
+
include Mihari::Commands::CIRCL
|
44
|
+
include Mihari::Commands::Config
|
45
|
+
include Mihari::Commands::Crtsh
|
46
|
+
include Mihari::Commands::DNPedia
|
47
|
+
include Mihari::Commands::DNSTwister
|
48
|
+
include Mihari::Commands::FreeText
|
49
|
+
include Mihari::Commands::HTTPHash
|
50
|
+
include Mihari::Commands::JSON
|
51
|
+
include Mihari::Commands::Onyphe
|
52
|
+
include Mihari::Commands::OTX
|
53
|
+
include Mihari::Commands::PassiveDNS
|
54
|
+
include Mihari::Commands::PassiveSSL
|
55
|
+
include Mihari::Commands::PassiveTotal
|
56
|
+
include Mihari::Commands::Pulsedive
|
57
|
+
include Mihari::Commands::ReverseWhois
|
58
|
+
include Mihari::Commands::SecurityTrails
|
59
|
+
include Mihari::Commands::SecurityTrailsDomainFeed
|
60
|
+
include Mihari::Commands::Shodan
|
61
|
+
include Mihari::Commands::Spyse
|
62
|
+
include Mihari::Commands::SSHFingerprint
|
63
|
+
include Mihari::Commands::Urlscan
|
64
|
+
include Mihari::Commands::VirusTotal
|
65
|
+
include Mihari::Commands::Web
|
66
|
+
include Mihari::Commands::ZoomEye
|
67
|
+
|
68
|
+
class << self
|
69
|
+
def exit_on_failure?
|
70
|
+
true
|
102
71
|
end
|
103
72
|
end
|
104
73
|
|
105
|
-
desc "dnpedia [QUERY]", "DNPedia domain search by a query"
|
106
|
-
method_option :title, type: :string, desc: "title"
|
107
|
-
method_option :description, type: :string, desc: "description"
|
108
|
-
method_option :tags, type: :array, desc: "tags"
|
109
|
-
def dnpedia(query)
|
110
|
-
with_error_handling do
|
111
|
-
run_analyzer Analyzers::DNPedia, query: query, options: options
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
desc "circl [DOMAIN|SHA1]", "CIRCL passive DNS/SSL lookup by a domain or SHA1 certificate fingerprint"
|
116
|
-
method_option :title, type: :string, desc: "title"
|
117
|
-
method_option :description, type: :string, desc: "description"
|
118
|
-
method_option :tags, type: :array, desc: "tags"
|
119
|
-
def circl(query)
|
120
|
-
with_error_handling do
|
121
|
-
run_analyzer Analyzers::CIRCL, query: refang(query), options: options
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
desc "passivetotal [IP|DOMAIN|EMAIL|SHA1]", "PassiveTotal lookup by an ip, domain, email or SHA1 certificate fingerprint"
|
126
|
-
method_option :title, type: :string, desc: "title"
|
127
|
-
method_option :description, type: :string, desc: "description"
|
128
|
-
method_option :tags, type: :array, desc: "tags"
|
129
|
-
def passivetotal(indicator)
|
130
|
-
with_error_handling do
|
131
|
-
run_analyzer Analyzers::PassiveTotal, query: refang(indicator), options: options
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
desc "zoomeye [QUERY]", "ZoomEye search by a query"
|
136
|
-
method_option :title, type: :string, desc: "title"
|
137
|
-
method_option :description, type: :string, desc: "description"
|
138
|
-
method_option :tags, type: :array, desc: "tags"
|
139
|
-
method_option :type, type: :string, desc: "type to search(host / web)", default: "host"
|
140
|
-
def zoomeye(query)
|
141
|
-
with_error_handling do
|
142
|
-
run_analyzer Analyzers::ZoomEye, query: query, options: options
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
desc "binaryedge [QUERY]", "BinaryEdge host search by a query"
|
147
|
-
method_option :title, type: :string, desc: "title"
|
148
|
-
method_option :description, type: :string, desc: "description"
|
149
|
-
method_option :tags, type: :array, desc: "tags"
|
150
|
-
def binaryedge(query)
|
151
|
-
with_error_handling do
|
152
|
-
run_analyzer Analyzers::BinaryEdge, query: query, options: options
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
desc "pulsedive [IP|DOMAIN]", "Pulsedive lookup by an ip or domain"
|
157
|
-
method_option :title, type: :string, desc: "title"
|
158
|
-
method_option :description, type: :string, desc: "description"
|
159
|
-
method_option :tags, type: :array, desc: "tags"
|
160
|
-
def pulsedive(indiactor)
|
161
|
-
with_error_handling do
|
162
|
-
run_analyzer Analyzers::Pulsedive, query: refang(indiactor), options: options
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
desc "dnstwister [DOMAIN]", "dnstwister lookup by a domain"
|
167
|
-
method_option :title, type: :string, desc: "title"
|
168
|
-
method_option :description, type: :string, desc: "description"
|
169
|
-
method_option :tags, type: :array, desc: "tags"
|
170
|
-
def dnstwister(domain)
|
171
|
-
with_error_handling do
|
172
|
-
run_analyzer Analyzers::DNSTwister, query: refang(domain), options: options
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
desc "otx [IP|DOMAIN]", "OTX lookup by an IP or domain"
|
177
|
-
method_option :title, type: :string, desc: "title"
|
178
|
-
method_option :description, type: :string, desc: "description"
|
179
|
-
method_option :tags, type: :array, desc: "tags"
|
180
|
-
def otx(domain)
|
181
|
-
with_error_handling do
|
182
|
-
run_analyzer Analyzers::OTX, query: refang(domain), options: options
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
desc "spyse [QUERY]", "Spyse search by a query"
|
187
|
-
method_option :title, type: :string, desc: "title"
|
188
|
-
method_option :description, type: :string, desc: "description"
|
189
|
-
method_option :tags, type: :array, desc: "tags"
|
190
|
-
method_option :type, type: :string, desc: "type to search (ip or domain)", default: "doamin"
|
191
|
-
def spyse(query)
|
192
|
-
with_error_handling do
|
193
|
-
run_analyzer Analyzers::Spyse, query: query, options: options
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
desc "passive_dns [IP|DOMAIN]", "Cross search with passive DNS services by an ip or domain"
|
198
|
-
method_option :title, type: :string, desc: "title"
|
199
|
-
method_option :description, type: :string, desc: "description"
|
200
|
-
method_option :tags, type: :array, desc: "tags"
|
201
|
-
def passive_dns(query)
|
202
|
-
with_error_handling do
|
203
|
-
run_analyzer Analyzers::PassiveDNS, query: refang(query), options: options
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
desc "passive_ssl [SHA1]", "Cross search with passive SSL services by an SHA1 certificate fingerprint"
|
208
|
-
method_option :title, type: :string, desc: "title"
|
209
|
-
method_option :description, type: :string, desc: "description"
|
210
|
-
method_option :tags, type: :array, desc: "tags"
|
211
|
-
def passive_ssl(query)
|
212
|
-
with_error_handling do
|
213
|
-
run_analyzer Analyzers::PassiveSSL, query: query, options: options
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
desc "reverse_whois [EMAIL]", "Cross search with reverse whois services by an email"
|
218
|
-
method_option :title, type: :string, desc: "title"
|
219
|
-
method_option :description, type: :string, desc: "description"
|
220
|
-
method_option :tags, type: :array, desc: "tags"
|
221
|
-
def reverse_whois(query)
|
222
|
-
with_error_handling do
|
223
|
-
run_analyzer Analyzers::ReveseWhois, query: refang(query), options: options
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
desc "http_hash", "Cross search with search engines by a hash of an HTTP response (SHA256, MD5 and MurmurHash3)"
|
228
|
-
method_option :title, type: :string, desc: "title"
|
229
|
-
method_option :description, type: :string, desc: "description"
|
230
|
-
method_option :tags, type: :array, desc: "tags"
|
231
|
-
method_option :md5, type: :string, desc: "MD5 hash"
|
232
|
-
method_option :sha256, type: :string, desc: "SHA256 hash"
|
233
|
-
method_option :mmh3, type: :numeric, desc: "MurmurHash3 hash"
|
234
|
-
method_option :html, type: :string, desc: "path to an HTML file"
|
235
|
-
def http_hash
|
236
|
-
with_error_handling do
|
237
|
-
run_analyzer Analyzers::HTTPHash, query: nil, options: options
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
desc "free_text [TEXT]", "Cross search with search engines by a free text"
|
242
|
-
method_option :title, type: :string, desc: "title"
|
243
|
-
method_option :description, type: :string, desc: "description"
|
244
|
-
method_option :tags, type: :array, desc: "tags"
|
245
|
-
def free_text(text)
|
246
|
-
with_error_handling do
|
247
|
-
run_analyzer Analyzers::FreeText, query: text, options: options
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
desc "ssh_fingerprint [FINGERPRINT]", "Cross search with search engines by an SSH fingerprint (e.g. dc:14:de:8e:d7:c1:15:43:23:82:25:81:d2:59:e8:c0)"
|
252
|
-
method_option :title, type: :string, desc: "title"
|
253
|
-
method_option :description, type: :string, desc: "description"
|
254
|
-
method_option :tags, type: :array, desc: "tags"
|
255
|
-
def ssh_fingerprint(fingerprint)
|
256
|
-
with_error_handling do
|
257
|
-
run_analyzer Analyzers::SSHFingerprint, query: fingerprint, options: options
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
desc "import_from_json", "Give a JSON input via STDIN"
|
262
|
-
def import_from_json(input = nil)
|
263
|
-
with_error_handling do
|
264
|
-
json = input || $stdin.gets.chomp
|
265
|
-
raise ArgumentError, "Input not found: please give an input in a JSON format" unless json
|
266
|
-
|
267
|
-
json = parse_as_json(json)
|
268
|
-
raise ArgumentError, "Invalid input format: an input JSON data should have title, description and artifacts key" unless valid_json?(json)
|
269
|
-
|
270
|
-
title = json["title"]
|
271
|
-
description = json["description"]
|
272
|
-
artifacts = json["artifacts"]
|
273
|
-
tags = json["tags"] || []
|
274
|
-
|
275
|
-
basic = Analyzers::Basic.new(title: title, description: description, artifacts: artifacts, source: "json", tags: tags)
|
276
|
-
basic.run
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
desc "web", "Launch the web app"
|
281
|
-
method_option :port, type: :numeric, default: 9292
|
282
|
-
method_option :host, type: :string, default: "localhost"
|
283
|
-
def web
|
284
|
-
port = options["port"].to_i || 9292
|
285
|
-
host = options["host"] || "localhost"
|
286
|
-
|
287
|
-
load_configuration
|
288
|
-
Mihari::App.run!(port: port, host: host)
|
289
|
-
end
|
290
|
-
|
291
74
|
no_commands do
|
292
75
|
def with_error_handling
|
293
76
|
yield
|
@@ -296,12 +79,6 @@ module Mihari
|
|
296
79
|
notifier.notify e
|
297
80
|
end
|
298
81
|
|
299
|
-
def parse_as_json(input)
|
300
|
-
JSON.parse input
|
301
|
-
rescue JSON::ParserError => _e
|
302
|
-
nil
|
303
|
-
end
|
304
|
-
|
305
82
|
# @return [true, false]
|
306
83
|
def valid_json?(json)
|
307
84
|
%w[title description artifacts].all? { |key| json.key? key }
|
@@ -322,16 +99,22 @@ module Mihari
|
|
322
99
|
options = normalize_options(options)
|
323
100
|
|
324
101
|
analyzer = analyzer_class.new(query, **options)
|
102
|
+
|
103
|
+
analyzer.ignore_old_artifacts = options[:ignore_old_artifacts] || false
|
104
|
+
analyzer.ignore_threshold = options[:ignore_threshold] || 0
|
105
|
+
|
325
106
|
analyzer.run
|
326
107
|
end
|
327
108
|
|
328
109
|
def symbolize_hash_keys(hash)
|
329
|
-
hash.
|
110
|
+
hash.transform_keys(&:to_sym)
|
330
111
|
end
|
331
112
|
|
332
113
|
def normalize_options(options)
|
333
114
|
# Delete :config because it is not intended to use for running an analyzer
|
334
|
-
|
115
|
+
[:config, :ignore_old_artifacts, :ignore_threshold].each do |ignore_key|
|
116
|
+
options.delete(ignore_key)
|
117
|
+
end
|
335
118
|
options
|
336
119
|
end
|
337
120
|
|