forked from digininja/pipal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
passpat.rb
executable file
·279 lines (235 loc) · 6.4 KB
/
passpat.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
#!/usr/bin/env ruby
# encoding: utf-8
#
# Author:: Robin Wood (robin@digi.ninja)
# Copyright:: Copyright (c) Robin Wood 2013
# Licence:: Creative Commons Attribution-Share Alike 2.0
#
require 'getoptlong'
def usage
puts"passpat 1.0 Robin Wood (robin@digi.ninja) (http://digi.ninja)
Usage: passpat.rb [OPTIONS] ... PASSWORD_FILE
--layout x, -l x: use the layout file specified. No default is set so this
must be specified
--list-layouts: show the available layout files
--help, -h: show help
--verbose, -v: verbose messages
PASSWORD_FILE: the list of passwords to check
The results are scored from 0 to upwards. A score of 0 means that a the
password didn't move from a single key, a score of 1 means that each key entered
was adjacent to the previous. The higher the score above 1, the lower the
grouping of the keys.
Based on this, the closer to 1 the score is, the more likely the password is
to be a keyboard pattern.
Note though, this will not pick up a pattern such as \"qpalzm\" as the system isn't
that smart (yet).
This project is sponsored by the BruCON 5x5 scheme.
"
end
def list_layouts
puts"passpat 1.0 Robin Wood (robin@digi.ninja) (http://digi.ninja)"
puts
puts "Available layouts:"
puts
# This gives the directory that passpat is running from
script_directory = File.dirname(__FILE__)
layouts = Dir[script_directory + '/layouts/*'].reject do |fn| File.directory?(fn) end
layouts.each do |layout|
layout_name = layout.match(/#{script_directory + '/layouts/'}(.*)\.rb$/)
if !layout_name.nil?
puts layout_name[1]
end
end
puts
end
opts = GetoptLong.new(
[ '--help', '-h', "-?", GetoptLong::NO_ARGUMENT ],
[ '--layout', "-l" , GetoptLong::REQUIRED_ARGUMENT ],
[ '--list-layouts', GetoptLong::NO_ARGUMENT ],
[ "--verbose", "-v" , GetoptLong::NO_ARGUMENT ]
)
verbose = false
begin
opts.each do |opt, arg|
case opt
when '--verbose'
verbose = true
when '--help'
usage
exit 0
when "--list-layouts"
list_layouts
exit 0
when "--layout"
# This gives the directory that passpat is running from
script_directory = File.dirname(__FILE__)
puts script_directory + "/layouts/" + arg + ".rb"
# Yes, directory traversal is here but don't care
if File.exist?(script_directory + "/layouts/" + arg + ".rb")
require_relative "layouts/" + arg + ".rb"
else
puts"passpat 1.0 Robin Wood (robin@digi.ninja) (http://digi.ninja)
Layout file not found
"
exit 1
end
end
end
rescue GetoptLong::InvalidOption => e
puts
usage
exit
rescue => e
puts "Something went wrong, please report it to robin@digi.ninja along with these messages:"
puts
puts e.message
puts
puts e.class.to_s
puts
puts "Backtrace:"
puts e.backtrace
puts
usage
exit 1
end
if $layout.nil?
puts"passpat 1.0 Robin Wood (robin@digi.ninja) (http://digi.ninja)
No layout file specified
"
exit 1
end
keyboard = $layout
if verbose
puts "Using layout file: " + $layout_description
puts
end
if ARGV.count < 1
puts"passpat 1.0 Robin Wood (robin@digi.ninja) (http://digi.ninja)
No password file specified
"
exit 1
end
filename = ARGV.shift
if filename.nil? or !File.exist? filename
puts"passpat 1.0 Robin Wood (robin@digi.ninja) (http://digi.ninja)
Can't find the password file
"
exit 1
end
total_lines = 0
total_score = 0
total_zeros = 0
total_ones = 0
catch :ctrl_c do
begin
File.open(filename, "r").each_line do |line|
begin
# because the password processed in the loop is lower case
# and has the first character stripped off it
original_password = line.strip
if original_password == ""
next
end
# Don't care about case
pass = original_password.downcase
if verbose
puts "Checking password: #{pass}"
puts
end
score = 0
# take the first character and store it
last_char = pass[0]
# remove the first character
pass = pass[1 .. -1]
pass.each_char do |c|
if c == last_char
# just to indicate what is actually happening
# the characters are the same so the score
# stays the same
score += 0
else
if keyboard.has_key?(c)
found = false
puts "Moving from #{last_char} to #{c}" if verbose
keyboard[c].each do | diff, chars|
puts "\tCharacters #{diff} keys away: #{chars}" if verbose
if chars.count(last_char) > 0
# The character is next to the last one
score += diff
puts "\tCharacter found at score #{diff}" if verbose
found = true
break
end
end
if !found
puts "\tNext key not found so using default score #{MAX_SCORE}" if verbose
score += MAX_SCORE
end
else
puts "Character not found in mapping: #{c} so scoring default #{MAX_SCORE}" if verbose
score += MAX_SCORE
end
end
last_char = c
puts "Current total score #{score}" if verbose
end
puts "Password: #{original_password}"
puts "Total score = #{score.to_s}"
puts "Number of moves = #{pass.length.to_s}"
# stops divide by zero problems
if pass.length == 0
puts "Pattern score = 0 out of " + MAX_SCORE.to_s
total_zeros += 1
else
avg_score = (score.to_f / pass.length)
puts "Pattern score = #{avg_score.to_s} out of " + MAX_SCORE.to_s
total_score += avg_score
if avg_score == 0
total_zeros += 1
elsif avg_score == 1
total_ones += 1
end
end
puts
total_lines += 1
rescue ArgumentError => e
puts "Encoding problem processing word: " + line
rescue => e
puts "Something went wrong, please report it to robin@digi.ninja along with these messages:"
puts
puts e.message
puts
puts e.class.to_s
puts
puts "Backtrace:"
puts e.backtrace
puts
usage
exit 1
end
end
puts "Total passwords processed: #{total_lines.to_s}"
if total_lines > 0
puts "Overall pattern score #{(total_score.to_f / total_lines).to_s} out of #{MAX_SCORE}"
end
puts "Total length zeros found: #{total_zeros.to_s}"
puts "Total length ones found: #{total_ones.to_s}"
rescue Errno::EACCES => e
puts"passpat 1.0 Robin Wood (robin@digi.ninja) (http://digi.ninja)
Unable to open the password file
"
exit 1
rescue => e
puts "Something went wrong, please report it to robin@digi.ninja along with these messages:"
puts
puts e.message
puts
puts e.class.to_s
puts
puts "Backtrace:"
puts e.backtrace
puts
usage
exit 1
end
end