uirusu 0.0.4 → 0.0.5
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/NEWS.markdown +4 -0
- data/Rakefile +4 -4
- data/lib/uirusu.rb +3 -1
- data/lib/uirusu/cli/application.rb +118 -80
- data/lib/uirusu/vtcomment.rb +2 -2
- data/lib/uirusu/vtfile.rb +37 -3
- data/lib/uirusu/vtresult.rb +76 -87
- data/lib/uirusu/vturl.rb +4 -3
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd1b2173bea70412a787d51e6968bfefe2a5af9b
|
4
|
+
data.tar.gz: 9c00e08d2ccc82b620f7ba1670f4c0c2b30f35c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83754cc68a7631fded5d617588239bcb5928ccd7f536bfff6c0ec46644ec85c4b04d46e77a847aef182f0d2ee3fa2b503665e0e2a07550b700de315029dbdd93
|
7
|
+
data.tar.gz: ffbe2e9e701597b5b47384f076c1b7b993a90c937a150f6278172627543ce8f70e56b3d2a61e4995c17938d8b315a51403d5b77a51711176c77444e805a9f350
|
data/NEWS.markdown
CHANGED
data/Rakefile
CHANGED
@@ -34,11 +34,11 @@ require 'rake'
|
|
34
34
|
require 'rake/testtask'
|
35
35
|
|
36
36
|
task :build do
|
37
|
-
|
37
|
+
system "gem build #{Uirusu::APP_NAME}.gemspec"
|
38
38
|
end
|
39
39
|
|
40
40
|
task :release => :build do
|
41
|
-
|
41
|
+
system "gem push #{Uirusu::APP_NAME}-#{Uirusu::VERSION}.gem"
|
42
42
|
puts "Just released #{Uirusu::APP_NAME} v#{Uirusu::VERSION}. #{Uirusu::APP_NAME} is rubygem for using the Virustotal web service! More information at http://arxopia.com/projects/uirusu/"
|
43
43
|
end
|
44
44
|
|
@@ -51,6 +51,6 @@ task :default => [:test]
|
|
51
51
|
|
52
52
|
Rake::TestTask.new("test") do |t|
|
53
53
|
t.libs << "test"
|
54
|
-
|
55
|
-
|
54
|
+
t.pattern = 'test/*/*_test.rb'
|
55
|
+
t.verbose = true
|
56
56
|
end
|
data/lib/uirusu.rb
CHANGED
@@ -28,8 +28,10 @@
|
|
28
28
|
|
29
29
|
module Uirusu
|
30
30
|
APP_NAME = "uirusu"
|
31
|
-
VERSION = "0.0.
|
31
|
+
VERSION = "0.0.5"
|
32
32
|
CONFIG_FILE = "~/.uirusu"
|
33
|
+
VT_API = "https://www.virustotal.com/vtapi/v2"
|
34
|
+
RESULT_FIELDS = [ :hash, :scanner, :version, :detected, :result, :md5, :sha1, :sha256, :update, :permalink, ]
|
33
35
|
end
|
34
36
|
|
35
37
|
require 'json'
|
@@ -1,20 +1,20 @@
|
|
1
1
|
# Copyright (c) 2012-2013 Arxopia LLC.
|
2
2
|
# All rights reserved.
|
3
|
-
#
|
3
|
+
#
|
4
4
|
# Redistribution and use in source and binary forms, with or without
|
5
5
|
# modification, are permitted provided that the following conditions are met:
|
6
|
-
#
|
7
|
-
# Redistributions of source code must retain the above copyright notice,
|
6
|
+
#
|
7
|
+
# Redistributions of source code must retain the above copyright notice,
|
8
8
|
# this list of conditions and the following disclaimer.
|
9
9
|
#
|
10
|
-
# Redistributions in binary form must reproduce the above copyright notice,
|
11
|
-
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
12
|
# and/or other materials provided with the distribution.
|
13
13
|
#
|
14
|
-
# Neither the name of the project's author nor the names of its contributors
|
15
|
-
# may be used to endorse or promote products derived from this software
|
14
|
+
# Neither the name of the project's author nor the names of its contributors
|
15
|
+
# may be used to endorse or promote products derived from this software
|
16
16
|
# without specific prior written permission.
|
17
|
-
#
|
17
|
+
#
|
18
18
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
19
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
20
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
@@ -29,7 +29,7 @@
|
|
29
29
|
module Uirusu
|
30
30
|
module CLI
|
31
31
|
class Application
|
32
|
-
|
32
|
+
|
33
33
|
# Creates a new instance of the [Application] class
|
34
34
|
#
|
35
35
|
def initialize
|
@@ -40,38 +40,44 @@ module Uirusu
|
|
40
40
|
@sites = Array.new
|
41
41
|
@uploads = Array.new
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
# Parses the command the line options and returns the parsed options hash
|
45
45
|
#
|
46
46
|
# @return [Hash] of the parsed options
|
47
47
|
def parse_options(args)
|
48
48
|
begin
|
49
|
-
@options['output']
|
49
|
+
@options['output'] = :stdout
|
50
50
|
@options['verbose'] = false
|
51
|
-
@options[
|
52
|
-
|
51
|
+
@options['rescan'] = false
|
52
|
+
@options[:timeout] = 25
|
53
|
+
|
53
54
|
opt = OptionParser.new do |opt|
|
54
|
-
opt.banner =
|
55
|
+
opt.banner = "#{APP_NAME} v#{VERSION}\nJacob Hammack\nhttp://www.arxopia.com\n\n"
|
55
56
|
opt.banner << "Usage: #{APP_NAME} <options>"
|
56
57
|
opt.separator('')
|
57
|
-
opt.separator(
|
58
|
-
|
59
|
-
opt.on('-h HASH', '--search-hash HASH', 'Searches a single hash on virustotal.com') do |hash|
|
58
|
+
opt.separator('File Options')
|
59
|
+
|
60
|
+
opt.on('-h HASH', '--search-hash HASH', 'Searches a single hash on virustotal.com') do |hash|
|
61
|
+
@hashes.push(hash)
|
62
|
+
end
|
63
|
+
|
64
|
+
opt.on('-r HASH[,HASH]', '--rescan-hash HASH[,HASH]', 'Requests a rescan of a single hash, or multiple hashes (comma separated), by virustotal.com') do |hash|
|
65
|
+
@options['rescan'] = true
|
60
66
|
@hashes.push(hash)
|
61
67
|
end
|
62
68
|
|
63
69
|
opt.on('-f FILE', '--search-hash-file FILE', 'Searches a each hash in a file of hashes on virustotal.com') do |file|
|
64
70
|
if File.exists?(file)
|
65
|
-
puts "[+] Adding file #{file}" if @options[
|
71
|
+
puts "[+] Adding file #{file}" if @options['verbose']
|
66
72
|
@files_of_hashes.push(file)
|
67
73
|
else
|
68
74
|
puts "[!] #{file} does not exist, please check your input!\n"
|
69
75
|
end
|
70
76
|
end
|
71
|
-
|
77
|
+
|
72
78
|
opt.on('-u FILE', '--upload-file FILE', 'Uploads a file to virustotal.com for analysis') do |file|
|
73
79
|
if File.exists?(file)
|
74
|
-
puts "[+] Adding file #{file}" if @options[
|
80
|
+
puts "[+] Adding file #{file}" if @options['verbose']
|
75
81
|
@uploads.push(file)
|
76
82
|
else
|
77
83
|
puts "[!] #{file} does not exist, please check your input!\n"
|
@@ -80,22 +86,26 @@ module Uirusu
|
|
80
86
|
|
81
87
|
opt.separator('')
|
82
88
|
opt.separator("Url Options")
|
83
|
-
|
84
|
-
opt.on('-s SITE', '--search-site SITE', 'Searches for a single url on virustotal.com') { |site|
|
89
|
+
|
90
|
+
opt.on('-s SITE', '--search-site SITE', 'Searches for a single url on virustotal.com') { |site|
|
85
91
|
@sites.push(site)
|
86
92
|
}
|
87
|
-
|
93
|
+
|
88
94
|
opt.separator('')
|
89
95
|
opt.separator('Output Options')
|
90
96
|
|
97
|
+
opt.on('-j', '--json-output', 'Print results as json to stdout') do
|
98
|
+
@options['output'] = :json
|
99
|
+
end
|
100
|
+
|
91
101
|
opt.on('-x', '--xml-output', 'Print results as xml to stdout') do
|
92
|
-
@options[
|
102
|
+
@options['output'] = :xml
|
93
103
|
end
|
94
|
-
|
104
|
+
|
95
105
|
opt.on('-y', '--yaml-output', 'Print results as yaml to stdout') do
|
96
106
|
@options['output'] = :yaml
|
97
107
|
end
|
98
|
-
|
108
|
+
|
99
109
|
opt.on('--stdout-output', 'Print results as normal text line to stdout, this is default') do
|
100
110
|
@options['output'] = :stdout
|
101
111
|
end
|
@@ -103,10 +113,10 @@ module Uirusu
|
|
103
113
|
opt.separator ''
|
104
114
|
opt.separator 'Advanced Options'
|
105
115
|
|
106
|
-
opt.on('-c', '--create-config', 'Creates a skeleton config file to use') do
|
116
|
+
opt.on('-c', '--create-config', 'Creates a skeleton config file to use') do
|
107
117
|
if File.exists?(File.expand_path(CONFIG_FILE)) == false
|
108
|
-
File.open(File.expand_path(CONFIG_FILE), 'w+') do |f|
|
109
|
-
f.write("virustotal: \n api-key: \n timeout: 25\n\n")
|
118
|
+
File.open(File.expand_path(CONFIG_FILE), 'w+') do |f|
|
119
|
+
f.write("virustotal: \n api-key: \n timeout: 25\n\n")
|
110
120
|
end
|
111
121
|
|
112
122
|
puts "[*] An empty #{File.expand_path(CONFIG_FILE)} has been created. Please edit and fill in the correct values."
|
@@ -122,35 +132,35 @@ module Uirusu
|
|
122
132
|
end
|
123
133
|
|
124
134
|
opt.on('--[no-]verbose', 'Print verbose information') do |v|
|
125
|
-
@options[
|
135
|
+
@options['verbose'] = v
|
126
136
|
end
|
127
|
-
|
137
|
+
|
128
138
|
opt.separator ''
|
129
139
|
opt.separator 'Other Options'
|
130
|
-
|
131
|
-
opt.on('-v', '--version',
|
140
|
+
|
141
|
+
opt.on('-v', '--version', 'Shows application version information') do
|
132
142
|
puts "#{APP_NAME} - #{VERSION}"
|
133
143
|
exit
|
134
144
|
end
|
135
145
|
|
136
|
-
opt.on_tail(
|
146
|
+
opt.on_tail('-?', '--help', 'Show this message') { |help|
|
137
147
|
puts opt.to_s + "\n"
|
138
148
|
exit
|
139
|
-
}
|
149
|
+
}
|
140
150
|
end
|
141
|
-
|
142
|
-
if ARGV.length != 0
|
151
|
+
|
152
|
+
if ARGV.length != 0
|
143
153
|
opt.parse!
|
144
154
|
else
|
145
155
|
puts opt.to_s + "\n"
|
146
156
|
exit
|
147
|
-
end
|
157
|
+
end
|
148
158
|
rescue OptionParser::MissingArgument => m
|
149
159
|
puts opt.to_s + "\n"
|
150
160
|
exit
|
151
161
|
end
|
152
162
|
end
|
153
|
-
|
163
|
+
|
154
164
|
# Loads the .uirusu config file for the api key
|
155
165
|
#
|
156
166
|
def load_config
|
@@ -161,9 +171,9 @@ module Uirusu
|
|
161
171
|
exit
|
162
172
|
end
|
163
173
|
|
164
|
-
@options[:timeout] = @config[
|
174
|
+
@options[:timeout] = @config['virustotal']['timeout'] if @config['virustotal']['timeout'] != nil
|
165
175
|
end
|
166
|
-
|
176
|
+
|
167
177
|
# Submits a file/url and waits for analysis to be complete and returns the results.
|
168
178
|
#
|
169
179
|
# @param mod
|
@@ -173,65 +183,91 @@ module Uirusu
|
|
173
183
|
def scan_and_wait(mod, resource, attempts)
|
174
184
|
method = nil
|
175
185
|
retries = attempts
|
176
|
-
|
186
|
+
|
177
187
|
if mod.name == "Uirusu::VTFile"
|
178
|
-
|
188
|
+
STDERR.puts "[*] Attempting to rescan #{resource}" if @options['verbose']
|
189
|
+
method = @options['rescan'] ? mod.method(:rescan_file) : mod.method(:scan_file)
|
179
190
|
else
|
191
|
+
STDERR.puts "[*] Attempting to upload file #{resource}" if @options['verbose']
|
180
192
|
method = mod.method :scan_url
|
181
193
|
end
|
182
194
|
|
183
195
|
begin
|
184
|
-
|
185
|
-
result = method.call(@config["virustotal"]["api-key"], resource)
|
196
|
+
result = method.call(@config['virustotal']['api-key'], resource)
|
186
197
|
rescue => e
|
187
|
-
|
198
|
+
if @options['rescan']
|
199
|
+
STDERR.puts "[!] An error has occurred with the rescan request. Retrying 60 seconds up #{retries} retries: #{e.message}\n" if @options['verbose']
|
200
|
+
else
|
201
|
+
STDERR.puts "[!] An error has occurred uploading the file. Retrying 60 seconds up #{retries} retries.\n" if @options['verbose']
|
202
|
+
end
|
203
|
+
|
188
204
|
if retries >= 0
|
189
205
|
sleep 60
|
190
206
|
retry
|
191
207
|
retries = retries - 1
|
192
208
|
end
|
193
209
|
end
|
194
|
-
|
210
|
+
|
195
211
|
begin
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
212
|
+
|
213
|
+
# Convert all single result replies to an array. This is because
|
214
|
+
# rescan_file returns an array of results if more than one hash
|
215
|
+
# is requested to be rescanned.
|
216
|
+
result_array = result.is_a?(Array) ? result : [ result ]
|
217
|
+
|
218
|
+
result_array.collect do |result|
|
219
|
+
if result['response_code'] == 1
|
220
|
+
STDERR.puts "[*] Attempting to parse the results for: #{result['resource']}" if @options['verbose']
|
221
|
+
results = mod.query_report(@config['virustotal']['api-key'], result['resource'])
|
222
|
+
|
223
|
+
while results['response_code'] != 1
|
224
|
+
STDERR.puts "[*] File has not been analyized yet, waiting 60 seconds to try again" if @options['verbose']
|
225
|
+
sleep 60
|
226
|
+
results = mod.query_report(@config['virustotal']['api-key'], result['resource'])
|
227
|
+
end
|
228
|
+
|
229
|
+
[result['resource'], results]
|
230
|
+
#return [result['resource'], results]
|
231
|
+
|
232
|
+
elsif result['response_code'] == 0 and @options['rescan']
|
233
|
+
STDERR.puts "[!] Unknown Virustotal error for rescan of #{result['resource']}." if @options['verbose']
|
234
|
+
next
|
235
|
+
|
236
|
+
elsif result['response_code'] == -1 and @options['rescan']
|
237
|
+
STDERR.puts "[!] Virustotal does not have a sample of #{result['resource']}." if @options['verbose']
|
238
|
+
next
|
239
|
+
|
240
|
+
elsif result['response_code'] == -2
|
241
|
+
STDERR.puts "[!] Virustotal limits exceeded, ***do not edit the timeout values.***"
|
242
|
+
exit(1)
|
243
|
+
else
|
244
|
+
nil
|
203
245
|
end
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
STDERR.puts "[!] Virustotal limits exceeded, ***do not edit the timeout values.***"
|
208
|
-
exit(1)
|
209
|
-
else
|
210
|
-
nil
|
211
|
-
end
|
212
|
-
rescue => e
|
213
|
-
STDERR.puts "[!] An error has occurred retrieving the report. Retrying 60 seconds up #{retries} retries.\n" if @options["verbose"]
|
246
|
+
end
|
247
|
+
rescue => e
|
248
|
+
STDERR.puts "[!] An error has occurred retrieving the report. Retrying 60 seconds up #{retries} retries. #{e.message}\n" if @options['verbose']
|
214
249
|
if retries >= 0
|
215
250
|
sleep 60
|
216
251
|
retry
|
217
252
|
retries = retries - 1
|
218
|
-
end
|
253
|
+
end
|
219
254
|
end
|
220
255
|
end
|
221
|
-
|
256
|
+
|
222
257
|
#
|
223
258
|
#
|
224
259
|
def main(args)
|
225
260
|
parse_options(args)
|
226
261
|
load_config
|
227
|
-
|
262
|
+
|
228
263
|
if @options['output'] == :stdout
|
229
264
|
output_method = :to_stdout
|
265
|
+
elsif @options['output'] == :json
|
266
|
+
output_method = :to_json
|
230
267
|
elsif @options['output'] == :yaml
|
231
268
|
output_method = :to_yaml
|
232
269
|
elsif @options['output'] == :xml
|
233
270
|
output_method = :to_xml
|
234
|
-
print "<results>\n"
|
235
271
|
end
|
236
272
|
|
237
273
|
if @options['proxy'] != nil
|
@@ -242,16 +278,21 @@ module Uirusu
|
|
242
278
|
@files_of_hashes.each do |file|
|
243
279
|
f = File.open(file, 'r')
|
244
280
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
281
|
+
f.each do |hash|
|
282
|
+
hash.chomp!
|
283
|
+
@hashes.push hash
|
284
|
+
end
|
249
285
|
end
|
250
|
-
end
|
286
|
+
end
|
251
287
|
|
252
288
|
if @hashes != nil
|
253
289
|
@hashes.each_with_index do |hash, index|
|
254
|
-
|
290
|
+
if @options['rescan']
|
291
|
+
results = scan_and_wait(Uirusu::VTFile, hash, 5)
|
292
|
+
else
|
293
|
+
results = Uirusu::VTFile.query_report(@config['virustotal']['api-key'], hash)
|
294
|
+
end
|
295
|
+
|
255
296
|
result = Uirusu::VTResult.new(hash, results)
|
256
297
|
print result.send output_method if result != nil
|
257
298
|
sleep @options[:timeout] if index != @hashes.length - 1
|
@@ -270,16 +311,13 @@ module Uirusu
|
|
270
311
|
if @uploads != nil
|
271
312
|
@uploads.each_with_index do |upload, index|
|
272
313
|
results = scan_and_wait(Uirusu::VTFile, upload, 5)
|
273
|
-
result = Uirusu::VTResult.new(results[0], results[1])
|
314
|
+
result = Uirusu::VTResult.new(results[0], results[1])
|
274
315
|
print result.send output_method if result != nil
|
275
316
|
sleep @options[:timeout] if index != @uploads.length - 1
|
276
317
|
end
|
277
318
|
end
|
278
|
-
|
279
|
-
if @options['output'] == :xml
|
280
|
-
print "</results>\n"
|
281
|
-
end
|
282
319
|
end
|
283
320
|
end
|
284
321
|
end
|
285
|
-
end
|
322
|
+
end
|
323
|
+
|
data/lib/uirusu/vtcomment.rb
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
# may be used to endorse or promote products derived from this software
|
16
16
|
# without specific prior written permission.
|
17
17
|
#
|
18
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND
|
19
19
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
20
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
21
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
@@ -30,7 +30,7 @@ module Uirusu
|
|
30
30
|
# Module for submiting comments to Virustotal.com resources using the
|
31
31
|
# Virustotal.com public API
|
32
32
|
module VTComment
|
33
|
-
POST_URL = "
|
33
|
+
POST_URL = Uirusu::VT_API + "/comments/put"
|
34
34
|
|
35
35
|
# Submits a comment to Virustotal.com for a specific resource
|
36
36
|
#
|
data/lib/uirusu/vtfile.rb
CHANGED
@@ -15,7 +15,7 @@
|
|
15
15
|
# may be used to endorse or promote products derived from this software
|
16
16
|
# without specific prior written permission.
|
17
17
|
#
|
18
|
-
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND
|
19
19
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
20
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
21
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
@@ -31,8 +31,9 @@ module Uirusu
|
|
31
31
|
# Module for Accessing the File scan and report functionalities of the
|
32
32
|
# Virustotal.com public API
|
33
33
|
module VTFile
|
34
|
-
SCAN_URL
|
35
|
-
|
34
|
+
SCAN_URL = Uirusu::VT_API + "/file/scan"
|
35
|
+
RESCAN_URL = Uirusu::VT_API + "/file/rescan"
|
36
|
+
REPORT_URL = Uirusu::VT_API + "/file/report"
|
36
37
|
|
37
38
|
# Queries a report from Virustotal.com
|
38
39
|
#
|
@@ -60,6 +61,8 @@ module Uirusu
|
|
60
61
|
JSON.parse(response)
|
61
62
|
when 500
|
62
63
|
nil
|
64
|
+
else
|
65
|
+
raise "Unknown Server error."
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
@@ -91,5 +94,36 @@ module Uirusu
|
|
91
94
|
raise "Unknown Server error."
|
92
95
|
end
|
93
96
|
end
|
97
|
+
|
98
|
+
# Requests an existing file to be rescanned.
|
99
|
+
#
|
100
|
+
# @param api_key Virustotal.com API key
|
101
|
+
# @param resource MD5/sha1/sha256/scan_id to rescan
|
102
|
+
#
|
103
|
+
# @return [JSON] Parsed response
|
104
|
+
def self.rescan_file(api_key, resource)
|
105
|
+
if api_key == nil
|
106
|
+
raise "Invalid API Key"
|
107
|
+
end
|
108
|
+
|
109
|
+
if resource == nil
|
110
|
+
raise "Invalid resource, must be md5/sha1/sha256/scan_id"
|
111
|
+
end
|
112
|
+
|
113
|
+
response = RestClient.post RESCAN_URL, :apikey => api_key, :resource => resource
|
114
|
+
|
115
|
+
case response.code
|
116
|
+
when 429
|
117
|
+
raise "Virustotal limit reached. Try again later."
|
118
|
+
when 403
|
119
|
+
raise "Invalid privileges, please check your API key."
|
120
|
+
when 200
|
121
|
+
JSON.parse(response)
|
122
|
+
when 500
|
123
|
+
nil
|
124
|
+
else
|
125
|
+
raise "Unknown Server error."
|
126
|
+
end
|
127
|
+
end
|
94
128
|
end
|
95
129
|
end
|
data/lib/uirusu/vtresult.rb
CHANGED
@@ -28,61 +28,65 @@
|
|
28
28
|
|
29
29
|
module Uirusu
|
30
30
|
|
31
|
-
#A wrapper class to hold all of the data for a single Virus total result
|
31
|
+
# A wrapper class to hold all of the data for a single Virus total result
|
32
32
|
class VTResult
|
33
|
-
|
34
|
-
|
33
|
+
RESULT_FIELDS = Uirusu::RESULT_FIELDS
|
34
|
+
|
35
|
+
def initialize hash, results
|
36
|
+
if results == nil or results.empty?
|
35
37
|
return
|
38
|
+
|
39
|
+
# Take into consideration being passed an array of results.
|
40
|
+
# For instance, rescan_file will return an array if more than
|
41
|
+
# one sample is given. This ensures single results work.
|
42
|
+
elsif not results.is_a? Array
|
43
|
+
results = [ [ hash, results ] ]
|
36
44
|
end
|
37
45
|
|
38
46
|
@results = Array.new
|
39
47
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
res[
|
78
|
-
|
79
|
-
|
48
|
+
# Results will be an array of: [ [resource, result hash ] ]
|
49
|
+
results.each do |entry|
|
50
|
+
hash = entry.first # Grab the resource (checksum hash)
|
51
|
+
result = entry.last # Grab the query report
|
52
|
+
|
53
|
+
if result['response_code'] == 0
|
54
|
+
res = Hash.new
|
55
|
+
RESULT_FIELDS.each{|field| res[field] = '-' }
|
56
|
+
res['result'] = result['verbose_msg']
|
57
|
+
@results.push res
|
58
|
+
|
59
|
+
elsif result['response_code'] == 0
|
60
|
+
abort "[!] Invalid API KEY! Please correct this! Check ~/.uirusu"
|
61
|
+
else
|
62
|
+
permalink = result['permalink']
|
63
|
+
date = result['scan_date']
|
64
|
+
md5 = result['md5']
|
65
|
+
sha1 = result['sha1']
|
66
|
+
sha256 = result['sha256']
|
67
|
+
|
68
|
+
result['scans'].each do |scanner, value|
|
69
|
+
if value != ''
|
70
|
+
res = Hash.new
|
71
|
+
res[:hash] = hash
|
72
|
+
res[:md5] = md5
|
73
|
+
res[:sha1] = sha1
|
74
|
+
res[:sha256] = sha256
|
75
|
+
res[:scanner] = scanner
|
76
|
+
res[:detected] = value['detected']
|
77
|
+
res[:version] = value['version']
|
78
|
+
|
79
|
+
if value['result'] == nil
|
80
|
+
res[:result] = "Nothing detected"
|
81
|
+
else
|
82
|
+
res[:result] = value['result']
|
83
|
+
end
|
84
|
+
|
85
|
+
res[:update] = value['update']
|
86
|
+
res[:permalink] = permalink unless permalink == nil
|
87
|
+
|
88
|
+
@results.push res
|
80
89
|
end
|
81
|
-
|
82
|
-
res['update'] = value['update']
|
83
|
-
res['permalink'] = permalink unless permalink == nil
|
84
|
-
|
85
|
-
@results.push res
|
86
90
|
end
|
87
91
|
end
|
88
92
|
end
|
@@ -90,17 +94,8 @@ module Uirusu
|
|
90
94
|
#if we didn't have any results lets create a fake not found
|
91
95
|
if @results.size == 0
|
92
96
|
res = Hash.new
|
93
|
-
res[
|
94
|
-
res['
|
95
|
-
res['md5'] = '-'
|
96
|
-
res['sha1'] = '-'
|
97
|
-
res['sha256'] = '-'
|
98
|
-
res['permalink'] = '-'
|
99
|
-
res['detected'] = '-'
|
100
|
-
res['version'] = '-'
|
101
|
-
res['result'] = '-'
|
102
|
-
res['update'] = '-'
|
103
|
-
res['result'] = result["verbose_msg"]
|
97
|
+
RESULT_FIELDS.each{|field| res[field] = '-' }
|
98
|
+
res['result'] = result['verbose_msg']
|
104
99
|
@results.push res
|
105
100
|
end
|
106
101
|
end
|
@@ -109,52 +104,46 @@ module Uirusu
|
|
109
104
|
#
|
110
105
|
def to_stdout
|
111
106
|
result_string = String.new
|
112
|
-
|
113
|
-
|
107
|
+
hashes = Array.new
|
108
|
+
|
109
|
+
@results.sort_by {|k| k[:scanner] }.each do |result|
|
110
|
+
unless hashes.include? result[:hash].downcase
|
111
|
+
result_string << "#{result[:hash]}:\n"
|
112
|
+
hashes << result[:hash].downcase
|
113
|
+
end
|
114
|
+
result_string << "#{result[:scanner]}: ".rjust(25) + "#{result[:result]}\n"
|
114
115
|
end if @results != nil
|
115
116
|
|
116
|
-
|
117
|
+
result_string
|
117
118
|
end
|
118
119
|
|
119
120
|
#
|
120
121
|
#
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
result_string << "vtresult:\n"
|
125
|
-
result_string << " hash: #{result['hash']}\n"
|
126
|
-
result_string << " md5: #{result['md5']}\n"
|
127
|
-
result_string << " sha1: #{result['sha1']}\n"
|
128
|
-
result_string << " sha256: #{result['sha256']}\n"
|
129
|
-
result_string << " scanner: #{result['scanner']}\n"
|
130
|
-
result_string << " detected: #{result['detected']}\n"
|
131
|
-
result_string << " date: #{result['date']}\n"
|
132
|
-
result_string << " permalink: #{result['permalink']}\n" unless result['permalink'] == nil
|
133
|
-
result_string << " result: #{result['result']}\n\n"
|
134
|
-
end if @results != nil
|
122
|
+
def to_json
|
123
|
+
JSON::pretty_generate(@results.map{|entry| { :vtresult => entry } })
|
124
|
+
end
|
135
125
|
|
136
|
-
|
126
|
+
#
|
127
|
+
#
|
128
|
+
def to_yaml
|
129
|
+
@results.map{|entry| { :vtresult => entry } }.to_yaml
|
137
130
|
end
|
138
131
|
|
139
132
|
#
|
140
133
|
#
|
141
134
|
def to_xml
|
142
135
|
result_string = String.new
|
136
|
+
result_string << "<results>\n"
|
143
137
|
@results.each do |result|
|
144
138
|
result_string << "\t<vtresult>\n"
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
result_string << "\t\t<sha256>#{result['sha256']}</sha256>\n"
|
149
|
-
result_string << "\t\t<scanner>#{result['scanner']}</scanner>\n"
|
150
|
-
result_string << "\t\t<detected>#{result['detected']}</detected>\n"
|
151
|
-
result_string << "\t\t<date>#{result['date']}</date>\n"
|
152
|
-
result_string << "\t\t<permalink>#{result['permalink']}</permalink>\n" unless result['permalink'] == nil
|
153
|
-
result_string << "\t\t<result>#{result['result']}</result>\n"
|
139
|
+
RESULT_FIELDS.each{|field|
|
140
|
+
result_string << "\t\t<#{field.to_s}>#{result[field]}</#{field.to_s}>\n" unless field == :permalink and result['permalink'].nil?
|
141
|
+
}
|
154
142
|
result_string << "\t</vtresult>\n"
|
155
143
|
end if @results != nil
|
144
|
+
result_string << "</results>\n"
|
156
145
|
|
157
|
-
|
146
|
+
result_string
|
158
147
|
end
|
159
148
|
end
|
160
149
|
end
|
data/lib/uirusu/vturl.rb
CHANGED
@@ -30,8 +30,8 @@ module Uirusu
|
|
30
30
|
#
|
31
31
|
#
|
32
32
|
module VTUrl
|
33
|
-
SCAN_URL
|
34
|
-
REPORT_URL = "
|
33
|
+
SCAN_URL = Uirusu::VT_API + "/url/scan"
|
34
|
+
REPORT_URL = Uirusu::VT_API + "/url/report"
|
35
35
|
|
36
36
|
# Submits a URL to be scanned by Virustotal.com
|
37
37
|
#
|
@@ -91,4 +91,5 @@ module Uirusu
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
94
|
-
end
|
94
|
+
end
|
95
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uirusu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob Hammack
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-06-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|