mihari 0.1.0 → 0.2.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/README.md +42 -13
- data/lib/mihari.rb +1 -0
- data/lib/mihari/analyzers/onyphe.rb +39 -0
- data/lib/mihari/analyzers/shodan.rb +6 -13
- data/lib/mihari/cli.rb +11 -3
- data/lib/mihari/notifiers/slack.rb +52 -18
- data/lib/mihari/version.rb +1 -1
- data/mihari.gemspec +2 -0
- data/screenshots/alert.png +0 -0
- data/screenshots/slack.png +0 -0
- metadata +33 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 777180f61db321707c6f72c02256fd9e2da24d5cc45f66430d8666a1baf36601
|
4
|
+
data.tar.gz: 888a4dea9f82823635b9c45418102a57ad60865bf769ddffbc14582d6244efe1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54c3574805ccd106646d4a75841d79d0ae8da6b1d1062b92d7d202439b15254772e625e6d64aeacc384c7727bc756f3452281ed1bf51ddc0a47df92c7f5873f5
|
7
|
+
data.tar.gz: 616b4e01a4af3454c9e1d9e5dad4834d553facff5981a462f371991ff391539335b76114652def78d19c64d10583e6bff9fa7ba2a5e475dc79f79065338916cd
|
data/README.md
CHANGED
@@ -12,27 +12,35 @@ mihari(`見張り`) is a framework for continuous malicious hosts (C2 / landing
|
|
12
12
|
- mihari creates an alert with the artifacts on the TheHive instance.
|
13
13
|
- mihari sends a notification to Slack. (Optional)
|
14
14
|
|
15
|
+
### Screenshots
|
16
|
+
|
17
|
+
- TheHive alert example
|
18
|
+
|
19
|
+

|
20
|
+
|
21
|
+
- Slack notification example
|
22
|
+
|
23
|
+

|
24
|
+
|
15
25
|
## Installation
|
16
26
|
|
17
27
|
```bash
|
18
28
|
gem install mihari
|
19
29
|
```
|
20
30
|
|
21
|
-
##
|
22
|
-
|
23
|
-
All configuration is done via ENV variables.
|
31
|
+
## Basic usage
|
24
32
|
|
25
|
-
|
26
|
-
|----------------------|--------------------|--------------------------------|
|
27
|
-
| THEHIVE_API_ENDPOINT | TheHive URL | Required |
|
28
|
-
| THEHIVE_API_KEY | TheHive API key | Required |
|
29
|
-
| SLACK_WEBHOOK_URL | Slack Webhook URL | Optional |
|
30
|
-
| SLACK_CHANNEL | Slack channel name | Optional (default: `#general`) |
|
31
|
-
| CENSYS_ID | CENSYS API ID | Optional |
|
32
|
-
| CENSYS_SECRET | CENSYS secret | Optional |
|
33
|
-
| SHODAN_API_KEY | Shodan API key | Optional |
|
33
|
+
mihari supports Censys, Shodan and Onyphe by default.
|
34
34
|
|
35
|
-
|
35
|
+
```bash
|
36
|
+
$ mihari
|
37
|
+
Commands:
|
38
|
+
mihari censys [QUERY] # Censys IPv4 lookup by a given query
|
39
|
+
mihari help [COMMAND] # Describe available commands or one specific command
|
40
|
+
mihari import_from_json # Give a JSON input via STDIN
|
41
|
+
mihari onyphe [QUERY] # Onyphe datascan lookup by a given query
|
42
|
+
mihari shodan [QUERY] # Shodan host lookup by a given query
|
43
|
+
```
|
36
44
|
|
37
45
|
### Censys
|
38
46
|
|
@@ -46,6 +54,12 @@ mihari censys "YOUR_QUERY"
|
|
46
54
|
mihari shodan "YOUR QUERY"
|
47
55
|
```
|
48
56
|
|
57
|
+
### Onyphe
|
58
|
+
|
59
|
+
```bash
|
60
|
+
mihari onyphe "YOUR QUERY"
|
61
|
+
```
|
62
|
+
|
49
63
|
### Import from JSON
|
50
64
|
|
51
65
|
```bash
|
@@ -68,6 +82,21 @@ The input is a JSON data should have `title`, `description` and `artifacts` key.
|
|
68
82
|
| description | A description of an alert |
|
69
83
|
| artifacts | An array of artifacts (supported data types: ip, domain, url, email, hash) |
|
70
84
|
|
85
|
+
## Configuration
|
86
|
+
|
87
|
+
All configuration is done via ENV variables.
|
88
|
+
|
89
|
+
| Key | Desc. | Required or optional |
|
90
|
+
|----------------------|--------------------|--------------------------------|
|
91
|
+
| THEHIVE_API_ENDPOINT | TheHive URL | Required |
|
92
|
+
| THEHIVE_API_KEY | TheHive API key | Required |
|
93
|
+
| SLACK_WEBHOOK_URL | Slack Webhook URL | Optional |
|
94
|
+
| SLACK_CHANNEL | Slack channel name | Optional (default: `#general`) |
|
95
|
+
| CENSYS_ID | Censys API ID | Optional |
|
96
|
+
| CENSYS_SECRET | Censys secret | Optional |
|
97
|
+
| SHODAN_API_KEY | Shodan API key | Optional |
|
98
|
+
| ONYPHE_API_KEY | Onyphe API key | Optional |
|
99
|
+
|
71
100
|
## How to create a custom analyzer
|
72
101
|
|
73
102
|
Create a class which extends `Mihari::Analyzers::Base` and implements the following methods.
|
data/lib/mihari.rb
CHANGED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "onyphe"
|
4
|
+
|
5
|
+
module Mihari
|
6
|
+
module Analyzers
|
7
|
+
class Onyphe < Base
|
8
|
+
attr_reader :api
|
9
|
+
attr_reader :title
|
10
|
+
attr_reader :description
|
11
|
+
attr_reader :query
|
12
|
+
|
13
|
+
def initialize(query)
|
14
|
+
super()
|
15
|
+
|
16
|
+
@api = ::Onyphe::API.new
|
17
|
+
@query = query
|
18
|
+
@title = "Onyphe lookup"
|
19
|
+
@description = "Query: #{query}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def artifacts
|
23
|
+
result = search
|
24
|
+
return [] unless result
|
25
|
+
|
26
|
+
results = result.dig("results") || []
|
27
|
+
results.map { |e| e.dig("ip") }.compact
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def search
|
33
|
+
api.datascan(query)
|
34
|
+
rescue ::Onyphe::Error => _e
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "
|
4
|
-
require "json"
|
3
|
+
require "shodan"
|
5
4
|
|
6
5
|
module Mihari
|
7
6
|
module Analyzers
|
8
7
|
class Shodan < Base
|
9
|
-
attr_reader :
|
8
|
+
attr_reader :api
|
10
9
|
attr_reader :title
|
11
10
|
attr_reader :description
|
12
11
|
attr_reader :query
|
@@ -14,10 +13,7 @@ module Mihari
|
|
14
13
|
def initialize(query)
|
15
14
|
super()
|
16
15
|
|
17
|
-
|
18
|
-
raise ArgumentError, "SHODAN_API_KEY is required" unless api_key
|
19
|
-
|
20
|
-
@api_key = api_key
|
16
|
+
@api = ::Shodan::API.new
|
21
17
|
@query = query
|
22
18
|
@title = "Shodan lookup"
|
23
19
|
@description = "Query: #{query}"
|
@@ -36,12 +32,9 @@ module Mihari
|
|
36
32
|
private
|
37
33
|
|
38
34
|
def search
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
rescue OpenURI::HTTPError
|
43
|
-
nil
|
44
|
-
end
|
35
|
+
api.host.search(query)
|
36
|
+
rescue ::Shodan::Error => _e
|
37
|
+
nil
|
45
38
|
end
|
46
39
|
end
|
47
40
|
end
|
data/lib/mihari/cli.rb
CHANGED
@@ -5,7 +5,7 @@ require "json"
|
|
5
5
|
|
6
6
|
module Mihari
|
7
7
|
class CLI < Thor
|
8
|
-
desc "censys [QUERY]", "Censys lookup by a given query"
|
8
|
+
desc "censys [QUERY]", "Censys IPv4 lookup by a given query"
|
9
9
|
def censys(query)
|
10
10
|
with_error_handling do
|
11
11
|
censys = Analyzers::Censys.new(query)
|
@@ -13,7 +13,7 @@ module Mihari
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
desc "shodan [QUERY]", "Shodan lookup by a given query"
|
16
|
+
desc "shodan [QUERY]", "Shodan host lookup by a given query"
|
17
17
|
def shodan(query)
|
18
18
|
with_error_handling do
|
19
19
|
shodan = Analyzers::Shodan.new(query)
|
@@ -21,6 +21,14 @@ module Mihari
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
+
desc "onyphe [QUERY]", "Onyphe datascan lookup by a given query"
|
25
|
+
def onyphe(query)
|
26
|
+
with_error_handling do
|
27
|
+
onyphe = Analyzers::Onyphe.new(query)
|
28
|
+
onyphe.run
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
24
32
|
desc "import_from_json", "Give a JSON input via STDIN"
|
25
33
|
def import_from_json(input = nil)
|
26
34
|
json = input || STDIN.gets.chomp
|
@@ -51,7 +59,7 @@ module Mihari
|
|
51
59
|
|
52
60
|
def parse_as_json(input)
|
53
61
|
JSON.parse input
|
54
|
-
rescue JSON::ParserError =>
|
62
|
+
rescue JSON::ParserError => e
|
55
63
|
nil
|
56
64
|
end
|
57
65
|
|
@@ -2,10 +2,13 @@
|
|
2
2
|
|
3
3
|
require "slack/incoming/webhooks"
|
4
4
|
require "digest/sha2"
|
5
|
+
require "mem"
|
5
6
|
|
6
7
|
module Mihari
|
7
8
|
module Notifiers
|
8
9
|
class Attachment
|
10
|
+
include Mem
|
11
|
+
|
9
12
|
attr_reader :data, :data_type
|
10
13
|
|
11
14
|
def initialize(data:, data_type:)
|
@@ -13,8 +16,53 @@ module Mihari
|
|
13
16
|
@data_type = data_type
|
14
17
|
end
|
15
18
|
|
19
|
+
def vt_link
|
20
|
+
return nil unless _vt_link
|
21
|
+
|
22
|
+
{
|
23
|
+
fallback: "VT link",
|
24
|
+
title: data,
|
25
|
+
title_link: _vt_link,
|
26
|
+
footer: "virustotal.com",
|
27
|
+
footer_icon: "http://www.google.com/s2/favicons?domain=virustotal.com"
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def urlscan_link
|
32
|
+
return nil unless _urlscan_link
|
33
|
+
|
34
|
+
{
|
35
|
+
fallback: "urlscan.io link",
|
36
|
+
title: data,
|
37
|
+
title_link: _urlscan_link,
|
38
|
+
footer: "urlscan.io",
|
39
|
+
footer_icon: "http://www.google.com/s2/favicons?domain=urlscan.io"
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Array]
|
44
|
+
def to_a
|
45
|
+
[vt_link, urlscan_link].compact
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
16
50
|
# @return [String]
|
17
|
-
def
|
51
|
+
def _urlscan_link
|
52
|
+
case data_type
|
53
|
+
when "ip"
|
54
|
+
"https://urlscan.io/ip/#{data}"
|
55
|
+
when "domain"
|
56
|
+
"https://urlscan.io/domain/#{data}"
|
57
|
+
when "url"
|
58
|
+
uri = URI(data)
|
59
|
+
"https://urlscan.io/domain/#{uri.hostname}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
memoize :_urlscan_link
|
63
|
+
|
64
|
+
# @return [String]
|
65
|
+
def _vt_link
|
18
66
|
case data_type
|
19
67
|
when "hash"
|
20
68
|
"https://www.virustotal.com/#/file/#{data}"
|
@@ -26,23 +74,9 @@ module Mihari
|
|
26
74
|
"https://www.virustotal.com/#/url/#{sha256}"
|
27
75
|
when "mail"
|
28
76
|
"https://www.virustotal.com/#/search/#{data}"
|
29
|
-
else
|
30
|
-
""
|
31
77
|
end
|
32
78
|
end
|
33
|
-
|
34
|
-
# @return [Hash]
|
35
|
-
def to_h
|
36
|
-
{
|
37
|
-
fallback: "VT link",
|
38
|
-
title: data,
|
39
|
-
title_link: link,
|
40
|
-
footer: "virustotal.com",
|
41
|
-
footer_icon: "http://www.google.com/s2/favicons?domain=virustotal.com"
|
42
|
-
}
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
79
|
+
memoize :_vt_link
|
46
80
|
|
47
81
|
# @return [String]
|
48
82
|
def sha256
|
@@ -72,8 +106,8 @@ module Mihari
|
|
72
106
|
|
73
107
|
def to_attachments(artifacts)
|
74
108
|
artifacts.map do |artifact|
|
75
|
-
Attachment.new(data: artifact.data, data_type: artifact.data_type).
|
76
|
-
end
|
109
|
+
Attachment.new(data: artifact.data, data_type: artifact.data_type).to_a
|
110
|
+
end.flatten
|
77
111
|
end
|
78
112
|
|
79
113
|
def notify(title:, description:, artifacts:)
|
data/lib/mihari/version.rb
CHANGED
data/mihari.gemspec
CHANGED
@@ -36,7 +36,9 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_dependency "email_address", "~> 0.1"
|
37
37
|
spec.add_dependency "hachi", "~> 0.1"
|
38
38
|
spec.add_dependency "mem", "~> 0.1"
|
39
|
+
spec.add_dependency "onyphe", "~> 0.2"
|
39
40
|
spec.add_dependency "public_suffix", "~> 3.0"
|
41
|
+
spec.add_dependency "shodanx", "~> 0.1"
|
40
42
|
spec.add_dependency "slack-incoming-webhooks", "~> 0.2"
|
41
43
|
spec.add_dependency "thor", "~> 0.19"
|
42
44
|
end
|
Binary file
|
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mihari
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manabu Niseki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-04
|
11
|
+
date: 2019-05-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0.1'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: onyphe
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0.2'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0.2'
|
167
181
|
- !ruby/object:Gem::Dependency
|
168
182
|
name: public_suffix
|
169
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,6 +192,20 @@ dependencies:
|
|
178
192
|
- - "~>"
|
179
193
|
- !ruby/object:Gem::Version
|
180
194
|
version: '3.0'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: shodanx
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0.1'
|
202
|
+
type: :runtime
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '0.1'
|
181
209
|
- !ruby/object:Gem::Dependency
|
182
210
|
name: slack-incoming-webhooks
|
183
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -230,6 +258,7 @@ files:
|
|
230
258
|
- lib/mihari/analyzers/base.rb
|
231
259
|
- lib/mihari/analyzers/basic.rb
|
232
260
|
- lib/mihari/analyzers/censys.rb
|
261
|
+
- lib/mihari/analyzers/onyphe.rb
|
233
262
|
- lib/mihari/analyzers/shodan.rb
|
234
263
|
- lib/mihari/artifact.rb
|
235
264
|
- lib/mihari/cli.rb
|
@@ -241,6 +270,8 @@ files:
|
|
241
270
|
- lib/mihari/type_checker.rb
|
242
271
|
- lib/mihari/version.rb
|
243
272
|
- mihari.gemspec
|
273
|
+
- screenshots/alert.png
|
274
|
+
- screenshots/slack.png
|
244
275
|
homepage: https://github.com/ninoseki/mihari
|
245
276
|
licenses:
|
246
277
|
- MIT
|