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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48d47d401250750c0ffa68ccf5f58602a1e0fa4918b0b812aebcd0c60cdfe484
4
- data.tar.gz: 9acbfd9d4465c25ddb20186fe57a2d0a84ba74ba24fa2e55b2b2103b5a4d12f9
3
+ metadata.gz: ca0bb26541d8e93bd9f566de4fa55440825d18363cb7f676062745ca378e4510
4
+ data.tar.gz: 23605936198cce076192bd4fca7e2eb704d9f89e08fa4870d2cee882f3272202
5
5
  SHA512:
6
- metadata.gz: 7690a2497203522b7c9469656a5e1ea9fd2e72671555548a1d0833841d8b3bce34a60c22b2e9597b94add35342dbae0b2cb3fd58fdb3ec08b9e16375f1ca6044
7
- data.tar.gz: 27d5aef80aa2bae48091bfaf398fae956e7226384414bac4505af3752090a722c1be1b421d3260773dd40d7d7c2dc65281b5555bbade267f83554d267371cd85
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
- websites = Crawler.execute(
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
@@ -3,12 +3,14 @@
3
3
  require "csv"
4
4
  require "http"
5
5
  require "json"
6
- require "thread/pool"
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
- suspicious_urls.each do |url|
84
- pool.process do
85
- website = Website.new(url)
86
- if website.has_kit?
87
- websites << website
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 "#{website.url}: it doesn't contain a phishing kit." if verbose
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
- pool.shutdown
131
+ end
95
132
 
96
- websites
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 self.execute(directory_traveling: false, size: 100, threads: 10, verbose: false)
100
- new(directory_traveling: directory_traveling, size: size, threads: threads, verbose: verbose).execute
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Miteru
4
- VERSION = "0.9.0"
4
+ VERSION = "0.9.1"
5
5
  end
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.0
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-16 00:00:00.000000000 Z
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: thor
168
+ name: parallel
169
169
  requirement: !ruby/object:Gem::Requirement
170
170
  requirements:
171
171
  - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: '0.19'
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: '0.19'
180
+ version: '1.12'
181
181
  - !ruby/object:Gem::Dependency
182
- name: thread
182
+ name: thor
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: 0.2.2
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.2.2
194
+ version: '0.19'
195
195
  description: An experimental phishing kit detector
196
196
  email:
197
197
  - manabu.niseki@gmail.com