/routes/analytics.rb

https://github.com/hashview/hashview · Ruby · 581 lines · 441 code · 76 blank · 64 comment · 110 complexity · a30f4d41781bc79eeb485d660686c24b MD5 · raw file

  1. require 'json'
  2. # displays analytics for a specific client, job
  3. get '/analytics' do
  4. varWash(params)
  5. @customer_id = params[:customer_id]
  6. @hashfile_id = params[:hashfile_id]
  7. @button_select_customers = Customers.order(Sequel.asc(:name)).all
  8. if params[:customer_id] && !params[:customer_id].empty?
  9. @button_select_hashfiles = Hashfiles.where(customer_id: params[:customer_id]).all
  10. end
  11. @customers = if params[:customer_id] && !params[:customer_id].empty?
  12. Customers.first(id: params[:customer_id])
  13. else
  14. Customers.order(Sequel.asc(:name)).all
  15. end
  16. if params[:customer_id] && !params[:customer_id].empty?
  17. @hashfiles = if params[:hashfile_id] && !params[:hashfile_id].empty?
  18. Hashfiles.first(id: params[:hashfile_id])
  19. else
  20. Hashfiles.order(Sequel.asc(:id)).all
  21. end
  22. end
  23. # get results of specific customer if customer_id is defined
  24. # if we have a customer
  25. if params[:customer_id] && !params[:customer_id].empty?
  26. # if we have a hashfile
  27. if params[:hashfile_id] && !params[:hashfile_id].empty?
  28. # Used for Complexity Breakdown doughnut: Customer: Hashfile
  29. @complexity_hashes = HVDB.fetch('SELECT a.username as username, h.plaintext as 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])
  30. @cracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (a.hashfile_id = ? AND h.cracked = 1)', params[:hashfile_id])[:count]
  31. @cracked_pw_count = @cracked_pw_count[:count]
  32. # Used for Total Accounts table: Customer: Hashfile
  33. @total_accounts = @uncracked_pw_count.to_i + @cracked_pw_count.to_i
  34. # Used for Total Unique Users and originalhashes Table: Customer: Hashfile
  35. @total_users_originalhash = HVDB.fetch('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 f.id = ?)', params[:customer_id], params[:hashfile_id])
  36. @total_unique_users_count = HVDB.fetch('SELECT COUNT(DISTINCT(username)) as count FROM hashfilehashes WHERE hashfile_id = ?', params[:hashfile_id])[:count]
  37. @total_unique_users_count = @total_unique_users_count[:count]
  38. @total_unique_originalhash_count = HVDB.fetch('SELECT COUNT(DISTINCT(h.originalhash)) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE a.hashfile_id = ?', params[:hashfile_id])[:count]
  39. @total_unique_originalhash_count = @total_unique_originalhash_count[:count]
  40. # Used for Total Run Time: Customer: Hashfile
  41. @total_run_time = Hashfiles.first(id: params[:hashfile_id])[:total_run_time]
  42. # Used for Mask Generator: Customer: Hashfile
  43. @hashes_for_mask = HVDB.fetch('SELECT h.plaintext as 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])
  44. # make list of unique hashes
  45. unique_hashes = Set.new
  46. @total_users_originalhash.each do |entry|
  47. unique_hashes.add(entry[:originalhash])
  48. end
  49. hashes = []
  50. # create array of all hashes to count dups
  51. @total_users_originalhash.each do |uh|
  52. unless uh[:originalhash].nil?
  53. hashes << uh[:originalhash] unless uh[:originalhash].empty?
  54. end
  55. end
  56. @duphashes = {}
  57. # count dup hashes
  58. hashes.each do |hash|
  59. if @duphashes[hash].nil?
  60. @duphashes[hash] = 1
  61. else
  62. @duphashes[hash] += 1
  63. end
  64. end
  65. # this will only display top 10 hash/passwords shared by users
  66. @duphashes = Hash[@duphashes.sort_by { |_k, v| -v }[0..20]]
  67. users_same_password = []
  68. @password_users = {}
  69. # for each unique password hash find the users and their plaintext
  70. @duphashes.each do |hash|
  71. dups = HVDB.fetch('SELECT a.username, h.plaintext, h.cracked 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 f.id = ? AND h.originalhash = ?)', params[:customer_id], params[:hashfile_id], hash[0] )
  72. # for each user with the same password hash add user to array
  73. dups.each do |d|
  74. users_same_password << if !d[:username].nil?
  75. d[:username]
  76. else
  77. 'NULL'
  78. end
  79. if d[:cracked]
  80. hash[0] = d[:plaintext]
  81. end
  82. end
  83. # assign array of users to hash of similar password hashes
  84. if users_same_password.length > 1
  85. @password_users[hash[0]] = users_same_password
  86. end
  87. users_same_password = []
  88. end
  89. else
  90. # Used for Complexity Breakdown doughnut: Customer
  91. @complexity_hashes = HVDB.fetch('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])
  92. @cracked_pw_count = HVDB.fetch('SELECT count(h.plaintext) as count 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])[:count]
  93. @cracked_pw_count = @cracked_pw_count[:count]
  94. # Used for Total Accounts Table: Customer
  95. @total_accounts = @uncracked_pw_count.to_i + @cracked_pw_count.to_i
  96. # Used for Total Unique Users and original hashes Table: Customer
  97. @total_unique_users_count = HVDB.fetch('SELECT COUNT(DISTINCT(username)) as count FROM hashfilehashes a LEFT JOIN hashfiles f ON a.hashfile_id = f.id WHERE f.customer_id = ?', params[:customer_id])[:count]
  98. @total_unique_users_count = @total_unique_users_count[:count]
  99. @total_unique_originalhash_count = HVDB.fetch('SELECT COUNT(DISTINCT(h.originalhash)) as count 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 = ?', params[:customer_id])[:count]
  100. @total_unique_originalhash_count = @total_unique_originalhash_count[:count]
  101. # Used for Total Run Time: Customer:
  102. @total_run_time = Hashfiles.where(:customer_id => params[:customer_id]).sum(:total_run_time)
  103. # Used for Mask Generator: Customer: Hashfile
  104. @hashes_for_mask = HVDB.fetch('SELECT 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])
  105. end
  106. else
  107. # Used for Complexity Breakdown Doughnut: Total
  108. @complexity_hashes = HVDB.fetch('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)')
  109. @cracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (h.cracked = 1)')[:count]
  110. @cracked_pw_count = @cracked_pw_count[:count]
  111. # Used for Total Accounts Table: Total
  112. @total_accounts = Hashfilehashes.count
  113. # Used for Total Unique Users and originalhashes Tables: Total
  114. @total_unique_users_count = HVDB.fetch('SELECT COUNT(DISTINCT(username)) as count FROM hashfilehashes')[:count]
  115. @total_unique_users_count = @total_unique_users_count[:count]
  116. @total_unique_originalhash_count = HVDB.fetch('SELECT COUNT(DISTINCT(originalhash)) as count FROM hashes')[:count]
  117. @total_unique_originalhash_count = @total_unique_originalhash_count[:count]
  118. # Used for Total Run Time:
  119. @total_run_time = Hashfiles.sum(:total_run_time)
  120. # Used for Mask Generator: Customer: Hashfile
  121. @hashes_for_mask = HVDB.fetch('SELECT h.plaintext as 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)')
  122. end
  123. # Parse Complexity variables
  124. @meets_complexity_count = 0
  125. @fails_complexity_count = 0
  126. @fails_complexity = {}
  127. @complexity_hashes.each do |entry|
  128. if entry[:plaintext].to_s.length < 6
  129. @fails_complexity[entry[:username]] = entry[:plaintext]
  130. else
  131. flags = 0
  132. flags += 1 if entry[:plaintext].to_s =~ /[a-z]/
  133. flags += 1 if entry[:plaintext].to_s =~ /[A-Z]/
  134. flags += 1 if entry[:plaintext].to_s =~ /\d/
  135. flags += 1 if entry[:plaintext].to_s.force_encoding('UTF-8') =~ /[^0-9A-Za-z]/u
  136. @fails_complexity[entry[:username]] = entry[:plaintext] if flags < 3
  137. end
  138. @fails_complexity_count = @fails_complexity.length
  139. @meets_complexity_count = @cracked_pw_count.to_i - @fails_complexity_count.to_i
  140. end
  141. # Mask Coposition variables
  142. @mask_list = {}
  143. @hashes_for_mask.each do |entry|
  144. entry = entry[:plaintext]
  145. entry = entry.gsub(/[A-Z]/, 'U') # Find all upper case chars
  146. entry = entry.gsub(/[a-z]/, 'L') # Find all lower case chars
  147. entry = entry.gsub(/[0-9]/, 'D') # Find all digits
  148. entry = entry.force_encoding('UTF-8').gsub(/[^0-9A-Za-z]/u, 'S')
  149. if @mask_list[entry].nil?
  150. @mask_list[entry] = 0
  151. else
  152. @mask_list[entry] += 1
  153. end
  154. end
  155. @top_ten_masks = []
  156. top_ten_entry = {}
  157. total = 0
  158. percent_total = 0
  159. @mask_list = @mask_list.sort_by { |_key, value| -value }[0..9]
  160. @mask_list.each do |key, value|
  161. key = key.gsub(/U/, '?u')
  162. key = key.gsub(/L/, '?l')
  163. key = key.gsub(/D/, '?d')
  164. key = key.gsub(/S/, '?s')
  165. value = value + 1
  166. top_ten_entry[:mask] = key
  167. top_ten_entry[:count] = value
  168. total += value
  169. top_ten_entry[:percentage] = ((value.to_f / @cracked_pw_count.to_f) * 100)
  170. percent_total += ((value.to_f / @cracked_pw_count.to_f) * 100)
  171. @top_ten_masks.push(top_ten_entry)
  172. top_ten_entry = {}
  173. end
  174. top_ten_entry[:mask] = 'OTHER'
  175. top_ten_entry[:count] = @cracked_pw_count - total
  176. top_ten_entry[:percentage] = (100 - percent_total).to_s
  177. @top_ten_masks.push(top_ten_entry)
  178. @passwords = @cracked_results.to_json
  179. haml :analytics
  180. end
  181. # Callback for d3 graph for displaying Total Hashes Cracked
  182. get '/analytics/graph/TotalHashesCracked' do
  183. varWash(params)
  184. if params[:customer_id] && !params[:customer_id].empty?
  185. # if we have a hashfile
  186. if params[:hashfile_id] && !params[:hashfile_id].empty?
  187. # Used for Total Hashes Cracked doughnut: Customer: Hashfile
  188. @cracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (a.hashfile_id = ? AND h.cracked = 1)', params[:hashfile_id])[:count]
  189. @cracked_pw_count = @cracked_pw_count[:count]
  190. @uncracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (a.hashfile_id = ? AND h.cracked = 0)', params[:hashfile_id])[:count]
  191. @uncracked_pw_count = @uncracked_pw_count[:count]
  192. else
  193. # Used for Total Hashes Cracked doughnut: Customer
  194. @cracked_pw_count = HVDB.fetch('SELECT count(h.plaintext) as count 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])[:count]
  195. @cracked_pw_count = @cracked_pw_count[:count]
  196. @uncracked_pw_count = HVDB.fetch('SELECT count(h.originalhash) as count 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])[:count]
  197. @uncracked_pw_count = @uncracked_pw_count[:count]
  198. end
  199. else
  200. # Used for Total Hash Cracked Doughnut: Total
  201. @cracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (h.cracked = 1)')[:count]
  202. @cracked_pw_count = @cracked_pw_count[:count]
  203. @uncracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (h.cracked = 0)')[:count]
  204. @uncracked_pw_count = @uncracked_pw_count[:count]
  205. end
  206. content = []
  207. content << { 'label':'Cracked', 'value':@cracked_pw_count }
  208. content << { 'label':'Uncracked', 'value':@uncracked_pw_count }
  209. return content.to_json
  210. end
  211. # Callback for d3 graph for displaying Complexity Breakdown
  212. get '/analytics/graph/ComplexityBreakdown' do
  213. varWash(params)
  214. if params[:customer_id] && !params[:customer_id].empty?
  215. # if we have a hashfile
  216. if params[:hashfile_id] && !params[:hashfile_id].empty?
  217. # Used for Complexity Breakdown doughnut: Customer: Hashfile
  218. @complexity_hashes = HVDB.fetch('SELECT a.username as username, h.plaintext as 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])
  219. @cracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (a.hashfile_id = ? AND h.cracked = 1)', params[:hashfile_id])[:count]
  220. @cracked_pw_count = @cracked_pw_count[:count]
  221. else
  222. # Used for Complexity Breakdown doughnut: Customer
  223. @complexity_hashes = HVDB.fetch('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])
  224. @cracked_pw_count = HVDB.fetch('SELECT count(h.plaintext) as count 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])[:count]
  225. @cracked_pw_count = @cracked_pw_count[:count]
  226. end
  227. else
  228. # Used for Complexity Breakdown Doughnut: Total
  229. @complexity_hashes = HVDB.fetch('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)')
  230. @cracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (h.cracked = 1)')[:count]
  231. @cracked_pw_count = @cracked_pw_count[:count]
  232. end
  233. @meets_complexity_count = 0
  234. @fails_complexity_count = 0
  235. @fails_complexity = {}
  236. @complexity_hashes.each do |entry|
  237. if entry[:plaintext].to_s.length < 6
  238. @fails_complexity[entry[:username]] = entry[:plaintext]
  239. else
  240. flags = 0
  241. flags += 1 if entry[:plaintext].to_s =~ /[a-z]/
  242. flags += 1 if entry[:plaintext].to_s =~ /[A-Z]/
  243. flags += 1 if entry[:plaintext].to_s =~ /\d/
  244. flags += 1 if entry[:plaintext].to_s.force_encoding('UTF-8') =~ /[^0-9A-Za-z]/u
  245. @fails_complexity[entry[:username]] = entry[:plaintext] if flags < 3
  246. end
  247. @fails_complexity_count = @fails_complexity.length
  248. @meets_complexity_count = @cracked_pw_count.to_i - @fails_complexity_count.to_i
  249. end
  250. content = []
  251. content << { 'label':'Fails Complexity', 'value':@fails_complexity_count }
  252. content << { 'label':'Meets Complexity', 'value':@meets_complexity_count }
  253. return content.to_json
  254. end
  255. # Callback for d3 graph for displaying Complexity Breakdown
  256. get '/analytics/graph/CharsetBreakdown' do
  257. varWash(params)
  258. if params[:customer_id] && !params[:customer_id].empty?
  259. # if we have a hashfile
  260. if params[:hashfile_id] && !params[:hashfile_id].empty?
  261. # Used for Complexity Breakdown doughnut: Customer: Hashfile
  262. @complexity_hashes = HVDB.fetch('SELECT a.username as username, h.plaintext as 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])
  263. else
  264. # Used for Complexity Breakdown doughnut: Customer
  265. @complexity_hashes = HVDB.fetch('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])
  266. end
  267. else
  268. # Used for Complexity Breakdown Doughnut: Total
  269. @complexity_hashes = HVDB.fetch('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)')
  270. end
  271. numeric = 0
  272. loweralpha = 0
  273. upperalpha = 0
  274. special = 0
  275. mixedalpha = 0
  276. loweralphanum = 0
  277. upperalphanum = 0
  278. loweralphaspecial = 0
  279. upperalphaspecial = 0
  280. specialnum = 0
  281. mixedalphaspecial = 0
  282. upperalphaspecialnum = 0
  283. loweralphaspecialnum = 0
  284. mixedalphanum = 0
  285. other = 0
  286. @fails_complexity = {}
  287. @complexity_hashes.each do |entry|
  288. entry = entry[:plaintext]
  289. entry = entry.gsub(/[A-Z]/, 'U') # Find all upper case chars
  290. entry = entry.gsub(/[a-z]/, 'L') # Find all lower case chars
  291. entry = entry.gsub(/[0-9]/, 'D') # Find all digits
  292. entry = entry.force_encoding('UTF-8').gsub(/[^0-9A-Za-z]/u, 'S')
  293. if entry !~ /U/ && entry !~ /L/ && entry =~ /D/ && entry !~ /S/
  294. numeric += 1
  295. elsif entry !~ /U/ && entry =~ /L/ && entry !~ /D/ && entry !~ /S/
  296. loweralpha += 1
  297. elsif entry =~ /U/ && entry !~ /L/ && entry !~ /D/ && entry !~ /S/
  298. upperalpha += 1
  299. elsif entry !~ /U/ && entry !~ /L/ && entry !~ /D/ && entry =~ /S/
  300. special += 1
  301. elsif entry =~ /U/ && entry =~ /L/ && entry !~ /D/ && entry !~ /S/
  302. mixedalpha += 1
  303. elsif entry =~ /U/ && entry !~ /L/ && entry =~ /D/ && entry !~ /S/
  304. loweralphanum += 1
  305. elsif entry =~ /U/ && entry =~ /L/ && entry =~ /D/ && entry !~ /S/
  306. upperalphanum += 1
  307. elsif entry !~ /U/ && entry =~ /L/ && entry !~ /D/ && entry =~ /S/
  308. loweralphaspecial += 1
  309. elsif entry =~ /U/ && entry !~ /L/ && entry !~ /D/ && entry =~ /S/
  310. upperalphaspecial += 1
  311. elsif entry !~ /U/ && entry !~ /L/ && entry =~ /D/ && entry =~ /S/
  312. specialnum += 1
  313. elsif entry =~ /U/ && entry =~ /L/ && entry !~ /D/ && entry =~ /S/
  314. mixedalphaspecial += 1
  315. elsif entry =~ /U/ && entry !~ /L/ && entry =~ /D/ && entry =~ /S/
  316. upperalphaspecialnum += 1
  317. elsif entry !~ /U/ && entry =~ /L/ && entry =~ /D/ && entry =~ /S/
  318. loweralphaspecialnum += 1
  319. elsif entry =~ /U/ && entry =~ /L/ && entry =~ /D/ && entry =~ /S/
  320. mixedalphanum += 1
  321. else
  322. other += 1
  323. end
  324. end
  325. charset_list = {}
  326. charset_list[:numeric] = numeric
  327. charset_list[:loweralpha] = loweralpha
  328. charset_list[:upperalpha] = upperalpha
  329. charset_list[:special] = special
  330. charset_list[:mixedalpha] = mixedalpha
  331. charset_list[:loweralphanum] = loweralphanum
  332. charset_list[:upperalphanum] = upperalphanum
  333. charset_list[:loweralphaspecial] = loweralphaspecial
  334. charset_list[:upperalphaspecial] = upperalphaspecial
  335. charset_list[:specialnum] = specialnum
  336. charset_list[:mixedalphaspecial] = mixedalphaspecial
  337. charset_list[:upperalphaspecialnum] = upperalphaspecialnum
  338. charset_list[:loweralphaspecialnum] = loweralphaspecialnum
  339. charset_list[:mixedalphanum] = mixedalphanum
  340. content = []
  341. charset_list = charset_list.sort_by { |_key, value| -value }[0..5]
  342. charset_list.each do |key, value|
  343. content << { 'label':key, 'value':value } if value > 0
  344. end
  345. return content.to_json
  346. end
  347. # callback for d3 graph displaying passwords by length
  348. get '/analytics/PasswordsCountByLength' do
  349. varWash(params)
  350. @counts = []
  351. @passwords = {}
  352. if params[:customer_id] && !params[:customer_id].empty?
  353. if params[:hashfile_id] && !params[:hashfile_id].empty?
  354. @cracked_results = HVDB.fetch('SELECT h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE h.cracked = 1 AND a.hashfile_id = ?', params[:hashfile_id])
  355. else
  356. @cracked_results = HVDB.fetch('SELECT 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 AND f.customer_id = ?', params[:customer_id])
  357. end
  358. else
  359. @cracked_results = HVDB.fetch('SELECT plaintext FROM hashes WHERE cracked = 1')
  360. end
  361. @cracked_results.each do |crack|
  362. unless crack[:plaintext].nil?
  363. unless crack[:plaintext].empty?
  364. len = crack[:plaintext].length
  365. @passwords[len] = @passwords[len].nil? ? 1 : @passwords[len].to_i + 1
  366. end
  367. end
  368. end
  369. # Sort on key
  370. @passwords = @passwords.sort.to_h
  371. # convert to array of json objects for d3
  372. @passwords.each do |key, value|
  373. @counts << { length: key, count: value }
  374. end
  375. return @counts.to_json
  376. end
  377. # callback for d3 graph displaying top 10 passwords
  378. get '/analytics/Top10Passwords' do
  379. varWash(params)
  380. # 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;
  381. plaintext = []
  382. if params[:customer_id] && !params[:customer_id].empty?
  383. if params[:hashfile_id] && !params[:hashfile_id].empty?
  384. @cracked_results = HVDB.fetch('SELECT h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE h.cracked = 1 AND a.hashfile_id = ?', params[:hashfile_id])
  385. else
  386. @cracked_results = HVDB.fetch('SELECT 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 AND f.customer_id = ?', params[:customer_id])
  387. end
  388. else
  389. @cracked_results = HVDB.fetch('SELECT h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE h.cracked = 1')
  390. end
  391. @cracked_results.each do |crack|
  392. unless crack[:plaintext].nil?
  393. plaintext << crack[:plaintext] unless crack[:plaintext].empty?
  394. end
  395. end
  396. @toppasswords = []
  397. @top10passwords = {}
  398. # get top 10 passwords
  399. plaintext.each do |pass|
  400. if @top10passwords[pass].nil?
  401. @top10passwords[pass] = 1
  402. else
  403. @top10passwords[pass] += 1
  404. end
  405. end
  406. # sort and convert to array of json objects for d3
  407. @top10passwords = @top10passwords.sort_by { |_key, value| value }.reverse.to_h
  408. # we only need top 10
  409. @top10passwords = Hash[@top10passwords.sort_by { |_k, v| -v }[0..9]]
  410. # convert to array of json objects for d3
  411. @top10passwords.each do |key, value|
  412. @toppasswords << { password: key, count: value }
  413. end
  414. return @toppasswords.to_json
  415. end
  416. # callback for d3 graph displaying top 10 base words
  417. get '/analytics/Top10BaseWords' do
  418. varWash(params)
  419. plaintext = []
  420. if params[:customer_id] && !params[:customer_id].empty?
  421. if params[:hashfile_id] && !params[:hashfile_id].empty?
  422. @cracked_results = HVDB.fetch('SELECT h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE h.cracked = 1 AND a.hashfile_id = ?', params[:hashfile_id])
  423. else
  424. @cracked_results = HVDB.fetch('SELECT 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 AND f.customer_id = ?', params[:customer_id])
  425. end
  426. else
  427. @cracked_results = HVDB.fetch('SELECT h.plaintext FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE h.cracked = 1')
  428. end
  429. @cracked_results.each do |crack|
  430. unless crack[:plaintext].nil?
  431. plaintext << crack[:plaintext] unless crack[:plaintext].empty?
  432. end
  433. end
  434. @topbasewords = []
  435. @top10basewords = {}
  436. # get top 10 basewords
  437. plaintext.each do |pass|
  438. word_just_alpha = pass.gsub(/^[^a-z]*/i, '').gsub(/[^a-z]*$/i, '')
  439. unless word_just_alpha.nil? || word_just_alpha.empty?
  440. if @top10basewords[word_just_alpha].nil?
  441. @top10basewords[word_just_alpha] = 1
  442. else
  443. @top10basewords[word_just_alpha] += 1
  444. end
  445. end
  446. end
  447. # sort and convert to array of json objects for d3
  448. @top10basewords = @top10basewords.sort_by { |_key, value| value }.reverse.to_h
  449. # we only need top 10
  450. @top10basewords = Hash[@top10basewords.sort_by { |_k, v| -v }[0..9]]
  451. # convert to array of json objects for d3
  452. @top10basewords.each do |key, value|
  453. @topbasewords << { password: key, count: value }
  454. end
  455. return @topbasewords.to_json
  456. end
  457. # callback for Accounts with Weak Passwords
  458. get '/analytics/AccountsWithWeakPasswords' do
  459. varWash(params)
  460. # TODO
  461. # complexity hashes and cracked pw count should be from a single query, not multiple.
  462. if params[:customer_id] && !params[:customer_id].empty?
  463. # if we have a hashfile
  464. if params[:hashfile_id] && !params[:hashfile_id].empty?
  465. # Used for Complexity Breakdown doughnut: Customer: Hashfile
  466. @complexity_hashes = HVDB.fetch('SELECT a.username as username, h.plaintext as 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])
  467. @cracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (a.hashfile_id = ? AND h.cracked = 1)', params[:hashfile_id])[:count]
  468. @cracked_pw_count = @cracked_pw_count[:count]
  469. else
  470. # Used for Complexity Breakdown doughnut: Customer
  471. @complexity_hashes = HVDB.fetch('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])
  472. @cracked_pw_count = HVDB.fetch('SELECT count(h.plaintext) as count 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])[:count]
  473. @cracked_pw_count = @cracked_pw_count[:count]
  474. end
  475. else
  476. # Used for Complexity Breakdown Doughnut: Total
  477. @complexity_hashes = HVDB.fetch('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)')
  478. @cracked_pw_count = HVDB.fetch('SELECT COUNT(h.originalhash) as count FROM hashes h LEFT JOIN hashfilehashes a ON h.id = a.hash_id WHERE (h.cracked = 1)')[:count]
  479. @cracked_pw_count = @cracked_pw_count[:count]
  480. end
  481. @meets_complexity_count = 0
  482. @fails_complexity_count = 0
  483. @fails_complexity = {}
  484. @complexity_hashes.each do |entry|
  485. if entry[:plaintext].to_s.length < 6
  486. @fails_complexity[entry[:username]] = entry[:plaintext]
  487. else
  488. flags = 0
  489. flags += 1 if entry[:plaintext].to_s =~ /[a-z]/
  490. flags += 1 if entry[:plaintext].to_s =~ /[A-Z]/
  491. flags += 1 if entry[:plaintext].to_s =~ /\d/
  492. flags += 1 if entry[:plaintext].to_s.force_encoding('UTF-8') =~ /[^0-9A-Za-z]/u
  493. @fails_complexity[entry[:username]] = entry[:plaintext] if flags < 3
  494. end
  495. @fails_complexity_count = @fails_complexity.length
  496. @meets_complexity_count = @cracked_pw_count.to_i - @fails_complexity_count.to_i
  497. end
  498. mass = []
  499. content = []
  500. content << { 'label':'Fails Complexity', 'value':'5' }
  501. content << { 'label':'Meets Complexity', 'value':'10' }
  502. data = {"content": content}
  503. mass << data
  504. return content.to_json
  505. end