mihari 1.3.1 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +44 -0
- data/README.md +23 -12
- data/Rakefile +1 -0
- data/docker/Dockerfile +3 -2
- data/{screenshots → images}/alert.png +0 -0
- data/{screenshots → images}/eyecatch.png +0 -0
- data/images/logo.png +0 -0
- data/{screenshots → images}/misp.png +0 -0
- data/{screenshots → images}/slack.png +0 -0
- data/lib/mihari/alert_viewer.rb +3 -3
- data/lib/mihari/analyzers/base.rb +1 -1
- data/lib/mihari/analyzers/basic.rb +3 -4
- data/lib/mihari/analyzers/binaryedge.rb +4 -7
- data/lib/mihari/analyzers/censys.rb +3 -7
- data/lib/mihari/analyzers/circl.rb +3 -5
- data/lib/mihari/analyzers/crtsh.rb +2 -6
- data/lib/mihari/analyzers/dnpedia.rb +3 -6
- data/lib/mihari/analyzers/dnstwister.rb +4 -9
- data/lib/mihari/analyzers/free_text.rb +2 -6
- data/lib/mihari/analyzers/http_hash.rb +3 -11
- data/lib/mihari/analyzers/onyphe.rb +3 -6
- data/lib/mihari/analyzers/otx.rb +4 -9
- data/lib/mihari/analyzers/passive_dns.rb +4 -9
- data/lib/mihari/analyzers/passive_ssl.rb +4 -9
- data/lib/mihari/analyzers/passivetotal.rb +9 -14
- data/lib/mihari/analyzers/pulsedive.rb +7 -12
- data/lib/mihari/analyzers/reverse_whois.rb +4 -9
- data/lib/mihari/analyzers/securitytrails.rb +12 -17
- data/lib/mihari/analyzers/securitytrails_domain_feed.rb +3 -7
- data/lib/mihari/analyzers/shodan.rb +9 -8
- data/lib/mihari/analyzers/spyse.rb +6 -11
- data/lib/mihari/analyzers/ssh_fingerprint.rb +2 -6
- data/lib/mihari/analyzers/urlscan.rb +21 -9
- data/lib/mihari/analyzers/virustotal.rb +6 -11
- data/lib/mihari/analyzers/zoomeye.rb +7 -11
- data/lib/mihari/cli.rb +14 -7
- data/lib/mihari/config.rb +1 -25
- data/lib/mihari/database.rb +1 -1
- data/lib/mihari/emitters/misp.rb +4 -2
- data/lib/mihari/emitters/slack.rb +18 -7
- data/lib/mihari/emitters/the_hive.rb +2 -2
- data/lib/mihari/errors.rb +2 -0
- data/lib/mihari/models/artifact.rb +1 -1
- data/lib/mihari/notifiers/exception_notifier.rb +5 -5
- data/lib/mihari/status.rb +1 -1
- data/lib/mihari/type_checker.rb +4 -4
- data/lib/mihari/version.rb +1 -1
- data/mihari.gemspec +23 -24
- metadata +44 -57
- data/.travis.yml +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 951201ccebc7b6c4c117a687c1abce9ab24fa8d450f5a0f0badeeececa6db5cb
|
4
|
+
data.tar.gz: aed5f37c4031ffbc1a635ddd4fc979f4e40ab68a94ef880730bc48a2df600678
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a81ff55bf880a3581ad52f6bf2d8652e1d7824119ed31daed81eb0e8f215d4d26a19a10d646b5497346e6795a320de18cd63fa817958081c2ba4393efad4c20a
|
7
|
+
data.tar.gz: b3fa4c9979fc22863d0e171d87918c320ec177b5e27c0820f4997919cb714d8c19516d37e15eccaed8b0a81102498820b4865864dcb56c650c186ef1bd057b56
|
@@ -0,0 +1,44 @@
|
|
1
|
+
name: Ruby CI
|
2
|
+
|
3
|
+
on: [pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
|
10
|
+
services:
|
11
|
+
db:
|
12
|
+
image: postgres:12
|
13
|
+
env:
|
14
|
+
POSTGRES_USER: postgres
|
15
|
+
POSTGRES_PASSWORD: postgres
|
16
|
+
POSTGRES_DB: test
|
17
|
+
options: >-
|
18
|
+
--health-cmd pg_isready
|
19
|
+
--health-interval 10s
|
20
|
+
--health-timeout 5s
|
21
|
+
--health-retries 5
|
22
|
+
ports:
|
23
|
+
- 5432:5432
|
24
|
+
|
25
|
+
strategy:
|
26
|
+
fail-fast: false
|
27
|
+
matrix:
|
28
|
+
ruby: [2.7, '3.0']
|
29
|
+
|
30
|
+
steps:
|
31
|
+
- uses: actions/checkout@v2
|
32
|
+
- name: Set up Ruby 2.7
|
33
|
+
uses: ruby/setup-ruby@v1
|
34
|
+
with:
|
35
|
+
ruby-version: ${{ matrix.ruby }}
|
36
|
+
bundler-cache: true
|
37
|
+
- name: Build and test with Rake
|
38
|
+
env:
|
39
|
+
DATABASE: postgresql://postgres:postgres@localhost:5432/test
|
40
|
+
run: |
|
41
|
+
sudo apt-get -yqq install libpq-dev
|
42
|
+
gem install bundler
|
43
|
+
bundle install
|
44
|
+
bundle exec rake
|
data/README.md
CHANGED
@@ -1,49 +1,59 @@
|
|
1
1
|
# mihari
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/mihari.svg)](https://badge.fury.io/rb/mihari)
|
4
|
-
[![
|
4
|
+
[![Ruby CI](https://github.com/ninoseki/mihari/actions/workflows/test.yml/badge.svg)](https://github.com/ninoseki/mihari/actions/workflows/test.yml)
|
5
5
|
[![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/ninoseki/mihari)](https://hub.docker.com/r/ninoseki/mihari)
|
6
6
|
[![Coverage Status](https://coveralls.io/repos/github/ninoseki/mihari/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/mihari?branch=master)
|
7
7
|
[![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/mihari/badge)](https://www.codefactor.io/repository/github/ninoseki/mihari)
|
8
8
|
|
9
|
+
![img](https://github.com/ninoseki/mihari/raw/master/images/logo.png)
|
10
|
+
|
9
11
|
Mihari is a helper to run queries & manage results continuously. Mihari can be used for C2, landing page and phishing hunting.
|
10
12
|
|
11
13
|
## How it works
|
12
14
|
|
13
|
-
- Mihari makes a query against Shodan, Censys, VirusTotal, SecurityTrails, etc. and extracts artifacts (IP addresses, domains, URLs and hashes)
|
15
|
+
- Mihari makes a query against Shodan, Censys, VirusTotal, SecurityTrails, etc. and extracts artifacts (IP addresses, domains, URLs and hashes).
|
14
16
|
- Mihari checks whether a DB (SQLite3 or PostgreSQL) contains the artifacts or not.
|
15
17
|
- If it doesn't contain the artifacts:
|
16
|
-
- Mihari creates an alert on TheHive.
|
17
|
-
- Mihari sends a notification to Slack.
|
18
|
-
- Mihari creates an event on MISP.
|
18
|
+
- Mihari creates an alert on TheHive.
|
19
|
+
- Mihari sends a notification to Slack.
|
20
|
+
- Mihari creates an event on MISP.
|
19
21
|
|
20
|
-
![img](https://github.com/ninoseki/mihari/raw/master/
|
22
|
+
![img](https://github.com/ninoseki/mihari/raw/master/images/eyecatch.png)
|
21
23
|
|
22
24
|
### Screenshots
|
23
25
|
|
24
26
|
- TheHive alert example
|
25
27
|
|
26
|
-
![img](https://github.com/ninoseki/mihari/raw/master/
|
28
|
+
![img](https://github.com/ninoseki/mihari/raw/master/images/alert.png)
|
27
29
|
|
28
30
|
- Slack notification example
|
29
31
|
|
30
|
-
![img](https://github.com/ninoseki/mihari/raw/master/
|
32
|
+
![img](https://github.com/ninoseki/mihari/raw/master/images/slack.png)
|
31
33
|
|
32
34
|
- MISP event example
|
33
35
|
|
34
|
-
![img](https://github.com/ninoseki/mihari/raw/master/
|
36
|
+
![img](https://github.com/ninoseki/mihari/raw/master/images/misp.png)
|
35
37
|
|
36
38
|
## Requirements
|
37
39
|
|
38
|
-
- Ruby 2.
|
39
|
-
- SQLite3
|
40
|
-
- libpq
|
40
|
+
- Ruby (2.7 or 3.0)
|
41
|
+
- SQLite3 or PostgreSQL
|
41
42
|
|
42
43
|
```bash
|
43
44
|
# For Debian / Ubuntu
|
44
45
|
apt-get install sqlite3 libsqlite3-dev libpq-dev
|
45
46
|
```
|
46
47
|
|
48
|
+
## Supported platforms & databases
|
49
|
+
|
50
|
+
| Name | Supported versions |
|
51
|
+
|------------|--------------------|
|
52
|
+
| PostgreSQL | v12 |
|
53
|
+
| SQLite | v3 |
|
54
|
+
| MISP | v2.4 |
|
55
|
+
| TheHive | v3.x & v4.x |
|
56
|
+
|
47
57
|
## Installation
|
48
58
|
|
49
59
|
```bash
|
@@ -69,6 +79,7 @@ Mihari supports the following services by default.
|
|
69
79
|
- [Onyphe](https://onyphe.io)
|
70
80
|
- [OTX](https://otx.alienvault.com/)
|
71
81
|
- [PassiveTotal](https://community.riskiq.com/)
|
82
|
+
- [Pulsedive](https://pulsedive.com/)
|
72
83
|
- [SecurityTrails](https://securitytrails.com/)
|
73
84
|
- [Shodan](https://shodan.io)
|
74
85
|
- [Spyse](https://spyse.com)
|
data/Rakefile
CHANGED
data/docker/Dockerfile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
FROM ruby:
|
1
|
+
FROM ruby:3.0.0-alpine3.13
|
2
|
+
|
2
3
|
RUN apk --no-cache add git build-base ruby-dev sqlite-dev postgresql-dev \
|
3
4
|
&& cd /tmp/ \
|
4
5
|
&& git clone https://github.com/ninoseki/mihari.git \
|
@@ -10,4 +11,4 @@ RUN apk --no-cache add git build-base ruby-dev sqlite-dev postgresql-dev \
|
|
10
11
|
|
11
12
|
ENTRYPOINT ["mihari"]
|
12
13
|
|
13
|
-
CMD ["--help"]
|
14
|
+
CMD ["--help"]
|
File without changes
|
File without changes
|
data/images/logo.png
ADDED
Binary file
|
File without changes
|
File without changes
|
data/lib/mihari/alert_viewer.rb
CHANGED
@@ -9,13 +9,13 @@ module Mihari
|
|
9
9
|
relation = Alert.includes(:tags, :artifacts)
|
10
10
|
relation = relation.where(title: title) if title
|
11
11
|
relation = relation.where(source: source) if source
|
12
|
-
relation = relation.where(tags: {
|
12
|
+
relation = relation.where(tags: {name: tag}) if tag
|
13
13
|
|
14
14
|
alerts = relation.limit(limit).order(id: :desc)
|
15
15
|
alerts.map do |alert|
|
16
16
|
json = AlertSerializer.new(alert).as_json
|
17
|
-
json[:artifacts] = (json
|
18
|
-
json[:tags] = (json
|
17
|
+
json[:artifacts] = (json[:artifacts] || []).map { |artifact_| artifact_[:data] }
|
18
|
+
json[:tags] = (json[:tags] || []).map { |tag_| tag_[:name] }
|
19
19
|
json
|
20
20
|
end
|
21
21
|
end
|
@@ -42,7 +42,7 @@ module Mihari
|
|
42
42
|
|
43
43
|
def run_emitter(emitter)
|
44
44
|
emitter.run(title: title, description: description, artifacts: unique_artifacts, source: source, tags: tags)
|
45
|
-
rescue
|
45
|
+
rescue => e
|
46
46
|
puts "Emission by #{emitter.class} is failed: #{e}"
|
47
47
|
end
|
48
48
|
|
@@ -4,12 +4,11 @@ module Mihari
|
|
4
4
|
module Analyzers
|
5
5
|
class Basic < Base
|
6
6
|
attr_accessor :title
|
7
|
-
attr_reader :description
|
8
|
-
attr_reader :artifacts
|
9
|
-
attr_reader :source
|
10
|
-
attr_reader :tags
|
7
|
+
attr_reader :description, :artifacts, :source, :tags
|
11
8
|
|
12
9
|
def initialize(title:, description:, artifacts:, source:, tags: [])
|
10
|
+
super()
|
11
|
+
|
13
12
|
@title = title
|
14
13
|
@description = description
|
15
14
|
@artifacts = artifacts
|
@@ -5,10 +5,7 @@ require "binaryedge"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class BinaryEdge < Base
|
8
|
-
attr_reader :title
|
9
|
-
attr_reader :description
|
10
|
-
attr_reader :query
|
11
|
-
attr_reader :tags
|
8
|
+
attr_reader :title, :description, :query, :tags
|
12
9
|
|
13
10
|
def initialize(query, title: nil, description: nil, tags: [])
|
14
11
|
super()
|
@@ -24,7 +21,7 @@ module Mihari
|
|
24
21
|
return [] unless results || results.empty?
|
25
22
|
|
26
23
|
results.map do |result|
|
27
|
-
events = result
|
24
|
+
events = result["events"] || []
|
28
25
|
events.map do |event|
|
29
26
|
event.dig "target", "ip"
|
30
27
|
end.compact
|
@@ -47,7 +44,7 @@ module Mihari
|
|
47
44
|
responses = []
|
48
45
|
(1..Float::INFINITY).each do |page|
|
49
46
|
res = search_with_page(query, page: page)
|
50
|
-
total = res
|
47
|
+
total = res["total"].to_i
|
51
48
|
|
52
49
|
responses << res
|
53
50
|
break if total <= page * PAGE_SIZE
|
@@ -56,7 +53,7 @@ module Mihari
|
|
56
53
|
end
|
57
54
|
|
58
55
|
def config_keys
|
59
|
-
%w
|
56
|
+
%w[binaryedge_api_key]
|
60
57
|
end
|
61
58
|
|
62
59
|
def api
|
@@ -5,11 +5,7 @@ require "censu"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class Censys < Base
|
8
|
-
attr_reader :title
|
9
|
-
attr_reader :description
|
10
|
-
attr_reader :query
|
11
|
-
attr_reader :tags
|
12
|
-
attr_reader :type
|
8
|
+
attr_reader :title, :description, :query, :tags, :type
|
13
9
|
|
14
10
|
def initialize(query, title: nil, description: nil, tags: [], type: "ipv4")
|
15
11
|
super()
|
@@ -37,7 +33,7 @@ module Mihari
|
|
37
33
|
private
|
38
34
|
|
39
35
|
def valid_type?
|
40
|
-
%w
|
36
|
+
%w[ipv4 websites certificates].include? type
|
41
37
|
end
|
42
38
|
|
43
39
|
def normalize(domain)
|
@@ -86,7 +82,7 @@ module Mihari
|
|
86
82
|
end
|
87
83
|
|
88
84
|
def config_keys
|
89
|
-
%w
|
85
|
+
%w[censys_id censys_secret]
|
90
86
|
end
|
91
87
|
|
92
88
|
def api
|
@@ -5,9 +5,7 @@ require "passive_circl"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class CIRCL < Base
|
8
|
-
attr_reader :title
|
9
|
-
attr_reader :description
|
10
|
-
attr_reader :tags
|
8
|
+
attr_reader :title, :description, :tags
|
11
9
|
|
12
10
|
def initialize(query, title: nil, description: nil, tags: [])
|
13
11
|
super()
|
@@ -27,7 +25,7 @@ module Mihari
|
|
27
25
|
private
|
28
26
|
|
29
27
|
def config_keys
|
30
|
-
%w
|
28
|
+
%w[circl_passive_password circl_passive_username]
|
31
29
|
end
|
32
30
|
|
33
31
|
def api
|
@@ -41,7 +39,7 @@ module Mihari
|
|
41
39
|
when "hash"
|
42
40
|
passive_ssl_lookup
|
43
41
|
else
|
44
|
-
raise InvalidInputError, "#{@query}(type: #{@type ||
|
42
|
+
raise InvalidInputError, "#{@query}(type: #{@type || "unknown"}) is not supported."
|
45
43
|
end
|
46
44
|
end
|
47
45
|
|
@@ -5,11 +5,7 @@ require "crtsh"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class Crtsh < Base
|
8
|
-
attr_reader :title
|
9
|
-
attr_reader :description
|
10
|
-
attr_reader :query
|
11
|
-
attr_reader :tags
|
12
|
-
attr_reader :exclude_expired
|
8
|
+
attr_reader :title, :description, :query, :tags, :exclude_expired
|
13
9
|
|
14
10
|
def initialize(query, title: nil, description: nil, tags: [], exclude_expired: nil)
|
15
11
|
super()
|
@@ -24,7 +20,7 @@ module Mihari
|
|
24
20
|
|
25
21
|
def artifacts
|
26
22
|
results = search
|
27
|
-
name_values = results.map { |result| result
|
23
|
+
name_values = results.map { |result| result["name_value"] }.compact
|
28
24
|
name_values.map(&:lines).flatten.uniq.map(&:chomp)
|
29
25
|
end
|
30
26
|
|
@@ -5,10 +5,7 @@ require "dnpedia"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class DNPedia < Base
|
8
|
-
attr_reader :query
|
9
|
-
attr_reader :title
|
10
|
-
attr_reader :description
|
11
|
-
attr_reader :tags
|
8
|
+
attr_reader :query, :title, :description, :tags
|
12
9
|
|
13
10
|
def initialize(query, title: nil, description: nil, tags: [])
|
14
11
|
super()
|
@@ -31,9 +28,9 @@ module Mihari
|
|
31
28
|
|
32
29
|
def lookup
|
33
30
|
res = api.search(query)
|
34
|
-
rows = res
|
31
|
+
rows = res["rows"] || []
|
35
32
|
rows.map do |row|
|
36
|
-
[row
|
33
|
+
[row["name"], row["zoneid"]].join(".")
|
37
34
|
end
|
38
35
|
end
|
39
36
|
end
|
@@ -7,12 +7,7 @@ require "parallel"
|
|
7
7
|
module Mihari
|
8
8
|
module Analyzers
|
9
9
|
class DNSTwister < Base
|
10
|
-
attr_reader :query
|
11
|
-
attr_reader :type
|
12
|
-
|
13
|
-
attr_reader :title
|
14
|
-
attr_reader :description
|
15
|
-
attr_reader :tags
|
10
|
+
attr_reader :query, :type, :title, :description, :tags
|
16
11
|
|
17
12
|
def initialize(query, title: nil, description: nil, tags: [])
|
18
13
|
super()
|
@@ -47,11 +42,11 @@ module Mihari
|
|
47
42
|
end
|
48
43
|
|
49
44
|
def lookup
|
50
|
-
raise InvalidInputError, "#{query}(type: #{type ||
|
45
|
+
raise InvalidInputError, "#{query}(type: #{type || "unknown"}) is not supported." unless valid_type?
|
51
46
|
|
52
47
|
res = api.fuzz(query)
|
53
|
-
fuzzy_domains = res
|
54
|
-
domains = fuzzy_domains.map { |domain| domain
|
48
|
+
fuzzy_domains = res["fuzzy_domains"] || []
|
49
|
+
domains = fuzzy_domains.map { |domain| domain["domain"] }
|
55
50
|
Parallel.map(domains) do |domain|
|
56
51
|
resolvable?(domain) ? domain : nil
|
57
52
|
end.compact
|
@@ -5,15 +5,11 @@ require "parallel"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class FreeText < Base
|
8
|
-
attr_reader :query
|
9
|
-
|
10
|
-
attr_reader :title
|
11
|
-
attr_reader :description
|
12
|
-
attr_reader :tags
|
8
|
+
attr_reader :query, :title, :description, :tags
|
13
9
|
|
14
10
|
ANALYZERS = [
|
15
11
|
Mihari::Analyzers::BinaryEdge,
|
16
|
-
Mihari::Analyzers::Censys
|
12
|
+
Mihari::Analyzers::Censys
|
17
13
|
].freeze
|
18
14
|
|
19
15
|
def initialize(query, title: nil, description: nil, tags: [])
|
@@ -5,15 +5,7 @@ require "parallel"
|
|
5
5
|
module Mihari
|
6
6
|
module Analyzers
|
7
7
|
class HTTPHash < Base
|
8
|
-
attr_reader :md5
|
9
|
-
attr_reader :sha256
|
10
|
-
attr_reader :mmh3
|
11
|
-
|
12
|
-
attr_reader :html
|
13
|
-
|
14
|
-
attr_reader :title
|
15
|
-
attr_reader :description
|
16
|
-
attr_reader :tags
|
8
|
+
attr_reader :md5, :sha256, :mmh3, :html, :title, :description, :tags
|
17
9
|
|
18
10
|
def initialize(_query, md5: nil, sha256: nil, mmh3: nil, html: nil, title: nil, description: nil, tags: [])
|
19
11
|
super()
|
@@ -57,7 +49,7 @@ module Mihari
|
|
57
49
|
[
|
58
50
|
md5 ? "md5:#{md5}" : nil,
|
59
51
|
sha256 ? "sha256:#{sha256}" : nil,
|
60
|
-
mmh3 ? "mmh3:#{mmh3}" : nil
|
52
|
+
mmh3 ? "mmh3:#{mmh3}" : nil
|
61
53
|
].compact.join(",")
|
62
54
|
end
|
63
55
|
|
@@ -92,7 +84,7 @@ module Mihari
|
|
92
84
|
binary_edge,
|
93
85
|
censys,
|
94
86
|
onyphe,
|
95
|
-
shodan
|
87
|
+
shodan
|
96
88
|
].compact
|
97
89
|
end
|
98
90
|
|