-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathkmsRequestV4.py
More file actions
133 lines (104 loc) · 4.56 KB
/
kmsRequestV4.py
File metadata and controls
133 lines (104 loc) · 4.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import binascii
import time
import logging
from kmsBase import kmsBase
from structure import Structure
from aes import AES
from formatText import shell_message, justify, byterize
# v4 AES Key
key = bytearray(
[0x05, 0x3D, 0x83, 0x07, 0xF9, 0xE5, 0xF0, 0x88, 0xEB, 0x5E, 0xA6, 0x68, 0x6C, 0xF0, 0x37, 0xC7, 0xE4, 0xEF, 0xD2,
0xD6])
# Xor Buffer
def xorBuffer(source, offset, destination, size):
for i in range(0, size):
destination[i] ^= source[i + offset]
class kmsRequestV4(kmsBase):
class RequestV4(Structure):
commonHdr = ()
structure = (
('bodyLength1', '<I'),
('bodyLength2', '<I'),
('request', ':', kmsBase.kmsRequestStruct),
('hash', '16s'),
('padding', ':'),
)
class ResponseV4(Structure):
commonHdr = ()
structure = (
('bodyLength1', '<I=len(response) + len(hash)'),
('unknown', '!I=0x00000200'),
('bodyLength2', '<I=len(response) + len(hash)'),
('response', ':', kmsBase.kmsResponseStruct),
('hash', '16s'),
('padding', ':'),
)
def executeRequestLogic(self):
requestData = self.RequestV4(self.data)
response = self.serverLogic(requestData['request'])
thehash = self.generateHash(bytearray(str(response).encode('latin-1'))) # *2to3*
self.responseData = self.generateResponse(response, thehash)
time.sleep(1) # request sent back too quick for Windows 2008 R2, slow it down.
def generateHash(self, message):
"""
The KMS v4 hash is a variant of CMAC-AES-128. There are two key differences:
* The 'AES' used is modified in particular ways:
* The basic algorithm is Rjindael with a conceptual 160bit key and 128bit blocks.
This isn't part of the AES standard, but it works the way you'd expect.
Accordingly, the algorithm uses 11 rounds and a 192 byte expanded key.
* The trailing block is not XORed with a generated subkey, as defined in CMAC.
This is probably because the subkey generation algorithm is only defined for
situations where block and key size are the same.
"""
aes = AES()
messageSize = len(message)
lastBlock = bytearray(16)
hashBuffer = bytearray(16)
# MessageSize / Blocksize.
j = messageSize >> 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