-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvncpasswd.py
More file actions
233 lines (203 loc) · 9.45 KB
/
Copy pathvncpasswd.py
File metadata and controls
233 lines (203 loc) · 9.45 KB
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
#!/usr/bin/env python2
"""vncpasswd.py: Python implementation of vncpasswd, w/decryption abilities & extra features ;-)"""
from __future__ import print_function
__author__ = "James Cuzella"
__copyright__ = "Copyright 2012-2020, James Cuzella"
__credits__ = [ 'Yusuke Shinyama', 'Richard Outerbridge', 'Dan Hoey', 'Jim Gillogly', 'Phil Karn' ]
__license__ = "MIT"
__version__ = "1.2.3"
__maintainer__ = "James Cuzella"
import sys
import argparse
import platform
#from struct import pack, unpack
from d3des import d3des as d
if platform.system().startswith('Windows'): from WindowsRegistry import WindowsRegistry as wreg
DEBUG = False
def eprint(*args, **kwargs):
if DEBUG:
_file = sys.stdout
else:
_file = sys.stderr
print(*args, file=_file, **kwargs)
def split_len(seq, length):
return [seq[i:i+length] for i in range(0, len(seq), length)]
def do_crypt(password, decrypt):
passpadd = (password + '\x00'*8)[:8]
strkey = ''.join([ chr(x) for x in d.vnckey ])
key = d.deskey(strkey, decrypt)
crypted = d.desfunc(passpadd, key)
return crypted
def do_file_in(filename, inhex):
f = open(filename, 'r')
data = f.read()
f.close()
if ( inhex ):
data = data.strip()
data = unhex(data)
return data
def do_file_out(filename, data, inhex):
f = open(filename, 'w')
if ( inhex ):
data = data.encode('hex')
f.write(data)
f.close()
def unhex(s):
"""
Decodes a string of hex characters
Return: This method returns an decoded version of the string.
If a hexidecimal string with odd length is passed, the last character is chopped off and the decoded version of this is returned.
Example:
>>> unhex("48656c6c6f20576f726c64")
'Hello World'
>>> unhex("48656c6c6f20576f726c6")
WARN: Odd-length string . Chopping last char off... "48656c6c6f20576f726c"
'Hello Worl'
>>> unhex('303132333435363738396162636465666768696a6b6c6d6e6f707172737475767778797a4142434445464748494a4b4c4d4e4f505152535455565758595a2122232425262728292a2b2c2d2e2f3a3b3c3d3e3f405b5c5d5e5f607b7c7d7e20090a0d0b0c')
'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~ \\t\\n\\r\\x0b\\x0c'
>>> unhex('000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F')
'\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f'
>>> unhex('abcdefghijklmnop')
Traceback (most recent call last):
File "/usr/lib/python2.7/doctest.py", line 1289, in __run
compileflags, 1) in test.globs
File "<doctest __main__.unhex[2]>", line 1, in <module>
unhex('abcdefghijklmnop')
File "./vncpasswd.py", line 51, in unhex
s = s.decode('hex')
File "/usr/lib/python2.7/encodings/hex_codec.py", line 42, in hex_decode
output = binascii.a2b_hex(input)
TypeError: Non-hexadecimal digit found
"""
try:
s = s.decode('hex')
except TypeError as e:
if e.message == 'Odd-length string':
eprint('WARN: %s . Chopping last char off... "%s"' % ( e.message, s[:-1] ))
s = s[:-1].decode('hex')
else:
raise
return s
def run_tests(verbose=False):
global DEBUG
DEBUG = True
print("Running Unit Tests...")
import doctest
import __main__
(failure_count, test_count) = doctest.testmod(None, None, None, verbose, True)
pass_count = test_count - failure_count
methods = dir(__main__)
ignore_methods = ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__warningregistry__', 'argparse', 'sys' ]
methods = [i for i in methods if not i in ignore_methods or ignore_methods.remove(i)]
print('%d tests in %s items.' % ( test_count, len(methods) ))
if failure_count > 0:
print('%d out of %d tests failed' % (failure_count, test_count))
else:
print('%d passed and %d failed.' % ( pass_count, failure_count ))
print('Test passed.')
sys.exit(failure_count)
def main():
parser = argparse.ArgumentParser(description='Encrypt or Decrypt a VNC password')
parser.add_argument("-d", "--decrypt", dest="decrypt", action="store_true", default=False, \
help="Decrypt an obfuscated password.")
parser.add_argument("-e", "--encrypt", dest="decrypt", action="store_false", default=False, \
help="Encrypt a plaintext password. (default mode)")
parser.add_argument("-H", "--hex", dest="hex", action="store_true", default=False, \
help="Assume input is in hex.")
parser.add_argument("-R", "--registry", dest="registry", action="store_true", default=False, \
help="Input or Output to the windows registry.")
parser.add_argument("-o", "--stdout", dest="stdout", action="store_true", default=False, \
help="Input or Output only the resulting value to STDOUT. Always output ciphertext in hexidecimal, and plaintext in ASCII / UTF-8. A newline is appended to the value. Useful for scripting.")
parser.add_argument("-f", "--file", dest="filename", \
help="Input or Output to a specified file.")
parser.add_argument("passwd", nargs='?', \
help="A password to encrypt")
parser.add_argument("-t", "--test", dest="test", action="store_true", default=False, \
help="Run the unit tests for this program.")
args = parser.parse_args()
if (args.test):
run_tests()
# Handle _key_type checks on all platforms
_key_type = None
if platform.system().startswith('Windows'):
_reg_sz_type = wreg.WindowsRegistry.REG_SZ
else:
_reg_sz_type = 1
if ( args.filename == None and args.passwd == None and (args.registry == False or not platform.system().startswith('Windows')) ):
parser.error('Error: No password file or password passed\n')
if ( args.registry and args.decrypt and platform.system().startswith('Windows')):
reg = get_realvnc_key()
if reg != None:
#pylint: disable=unused-variable
( args.passwd, _key_type) = reg.getval("Password")
else:
eprint('ERROR: Cannot find VNC registry key to read the password from')
eprint('Are you sure that you have RealVNC / WinVNC4 installed?')
elif ( args.registry and not platform.system().startswith('Windows') ):
eprint('Cannot read from Windows Registry on a %s system' % platform.system())
if ( args.passwd != None and (args.hex or _key_type == _reg_sz_type)):
args.passwd = unhex(args.passwd)
if ( args.filename != None and args.decrypt ):
args.passwd = do_file_in(args.filename, args.hex)
# If the hex encoded passwd length is longer than 16 hex chars and divisible
# by 16, then we chop the passwd into blocks of 64 bits (16 hex chars)
# (1 hex char = 4 binary bits = 1 nibble)
if ( args.passwd != None):
hexpasswd = args.passwd.encode('hex')
else:
eprint('ERROR: No password available to encode / decode!')
sys.exit(1)
if ( len(hexpasswd) > 16 and (len(hexpasswd) % 16) == 0 ):
eprint('INFO: Detected ciphertext > 64 bits... breaking into blocks to decrypt...')
splitstr = split_len(args.passwd.encode('hex'), 16)
eprint('INFO: Split blocks = %s' % splitstr)
cryptedblocks = []
for sblock in splitstr:
cryptedblocks.append( do_crypt(sblock.decode('hex'), args.decrypt) )
#eprint('%016s\t%s' % ( sblock, cryptedblocks ))
crypted = ''.join(cryptedblocks)
elif ( len(hexpasswd) <= 16):
crypted = do_crypt(args.passwd, args.decrypt)
# print("crypted 1: ",crypted)
else:
if ( args.decrypt and not args.stdout ):
eprint('WARN: Ciphertext length was not divisible by 8 (hex/16).')
eprint('Length: %d' % len(args.passwd))
eprint('Hex Length: %d' % len(hexpasswd))
crypted = do_crypt(args.passwd, args.decrypt)
print("args.decrypt: ", args.decrypt)
print("crypted: ", crypted)
if ( args.filename != None and not args.decrypt ):
do_file_out(args.filename, crypted, args.hex)
if ( args.registry and not args.decrypt and platform.system().startswith('Windows')):
reg = get_realvnc_key()
if reg != None:
reg.setval('Password', crypted, wreg.WindowsRegistry.REG_BINARY)
else:
eprint('ERROR: Cannot find VNC registry key to store the password into')
eprint('Are you sure that you have RealVNC / WinVNC4 installed?')
elif ( args.registry and not platform.system().startswith('Windows') ):
eprint('WARN: Cannot write to Windows Registry on a %s system' % platform.system())
prefix = ('En','De')[args.decrypt == True]
if ( args.stdout ):
if ( args.decrypt == True ):
print("%s" % ( crypted ))
else:
print("%s" % ( crypted.encode('hex') ))
else:
print("%scrypted Bin Pass= '%s'" % ( prefix, crypted ))
print("%scrypted Hex Pass= '%s'" % ( prefix, crypted.encode('hex') ))
def get_realvnc_key():
reg = None
for k in ['vncserver', 'WinVNC4',]:
try:
reg = wreg.WindowsRegistry('RealVNC', k)
break
except WindowsError as e:
if 'The system cannot find the file specified' in str(e):
pass
else:
raise e
return reg
if __name__ == '__main__':
main()