Module: Rex::Parser::WinSCP

Defined in:
lib/rex/parser/winscp.rb

Constant Summary collapse

PWDALG_SIMPLE_MAGIC =
0xA3
PWDALG_SIMPLE_FLAG =
0xFF

Instance Method Summary collapse

Instance Method Details

#decrypt_next_char(pwd) ⇒ Object

Decrypts the next character in the password sequence


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/rex/parser/winscp.rb', line 63

def decrypt_next_char(pwd)
  if pwd.nil? || pwd.length <= 0
    return 0, pwd
  end

  # Takes the first char from the encrypted password and then left shifts the returned index by 4 bits
  a = pwd[0].hex << 4

  # Takes the second char from the encrypted password
  b = pwd[1].hex

  # Adds the two results, XORs against 0xA3, NOTs it and then ANDs it with 0xFF
  result = ~((a + b) ^ PWDALG_SIMPLE_MAGIC) & PWDALG_SIMPLE_FLAG

  # Strips the first two chars off and returns our result
  return result, pwd[2..-1]
end

#decrypt_password(pwd, key) ⇒ Object


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
# File 'lib/rex/parser/winscp.rb', line 81

def decrypt_password(pwd, key)
  flag, pwd = decrypt_next_char(pwd)

  if flag == PWDALG_SIMPLE_FLAG
    _, pwd = decrypt_next_char(pwd)
    length, pwd = decrypt_next_char(pwd)
  else
    length = flag
  end

  del, pwd = decrypt_next_char(pwd)
  pwd = pwd[del*2..-1]

  result = ""
  length.times do
    r, pwd = decrypt_next_char(pwd)
    result << r.chr
  end

  if flag == PWDALG_SIMPLE_FLAG
    result = result[key.length..-1]
  end

  result
end

#parse_ini(file) ⇒ Object

Raises:


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
# File 'lib/rex/parser/winscp.rb', line 26

def parse_ini(file)
  results = []
  raise RuntimeError, 'No data to parse' if file.nil? || file.empty?

  ini = Rex::Parser::Ini.from_s(file)

  if ini['Configuration\\Security']
    # if a Master Password is in use we give up
    if ini['Configuration\\Security']['UseMasterPassword'].to_i == 1
      raise RuntimeError, 'Master Password Set, unable to recover saved passwords!'
    end
  end

  # Runs through each group in the ini file looking for all of the Sessions
  ini.each_key do |group|
    if group.include?('Sessions') && ini[group].has_key?('Password')
      # Decrypt our password, and report on results
      encrypted_password = ini[group]['Password']
      user = ini[group]['UserName']
      host = ini[group]['HostName']
      sname = parse_protocol(ini[group]['FSProtocol'].to_i)
      plaintext = decrypt_password(encrypted_password, "#{user}#{host}")

      results << {
        hostname: host,
        password: plaintext,
        portnumber: ini[group]['PortNumber'] || 22,
        username: user,
        protocol: sname
      }
    end
  end

  results
end

#parse_protocol(fsprotocol) ⇒ Object


15
16
17
18
19
20
21
22
23
24
# File 'lib/rex/parser/winscp.rb', line 15

def parse_protocol(fsprotocol)
  return 'Unknown' if fsprotocol.nil?

  case fsprotocol
  when 5 then 'FTP'
  when 0 then 'SSH'
  else
    'Unknown'
  end
end

#read_and_parse_ini(filename) ⇒ Object


9
10
11
12
13
# File 'lib/rex/parser/winscp.rb', line 9

def read_and_parse_ini(filename)
  file = File.read(filename)
  return if file.to_s.empty?
  parse_ini(file)
end