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/lib/rex/parser/winscp.rb
Views: 1904
1
2
module Rex
3
module Parser
4
module WinSCP
5
PWDALG_SIMPLE_MAGIC = 0xA3
6
PWDALG_SIMPLE_FLAG = 0xFF
7
8
def read_and_parse_ini(filename)
9
file = File.read(filename)
10
return if file.to_s.empty?
11
parse_ini(file)
12
end
13
14
def parse_protocol(fsprotocol)
15
return 'Unknown' if fsprotocol.nil?
16
17
case fsprotocol
18
when 5 then 'FTP'
19
when 0 then 'SSH'
20
else
21
'Unknown'
22
end
23
end
24
25
def parse_ini(file)
26
results = []
27
raise RuntimeError, 'No data to parse' if file.nil? || file.empty?
28
29
ini = Rex::Parser::Ini.from_s(file)
30
31
if ini['Configuration\\Security']
32
# if a Master Password is in use we give up
33
if ini['Configuration\\Security']['UseMasterPassword'].to_i == 1
34
raise RuntimeError, 'Master Password Set, unable to recover saved passwords!'
35
end
36
end
37
38
# Runs through each group in the ini file looking for all of the Sessions
39
ini.each_key do |group|
40
if group.include?('Sessions') && ini[group].has_key?('Password')
41
# Decrypt our password, and report on results
42
encrypted_password = ini[group]['Password']
43
user = ini[group]['UserName']
44
host = ini[group]['HostName']
45
sname = parse_protocol(ini[group]['FSProtocol'].to_i)
46
plaintext = decrypt_password(encrypted_password, "#{user}#{host}")
47
48
results << {
49
hostname: host,
50
password: plaintext,
51
portnumber: ini[group]['PortNumber'] || 22,
52
username: user,
53
protocol: sname
54
}
55
end
56
end
57
58
results
59
end
60
61
# Decrypts the next character in the password sequence
62
def decrypt_next_char(pwd)
63
if pwd.nil? || pwd.length <= 0
64
return 0, pwd
65
end
66
67
# Takes the first char from the encrypted password and then left shifts the returned index by 4 bits
68
a = pwd[0].hex << 4
69
70
# Takes the second char from the encrypted password
71
b = pwd[1].hex
72
73
# Adds the two results, XORs against 0xA3, NOTs it and then ANDs it with 0xFF
74
result = ~((a + b) ^ PWDALG_SIMPLE_MAGIC) & PWDALG_SIMPLE_FLAG
75
76
# Strips the first two chars off and returns our result
77
return result, pwd[2..-1]
78
end
79
80
def decrypt_password(pwd, key)
81
flag, pwd = decrypt_next_char(pwd)
82
83
if flag == PWDALG_SIMPLE_FLAG
84
_, pwd = decrypt_next_char(pwd)
85
length, pwd = decrypt_next_char(pwd)
86
else
87
length = flag
88
end
89
90
del, pwd = decrypt_next_char(pwd)
91
pwd = pwd[del*2..-1]
92
93
result = ""
94
length.times do
95
r, pwd = decrypt_next_char(pwd)
96
result << r.chr
97
end
98
99
if flag == PWDALG_SIMPLE_FLAG
100
result = result[key.length..-1]
101
end
102
103
result
104
end
105
end
106
end
107
end
108
109