diff --git a/.gitignore b/.gitignore
index 2a468241..d00e0de2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,10 @@ control/wordlists/*
!control/wordlists/password.gz
control/tmp/*
!control/tmp/.blank
+logs/*.log
+!logs/.blank
+logs/jobs/*
+!logs/jobs/.blank
*.pot
*.restore
*.idea
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 577f692c..1187567b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,8 +2,26 @@
Notable changes will be documented here
## Current Release
+## [v0.7.2-beta] - 2017-10-19
+### Added
+ - Added Logging Facility, logs should now be under logs/*.log and logs/jobs/*.log (Logs will rotate daily. Logs greater than 30 days will be automatically deleted
+ - Added Collapsing window in analytics in Weak Account Password
+ - Added ability to download user accounts/passwords for accounts that are found to be weak in csv format
+ - Added ability to set OTP passwords for users using google authenticate (thanks: https://github.com/nicbrink)
+
+### Removed
+ - Wordlist Checksums is no longer a background task that fires every 5 seconds. Instead its queued up by wordlist importer.
-## [v0.7.1-beta] - 2017-9-4
+### Fixed
+ - Fixed calculation bug where SmartWordlist was being refactored into new SmartWordlist. Now calculations are quicker
+ - Fixed (hopefully) bug where hashview prematurely 'completes' a job (and subsequently kills a running task). This only happens in rare cases where multiple agents are involved.
+ - Fixed (hopefully) issue where threads not exiting when they're told to. This resulted in issues related to: https://github.com/hashview/hashview/issues/264
+ - Fixed issue where rules listed under task details was displaying rule.id, and not the rule.name: https://github.com/hashview/hashview/issues/342
+ - Fixed SMTP sender error experienced when user sends test message
+ https://github.com/hashview/hashview/issues/341
+ - Fixed issue where foreign DB's listed in config were not being connected too: https://github.com/hashview/hashview/issues/351
+
+## [v0.7.1-beta] - 2017-09-04
### Added
- Rake task to reset db (thanks: nicbrink)
- New hub route/tab if registered
@@ -16,7 +34,7 @@ Notable changes will be documented here
### Fixed
- Fixed issue where importing the same hash twice into the db where one had an incorrect hashtype resulted in a 500 error. Now the entry is updated with the new hashtype.
- - Fixed timouts when searching large hash sets with Hashview Hub
+ - Fixed timeouts when searching large hash sets with Hashview Hub
## [v0.7.0-beta] - 2017-07-22
### Added
diff --git a/Gemfile b/Gemfile
index 5f2b8ca9..d9f8c7b1 100644
--- a/Gemfile
+++ b/Gemfile
@@ -4,6 +4,7 @@ ruby '2.2.2'
group :development do
gem 'rubocop'
gem 'factory_girl'
+ gem 'rotp'
end
group :test do
@@ -28,5 +29,6 @@ gem 'mysql'
gem 'foreman'
gem 'rest-client'
gem 'digest'
+gem 'logger'
diff --git a/Gemfile.lock b/Gemfile.lock
index 6313a134..627f3551 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -111,6 +111,7 @@ GEM
less (~> 2.6.0)
sprockets (> 2, < 4)
tilt
+ logger (1.2.8)
loofah (2.0.3)
nokogiri (>= 1.5.9)
mail (2.6.4)
@@ -177,6 +178,7 @@ GEM
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
+ rotp (3.3.0)
rubocop (0.42.0)
parser (>= 2.3.1.1, < 3.0)
powerpack (~> 0.1)
@@ -238,6 +240,7 @@ DEPENDENCIES
foreman
haml
json
+ logger
mysql
pony
rake
@@ -246,6 +249,7 @@ DEPENDENCIES
resque-scheduler
resque-web
rest-client
+ rotp
rubocop
sinatra (~> 1.4.7)
sinatra-flash
@@ -254,4 +258,4 @@ RUBY VERSION
ruby 2.2.2p95
BUNDLED WITH
- 1.14.6
+ 1.15.4
diff --git a/Rakefile b/Rakefile
index 56dc631a..73438ae9 100644
--- a/Rakefile
+++ b/Rakefile
@@ -16,6 +16,31 @@ Rake::TestTask.new do |t|
t.verbose
end
+# Catching Sigterm
+def shut_down
+ puts 'Attempting to shutdown gracefully...'
+ # Technique based off of https://bugs.ruby-lang.org/issue/7917
+ # and
+ # https://stackoverflow.com/questions/7416318/how-do-i-clear-stuck-stale-resque-workers
+ t = Thread.new do
+ Resque.workers.each {| w | w.unregister_worker}
+ end
+ t.join
+ sleep(5)
+end
+
+# Trap ^C
+Signal.trap('INT') {
+ shut_down
+ exit
+}
+
+# Trap `kill `
+Signal.trap('TERM') {
+ shut_down
+ exit
+}
+
# resque-scheduler needs to know basics from resque::setup
desc 'Resque scheduler setup'
namespace :resque do
@@ -44,14 +69,6 @@ namespace :db do
desc 'Drop from all tables except users and task'
task :reset
- # Are the below ever needed beyond our testing?
- #desc 'create and setup schema'
- #task :clean => [:create] # Should really be made to a series of DELETE FROM
- #desc 'destroy db, create db, setup schema, load defaults'
- #task :reset => [:destroy, :create, :provision_agent, :provision_defaults]
- #desc 'destroy db, create db, setup schema'
- #task :reset_clean => [:destroy, :create, :upgrade, :provision_agent]
-
task :create do
if ENV['RACK_ENV'].nil?
ENV['RACK_ENV'] = 'development'
@@ -59,7 +76,7 @@ namespace :db do
puts "setting up database for environment: #{ENV['RACK_ENV']}"
config = YAML.load_file('config/database.yml')
config = config[ENV['RACK_ENV']]
- user, password, host = config['user'], config['password'], config['hostname']
+ user, password, host = config['user'], config['password'], config['host']
database = config['database']
charset = config['charset'] || ENV['CHARSET'] || 'utf8'
collation = config['collation'] || ENV['COLLATION'] || 'utf8_unicode_ci'
@@ -117,7 +134,7 @@ namespace :db do
puts "destroying database for environment: #{ENV['RACK_ENV']}"
config = YAML.load_file('config/database.yml')
config = config[ENV['RACK_ENV']]
- user, password, host = config['user'], config['password'], config['hostname']
+ user, password, host = config['user'], config['password'], config['host']
database = config['database']
# destroy database in mysql for datamapper
@@ -138,7 +155,7 @@ namespace :db do
puts "removing all data in the database for environment: #{ENV['RACK_ENV']}"
config = YAML.load_file('config/database.yml')
config = config[ENV['RACK_ENV']]
- user, password, host = config['user'], config['password'], config['hostname']
+ user, password, host = config['user'], config['password'], config['host']
database = config['database']
tables = [ 'customers','hashes','hashfilehashes','hashfiles','jobs','jobtasks','rules','sessions','taskqueues','wordlists' ]
@@ -151,13 +168,13 @@ namespace :db do
rescue
raise 'Something went wrong. double check your config/database.yml file and manually test access to mysql.'
end
- end
+ end
end
task :provision_defaults do
config = YAML.load_file('config/database.yml')
config = config[ENV['RACK_ENV']]
- user, password, host = config['user'], config['password'], config['hostname']
+ user, password, host = config['user'], config['password'], config['host']
database = config['database']
puts '[*] Setting up default settings ...'
@@ -242,7 +259,7 @@ namespace :db do
rescue
raise 'Error in creating default dictionary task + rule'
end
-
+
# Create Default SmartWordlist Dictionary
puts '[*] Setting up default smart wordlist task'
query = [
@@ -253,7 +270,7 @@ namespace :db do
rescue
raise 'Error in creating default SmartWordlist task'
end
-
+
# Create Default SmartWordlist Dictionary + Rule Task
puts '[*] Setting up Smart Wordlist dictionary + rule task'
query = [
@@ -264,8 +281,6 @@ namespace :db do
rescue
raise 'Error in creating Smart Wordlist dictionary task + rule'
end
-
-
# Create Default Mask task
puts '[*] Setting up default mask task'
@@ -288,7 +303,7 @@ namespace :db do
rescue
raise 'Error in creating default brute task'
end
-
+
# Create Default Hub Settings
puts '[*] Setting up default hub settings'
query = [
@@ -298,7 +313,7 @@ namespace :db do
system(query.compact.join(' '))
rescue
raise 'Error in creating default hub settings'
- end
+ end
end
desc 'Setup local agent'
@@ -310,7 +325,7 @@ namespace :db do
puts "setting up local agent for environment: #{ENV['RACK_ENV']}"
config = YAML.load_file('config/database.yml')
config = config[ENV['RACK_ENV']]
- user, password, host = config['user'], config['password'], config['hostname']
+ user, password, host = config['user'], config['password'], config['host']
database = config['database']
agent_config = {}
@@ -348,7 +363,7 @@ namespace :db do
config = YAML.load_file('config/database.yml')
config = config[ENV['RACK_ENV']]
- user, password, host = config['user'], config['password'], config['hostname']
+ user, password, host = config['user'], config['password'], config['host']
database = config['database']
puts '[*] Connecting to DB'
@@ -365,9 +380,7 @@ namespace :db do
end
end
- if has_version_column == false
- db_version = Gem::Version.new('0.5.1')
- end
+ db_version = Gem::Version.new('0.5.1') unless has_version_column
# TODO turn into hash where version is key, and value is method/function name?
if Gem::Version.new(db_version) < Gem::Version.new(application_version)
@@ -388,6 +401,10 @@ namespace :db do
if Gem::Version.new(db_version) < Gem::Version.new('0.7.1')
upgrade_to_v071(user, password, host, database)
end
+ # Upgrade to v0.7.2
+ if Gem::Version.new(db_version) < Gem::Version.new('0.7.2')
+ upgrade_to_v072(user, password, host, database)
+ end
else
puts '[*] Your version is up to date!'
end
@@ -395,7 +412,7 @@ namespace :db do
# Incase we missed anything
DataMapper.repository.auto_upgrade!
# DataMapper::Model.descendants.each {|m| m.auto_upgrade! if m.superclass == Object}
- #puts 'db:auto:upgrade executed'
+ # puts 'db:auto:upgrade executed'
end
desc 'Migrate From old DB to new DB schema'
@@ -406,7 +423,7 @@ namespace :db do
config = YAML.load_file('config/database.yml')
config = config[ENV['RACK_ENV']]
- user, password, host = config['user'], config['password'], config['hostname']
+ user, password, host = config['user'], config['password'], config['host']
database = config['database']
begin
@@ -453,13 +470,11 @@ namespace :db do
puts '[*] Inserting new data into table... standby..'
hashes = conn.query("SELECT id,originalhash FROM hashes")
- hashes.each_hash do | entry |
+ hashes.each_hash do |entry|
olddata = conn.query("SELECT username,hashfile_id FROM targets WHERE originalhash='" + entry['originalhash'] + "'")
hash_id = entry['id']
- olddata.each_hash do | row |
- if row['username'].nil?
- row['username'] = 'none'
- end
+ olddata.each_hash do |row|
+ row['username'] = 'none' if row['username'].nil?
row['username'] = row['username'].gsub("'", "\\\\'")
conn.query("INSERT INTO hashfilehashes(hash_id,username,hashfile_id) VALUES ('#{hash_id}','#{row['username']}','#{row['hashfile_id']}')")
end
@@ -467,17 +482,15 @@ namespace :db do
# Remove old tables
puts '[*] Removing old tables'
- conn.query("DROP TABLE targets")
-
+ conn.query('DROP TABLE targets')
rescue Mysql::Error => e
puts e.errno
puts e.error
ensure
- conn.close if conn
+ conn.close if conn
end
-
end
end
@@ -594,12 +607,12 @@ def upgrade_to_v060(user, password, host, database)
conn.query("UPDATE settings SET version = '0.6.0'")
puts '[*] Upgrade to v0.6.0 complete.'
- return '0.6.0'
+ '0.6.0'
end
def upgrade_to_v061(user, password, host, database)
#DataMapper.repository.auto_upgrade!
- DataMapper::Model.descendants.each {|m| m.auto_upgrade! if m.superclass == Object}
+ DataMapper::Model.descendants.each { |m| m.auto_upgrade! if m.superclass == Object }
puts '[*] Upgrading from v0.6.0 to v0.6.1'
conn = Mysql.new host, user, password, database
@@ -608,12 +621,12 @@ def upgrade_to_v061(user, password, host, database)
conn.query("UPDATE settings SET version = '0.6.1'")
puts '[*] Upgrade to v0.6.1 complete.'
- return '0.6.1'
+ '0.6.1'
end
def upgrade_to_v070(user, password, host, database)
DataMapper.repository.auto_upgrade!
- DataMapper::Model.descendants.each {|m| m.auto_upgrade! if m.superclass == Object}
+ DataMapper::Model.descendants.each { |m| m.auto_upgrade! if m.superclass == Object }
puts '[*] Upgrading from v0.6.1 to v0.7.0'
conn = Mysql.new host, user, password, database
@@ -674,7 +687,7 @@ def upgrade_to_v070(user, password, host, database)
# Adding to DB
rule_file = Rules.new
- rule_file.lastupdated = Time.now()
+ rule_file.lastupdated = Time.now
rule_file.name = name
rule_file.path = path_file
rule_file.size = 0
@@ -727,7 +740,7 @@ def upgrade_to_v070(user, password, host, database)
end
def upgrade_to_v071(user, password, host, database)
- DataMapper::Model.descendants.each {|m| m.auto_upgrade! if m.superclass == Object}
+ DataMapper::Model.descendants.each { |m| m.auto_upgrade! if m.superclass == Object }
puts '[*] Upgrading from v0.7.0 to v0.7.1'
conn = Mysql.new host, user, password, database
@@ -737,3 +750,29 @@ def upgrade_to_v071(user, password, host, database)
puts '[+] Upgrade to v0.7.1 complete.'
end
+def upgrade_to_v072(user, password, host, database)
+ DataMapper::Model.descendants.each { |m| m.auto_upgrade! if m.superclass == Object }
+
+ puts '[*] Upgrading from v0.7.1 to v0.7.2'
+ conn = Mysql.new host, user, password, database
+
+ # Remove unused columns
+ puts '[*] Removing unused database structures.'
+ conn.query('ALTER TABLE jobs DROP COLUMN policy_min_pass_length')
+ conn.query('ALTER TABLE jobs DROP COLUMN policy_complexity_default')
+ conn.query('ALTER TABLE jobs DROP COLUMN targettype')
+ conn.query('ALTER TABLE settings DROP COLUMN clientmode')
+
+ # Updating database config file
+ puts '[*] Fixing db config entries.'
+ File.rename('config/database.yml', 'config/database.yml.old')
+ config_content = File.read('config/database.yml.old').gsub(/hostname:/, 'host:')
+ File.open('config/database.yml', 'w') do |out|
+ out << config_content
+ end
+ File.delete('config/database.yml.old')
+
+ # FINALIZE UPGRADE
+ conn.query('UPDATE settings SET version = \'0.7.2\'')
+ puts '[+] Upgrade to v0.7.2 complete.'
+end
diff --git a/VERSION b/VERSION
index 7deb86fe..d5cc44d1 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.7.1
\ No newline at end of file
+0.7.2
\ No newline at end of file
diff --git a/config/database.travis.yml b/config/database.travis.yml
index 229f3896..590015db 100644
--- a/config/database.travis.yml
+++ b/config/database.travis.yml
@@ -1,6 +1,6 @@
production:
adapter: mysql
- hostname: "localhost"
+ host: "localhost"
port: 3306
user: "root"
password: ""
@@ -8,7 +8,7 @@ production:
development:
adapter: mysql
- hostname: "localhost"
+ host: "localhost"
port: 3306
user: "root"
password: ""
@@ -16,8 +16,8 @@ development:
test:
adapter: mysql
- hostname: "localhost"
+ host: "localhost"
port: 3306
user: "root"
password: ""
- database: "hashview_test"
\ No newline at end of file
+ database: "hashview_test"
diff --git a/config/database.yml.example b/config/database.yml.example
index 36ac48b8..651c3a37 100644
--- a/config/database.yml.example
+++ b/config/database.yml.example
@@ -1,6 +1,6 @@
production:
adapter: mysql
- hostname: "localhost"
+ host: "localhost"
port: 3306
user: "root"
password: "CHANGEME"
@@ -8,7 +8,7 @@ production:
development:
adapter: mysql
- hostname: "localhost"
+ host: "localhost"
port: 3306
user: "root"
password: "CHANGEME"
@@ -16,8 +16,8 @@ development:
test:
adapter: mysql
- hostname: "localhost"
+ host: "localhost"
port: 3306
user: "root"
password: "CHANGEME"
- database: "hashview_test"
\ No newline at end of file
+ database: "hashview_test"
diff --git a/config/resque_schedule.yml b/config/resque_schedule.yml
index 04b5eb78..58e743da 100644
--- a/config/resque_schedule.yml
+++ b/config/resque_schedule.yml
@@ -14,23 +14,17 @@
# schedule for wordlist importer
do_wordlist_importer:
- every: 5s
+ every: 60s
class: "WordlistImporter"
queue: management
description: "This job automatically imports wordlists if placed in controls/wordlists dir"
do_rulelist_importer:
- every: 5s
+ every: 10s
class: "RuleImporter"
queue: management
description: "This job automatically imports rule files if placed in controls/rules dir"
-do_wordlist_checksum:
- every: 20s
- class: "WordlistChecksum"
- queue: management
- description: "This job creates a sha256 checksum for all wordlists. This keeps wordlists in sync for multiple agents"
-
# schedule for cleanup task
do_cleanUp:
every: 1d
diff --git a/hashview.rb b/hashview.rb
index a43542f7..b15a5b09 100644
--- a/hashview.rb
+++ b/hashview.rb
@@ -4,6 +4,7 @@
require 'haml'
require 'resque'
require 'resque/server'
+require 'logger'
require_relative 'models/master'
require_relative 'helpers/init'
@@ -47,4 +48,4 @@
Rack::Utils.key_space_limit = 68719476736
# start our local agent
-Resque.enqueue(LocalAgent)
+Resque.enqueue(LocalAgent)
\ No newline at end of file
diff --git a/helpers/build_crack_cmd.rb b/helpers/build_crack_cmd.rb
index 44807dfa..b0a25f4b 100644
--- a/helpers/build_crack_cmd.rb
+++ b/helpers/build_crack_cmd.rb
@@ -37,7 +37,7 @@ def buildCrackCmd(job_id, task_id)
chunks[chunk_num] = [skip, limit]
- chunk_num = chunk_num + 1
+ chunk_num += 1
chunk_skip = limit
end
end
@@ -55,57 +55,60 @@ def buildCrackCmd(job_id, task_id)
# we assign and write output file before hashcat.
# if hashcat creates its own output it does so with
- # elvated permissions and we wont be able to read it
+ # elevated permissions and we wont be able to read it
crack_file = 'control/outfiles/hc_cracked_' + @job.id.to_s + '_' + @task.id.to_s + '.txt'
File.open(crack_file, 'w')
- if attackmode == 'bruteforce'
+ case attackmode
+ when 'bruteforce'
cmd = hc_binpath + ' -m ' + hashtype + ' --potfile-disable' + ' --status --status-timer=15' + ' --runtime=' + max_task_time + ' --outfile-format 5 ' + ' --outfile ' + crack_file + ' ' + ' -a 3 ' + target_file
- elsif attackmode == 'maskmode'
+ when 'maskmode'
cmd = hc_binpath + ' -m ' + hashtype + ' --potfile-disable' + ' --status --status-timer=15' + ' --outfile-format 5 ' + ' --outfile ' + crack_file + ' ' + ' -a 3 ' + target_file + ' ' + mask
- elsif attackmode == 'dictionary'
+ when 'dictionary'
if @task.hc_rule.nil? || @task.hc_rule == 'none'
cmd = hc_binpath + ' -m ' + hashtype + ' --potfile-disable' + ' --status --status-timer=15' + ' --outfile-format 5 ' + ' --outfile ' + crack_file + ' ' + target_file + ' ' + wordlist.path
else
cmd = hc_binpath + ' -m ' + hashtype + ' --potfile-disable' + ' --status --status-timer=15' + ' --outfile-format 5 ' + ' --outfile ' + crack_file + ' ' + ' -r ' + rules_file.path + ' ' + target_file + ' ' + wordlist.path
end
- elsif attackmode == 'combinator'
+ when 'combinator'
cmd = hc_binpath + ' -m ' + hashtype + ' --potfile-disable' + ' --status --status-timer=15' + ' --outfile-format 5 ' + ' --outfile ' + crack_file + ' ' + ' -a 1 ' + target_file + ' ' + wordlist_one.path + ' ' + ' ' + wordlist_two.path + ' ' + @task.hc_rule.to_s
+ else
+ puts 'INVALID ATTACK MODE: ' + attackmode.to_s
end
# Add global options
# --opencl-device-types
if hc_settings.opencl_device_types.to_s != '0'
- cmd = cmd + ' --opencl-device-types ' + hc_settings.opencl_device_types.to_s
+ cmd += ' --opencl-device-types ' + hc_settings.opencl_device_types.to_s
end
# --workload-profile
if hc_settings.workload_profile.to_s != '0'
- cmd = cmd + ' --workload-profile ' + hc_settings.workload_profile.to_s
+ cmd += ' --workload-profile ' + hc_settings.workload_profile.to_s
end
# --gpu-temp-disable
if hc_settings.gpu_temp_disable == true
- cmd = cmd + ' --gpu-temp-disable'
+ cmd += ' --gpu-temp-disable'
end
# --gpu-temp-abort
if hc_settings.gpu_temp_abort.to_s != '0'
- cmd = cmd + ' --gpu-temp-abort=' + hc_settings.gpu_temp_abort.to_s
+ cmd += ' --gpu-temp-abort=' + hc_settings.gpu_temp_abort.to_s
end
# --gpu-temp-retain
if hc_settings.gpu_temp_retain.to_s != '0'
- cmd = cmd + ' --gpu-temp-retain=' + hc_settings.gpu_temp_retain.to_s
+ cmd += ' --gpu-temp-retain=' + hc_settings.gpu_temp_retain.to_s
end
# --force
if hc_settings.hc_force == true
- cmd = cmd + ' --force'
+ cmd += ' --force'
end
# add skip and limit if we are chunking this task
- if chunking
+ if chunking == true
chunks.each do |_unused, value|
if attackmode == 'maskmode' || attackmode == 'dictionary'
cmds << cmd + ' -s ' + value[0].to_s + ' -l ' + value[1].to_s
@@ -116,6 +119,6 @@ def buildCrackCmd(job_id, task_id)
cmds << cmd
end
- return cmds
+ cmds
end
end
diff --git a/helpers/compute_task_keyspace.rb b/helpers/compute_task_keyspace.rb
index 86e86d46..abb77322 100644
--- a/helpers/compute_task_keyspace.rb
+++ b/helpers/compute_task_keyspace.rb
@@ -1,6 +1,5 @@
# this helper generates the keyspace of a given task. Helpful when chunking the task for multiple agents.
def getKeyspace(task)
- p '=============== generateing keyspace for task ====================='
# get hashcat binarypath from config
hashcatbinpath = JSON.parse(File.read('config/agent_config.json'))['hc_binary_path']
@@ -39,7 +38,6 @@ def getKeyspace(task)
cmd = hashcatbinpath + ' ' + wordlist1_path + ' --keyspace'
keyspace2 = `#{cmd}`
cmd = hashcatbinpath + ' ' + wordlist2_path + ' --keyspace'
-
end
# run hashcat keyspace command
diff --git a/helpers/cracked_importer.rb b/helpers/cracked_importer.rb
index 27e6881b..eac3243b 100644
--- a/helpers/cracked_importer.rb
+++ b/helpers/cracked_importer.rb
@@ -12,13 +12,8 @@ def updateDbRunTime(job_id, hashfile_id, run_time)
# imports the uploaded crackfile
def importCracked(id, crack_file, run_time)
# this assumes a job completed successfully. we need to add check for failures or killed processes
- puts '==== Importing cracked hashes ====='
-
- # Disabling now that we are chunking. Not sure if this is a good idea yet
- #updateJobTaskStatus(id, 'Importing')
jobtasks = Jobtasks.first(id: id)
- #crack_file = 'control/outfiles/hc_cracked_' + jobtasks.job_id.to_s + '_' + jobtasks.task_id.to_s + '.txt'
job = Jobs.first(id: jobtasks.job_id)
# determine hashfile path
@@ -40,8 +35,7 @@ def importCracked(id, crack_file, run_time)
hash_pass.pop # removes tail entry which should have been the plaintext (in hex)
# Handle salted hashes
# There's gotta be a better way to do this
- if hashtype == '10' or hashtype == '20' or hashtype == '30' or hashtype == '40' or hashtype == '50' or hashtype == '60' or hashtype == '110' or hashtype == '120' or hashtype == '121' or hashtype == '130' or hashtype == '140' or hashtype == '150' or hashtype == '160' or hashtype == '1100' or hashtype == '1410' or hashtype == '1420' or hashtype == '1430' or hashtype == '1440' or hashtype == '1450' or hashtype == '1460' or hashtype == '2611' or hashtype == '2711' or hashtype == '3610' or hashtype == '3710' or hashtype == '3720' or hashtype == '3910' or hashtype == '4010' or hashtype == '4110' or hashtype == '2711' or hashtype == '11000'
- #hash = hash_pass[0].to_s + ':' + hash_pass[1].to_s
+ if hashtype == '10' || hashtype == '20' || hashtype == '30' || hashtype == '40' || hashtype == '50' || hashtype == '60' ||hashtype == '110' || hashtype == '120' || hashtype == '121' || hashtype == '130' || hashtype == '140' || hashtype == '150' || hashtype == '160' || hashtype == '1100' || hashtype == '1410' || hashtype == '1420' || hashtype == '1430' || hashtype == '1440' || hashtype == '1450' || hashtype == '1460' || hashtype == '2611' || hashtype == '2711' || hashtype == '3610' || hashtype == '3710' || hashtype == '3720' || hashtype == '3910' || hashtype == '4010' || hashtype == '4110' || hashtype == '2711' || hashtype == '11000'
hash = hash_pass.join(":")
elsif hashtype == '5500'
hash = hash_pass[3] + ':' + hash_pass[4] + ':' + hash_pass[5]
@@ -49,23 +43,18 @@ def importCracked(id, crack_file, run_time)
hash = hash_pass[0] + ':' + hash_pass[1] + ':' + hash_pass[2] + ':' + hash_pass[3] + ':' + hash_pass[4] + ':' + hash_pass[5]
elsif hashtype == '7400'
parts = hash_pass[0].split('$')
- p 'PARTS: ' + parts.to_s
- hash = '%' + parts[3].to_s + '$' + parts[4].to_s
+ # p 'PARTS: ' + parts.to_s
+ hash = '%' + parts[3].to_s + '$' + parts[4].to_s
else
hash = hash_pass[0]
end
- # p 'job.hashfile_id: ' + job.hashfile_id.to_s
- # p 'hashfilehash.hash_id: ' + hashfilehash.to_s
- # p 'hashtype: ' + hashtype.to_s
- # p 'PLAINTEXT: ' + plaintext.to_s
- # p 'Hash: ' + hash.to_s
# This will pull all hashes from DB regardless of job id
if hashtype == '7400'
results = repository(:default).adapter.select('SELECT * FROM hashes WHERE (hashtype = 7400 AND originalhash like ?)', hash)[0]
records = Hashes.all(fields: [:id, :cracked, :plaintext, :lastupdated], id: results.id)
else
- records = Hashes.all(fields: [:id, :cracked, :plaintext, :lastupdated], originalhash: hash, cracked: 0 )
+ records = Hashes.all(fields: [:id, :cracked, :plaintext, :lastupdated], originalhash: hash, cracked: 0)
end
# Yes its slow... we know.
records.each do |entry|
@@ -87,10 +76,6 @@ def importCracked(id, crack_file, run_time)
p 'ERROR: ' + $!.to_s
end
- puts '==== Crack File Deleted ===='
-
- # commenting this out now that we are chunking
- #updateJobTaskStatus(id, 'Completed')
# TODO this might be broken now that we are chunking
updateDbRunTime(id, job.hashfile_id, run_time)
end
diff --git a/helpers/email.rb b/helpers/email.rb
index faeb63bd..3df10b0b 100644
--- a/helpers/email.rb
+++ b/helpers/email.rb
@@ -10,7 +10,7 @@ def sendEmail(recipient, sub, msg)
Pony.options = {
:via => :smtp,
:via_options => {
- :from => smtp_sender.to_s,
+ :from => smtp_settings.smtp_sender.to_s,
:address => smtp_server.to_s,
:port => smtp_port.to_s,
:enable_starttls_auto => use_tls.to_s,
@@ -31,7 +31,7 @@ def sendEmail(recipient, sub, msg)
}
end
- if smtp_settings.smtp_sender.nil? or smtp_settings.smtp_sender.empty?
+ if smtp_settings.smtp_sender.nil? || smtp_settings.smtp_sender.empty?
sender_addr = 'no-reply@hashview'
else
sender_addr = smtp_settings.smtp_sender.to_s
diff --git a/helpers/hashimporter.rb b/helpers/hashimporter.rb
index a1bc11df..1e27857d 100644
--- a/helpers/hashimporter.rb
+++ b/helpers/hashimporter.rb
@@ -73,11 +73,7 @@ def importPwdump(hash, hashfile_id, type)
end
def machineAcct?(username)
- if username =~ /\$/
- return true
- else
- return false
- end
+ username =~ /\$/ ? true : false
end
def importShadow(hash, hashfile_id, type)
@@ -398,12 +394,14 @@ def modeToFriendly(mode)
return 'sha1($salt.unicode($pass))' if mode == '140'
return 'HMAC-SHA1 (key = $pass)' if mode == '150'
return 'HMAC-SHA1 (key = $salt)' if mode == '160'
- # return 'sha1(LinkedIn)' if mode == '190'
+ # 'sha1(LinkedIn)' if mode == '190'
return 'MySQL323' if mode == '200'
return 'md5crypt' if mode == '500'
return 'MD4' if mode == '900'
return 'NTLM' if mode == '1000'
+ return 'SHA-256' if mode == '1400'
return 'descrypt' if mode == '1500'
+ return 'SHA-512' if mode == '1700'
return 'sha512crypt' if mode == '1800'
return 'Double MD5' if mode == '2600'
return 'LM' if mode == '3000'
@@ -429,28 +427,44 @@ def modeToFriendly(mode)
return 'Lotus Notes/Domino 5' if mode == '8600'
return 'PrestaShop' if mode == '11000'
return 'unknown' if mode == '99999'
- return 'unknown'
+ 'unknown'
end
def friendlyToMode(friendly)
return '0' if friendly == 'MD5'
- return '1000' if friendly == 'NTLM'
- return '3000' if friendly == 'LM'
+ return '10' if friendly == 'md5($pass.$salt)'
+ return '20' if friendly == 'md5($salt.$pass)'
+ return '30' if friendly == 'md5(unicode($pass).$salt)'
+ return '40' if friendly == 'md5($salt.unicode($pass))'
return '100' if friendly == 'SHA-1'
+ return '200' if friendly == 'MySQL323'
return '500' if friendly == 'md5crypt'
+ return '900' if friendly == 'MD4'
+ return '1000' if friendly == 'NTLM'
+ return '1400' if friendly == 'SHA-256'
+ return '1700' if friendly == 'SHA-512'
+ return '2600' if friendly == 'Double MD5'
+ return '3000' if friendly == 'LM'
+ return '3500' if friendly == 'md5(md5(md5($pass)))'
+ return '4400' if friendly == 'md5(sha1($pass))'
+ return '4500' if friendly == 'sha1(sha1($pass))'
+ return '4600' if friendly == 'sha1(sha1(sha1($pass)))'
+ return '4700' if friendly == 'sha1(md5($pass))'
return '3200' if friendly == 'bcrypt'
return '7400' if friendly == 'sha512crypt'
return '1800' if friendly == 'sha256crypt'
return '1500' if friendly == 'descrypt'
return '5500' if friendly == 'NetNTLMv1'
return '5600' if friendly == 'NetNTLMv2'
+ return '6000' if friendly == 'RipeMD160'
+ '99999'
end
def importHash(hash_array, hashfile_id, file_type, hashtype)
hash_array.each do |entry|
entry = entry.gsub(/\s+/, '') # remove all spaces
- if file_type == 'pwdump' or file_type == 'smart hashdump'
- importPwdump(entry.chomp, hashfile_id, hashtype) #because the format is the same aside from the trailing ::
+ if file_type == 'pwdump' || file_type == 'smart hashdump'
+ importPwdump(entry.chomp, hashfile_id, hashtype) # because the format is the same aside from the trailing ::
elsif file_type == 'shadow'
importShadow(entry.chomp, hashfile_id, hashtype)
elsif file_type == 'hash_only'
@@ -475,7 +489,7 @@ def detectHashType(hash_file, file_type)
@hashtypes = []
File.readlines(hash_file).each do |entry|
entry = entry.gsub(/\s+/, "") # remove all spaces
- if file_type == 'pwdump' or file_type == 'smart_hashdump'
+ if file_type == 'pwdump' || file_type == 'smart_hashdump'
elements = entry.split(':')
@modes = getMode(elements[2])
@modes.each do |mode|
diff --git a/helpers/hc_stdout_parser.rb b/helpers/hc_stdout_parser.rb
index 274e2dd0..5b8c61ed 100644
--- a/helpers/hc_stdout_parser.rb
+++ b/helpers/hc_stdout_parser.rb
@@ -25,6 +25,6 @@ def hashcatParser(file)
rescue SystemCallError => e
puts e
end
- return status
+ status
end
end
diff --git a/helpers/init.rb b/helpers/init.rb
index bbb362ec..de2118f5 100644
--- a/helpers/init.rb
+++ b/helpers/init.rb
@@ -10,4 +10,4 @@
require_relative 'tasks'
require_relative 'cracked_importer'
require_relative 'compute_task_keyspace'
-require_relative 'smartWordlist'
+require_relative 'smartWordlist'
\ No newline at end of file
diff --git a/helpers/sanitization.rb b/helpers/sanitization.rb
index a3276353..f8fb5545 100644
--- a/helpers/sanitization.rb
+++ b/helpers/sanitization.rb
@@ -2,12 +2,8 @@
# Take you to the var wash baby
def varWash(params)
params.keys.each do |key|
- if params[key].is_a?(String)
- params[key] = cleanString(params[key])
- end
- if params[key].is_a?(Array)
- params[key] = cleanArray(params[key])
- end
+ params[key] = cleanString(params[key]) if params[key].is_a?(String)
+ params[key] = cleanArray(params[key]) if params[key].is_a?(Array)
end
end
@@ -20,7 +16,7 @@ def cleanArray(array)
array.each do |entry|
clean_array.push(cleanString(entry))
end
- return clean_array
+ clean_array
end
end
diff --git a/helpers/sessions.rb b/helpers/sessions.rb
index 7d68c55a..8859abb0 100644
--- a/helpers/sessions.rb
+++ b/helpers/sessions.rb
@@ -12,10 +12,6 @@ def getUsername
def agentAuthorized(uuid)
auth = Agents.first(:uuid => uuid, :status.not => 'Pending')
- if auth
- return true
- else
- return false
- end
+ auth ? true : false
end
end
diff --git a/helpers/smartWordlist.rb b/helpers/smartWordlist.rb
index aed5ba27..e2ecb315 100644
--- a/helpers/smartWordlist.rb
+++ b/helpers/smartWordlist.rb
@@ -8,7 +8,7 @@ def updateSmartWordlist
end
wordlist = Wordlists.first(name: 'Smart Wordlist')
- # Create Smart word list if one doesnt exists
+ # Create Smart word list if one doesn't exists
if wordlist.nil?
wordlist = Wordlists.new
wordlist.lastupdated = Time.now
@@ -39,7 +39,7 @@ def updateSmartWordlist
shell_cmd = 'sort --parallel ' + cpu_count.to_s + ' -u control/tmp/plaintext.txt '
@wordlists = Wordlists.all
@wordlists.each do |entry|
- shell_cmd = shell_cmd + entry.path.to_s + ' '
+ shell_cmd = shell_cmd + entry.path.to_s + ' ' if entry.type == 'static'
end
# We move to temp to prevent wordlist importer from accidentally loading the smart wordlist too early
shell_cmd += '-o control/tmp/SmartWordlist.txt'
@@ -62,7 +62,7 @@ def updateSmartWordlist
# Remove plaintext list
File.delete('control/tmp/plaintext.txt') if File.exist?('control/tmp/plaintext.txt')
- # Update keyspace per task ( really shouldbe done at runtime)
+ # Update keyspace per task ( really should be done at runtime)
tasks = Tasks.all(wl_id: wordlist.id)
tasks.each do |task|
task.keyspace = getKeyspace(task)
diff --git a/helpers/status.rb b/helpers/status.rb
index 3f2cca6e..08f68819 100644
--- a/helpers/status.rb
+++ b/helpers/status.rb
@@ -1,6 +1,6 @@
def isBusy?
@results = `ps awwux | grep -i Hashcat | egrep -v "(grep|sudo|resque|^$)"`
- return true if @results.length > 1
+ true if @results.length > 1
end
def isDevelopment?
@@ -19,9 +19,7 @@ def isOldVersion?
has_version_column = false
@tables = repository(:default).adapter.select('DESC settings')
@tables.each do |row|
- if row.field == 'version'
- has_version_column = true
- end
+ has_version_column = true if row.field == 'version'
end
if has_version_column
@@ -35,9 +33,8 @@ def isOldVersion?
end
else
puts 'No version column found. Assuming Version 0.5.1'
- return true
+ true
end
- return false
end
def updateTaskqueueStatus(taskqueue_id, status, agent_id)
@@ -49,8 +46,10 @@ def updateTaskqueueStatus(taskqueue_id, status, agent_id)
# if we are setting a status to completed, check to see if this is the last task in queue. if so, set jobtask to completed
if status == 'Completed'
- remainingtasks = Taskqueues.all(jobtask_id: queue.jobtask_id, job_id: queue.job_id, status: 'Queued')
- if remainingtasks.empty?
+ remaining_queued_tasks = Taskqueues.all(jobtask_id: queue.jobtask_id, job_id: queue.job_id, status: 'Queued')
+ remaining_running_tasks = Taskqueues.all(jobtask_id: queue.jobtask_id, job_id: queue.job_id, status: 'Running')
+ remaining_importing_tasks = Taskqueues.all(jobtask_id: queue.jobtask_id, job_id: queue.job_id, status: 'Importing')
+ if remaining_queued_tasks.empty? && remaining_running_tasks.empty? && remaining_importing_tasks.empty?
updateJobTaskStatus(queue.jobtask_id, 'Completed')
end
end
@@ -68,6 +67,7 @@ def updateJobTaskStatus(jobtask_id, status)
job = Jobs.first(id: jobtask.job_id)
if job.status == 'Queued'
job.status = 'Running'
+ job.started_at = Time.now
job.save
end
# find all tasks for current job:
@@ -101,8 +101,9 @@ def updateJobTaskStatus(jobtask_id, status)
end
# toggle job status
- if done == true
+ if done
job.status = 'Completed'
+ job.ended_at = Time.now
job.save
# purge all queued tasks
taskqueues = Taskqueues.all(job_id: job.id)
diff --git a/helpers/test_helper.rb b/helpers/test_helper.rb
index 00ed26f6..6fad7abf 100644
--- a/helpers/test_helper.rb
+++ b/helpers/test_helper.rb
@@ -2,8 +2,5 @@
require 'minitest/autorun'
require 'rack/test'
require 'factory_girl'
-#require 'sinatra'
-#require 'sinatra/flash'
-#require 'haml'
-require File.expand_path '../../hashview.rb', __FILE__
+require File.expand_path '../../hashview.rb', __FILE__
\ No newline at end of file
diff --git a/jobs/background_worker.rb b/jobs/background_worker.rb
index 3ef87e3c..5b905d88 100644
--- a/jobs/background_worker.rb
+++ b/jobs/background_worker.rb
@@ -9,21 +9,21 @@ class Api
# obtain remote ip and port from local config
begin
options = JSON.parse(File.read('config/agent_config.json'))
- @server = options['ip'] + ":" + options['port']
+ @server = options['ip'] + ':' + options['port']
@uuid = options['uuid']
@hashcatbinpath = options['hc_binary_path'].to_s
rescue
- "Error reading config/agent_config.json. Did you run rake db:provision_agent ???"
+ 'Error reading config/agent_config.json. Did you run rake db:provision_agent ???'
end
######### generic api handling of GET and POST request ###########
def self.get(url)
begin
response = RestClient::Request.execute(
- :method => :get,
- :url => url,
- :cookies => {:agent_uuid => @uuid},
- :verify_ssl => false
+ method: :get,
+ url: url,
+ cookies: {agent_uuid: @uuid},
+ verify_ssl: false
)
return response.body
rescue RestClient::Exception => e
@@ -34,12 +34,12 @@ def self.get(url)
def self.post(url, payload)
begin
response = RestClient::Request.execute(
- :method => :post,
- :url => url,
- :payload => payload.to_json,
- :headers => {:accept => :json},
- :cookies => {:agent_uuid => @uuid},
- :verify_ssl => false
+ method: :post,
+ url: url,
+ payload: payload.to_json,
+ headers: {accept: :json},
+ cookies: {agent_uuid: @uuid},
+ verify_ssl: false
)
return response.body
rescue RestClient::Exception => e
@@ -53,14 +53,14 @@ def self.post(url, payload)
# get heartbeat when we are looking for work to do
def self.heartbeat()
url = "https://#{@server}/v1/agents/#{@uuid}/heartbeat"
- puts "HEARTBEETING"
+ # puts "HEARTBEETING"
return self.get(url)
end
# post hearbeat is used when agent is working
def self.post_heartbeat(payload)
url = "https://#{@server}/v1/agents/#{@uuid}/heartbeat"
- puts "HEARTBEETING"
+ # puts "HEARTBEETING"
return self.post(url, payload)
end
@@ -136,32 +136,21 @@ def self.wordlists()
return self.get(url)
end
- # download a wordlist
- #def self.wordlist()
- # url = "https://#{@server}/v1/wordlist/:id"
- # return self.get(url)
- #end
-
- # save wordlist to disk
- #def self.save_wordlist(localpath='control/wordlists/thisisjustatest.txt')
- # File.write(localpath)
- #end
-
# upload crack file
def self.upload_crackfile(jobtask_id, crack_file, run_time)
url = "https://#{@server}/v1/jobtask/#{jobtask_id}/crackfile/upload"
- puts "attempting upload #{crack_file}"
+ # puts "attempting upload #{crack_file}"
begin
request = RestClient::Request.new(
- :method => :post,
- :url => url,
- :payload => {
- :multipart => true,
- :file => File.new(crack_file, 'rb'),
- :runtime => run_time
+ method: :post,
+ url: url,
+ payload: {
+ multipart: true,
+ file: File.new(crack_file, 'rb'),
+ runtime: run_time
},
- :cookies => {:agent_uuid => @uuid},
- :verify_ssl => false
+ cookies: {agent_uuid: @uuid},
+ verify_ssl: false
)
response = request.execute
rescue RestClient::Exception => e
@@ -268,6 +257,16 @@ def self.perform()
# this is our background worker for the task queue
# other workers will be ran from a hashview agent
+ # Setup Logger
+ logger_background_worker = Logger.new('logs/jobs/background_worker.log', 'daily')
+ if ENV['RACK_ENV'] == 'development'
+ logger_background_worker.level = Logger::DEBUG
+ else
+ logger_background_worker.level = Logger::INFO
+ end
+
+ logger_background_worker.debug('Background Worker Class() - has started')
+
hashcatbinpath = JSON.parse(File.read('config/agent_config.json'))['hc_binary_path']
# is hashcat working? if so, how fast are you? provide basic information to master server
@@ -278,21 +277,22 @@ def self.perform()
hc_perfstats = hashcatBenchmarkParser(hc_benchmark(hashcatbinpath))
Api.stats(hc_devices, hc_perfstats)
- while(1)
+ while(true)
sleep(4)
# find pid
pid = getHashcatPid
# wait a bit to avoid race condition
- if !pid.nil? and File.exist?('control/tmp/agent_current_task.txt')
+ if !pid.nil? && File.exist?('control/tmp/agent_current_task.txt')
sleep(10)
pid = getHashcatPid
end
# ok either do nothing or start working
if pid.nil?
- puts "AGENT IS WORKING RIGHT NOW"
+ # Do we need to even log this?
+ logger_background_worker.debug('Agent is working right now')
else
# if we have taskqueue tmp file locally, delete it
@@ -303,11 +303,10 @@ def self.perform()
payload['agent_status'] = 'Idle'
payload['hc_benchmark'] = 'example data'
heartbeat = Api.post_heartbeat(payload)
- puts '======================================'
heartbeat = JSON.parse(heartbeat)
- puts heartbeat
+ logger_background_worker.info(heartbeat)
- if heartbeat['type'] == 'message' and heartbeat['msg'] == 'START'
+ if heartbeat['type'] == 'message' && heartbeat['msg'] == 'START'
jdata = Api.queue_by_id(heartbeat['task_id'])
jdata = JSON.parse(jdata)
@@ -359,7 +358,7 @@ def self.perform()
# get our hashcat command and sub out the binary path
cmd = jdata['command']
cmd = replaceHashcatBinPath(cmd)
- puts cmd
+ logger_background_worker.debug(cmd)
# this variable is used to determine if the job was canceled
@canceled = false
@@ -377,8 +376,8 @@ def self.perform()
catch :mainloop do
while thread1.status do
sleep 4
- puts 'WORKING IN THREAD'
- puts "WORKING ON ID: #{jdata['id']}"
+ #puts 'WORKING IN THREAD'
+ logger_background_worker.info("WORKING ON ID: #{jdata['id']}")
payload = {}
payload['agent_status'] = 'Working'
payload['agent_task'] = jdata['id']
@@ -410,25 +409,19 @@ def self.perform()
if File.exist?(crack_file) && ! File.zero?(crack_file)
Api.upload_crackfile(jobtask['id'], crack_file, run_time)
else
- puts "No successful cracks for this task. Skipping upload."
+ # Does this need to be logged?
+ logger_background_worker.info('No successful cracks for this task. Skipping upload.')
end
# remove task data tmp file
File.delete('control/tmp/agent_current_task.txt') if File.exist?('control/tmp/agent_current_task.txt')
- # change status to completed for jobtask
- # commenting out now that we are chunking
- # if @canceled
- # Api.post_jobtask_status(jdata['jobtask_id'], 'Canceled')
- # else
- # Api.post_jobtask_status(jdata['jobtask_id'], 'Completed')
- # end
-
# set taskqueue item to complete and remove from queue
Api.post_queue_status(jdata['id'], 'Completed')
end
end
end
end
+ logger_background_worker.debug('Background Worker Class() - has Completed')
end
-end
+end
\ No newline at end of file
diff --git a/jobs/clean_up.rb b/jobs/clean_up.rb
index a873e3a2..7f997c4c 100644
--- a/jobs/clean_up.rb
+++ b/jobs/clean_up.rb
@@ -3,12 +3,20 @@
require_relative '../helpers/status'
def cleanDir(path)
+ # Setup Logger
+ # Not a fan of this, not sure if i can just pass the logger_cleanup object instead?
+ logger_cleanup = Logger.new('logs/jobs/cleanup.log', 'daily')
+ if ENV['RACK_ENV'] == 'development'
+ logger_cleanup.level = Logger::DEBUG
+ else
+ logger_cleanup.level = Logger::INFO
+ end
+
+
@files = Dir.glob(File.join(path))
@files.each do |path_file|
- if (Time.now - File.ctime(path_file))/(24*3600) > 30 # Need to change to a user defined setting
- if ENV['RACK_ENV'] == 'development'
- puts 'File: ' + path_file.to_s + ' is greater than 30 days old. Deleting'
- end
+ if (Time.now - File.ctime(path_file)) / (24 * 3600) > 30 # TODO Need to change to a user defined setting
+ logger_cleanup.info('File: ' + path_file.to_s + ' is greater than 30 days old. Deleting')
File.delete(path_file)
end
end
@@ -17,10 +25,16 @@ def cleanDir(path)
module CleanUp
@queue = :management
def self.perform()
+ # Setup Logger
+ logger_cleanup = Logger.new('logs/jobs/cleanup.log', 'daily')
if ENV['RACK_ENV'] == 'development'
- p 'CleanUp Class - start'
+ logger_cleanup.level = Logger::DEBUG
+ else
+ logger_cleanup.level = Logger::INFO
end
+ logger_cleanup.debug('Cleanup Class() - has started')
+
# control/tmp/*
cleanDir('control/tmp/*')
@@ -31,7 +45,13 @@ def self.perform()
cleanDir('control/outfiles/left_*')
# control/hashes/hashfile_upload_*
- cleanDir('hashes/hashfile_upload_*')
+ cleanDir('control/hashes/hashfile_upload_*')
+
+ # control/logs/*.log
+ cleanDir('control/logs/*.log')
+
+ # control/logs/jobs/*.log
+ cleanDir('control/logs/jobs/*.log')
# TODO
# Maybe do a better way of validating we're not going to delete an actively used file?
@@ -46,8 +66,6 @@ def self.perform()
cleanDir('control/hashes/hashfile_*.txt')
end
- if ENV['RACK_ENV'] == 'development'
- p 'CleanUp Class - Done'
- end
+ logger_cleanup.debug('Cleanup Class() - has completed')
end
end
diff --git a/jobs/file_checksum.rb b/jobs/file_checksum.rb
deleted file mode 100644
index 200dea40..00000000
--- a/jobs/file_checksum.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-class FileChecksum
- @queue = :management
- def self.perform(type, id)
-
- if type == 'rules'
- rules_file = Rules.first(id: id)
- cmd = rules_file.path
- checksum = `sha256sum "#{cmd}"`
- rules_file.checksum = checksum
- rules_file.save
- elsif type == 'wordlist'
- wordlists = Wordlists.first(id: id)
- cmd = wordlists.path
- checksum = `sha256sum "#{cmd}"`
- wordlists.checksum = checksum
- wordlists.save
- end
- end
-end
diff --git a/jobs/init.rb b/jobs/init.rb
index d296f568..f7829f49 100644
--- a/jobs/init.rb
+++ b/jobs/init.rb
@@ -3,4 +3,4 @@
require_relative 'background_worker'
require_relative 'wordlistChecksum'
require_relative 'clean_up'
-require_relative 'ruleImporter'
+require_relative 'ruleImporter'
\ No newline at end of file
diff --git a/jobs/ruleImporter.rb b/jobs/ruleImporter.rb
index 32156547..206572b1 100644
--- a/jobs/ruleImporter.rb
+++ b/jobs/ruleImporter.rb
@@ -1,13 +1,17 @@
-require_relative 'file_checksum'
module RuleImporter
@queue = :management
def self.perform()
sleep(rand(10))
+ logger_ruleimporter = Logger.new('logs/jobs/ruleImporter.log', 'daily')
if ENV['RACK_ENV'] == 'development'
- puts 'Rule fil Importer Class'
+ logger_ruleimporter.level = Logger::DEBUG
+ else
+ logger_ruleimporter.level = Logger::INFO
end
+ logger_ruleimporter.debug('Rule Importer Class() - has started')
+
# Identify all rules in directory
@files = Dir.glob(File.join('control/rules/', '*.rule'))
@files.each do |path_file|
@@ -15,12 +19,11 @@ def self.perform()
unless rule_entry
# Get Name
name = path_file.split('/')[-1]
-
- puts 'Importing new Rule file "' + name + '" into HashView.'
+ logger_ruleimporter.info('Importing new Rule ""' + name + '"" into HashView.')
# Adding to DB
rule_file = Rules.new
- rule_file.lastupdated = Time.now()
+ rule_file.lastupdated = Time.now
rule_file.name = name
rule_file.path = path_file
rule_file.size = 0
@@ -33,16 +36,15 @@ def self.perform()
@files = Dir.glob(File.join('control/rules/', '*.rule'))
@files.each do |path_file|
rule_file = Rules.first(path: path_file)
- id = rule_file.id
if rule_file.size == '0'
- size = File.foreach(path_file).inject(0) { |c| c + 1}
+ size = File.foreach(path_file).inject(0) do |c|
+ c + 1
+ end
rule_file.size = size
rule_file.save
end
end
- if ENV['RACK_ENV'] == 'development'
- puts 'Rule file Importer Class() - done'
- end
+ logger_ruleimporter.debug('Rule Importer Class() - has completed')
end
end
diff --git a/jobs/wordlistChecksum.rb b/jobs/wordlistChecksum.rb
index 18ea2d60..966e4825 100644
--- a/jobs/wordlistChecksum.rb
+++ b/jobs/wordlistChecksum.rb
@@ -3,12 +3,21 @@
module WordlistChecksum
@queue = :management
def self.perform()
- puts '============== generating wordlist checksum ========================'
+ # Setup Logger
+ logger_wordlistchecksum = Logger.new('logs/jobs/wordlistchecksum.log', 'daily')
+ if ENV['RACK_ENV'] == 'development'
+ logger_wordlistchecksum.level = Logger::DEBUG
+ else
+ logger_wordlistchecksum.level = Logger::INFO
+ end
+
+ logger_wordlistchecksum.debug('Wordlist Checksum Class() - has started')
+
# Identify all wordlists without checksums
@wordlist = Wordlists.all(checksum: nil)
@wordlist.each do |wl|
# generate checksum
- puts 'generating checksum for: ' + wl.path.to_s
+ logger_wordlistchecksum.info('generating checksum for: ' + wl.path.to_s)
checksum = Digest::SHA2.hexdigest(File.read(wl.path))
# save checksum to database
@@ -16,5 +25,6 @@ def self.perform()
wl.save
end
+ logger_wordlistchecksum.debug('Wordlist Checksum Class() - has completed')
end
end
diff --git a/jobs/wordlistImporter.rb b/jobs/wordlistImporter.rb
index a350d614..8dc0be55 100644
--- a/jobs/wordlistImporter.rb
+++ b/jobs/wordlistImporter.rb
@@ -3,12 +3,18 @@ module WordlistImporter
def self.perform()
sleep(rand(10))
+ # Setup Logger
+ logger_wordlistimporter = Logger.new('logs/jobs/wordlistImporter.log', 'daily')
if ENV['RACK_ENV'] == 'development'
- puts 'Wordlist Importer Class'
+ logger_wordlistimporter.level = Logger::DEBUG
+ else
+ logger_wordlistimporter.level = Logger::INFO
end
+ logger_wordlistimporter.debug('Wordlist Importer Class() - has started')
+
# Identify all wordlists in directory
- @files = Dir.glob(File.join('control/wordlists/', "*"))
+ @files = Dir.glob(File.join('control/wordlists/', '*'))
@files.each do |path_file|
wordlist_entry = Wordlists.first(path: path_file)
unless wordlist_entry
@@ -17,11 +23,11 @@ def self.perform()
# Make sure we're not dealing with a tar, gz, tgz, etc. Not 100% accurate!
unless name.match(/\.tar|\.7z|\.gz|\.tgz|\.checksum/)
- puts 'Importing new wordslist "' + name + '" into HashView.'
+ logger_wordlistimporter.info('Importing new wordslist "' + name + '" into HashView.')
# Adding to DB
wordlist = Wordlists.new
- wordlist.lastupdated = Time.now()
+ wordlist.lastupdated = Time.now
wordlist.type = 'static'
wordlist.name = name
wordlist.path = path_file
@@ -32,15 +38,16 @@ def self.perform()
end
end
-
- @files = Dir.glob(File.join('control/wordlists/', "*"))
+ @files = Dir.glob(File.join('control/wordlists/', '*'))
@files.each do |path_file|
# Get Name
name = path_file.split('/')[-1]
unless name.match(/\.tar|\.7z|\.gz|\.tgz|\.checksum/)
wordlist = Wordlists.first(path: path_file)
if wordlist.size == '0'
- size = File.foreach(path_file).inject(0) { |c| c + 1 }
+ size = File.foreach(path_file).inject(0) do |c|
+ c + 1
+ end
wordlist.size = size
wordlist.save
end
@@ -51,8 +58,6 @@ def self.perform()
# this checksum is used to compare differences with agents
Resque.enqueue(WordlistChecksum)
- if ENV['RACK_ENV'] == 'development'
- puts 'Wordlist Importer Class() - done'
- end
+ logger_wordlistimporter.debug('Wordlist Importer Class() - has completed')
end
end
diff --git a/logs/.blank b/logs/.blank
new file mode 100644
index 00000000..e69de29b
diff --git a/logs/jobs/.blank b/logs/jobs/.blank
new file mode 100644
index 00000000..e69de29b
diff --git a/models/master.rb b/models/master.rb
index 7fbb2718..07e3c40b 100644
--- a/models/master.rb
+++ b/models/master.rb
@@ -1,21 +1,22 @@
require 'rubygems'
require 'data_mapper'
require 'bcrypt'
+require 'rotp'
# read config
options = YAML.load_file('config/database.yml')
# there has to be a better way to handle this shit
if ENV['RACK_ENV'] == 'test'
- DataMapper::Logger.new($stdout, :debug)
+ # DataMapper::Logger.new($stdout, :debug)
DataMapper.setup(:default, options['test'])
elsif ENV['RACK_ENV'] == 'development'
- DataMapper::Logger.new($stdout, :debug)
+ # DataMapper::Logger.new($stdout, :debug)
DataMapper.setup(:default, options['development'])
elsif ENV['RACK_ENV'] == ('production' || 'default')
DataMapper.setup(:default, options['production'])
else
- puts 'ERROR: You must define an evironment. ex: RACK_ENV=production'
+ puts 'ERROR: You must define an environment. ex: RACK_ENV=production'
exit
end
@@ -30,6 +31,8 @@ class User
property :created_at, DateTime, default: DateTime.now
property :phone, String, required: false
property :email, String, required: false
+ property :mfa, Boolean
+ property :auth_secret, String
attr_accessor :password
validates_presence_of :username
@@ -45,7 +48,9 @@ def self.encrypt(pass)
def self.authenticate(username, pass)
user = User.first(username: username)
- if user
+ if user.mfa
+ return user.username if pass == ROTP::TOTP.new(user.auth_secret).now.to_s
+ elsif user
return user.username if BCrypt::Password.new(user.hashed_password) == pass
end
end
@@ -69,7 +74,7 @@ def self.delete_test_user(id)
user.destroy
end
- def self.delete_all_users()
+ def self.delete_all_users
@users = User.all
@users.destroy
end
@@ -117,6 +122,7 @@ class Customers
property :description, String, length: 500
end
+# Agents table use to record status
class Agents
include DataMapper::Resource
property :id, Serial
@@ -144,10 +150,9 @@ class Jobs
# status options should be "Running", "Paused", "Completed", "Queued", "Canceled", "Ready"
property :status, String, length: 100
property :queued_at, DateTime
- property :targettype, String, length: 2000
+ property :started_at, DateTime
+ property :ended_at, DateTime
property :hashfile_id, Integer
- property :policy_min_pass_length, Integer
- property :policy_complexity_default, Boolean
property :customer_id, Integer
property :notify_completed, Boolean
end
@@ -215,7 +220,6 @@ class Settings
property :smtp_pass, String
property :smtp_use_tls, Boolean
property :smtp_auth_type, String # Options are plain, login, cram_md5, none
- property :clientmode, Boolean
property :ui_themes, String, default: 'Light', required: true
property :version, String, length: 5
property :chunk_size, Integer, max: 9999999999999999999, default: 500000
@@ -249,7 +253,6 @@ class HubSettings
property :balance, Integer, default: 0
end
-
# Wordlist Class
class Wordlists
include DataMapper::Resource
@@ -261,7 +264,6 @@ class Wordlists
property :path, String, length: 2000
property :size, String, length: 100
property :checksum, String, length: 64
-
end
# Rules Class
@@ -274,7 +276,6 @@ class Rules
property :path, String, length: 2000
property :size, String, length: 100
property :checksum, String, length: 64
-
end
# Hashfile Class
@@ -299,7 +300,7 @@ class Taskqueues
# status options should be "Running", "Completed", "Queued", "Canceled", "Paused"
property :queued_at, DateTime
property :status, String, length: 100
- property :agent_id, String, length: 2000
+ property :agent_id, Integer
property :command, String, length: 4000
end
diff --git a/routes/accounts.rb b/routes/accounts.rb
index 9e371ecf..97af4731 100644
--- a/routes/accounts.rb
+++ b/routes/accounts.rb
@@ -16,12 +16,12 @@
redirect to('/accounts/create')
end
- if params[:password].nil? || params[:password].empty?
+ if (params[:password].nil? || params[:password].empty?) && !params[:mfa]
flash[:error] = 'You must have a password.'
redirect to('/accounts/create')
end
- if params[:confirm].nil? || params[:confirm].empty?
+ if params[:confirm].nil? || params[:confirm].empty? && !params[:mfa]
flash[:error] = 'You must have a password.'
redirect to('/accounts/create')
end
@@ -37,6 +37,13 @@
new_user.username = params[:username]
new_user.password = params[:password]
new_user.email = params[:email] unless params[:email].nil? || params[:email].empty?
+ if params[:mfa]
+ new_user.mfa = 't'
+ new_user.auth_secret = ROTP::Base32.random_base32
+ else
+ new_user.mfa = 'f'
+ new_user.auth_secret = ''
+ end
new_user.admin = 't'
new_user.save
end
@@ -51,7 +58,8 @@
varWash(params)
@user = User.first(id: params[:account_id])
-
+ data = Rack::Utils.escape(ROTP::TOTP.new(@user.auth_secret).provisioning_uri(@user.username))
+ @otp = "https://chart.googleapis.​com/chart?chs=200x200&chld=M|0&cht=qr&chl=#{data}"
haml :account_edit
end
@@ -77,9 +85,18 @@
user.username = params[:username]
user.password = params[:password] unless params[:password].nil? || params[:password].empty?
user.email = params[:email] unless params[:email].nil? || params[:email].empty?
+ if params[:mfa] && user.auth_secret == ''
+ user.mfa = 't'
+ user.auth_secret = ROTP::Base32.random_base32
+ elsif params[:mfa]
+ user.mfa='t'
+ else
+ user.mfa = 'f'
+ user.auth_secret = ''
+ end
user.save
-
- flash[:success] = 'Account successfuly updated.'
+
+ flash[:success] = 'Account successfully updated.'
redirect to('/accounts/list')
end
diff --git a/routes/agents.rb b/routes/agents.rb
index c26c8614..d3024778 100644
--- a/routes/agents.rb
+++ b/routes/agents.rb
@@ -9,36 +9,36 @@
end
get '/agents/:id/edit' do
- @agent = Agents.first(:id => params[:id])
+ @agent = Agents.first(id: params[:id])
haml :agent_edit
end
post '/agents/:id/edit' do
- agent = Agents.first(:id => params[:id])
- agent.name = params["name"]
+ agent = Agents.first(id: params[:id])
+ agent.name = params['name']
agent.save
redirect to('/agents/list')
end
get '/agents/:id/delete' do
- agent = Agents.first(:id => params[:id])
+ agent = Agents.first(id: params[:id])
agent.destroy
redirect to('/agents/list')
end
get '/agents/:id/authorize' do
- agent = Agents.first(:id => params[:id])
- agent.status = "Authorized"
+ agent = Agents.first(id: params[:id])
+ agent.status = 'Authorized'
agent.save
redirect to('/agents/list')
end
get '/agents/:id/deauthorize' do
- agent = Agents.first(:id => params[:id])
- if agent.status == "Working"
+ agent = Agents.first(id: params[:id])
+ if agent.status == 'Working'
flash[:warning] = 'Agent was working. The active task was not stopped and you will not receive the results.'
end
- agent.status = "Pending"
+ agent.status = 'Pending'
agent.save
redirect to('/agents/list')
end
\ No newline at end of file
diff --git a/routes/analytics.rb b/routes/analytics.rb
index a842b441..b59da812 100644
--- a/routes/analytics.rb
+++ b/routes/analytics.rb
@@ -229,7 +229,7 @@
# Used for Total Unique Users and originalhashes Tables: Total
@total_unique_users_count = repository(:default).adapter.select('SELECT COUNT(DISTINCT(username)) FROM hashfilehashes')[0].to_s
@total_unique_originalhash_count = repository(:default).adapter.select('SELECT COUNT(DISTINCT(originalhash)) FROM hashes')[0].to_s
-
+
# Used for Total Run Time:
@total_run_time = Hashfiles.sum(:total_run_time)
@@ -289,11 +289,7 @@
unless crack.nil?
unless crack.length == 0
len = crack.length
- if @passwords[len].nil?
- @passwords[len] = 1
- else
- @passwords[len] = @passwords[len].to_i + 1
- end
+ @passwords[len].nil? ? @passwords[len] = 1 : @passwords[len] = @passwords[len].to_i + 1
end
end
end
@@ -308,13 +304,13 @@
return @counts.to_json
end
-
+
# callback for d3 graph displaying top 10 passwords
get '/analytics/graph2' do
varWash(params)
-
+
# This could probably be replaced with: SELECT COUNT(a.hash_id) AS frq, h.plaintext FROM hashfilehashes a LEFT JOIN hashes h ON h.id = a.hash_id WHERE h.cracked = '1' GROUP BY a.hash_id ORDER BY frq DESC LIMIT 10;
-
+
plaintext = []
if params[:customer_id] && !params[:customer_id].empty?
if params[:hashfile_id] && !params[:hashfile_id].empty?
@@ -325,13 +321,13 @@
else
@cracked_results = repository(:default).adapter.select('SELECT h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE h.cracked = 1')
end
-
+
@cracked_results.each do |crack|
unless crack.nil?
plaintext << crack unless crack.empty?
end
end
-
+
@toppasswords = []
@top10passwords = {}
# get top 10 passwords
@@ -342,7 +338,7 @@
@top10passwords[pass] += 1
end
end
-
+
# sort and convert to array of json objects for d3
@top10passwords = @top10passwords.sort_by { |_key, value| value }.reverse.to_h
# we only need top 10
@@ -351,10 +347,10 @@
@top10passwords.each do |key, value|
@toppasswords << { password: key, count: value }
end
-
+
return @toppasswords.to_json
end
-
+
# callback for d3 graph displaying top 10 base words
get '/analytics/graph3' do
varWash(params)
@@ -374,13 +370,13 @@
plaintext << crack unless crack.empty?
end
end
-
+
@topbasewords = []
@top10basewords = {}
# get top 10 basewords
plaintext.each do |pass|
word_just_alpha = pass.gsub(/^[^a-z]*/i, '').gsub(/[^a-z]*$/i, '')
- unless word_just_alpha.nil? or word_just_alpha.empty?
+ unless word_just_alpha.nil? || word_just_alpha.empty?
if @top10basewords[word_just_alpha].nil?
@top10basewords[word_just_alpha] = 1
else
@@ -388,7 +384,7 @@
end
end
end
-
+
# sort and convert to array of json objects for d3
@top10basewords = @top10basewords.sort_by { |_key, value| value }.reverse.to_h
# we only need top 10
@@ -397,6 +393,6 @@
@top10basewords.each do |key, value|
@topbasewords << { password: key, count: value }
end
-
+
return @topbasewords.to_json
end
diff --git a/routes/api.rb b/routes/api.rb
index 8b8dd917..f05d0184 100644
--- a/routes/api.rb
+++ b/routes/api.rb
@@ -2,9 +2,9 @@
get '/v1/notauthorized' do
{
- status: 200,
- type: 'Error',
- msg: 'Your agent is not authorized to work with this cluster.'
+ status: 200,
+ type: 'Error',
+ msg: 'Your agent is not authorized to work with this cluster.'
}.to_json
end
@@ -20,9 +20,9 @@
else
status 200
{
- status: 200,
- type: 'Error',
- msg: 'There are no items on the queue to process'
+ status: 200,
+ type: 'Error',
+ msg: 'There are no items on the queue to process'
}.to_json
end
end
@@ -46,17 +46,14 @@
else
status 200
{
- status: 200,
- type: 'Error',
- msg: 'Missing UUID'
+ status: 200,
+ type: 'Error',
+ msg: 'Missing UUID'
}.to_json
end
# check to see if this agent is suppose to be working on something
- if @assigned_task
- puts @assigned_task.to_json
- return @assigned_task.to_json
- end
+ return @assigned_task.to_json if @assigned_task
end
# remove item from queue
@@ -76,7 +73,7 @@
jdata = JSON.parse(request.body.read)
agent = Agents.first(uuid: jdata['agent_uuid'])
- puts "[+] updating taskqueue id: #{params[:taskqueue_id]} to status: #{jdata['status']}"
+
updateTaskqueueStatus(params[:taskqueue_id], jdata['status'], agent.id)
end
@@ -86,9 +83,7 @@
redirect to('/v1/notauthorized') unless agentAuthorized(request.cookies['agent_uuid'])
jdata = JSON.parse(request.body.read)
- puts jdata
- puts "======================================="
- puts "[+] updating jobtask id: #{params['jobtask_id']} to status: #{jdata['status']}"
+
updateJobTaskStatus(jdata['jobtask_id'], jdata['status'])
end
@@ -142,8 +137,6 @@
# Execute our compression
`#{cmd}`
# Serve File
- #send_file wordlist.path, :type => 'application/octet-stream', :filename => "#{wordlist.path.split('/')[-1]}.gz"
- #send_file wordlist.path, :type => 'application/octet-stream', :filename => "control/tmp/#{wordlist_orig}.gz"
send_file "control/tmp/#{wordlist_orig}.gz", :type => 'application/octet-stream', :filename => "#{wordlist_orig}.gz"
end
@@ -169,9 +162,9 @@
redirect to('/v1/notauthorized') unless agentAuthorized(request.cookies['agent_uuid'])
updateSmartWordlist
data = {
- status: 200,
- type: 'message',
- msg: 'OK'
+ status: 200,
+ type: 'message',
+ msg: 'OK'
}
return data.to_json
end
@@ -202,7 +195,6 @@
# is agent authorized
redirect to('/v1/notauthorized') unless agentAuthorized(request.cookies['agent_uuid'])
- puts '===== creating hash_file ======='
jobtask_id = params[:jobtask_id]
hashfile_id = params[:hashfile_id]
@@ -233,8 +225,6 @@
f.close
end
- puts '===== Hash_File Created ======'
-
send_file hash_file
end
@@ -245,7 +235,7 @@
redirect to('/v1/notauthorized') unless agentAuthorized(request.cookies['agent_uuid'])
tmpfile = "control/tmp/#{rand.to_s[2..2048]}.txt"
- puts "[+] Agent uploaded crack file. Saving to: #{tmpfile}"
+ # puts "[+] Agent uploaded crack file. Saving to: #{tmpfile}"
File.open(tmpfile, 'wb') do |f|
f.write(params[:file][:tempfile].read)
end
@@ -259,7 +249,6 @@
# is agent authorized
redirect to('/v1/notauthorized') unless agentAuthorized(request.cookies['agent_uuid'])
- puts 'parsing uploaded hcoutput hash'
return request.body.read
end
@@ -279,7 +268,7 @@
payload = JSON.parse(request.body.read)
# get agent data from db if available
- @agent = Agents.first(:uuid => params[:uuid])
+ @agent = Agents.first(uuid: params[:uuid])
if !@agent.nil?
if @agent.status == 'Authorized'
# if agent is set to authorized, continue to authorization process
@@ -314,9 +303,14 @@
# update db with the agents hashcat status
if payload['hc_status']
- puts payload['hc_status']
+ #puts payload['hc_status']
@agent.status = payload['agent_status']
@agent.hc_status = payload['hc_status'].to_json
+ payload['hc_status'].each do |item|
+ if item.to_s =~ /Speed Dev #/
+ @agent.benchmark = item[1].split(' ')[0].to_s + ' ' + item[1].split(' ')[1].to_s
+ end
+ end
@agent.save
end
@@ -331,31 +325,12 @@
@agent.status = payload['agent_status']
@agent.save
{
- status: 200,
- type: 'message',
- msg: 'OK'
+ status: 200,
+ type: 'message',
+ msg: 'OK'
}.to_json
end
- # # are agent and server in sync
- # if agenttask.to_i == taskqueue.id
- # # update heartbeat and save hc_output for ui
- # @agent.heartbeat = Time.now
- # @agent.save
- # {
- # status: 200,
- # type: 'message',
- # msg: 'OK'
- # }.to_json
- # else
- # # server and agent are out of sync, tell agent to stop working
- # {
- # status: 200,
- # type: 'message',
- # msg: 'Canceled'
- # }.to_json
- # end
-
elsif payload['agent_status'] == 'Idle'
# assign work to agent
@@ -380,7 +355,6 @@
}.to_json
else
# update agent heartbeat but do nothing for now
- p '########### I have nothing for you to do now ###########'
@agent.heartbeat = Time.now
@agent.status = payload['agent_status']
@agent.hc_status = ''
@@ -413,13 +387,13 @@
if params[:uuid].nil?
status 200
{
- status: 200,
- type: 'Error',
- msg: 'Missing UUID'
+ status: 200,
+ type: 'Error',
+ msg: 'Missing UUID'
}.to_json
else
#TODO SECURITY - make sure this param is a formated as a valid uuid
- agent = Agents.first(:uuid => params[:uuid])
+ agent = Agents.first(uuid: params[:uuid])
end
if !agent.nil?
@@ -457,9 +431,9 @@
redirect to('/v1/notauthorized') unless agentAuthorized(request.cookies['agent_uuid'])
payload = JSON.parse(request.body.read)
- puts payload
+ #puts payload
- agent = Agents.first(:uuid => params[:uuid])
+ agent = Agents.first(uuid: params[:uuid])
agent.cpu_count = payload['cpu_count'].to_i
agent.gpu_count = payload['gpu_count'].to_i
agent.benchmark = payload['benchmark'].to_s
diff --git a/routes/customers.rb b/routes/customers.rb
index 0e621d63..fb6dbbf7 100644
--- a/routes/customers.rb
+++ b/routes/customers.rb
@@ -249,5 +249,4 @@
url += "?job_id=#{params[:job_id]}"
url += '&edit=1' if params[:edit]
redirect to(url)
-
end
\ No newline at end of file
diff --git a/routes/downloads.rb b/routes/downloads.rb
index 844a5b07..6fa995e7 100644
--- a/routes/downloads.rb
+++ b/routes/downloads.rb
@@ -1,105 +1,111 @@
# encoding: utf-8
get '/download' do
varWash(params)
-
- if params[:customer_id] && !params[:customer_id].empty?
- if params[:hashfile_id] && !params[:hashfile_id].nil?
- # Until we can figure out JOIN statments, we're going to have to hack it
+ if params[:graph] && !params[:graph].empty?
+ # What kind of graph data are we dealing with here
+ if params[:graph] == '1' # Total Hashes Cracked
+ # Do Something
+ elsif params[:graph] == '2' # Composition Breakdown
+ # Do Something
+ elsif params[:graph] == '3' # Analysis Detail
@filecontents = Set.new
- Hashfilehashes.all(fields: [:id], hashfile_id: params[:hashfile_id]).each do |entry|
- if params[:type] == 'cracked' and Hashes.first(fields: [:cracked], id: entry.hash_id).cracked
- if entry.username.nil? # no username
- line = ''
+ file_name = 'error.txt'
+ if params[:customer_id] && !params[:customer_id].empty?
+ if params[:hashfile_id] && !params[:hashfile_id].nil?
+ # Customer and Hashfile
+ if params[:type] == 'cracked'
+ @results = repository(:default).adapter.select('SELECT a.username, h.originalhash, h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id LEFT JOIN hashfiles f on a.hashfile_id = f.id WHERE (f.customer_id = ? AND a.hashfile_id = ? and h.cracked = 1)', params[:customer_id],params[:hashfile_id])
+ file_name = "found_#{params[:customer_id]}_#{params[:hashfile_id]}.txt"
+ elsif params[:type] == 'uncracked'
+ @results = repository(:default).adapter.select('SELECT a.username, h.originalhash FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id LEFT JOIN hashfiles f on a.hashfile_id = f.id WHERE (f.customer_id = ? AND a.hashfile_id = ? and h.cracked = 0)', params[:customer_id],params[:hashfile_id])
+ file_name = "left_#{params[:customer_id]}_#{params[:hashfile_id]}.txt"
else
- line = entry.username + ':'
+ # Do Something
+ file_name = 'error.txt'
end
- line = line + Hashes.first(fields: [:originalhash], id: entry.hash_id).originalhash
- line = line + ':' + Hashes.first(fields: [:plaintext], id: entry.hash_id, cracked: 1).plaintext
- @filecontents.add(line)
- elsif params[:type] == 'uncracked' and not Hashes.first(fields: [:cracked], id: entry.hash_id).cracked
- if entry.username.nil? # no username
- line = ''
+ else
+ # Just Customer
+ if params[:type] == 'cracked'
+ @results = repository(:default).adapter.select('SELECT a.username, h.originalhash, h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id LEFT JOIN hashfiles f on a.hashfile_id = f.id WHERE (f.customer_id = ? and h.cracked = 1)', params[:customer_id])
+ file_name = "found_#{params[:customer_id]}.txt"
+ elsif params[:type] == 'uncracked'
+ @results = repository(:default).adapter.select('SELECT a.username, h.originalhash FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id LEFT JOIN hashfiles f on a.hashfile_id = f.id WHERE (f.customer_id = ? and h.cracked = 0)', params[:customer_id])
+ file_name = "left_#{params[:customer_id]}.txt"
else
- line = entry.username + ':'
+ # Do Something
+ file_name = 'error.txt'
end
- line = line + Hashes.first(fields: [:originalhash], id: entry.hash_id).originalhash
- @filecontents.add(line)
+ end
+ else
+ # All
+ if params[:type] == 'cracked'
+ @results = repository(:default).adapter.select('SELECT a.username, h.originalhash, h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id LEFT JOIN hashfiles f on a.hashfile_id = f.id WHERE (h.cracked = 1)')
+ file_name = 'found_all.txt'
+ elsif params[:type] == 'uncracked'
+ @results = repository(:default).adapter.select('SELECT a.username, h.originalhash FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id LEFT JOIN hashfiles f on a.hashfile_id = f.id WHERE (h.cracked = 0)')
+ file_name = 'left_all.txt'
+ else
+ # Do Something
+ file_name = 'error.txt'
end
end
- else
- @filecontents = Set.new
- @hashfiles_ids = Hashfiles.all(fields: [:id], customer_id: params[:customer_id]).each do |hashfile|
- Hashfilehashes.all(fields: [:id], hashfile_id: hashfile.id).each do |entry|
- if params[:type] == 'cracked' and Hashes.first(fields: [:cracked], id: entry.hash_id).cracked
- if entry.username.nil? # no username
- line = ''
- else
- line = entry.username + ':'
- end
- line = line + Hashes.first(fields: [:originalhash], id: entry.hash_id).originalhash
- line = line + ':' + Hashes.first(fields: [:plaintext], id: entry.hash_id, cracked: 1).plaintext
- @filecontents.add(line)
- elsif params[:type] == 'uncracked' and not Hashes.first(fields: [:cracked], id: entry.hash_id).cracked
- if entry.username.nil? # no username
- line = ''
- else
- line = entry.username + ':'
- end
- line = line + Hashes.first(fields: [:originalhash], id: entry.hash_id).originalhash
- @filecontents.add(line)
- end
- end
+
+ @results.each do |entry|
+ entry.username.nil? ? line = '' : line = entry.username.to_s + ':'
+ line += entry.originalhash.to_s
+ line += ':' + entry.plaintext.to_s if params[:type] == 'cracked'
+ @filecontents.add(line)
end
- end
- else
- @filecontents = Set.new
- @hashfiles_ids = Hashfiles.all(fields: [:id]).each do |hashfile|
- Hashfilehashes.all(fields: [:id], hashfile_id: hashfile.id).each do |entry|
- if params[:type] == 'cracked' and Hashes.first(fields: [:cracked], id: entry.hash_id).cracked
- if entry.username.nil? # no username
- line = ''
- else
- line = entry.username + ':'
- end
- line = line + Hashes.first(fields: [:originalhash], id: entry.hash_id).originalhash
- line = line + ':' + Hashes.first(fields: [:plaintext], id: entry.hash_id, cracked: 1).plaintext
- @filecontents.add(line)
- elsif params[:type] == 'uncracked' and not Hashes.first(fields: [:cracked], id: entry.hash_id).cracked
- if entry.username.nil? # no username
- line = ''
- else
- line = entry.username + ':'
+
+ file_name = 'control/tmp/' + file_name
+
+ File.open(file_name, 'w') do |f|
+ @filecontents.each do |entry|
+ f.puts entry
+ end
+ end
+
+ send_file file_name, filename: file_name, type: 'Application/octet-stream'
+ elsif params[:graph] == '4' # Password Count by Length
+ # Do Something
+ elsif params[:graph] == '5' # Top 10 Passwords
+ # Do Something
+ elsif params[:graph] == '6' # Accounts With Weak Passwords
+ file_name = 'error.txt'
+ if params[:customer_id] && !params[:customer_id].empty?
+ if params[:hashfile_id] && !params[:hashfile_id].nil?
+ @complexity_hashes = repository(:default).adapter.select('SELECT a.username, h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (a.hashfile_id = ? AND h.cracked = 1)', params[:hashfile_id])
+ file_name = "Weak_Accounts_#{params[:customer_id]}_#{params[:hashfile_id]}.csv"
+ else
+ @complexity_hashes = repository(:default).adapter.select('SELECT a.username, h.plaintext FROM hashes h LEFT JOIN hashfilehashes a on h.id = a.hash_id LEFT JOIN hashfiles f on a.hashfile_id = f.id WHERE (f.customer_id = ? AND h.cracked = 1)', params[:customer_id])
+ file_name = "Weak_Accounts_#{params[:customer_id]}.csv"
+ end
+ else
+ @complexity_hashes = repository(:default).adapter.select('SELECT a.username, h.plaintext FROM hashes h LEFT JOIN hashfilehashes a on h.id = a.hash_id LEFT JOIN hashfiles f on a.hashfile_id = f.id WHERE (h.cracked = 1)')
+ file_name = "Weak_Accounts_all.csv"
+ end
+
+ file_name = 'control/tmp/' + file_name
+
+ File.open(file_name, 'w') do |f|
+ line = 'username,password'
+ f.puts line
+ @complexity_hashes.each do |entry|
+ unless entry.plaintext.to_s =~ /^(?:(?=.*[a-z])(?:(?=.*[A-Z])(?=.*[\d\W])|(?=.*\W)(?=.*\d))|(?=.*\W)(?=.*[A-Z])(?=.*\d)).{8,}$/
+ line = entry.username.to_s + ',' + entry.plaintext.to_s
+ f.puts line
end
- line = line + Hashes.first(fields: [:originalhash], id: entry.hash_id).originalhash
- @filecontents.add(line)
end
end
- end
- end
- # Write temp output file
- if params[:customer_id] && !params[:customer_id].empty?
- if params[:hashfile_id] && !params[:hashfile_id].nil?
- file_name = "found_#{params[:customer_id]}_#{params[:hashfile_id]}.txt" if params[:type] == 'cracked'
- file_name = "left_#{params[:customer_id]}_#{params[:hashfile_id]}.txt" if params[:type] == 'uncracked'
- else
- file_name = "found_#{params[:customer_id]}.txt" if params[:type] == 'cracked'
- file_name = "left_#{params[:customer_id]}.txt" if params[:type] == 'uncracked'
- end
- else
- file_name = 'found_all.txt' if params[:type] == 'cracked'
- file_name = 'left_all.txt' if params[:type] == 'uncracked'
- end
-
- file_name = 'control/outfiles/' + file_name
+ send_file file_name, filename: file_name, type: 'Application/octet-stream'
- File.open(file_name, 'w') do |f|
- @filecontents.each do |entry|
- f.puts entry
+ elsif params[:graph] == '7' # Top 20 Password/Hashes Shared by Users
+ # Do something
+ else
+ # DO Something
end
end
-
- send_file file_name, filename: file_name, type: 'Application/octet-stream'
end
diff --git a/routes/hashfiles.rb b/routes/hashfiles.rb
index 93233348..95993bf7 100644
--- a/routes/hashfiles.rb
+++ b/routes/hashfiles.rb
@@ -6,8 +6,6 @@
@cracked_status = {}
@local_cracked_cnt = {}
@local_uncracked_cnt = {}
-# @hub_download_cnt = {}
-# @hub_upload_cnt = {}
@hashfiles.each do |hashfile|
hashfile_cracked_count = repository(:default).adapter.select('SELECT COUNT(h.originalhash) FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (a.hashfile_id = ? AND h.cracked = 1)', hashfile.id)[0].to_s
@@ -15,73 +13,6 @@
@local_cracked_cnt[hashfile.id] = hashfile_cracked_count.to_s
@local_uncracked_cnt[hashfile.id] = hashfile_total_count.to_i - hashfile_cracked_count.to_i
@cracked_status[hashfile.id] = hashfile_cracked_count.to_s + '/' + hashfile_total_count.to_s
- # hub upload cnt
-# if @hub_settings.status == 'registered'
-# @hashfile_hashes = Hashfilehashes.all(hashfile_id: hashfile.id)
-# upload_cnt = 0
-# download_cnt = 0
-# @hash_array = []
-# @hashfile_hashes.each do |entry|
-# # p 'HASH ID: ' + entry.hash_id.to_s
-# # Build list of locally cracked hashes per hashfile
-# local_cracked = Hashes.all(id: entry.hash_id, cracked: '1')
-# unless local_cracked.nil?
-# local_cracked.each do |hash|
-# element = {}
-# element['ciphertext'] = hash.originalhash
-# element['hashtype'] = hash.hashtype.to_s
-# # p 'ELEMENT: ' + element.to_s
-# @hash_array.push(element)
-# end
-# end
-# end
-# # p 'HASH_ARRAY: ' + @hash_array.to_s
-# # Submit query and record how many the hub doesnt have
-# hub_response = Hub.hashSearch(@hash_array)
-# hub_response = JSON.parse(hub_response)
-# if hub_response['status'] == '200'
-# @hub_hash_results = hub_response['hashes']
-# @hub_hash_results.each do |entry|
-# if entry['cracked'] == '0'
-# upload_cnt += 1
-# end
-# end
-# end
-
-# @hash_array = []
-# @hashfile_hashes.each do |entry|
-# # Build list of locally uncracked per hashfile
-# local_uncracked = Hashes.all(id: entry.hash_id, cracked: '0')
-# unless local_uncracked.nil? || local_uncracked.empty?
-# local_uncracked.each do |hash|
-# element = {}
-# element['ciphertext'] = hash.originalhash
-# element['hashtype'] = hash.hashtype.to_s
-# @hash_array.push(element)
-# end
-# end
-# end
-
-# # Submit query and record how many the hub doesn't have
-# hub_response = Hub.hashSearch(@hash_array)
-# hub_response = JSON.parse(hub_response)
-# if hub_response['status'] == '200'
-# @hub_hash_results = hub_response['hashes']
-# @hub_hash_results.each do |entry|
-# if entry['cracked'] == '1'
-# # p 'ENTRY' + entry.to_s
-# download_cnt += 1
-# end
-# end
-# end
-# else
-# upload_cnt = 0
-# download_cnt = 0
-# end
-
- # hub download cnt
-# @hub_download_cnt[hashfile.id] = download_cnt
-# @hub_upload_cnt[hashfile.id] = upload_cnt
end
haml :hashfile_list
@@ -96,8 +27,7 @@
@hashfile = Hashfiles.first(id: params[:hashfile_id])
@hashfile.destroy unless @hashfile.nil?
- flash[:success] = 'Successfuly removed hashfile.'
+ flash[:success] = 'Successfully removed hashfile.'
redirect to('/hashfiles/list')
-end
-
+end
\ No newline at end of file
diff --git a/routes/hub.rb b/routes/hub.rb
index e03796f0..4eadf993 100644
--- a/routes/hub.rb
+++ b/routes/hub.rb
@@ -10,8 +10,8 @@ class Hub
# Provision new config if none exists.
unless File.exist?('config/hub_config.json')
hub_config = {
- :host => 'hub.hashview.io',
- :port => '443',
+ host: 'hub.hashview.io',
+ port: '443'
}
File.open('config/hub_config.json', 'w') do |f|
f.write(JSON.pretty_generate(hub_config))
@@ -45,13 +45,12 @@ def self.get(url)
hub_settings = HubSettings.first
response = RestClient::Request.execute(
- :method => :get,
- :url => url,
- :cookies => {:uuid => hub_settings.uuid, :auth_key => hub_settings.auth_key},
- :timeout => nil,
- :open_timeout => nil,
- #:verify_ssl => false
- :verify_ssl => true
+ method: :get,
+ url: url,
+ cookies: {uuid: hub_settings.uuid, auth_key: hub_settings.auth_key},
+ timeout: nil,
+ open_timeout: nil,
+ verify_ssl: true
)
p 'response: ' + response.body.to_s
return response.body
@@ -68,15 +67,14 @@ def self.post(url, payload)
hub_settings = HubSettings.first
p 'cookie: ' + hub_settings.uuid.to_s + ' ' + hub_settings.auth_key.to_s
response = RestClient::Request.execute(
- :method => :post,
- :url => url,
- :payload => payload.to_json,
- :headers => {:accept => :json},
- :cookies => {:uuid => hub_settings.uuid, :auth_key => hub_settings.auth_key},
- :timeout => nil,
- :open_timeout => nil,
- #:verify_ssl => false #TODO VALIDATE
- :verify_ssl => true
+ method: :post,
+ url: url,
+ payload: payload.to_json,
+ headers: {accept: :json},
+ cookies: {uuid: hub_settings.uuid, auth_key: hub_settings.auth_key},
+ timeout: nil,
+ open_timeout: nil,
+ verify_ssl: true
)
p 'response: ' + response.body.to_s
return response.body
@@ -258,7 +256,7 @@ def self.statusAuth()
entry.plaintext = element['plaintext']
entry.cracked = '1'
entry.save
- hub_count = hub_count + 1
+ hub_count += 1
end
end
@@ -337,7 +335,7 @@ def self.statusAuth()
if hub_response['status'] == '200'
@hashes = hub_response['hashes']
@hashes.each do |element|
- cnt = cnt + 1
+ cnt += 1
# Add to local db
entry = Hashes.first(hashtype: element['hashtype'], originalhash: element['ciphertext'])
entry.lastupdated = Time.now
diff --git a/routes/jobs.rb b/routes/jobs.rb
index 2fea06bc..224beadd 100644
--- a/routes/jobs.rb
+++ b/routes/jobs.rb
@@ -100,7 +100,7 @@
flash[:error] = 'Customer ' + params[:name] + ' already exists.'
redirect to('/jobs/create')
end
-
+
customer = Customers.new
customer.name = params[:cust_name]
customer.description = params[:cust_desc]
@@ -136,7 +136,7 @@
@hashfiles = Hashfiles.all(customer_id: params[:customer_id])
@customer = Customers.first(id: params[:customer_id])
- @cracked_status = Hash.new
+ @cracked_status = {}
@hashfiles.each do |hashfile|
hashfile_cracked_count = repository(:default).adapter.select('SELECT COUNT(h.originalhash) FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (a.hashfile_id = ? AND h.cracked = 1)', hashfile.id)[0].to_s
hashfile_total_count = repository(:default).adapter.select('SELECT COUNT(h.originalhash) FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE a.hashfile_id = ?', hashfile.id)[0].to_s
@@ -165,7 +165,7 @@
end
url = "/jobs/assign_tasks?job_id=#{params[:job_id]}&customer_id=#{params[:customer_id]}&hashid=#{params[:hash_file]}"
- url = url + '&edit=1' if params[:edit]
+ url += '&edit=1' if params[:edit]
redirect to(url)
end
@@ -228,9 +228,9 @@
@temp_jobtasks.each_with_index do |task_id, index|
if @temp_jobtasks[index] == params[:task_id].to_i
- @new_jobtasks << @temp_jobtasks[index+1]
+ @new_jobtasks << @temp_jobtasks[index + 1]
@new_jobtasks << params[:task_id].to_i
- @temp_jobtasks.delete_at(index+1)
+ @temp_jobtasks.delete_at(index + 1)
else
@new_jobtasks << @temp_jobtasks[index].to_i
end
@@ -289,17 +289,12 @@
job.status = 'Ready'
job.save
- if params[:edit].to_s == '1'
- flash[:success] = 'Job updated.'
- else
- flash[:success] = 'Job created.'
- end
+ params[:edit].to_s == '1' ? flash[:success] = 'Job updated.' : flash[:success] = 'Job created.'
redirect to('/jobs/list')
end
post '/jobs/complete' do
-
if !params[:tasks] || params[:tasks].nil?
if !params[:edit] || params[:edit].nil?
flash[:error] = 'You must assign at least one task'
@@ -317,8 +312,7 @@
@tasks = Tasks.all
# prevent adding duplicate tasks to a job
- #count = Hash.new 0
- #params[:tasks] = params[:task].uniq
+
puts params
if params[:tasks]
# make sure the task that the user is adding is not already assigned to the job
@@ -425,6 +419,7 @@
@jobtasks = Jobtasks.all(job_id: params[:id])
@job.status = 'Canceled'
+ @job.ended_at = Time.now
@job.save
@jobtasks.each do |task|
@@ -558,7 +553,6 @@
results_entry['id'] = hash.id
# TODO
# Adding usernames to this result would be great
- #results_entry['username'] = entry.username
results_entry['ciphertext'] = element['ciphertext']
results_entry['hub_hash_id'] = element['hash_id']
results_entry['hashtype'] = element['hashtype']
diff --git a/routes/login.rb b/routes/login.rb
index 1d69d3c2..8c38294b 100644
--- a/routes/login.rb
+++ b/routes/login.rb
@@ -8,7 +8,7 @@
haml :login
end
end
-
+
get '/logout' do
varWash(params)
if session[:session_id]
@@ -17,24 +17,24 @@
end
redirect to('/')
end
-
+
post '/login' do
varWash(params)
if !params[:username] || params[:username].nil?
flash[:error] = 'You must supply a username.'
redirect to('/login')
end
-
+
if !params[:password] || params[:password].nil?
flash[:error] = 'You must supply a password.'
redirect to('/login')
end
-
+
@user = User.first(username: params[:username])
if @user
usern = User.authenticate(params['username'], params['password'])
-
+
# if usern and session[:session_id]
unless usern.nil?
# only delete session if one exists
@@ -57,11 +57,11 @@
redirect to('/login')
end
end
-
+
get '/protected' do
return 'This is a protected page, you must be logged in.'
end
-
+
get '/not_authorized' do
return 'You are not authorized.'
end
diff --git a/routes/main.rb b/routes/main.rb
index 83af0eea..ebefa629 100644
--- a/routes/main.rb
+++ b/routes/main.rb
@@ -13,51 +13,107 @@
get '/home' do
- @results = `ps awwux | grep -i Hashcat | egrep -v "(grep|screen|SCREEN|resque|^$)"`
- @jobs = Jobs.all(:order => [:queued_at.asc])
+
+ @jobs = Jobs.all(order: [:queued_at.asc])
@jobtasks = Jobtasks.all
@tasks = Tasks.all
@taskqueues = Taskqueues.all
@agents = Agents.all
-
- # not used anymore
- # @recentlycracked = repository(:default).adapter.select('SELECT CONCAT(timestampdiff(minute, h.lastupdated, NOW()) ) AS time_period, h.plaintext, a.username FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (h.cracked = 1) ORDER BY h.lastupdated DESC LIMIT 10')
+ @time_now = Time.now
@customers = Customers.all
@active_jobs = Jobs.all(fields: [:id, :status], status: 'Running')
- # nvidia works without sudo:
- @gpustatus = `nvidia-settings -q \"GPUCoreTemp\" | grep Attribute | grep -v gpu | awk '{print $3,$4}'`
- if @gpustatus.empty?
- @gpustatus = `lspci | grep "VGA compatible controller" | cut -d: -f3 | sed 's/\(rev a1\)//g'`
- end
- @gpustatus = @gpustatus.split("\n")
- @gpustat = []
- @gpustatus.each do |line|
- unless line.chomp.empty?
- line = line.delete('.')
- @gpustat << line
- end
- end
+ # JumboTron Display
+ # Building out an array of hashes for the jumbotron display
+ @jumbotron = []
+ @jobs.each do |job|
+ element = {}
+ if job.status == 'Running' || job.status == 'importing'
+ #@Customers = Customers.first(id: job.customer_id).each do |customer|
+ @customers.each do |customer|
+ if customer.id == job.customer_id.to_i
+ element['customer_name'] = customer.name
+ end
+ end
- @jobs.each do |j|
- if j.status == 'Running'
- # gather info for statistics
+ element['job_name'] = job.name
@hash_ids = Array.new
- Hashfilehashes.all(fields: [:hash_id], hashfile_id: j.hashfile_id).each do |entry|
+ Hashfilehashes.all(fields: [:hash_id], hashfile_id: job.hashfile_id).each do |entry|
@hash_ids.push(entry.hash_id)
end
- @alltargets = Hashes.count(id: @hash_ids)
- @crackedtargets = Hashes.count(id: @hash_ids, cracked: 1)
+ hashfile_total = Hashes.count(id: @hash_ids)
+ hashfile_cracked = Hashes.count(id: @hash_ids, cracked: 1)
+
+ element['hashfile_cracked'] = hashfile_cracked
+ element['hashfile_total'] = hashfile_total
+ element['hashfile_progress'] = (hashfile_cracked.to_f / hashfile_total.to_f) * 100
+ element['job_starttime'] = job.started_at
+
+ time_now = Time.now
+ if time_now.to_time - job.started_at.to_time > 86400
+ element['job_runtime'] = ((time_now.to_time - job.started_at.to_time).to_f / 86400).round(2).to_s + ' Days'
+ elsif time_now.to_time - job.started_at.to_time > 3600
+ element['job_runtime'] = ((time_now.to_time - job.started_at.to_time).to_f / 3600).round(2).to_s + ' Hours'
+ elsif time_now.to_time - job.started_at.to_time > 60
+ element['job_runtime'] = ((time_now.to_time - job.started_at.to_time).to_f / 60).round(2).to_s + ' Minutes'
+ elsif time_now.to_time - job.started_at.to_time >= 0
+ element['job_runtime'] = (time_now.to_time - job.started_at.to_time).to_f.round(2).to_s + ' Seconds'
+ else
+ element['job_runtime'] = 'Im ready coach, just send me in.'
+ end
+
+ total_speed = 0
+ Taskqueues.all(job_id: job.id, status: 'Running').each do |queued_task|
+ agent = Agents.first(id: queued_task.agent_id)
+
+ if agent.benchmark
+ # Normalizing Benchmark Speeds
+ if agent.benchmark.include? ' H/s'
+ speed = agent.benchmark.split[0].to_f
+ total_speed += speed
+ elsif agent.benchmark.include? 'kH/s'
+ speed = agent.benchmark.split[0].to_f
+ speed *= 1000
+ total_speed += speed
+ elsif agent.benchmark.include? 'MH/s'
+ speed = agent.benchmark.split[0].to_f
+ speed *= 1000000
+ total_speed += speed
+ elsif agent.benchmark.include? 'GH/s'
+ speed = agent.benchmark.split[0].to_f
+ speed *= 1000000000
+ total_speed += speed
+ elsif agent.benchmark.include? 'TH/s'
+ speed = agent.benchmark.split[0].to_f
+ speed *= 1000000000000
+ total_speed += speed
+ else
+ total_speed += 0
+ end
+ end
+ end
+
+ # Convert to Human Readable Format
+ if total_speed > 1000000000000
+ element['job_crackrate'] = (total_speed.to_f / 1000000000000).round(2).to_s + ' TH/s'
+ elsif total_speed > 1000000000
+ element['job_crackrate'] = (total_speed.to_f / 1000000000).round(2).to_s + ' GH/s'
+ elsif total_speed > 1000000
+ element['job_crackrate'] = (total_speed.to_f / 1000000).round(2).to_s + ' MH/s'
+ elsif total_speed > 1000
+ element['job_crackrate'] = (total_speed.to_f / 1000).round(2).to_s + ' kH/s'
+ elsif total_speed >= 0
+ element['job_crackrate'] = total_speed.to_f.round(2).to_s + ' H/s'
+ else
+ element['job_crackrate'] = '0 H/s'
+ end
- @progress = (@crackedtargets.to_f / @alltargets.to_f) * 100
- # parse a hashcat status file
- @hashcat_status = hashcatParser('control/outfiles/hcoutput_' + j.id.to_s + '.txt')
+ @jumbotron.push(element)
end
end
haml :home
-end
-
+end
\ No newline at end of file
diff --git a/routes/rules.rb b/routes/rules.rb
index 256fc01c..3e8fd117 100644
--- a/routes/rules.rb
+++ b/routes/rules.rb
@@ -46,10 +46,10 @@
redirect to('/rules/list')
end
- # Create DB entry first so that our background job doesnt accidentally pull it in.
+ # Create DB entry first so that our background job doesn't accidentally pull it in.
rules_file = Rules.new
rules_file.name = name
- rules_file.lastupdated = Time.now()
+ rules_file.lastupdated = Time.now
# temporarily save file for testing
rules_file_path_name = "control/rules/#{name}.rule"
@@ -59,7 +59,9 @@
# Parse uploaded file into an array
rules_array = params[:new_rules].to_s.gsub(/\x0d\x0a/, "\x0a") # in theory we shouldnt run into any false positives?
- File.open(rules_file_path_name, 'w') { |f| f.puts(rules_array) }
+ File.open(rules_file_path_name, 'w') do |f|
+ f.puts(rules_array)
+ end
results = Rules.first(name: name)
Resque.enqueue(FileChecksum('rules', results.id))
@@ -121,7 +123,9 @@
File.open(rules_file.path, 'w') { |f| f.puts(@rules) }
# update file size
- size = File.foreach(rules_file.path).inject(0) { |c| c + 1}
+ size = File.foreach(rules_file.path).inject(0) do |c|
+ c + 1
+ end
rules_file.size = size
rules_file.checksum = Digest::SHA2.hexdigest(File.read(rules_file.path))
rules_file.save
diff --git a/routes/settings.rb b/routes/settings.rb
index 54a099f8..2e1005eb 100644
--- a/routes/settings.rb
+++ b/routes/settings.rb
@@ -50,10 +50,10 @@
# get hcbinpath (stored in config file vs db)
@hc_binpath = JSON.parse(File.read('config/agent_config.json'))['hc_binary_path']
-
+
haml :global_settings
end
-
+
post '/settings' do
if params[:form_id] == '1' # Hashcat Settings
@@ -200,7 +200,7 @@
get '/test/email' do
account = User.first(username: getUsername)
- if account.email.nil? or account.email.empty?
+ if account.email.nil? || account.email.empty?
flash[:error] = 'Current logged on user has no email address associated.'
redirect to('/settings')
end
@@ -210,9 +210,5 @@
end
flash[:success] = 'Email sent.'
-
redirect to('/settings')
-end
-
-
-
+end
\ No newline at end of file
diff --git a/routes/tasks.rb b/routes/tasks.rb
index d738e01d..01b24dd4 100644
--- a/routes/tasks.rb
+++ b/routes/tasks.rb
@@ -2,10 +2,11 @@
get '/tasks/list' do
@tasks = Tasks.all
@wordlists = Wordlists.all
+ @rules = Rules.all
haml :task_list
end
-
+
get '/tasks/delete/:id' do
varWash(params)
@@ -20,7 +21,7 @@
redirect to('/tasks/list')
end
-
+
get '/tasks/edit/:id' do
varWash(params)
@task = Tasks.first(id: params[:id])
@@ -38,18 +39,12 @@
@combinator_right_rule = $1
end
end
-
+
@rules = Rules.all
- #@rules = []
- # list wordlists that can be used
- #Dir.foreach('control/rules/') do |item|
- # next if item == '.' || item == '..'
- # @rules << item
- #end
haml :task_edit
end
-
+
post '/tasks/edit/:id' do
varWash(params)
if !params[:name] || params[:name].nil?
@@ -71,18 +66,18 @@
if wordlist_list == ''
wordlist_list = wordlist_check.id.to_s + ','
else
- wordlist_list = wordlist_list + wordlist_check.id.to_s
+ wordlist_list += wordlist_check.id.to_s
end
- wordlist_count = wordlist_count + 1
+ wordlist_count += 1
end
end
end
-
+
if wordlist_count != 2
flash[:error] = 'You must specify at exactly 2 wordlists.'
redirect to("/tasks/edit/#{params[:id]}")
end
-
+
if params[:combinator_left_rule] && !params[:combinator_left_rule].empty? && params[:combinator_right_rule] && !params[:combinator_right_rule].empty?
rule_list = '--rule-left=' + params[:combinator_left_rule] + ' --rule-right=' + params[:combinator_right_rule]
elsif params[:combinator_left_rule] && !params[:combinator_left_rule].empty?
@@ -93,12 +88,12 @@
rule_list = ''
end
end
-
+
task = Tasks.first(id: params[:id])
task.name = params[:name]
-
+
task.hc_attackmode = params[:attackmode]
-
+
if params[:attackmode] == 'dictionary'
task.wl_id = wordlist.id
task.hc_rule = params[:rule].to_i
@@ -108,27 +103,20 @@
task.hc_rule = 'NULL'
task.hc_mask = params[:mask]
elsif params[:attackmode] == 'combinator'
- task.wl_id = wordlist_list
+ task.wl_id = wordlist_list
task.hc_rule = rule_list
task.hc_mask = 'NULL'
end
task.save
-
+
redirect to('/tasks/list')
end
-
+
get '/tasks/create' do
varWash(params)
@hc_settings = HashcatSettings.first
@rules = Rules.all
- #@rules = []
- # list wordlists that can be used
- #Dir.foreach('control/rules/') do |item|
- # next if item == '.' || item == '..'
- # @rules << item
- #end
-
@wordlists = Wordlists.all
haml :task_edit
@@ -174,9 +162,9 @@
if wordlist_list == ''
wordlist_list = wordlist_check.id.to_s + ','
else
- wordlist_list = wordlist_list + wordlist_check.id.to_s
+ wordlist_list += wordlist_check.id.to_s
end
- wordlist_count = wordlist_count + 1
+ wordlist_count += 1
end
end
end
@@ -214,10 +202,8 @@
# generate keyspace of new task and save to db
task.keyspace = getKeyspace(task)
-
task.save
flash[:success] = "Task #{task.name} successfully created."
-
redirect to('/tasks/list')
end
diff --git a/routes/wordlists.rb b/routes/wordlists.rb
index ea1c256c..ce2f2aec 100644
--- a/routes/wordlists.rb
+++ b/routes/wordlists.rb
@@ -1,5 +1,5 @@
# encoding: utf-8
-# require_relative '../jobs/init' # this shouldn't be needed?
+
get '/wordlists/list' do
@wordlists = Wordlists.all
@@ -20,7 +20,7 @@
else
# check if wordlist is in use
@task_list = Tasks.all(wl_id: @wordlist.id)
- if !@task_list.empty?
+ unless @task_list.empty?
flash[:error] = 'This word list is associated with a task, it cannot be deleted.'
redirect to('/wordlists/list')
end
@@ -31,8 +31,6 @@
# delete from db
@wordlist.destroy
- # Update our magic wordlist
- # Resque.enqueue(MagicWordlist)
end
redirect to('/wordlists/list')
end
@@ -68,8 +66,8 @@
wordlist.save
File.open(file_name, 'wb') { |f| f.write(params[:file][:tempfile].read) }
+ Resque.enqueue(WordlistImporter)
Resque.enqueue(WordlistChecksum)
- # Update our magic wordlist
- # Resque.enqueue(MagicWordlist)
+
redirect to('/wordlists/list')
end
diff --git a/views/account_edit.haml b/views/account_edit.haml
index a3276019..20c3ca96 100644
--- a/views/account_edit.haml
+++ b/views/account_edit.haml
@@ -1,6 +1,5 @@
!!!
%html
- /https://jsfiddle.net/vigneshmoha/bbxMe/2/
%body
.span15
%br
@@ -13,45 +12,60 @@
.row
.col-md-10
- if @user
- %form{:class => "form-horizontal", :action => "/accounts/save", :method => "post"}
+ %form{class: 'form-horizontal', action: '/accounts/save', method: 'post'}
.form-group
- %label.control-label.col-xs-2{:for => ""} Username
+ %label.control-label.col-xs-2{for: ''} Username
.col-md-6
- %input{:type => "textbox", :class => "form-control", :name => "username", :id => "username", :value => @user.username}
+ %input{type: 'textbox', class: 'form-control', name: 'username', id: 'username', value: @user.username}
.form-group
- %label.control-label.col-xs-2{:for => ""} Reset Password
+ %label.control-label.col-xs-2{for: ''} Reset Password
.col-md-6
- %input{:type => "password", :class => "form-control", :name => "password", :id => "password"}
+ %input{type: 'password', class: 'form-control', name: 'password', id: 'password'}
.form-group
- %label.control-label.col-xs-2{:for => ""} Confirm Password
+ %label.control-label.col-xs-2{for: ''} Confirm Password
.col-md-6
- %input{:type => "password", :class => "form-control", :name => "confirm", :id => "confirm"}
+ %input{type: 'password', class: 'form-control', name: 'confirm', id: 'confirm'}
.form-group
- %label.control-label.col-xs-2{:for => ""} Email Address (optional)
+ %label.control-label.col-xs-2{for: ''} Email Address (optional)
.col-md-6
- %input{:type => 'textbox', :class => 'form-control', :name => 'email', :id => 'email', :value => @user.email}
+ %input{type: 'textbox', class: 'form-control', name: 'email', id: 'email', value: @user.email}
+ .form-group
+ %label.control-label.col-xs-2{:for => ""} MFA (Google Authenticator)
+ .col-md-6
+ %input{:type => 'checkbox', :class => 'form-control', :name => 'mfa', :id => 'mfa', :checked => @user.mfa}
+ - if @user.mfa
+ .form-group
+ %label.control-label.col-xs-2{:for => ""} QR Code
+ .col-md-6
+ %img{:src => @otp, :alt => "Google Authenticator QR Code" }
+ %link{:href => "#{@otp}", :rel => "stylesheet"}
+
.form-group
.col-xs-offset-2.col-xs-10
- %input{:type => 'hidden', :name => 'account_id', :value => "#{params[:account_id].to_s}"}
- %button.btn.btn-primary{:type => "submit"} Save
+ %input{type: 'hidden', name: 'account_id', value: "#{params[:account_id].to_s}"}
+ %button.btn.btn-primary{type: 'submit'} Save
- else
- %form{:class => "form-horizontal", :action => "/accounts/create", :method => "post"}
+ %form{class: 'form-horizontal', action: '/accounts/create', method: 'post'}
+ .form-group
+ %label.control-label.col-xs-2{for: ''} Username
+ .col-md-6
+ %input{type: 'textbox', class: 'form-control', name: 'username', id: 'username'}
.form-group
- %label.control-label.col-xs-2{:for => ""} Username
+ %label.control-label.col-xs-2{for: ''} Password
.col-md-6
- %input{:type => "textbox", :class => "form-control", :name => "username", :id => "username"}
+ %input{type: 'password', class: 'form-control', name: 'password', id: 'password'}
.form-group
- %label.control-label.col-xs-2{:for => ""} Password
+ %label.control-label.col-xs-2{for: ''} Confirm Password
.col-md-6
- %input{:type => "password", :class => "form-control", :name => "password", :id => "password"}
+ %input{type: 'password', class: 'form-control', name: 'confirm', id: 'confirm'}
.form-group
- %label.control-label.col-xs-2{:for => ""} Confirm Password
+ %label.control-label.col-xs-2{for: ''} Email Address (optional)
.col-md-6
- %input{:type => "password", :class => "form-control", :name => "confirm", :id => "confirm"}
+ %input{type: 'textbox', class: 'form-control', name: 'email', id: 'email'}
.form-group
- %label.control-label.col-xs-2{:for => ""} Email Address (optional)
+ %label.control-label.col-xs-2{:for => ""} MFA (Google Authenticator)
.col-md-6
- %input{:type => 'textbox', :class => 'form-control', :name => 'email', :id => 'email'}
+ %input{:type => 'checkbox', :class => 'form-control', :name => 'mfa', :id => 'mfa'}
.form-group
.col-xs-offset-2.col-xs-10
- %button.btn.btn-primary{:type => "submit"} Create
+ %button.btn.btn-primary{type: 'submit'} Create
diff --git a/views/account_list.haml b/views/account_list.haml
index 82d79686..79a98231 100644
--- a/views/account_list.haml
+++ b/views/account_list.haml
@@ -12,7 +12,7 @@
.col-md-10.pull-left
These are awesome people that use Hashview.
.col-md-2.pull-right
- %a.btn.btn-primary.pull-right{:href => "/accounts/create"}
+ %a.btn.btn-primary.pull-right{href: '/accounts/create'}
Add User
%br
%br
@@ -20,7 +20,7 @@
.row
.col-md-12
.table
- %table{:class => 'table table-striped'}
+ %table{class: 'table table-striped'}
%thead
%tr
%th
@@ -33,7 +33,7 @@
%tr
%td #{user.username}
%td
- %a.btn.btn-warning{:href => "/accounts/edit/#{user.id}"}
- %i.glyphicon.glyphicon-cog{:title => 'Edit'}
- %a.btn.btn-danger{:href => "/accounts/delete/#{user.id}"}
- %i.glyphicon.glyphicon-trash{:title => 'Delete'}
+ %a.btn.btn-warning{href: "/accounts/edit/#{user.id}"}
+ %i.glyphicon.glyphicon-cog{title: 'Edit'}
+ %a.btn.btn-danger{href: "/accounts/delete/#{user.id}"}
+ %i.glyphicon.glyphicon-trash{title: 'Delete'}
diff --git a/views/agent_edit.haml b/views/agent_edit.haml
index c4267c80..320ef5f8 100644
--- a/views/agent_edit.haml
+++ b/views/agent_edit.haml
@@ -10,11 +10,11 @@
%br
.row
.col-md-10
- %form{:class => "form-horizontal", :action => "/agents/#{@agent.id}/edit", :method => "post"}
+ %form{class: 'form-horizontal', action: "/agents/#{@agent.id}/edit", method: 'post'}
.form-group
- %label.control-label.col-xs-2{:for => ""} Agent Name
+ %label.control-label.col-xs-2{for: ''} Agent Name
.col-xs-10
- %input{:type => "textbox", :class => "form-control", :name => "name", :id => "name", :value => "#{@agent.name}"}
+ %input{type: 'textbox', class: 'form-control', name: 'name', id: 'name', value: "#{@agent.name}"}
.form-group
.col-xs-offset-2.col-xs-10
- %button.btn.btn-primary{:type => "submit"} Update
\ No newline at end of file
+ %button.btn.btn-primary{type: 'submit'} Update
\ No newline at end of file
diff --git a/views/agent_list.haml b/views/agent_list.haml
index ea1ab3e5..d532b9e9 100644
--- a/views/agent_list.haml
+++ b/views/agent_list.haml
@@ -74,7 +74,7 @@
.row
.col-md-12
.table
- %table{:id => 'agenttable', :class => 'table table-striped'}
+ %table{id: 'agenttable', class: 'table table-striped'}
%thead
%tr
%th
@@ -94,42 +94,42 @@
- @agents.each do |agent|
- link = rand(36**8).to_s(36)
%tr
- %td{:class => "accordian-toggle", "data-toggle" => "collapse", :href => "#collapse-#{link}"} #{agent.name}
- %td{:class => "accordian-toggle", "data-toggle" => "collapse", :href => "#collapse-#{link}"} #{agent.status}
- %td{:class => "accordian-toggle", "data-toggle" => "collapse", :href => "#collapse-#{link}"} #{agent.src_ip}
- %td{:class => "accordian-toggle", "data-toggle" => "collapse", :href => "#collapse-#{link}"} #{agent.benchmark}
- %td{:class => "accordian-toggle", "data-toggle" => "collapse", :href => "#collapse-#{link}"} #{agent.heartbeat}
+ %td{class: 'accordian-toggle', "data-toggle" => 'collapse', href: "#collapse-#{link}"} #{agent.name}
+ %td{class: 'accordian-toggle', "data-toggle" => 'collapse', href: "#collapse-#{link}"} #{agent.status}
+ %td{class: 'accordian-toggle', "data-toggle" => 'collapse', href: "#collapse-#{link}"} #{agent.src_ip}
+ %td{class: 'accordian-toggle', "data-toggle" => 'collapse', href: "#collapse-#{link}"} #{agent.benchmark}
+ %td{class: 'accordian-toggle', "data-toggle" => 'collapse', href: "#collapse-#{link}"} #{agent.heartbeat}
%td
- if agent.src_ip == '127.0.0.1' || agent.src_ip == 'localhost'
- if agent.status == 'Authorized' || agent.status == 'Online' || agent.status == 'Offline' || agent.status == 'Working' || agent.status == 'Idle'
- %a.btn.btn-primary.disabled{:href => "/agents/#{agent.id}/deauthorize"}
+ %a.btn.btn-primary.disabled{href: "/agents/#{agent.id}/deauthorize"}
Deauthorize
- else
- %a.btn.btn-primary{:href => "/agents/#{agent.id}/authorize"}
+ %a.btn.btn-primary{href: "/agents/#{agent.id}/authorize"}
Authorize
- %a.btn.btn-warning{:href => "/agents/#{agent.id}/edit"}
- %i.glyphicon.glyphicon-cog{:title => 'Edit'}
- %a.btn.btn-danger.disabled{:href => "/agents/#{agent.id}/delete"}
- %i.glyphicon.glyphicon-trash{:title => 'Delete'}
+ %a.btn.btn-warning{href: "/agents/#{agent.id}/edit"}
+ %i.glyphicon.glyphicon-cog{title: 'Edit'}
+ %a.btn.btn-danger.disabled{href: "/agents/#{agent.id}/delete"}
+ %i.glyphicon.glyphicon-trash{title: 'Delete'}
- else
- if agent.status == 'Pending'
- %a.btn.btn-primary{:href => "/agents/#{agent.id}/authorize"}
+ %a.btn.btn-primary{href: "/agents/#{agent.id}/authorize"}
Authorize
- else
- %a.btn.btn-primary{:href => "/agents/#{agent.id}/deauthorize"}
+ %a.btn.btn-primary{href: "/agents/#{agent.id}/deauthorize"}
Deauthorize
- %a.btn.btn-warning{:href => "/agents/#{agent.id}/edit"}
- %i.glyphicon.glyphicon-cog{:title => 'Edit'}
- %a.btn.btn-danger{:href => "/agents/#{agent.id}/delete"}
- %i.glyphicon.glyphicon-trash{:title => 'Delete'}
+ %a.btn.btn-warning{href: "/agents/#{agent.id}/edit"}
+ %i.glyphicon.glyphicon-cog{title: 'Edit'}
+ %a.btn.btn-danger{href: "/agents/#{agent.id}/delete"}
+ %i.glyphicon.glyphicon-trash{title: 'Delete'}
- if agent.hc_status
- unless agent.hc_status.empty?
%tr
- %td{:colspan => 6}
- %div{:id => "collapse-#{link}", :class => "panel-collapse collapse in"}
+ %td{colspan: 6}
+ %div{id: "collapse-#{link}", class: "panel-collapse collapse in"}
%h4 Hashcat Status
%br
- %table{:class => 'table'}
+ %table{class: 'table'}
- unless agent.hc_status.empty?
- JSON.parse(agent.hc_status).each do |k,v|
%tr
diff --git a/views/analytics.haml b/views/analytics.haml
index 9c57d78b..ec9b6ca1 100644
--- a/views/analytics.haml
+++ b/views/analytics.haml
@@ -6,9 +6,9 @@
.container
.col-md-12
.form-group.text-right
- %label.control-label{:for => ""} Customer:
+ %label.control-label{for: ''} Customer:
.btn-group
- .btn.btn-default.dropdown-toggle{"data-toggle" => "dropdown"}
+ .btn.btn-default.dropdown-toggle{"data-toggle" => 'dropdown'}
- if @customer_id.nil?
All Time
- else
@@ -16,15 +16,15 @@
%span.caret
%ul.dropdown-menu.dropdown-menu-right
%li
- %a{:href => "/analytics"} All
- %li.divider{:role => "separator"}
+ %a{href: '/analytics'} All
+ %li.divider{role: 'separator'}
- @button_select_customers.each do |customer|
%li
- %a{:href => "/analytics?customer_id=#{customer.id}"} #{customer.name}
+ %a{href: "/analytics?customer_id=#{customer.id}"} #{customer.name}
- if @customer_id
 
- %label.control-label.text-right{:for => ""} Hash File:
- .btn.btn-default.dropdown-toggle{"data-toggle" => "dropdown"}
+ %label.control-label.text-right{for: ''} Hash File:
+ .btn.btn-default.dropdown-toggle{"data-toggle" => 'dropdown'}
- if !params[:hashfile_id]
All Hashes
- else
@@ -32,11 +32,11 @@
%span.caret
%ul.dropdown-menu.dropdown-menu-right
%li
- %a{:href => "/analytics?customer_id=#{@customer_id}"} All Hashes
- %li.divider{:role => "separator"}
+ %a{href: "/analytics?customer_id=#{@customer_id}"} All Hashes
+ %li.divider{role: 'separator'}
- @button_select_hashfiles.each do |hashfile|
%li
- %a{:href => "/analytics?customer_id=#{@customer_id}&hashfile_id=#{hashfile.id}"} #{hashfile.name}
+ %a{href: "/analytics?customer_id=#{@customer_id}&hashfile_id=#{hashfile.id}"} #{hashfile.name}
 
.col-md-12
.page-header
@@ -58,7 +58,7 @@
%b Total Hashes Cracked
.panel-body
%br
- %div{:id =>"chart1", :style => 'text-align:center'}
+ %div{id: 'chart1', style: 'text-align:center'}
//cred to http://jsfiddle.net/ragingsquirrel3/qkHK6 for this
:plain
@@ -126,7 +126,7 @@
%b Composition Breakdown
.panel-body
%br
- %div{:id =>"chart2", :style => 'text-align:center'}
+ %div{id: "chart2", :style => 'text-align:center'}
//cred to http://jsfiddle.net/ragingsquirrel3/qkHK6 for this
:plain
@@ -190,7 +190,7 @@
.panel-heading
%b Analysis Details
.panel-body
- %table{:class => 'table'}
+ %table{class: 'table'}
- if params[:hashfile_id].nil?
%td
%b All Hashfiles
@@ -224,29 +224,29 @@
%b Download
%td
- if @customer_id.nil?
- %a.btn.btn-success.pull-left{:href => '/download?type=cracked'}
+ %a.btn.btn-success.pull-left{href: '/download?graph=3&type=cracked'}
Cracked
- %a.btn.btn-danger.pull-center{:href => '/download?type=uncracked'}
+ %a.btn.btn-danger.pull-center{href: '/download?graph=3&type=uncracked'}
Uncracked
- elsif @customer_id && @hashfile_id.nil?
- %a.btn.btn-success.pull-left{:href => "/download?type=cracked&customer_id=#{@customer_id}"}
+ %a.btn.btn-success.pull-left{href: "/download?graph=3&type=cracked&customer_id=#{@customer_id}"}
Cracked
- %a.btn.btn-danger.pull-center{:href => "/download?type=uncracked&customer_id=#{@customer_id}"}
+ %a.btn.btn-danger.pull-center{href: "/download?graph=3&type=uncracked&customer_id=#{@customer_id}"}
Uncracked
- elsif @customer_id && @hashfile_id
- %a.btn.btn-success.pull-left{:href => "/download?type=cracked&customer_id=#{@customer_id}&hashfile_id=#{@hashfile_id}"}
+ %a.btn.btn-success.pull-left{href: "/download?graph=3&type=cracked&customer_id=#{@customer_id}&hashfile_id=#{@hashfile_id}"}
Cracked
- %a.btn.btn-danger.pull-center{:href => "/download?type=uncracked&customer_id=#{@customer_id}&hashfile_id=#{@hashfile_id}"}
+ %a.btn.btn-danger.pull-center{href: "/download?graph=3&type=uncracked&customer_id=#{@customer_id}&hashfile_id=#{@hashfile_id}"}
Uncracked
.col-md-6
.panel.panel-default
.panel-heading
%b Composition Details
.panel-body
- %table{:class => 'table'}
+ %table{class: 'table'}
%td
%b Mask
%td
@@ -260,7 +260,7 @@
.panel-heading
%b Password Count by Length
.panel-body
- %div{:id =>"chart3"}
+ %div{id: 'chart3'}
:plain