PageRenderTime 40ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/vncpasswd.py

https://github.com/trinitronx/vncpasswd.py
Python | 172 lines | 163 code | 6 blank | 3 comment | 5 complexity | e5d6dae06447b8828b86a897f6fbeb77 MD5 | raw file
  1. #!/usr/bin/env python
  2. """vncpasswd.py: Python implementation of vncpasswd, w/decryption abilities & extra features ;-)"""
  3. __author__ = "James Cuzella"
  4. __copyright__ = "Copyright 2012,2013, James Cuzella"
  5. __credits__ = [ 'Yusuke Shinyama', 'Richard Outerbridge', 'Dan Hoey', 'Jim Gillogly', 'Phil Karn' ]
  6. __license__ = "MIT"
  7. __version__ = "0.0.2"
  8. __maintainer__ = "James Cuzella"
  9. import sys
  10. import argparse
  11. import platform
  12. #from struct import pack, unpack
  13. import d3des as d
  14. if platform.system().startswith('Windows'): import WindowsRegistry as wreg
  15. def split_len(seq, length):
  16. return [seq[i:i+length] for i in range(0, len(seq), length)]
  17. def do_crypt(password, decrypt):
  18. passpadd = (password + '\x00'*8)[:8]
  19. strkey = ''.join([ chr(x) for x in d.vnckey ])
  20. key = d.deskey(strkey, decrypt)
  21. crypted = d.desfunc(passpadd, key)
  22. return crypted
  23. def do_file_in(filename, inhex):
  24. f = open(filename, 'r')
  25. data = f.read()
  26. f.close()
  27. if ( inhex ):
  28. data = data.strip()
  29. data = unhex(data)
  30. return data
  31. def do_file_out(filename, data, inhex):
  32. f = open(filename, 'w')
  33. if ( inhex ):
  34. data = data.encode('hex')
  35. f.write(data)
  36. f.close()
  37. def unhex(s):
  38. """
  39. Decodes a string of hex characters
  40. Return: This method returns an decoded version of the string.
  41. If a hexidecimal string with odd length is passed, the last character is chopped off and the decoded version of this is returned.
  42. Example:
  43. >>> unhex("48656c6c6f20576f726c64")
  44. 'Hello World'
  45. >>> unhex("48656c6c6f20576f726c6")
  46. WARN: Odd-length string . Chopping last char off... "48656c6c6f20576f726c"
  47. 'Hello Worl'
  48. >>> unhex('303132333435363738396162636465666768696a6b6c6d6e6f707172737475767778797a4142434445464748494a4b4c4d4e4f505152535455565758595a2122232425262728292a2b2c2d2e2f3a3b3c3d3e3f405b5c5d5e5f607b7c7d7e20090a0d0b0c')
  49. '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\\'()*+,-./:;<=>?@[\\\\]^_`{|}~ \\t\\n\\r\\x0b\\x0c'
  50. >>> unhex('000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F')
  51. '\\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'
  52. >>> unhex('abcdefghijklmnop')
  53. Traceback (most recent call last):
  54. File "/usr/lib/python2.7/doctest.py", line 1289, in __run
  55. compileflags, 1) in test.globs
  56. File "<doctest __main__.unhex[2]>", line 1, in <module>
  57. unhex('abcdefghijklmnop')
  58. File "./vncpasswd.py", line 51, in unhex
  59. s = s.decode('hex')
  60. File "/usr/lib/python2.7/encodings/hex_codec.py", line 42, in hex_decode
  61. output = binascii.a2b_hex(input)
  62. TypeError: Non-hexadecimal digit found
  63. """
  64. try:
  65. s = s.decode('hex')
  66. except TypeError as e:
  67. if e.message == 'Odd-length string':
  68. print 'WARN: %s . Chopping last char off... "%s"' % ( e.message, s[:-1] )
  69. s = s[:-1].decode('hex')
  70. else:
  71. raise
  72. return s
  73. def run_tests(verbose=False):
  74. print "Running Unit Tests..."
  75. import doctest
  76. import __main__
  77. (failure_count, test_count) = doctest.testmod(None, None, None, verbose, True)
  78. pass_count = test_count - failure_count
  79. methods = dir(__main__)
  80. ignore_methods = ['__builtins__', '__doc__', '__file__', '__name__', '__package__', '__warningregistry__', 'argparse', 'sys' ]
  81. methods = [i for i in methods if not i in ignore_methods or ignore_methods.remove(i)]
  82. print '%d tests in %s items.' % ( test_count, len(methods) )
  83. if failure_count > 0:
  84. print '%d out of %d tests failed' % (failure_count, test_count)
  85. else:
  86. print '%d passed and %d failed.' % ( pass_count, failure_count )
  87. print 'Test passed.'
  88. sys.exit(failure_count)
  89. def main():
  90. parser = argparse.ArgumentParser(description='Encrypt or Decrypt a VNC password')
  91. parser.add_argument("-d", "--decrypt", dest="decrypt", action="store_true", default=False, \
  92. help="Decrypt an obfuscated password.")
  93. parser.add_argument("-e", "--encrypt", dest="decrypt", action="store_false", default=False, \
  94. help="Encrypt a plaintext password. (default mode)")
  95. parser.add_argument("-H", "--hex", dest="hex", action="store_true", default=False, \
  96. help="Assume input is in hex.")
  97. parser.add_argument("-R", "--registry", dest="registry", action="store_true", default=False, \
  98. help="Input or Output to the windows registry.")
  99. parser.add_argument("-f", "--file", dest="filename", \
  100. help="Input or Output to a specified file.")
  101. parser.add_argument("passwd", nargs='?', \
  102. help="A password to encrypt")
  103. parser.add_argument("-t", "--test", dest="test", action="store_true", default=False, \
  104. help="Run the unit tests for this program.")
  105. args = parser.parse_args()
  106. if (args.test):
  107. run_tests()
  108. if ( args.filename == None and args.passwd == None and (args.registry == False or not platform.system().startswith('Windows')) ):
  109. parser.error('Error: No password file or password passed\n')
  110. if ( args.registry and args.decrypt and platform.system().startswith('Windows')):
  111. reg = wreg.WindowsRegistry("RealVNC", "WinVNC4")
  112. ( args.passwd, key_type) = reg.getval("Password")
  113. elif not platform.system().startswith('Windows'):
  114. print 'Cannot read from Windows Registry on a %s system' % platform.system()
  115. if ( args.passwd != None and args.hex ):
  116. args.passwd = unhex(args.passwd)
  117. if ( args.filename != None and args.decrypt ):
  118. args.passwd = do_file_in(args.filename, args.hex)
  119. # If the hex encoded passwd length is longer than 16 hex chars and divisible
  120. # by 16, then we chop the passwd into blocks of 64 bits (16 hex chars)
  121. # (1 hex char = 4 binary bits = 1 nibble)
  122. hexpasswd = args.passwd.encode('hex')
  123. if ( len(hexpasswd) > 16 and (len(hexpasswd) % 16) == 0 ):
  124. print 'INFO: Detected ciphertext > 64 bits... breaking into blocks to decrypt...'
  125. splitstr = split_len(args.passwd.encode('hex'), 16)
  126. print 'INFO: Split blocks = %s' % splitstr
  127. cryptedblocks = []
  128. for sblock in splitstr:
  129. cryptedblocks.append( do_crypt(sblock.decode('hex'), args.decrypt) )
  130. #print '%016s\t%s' % ( sblock, cryptedblocks )
  131. crypted = ''.join(cryptedblocks)
  132. elif ( len(hexpasswd) <= 16):
  133. crypted = do_crypt(args.passwd, args.decrypt)
  134. else:
  135. if ( args.decrypt ):
  136. print 'WARN: Ciphertext length was not divisible by 8 (hex/16).'
  137. print 'Length: %d' % len(args.passwd)
  138. print 'Hex Length: %d' % len(hexpasswd)
  139. crypted = do_crypt(args.passwd, args.decrypt)
  140. if ( args.filename != None and not args.decrypt ):
  141. do_file_out(args.filename, crypted, args.hex)
  142. if ( args.registry and not args.decrypt and platform.system().startswith('Windows')):
  143. reg = wreg.WindowsRegistry("RealVNC", "WinVNC4")
  144. reg.setval('Password', crypted, wreg.WindowsRegistry.REG_BINARY)
  145. elif not platform.system().startswith('Windows'):
  146. print 'Cannot write to Windows Registry on a %s system' % platform.system()
  147. prefix = ('En','De')[args.decrypt == True]
  148. print "%scrypted Bin Pass= '%s'" % ( prefix, crypted )
  149. print "%scrypted Hex Pass= '%s'" % ( prefix, crypted.encode('hex') )
  150. if __name__ == '__main__':
  151. main()