-
Notifications
You must be signed in to change notification settings - Fork 132
/
decrypt.py
executable file
·86 lines (63 loc) · 2.46 KB
/
decrypt.py
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
#!/usr/bin/env python3
import re
import sys
import base64
from hashlib import sha256
from binascii import hexlify, unhexlify
from Crypto.Cipher import AES
MAGIC = b"::::MAGIC::::"
def usage():
print("./decrypt.py <master.key> <hudson.util.Secret> <credentials.xml>")
sys.exit(0)
def decryptNewPassword(secret, p):
p = p[1:] #Strip the version
# Get the length of the IV, almost certainly 16 bytes, but calculating for completeness sake
iv_length = ((p[0] & 0xff) << 24) | ((p[1] & 0xff) << 16) | ((p[2] & 0xff) << 8) | (p[3] & 0xff)
# Strip the iv length
p = p[4:]
# Get the data length
data_length = ((p[0] & 0xff) << 24) | ((p[1] & 0xff) << 16) | ((p[2] & 0xff) << 8) | (p[3] & 0xff)
# Strip the data length
p = p[4:]
iv = p[:iv_length]
p = p[iv_length:]
o = AES.new(secret, AES.MODE_CBC, iv)
decrypted_p = o.decrypt(p)
# We may need to strip PKCS7 padding
fully_decrypted_blocks = decrypted_p[:-16]
possibly_padded_block = decrypted_p[-16:]
padding_length = possibly_padded_block[-1]
if padding_length <= 16: # Less than size of one block, so we have padding
possibly_padded_block = possibly_padded_block[:-padding_length]
pw = fully_decrypted_blocks + possibly_padded_block
pw = pw.decode('utf-8')
return pw
def decryptOldPassword(secret, p):
# Copying the old code, I have not verified if it works
o = AES.new(secret, AES.MODE_ECB)
x = o.decrypt(p)
assert MAGIC in x
return re.findall('(.*)' + MAGIC, x)[0]
def main():
if len(sys.argv) != 4:
usage()
master_key = open(sys.argv[1], 'rb').read()
hudson_secret_key = open(sys.argv[2], 'rb').read()
hashed_master_key = sha256(master_key).digest()[:16]
o = AES.new(hashed_master_key, AES.MODE_ECB)
secret = o.decrypt(hudson_secret_key)
secret = secret[:-16]
secret = secret[:16]
credentials = open(sys.argv[3]).read()
passwords = re.findall(r'<p(?:assword|rivateKey)>\{?(.*?)\}?</p(?:assword|rivateKey)>', credentials)
# You can find the password format at https://github.com/jenkinsci/jenkins/blob/master/core/src/main/java/hudson/util/Secret.java#L167-L216
for password in passwords:
p = base64.decodestring(bytes(password, 'utf-8'))
# Get payload version
payload_version = p[0]
if payload_version == 1:
print(decryptNewPassword(secret, p))
else: # Assuming we don't have a V2 payload, seeing as current crypto isn't horrible that's a fair assumption
print(decryptOldPassword(secret,p))
if __name__ == '__main__':
main()