forked from AlessandroZ/LaZagne
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request AlessandroZ#62 from righettod/master
Add new extractors
- Loading branch information
Showing
5 changed files
with
306 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
137 changes: 137 additions & 0 deletions
137
Windows/src/LaZagne/softwares/maven/mavenrepositories.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import os | ||
from config.write_output import print_output, print_debug | ||
from config.constant import * | ||
from config.header import Header | ||
from config.moduleInfo import ModuleInfo | ||
import xml.etree.ElementTree as ET | ||
|
||
class MavenRepositories(ModuleInfo): | ||
|
||
def __init__(self): | ||
options = {'command': '-mvn', 'action': 'store_true', 'dest': 'mavenrepositories', 'help': 'Maven repositories'} | ||
ModuleInfo.__init__(self, 'mavenrepositories', 'maven', options) | ||
# Interesting XML nodes in Maven repository configuration | ||
self.nodes_to_extract = ["id", "username", "password", "privateKey", "passphrase"] | ||
self.settings_namespace = "{http://maven.apache.org/SETTINGS/1.0.0}" | ||
|
||
def extract_master_password(self): | ||
""" | ||
Detect if a Master password exists and then extract it. | ||
See https://maven.apache.org/guides/mini/guide-encryption.html#How_to_create_a_master_password | ||
:return: The master password value or None if no master password exists. | ||
""" | ||
master_password = None | ||
master_password_file_location = os.environ.get("USERPROFILE") + "\\.m2\\settings-security.xml" | ||
if os.path.isfile(master_password_file_location): | ||
try: | ||
config = ET.parse(master_password_file_location).getroot() | ||
master_password_node = config.find(".//master") | ||
if master_password_node is not None: | ||
master_password = master_password_node.text | ||
except Exception as e: | ||
print_debug("ERROR", "Cannot retrieve master password '%s'" % e) | ||
master_password = None | ||
|
||
return master_password | ||
|
||
|
||
def extract_repositories_credentials(self): | ||
""" | ||
Extract all repositories's credentials. | ||
See https://maven.apache.org/settings.html#Servers | ||
:return: List of dict in which one dict contains all information for a repository. | ||
""" | ||
repos_creds = [] | ||
maven_settings_file_location = os.environ.get("USERPROFILE") + "\\.m2\\settings.xml" | ||
if os.path.isfile(maven_settings_file_location): | ||
try: | ||
settings = ET.parse(maven_settings_file_location).getroot() | ||
server_nodes = settings.findall(".//%sserver" % self.settings_namespace) | ||
for server_node in server_nodes: | ||
creds = {} | ||
for child_node in server_node: | ||
tag_name = child_node.tag.replace(self.settings_namespace, "") | ||
if tag_name in self.nodes_to_extract: | ||
creds[tag_name] = child_node.text.strip() | ||
if len(creds) > 0: | ||
repos_creds.append(creds) | ||
except Exception as e: | ||
print_debug("ERROR", "Cannot retrieve repositories credentials '%s'" % e) | ||
pass | ||
|
||
return repos_creds | ||
|
||
def use_key_auth(self, creds_dict): | ||
""" | ||
Utility function to determine if a repository use private key authentication. | ||
:param creds_dict: Repository credentials dict | ||
:return: True only if the repositry use private key authentication | ||
""" | ||
state = False | ||
if "privateKey" in creds_dict: | ||
pk_file_location = creds_dict["privateKey"] | ||
pk_file_location = pk_file_location.replace("${user.home}", os.environ.get("USERPROFILE")) | ||
state = os.path.isfile(pk_file_location) | ||
|
||
return state | ||
|
||
|
||
def run(self): | ||
""" | ||
Main function: | ||
- For encrypted password, provides the encrypted version of the password with the master password in order | ||
to allow "LaZagne run initiator" the use the encryption parameter associated with the version of Maven because | ||
encryption parameters can change between version of Maven. | ||
- "LaZagne run initiator" can also use the encrypted password and the master password "AS IS" | ||
in a Maven distribution to access repositories. | ||
See https://github.com/jelmerk/maven-settings-decoder | ||
See https://github.com/sonatype/plexus-cipher/blob/master/src/main/java/org/sonatype/plexus/components/cipher/PBECipher.java | ||
""" | ||
# Print title | ||
title = "MavenRepositories" | ||
Header().title_info(title) | ||
|
||
# Extract the master password | ||
master_password = self.extract_master_password() | ||
|
||
# Extract all available repositories credentials | ||
repos_creds = self.extract_repositories_credentials() | ||
|
||
# Parse and process the list of repositories's credentials | ||
# 3 cases are handled: | ||
# => Authentication using password protected with the master password (encrypted) | ||
# => Authentication using password not protected with the master password (plain text) | ||
# => Authentication using private key | ||
pwd_found = [] | ||
for creds in repos_creds: | ||
values = {} | ||
values["Id"] = creds["id"] | ||
values["Username"] = creds["username"] | ||
if not self.use_key_auth(creds): | ||
pwd = creds["password"].strip() | ||
# Case for authentication using password protected with the master password | ||
if pwd.startswith("{") and pwd.endswith("}"): | ||
values["SymetricEncryptionKey"] = master_password | ||
values["PasswordEncrypted"] = pwd | ||
else: | ||
values["Password"] = pwd | ||
else: | ||
# Case for authentication using private key | ||
pk_file_location = creds["privateKey"] | ||
pk_file_location = pk_file_location.replace("${user.home}", os.environ.get("USERPROFILE")) | ||
with open(pk_file_location, "r") as pk_file: | ||
values["PrivateKey"] = pk_file.read() | ||
if "passphrase" in creds: | ||
values["Passphrase"] = creds["passphrase"] | ||
pwd_found.append(values) | ||
|
||
# Print the results | ||
print_output(title, pwd_found) |
66 changes: 66 additions & 0 deletions
66
Windows/src/LaZagne/softwares/sysadmin/apachedirectorystudio.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import os | ||
from config.write_output import print_output, print_debug | ||
from config.constant import * | ||
from config.header import Header | ||
from config.moduleInfo import ModuleInfo | ||
import xml.etree.ElementTree as ET | ||
|
||
class ApacheDirectoryStudio(ModuleInfo): | ||
|
||
def __init__(self): | ||
options = {'command': '-ads', 'action': 'store_true', 'dest': 'apachedirectorystudio', 'help': 'Apache Directory Studio'} | ||
ModuleInfo.__init__(self, 'apachedirectorystudio', 'sysadmin', options) | ||
# Interesting XML attributes in ADS connection configuration | ||
self.attr_to_extract = ["host", "port", "bindPrincipal", "bindPassword", "authMethod"] | ||
|
||
|
||
def extract_connections_credentials(self): | ||
""" | ||
Extract all connection's credentials. | ||
:return: List of dict in which one dict contains all information for a connection. | ||
""" | ||
repos_creds = [] | ||
connection_file_location = os.environ.get("USERPROFILE") + "\\.ApacheDirectoryStudio\\.metadata\\.plugins\\org.apache.directory.studio.connection.core\\connections.xml" | ||
if os.path.isfile(connection_file_location): | ||
try: | ||
connections = ET.parse(connection_file_location).getroot() | ||
connection_nodes = connections.findall(".//connection") | ||
for connection_node in connection_nodes: | ||
creds = {} | ||
for connection_attr_name in connection_node.attrib: | ||
if connection_attr_name in self.attr_to_extract: | ||
creds[connection_attr_name] = connection_node.attrib[connection_attr_name].strip() | ||
if len(creds) > 0: | ||
repos_creds.append(creds) | ||
except Exception as e: | ||
print_debug("ERROR", "Cannot retrieve connections credentials '%s'" % e) | ||
pass | ||
|
||
return repos_creds | ||
|
||
|
||
def run(self): | ||
""" | ||
Main function | ||
""" | ||
# Print title | ||
title = "ApacheDirectoryStudio" | ||
Header().title_info(title) | ||
|
||
# Extract all available connections credentials | ||
repos_creds = self.extract_connections_credentials() | ||
|
||
# Parse and process the list of connections credentials | ||
pwd_found = [] | ||
for creds in repos_creds: | ||
values = {} | ||
values["Host"] = creds["host"] | ||
values["Port"] = creds["port"] | ||
values["BindPrincipal"] = creds["bindPrincipal"] | ||
values["BindPassword"] = creds["bindPassword"] | ||
values["AuthenticationMethod"] = creds["authMethod"] | ||
pwd_found.append(values) | ||
|
||
# Print the results | ||
print_output(title, pwd_found) |
94 changes: 94 additions & 0 deletions
94
Windows/src/LaZagne/softwares/sysadmin/opensshforwindows.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
from os import environ, walk | ||
from os.path import isdir, isfile, join | ||
from config.write_output import print_output, print_debug | ||
from config.constant import * | ||
from config.header import Header | ||
from config.moduleInfo import ModuleInfo | ||
from Crypto.PublicKey import RSA | ||
from Crypto.PublicKey import DSA | ||
|
||
class OpenSSHForWindows(ModuleInfo): | ||
|
||
def __init__(self): | ||
options = {'command': '-winssh', 'action': 'store_true', 'dest': 'opensshforwindows', 'help': 'OpenSSH for Windows'} | ||
ModuleInfo.__init__(self, 'opensshforwindows', 'sysadmin', options) | ||
self.key_files_location = environ.get("USERPROFILE") + "\\.ssh" | ||
|
||
def is_private_key_unprotected(self, key_content_encoded, key_algorithm): | ||
""" | ||
Check if the private key can be loaded without specifying any passphrase. | ||
PyCrypto >= 2.6.1 required in order to have the method importKey() in DSA class. | ||
:param key_content_encoded: Encoded content of the private key to test | ||
:param key_algorithm: Algorithm of the key (RSA or DSA) | ||
:return: True only if the key can be successfuly loaded and is usable | ||
""" | ||
state = False | ||
try: | ||
# Try to load it | ||
if key_algorithm == "RSA": | ||
key = RSA.importKey(key_content_encoded) | ||
else: | ||
key = DSA.importKey(key_content_encoded) | ||
# Validate loading | ||
state = (key is not None and key.can_sign() and key.has_private()) | ||
except Exception as e: | ||
print_debug("ERROR", "Cannot validate key protection '%s'" % e) | ||
state = False | ||
pass | ||
|
||
return state | ||
|
||
def extract_private_keys_unprotected(self): | ||
""" | ||
Extract all DSA/RSA private keys that are not protected with a passphrase. | ||
:return: List of encoded key (key file content) | ||
""" | ||
keys = [] | ||
if isdir(self.key_files_location): | ||
for (dirpath, dirnames, filenames) in walk(self.key_files_location, followlinks=True): | ||
for f in filenames: | ||
key_file_path = join(dirpath, f) | ||
if isfile(key_file_path): | ||
try: | ||
# Read encoded content of the key | ||
with open(key_file_path, "r") as key_file: | ||
key_content_encoded = key_file.read() | ||
# Determine the type of the key (public/private) and what is it algorithm | ||
if "DSA PRIVATE KEY" in key_content_encoded: | ||
key_algorithm = "DSA" | ||
elif "RSA PRIVATE KEY" in key_content_encoded: | ||
key_algorithm = "RSA" | ||
else: | ||
key_algorithm = None | ||
# Check if the key can be loaded (used) without passphrase | ||
if key_algorithm is not None and self.is_private_key_unprotected(key_content_encoded, | ||
key_algorithm): | ||
keys.append(key_content_encoded) | ||
except Exception as e: | ||
print_debug("ERROR", "Cannot load key file '%s' '%s'" % (key_file_path, e)) | ||
pass | ||
|
||
return keys | ||
|
||
def run(self): | ||
""" | ||
Main function | ||
""" | ||
# Print title | ||
title = "OpenSSHForWindows" | ||
Header().title_info(title) | ||
|
||
# Extract all DSA/RSA private keys that are not protected with a passphrase | ||
unprotected_private_keys = self.extract_private_keys_unprotected() | ||
|
||
# Parse and process the list of keys | ||
key_found = [] | ||
for key in unprotected_private_keys: | ||
values = {"PrivateKey": key} | ||
key_found.append(values) | ||
|
||
# Print the results | ||
print_output(title, key_found) |