miteru 0.9.0 → 0.9.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/lib/miteru/cli.rb +1 -58
- data/lib/miteru/crawler.rb +78 -15
- data/lib/miteru/version.rb +1 -1
- data/miteru.gemspec +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca0bb26541d8e93bd9f566de4fa55440825d18363cb7f676062745ca378e4510
|
|
4
|
+
data.tar.gz: 23605936198cce076192bd4fca7e2eb704d9f89e08fa4870d2cee882f3272202
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7ef37ae37316fbf4b53bb388e4dca0689789ee7521d1c56b875bf69a0aabf977e32bf928b8e323383f57ff2c391f8643cdabd6efc4827603e5d5b2b8275773f9
|
|
7
|
+
data.tar.gz: be702606497f66282717e9614b3b27ff7e2f18ac16046ef9a707b32e14837bac4ac7f4a4ad519fb704d5e8474c8b975765d52ad16c0a688729ed3bdddc0a28c6
|
data/lib/miteru/cli.rb
CHANGED
|
@@ -17,64 +17,7 @@ module Miteru
|
|
|
17
17
|
method_option :verbose, type: :boolean, default: true
|
|
18
18
|
desc "execute", "Execute the crawler"
|
|
19
19
|
def execute
|
|
20
|
-
|
|
21
|
-
directory_traveling: options[:directory_traveling],
|
|
22
|
-
size: options[:size],
|
|
23
|
-
threads: options[:threads],
|
|
24
|
-
verbose: options[:verbose]
|
|
25
|
-
)
|
|
26
|
-
websites.each do |website|
|
|
27
|
-
next unless website.has_kit?
|
|
28
|
-
|
|
29
|
-
message = "#{website.url}: it might contain phishing kit(s) (#{website.compressed_files.join(', ')})."
|
|
30
|
-
puts message.colorize(:light_red)
|
|
31
|
-
post_to_slack(website.message) if options[:post_to_slack] && valid_slack_setting?
|
|
32
|
-
download_compressed_files(website.url, website.compressed_files, options[:download_to]) if options[:auto_download]
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
no_commands do
|
|
37
|
-
def download_compressed_files(url, compressed_files, base_dir)
|
|
38
|
-
compressed_files.each do |path|
|
|
39
|
-
target_url = "#{url}/#{path}"
|
|
40
|
-
begin
|
|
41
|
-
download_file_path = HTTPClient.download(target_url, base_dir)
|
|
42
|
-
if duplicated?(download_file_path, base_dir)
|
|
43
|
-
puts "Do not download #{target_url} because there is a same hash file in the directory (SHA256: #{sha256(download_file_path)})."
|
|
44
|
-
FileUtils.rm download_file_path
|
|
45
|
-
else
|
|
46
|
-
puts "Download #{target_url} as #{download_file_path}"
|
|
47
|
-
end
|
|
48
|
-
rescue Down::Error => e
|
|
49
|
-
puts "Failed to download: #{target_url} (#{e})"
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def sha256(path)
|
|
55
|
-
digest = Digest::SHA256.file(path)
|
|
56
|
-
digest.hexdigest
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def duplicated?(file_path, base_dir)
|
|
60
|
-
base = sha256(file_path)
|
|
61
|
-
sha256s = Dir.glob("#{base_dir}/*.zip").map { |path| sha256(path) }
|
|
62
|
-
sha256s.select { |sha256| sha256 == base }.length > 1
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def valid_slack_setting?
|
|
66
|
-
ENV["SLACK_WEBHOOK_URL"] != nil
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def post_to_slack(message)
|
|
70
|
-
webhook_url = ENV["SLACK_WEBHOOK_URL"]
|
|
71
|
-
raise ArgumentError, "Please set the Slack webhook URL via SLACK_WEBHOOK_URL env" unless webhook_url
|
|
72
|
-
|
|
73
|
-
channel = ENV["SLACK_CHANNEL"] || "#general"
|
|
74
|
-
|
|
75
|
-
payload = { text: message, channel: channel }
|
|
76
|
-
HTTP.post(webhook_url, json: payload)
|
|
77
|
-
end
|
|
20
|
+
Crawler.execute options.map { |k, v| [k.to_sym, v] }.to_h
|
|
78
21
|
end
|
|
79
22
|
end
|
|
80
23
|
end
|
data/lib/miteru/crawler.rb
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
require "csv"
|
|
4
4
|
require "http"
|
|
5
5
|
require "json"
|
|
6
|
-
require "
|
|
6
|
+
require "parallel"
|
|
7
7
|
require "uri"
|
|
8
8
|
|
|
9
9
|
module Miteru
|
|
10
10
|
class Crawler
|
|
11
|
+
attr_reader :auto_download
|
|
11
12
|
attr_reader :directory_traveling
|
|
13
|
+
attr_reader :download_to
|
|
12
14
|
attr_reader :size
|
|
13
15
|
attr_reader :threads
|
|
14
16
|
attr_reader :verbose
|
|
@@ -17,8 +19,11 @@ module Miteru
|
|
|
17
19
|
OPENPHISH_ENDPOINT = "https://openphish.com"
|
|
18
20
|
PHISHTANK_ENDPOINT = "http://data.phishtank.com"
|
|
19
21
|
|
|
20
|
-
def initialize(directory_traveling: false, size: 100, threads: 10, verbose: false)
|
|
22
|
+
def initialize(auto_download: false, directory_traveling: false, download_to: "/tmp", post_to_slack: false, size: 100, threads: 10, verbose: false)
|
|
23
|
+
@auto_download = auto_download
|
|
21
24
|
@directory_traveling = directory_traveling
|
|
25
|
+
@download_to = download_to
|
|
26
|
+
@post_to_slack = post_to_slack
|
|
22
27
|
@size = size
|
|
23
28
|
@threads = threads
|
|
24
29
|
@verbose = verbose
|
|
@@ -69,7 +74,7 @@ module Miteru
|
|
|
69
74
|
|
|
70
75
|
def suspicious_urls
|
|
71
76
|
@suspicious_urls ||= [].tap do |arr|
|
|
72
|
-
urls = (urlscan_feed + openphish_feed + phishtank_feed)
|
|
77
|
+
urls = (urlscan_feed + openphish_feed + phishtank_feed).select { |url| url.start_with?("http://", "https://") }
|
|
73
78
|
urls.map { |url| breakdown(url) }.flatten.uniq.sort.each { |url| arr << url }
|
|
74
79
|
end
|
|
75
80
|
end
|
|
@@ -77,31 +82,89 @@ module Miteru
|
|
|
77
82
|
def execute
|
|
78
83
|
puts "Loaded #{suspicious_urls.length} URLs to crawl." if verbose
|
|
79
84
|
|
|
80
|
-
pool = Thread.pool(threads)
|
|
81
85
|
websites = []
|
|
86
|
+
Parallel.each(suspicious_urls, in_threads: threads) do |url|
|
|
87
|
+
website = Website.new(url)
|
|
88
|
+
|
|
89
|
+
if website.has_kit?
|
|
90
|
+
message = "#{website.url}: it might contain phishing kit(s) (#{website.compressed_files.join(', ')})."
|
|
91
|
+
puts message.colorize(:light_red)
|
|
92
|
+
post_message_to_slack(website.message) if post_to_slack? && valid_slack_setting?
|
|
93
|
+
download_compressed_files(website.url, website.compressed_files, download_to) if auto_download?
|
|
94
|
+
else
|
|
95
|
+
puts "#{website.url}: it doesn't contain a phishing kit." if verbose
|
|
96
|
+
end
|
|
97
|
+
break
|
|
98
|
+
rescue StandardError => e
|
|
99
|
+
puts "Failed to load #{url} (#{e})" if verbose
|
|
100
|
+
end
|
|
101
|
+
websites
|
|
102
|
+
end
|
|
82
103
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
104
|
+
def self.execute(auto_download: false, directory_traveling: false, download_to: "/tmp", post_to_slack: false, size: 100, threads: 10, verbose: false)
|
|
105
|
+
new(
|
|
106
|
+
auto_download: auto_download,
|
|
107
|
+
directory_traveling: directory_traveling,
|
|
108
|
+
download_to: download_to,
|
|
109
|
+
post_to_slack: post_to_slack,
|
|
110
|
+
size: size,
|
|
111
|
+
threads: threads,
|
|
112
|
+
verbose: verbose
|
|
113
|
+
).execute
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def download_compressed_files(url, compressed_files, base_dir)
|
|
117
|
+
compressed_files.each do |path|
|
|
118
|
+
target_url = "#{url}/#{path}"
|
|
119
|
+
begin
|
|
120
|
+
download_file_path = HTTPClient.download(target_url, base_dir)
|
|
121
|
+
if duplicated?(download_file_path, base_dir)
|
|
122
|
+
puts "Do not download #{target_url} because there is a same hash file in the directory (SHA256: #{sha256(download_file_path)})."
|
|
123
|
+
FileUtils.rm download_file_path
|
|
88
124
|
else
|
|
89
|
-
puts "#{
|
|
90
|
-
website.unbuild
|
|
125
|
+
puts "Download #{target_url} as #{download_file_path}"
|
|
91
126
|
end
|
|
127
|
+
rescue Down::Error => e
|
|
128
|
+
puts "Failed to download: #{target_url} (#{e})"
|
|
92
129
|
end
|
|
93
130
|
end
|
|
94
|
-
|
|
131
|
+
end
|
|
95
132
|
|
|
96
|
-
|
|
133
|
+
def post_to_slack(message)
|
|
134
|
+
webhook_url = ENV["SLACK_WEBHOOK_URL"]
|
|
135
|
+
raise ArgumentError, "Please set the Slack webhook URL via SLACK_WEBHOOK_URL env" unless webhook_url
|
|
136
|
+
|
|
137
|
+
channel = ENV["SLACK_CHANNEL"] || "#general"
|
|
138
|
+
|
|
139
|
+
payload = { text: message, channel: channel }
|
|
140
|
+
HTTP.post(webhook_url, json: payload)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def post_to_slack?
|
|
144
|
+
@post_to_slack
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def auto_download?
|
|
148
|
+
@auto_download
|
|
97
149
|
end
|
|
98
150
|
|
|
99
|
-
def
|
|
100
|
-
|
|
151
|
+
def valid_slack_setting?
|
|
152
|
+
ENV["SLACK_WEBHOOK_URL"] != nil
|
|
101
153
|
end
|
|
102
154
|
|
|
103
155
|
private
|
|
104
156
|
|
|
157
|
+
def sha256(path)
|
|
158
|
+
digest = Digest::SHA256.file(path)
|
|
159
|
+
digest.hexdigest
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def duplicated?(file_path, base_dir)
|
|
163
|
+
base = sha256(file_path)
|
|
164
|
+
sha256s = Dir.glob("#{base_dir}/*.zip").map { |path| sha256(path) }
|
|
165
|
+
sha256s.select { |sha256| sha256 == base }.length > 1
|
|
166
|
+
end
|
|
167
|
+
|
|
105
168
|
def get(url)
|
|
106
169
|
res = HTTP.follow(max_hops: 3).get(url)
|
|
107
170
|
raise HTTPResponseError if res.code != 200
|
data/lib/miteru/version.rb
CHANGED
data/miteru.gemspec
CHANGED
|
@@ -36,6 +36,6 @@ Gem::Specification.new do |spec|
|
|
|
36
36
|
spec.add_dependency "down", "~> 4.5"
|
|
37
37
|
spec.add_dependency "http", "~> 3.3"
|
|
38
38
|
spec.add_dependency "oga", "~> 2.15"
|
|
39
|
+
spec.add_dependency "parallel", "~> 1.12"
|
|
39
40
|
spec.add_dependency "thor", "~> 0.19"
|
|
40
|
-
spec.add_dependency "thread", "~> 0.2.2"
|
|
41
41
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: miteru
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.9.
|
|
4
|
+
version: 0.9.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Manabu Niseki
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2018-10-
|
|
11
|
+
date: 2018-10-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -165,33 +165,33 @@ dependencies:
|
|
|
165
165
|
- !ruby/object:Gem::Version
|
|
166
166
|
version: '2.15'
|
|
167
167
|
- !ruby/object:Gem::Dependency
|
|
168
|
-
name:
|
|
168
|
+
name: parallel
|
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
|
170
170
|
requirements:
|
|
171
171
|
- - "~>"
|
|
172
172
|
- !ruby/object:Gem::Version
|
|
173
|
-
version: '
|
|
173
|
+
version: '1.12'
|
|
174
174
|
type: :runtime
|
|
175
175
|
prerelease: false
|
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
|
177
177
|
requirements:
|
|
178
178
|
- - "~>"
|
|
179
179
|
- !ruby/object:Gem::Version
|
|
180
|
-
version: '
|
|
180
|
+
version: '1.12'
|
|
181
181
|
- !ruby/object:Gem::Dependency
|
|
182
|
-
name:
|
|
182
|
+
name: thor
|
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
|
184
184
|
requirements:
|
|
185
185
|
- - "~>"
|
|
186
186
|
- !ruby/object:Gem::Version
|
|
187
|
-
version: 0.
|
|
187
|
+
version: '0.19'
|
|
188
188
|
type: :runtime
|
|
189
189
|
prerelease: false
|
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
|
191
191
|
requirements:
|
|
192
192
|
- - "~>"
|
|
193
193
|
- !ruby/object:Gem::Version
|
|
194
|
-
version: 0.
|
|
194
|
+
version: '0.19'
|
|
195
195
|
description: An experimental phishing kit detector
|
|
196
196
|
email:
|
|
197
197
|
- manabu.niseki@gmail.com
|