From fb67af7870d0586f37c482ab643c78b9d8b3d79a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matteo=20=E2=84=B1an?= Date: Wed, 28 Feb 2018 23:27:12 +0100 Subject: [PATCH] py3-kms upload --- py3-kms/aes.py | 741 ++++++++++++++++++++++++++++++++ py3-kms/client.py | 263 ++++++++++++ py3-kms/dcerpc.py | 765 +++++++++++++++++++++++++++++++++ py3-kms/filetimes.py | 106 +++++ py3-kms/formatText.py | 113 +++++ py3-kms/kmsBase.py | 638 ++++++++++++++++++++++++++++ py3-kms/kmsPidGenerator.py | 136 ++++++ py3-kms/kmsRequestUnknown.py | 13 + py3-kms/kmsRequestV4.py | 131 ++++++ py3-kms/kmsRequestV5.py | 179 ++++++++ py3-kms/kmsRequestV6.py | 110 +++++ py3-kms/randomHWID.py | 6 + py3-kms/randomPID.py | 38 ++ py3-kms/rpcBase.py | 62 +++ py3-kms/rpcBind.py | 175 ++++++++ py3-kms/rpcRequest.py | 72 ++++ py3-kms/server.py | 135 ++++++ py3-kms/structure.py | 793 +++++++++++++++++++++++++++++++++++ py3-kms/timezones.py | 263 ++++++++++++ 19 files changed, 4739 insertions(+) create mode 100644 py3-kms/aes.py create mode 100644 py3-kms/client.py create mode 100644 py3-kms/dcerpc.py create mode 100644 py3-kms/filetimes.py create mode 100644 py3-kms/formatText.py create mode 100644 py3-kms/kmsBase.py create mode 100644 py3-kms/kmsPidGenerator.py create mode 100644 py3-kms/kmsRequestUnknown.py create mode 100644 py3-kms/kmsRequestV4.py create mode 100644 py3-kms/kmsRequestV5.py create mode 100644 py3-kms/kmsRequestV6.py create mode 100644 py3-kms/randomHWID.py create mode 100644 py3-kms/randomPID.py create mode 100644 py3-kms/rpcBase.py create mode 100644 py3-kms/rpcBind.py create mode 100644 py3-kms/rpcRequest.py create mode 100644 py3-kms/server.py create mode 100644 py3-kms/structure.py create mode 100644 py3-kms/timezones.py diff --git a/py3-kms/aes.py b/py3-kms/aes.py new file mode 100644 index 0000000..96b44ec --- /dev/null +++ b/py3-kms/aes.py @@ -0,0 +1,741 @@ +#!/usr/bin/env python3 + +# aes.py: implements AES - Advanced Encryption Standard +# from the SlowAES project, http://code.google.com/p/slowaes/ +# +# Copyright (c) 2008 Josh Davis ( http://www.josh-davis.org ), Alex Martelli ( http://www.aleax.it ) +# +# Ported from C code written by Laurent Haan ( http://www.progressive-coding.com ) + +# Licensed under the Apache License, Version 2.0 +# http://www.apache.org/licenses/ +# + +""" +Ported to Python3 with minimal changements. +© Copyright 2018 Matteo ℱan +""" + +import os +import math + + +def append_PKCS7_padding(val): + """ Function to pad the given data to a multiple of 16-bytes by PKCS7 padding. """ + + numpads = 16 - (len(val) % 16) + return val + numpads * bytes(chr(numpads), 'utf-8') + +def strip_PKCS7_padding(val): + """ Function to strip off PKCS7 padding. """ + + if len(val) % 16 or not val: + raise ValueError("String of len %d can't be PCKS7-padded" % len(val)) + numpads = val[-1] + if numpads > 16: + raise ValueError("String ending with %r can't be PCKS7-padded" % val[-1]) + return val[:-numpads] + + +class AES( object ): + """ Class implementing the Advanced Encryption Standard algorithm. """ + + #*py-kms* + v6 = False + + # Valid key sizes + KeySize = { + "SIZE_128": 16, + "SIZE_192": 24, + "SIZE_256": 32 + } + + # Rijndael S-box + sbox = [ 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, + 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, + 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, + 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, + 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, + 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, + 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, + 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, + 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, + 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, + 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, + 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, + 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, + 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, + 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, + 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, + 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, + 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, + 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, + 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, + 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, + 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, + 0x54, 0xbb, 0x16 ] + + # Rijndael Inverted S-box + rsbox = [ 0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, + 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, + 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, + 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, + 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, + 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, + 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, + 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, + 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, + 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, + 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, + 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, + 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, + 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, + 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, + 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, + 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, + 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, + 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, + 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, + 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, + 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, + 0x21, 0x0c, 0x7d ] + + # Rijndael Rcon + Rcon = [ 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, + 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, + 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, + 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, + 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, + 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, + 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, + 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, + 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, + 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, + 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, + 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, + 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, + 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, + 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, + 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, + 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, + 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, + 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, + 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, + 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, + 0xe8, 0xcb ] + + def getSBoxValue(self,num): + """ Method to retrieve a given S-Box value. """ + return self.sbox[num] + + def getSBoxInvert(self,num): + """ Method to retrieve a given Inverted S-Box value.""" + return self.rsbox[num] + + def rotate(self, word): + """ Method performing Rijndael's key schedule rotate operation. + Rotate a word eight bits to the left: eg, rotate(1d2c3a4f) == 2c3a4f1d + @param word: char list of size 4 (32 bits overall). + """ + return word[1:] + word[:1] + + def getRconValue(self, num): + """ Method to retrieve a given Rcon value. """ + return self.Rcon[num] + + def core(self, word, iteration): + """ Method performing the key schedule core operation. """ + # Rotate the 32-bit word 8 bits to the left. + word = self.rotate(word) + # Apply S-Box substitution on all 4 parts of the 32-bit word. + for i in range(4): + word[i] = self.getSBoxValue(word[i]) + # XOR the output of the rcon operation with i to the first part (leftmost) only. + word[0] = word[0] ^ self.getRconValue(iteration) + return word + + def expandKey(self, key, size, expandedKeySize): + """ Method performing Rijndael's key expansion. + Expands an 128, 192, 256 key into an 176, 208, 240 bit key. + """ + # Current expanded keySize, in bytes. + currentSize = 0 + rconIteration = 1 + expandedKey = [0] * expandedKeySize + + # Set the 16, 24, 32 bytes of the expanded key to the input key. + for j in range(size): + expandedKey[j] = key[j] + currentSize += size + + while currentSize < expandedKeySize: + # Assign the previous 4 bytes to the temporary value t. + t = expandedKey[currentSize - 4:currentSize] + + # Every 16,24,32 bytes we apply the core schedule to t + # and increment rconIteration afterwards. + if currentSize % size == 0: + t = self.core(t, rconIteration) + rconIteration += 1 + # For 256-bit keys, we add an extra sbox to the calculation. + if size == self.KeySize["SIZE_256"] and ((currentSize % size) == 16): + for l in range(4): + t[l] = self.getSBoxValue(t[l]) + + # We XOR t with the four-byte block 16,24,32 bytes before the new + # expanded key. This becomes the next four bytes in the expanded key. + for m in range(4): + expandedKey[currentSize] = expandedKey[currentSize - size] ^ t[m] + currentSize += 1 + return expandedKey + + def addRoundKey(self, state, roundKey): + """ Method to add (XORs) the round key to the state. """ + for i in range(16): + state[i] ^= roundKey[i] + return state + + def createRoundKey(self, expandedKey, roundKeyPointer): + """ Creates a round key from the given expanded key and the + position within the expanded key. + """ + roundKey = [0] * 16 + for i in range(4): + for j in range(4): + roundKey[j * 4 + i] = expandedKey[roundKeyPointer + i * 4 + j] + return roundKey + + def galois_multiplication(self, a, b): + """ Method to perform a Galois multiplication of 8 bit characters + a and b. + """ + p = 0 + for counter in range(8): + if b & 1: + p ^= a + hi_bit_set = a & 0x80 + a <<= 1 + # keep a 8 bit + a &= 0xFF + if hi_bit_set: + a ^= 0x1b + b >>= 1 + return p + + def subBytes(self, state, isInv): + """ Method to substitute all the values from the state with the + value in the SBox using the state value as index for the SBox. + """ + if isInv: + getter = self.getSBoxInvert + else: + getter = self.getSBoxValue + for i in range(16): + state[i] = getter(state[i]) + return state + + + def shiftRows(self, state, isInv): + """ Method to iterate over the 4 rows and call shiftRow(...) with that row. """ + for i in range(4): + state = self.shiftRow(state, i * 4, i, isInv) + return state + + + def shiftRow(self, state, statePointer, nbr, isInv): + """ Method to shift the row to the left. """ + for i in range(nbr): + if isInv: + state[statePointer:statePointer + 4] = state[statePointer + 3:statePointer + 4] + \ + state[statePointer:statePointer + 3] + else: + state[statePointer:statePointer + 4] = state[statePointer + 1:statePointer + 4] + \ + state[statePointer:statePointer + 1] + return state + + + def mixColumns(self, state, isInv): + """ Method to perform a galois multiplication of the 4x4 matrix. """ + # Iterate over the 4 columns. + for i in range(4): + # Construct one column by slicing over the 4 rows. + column = state[i:i + 16:4] + # Apply the mixColumn on one column. + column = self.mixColumn(column, isInv) + # Put the values back into the state. + state[i:i + 16:4] = column + return state + + def mixColumn(self, column, isInv): + """ Method to perform a galois multiplication of 1 column the 4x4 matrix. """ + if isInv: + mult = [14, 9, 13, 11] + else: + mult = [2, 1, 1, 3] + cpy = list(column) + g = self.galois_multiplication + + column[0] = g(cpy[0], mult[0]) ^ g(cpy[3], mult[1]) ^ \ + g(cpy[2], mult[2]) ^ g(cpy[1], mult[3]) + column[1] = g(cpy[1], mult[0]) ^ g(cpy[0], mult[1]) ^ \ + g(cpy[3], mult[2]) ^ g(cpy[2], mult[3]) + column[2] = g(cpy[2], mult[0]) ^ g(cpy[1], mult[1]) ^ \ + g(cpy[0], mult[2]) ^ g(cpy[3], mult[3]) + column[3] = g(cpy[3], mult[0]) ^ g(cpy[2], mult[1]) ^ \ + g(cpy[1], mult[2]) ^ g(cpy[0], mult[3]) + return column + + + def aes_round(self, state, roundKey, roundKms): + """ Method to apply the 4 operations of the forward round in sequence. """ + state = self.subBytes(state, False) + state = self.shiftRows(state, False) + state = self.mixColumns(state, False) + + #*py-kms* + if self.v6: + if roundKms == 4: + state[0] ^= 0x73 + if roundKms == 6: + state[0] ^= 0x09 + if roundKms == 8: + state[0] ^= 0xE4 + + state = self.addRoundKey(state, roundKey) + return state + + def aes_invRound(self, state, roundKey, roundKms): + """ Method to apply the 4 operations of the inverse round in sequence. """ + state = self.shiftRows(state, True) + state = self.subBytes(state, True) + state = self.addRoundKey(state, roundKey) + + #*py-kms* + if self.v6: + if roundKms == 4: + state[0] ^= 0x73 + if roundKms == 6: + state[0] ^= 0x09 + if roundKms == 8: + state[0] ^= 0xE4 + + state = self.mixColumns(state, True) + return state + + + def aes_main(self, state, expandedKey, nbrRounds): + """ Method to do the AES encryption for one round. + + Perform the initial operations, the standard round and the + final operations of the forward AES, creating a round key for each round. + """ + state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0)) + i = 1 + while i < nbrRounds: + state = self.aes_round(state, self.createRoundKey(expandedKey, 16 * i), i) + i += 1 + state = self.subBytes(state, False) + state = self.shiftRows(state, False) + state = self.addRoundKey(state, self.createRoundKey(expandedKey, 16 * nbrRounds)) + return state + + + def aes_invMain(self, state, expandedKey, nbrRounds): + """ Method to do the inverse AES encryption for one round. + + Perform the initial operations, the standard round, and the + final operations of the inverse AES, creating a round key for each round. + """ + state = self.addRoundKey(state, self.createRoundKey(expandedKey, 16 * nbrRounds)) + i = nbrRounds - 1 + while i > 0: + state = self.aes_invRound(state, self.createRoundKey(expandedKey, 16 * i), i) + i -= 1 + state = self.shiftRows(state, True) + state = self.subBytes(state, True) + state = self.addRoundKey(state, self.createRoundKey(expandedKey, 0)) + return state + + def encrypt(self, iput, key, size): + """ Method to encrypt a 128 bit input block against the given key + of size specified. + """ + output = [0] * 16 + # The number of rounds. + nbrRounds = 0 + # The 128 bit block to encode. + block = [0] * 16 + # Set the number of rounds. + if size == self.KeySize["SIZE_128"]: + nbrRounds = 10 + elif size == self.KeySize["SIZE_192"]: + nbrRounds = 12 + elif size == self.KeySize["SIZE_256"]: + nbrRounds = 14 + # *py-kms* The KMS v4 parameters. + elif size == 20: + nbrRounds = 11 + else: + raise ValueError("Wrong key size given ({}).".format(size)) + + # The expanded keySize. + expandedKeySize = 16 * (nbrRounds + 1) + + # Set the block values, for the block: + # a[0,0] a[0,1] a[0,2] a[0,3] + # a[1,0] a[1,1] a[1,2] a[1,3] + # a[2,0] a[2,1] a[2,2] a[2,3] + # a[3,0] a[3,1] a[3,2] a[3,3] + # the mapping order is a[0,0] a[1,0] a[2,0] a[3,0] a[0,1] a[1,1] ... a[2,3] a[3,3] + + # Iterate over the columns and over the rows. + for i in range(4): + for j in range(4): + block[i + j * 4] = iput[i * 4 +j] + + # Expand the key into an 176, 208, 240 bit key + expandedKey = self.expandKey(key, size, expandedKeySize) + + # Encrypt the block using the expandedKey. + block = self.aes_main(block, expandedKey, nbrRounds) + + # Unmap the block again into the output. + for k in range(4): + for l in range(4): + output[k * 4 + l] = block[k + l * 4] + return output + + + def decrypt(self, iput, key, size): + """ Method to decrypt a 128 bit input block against the given key + of size specified. + """ + output = [0] * 16 + # The number of rounds. + nbrRounds = 0 + # The 128 bit block to decode. + block = [0] * 16 + # Set the number of rounds. + if size == self.KeySize["SIZE_128"]: + nbrRounds = 10 + elif size == self.KeySize["SIZE_192"]: + nbrRounds = 12 + elif size == self.KeySize["SIZE_256"]: + nbrRounds = 14 + #*py-kms* The KMS v4 parameters. + elif size == 20: + nbrRounds = 11 + else: + raise ValueError("Wrong key size given ({}).".format(size)) + + # The expanded keySize. + expandedKeySize = 16 * (nbrRounds + 1) + + # Set the block values, for the block: + # a[0,0] a[0,1] a[0,2] a[0,3] + # a[1,0] a[1,1] a[1,2] a[1,3] + # a[2,0] a[2,1] a[2,2] a[2,3] + # a[3,0] a[3,1] a[3,2] a[3,3] + # the mapping order is a[0,0] a[1,0] a[2,0] a[3,0] a[0,1] a[1,1] ... a[2,3] a[3,3] + + # Iterate over the columns and the rows. + for i in range(4): + for j in range(4): + block[i + j * 4] = iput[i * 4 + j] + + # Expand the key into an 176, 208, 240 bit key. + expandedKey = self.expandKey(key, size, expandedKeySize) + # Decrypt the block using the expandedKey. + block = self.aes_invMain(block, expandedKey, nbrRounds) + # Unmap the block again into the output. + for k in range(4): + for l in range(4): + output[k * 4 +l] = block[k + l * 4] + return output + + +class AESModeOfOperation( object ): + """ Class implementing the different AES mode of operations. """ + + aes = AES() + + # Supported modes of operation. + ModeOfOperation = { + "OFB": 0, + "CFB": 1, + "CBC": 2 + } + + def convertString(self, string, start, end, mode): + """ Method to convert a 16 character string into a number array. """ + if end - start > 16: + end = start + 16 + if mode == self.ModeOfOperation["CBC"]: + ar = [0] * 16 + else: + ar = [] + + i = start + j = 0 + while len(ar) < end - start: + ar.append(0) + while i < end: + ar[j] = string[i] + j += 1 + i += 1 + return ar + + + def encrypt(self, stringIn, mode, key, size, IV): + """ Method to perform the encryption operation. + + @param stringIn: input string to be encrypted + @param mode: mode of operation (0, 1 or 2) + @param key: a hex key of the bit length size + @param size: the bit length of the key (16, 24 or 32) + @param IV: the 128 bit hex initilization vector + @return tuple with mode of operation, length of the input and the encrypted data + """ + if len(key) % size: + raise ValueError("Illegal size ({}) for key '{}'.".format(size, key)) + if len(IV) % 16: + raise ValueError("IV is not a multiple of 16.") + # The AES input/output. + plaintext = [] + iput = [0] * 16 + output = [] + ciphertext = [0] * 16 + # The output cipher string. + cipherOut = [] + + firstRound = True + if stringIn != None: + for j in range(int(math.ceil(float(len(stringIn))/16))): + start = j * 16 + end = j * 16 + 16 + if end > len(stringIn): + end = len(stringIn) + plaintext = self.convertString(stringIn, start, end, mode) + + if mode == self.ModeOfOperation["CFB"]: + if firstRound: + output = self.aes.encrypt(IV, key, size) + firstRound = False + else: + output = self.aes.encrypt(iput, key, size) + for i in range(16): + if len(plaintext) - 1 < i: + ciphertext[i] = 0 ^ output[i] + elif len(output) - 1 < i: + ciphertext[i] = plaintext[i] ^ 0 + elif len(plaintext) - 1 < i and len(output) < i: + ciphertext[i] = 0 ^ 0 + else: + ciphertext[i] = plaintext[i] ^ output[i] + for k in range(end - start): + cipherOut.append(ciphertext[k]) + iput = ciphertext + + elif mode == self.ModeOfOperation["OFB"]: + if firstRound: + output = self.aes.encrypt(IV, key, size) + firstRound = False + else: + output = self.aes.encrypt(iput, key, size) + for i in range(16): + if len(plaintext) - 1 < i: + ciphertext[i] = 0 ^ output[i] + elif len(output) - 1 < i: + ciphertext[i] = plaintext[i] ^ 0 + elif len(plaintext) - 1 < i and len(output) < i: + ciphertext[i] = 0 ^ 0 + else: + ciphertext[i] = plaintext[i] ^ output[i] + for k in range(end - start): + cipherOut.append(ciphertext[k]) + iput = output + + elif mode == self.ModeOfOperation["CBC"]: + for i in range(16): + if firstRound: + iput[i] = plaintext[i] ^ IV[i] + else: + iput[i] = plaintext[i] ^ ciphertext[i] + firstRound = False + ciphertext = self.aes.encrypt(iput, key, size) + # Always 16 bytes because of the padding for CBC. + for k in range(16): + cipherOut.append(ciphertext[k]) + return mode, len(stringIn), cipherOut + + + def decrypt(self, cipherIn, originalsize, mode, key, size, IV): + """ Method to perform the decryption operation. + + @param cipherIn: encrypted string to be decrypted + @param originalsize: unencrypted string length (required for CBC) + @param mode: mode of operation (0, 1 or 2) + @param key: a number array of the bit length size + @param size: the bit length of the key (16, 24 or 32) + @param IV: the 128 bit number array initilization vector + @return decrypted data + """ + if len(key) % size: + raise ValueError("Illegal size ({}) for key '{}'.".format(size, key)) + if len(IV) % 16: + raise ValueError("IV is not a multiple of 16.") + # The AES input/output. + ciphertext = [] + iput = [] + output = [] + plaintext = [0] * 16 + # The output plain text character list. + chrOut = [] + + firstRound = True + if cipherIn != None: + for j in range(int(math.ceil(float(len(cipherIn))/16))): + start = j * 16 + end = j * 16 + 16 + if j * 16 + 16 > len(cipherIn): + end = len(cipherIn) + ciphertext = cipherIn[start:end] + + if mode == self.ModeOfOperation["CFB"]: + if firstRound: + output = self.aes.encrypt(IV, key, size) + firstRound = False + else: + output = self.aes.encrypt(iput, key, size) + for i in range(16): + if len(output) - 1 < i: + plaintext[i] = 0 ^ ciphertext[i] + elif len(ciphertext) - 1 < i: + plaintext[i] = output[i] ^ 0 + elif len(output) - 1 < i and len(ciphertext) < i: + plaintext[i] = 0 ^ 0 + else: + plaintext[i] = output[i] ^ ciphertext[i] + for k in range(end - start): + chrOut.append(plaintext[k]) + iput = ciphertext + + elif mode == self.ModeOfOperation["OFB"]: + if firstRound: + output = self.aes.encrypt(IV, key, size) + firstRound = False + else: + output = self.aes.encrypt(iput, key, size) + for i in range(16): + if len(output) - 1 < i: + plaintext[i] = 0 ^ ciphertext[i] + elif len(ciphertext) - 1 < i: + plaintext[i] = output[i] ^ 0 + elif len(output) - 1 < i and len(ciphertext) < i: + plaintext[i] = 0 ^ 0 + else: + plaintext[i] = output[i] ^ ciphertext[i] + for k in range(end - start): + chrOut.append(plaintext[k]) + iput = output + + elif mode == self.ModeOfOperation["CBC"]: + output = self.aes.decrypt(ciphertext, key, size) + for i in range(16): + if firstRound: + plaintext[i] = IV[i] ^ output[i] + else: + plaintext[i] = iput[i] ^ output[i] + firstRound = False + if originalsize is not None and originalsize < end: + for k in range(originalsize - start): + chrOut.append(plaintext[k]) + else: + for k in range(end - start): + chrOut.append(plaintext[k]) + iput = ciphertext + return chrOut + + +def encryptData(key, data, mode=AESModeOfOperation.ModeOfOperation["CBC"]): + """ Module function to encrypt the given data with the given key. + + @param key: key to be used for encryption + @param data: data to be encrypted + @param mode: mode of operations (0, 1 or 2) + @return encrypted data prepended with the initialization vector + """ + if mode == AESModeOfOperation.ModeOfOperation["CBC"]: + data = append_PKCS7_padding(data) + + keysize = len(key) + assert keysize in AES.KeySize.values(), 'invalid key size: {}'.format(keysize) + # Create a new iv using random data. + iv = os.urandom(16) + moo = AESModeOfOperation() + (mode, length, ciph) = moo.encrypt(data, mode, key, keysize, iv) + # With padding, the original length does not need to be known. + # It's a bad idea to store the original message length prepend the iv. + return iv + bytes(ciph) + +def decryptData(key, data, mode=AESModeOfOperation.ModeOfOperation["CBC"]): + """ Module function to decrypt the given data with the given key. + + @param key: key to be used for decryption + @param data: data to be decrypted with initialization vector prepended + @param mode: mode of operations (0, 1 or 2) + @return decrypted data + """ + keysize = len(key) + assert keysize in AES.KeySize.values(), 'invalid key size: {}'.format(keysize) + # iv is first 16 bytes. + iv = data[:16] + data = data[16:] + moo = AESModeOfOperation() + decr = moo.decrypt(data, None, mode, key, keysize, iv) + if mode == AESModeOfOperation.ModeOfOperation["CBC"]: + decr = strip_PKCS7_padding(decr) + return decr + + +class Test(object): + def generateRandomKey(self, keysize): + """ Generates a key from random data of length `keysize`. + The returned key is a string of bytes. + """ + if keysize not in (16, 24, 32): + raise ValueError('Invalid keysize, %s. Should be one of (16, 24, 32).' % keysize) + return os.urandom(keysize) + + def testString(self, cleartext, keysize = 16, modeName = "CBC"): + """ Test with random key, choice of mode. """ + print('Random key test with Mode:', modeName) + print('ClearText:', cleartext) + key = self.generateRandomKey(keysize) + print('Key:', bytes([x for x in key])) + mode = AESModeOfOperation.ModeOfOperation[modeName] + cipher = encryptData(key, cleartext, mode) + print('Cipher:', bytes([x for x in cipher])) + decr = decryptData(key, cipher, mode) + print('Decrypted:', bytes(decr)) + + +if __name__ == "__main__": + moo = AESModeOfOperation() + cleartext = "This is a test with several blocks ! Some utf-8 characters åäö and test continues" + print('ClearText: %s\n' % cleartext) + cleartext = bytes(cleartext, 'utf-8') + + cipherkey = [143, 194, 34, 208, 145, 203, 230, 143, 177, 246, 97, 206, 145, 92, 255, 84] + iv = [103, 35, 148, 239, 76, 213, 47, 118, 255, 222, 123, 176, 106, 134, 98, 92] + mode, orig_len, ciph = moo.encrypt(cleartext, moo.ModeOfOperation["CBC"], + cipherkey, moo.aes.KeySize["SIZE_128"], iv) + print('Encrypt result: mode = %s, length = %s (%s), encrypted = %s\n' % (mode, orig_len, len(cleartext), bytes(ciph))) + + decr = moo.decrypt(ciph, orig_len, mode, cipherkey, moo.aes.KeySize["SIZE_128"], iv) + print('Decrypt result: %s\n' % bytes(decr)) + Test().testString(cleartext, 16, "CBC") diff --git a/py3-kms/client.py b/py3-kms/client.py new file mode 100644 index 0000000..fee2e4b --- /dev/null +++ b/py3-kms/client.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 + +import argparse +import binascii +import datetime +import random +import socket +import string +import struct +import sys +import uuid +import logging +import os + +import filetimes, rpcBind, rpcRequest +from dcerpc import MSRPCHeader, MSRPCBindNak, MSRPCRequestHeader, MSRPCRespHeader +from kmsBase import kmsBase, UUID +from kmsRequestV4 import kmsRequestV4 +from kmsRequestV5 import kmsRequestV5 +from kmsRequestV6 import kmsRequestV6 +from rpcBase import rpcBase +from formatText import shell_message, justify, byterize + +config = {} + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("ip", action="store", help='The IP address or hostname of the KMS server.', type=str) + parser.add_argument("port", nargs="?", action="store", default=1688, + help='The port the KMS service is listening on. The default is \"1688\".', type=int) + parser.add_argument("-m", "--mode", dest="mode", + choices=["WindowsVista","Windows7","Windows8","Windows81","Windows10","Office2010","Office2013","Office2016"], default="Windows7", + help='Use this flag to manually specify a Microsoft product for testing the server. The default is \"Windows81\".', type=str) + parser.add_argument("-c", "--cmid", dest="cmid", default=None, + help='Use this flag to manually specify a CMID to use. If no CMID is specified, a random CMID will be generated.', type=str) + parser.add_argument("-n", "--name", dest="machineName", default=None, + help='Use this flag to manually specify an ASCII machineName to use. If no machineName is specified,\ +a random machineName will be generated.', type=str) + parser.add_argument("-v", "--loglevel", dest="loglevel", action="store", default="ERROR", choices=["CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"], + help='Use this flag to set a Loglevel. The default is \"ERROR\".', type=str) + parser.add_argument("-f", "--logfile", dest="logfile", action="store", default=os.path.dirname(os.path.abspath( __file__ )) + "/py3kms_client.log", + help='Use this flag to set an output Logfile. The default is \"pykms_client.log\".', type=str) + + config.update(vars(parser.parse_args())) + + logging.basicConfig(level=config['loglevel'], format='%(asctime)s %(levelname)-8s %(message)s', + datefmt='%a, %d %b %Y %H:%M:%S', filename=config['logfile'], filemode='w') + + checkConfig() + config['call_id'] = 1 + updateConfig() + s = socket.socket() + logging.info("Connecting to %s on port %d..." % (config['ip'], config['port'])) + s.connect((config['ip'], config['port'])) + logging.info("Connection successful !") + binder = rpcBind.handler(None, config) + RPC_Bind = str(binder.generateRequest()).encode('latin-1') #*2to3* + logging.info("Sending RPC bind request...") + shell_message(nshell = [-1, 1]) + s.send(RPC_Bind) + try: + shell_message(nshell = [-4, 7]) + bindResponse = s.recv(1024) + except socket.error as e: #*2to3* + if e[0] == 104: + logging.error("Connection reset by peer. Exiting...") + sys.exit() + else: + raise + if bindResponse == '' or not bindResponse: + logging.error("No data received ! Exiting...") + sys.exit() + packetType = MSRPCHeader(bindResponse)['type'] + if packetType == rpcBase.packetType['bindAck']: + logging.info("RPC bind acknowledged.") + shell_message(nshell = 8) + kmsRequest = createKmsRequest() + requester = rpcRequest.handler(kmsRequest, config) + s.send(str(requester.generateRequest()).encode('latin-1')) #*2to3* + shell_message(nshell = [-1, 12]) + response = s.recv(1024) + logging.debug("Response: \n%s\n" % justify(binascii.b2a_hex(response).decode('latin-1'))) #*2to3* + shell_message(nshell = [-4, 20]) + parsed = MSRPCRespHeader(response) + kmsData = readKmsResponse(parsed['pduData'], kmsRequest, config) + kmsResp = kmsData['response'] + + try: + hwid = kmsData['hwid'] + except: + hwid = None + logging.info("KMS Host ePID: %s" % kmsResp['kmsEpid'].encode('utf-8').decode('utf-16le')) #*2to3* + if hwid is not None: + logging.info("KMS Host HWID: %s" % binascii.b2a_hex(hwid.encode('latin-1')).upper().decode('utf-8')) #*2to3* + + logging.info("KMS Host Current Client Count: %s" % kmsResp['currentClientCount']) + logging.info("KMS VL Activation Interval: %s" % kmsResp['vLActivationInterval']) + logging.info("KMS VL Renewal Interval: %s" % kmsResp['vLRenewalInterval']) + shell_message(nshell = 21) + + elif packetType == rpcBase.packetType['bindNak']: + logging.info(justify(MSRPCBindNak(bindResponse).dump(print_to_stdout = False))) + sys.exit() + else: + logging.critical("Something went wrong.") + sys.exit() + + +def checkConfig(): + if config['cmid'] is not None: + try: + uuid.UUID(config['cmid']) + except: + logging.error("Bad CMID. Exiting...") + sys.exit() + if config['machineName'] is not None: + if len(config['machineName']) < 2 or len(config['machineName']) > 63: + logging.error("machineName must be between 2 and 63 characters in length.") + sys.exit() + +def updateConfig(): + if config['mode'] == 'WindowsVista': + config['RequiredClientCount'] = 25 + config['KMSProtocolMajorVersion'] = 4 + config['KMSProtocolMinorVersion'] = 0 + config['KMSClientLicenseStatus'] = 2 + config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f" + config['KMSClientSkuID'] = "cfd8ff08-c0d7-452b-9f60-ef5c70c32094" + config['KMSClientKMSCountedID'] = "212a64dc-43b1-4d3d-a30c-2fc69d2095c6" + elif config['mode'] == 'Windows7': + config['RequiredClientCount'] = 25 + config['KMSProtocolMajorVersion'] = 4 + config['KMSProtocolMinorVersion'] = 0 + config['KMSClientLicenseStatus'] = 2 + config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f" + config['KMSClientSkuID'] = "ae2ee509-1b34-41c0-acb7-6d4650168915" + config['KMSClientKMSCountedID'] = "7fde5219-fbfa-484a-82c9-34d1ad53e856" + elif config['mode'] == 'Windows8': + config['RequiredClientCount'] = 25 + config['KMSProtocolMajorVersion'] = 5 + config['KMSProtocolMinorVersion'] = 0 + config['KMSClientLicenseStatus'] = 2 + config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f" + config['KMSClientSkuID'] = "458e1bec-837a-45f6-b9d5-925ed5d299de" + config['KMSClientKMSCountedID'] = "3c40b358-5948-45af-923b-53d21fcc7e79" + elif config['mode'] == 'Windows81': + config['RequiredClientCount'] = 25 + config['KMSProtocolMajorVersion'] = 6 + config['KMSProtocolMinorVersion'] = 0 + config['KMSClientLicenseStatus'] = 2 + config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f" + config['KMSClientSkuID'] = "81671aaf-79d1-4eb1-b004-8cbbe173afea" + config['KMSClientKMSCountedID'] = "cb8fc780-2c05-495a-9710-85afffc904d7" + elif config['mode'] == 'Windows10': + config['RequiredClientCount'] = 25 + config['KMSProtocolMajorVersion'] = 6 + config['KMSProtocolMinorVersion'] = 0 + config['KMSClientLicenseStatus'] = 2 + config['KMSClientAppID'] = "55c92734-d682-4d71-983e-d6ec3f16059f" + config['KMSClientSkuID'] = "73111121-5638-40f6-bc11-f1d7b0d64300" + config['KMSClientKMSCountedID'] = "58e2134f-8e11-4d17-9cb2-91069c151148" + elif config['mode'] == 'Office2010': + config['RequiredClientCount'] = 5 + config['KMSProtocolMajorVersion'] = 4 + config['KMSProtocolMinorVersion'] = 0 + config['KMSClientLicenseStatus'] = 2 + config['KMSClientAppID'] = "59a52881-a989-479d-af46-f275c6370663" + config['KMSClientSkuID'] = "6f327760-8c5c-417c-9b61-836a98287e0c" + config['KMSClientKMSCountedID'] = "e85af946-2e25-47b7-83e1-bebcebeac611" + elif config['mode'] == 'Office2013': + config['RequiredClientCount'] = 5 + config['KMSProtocolMajorVersion'] = 5 + config['KMSProtocolMinorVersion'] = 0 + config['KMSClientLicenseStatus'] = 2 + config['KMSClientAppID'] = "0ff1ce15-a989-479d-af46-f275c6370663" + config['KMSClientSkuID'] = "b322da9c-a2e2-4058-9e4e-f59a6970bd69" + config['KMSClientKMSCountedID'] = "e6a6f1bf-9d40-40c3-aa9f-c77ba21578c0" + elif config['mode'] == 'Office2016': + config['RequiredClientCount'] = 5 + config['KMSProtocolMajorVersion'] = 6 + config['KMSProtocolMinorVersion'] = 0 + config['KMSClientLicenseStatus'] = 2 + config['KMSClientAppID'] = "0ff1ce15-a989-479d-af46-f275c6370663" + config['KMSClientSkuID'] = "d450596f-894d-49e0-966a-fd39ed4c4c64" + config['KMSClientKMSCountedID'] = "85b5f61b-320b-4be3-814a-b76b2bfafc82" + +def createKmsRequestBase(): + requestDict = kmsBase.kmsRequestStruct() + requestDict['versionMinor'] = config['KMSProtocolMinorVersion'] + requestDict['versionMajor'] = config['KMSProtocolMajorVersion'] + requestDict['isClientVm'] = 0 + requestDict['licenseStatus'] = config['KMSClientLicenseStatus'] + requestDict['graceTime'] = 43200 + requestDict['applicationId'] = UUID(uuid.UUID(config['KMSClientAppID']).bytes_le) + requestDict['skuId'] = UUID(uuid.UUID(config['KMSClientSkuID']).bytes_le) + requestDict['kmsCountedId'] = UUID(uuid.UUID(config['KMSClientKMSCountedID']).bytes_le) + requestDict['clientMachineId'] = UUID(uuid.UUID(config['cmid']).bytes_le if (config['cmid'] is not None) else uuid.uuid4().bytes_le) + requestDict['previousClientMachineId'] = '\0' * 16 #requestDict['clientMachineId'] # I'm pretty sure this is supposed to be a null UUID. + requestDict['requiredClientCount'] = config['RequiredClientCount'] + requestDict['requestTime'] = filetimes.dt_to_filetime(datetime.datetime.utcnow()) + requestDict['machineName'] = (config['machineName'] if (config['machineName'] is not None) else + ''.join(random.choice(string.ascii_letters + string.digits) for i in range(random.randint(2,63)))).encode('utf-16le') + requestDict['mnPad'] = '\0'.encode('utf-16le') * (63 - len(requestDict['machineName'].decode('utf-16le'))) + + # Debug Stuff + shell_message(nshell = 9) + requestDict = byterize(requestDict) + logging.debug("Request Base Dictionary: \n%s\n" % justify(requestDict.dump(print_to_stdout = False))) + + return requestDict + +def createKmsRequest(): + # Update the call ID + config['call_id'] += 1 + + # KMS Protocol Major Version + if config['KMSProtocolMajorVersion'] == 4: + handler = kmsRequestV4(None, config) + elif config['KMSProtocolMajorVersion'] == 5: + handler = kmsRequestV5(None, config) + elif config['KMSProtocolMajorVersion'] == 6: + handler = kmsRequestV6(None, config) + else: + return None + + requestBase = createKmsRequestBase() + return handler.generateRequest(requestBase) + +def readKmsResponse(data, request, config): + if config['KMSProtocolMajorVersion'] == 4: + logging.info("Received V4 response") + response = readKmsResponseV4(data, request) + elif config['KMSProtocolMajorVersion'] == 5: + logging.info("Received V5 response") + response = readKmsResponseV5(data) + elif config['KMSProtocolMajorVersion'] == 6: + logging.info("Received V6 response") + response = readKmsResponseV6(data) + else: + logging.info("Unhandled response version: %d.%d" % (config['KMSProtocolMajorVersion'], config['KMSProtocolMinorVersion'])) + logging.info("I'm not even sure how this happened...") + return response + +def readKmsResponseV4(data, request): + response = kmsRequestV4.ResponseV4(data) + hashed = kmsRequestV4(data, config).generateHash(bytearray(str(response['response']).encode('latin-1'))) #*2to3* + if hashed.decode('latin-1') == response['hash']: + logging.info("Response Hash has expected value !!") + return response + +def readKmsResponseV5(data): + response = kmsRequestV5.ResponseV5(data) + decrypted = kmsRequestV5(data, config).decryptResponse(response) + return decrypted + +def readKmsResponseV6(data): + response = kmsRequestV6.ResponseV5(data) + decrypted = kmsRequestV6(data, config).decryptResponse(response) + message = decrypted['message'] + return message + +if __name__ == "__main__": + main() diff --git a/py3-kms/dcerpc.py b/py3-kms/dcerpc.py new file mode 100644 index 0000000..e285554 --- /dev/null +++ b/py3-kms/dcerpc.py @@ -0,0 +1,765 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2003-2014 CORE Security Technologies +# +# This software is provided under under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +# Description: +# Partial C706.pdf + [MS-RPCE] implementation +# +# Best way to learn how to use these calls is to grab the protocol standard +# so you understand what the call does, and then read the test case located +# at https://github.com/CoreSecurity/impacket/tree/master/impacket/testcases/SMB_RPC +# +# ToDo: +# [ ] Take out all the security provider stuff out of here (e.g. RPC_C_AUTHN_WINNT) +# and put it elsewhere. This will make the coder cleaner and easier to add +# more SSP (e.g. NETLOGON) +# + +""" +Stripped down version of `https://github.com/CoreSecurity/impacket/blob/master/impacket/dcerpc/v5/rpcrt.py`, +modified for Python3. +""" + +from structure import Structure, pack, unpack + +# MS/RPC Constants +MSRPC_REQUEST = 0x00 +MSRPC_PING = 0x01 +MSRPC_RESPONSE = 0x02 +MSRPC_FAULT = 0x03 +MSRPC_WORKING = 0x04 +MSRPC_NOCALL = 0x05 +MSRPC_REJECT = 0x06 +MSRPC_ACK = 0x07 +MSRPC_CL_CANCEL = 0x08 +MSRPC_FACK = 0x09 +MSRPC_CANCELACK = 0x0A +MSRPC_BIND = 0x0B +MSRPC_BINDACK = 0x0C +MSRPC_BINDNAK = 0x0D +MSRPC_ALTERCTX = 0x0E +MSRPC_ALTERCTX_R= 0x0F +MSRPC_AUTH3 = 0x10 +MSRPC_SHUTDOWN = 0x11 +MSRPC_CO_CANCEL = 0x12 +MSRPC_ORPHANED = 0x13 + +# MS/RPC Packet Flags +MSRPC_FIRSTFRAG = 0x01 +MSRPC_LASTFRAG = 0x02 + +# For PDU types bind, bind_ack, alter_context, and +# alter_context_resp, this flag MUST be interpreted as PFC_SUPPORT_HEADER_SIGN +MSRPC_SUPPORT_SIGN = 0x04 + +#For the +#remaining PDU types, this flag MUST be interpreted as PFC_PENDING_CANCEL. +MSRPC_PENDING_CANCEL= 0x04 + +MSRPC_NOTAFRAG = 0x04 +MSRPC_RECRESPOND = 0x08 +MSRPC_NOMULTIPLEX = 0x10 +MSRPC_NOTFORIDEMP = 0x20 +MSRPC_NOTFORBCAST = 0x40 +MSRPC_NOUUID = 0x80 + +# Auth Types - Security Providers +RPC_C_AUTHN_NONE = 0x00 +RPC_C_AUTHN_GSS_NEGOTIATE = 0x09 +RPC_C_AUTHN_WINNT = 0x0A +RPC_C_AUTHN_GSS_SCHANNEL = 0x0E +RPC_C_AUTHN_GSS_KERBEROS = 0x10 +RPC_C_AUTHN_NETLOGON = 0x44 +RPC_C_AUTHN_DEFAULT = 0xFF + +# Auth Levels +RPC_C_AUTHN_LEVEL_NONE = 1 +RPC_C_AUTHN_LEVEL_CONNECT = 2 +RPC_C_AUTHN_LEVEL_CALL = 3 +RPC_C_AUTHN_LEVEL_PKT = 4 +RPC_C_AUTHN_LEVEL_PKT_INTEGRITY = 5 +RPC_C_AUTHN_LEVEL_PKT_PRIVACY = 6 + +#Reasons for rejection of a context element, included in bind_ack result reason +rpc_provider_reason = { + 0 : 'reason_not_specified', + 1 : 'abstract_syntax_not_supported', + 2 : 'proposed_transfer_syntaxes_not_supported', + 3 : 'local_limit_exceeded', + 4 : 'protocol_version_not_specified', + 8 : 'authentication_type_not_recognized', + 9 : 'invalid_checksum' +} + +MSRPC_CONT_RESULT_ACCEPT = 0 +MSRPC_CONT_RESULT_USER_REJECT = 1 +MSRPC_CONT_RESULT_PROV_REJECT = 2 + +#Results of a presentation context negotiation +rpc_cont_def_result = { + 0 : 'acceptance', + 1 : 'user_rejection', + 2 : 'provider_rejection' +} + +#status codes, references: +#http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rpc/rpc/rpc_return_values.asp +#http://msdn.microsoft.com/library/default.asp?url=/library/en-us/randz/protocol/common_return_values.asp +#winerror.h +#http://www.opengroup.org/onlinepubs/9629399/apdxn.htm + +#*2to3* 0x00000005L -> 0x00000005..... +rpc_status_codes = { + 0x00000005 : 'rpc_s_access_denied', + 0x00000008 : 'Authentication type not recognized', + 0x000006D8 : 'rpc_fault_cant_perform', + 0x000006C6 : 'rpc_x_invalid_bound', # the arrays bound are invalid + 0x000006E4 : 'rpc_s_cannot_support: The requested operation is not supported.', # some operation is not supported + 0x000006F7 : 'rpc_x_bad_stub_data', # the stub data is invalid, doesn't match with the IDL definition + 0x1C010001 : 'nca_s_comm_failure', # unable to get response from server: + 0x1C010002 : 'nca_s_op_rng_error', # bad operation number in call + 0x1C010003 : 'nca_s_unk_if', # unknown interface + 0x1C010006 : 'nca_s_wrong_boot_time', # client passed server wrong server boot time + 0x1C010009 : 'nca_s_you_crashed', # a restarted server called back a client + 0x1C01000B : 'nca_s_proto_error', # someone messed up the protocol + 0x1C010013 : 'nca_s_out_args_too_big ', # output args too big + 0x1C010014 : 'nca_s_server_too_busy', # server is too busy to handle call + 0x1C010015 : 'nca_s_fault_string_too_long', # string argument longer than declared max len + 0x1C010017 : 'nca_s_unsupported_type ', # no implementation of generic operation for object + 0x1C000001 : 'nca_s_fault_int_div_by_zero', + 0x1C000002 : 'nca_s_fault_addr_error ', + 0x1C000003 : 'nca_s_fault_fp_div_zero', + 0x1C000004 : 'nca_s_fault_fp_underflow', + 0x1C000005 : 'nca_s_fault_fp_overflow', + 0x1C000006 : 'nca_s_fault_invalid_tag', + 0x1C000007 : 'nca_s_fault_invalid_bound ', + 0x1C000008 : 'nca_s_rpc_version_mismatch', + 0x1C000009 : 'nca_s_unspec_reject ', + 0x1C00000A : 'nca_s_bad_actid', + 0x1C00000B : 'nca_s_who_are_you_failed', + 0x1C00000C : 'nca_s_manager_not_entered ', + 0x1C00000D : 'nca_s_fault_cancel', + 0x1C00000E : 'nca_s_fault_ill_inst', + 0x1C00000F : 'nca_s_fault_fp_error', + 0x1C000010 : 'nca_s_fault_int_overflow', + 0x1C000012 : 'nca_s_fault_unspec', + 0x1C000013 : 'nca_s_fault_remote_comm_failure ', + 0x1C000014 : 'nca_s_fault_pipe_empty ', + 0x1C000015 : 'nca_s_fault_pipe_closed', + 0x1C000016 : 'nca_s_fault_pipe_order ', + 0x1C000017 : 'nca_s_fault_pipe_discipline', + 0x1C000018 : 'nca_s_fault_pipe_comm_error', + 0x1C000019 : 'nca_s_fault_pipe_memory', + 0x1C00001A : 'nca_s_fault_context_mismatch ', + 0x1C00001B : 'nca_s_fault_remote_no_memory ', + 0x1C00001C : 'nca_s_invalid_pres_context_id', + 0x1C00001D : 'nca_s_unsupported_authn_level', + 0x1C00001F : 'nca_s_invalid_checksum ', + 0x1C000020 : 'nca_s_invalid_crc', + 0x1C000021 : 'nca_s_fault_user_defined', + 0x1C000022 : 'nca_s_fault_tx_open_failed', + 0x1C000023 : 'nca_s_fault_codeset_conv_error', + 0x1C000024 : 'nca_s_fault_object_not_found ', + 0x1C000025 : 'nca_s_fault_no_client_stub', + 0x16c9a000 : "rpc_s_mod", + 0x16c9a001 : "rpc_s_op_rng_error", + 0x16c9a002 : "rpc_s_cant_create_socket", + 0x16c9a003 : "rpc_s_cant_bind_socket", + 0x16c9a004 : "rpc_s_not_in_call", + 0x16c9a005 : "rpc_s_no_port", + 0x16c9a006 : "rpc_s_wrong_boot_time", + 0x16c9a007 : "rpc_s_too_many_sockets", + 0x16c9a008 : "rpc_s_illegal_register", + 0x16c9a009 : "rpc_s_cant_recv", + 0x16c9a00a : "rpc_s_bad_pkt", + 0x16c9a00b : "rpc_s_unbound_handle", + 0x16c9a00c : "rpc_s_addr_in_use", + 0x16c9a00d : "rpc_s_in_args_too_big", + 0x16c9a00e : "rpc_s_string_too_long", + 0x16c9a00f : "rpc_s_too_many_objects", + 0x16c9a010 : "rpc_s_binding_has_no_auth", + 0x16c9a011 : "rpc_s_unknown_authn_service", + 0x16c9a012 : "rpc_s_no_memory", + 0x16c9a013 : "rpc_s_cant_nmalloc", + 0x16c9a014 : "rpc_s_call_faulted", + 0x16c9a015 : "rpc_s_call_failed", + 0x16c9a016 : "rpc_s_comm_failure", + 0x16c9a017 : "rpc_s_rpcd_comm_failure", + 0x16c9a018 : "rpc_s_illegal_family_rebind", + 0x16c9a019 : "rpc_s_invalid_handle", + 0x16c9a01a : "rpc_s_coding_error", + 0x16c9a01b : "rpc_s_object_not_found", + 0x16c9a01c : "rpc_s_cthread_not_found", + 0x16c9a01d : "rpc_s_invalid_binding", + 0x16c9a01e : "rpc_s_already_registered", + 0x16c9a01f : "rpc_s_endpoint_not_found", + 0x16c9a020 : "rpc_s_invalid_rpc_protseq", + 0x16c9a021 : "rpc_s_desc_not_registered", + 0x16c9a022 : "rpc_s_already_listening", + 0x16c9a023 : "rpc_s_no_protseqs", + 0x16c9a024 : "rpc_s_no_protseqs_registered", + 0x16c9a025 : "rpc_s_no_bindings", + 0x16c9a026 : "rpc_s_max_descs_exceeded", + 0x16c9a027 : "rpc_s_no_interfaces", + 0x16c9a028 : "rpc_s_invalid_timeout", + 0x16c9a029 : "rpc_s_cant_inq_socket", + 0x16c9a02a : "rpc_s_invalid_naf_id", + 0x16c9a02b : "rpc_s_inval_net_addr", + 0x16c9a02c : "rpc_s_unknown_if", + 0x16c9a02d : "rpc_s_unsupported_type", + 0x16c9a02e : "rpc_s_invalid_call_opt", + 0x16c9a02f : "rpc_s_no_fault", + 0x16c9a030 : "rpc_s_cancel_timeout", + 0x16c9a031 : "rpc_s_call_cancelled", + 0x16c9a032 : "rpc_s_invalid_call_handle", + 0x16c9a033 : "rpc_s_cannot_alloc_assoc", + 0x16c9a034 : "rpc_s_cannot_connect", + 0x16c9a035 : "rpc_s_connection_aborted", + 0x16c9a036 : "rpc_s_connection_closed", + 0x16c9a037 : "rpc_s_cannot_accept", + 0x16c9a038 : "rpc_s_assoc_grp_not_found", + 0x16c9a039 : "rpc_s_stub_interface_error", + 0x16c9a03a : "rpc_s_invalid_object", + 0x16c9a03b : "rpc_s_invalid_type", + 0x16c9a03c : "rpc_s_invalid_if_opnum", + 0x16c9a03d : "rpc_s_different_server_instance", + 0x16c9a03e : "rpc_s_protocol_error", + 0x16c9a03f : "rpc_s_cant_recvmsg", + 0x16c9a040 : "rpc_s_invalid_string_binding", + 0x16c9a041 : "rpc_s_connect_timed_out", + 0x16c9a042 : "rpc_s_connect_rejected", + 0x16c9a043 : "rpc_s_network_unreachable", + 0x16c9a044 : "rpc_s_connect_no_resources", + 0x16c9a045 : "rpc_s_rem_network_shutdown", + 0x16c9a046 : "rpc_s_too_many_rem_connects", + 0x16c9a047 : "rpc_s_no_rem_endpoint", + 0x16c9a048 : "rpc_s_rem_host_down", + 0x16c9a049 : "rpc_s_host_unreachable", + 0x16c9a04a : "rpc_s_access_control_info_inv", + 0x16c9a04b : "rpc_s_loc_connect_aborted", + 0x16c9a04c : "rpc_s_connect_closed_by_rem", + 0x16c9a04d : "rpc_s_rem_host_crashed", + 0x16c9a04e : "rpc_s_invalid_endpoint_format", + 0x16c9a04f : "rpc_s_unknown_status_code", + 0x16c9a050 : "rpc_s_unknown_mgr_type", + 0x16c9a051 : "rpc_s_assoc_creation_failed", + 0x16c9a052 : "rpc_s_assoc_grp_max_exceeded", + 0x16c9a053 : "rpc_s_assoc_grp_alloc_failed", + 0x16c9a054 : "rpc_s_sm_invalid_state", + 0x16c9a055 : "rpc_s_assoc_req_rejected", + 0x16c9a056 : "rpc_s_assoc_shutdown", + 0x16c9a057 : "rpc_s_tsyntaxes_unsupported", + 0x16c9a058 : "rpc_s_context_id_not_found", + 0x16c9a059 : "rpc_s_cant_listen_socket", + 0x16c9a05a : "rpc_s_no_addrs", + 0x16c9a05b : "rpc_s_cant_getpeername", + 0x16c9a05c : "rpc_s_cant_get_if_id", + 0x16c9a05d : "rpc_s_protseq_not_supported", + 0x16c9a05e : "rpc_s_call_orphaned", + 0x16c9a05f : "rpc_s_who_are_you_failed", + 0x16c9a060 : "rpc_s_unknown_reject", + 0x16c9a061 : "rpc_s_type_already_registered", + 0x16c9a062 : "rpc_s_stop_listening_disabled", + 0x16c9a063 : "rpc_s_invalid_arg", + 0x16c9a064 : "rpc_s_not_supported", + 0x16c9a065 : "rpc_s_wrong_kind_of_binding", + 0x16c9a066 : "rpc_s_authn_authz_mismatch", + 0x16c9a067 : "rpc_s_call_queued", + 0x16c9a068 : "rpc_s_cannot_set_nodelay", + 0x16c9a069 : "rpc_s_not_rpc_tower", + 0x16c9a06a : "rpc_s_invalid_rpc_protid", + 0x16c9a06b : "rpc_s_invalid_rpc_floor", + 0x16c9a06c : "rpc_s_call_timeout", + 0x16c9a06d : "rpc_s_mgmt_op_disallowed", + 0x16c9a06e : "rpc_s_manager_not_entered", + 0x16c9a06f : "rpc_s_calls_too_large_for_wk_ep", + 0x16c9a070 : "rpc_s_server_too_busy", + 0x16c9a071 : "rpc_s_prot_version_mismatch", + 0x16c9a072 : "rpc_s_rpc_prot_version_mismatch", + 0x16c9a073 : "rpc_s_ss_no_import_cursor", + 0x16c9a074 : "rpc_s_fault_addr_error", + 0x16c9a075 : "rpc_s_fault_context_mismatch", + 0x16c9a076 : "rpc_s_fault_fp_div_by_zero", + 0x16c9a077 : "rpc_s_fault_fp_error", + 0x16c9a078 : "rpc_s_fault_fp_overflow", + 0x16c9a079 : "rpc_s_fault_fp_underflow", + 0x16c9a07a : "rpc_s_fault_ill_inst", + 0x16c9a07b : "rpc_s_fault_int_div_by_zero", + 0x16c9a07c : "rpc_s_fault_int_overflow", + 0x16c9a07d : "rpc_s_fault_invalid_bound", + 0x16c9a07e : "rpc_s_fault_invalid_tag", + 0x16c9a07f : "rpc_s_fault_pipe_closed", + 0x16c9a080 : "rpc_s_fault_pipe_comm_error", + 0x16c9a081 : "rpc_s_fault_pipe_discipline", + 0x16c9a082 : "rpc_s_fault_pipe_empty", + 0x16c9a083 : "rpc_s_fault_pipe_memory", + 0x16c9a084 : "rpc_s_fault_pipe_order", + 0x16c9a085 : "rpc_s_fault_remote_comm_failure", + 0x16c9a086 : "rpc_s_fault_remote_no_memory", + 0x16c9a087 : "rpc_s_fault_unspec", + 0x16c9a088 : "uuid_s_bad_version", + 0x16c9a089 : "uuid_s_socket_failure", + 0x16c9a08a : "uuid_s_getconf_failure", + 0x16c9a08b : "uuid_s_no_address", + 0x16c9a08c : "uuid_s_overrun", + 0x16c9a08d : "uuid_s_internal_error", + 0x16c9a08e : "uuid_s_coding_error", + 0x16c9a08f : "uuid_s_invalid_string_uuid", + 0x16c9a090 : "uuid_s_no_memory", + 0x16c9a091 : "rpc_s_no_more_entries", + 0x16c9a092 : "rpc_s_unknown_ns_error", + 0x16c9a093 : "rpc_s_name_service_unavailable", + 0x16c9a094 : "rpc_s_incomplete_name", + 0x16c9a095 : "rpc_s_group_not_found", + 0x16c9a096 : "rpc_s_invalid_name_syntax", + 0x16c9a097 : "rpc_s_no_more_members", + 0x16c9a098 : "rpc_s_no_more_interfaces", + 0x16c9a099 : "rpc_s_invalid_name_service", + 0x16c9a09a : "rpc_s_no_name_mapping", + 0x16c9a09b : "rpc_s_profile_not_found", + 0x16c9a09c : "rpc_s_not_found", + 0x16c9a09d : "rpc_s_no_updates", + 0x16c9a09e : "rpc_s_update_failed", + 0x16c9a09f : "rpc_s_no_match_exported", + 0x16c9a0a0 : "rpc_s_entry_not_found", + 0x16c9a0a1 : "rpc_s_invalid_inquiry_context", + 0x16c9a0a2 : "rpc_s_interface_not_found", + 0x16c9a0a3 : "rpc_s_group_member_not_found", + 0x16c9a0a4 : "rpc_s_entry_already_exists", + 0x16c9a0a5 : "rpc_s_nsinit_failure", + 0x16c9a0a6 : "rpc_s_unsupported_name_syntax", + 0x16c9a0a7 : "rpc_s_no_more_elements", + 0x16c9a0a8 : "rpc_s_no_ns_permission", + 0x16c9a0a9 : "rpc_s_invalid_inquiry_type", + 0x16c9a0aa : "rpc_s_profile_element_not_found", + 0x16c9a0ab : "rpc_s_profile_element_replaced", + 0x16c9a0ac : "rpc_s_import_already_done", + 0x16c9a0ad : "rpc_s_database_busy", + 0x16c9a0ae : "rpc_s_invalid_import_context", + 0x16c9a0af : "rpc_s_uuid_set_not_found", + 0x16c9a0b0 : "rpc_s_uuid_member_not_found", + 0x16c9a0b1 : "rpc_s_no_interfaces_exported", + 0x16c9a0b2 : "rpc_s_tower_set_not_found", + 0x16c9a0b3 : "rpc_s_tower_member_not_found", + 0x16c9a0b4 : "rpc_s_obj_uuid_not_found", + 0x16c9a0b5 : "rpc_s_no_more_bindings", + 0x16c9a0b6 : "rpc_s_invalid_priority", + 0x16c9a0b7 : "rpc_s_not_rpc_entry", + 0x16c9a0b8 : "rpc_s_invalid_lookup_context", + 0x16c9a0b9 : "rpc_s_binding_vector_full", + 0x16c9a0ba : "rpc_s_cycle_detected", + 0x16c9a0bb : "rpc_s_nothing_to_export", + 0x16c9a0bc : "rpc_s_nothing_to_unexport", + 0x16c9a0bd : "rpc_s_invalid_vers_option", + 0x16c9a0be : "rpc_s_no_rpc_data", + 0x16c9a0bf : "rpc_s_mbr_picked", + 0x16c9a0c0 : "rpc_s_not_all_objs_unexported", + 0x16c9a0c1 : "rpc_s_no_entry_name", + 0x16c9a0c2 : "rpc_s_priority_group_done", + 0x16c9a0c3 : "rpc_s_partial_results", + 0x16c9a0c4 : "rpc_s_no_env_setup", + 0x16c9a0c5 : "twr_s_unknown_sa", + 0x16c9a0c6 : "twr_s_unknown_tower", + 0x16c9a0c7 : "twr_s_not_implemented", + 0x16c9a0c8 : "rpc_s_max_calls_too_small", + 0x16c9a0c9 : "rpc_s_cthread_create_failed", + 0x16c9a0ca : "rpc_s_cthread_pool_exists", + 0x16c9a0cb : "rpc_s_cthread_no_such_pool", + 0x16c9a0cc : "rpc_s_cthread_invoke_disabled", + 0x16c9a0cd : "ept_s_cant_perform_op", + 0x16c9a0ce : "ept_s_no_memory", + 0x16c9a0cf : "ept_s_database_invalid", + 0x16c9a0d0 : "ept_s_cant_create", + 0x16c9a0d1 : "ept_s_cant_access", + 0x16c9a0d2 : "ept_s_database_already_open", + 0x16c9a0d3 : "ept_s_invalid_entry", + 0x16c9a0d4 : "ept_s_update_failed", + 0x16c9a0d5 : "ept_s_invalid_context", + 0x16c9a0d6 : "ept_s_not_registered", + 0x16c9a0d7 : "ept_s_server_unavailable", + 0x16c9a0d8 : "rpc_s_underspecified_name", + 0x16c9a0d9 : "rpc_s_invalid_ns_handle", + 0x16c9a0da : "rpc_s_unknown_error", + 0x16c9a0db : "rpc_s_ss_char_trans_open_fail", + 0x16c9a0dc : "rpc_s_ss_char_trans_short_file", + 0x16c9a0dd : "rpc_s_ss_context_damaged", + 0x16c9a0de : "rpc_s_ss_in_null_context", + 0x16c9a0df : "rpc_s_socket_failure", + 0x16c9a0e0 : "rpc_s_unsupported_protect_level", + 0x16c9a0e1 : "rpc_s_invalid_checksum", + 0x16c9a0e2 : "rpc_s_invalid_credentials", + 0x16c9a0e3 : "rpc_s_credentials_too_large", + 0x16c9a0e4 : "rpc_s_call_id_not_found", + 0x16c9a0e5 : "rpc_s_key_id_not_found", + 0x16c9a0e6 : "rpc_s_auth_bad_integrity", + 0x16c9a0e7 : "rpc_s_auth_tkt_expired", + 0x16c9a0e8 : "rpc_s_auth_tkt_nyv", + 0x16c9a0e9 : "rpc_s_auth_repeat", + 0x16c9a0ea : "rpc_s_auth_not_us", + 0x16c9a0eb : "rpc_s_auth_badmatch", + 0x16c9a0ec : "rpc_s_auth_skew", + 0x16c9a0ed : "rpc_s_auth_badaddr", + 0x16c9a0ee : "rpc_s_auth_badversion", + 0x16c9a0ef : "rpc_s_auth_msg_type", + 0x16c9a0f0 : "rpc_s_auth_modified", + 0x16c9a0f1 : "rpc_s_auth_badorder", + 0x16c9a0f2 : "rpc_s_auth_badkeyver", + 0x16c9a0f3 : "rpc_s_auth_nokey", + 0x16c9a0f4 : "rpc_s_auth_mut_fail", + 0x16c9a0f5 : "rpc_s_auth_baddirection", + 0x16c9a0f6 : "rpc_s_auth_method", + 0x16c9a0f7 : "rpc_s_auth_badseq", + 0x16c9a0f8 : "rpc_s_auth_inapp_cksum", + 0x16c9a0f9 : "rpc_s_auth_field_toolong", + 0x16c9a0fa : "rpc_s_invalid_crc", + 0x16c9a0fb : "rpc_s_binding_incomplete", + 0x16c9a0fc : "rpc_s_key_func_not_allowed", + 0x16c9a0fd : "rpc_s_unknown_stub_rtl_if_vers", + 0x16c9a0fe : "rpc_s_unknown_ifspec_vers", + 0x16c9a0ff : "rpc_s_proto_unsupp_by_auth", + 0x16c9a100 : "rpc_s_authn_challenge_malformed", + 0x16c9a101 : "rpc_s_protect_level_mismatch", + 0x16c9a102 : "rpc_s_no_mepv", + 0x16c9a103 : "rpc_s_stub_protocol_error", + 0x16c9a104 : "rpc_s_class_version_mismatch", + 0x16c9a105 : "rpc_s_helper_not_running", + 0x16c9a106 : "rpc_s_helper_short_read", + 0x16c9a107 : "rpc_s_helper_catatonic", + 0x16c9a108 : "rpc_s_helper_aborted", + 0x16c9a109 : "rpc_s_not_in_kernel", + 0x16c9a10a : "rpc_s_helper_wrong_user", + 0x16c9a10b : "rpc_s_helper_overflow", + 0x16c9a10c : "rpc_s_dg_need_way_auth", + 0x16c9a10d : "rpc_s_unsupported_auth_subtype", + 0x16c9a10e : "rpc_s_wrong_pickle_type", + 0x16c9a10f : "rpc_s_not_listening", + 0x16c9a110 : "rpc_s_ss_bad_buffer", + 0x16c9a111 : "rpc_s_ss_bad_es_action", + 0x16c9a112 : "rpc_s_ss_wrong_es_version", + 0x16c9a113 : "rpc_s_fault_user_defined", + 0x16c9a114 : "rpc_s_ss_incompatible_codesets", + 0x16c9a115 : "rpc_s_tx_not_in_transaction", + 0x16c9a116 : "rpc_s_tx_open_failed", + 0x16c9a117 : "rpc_s_partial_credentials", + 0x16c9a118 : "rpc_s_ss_invalid_codeset_tag", + 0x16c9a119 : "rpc_s_mgmt_bad_type", + 0x16c9a11a : "rpc_s_ss_invalid_char_input", + 0x16c9a11b : "rpc_s_ss_short_conv_buffer", + 0x16c9a11c : "rpc_s_ss_iconv_error", + 0x16c9a11d : "rpc_s_ss_no_compat_codeset", + 0x16c9a11e : "rpc_s_ss_no_compat_charsets", + 0x16c9a11f : "dce_cs_c_ok", + 0x16c9a120 : "dce_cs_c_unknown", + 0x16c9a121 : "dce_cs_c_notfound", + 0x16c9a122 : "dce_cs_c_cannot_open_file", + 0x16c9a123 : "dce_cs_c_cannot_read_file", + 0x16c9a124 : "dce_cs_c_cannot_allocate_memory", + 0x16c9a125 : "rpc_s_ss_cleanup_failed", + 0x16c9a126 : "rpc_svc_desc_general", + 0x16c9a127 : "rpc_svc_desc_mutex", + 0x16c9a128 : "rpc_svc_desc_xmit", + 0x16c9a129 : "rpc_svc_desc_recv", + 0x16c9a12a : "rpc_svc_desc_dg_state", + 0x16c9a12b : "rpc_svc_desc_cancel", + 0x16c9a12c : "rpc_svc_desc_orphan", + 0x16c9a12d : "rpc_svc_desc_cn_state", + 0x16c9a12e : "rpc_svc_desc_cn_pkt", + 0x16c9a12f : "rpc_svc_desc_pkt_quotas", + 0x16c9a130 : "rpc_svc_desc_auth", + 0x16c9a131 : "rpc_svc_desc_source", + 0x16c9a132 : "rpc_svc_desc_stats", + 0x16c9a133 : "rpc_svc_desc_mem", + 0x16c9a134 : "rpc_svc_desc_mem_type", + 0x16c9a135 : "rpc_svc_desc_dg_pktlog", + 0x16c9a136 : "rpc_svc_desc_thread_id", + 0x16c9a137 : "rpc_svc_desc_timestamp", + 0x16c9a138 : "rpc_svc_desc_cn_errors", + 0x16c9a139 : "rpc_svc_desc_conv_thread", + 0x16c9a13a : "rpc_svc_desc_pid", + 0x16c9a13b : "rpc_svc_desc_atfork", + 0x16c9a13c : "rpc_svc_desc_cma_thread", + 0x16c9a13d : "rpc_svc_desc_inherit", + 0x16c9a13e : "rpc_svc_desc_dg_sockets", + 0x16c9a13f : "rpc_svc_desc_timer", + 0x16c9a140 : "rpc_svc_desc_threads", + 0x16c9a141 : "rpc_svc_desc_server_call", + 0x16c9a142 : "rpc_svc_desc_nsi", + 0x16c9a143 : "rpc_svc_desc_dg_pkt", + 0x16c9a144 : "rpc_m_cn_ill_state_trans_sa", + 0x16c9a145 : "rpc_m_cn_ill_state_trans_ca", + 0x16c9a146 : "rpc_m_cn_ill_state_trans_sg", + 0x16c9a147 : "rpc_m_cn_ill_state_trans_cg", + 0x16c9a148 : "rpc_m_cn_ill_state_trans_sr", + 0x16c9a149 : "rpc_m_cn_ill_state_trans_cr", + 0x16c9a14a : "rpc_m_bad_pkt_type", + 0x16c9a14b : "rpc_m_prot_mismatch", + 0x16c9a14c : "rpc_m_frag_toobig", + 0x16c9a14d : "rpc_m_unsupp_stub_rtl_if", + 0x16c9a14e : "rpc_m_unhandled_callstate", + 0x16c9a14f : "rpc_m_call_failed", + 0x16c9a150 : "rpc_m_call_failed_no_status", + 0x16c9a151 : "rpc_m_call_failed_errno", + 0x16c9a152 : "rpc_m_call_failed_s", + 0x16c9a153 : "rpc_m_call_failed_c", + 0x16c9a154 : "rpc_m_errmsg_toobig", + 0x16c9a155 : "rpc_m_invalid_srchattr", + 0x16c9a156 : "rpc_m_nts_not_found", + 0x16c9a157 : "rpc_m_invalid_accbytcnt", + 0x16c9a158 : "rpc_m_pre_v2_ifspec", + 0x16c9a159 : "rpc_m_unk_ifspec", + 0x16c9a15a : "rpc_m_recvbuf_toosmall", + 0x16c9a15b : "rpc_m_unalign_authtrl", + 0x16c9a15c : "rpc_m_unexpected_exc", + 0x16c9a15d : "rpc_m_no_stub_data", + 0x16c9a15e : "rpc_m_eventlist_full", + 0x16c9a15f : "rpc_m_unk_sock_type", + 0x16c9a160 : "rpc_m_unimp_call", + 0x16c9a161 : "rpc_m_invalid_seqnum", + 0x16c9a162 : "rpc_m_cant_create_uuid", + 0x16c9a163 : "rpc_m_pre_v2_ss", + 0x16c9a164 : "rpc_m_dgpkt_pool_corrupt", + 0x16c9a165 : "rpc_m_dgpkt_bad_free", + 0x16c9a166 : "rpc_m_lookaside_corrupt", + 0x16c9a167 : "rpc_m_alloc_fail", + 0x16c9a168 : "rpc_m_realloc_fail", + 0x16c9a169 : "rpc_m_cant_open_file", + 0x16c9a16a : "rpc_m_cant_read_addr", + 0x16c9a16b : "rpc_svc_desc_libidl", + 0x16c9a16c : "rpc_m_ctxrundown_nomem", + 0x16c9a16d : "rpc_m_ctxrundown_exc", + 0x16c9a16e : "rpc_s_fault_codeset_conv_error", + 0x16c9a16f : "rpc_s_no_call_active", + 0x16c9a170 : "rpc_s_cannot_support", + 0x16c9a171 : "rpc_s_no_context_available", +} + +class Exception(Exception): + pass + +##class DCERPCException(Exception): +## def __init__( self, error_code): +## Exception.__init__(self) +## self.error_code = error_code +## +## def get_error_code( self ): +## return self.error_code +## +## def get_packet( self ): +## return self.packet +## +## def __str__( self ): +## key = self.error_code +## if (rpc_status_codes.has_key(key)): +## error_msg_short = rpc_status_codes[key] +## return 'DCERPC Runtime Error: code: 0x%x - %s ' % (self.error_code, error_msg_short) +## else: +## return 'DCERPC Runtime Error: unknown error code: 0x%x' % (self.error_code) + +# Context Item +class CtxItem(Structure): + structure = ( + ('ContextID',' 0 else 0)+len(pduData)+len(pad)+len(sec_trailer)'), # 8 + ('auth_len',' 0 else 0)'), + ('pduData',':'), + ('_pad', '_-pad','(4 - ((self._SIZE + (16 if (self["flags"] & 0x80) > 0 else 0) + len(self["pduData"])) & 3) & 3)'), + ('pad', ':'), + ('_sec_trailer', '_-sec_trailer', '8 if self["auth_len"] > 0 else 0'), + ('sec_trailer',':'), + ('auth_dataLen','_-auth_data','self["auth_len"]'), + ('auth_data',':'), + ) + + def __init__(self, data = None, alignment = 0): + Structure.__init__(self,data, alignment) + if data is None: + self['ver_major'] = 5 + self['ver_minor'] = 0 + self['flags'] = MSRPC_FIRSTFRAG | MSRPC_LASTFRAG + self['type'] = MSRPC_REQUEST + self.__frag_len_set = 0 + self['auth_len'] = 0 + self['pduData'] = '' + self['auth_data'] = '' + self['sec_trailer'] = '' + self['pad'] = '' + + def get_header_size(self): + return self._SIZE + (16 if (self["flags"] & MSRPC_NOUUID) > 0 else 0) + + def get_packet(self): + if self['auth_data'] != '': + self['auth_len'] = len(self['auth_data']) + # The sec_trailer structure MUST be 4-byte aligned with respect to + # the beginning of the PDU. Padding octets MUST be used to align the + # sec_trailer structure if its natural beginning is not already 4-byte aligned + ##self['pad'] = '\xAA' * (4 - ((self._SIZE + len(self['pduData'])) & 3) & 3) + + return self.getData() + + +class MSRPCRequestHeader(MSRPCHeader): + _SIZE = 24 + commonHdr = MSRPCHeader.commonHdr + ( + ('alloc_hint',' 0 else 0' ), # 22 + ('uuid',':'), # 22 + ) + + def __init__(self, data = None, alignment = 0): + MSRPCHeader.__init__(self, data, alignment) + if data is None: + self['type'] = MSRPC_REQUEST + self['ctx_id'] = 0 + self['uuid'] = '' + + +class MSRPCRespHeader(MSRPCHeader): + _SIZE = 24 + commonHdr = MSRPCHeader.commonHdr + ( + ('alloc_hint',' 0 else 0'), + ('sec_trailer',':'), + ('auth_dataLen','_-auth_data','self["auth_len"]'), + ('auth_data',':'), + ) + def __init__(self, data = None, alignment = 0): + self.__ctx_items = [] + MSRPCHeader.__init__(self,data,alignment) + if data is None: + self['Pad'] = '' + self['ctx_items'] = '' + self['sec_trailer'] = '' + self['auth_data'] = '' + + def getCtxItems(self): + return self.__ctx_items + + def getCtxItem(self,index): + return self.__ctx_items[index-1] + + def fromString(self, data): + Structure.fromString(self,data) + # Parse the ctx_items + data = self['ctx_items'] + for i in range(self['ctx_num']): + item = CtxItemResult(data) + self.__ctx_items.append(item) + data = data[len(item):] + + +class MSRPCBindNak(Structure): + structure = ( + ('RejectedReason',' +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +"""Tools to convert between Python datetime instances and Microsoft times. +""" +from datetime import datetime, timedelta, tzinfo +from calendar import timegm + + +# http://support.microsoft.com/kb/167296 +# How To Convert a UNIX time_t to a Win32 FILETIME or SYSTEMTIME +EPOCH_AS_FILETIME = 116444736000000000 # January 1, 1970 as MS file time +HUNDREDS_OF_NANOSECONDS = 10000000 + + +ZERO = timedelta(0) +HOUR = timedelta(hours=1) + + +class UTC(tzinfo): + """UTC""" + def utcoffset(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return ZERO + + +utc = UTC() + + +def dt_to_filetime(dt): + """Converts a datetime to Microsoft filetime format. If the object is + time zone-naive, it is forced to UTC before conversion. + + >>> "%.0f" % dt_to_filetime(datetime(2009, 7, 25, 23, 0)) + '128930364000000000' + + >>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0, tzinfo=utc)) + '116444736000000000' + + >>> "%.0f" % dt_to_filetime(datetime(1970, 1, 1, 0, 0)) + '116444736000000000' + + >>> dt_to_filetime(datetime(2009, 7, 25, 23, 0, 0, 100)) + 128930364000001000 + """ + if (dt.tzinfo is None) or (dt.tzinfo.utcoffset(dt) is None): + dt = dt.replace(tzinfo=utc) + ft = EPOCH_AS_FILETIME + (timegm(dt.timetuple()) * HUNDREDS_OF_NANOSECONDS) + return ft + (dt.microsecond * 10) + + +def filetime_to_dt(ft): + """Converts a Microsoft filetime number to a Python datetime. The new + datetime object is time zone-naive but is equivalent to tzinfo=utc. + + >>> filetime_to_dt(116444736000000000) + datetime.datetime(1970, 1, 1, 0, 0) + + >>> filetime_to_dt(128930364000000000) + datetime.datetime(2009, 7, 25, 23, 0) + + >>> filetime_to_dt(128930364000001000) + datetime.datetime(2009, 7, 25, 23, 0, 0, 100) + """ + # Get seconds and remainder in terms of Unix epoch + (s, ns100) = divmod(ft - EPOCH_AS_FILETIME, HUNDREDS_OF_NANOSECONDS) + # Convert to datetime object + dt = datetime.utcfromtimestamp(s) + # Add remainder in as microseconds. Python 3.2 requires an integer + dt = dt.replace(microsecond=(ns100 // 10)) + return dt + + +if __name__ == "__main__": + import doctest + + doctest.testmod() + diff --git a/py3-kms/formatText.py b/py3-kms/formatText.py new file mode 100644 index 0000000..e4f2994 --- /dev/null +++ b/py3-kms/formatText.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +import sys + +def byterize(obj): + objdict = obj.__dict__['fields'] + + def do_encode(dictio, key): + if isinstance(dictio[key], str) and len(dictio[key]) > 0 and key not in ['SecondaryAddr']: + dictio[key] = dictio[key].encode('latin-1') + elif hasattr(dictio[key], '__dict__'): + subdictio = dictio[key].__dict__['fields'] + for subkey in subdictio: + do_encode(subdictio, subkey) + + for field in objdict: + do_encode(objdict, field) + + return obj + + +def justify(astring, indent = 35, break_every = 100): + str_indent = ('\n' + ' ' * indent) + splitted = astring.split('\n') + longests = [(n, s) for n, s in enumerate(splitted) if len(s) >= break_every] + + for longest in longests: + lines = [] + for i in range(0, len(longest[1]), break_every): + lines.append(longest[1][i : i + break_every]) + splitted[longest[0]] = str_indent.join(lines) + + if len(splitted) > 1: + justy = str_indent.join(splitted) + else: + justy = str_indent + str_indent.join(splitted) + + return justy + + +class ShellStyle(object): + def style(self, s, style): + return style + s + '\033[0m' + + def green(self, s): + return self.style(s, '\033[92m') + + def blue(self, s): + return self.style(s, '\033[94m') + + def yellow(self, s): + return self.style(s, '\033[93m') + + def red(self, s): + return self.style(s, '\033[91m') + + def magenta(self, s): + return self.style(s, '\033[95m') + + def cyan(self, s): + return self.style(s, '\033[96m') + + def white(self, s): + return self.style(s, '\033[97m') + + def bold(self, s): + return self.style(s, '\033[1m') + + def underline(self, s): + return self.style(s, '\033[4m') + + + + +def shell_message(nshell): + + shelldict = {0: ShellStyle().yellow("Client generating RPC Bind Request..."), + 1: ShellStyle().yellow("Client sending RPC Bind Request...") + ShellStyle().red("\t\t\t\t===============>"), + 2: ShellStyle().red("===============>\t\t") + ShellStyle().yellow("Server received RPC Bind Request !!!"), + 3: ShellStyle().yellow("\t\t\t\tServer parsing RPC Bind Request..."), + 4: ShellStyle().yellow("\t\t\t\tServer generating RPC Bind Response..."), + 5: ShellStyle().red("<===============\t\t") + ShellStyle().yellow("Server sending RPC Bind Response..."), + 6: ShellStyle().green("\t\t\t\tRPC Bind acknowledged !!!\n"), + 7: ShellStyle().yellow("Client received RPC Bind Response !!!") + ShellStyle().red("\t\t\t\t<==============="), + 8: ShellStyle().green("RPC Bind acknowledged !!!\n"), + 9: ShellStyle().blue("Client generating Activation Request dictionary..."), + 10: ShellStyle().blue("Client generating Activation Request data..."), + 11: ShellStyle().blue("Client generating RPC Activation Request..."), + 12: ShellStyle().blue("Client sending RPC Activation Request...") + ShellStyle().red("\t\t\t===============>"), + 13: ShellStyle().red("===============>\t\t") + ShellStyle().blue("Server received RPC Activation Request !!!"), + 14: ShellStyle().blue("\t\t\t\tServer parsing RPC Activation Request..."), + 15: ShellStyle().blue("\t\t\t\tServer processing KMS Activation Request..."), + 16: ShellStyle().blue("\t\t\t\tServer processing KMS Activation Response..."), + 17: ShellStyle().blue("\t\t\t\tServer generating RPC Activation Response..."), + 18: ShellStyle().red("<===============\t\t") + ShellStyle().blue("Server sending RPC Activation Response..."), + 19: ShellStyle().green("\t\t\t\tServer responded, now in Stand by...\n"), + 20: ShellStyle().blue("Client received Response !!!") + ShellStyle().red("\t\t\t\t\t<==============="), + 21: ShellStyle().green("Activation Done !!!"), + -1: ShellStyle().red("\t\t\t\t\t\t\t\tServer receiving"), + -2: ShellStyle().red("Client sending"), + -3: ShellStyle().red("Client receiving"), + -4: ShellStyle().red("\t\t\t\t\t\t\t\tServer sending") + } + + if isinstance(nshell, list): + for n in nshell: + print(shelldict[n]) + else: + print(shelldict[nshell]) + + + + diff --git a/py3-kms/kmsBase.py b/py3-kms/kmsBase.py new file mode 100644 index 0000000..74fb6b6 --- /dev/null +++ b/py3-kms/kmsBase.py @@ -0,0 +1,638 @@ +#!/usr/bin/env python3 + +import binascii +import logging +import datetime +import os +import struct +import sys +import time +import uuid + +from structure import Structure +import filetimes +import kmsPidGenerator +from formatText import justify, shell_message, byterize + +# sqlite3 is optional +try: + import sqlite3 +except: + pass + + +class UUID(Structure): + commonHdr = () + structure = ( + ('raw', '16s'), + ) + + def get(self): + return uuid.UUID(bytes_le=str(self).encode('latin-1')) #*2to3* + +class kmsBase: + class kmsRequestStruct(Structure): + commonHdr = () + structure = ( + ('versionMinor', '> 4 + + # Remainding bytes. + k = messageSize & 0xf + + # Hash. + for i in range(0, j): + xorBuffer(message, i << 4, hashBuffer, 16) + hashBuffer = bytearray(aes.encrypt(hashBuffer, key, len(key))) + + # Bit Padding. + ii = 0 + for i in range(j << 4, k + (j << 4)): + lastBlock[ii] = message[i] + ii += 1 + lastBlock[k] = 0x80 + + xorBuffer(lastBlock, 0, hashBuffer, 16) + hashBuffer = bytearray(aes.encrypt(hashBuffer, key, len(key))) + + return hashBuffer #*2to3* + + def generateResponse(self, responseBuffer, thehash): + bodyLength = len(responseBuffer) + len(thehash) + response = self.ResponseV4() + response['response'] = responseBuffer + response['hash'] = thehash.decode('latin-1') #*2to3* + response['padding'] = self.getResponsePadding(bodyLength).decode('latin-1') #*2to3* + + ## Debug stuff. + shell_message(nshell = 16) + response = byterize(response) + logging.debug("KMS V4 Response: \n%s\n" % justify(response.dump(print_to_stdout = False))) + logging.debug("KMS V4 Response Bytes: \n%s\n" % justify(binascii.b2a_hex(str(response).encode('latin-1')).decode('utf-8'))) #*2to3* + + return str(response) + + def getResponse(self): + return self.responseData + + def generateRequest(self, requestBase): + thehash = self.generateHash(bytearray(str(requestBase).encode('latin-1'))) #*2to3* + + bodyLength = len(requestBase) + len(thehash) + + request = kmsRequestV4.RequestV4() + request['bodyLength1'] = bodyLength + request['bodyLength2'] = bodyLength + request['request'] = requestBase + request['hash'] = thehash.decode('latin-1') #*2to3* + request['padding'] = self.getResponsePadding(bodyLength).decode('latin-1') #*2to3* + + ## Debug stuff. + shell_message(nshell = 10) + request = byterize(request) + logging.debug("Request V4 Data: \n%s\n" % justify(request.dump(print_to_stdout = False))) + logging.debug("Request V4: \n%s\n" % justify(binascii.b2a_hex(str(request).encode('latin-1')).decode('utf-8'))) #*2to3* + + return request diff --git a/py3-kms/kmsRequestV5.py b/py3-kms/kmsRequestV5.py new file mode 100644 index 0000000..40d13d9 --- /dev/null +++ b/py3-kms/kmsRequestV5.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 + +import logging +import binascii +import hashlib +import random +import struct + +from kmsBase import kmsBase +from structure import Structure +import aes +from formatText import justify, shell_message, byterize + +class kmsRequestV5(kmsBase): + class RequestV5(Structure): + class Message(Structure): + commonHdr = () + structure = ( + ('salt', '16s'), + ('encrypted', '236s'), #kmsBase.kmsRequestStruct + ('padding', ':'), + ) + + commonHdr = () + structure = ( + ('bodyLength1', ' 16: + logging.error("HWID \"%s\" is invalid. Hex string is too long." % binascii.b2a_hex(config['hwid']).decode('utf-8').upper()) + return + except TypeError: + logging.error("HWID \"%s\" is invalid. Odd-length hex string." % binascii.b2a_hex(config['hwid']).decode('utf-8').upper()) + return + + logging.basicConfig(level=config['loglevel'], format='%(asctime)s %(levelname)-8s %(message)s', + datefmt='%a, %d %b %Y %H:%M:%S', filename=config['logfile'], filemode='w') + + try: + import sqlite3 + config['dbSupport'] = True + except: + logging.warning("Module \"sqlite3\" is not installed, database support disabled.") + config['dbSupport'] = False + server = socketserver.TCPServer((config['ip'], config['port']), kmsServer) + server.timeout = 5 + logging.info("TCP server listening at %s on port %d." % (config['ip'], config['port'])) + logging.info("HWID: %s" % binascii.b2a_hex(config['hwid']).decode('utf-8').upper()) + server.serve_forever() + + +class kmsServer(socketserver.BaseRequestHandler): + def setup(self): + self.connection = self.request + logging.info("Connection accepted: %s:%d" % (self.client_address[0], self.client_address[1])) + + def handle(self): + while True: + # self.request is the TCP socket connected to the client + try: + self.data = self.connection.recv(1024) + except socket.error as e: #*2to3* + if e[0] == 104: + logging.error("Connection reset by peer.") + break + else: + raise + if self.data == '' or not self.data: + logging.warning("No data received !") + break + # self.data = bytearray(self.data.strip()) + # logging.debug(binascii.b2a_hex(str(self.data))) + packetType = MSRPCHeader(self.data)['type'] + if packetType == rpcBase.packetType['bindReq']: + logging.info("RPC bind request received.") + shell_message(nshell = [-2, 2]) + handler = rpcBind.handler(self.data, config) + elif packetType == rpcBase.packetType['request']: + logging.info("Received activation request.") + shell_message(nshell = [-2, 13]) + handler = rpcRequest.handler(self.data, config) + else: + logging.error("Invalid RPC request type ", packetType) + break + + handler.populate() + res = str(handler.getResponse()).encode('latin-1') #*2to3* + self.connection.send(res) + + if packetType == rpcBase.packetType['bindReq']: + logging.info("RPC bind acknowledged.") + shell_message(nshell = [-3, 5, 6]) + elif packetType == rpcBase.packetType['request']: + logging.info("Responded to activation request.") + shell_message(nshell = [-3, 18, 19]) + break + + def finish(self): + self.connection.close() + logging.info("Connection closed: %s:%d" % (self.client_address[0], self.client_address[1])) + +if __name__ == "__main__": + main() diff --git a/py3-kms/structure.py b/py3-kms/structure.py new file mode 100644 index 0000000..6557418 --- /dev/null +++ b/py3-kms/structure.py @@ -0,0 +1,793 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2003-2012 CORE Security Technologies +# +# This software is provided under under a slightly modified version +# of the Apache Software License. See the accompanying LICENSE file +# for more information. +# +""" Version of https://github.com/CoreSecurity/impacket/blob/python3/impacket/structure.py + with modifications in the function dump(...). + © Copyright 2018 Matteo ℱan +""" + +from struct import pack, unpack, calcsize + +# Trying to support both Python 2 and 3 +import sys + +if sys.version_info[0] == 2: + # Python 2.x + def b(x): + return x + def buildStr(x): + return x +else: + import codecs + def b(x): + if isinstance(x, bytes) is False: + return codecs.latin_1_encode(x)[0] + return x + def buildStr(x): + if isinstance(x, bytes): + return "".join(map(chr,x)) + else: + return x + + +class Structure: + """ sublcasses can define commonHdr and/or structure. + each of them is an tuple of either two: (fieldName, format) or three: (fieldName, ':', class) fields. + [it can't be a dictionary, because order is important] + + where format specifies how the data in the field will be converted to/from bytes (string) + class is the class to use when unpacking ':' fields. + + each field can only contain one value (or an array of values for *) + i.e. struct.pack('Hl',1,2) is valid, but format specifier 'Hl' is not (you must use 2 dfferent fields) + + format specifiers: + specifiers from module pack can be used with the same format + see struct.__doc__ (pack/unpack is finally called) + x [padding byte] + c [character] + b [signed byte] + B [unsigned byte] + h [signed short] + H [unsigned short] + l [signed long] + L [unsigned long] + i [signed integer] + I [unsigned integer] + q [signed long long (quad)] + Q [unsigned long long (quad)] + s [string (array of chars), must be preceded with length in format specifier, padded with zeros] + p [pascal string (includes byte count), must be preceded with length in format specifier, padded with zeros] + f [float] + d [double] + = [native byte ordering, size and alignment] + @ [native byte ordering, standard size and alignment] + ! [network byte ordering] + < [little endian] + > [big endian] + + usual printf like specifiers can be used (if started with %) + [not recommeneded, there is no why to unpack this] + + %08x will output an 8 bytes hex + %s will output a string + %s\\x00 will output a NUL terminated string + %d%d will output 2 decimal digits (against the very same specification of Structure) + ... + + some additional format specifiers: + : just copy the bytes from the field into the output string (input may be string, other structure, or anything responding to __str__()) (for unpacking, all what's left is returned) + z same as :, but adds a NUL byte at the end (asciiz) (for unpacking the first NUL byte is used as terminator) [asciiz string] + u same as z, but adds two NUL bytes at the end (after padding to an even size with NULs). (same for unpacking) [unicode string] + w DCE-RPC/NDR string (it's a macro for [ ' 2: + dataClassOrCode = field[2] + try: + self[field[0]] = self.unpack(field[1], data[:size], dataClassOrCode = dataClassOrCode, field = field[0]) + except Exception as e: + e.args += ("When unpacking field '%s | %s | %r[:%d]'" % (field[0], field[1], data, size),) + raise + + size = self.calcPackSize(field[1], self[field[0]], field[0]) + if self.alignment and size % self.alignment: + size += self.alignment - (size % self.alignment) + data = data[size:] + + return self + + def __setitem__(self, key, value): + self.fields[key] = value + self.data = None # force recompute + + def __getitem__(self, key): + return self.fields[key] + + def __delitem__(self, key): + del self.fields[key] + + def __str__(self): + return self.getData() + + def __len__(self): + # XXX: improve + return len(self.getData()) + + def pack(self, format, data, field = None): + if self.debug: + print(" pack( %s | %r | %s)" % (format, data, field)) + + if field: + addressField = self.findAddressFieldFor(field) + if (addressField is not None) and (data is None): + return b'' + + # void specifier + if format[:1] == '_': + return b'' + + # quote specifier + if format[:1] == "'" or format[:1] == '"': + return b(format[1:]) + + # code specifier + two = format.split('=') + if len(two) >= 2: + try: + return self.pack(two[0], data) + except: + fields = {'self':self} + fields.update(self.fields) + return self.pack(two[0], eval(two[1], {}, fields)) + + # address specifier + two = format.split('&') + if len(two) == 2: + try: + return self.pack(two[0], data) + except: + if (two[1] in self.fields) and (self[two[1]] is not None): + return self.pack(two[0], id(self[two[1]]) & ((1<<(calcsize(two[0])*8))-1) ) + else: + return self.pack(two[0], 0) + + # length specifier + two = format.split('-') + if len(two) == 2: + try: + return self.pack(two[0],data) + except: + return self.pack(two[0], self.calcPackFieldSize(two[1])) + + # array specifier + two = format.split('*') + if len(two) == 2: + answer = b'' + for each in data: + answer += self.pack(two[1], each) + if two[0]: + if two[0].isdigit(): + if int(two[0]) != len(data): + raise Exception("Array field has a constant size, and it doesn't match the actual value") + else: + return self.pack(two[0], len(data))+answer + return answer + + # "printf" string specifier + if format[:1] == '%': + # format string like specifier + return format % data + + # asciiz specifier + if format[:1] == 'z': + return b(data)+b'\0' + + # unicode specifier + if format[:1] == 'u': + return b(data)+b'\0\0' + (len(data) & 1 and b'\0' or b'') + + # DCE-RPC/NDR string specifier + if format[:1] == 'w': + if len(data) == 0: + data = '\0\0' + elif len(data) % 2: + data += '\0' + l = pack('= 2: + return self.unpack(two[0],data) + + # length specifier + two = format.split('-') + if len(two) == 2: + return self.unpack(two[0],data) + + # array specifier + two = format.split('*') + if len(two) == 2: + answer = [] + sofar = 0 + if two[0].isdigit(): + number = int(two[0]) + elif two[0]: + sofar += self.calcUnpackSize(two[0], data) + number = self.unpack(two[0], data[:sofar]) + else: + number = -1 + while number and sofar < len(data): + nsofar = sofar + self.calcUnpackSize(two[1],data[sofar:]) + answer.append(self.unpack(two[1], data[sofar:nsofar], dataClassOrCode)) + number -= 1 + sofar = nsofar + return answer + + # "printf" string specifier + if format[:1] == '%': + # format string like specifier + return format % data + + # asciiz specifier + if format == 'z': + if data[-1] != '\x00': + raise Exception("%s 'z' field is not NUL terminated: %r" % (field, data)) + return data[:-1] # remove trailing NUL + + # unicode specifier + if format == 'u': + if data[-2:] != '\x00\x00': + raise Exception("%s 'u' field is not NUL-NUL terminated: %r" % (field, data)) + return data[:-2] # remove trailing NUL + + # DCE-RPC/NDR string specifier + if format == 'w': + l = unpack('=0: + return buildStr(unpack(format, b(data))[0]) + else: + return unpack(format, b(data))[0] + + def calcPackSize(self, format, data, field = None): + #print( " calcPackSize %s:%r" % (format, data)) + if field: + addressField = self.findAddressFieldFor(field) + if addressField is not None: + if not self[addressField]: + return 0 + + # void specifier + if format[:1] == '_': + return 0 + + # quote specifier + if format[:1] == "'" or format[:1] == '"': + return len(format)-1 + + # address specifier + two = format.split('&') + if len(two) == 2: + return self.calcPackSize(two[0], data) + + # code specifier + two = format.split('=') + if len(two) >= 2: + return self.calcPackSize(two[0], data) + + # length specifier + two = format.split('-') + if len(two) == 2: + return self.calcPackSize(two[0], data) + + # array specifier + two = format.split('*') + if len(two) == 2: + answer = 0 + if two[0].isdigit(): + if int(two[0]) != len(data): + raise Exception("Array field has a constant size, and it doesn't match the actual value") + elif two[0]: + answer += self.calcPackSize(two[0], len(data)) + + for each in data: + answer += self.calcPackSize(two[1], each) + return answer + + # "printf" string specifier + if format[:1] == '%': + # format string like specifier + return len(format % data) + + # asciiz specifier + if format[:1] == 'z': + return len(data)+1 + + # asciiz specifier + if format[:1] == 'u': + l = len(data) + return l + (l & 1 and 3 or 2) + + # DCE-RPC/NDR string specifier + if format[:1] == 'w': + l = len(data) + return int((12+l+(l % 2))) + + # literal specifier + if format[:1] == ':': + return len(data) + + # struct like specifier + return calcsize(format) + + def calcUnpackSize(self, format, data, field = None): + if self.debug: + print(" calcUnpackSize( %s | %s | %r)" % (field, format, data)) + + # void specifier + if format[:1] == '_': + return 0 + + addressField = self.findAddressFieldFor(field) + if addressField is not None: + if not self[addressField]: + return 0 + + try: + lengthField = self.findLengthFieldFor(field) + return int(self[lengthField]) + except: + pass + + # XXX: Try to match to actual values, raise if no match + + # quote specifier + if format[:1] == "'" or format[:1] == '"': + return len(format)-1 + + # address specifier + two = format.split('&') + if len(two) == 2: + return self.calcUnpackSize(two[0], data) + + # code specifier + two = format.split('=') + if len(two) >= 2: + return self.calcUnpackSize(two[0], data) + + # length specifier + two = format.split('-') + if len(two) == 2: + return self.calcUnpackSize(two[0], data) + + # array specifier + two = format.split('*') + if len(two) == 2: + answer = 0 + if two[0]: + if two[0].isdigit(): + number = int(two[0]) + else: + answer += self.calcUnpackSize(two[0], data) + number = self.unpack(two[0], data[:answer]) + + while number: + number -= 1 + answer += self.calcUnpackSize(two[1], data[answer:]) + else: + while answer < len(data): + answer += self.calcUnpackSize(two[1], data[answer:]) + return answer + + # "printf" string specifier + if format[:1] == '%': + raise Exception("Can't guess the size of a printf like specifier for unpacking") + + # asciiz specifier + if format[:1] == 'z': + return data.index('\x00')+1 + + # asciiz specifier + if format[:1] == 'u': + l = data.index('\x00\x00') + return l + (l & 1 and 3 or 2) + + # DCE-RPC/NDR string specifier + if format[:1] == 'w': + l = unpack('L'), + ('code1','>L=len(arr1)*2+0x1000'), + ) + + def populate(self, a): + a['default'] = 'hola' + a['int1'] = 0x3131 + a['int3'] = 0x45444342 + a['z1'] = 'hola' + a['u1'] = 'hola'.encode('utf_16_le') + a[':1'] = ':1234:' + a['arr1'] = (0x12341234,0x88990077,0x41414141) + # a['len1'] = 0x42424242 + +class _Test_fixedLength(_Test_simple): + def populate(self, a): + _Test_simple.populate(self, a) + a['len1'] = 0x42424242 + +class _Test_simple_aligned4(_Test_simple): + alignment = 4 + +class _Test_nested(_StructureTest): + class theClass(Structure): + class _Inner(Structure): + structure = (('data', 'z'),) + + structure = ( + ('nest1', ':', _Inner), + ('nest2', ':', _Inner), + ('int', '> 8)'), + ('pad', '_','((iv >>2) & 0x3F)'), + ('keyid', '_','( iv & 0x03 )'), + ('dataLen', '_-data', 'len(inputDataLeft)-4'), + ('data',':'), + ('icv','>L'), + ) + + def populate(self, a): + a['init_vector']=0x01020304 + #a['pad']=int('01010101',2) + a['pad']=int('010101',2) + a['keyid']=0x07 + a['data']="\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9" + a['icv'] = 0x05060708 + #a['iv'] = 0x01020304 + +if __name__ == '__main__': + _Test_simple().run() + + try: + _Test_fixedLength().run() + except: + print("cannot repack because length is bogus") + + _Test_simple_aligned4().run() + _Test_nested().run() + _Test_Optional().run() + _Test_Optional_sparse().run() + _Test_AsciiZArray().run() + _Test_UnpackCode().run() + _Test_AAA().run() diff --git a/py3-kms/timezones.py b/py3-kms/timezones.py new file mode 100644 index 0000000..e95adb2 --- /dev/null +++ b/py3-kms/timezones.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python3 + +# vim: set ts=2 sw=2 et sts=2 ai: +# +# Copyright 2009 Google Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Disable the invalid name warning as we are inheriting from a standard library +# object. +# pylint: disable=invalid-name,protected-access + +""" +Stripped down version of `python-datetime-tz` +( https://github.com/mithro/python-datetime-tz/blob/master/datetime_tz/__init__.py ) +that only contains the "find local timezone" bits. +""" + + +import datetime +import os.path +import time +import warnings +import pytz + +# Need to patch pytz.utc to have a _utcoffset so you can normalize/localize +# using it. +pytz.utc._utcoffset = datetime.timedelta() + +timedelta = datetime.timedelta + + +def _tzinfome(tzinfo): + """Gets a tzinfo object from a string. + + Args: + tzinfo: A string (or string like) object, or a datetime.tzinfo object. + + Returns: + An datetime.tzinfo object. + + Raises: + UnknownTimeZoneError: If the timezone given can't be decoded. + """ + if not isinstance(tzinfo, datetime.tzinfo): + try: + tzinfo = pytz.timezone(tzinfo) + assert tzinfo.zone in pytz.all_timezones + except AttributeError: + raise pytz.UnknownTimeZoneError("Unknown timezone! %s" % tzinfo) + return tzinfo + + +# Our "local" timezone +_localtz = None + + +def localtz(): + """Get the local timezone. + + Returns: + The localtime timezone as a tzinfo object. + """ + # pylint: disable=global-statement + global _localtz + if _localtz is None: + _localtz = detect_timezone() + return _localtz + + +def detect_timezone(): + """Try and detect the timezone that Python is currently running in. + + We have a bunch of different methods for trying to figure this out (listed in + order they are attempted). + * In windows, use win32timezone.TimeZoneInfo.local() + * Try TZ environment variable. + * Try and find /etc/timezone file (with timezone name). + * Try and find /etc/localtime file (with timezone data). + * Try and match a TZ to the current dst/offset/shortname. + + Returns: + The detected local timezone as a tzinfo object + + Raises: + pytz.UnknownTimeZoneError: If it was unable to detect a timezone. + """ + + # First we try the TZ variable + tz = _detect_timezone_environ() + if tz is not None: + return tz + + # Second we try /etc/timezone and use the value in that + tz = _detect_timezone_etc_timezone() + if tz is not None: + return tz + + # Next we try and see if something matches the tzinfo in /etc/localtime + tz = _detect_timezone_etc_localtime() + if tz is not None: + return tz + + # Next we try and use a similiar method to what PHP does. + # We first try to search on time.tzname, time.timezone, time.daylight to + # match a pytz zone. + warnings.warn("Had to fall back to worst detection method (the 'PHP' " + "method).") + + tz = _detect_timezone_php() + if tz is not None: + return tz + + raise pytz.UnknownTimeZoneError("Unable to detect your timezone!") + + +def _detect_timezone_environ(): + if "TZ" in os.environ: + try: + return pytz.timezone(os.environ["TZ"]) + except (IOError, pytz.UnknownTimeZoneError): + warnings.warn("You provided a TZ environment value (%r) we did not " + "understand!" % os.environ["TZ"]) + + +def _detect_timezone_etc_timezone(): + if os.path.exists("/etc/timezone"): + try: + tz = open("/etc/timezone").read().strip() + try: + return pytz.timezone(tz) + except (IOError, pytz.UnknownTimeZoneError) as ei: + warnings.warn("Your /etc/timezone file references a timezone (%r) that" + " is not valid (%r)." % (tz, ei)) + + # Problem reading the /etc/timezone file + except IOError as eo: + warnings.warn("Could not access your /etc/timezone file: %s" % eo) + + +def _load_local_tzinfo(): + """Load zoneinfo from local disk.""" + tzdir = os.environ.get("TZDIR", "/usr/share/zoneinfo/posix") + + localtzdata = {} + for dirpath, _, filenames in os.walk(tzdir): + for filename in filenames: + filepath = os.path.join(dirpath, filename) + name = os.path.relpath(filepath, tzdir) + + f = open(filepath, "rb") + tzinfo = pytz.tzfile.build_tzinfo(name, f) + f.close() + localtzdata[name] = tzinfo + + return localtzdata + + +def _detect_timezone_etc_localtime(): + """Detect timezone based on /etc/localtime file.""" + matches = [] + if os.path.exists("/etc/localtime"): + f = open("/etc/localtime", "rb") + localtime = pytz.tzfile.build_tzinfo("/etc/localtime", f) + f.close() + + # We want to match against the local database because /etc/localtime will + # be copied from that. Once we have found a name for /etc/localtime, we can + # use the name to get the "same" timezone from the inbuilt pytz database. + + tzdatabase = _load_local_tzinfo() + if tzdatabase: + tznames = tzdatabase.keys() + tzvalues = tzdatabase.__getitem__ + else: + tznames = pytz.all_timezones + tzvalues = _tzinfome + + # See if we can find a "Human Name" for this.. + for tzname in tznames: + tz = tzvalues(tzname) + + if dir(tz) != dir(localtime): + continue + + for attrib in dir(tz): + # Ignore functions and specials + if callable(getattr(tz, attrib)) or attrib.startswith("__"): + continue + + # This will always be different + if attrib == "zone" or attrib == "_tzinfos": + continue + + if getattr(tz, attrib) != getattr(localtime, attrib): + break + + # We get here iff break didn't happen, i.e. no meaningful attributes + # differ between tz and localtime + else: + # Try and get a timezone from pytz which has the same name as the zone + # which matches in the local database. + if tzname not in pytz.all_timezones: + warnings.warn("Skipping %s because not in pytz database." % tzname) + continue + + matches.append(_tzinfome(tzname)) + + matches.sort(key=lambda x: x.zone) + + if len(matches) == 1: + return matches[0] + + if len(matches) > 1: + warnings.warn("We detected multiple matches for your /etc/localtime. " + "(Matches where %s)" % matches) + return matches[0] + else: + warnings.warn("We detected no matches for your /etc/localtime.") + + # Register /etc/localtime as the timezone loaded. + pytz._tzinfo_cache["/etc/localtime"] = localtime + return localtime + + +def _detect_timezone_php(): + tomatch = (time.tzname[0], time.timezone, time.daylight) + now = datetime.datetime.now() + + matches = [] + for tzname in pytz.all_timezones: + try: + tz = pytz.timezone(tzname) + except IOError: + continue + + try: + indst = tz.localize(now).timetuple()[-1] + + if tomatch == (tz._tzname, -tz._utcoffset.seconds, indst): + matches.append(tzname) + + # pylint: disable=pointless-except + except AttributeError: + pass + + if len(matches) > 1: + warnings.warn("We detected multiple matches for the timezone, choosing " + "the first %s. (Matches where %s)" % (matches[0], matches)) + if matches: + return pytz.timezone(matches[0]) +