Skip to content

Commit 2cd6b1c

Browse files
committed
Update parser, fix UseMasterPassword bug
1 parent 8e1a0e0 commit 2cd6b1c

File tree

1 file changed

+35
-33
lines changed

1 file changed

+35
-33
lines changed

lib/rex/parser/winscp.rb

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33
module Rex
44
module Parser
55
module WinSCP
6+
PWDALG_SIMPLE_MAGIC = 0xA3
7+
PWDALG_SIMPLE_FLAG = 0xFF
8+
69
def read_and_parse_ini(filename)
710
file = File.read(filename)
811
return if file.to_s.empty?
912
parse_ini(file)
1013
end
1114

1215
def parse_protocol(fsprotocol)
13-
case fsprotocol.to_i
16+
return 'Unknown' if fsprotocol.nil?
17+
18+
case fsprotocol
1419
when 5 then 'FTP'
1520
when 0 then 'SSH'
1621
else
@@ -26,7 +31,7 @@ def parse_ini(file)
2631

2732
if ini['Configuration\\Security']
2833
# if a Master Password is in use we give up
29-
if ini['Configuration\\Security']['MasterPassword'].to_i == 1
34+
if ini['Configuration\\Security']['UseMasterPassword'].to_i == 1
3035
raise RuntimeError, 'Master Password Set, unable to recover saved passwords!'
3136
end
3237
end
@@ -38,7 +43,7 @@ def parse_ini(file)
3843
encrypted_password = ini[group]['Password']
3944
user = ini[group]['UserName']
4045
host = ini[group]['HostName']
41-
sname = parse_protocol(ini[group]['FSProtocol'])
46+
sname = parse_protocol(ini[group]['FSProtocol'].to_i)
4247
plaintext = decrypt_password(encrypted_password, "#{user}#{host}")
4348

4449
results << {
@@ -54,49 +59,46 @@ def parse_ini(file)
5459
results
5560
end
5661

57-
def decrypt_next_char
58-
pwalg_simple_magic = 0xA3
59-
pwalg_simple_string = "0123456789ABCDEF"
60-
61-
# Decrypts the next character in the password sequence
62-
if @password.length > 0
63-
# Takes the first char from the encrypted password and finds its position in the
64-
# pre-defined string, then left shifts the returned index by 4 bits
65-
unpack1 = pwalg_simple_string.index(@password[0,1])
66-
unpack1 = unpack1 << 4
67-
68-
# Takes the second char from the encrypted password and finds its position in the
69-
# pre-defined string
70-
unpack2 = pwalg_simple_string.index(@password[1,1])
71-
# Adds the two results, XORs against 0xA3, NOTs it and then ands it with 0xFF
72-
result= ~((unpack1+unpack2) ^ pwalg_simple_magic) & 0xff
73-
# Strips the first two chars off and returns our result
74-
@password = @password[2,@password.length]
75-
return result
62+
# Decrypts the next character in the password sequence
63+
def decrypt_next_char(pwd)
64+
if pwd.nil? || pwd.length <= 0
65+
return 0, pwd
7666
end
67+
68+
# Takes the first char from the encrypted password and then left shifts the returned index by 4 bits
69+
a = pwd[0].hex << 4
70+
71+
# Takes the second char from the encrypted password
72+
b = pwd[1].hex
73+
74+
# Adds the two results, XORs against 0xA3, NOTs it and then ANDs it with 0xFF
75+
result = ~((a + b) ^ PWDALG_SIMPLE_MAGIC) & PWDALG_SIMPLE_FLAG
76+
77+
# Strips the first two chars off and returns our result
78+
return result, pwd[2..-1]
7779
end
7880

7981
def decrypt_password(pwd, key)
80-
pwalg_simple_flag = 0xFF
81-
@password = pwd
82-
flag = decrypt_next_char()
82+
flag, pwd = decrypt_next_char(pwd)
8383

84-
if flag == pwalg_simple_flag
85-
decrypt_next_char()
86-
length = decrypt_next_char()
84+
if flag == PWDALG_SIMPLE_FLAG
85+
_, pwd = decrypt_next_char(pwd)
86+
length, pwd = decrypt_next_char(pwd)
8787
else
8888
length = flag
8989
end
90-
ldel = (decrypt_next_char())*2
91-
@password = @password[ldel,@password.length]
90+
91+
del, pwd = decrypt_next_char(pwd)
92+
pwd = pwd[del*2..-1]
9293

9394
result = ""
9495
length.times do
95-
result << decrypt_next_char().chr
96+
r, pwd = decrypt_next_char(pwd)
97+
result << r.chr
9698
end
9799

98-
if flag == pwalg_simple_flag
99-
result = result[key.length, result.length]
100+
if flag == PWDALG_SIMPLE_FLAG
101+
result = result[key.length..-1]
100102
end
101103

102104
result

0 commit comments

Comments
 (0)