Merge pull request #2240 from guardicore/2217-pydantic-credentials

2217 pydantic credentials
This commit is contained in:
Mike Salvatore 2022-09-07 09:10:56 -04:00 committed by GitHub
commit d39d6ea10f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 918 additions and 1305 deletions

View File

@ -1,7 +1,7 @@
import json
import logging
import time
from typing import Sequence, Union
from typing import List, Sequence, Union
from bson import json_util
@ -30,7 +30,7 @@ class MonkeyIslandClient(object):
def get_propagation_credentials(self) -> Sequence[Credentials]:
response = self.requests.get("api/propagation-credentials")
return [Credentials.from_mapping(credentials) for credentials in response.json()]
return [Credentials(**credentials) for credentials in response.json()]
@avoid_race_condition
def import_config(self, test_configuration: TestConfiguration):
@ -59,9 +59,9 @@ class MonkeyIslandClient(object):
assert False
@avoid_race_condition
def _import_credentials(self, propagation_credentials: Credentials):
def _import_credentials(self, propagation_credentials: List[Credentials]):
serialized_propagation_credentials = [
Credentials.to_mapping(credentials) for credentials in propagation_credentials
credentials.dict(simplify=True) for credentials in propagation_credentials
]
response = self.requests.put_json(
"/api/propagation-credentials/configured-credentials",

View File

@ -1,8 +1,3 @@
from .credential_component_type import CredentialComponentType
from .i_credential_component import ICredentialComponent
from .validators import InvalidCredentialComponentError, InvalidCredentialsError
from .lm_hash import LMHash
from .nt_hash import NTHash
from .password import Password

View File

@ -1,20 +0,0 @@
from marshmallow import Schema, post_load, validate
from marshmallow_enum import EnumField
from common.utils.code_utils import del_key
from . import CredentialComponentType
class CredentialTypeField(EnumField):
def __init__(self, credential_component_type: CredentialComponentType):
super().__init__(
CredentialComponentType, validate=validate.Equal(credential_component_type)
)
class CredentialComponentSchema(Schema):
@post_load
def _strip_credential_type(self, data, **kwargs):
del_key(data, "credential_type")
return data

View File

@ -1,9 +0,0 @@
from enum import Enum, auto
class CredentialComponentType(Enum):
USERNAME = auto()
PASSWORD = auto()
NT_HASH = auto()
LM_HASH = auto()
SSH_KEYPAIR = auto()

View File

@ -1,190 +1,35 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Mapping, Optional, Type
from typing import Optional, Union
from marshmallow import Schema, fields, post_load, pre_dump
from marshmallow.exceptions import MarshmallowError
from pydantic import SecretBytes, SecretStr
from ..utils import IJSONSerializable
from . import (
CredentialComponentType,
InvalidCredentialComponentError,
InvalidCredentialsError,
LMHash,
NTHash,
Password,
SSHKeypair,
Username,
)
from .i_credential_component import ICredentialComponent
from .lm_hash import LMHashSchema
from .nt_hash import NTHashSchema
from .password import PasswordSchema
from .ssh_keypair import SSHKeypairSchema
from .username import UsernameSchema
from ..base_models import InfectionMonkeyBaseModel
from . import LMHash, NTHash, Password, SSHKeypair, Username
CREDENTIAL_COMPONENT_TYPE_TO_CLASS: Mapping[CredentialComponentType, Type[ICredentialComponent]] = {
CredentialComponentType.LM_HASH: LMHash,
CredentialComponentType.NT_HASH: NTHash,
CredentialComponentType.PASSWORD: Password,
CredentialComponentType.SSH_KEYPAIR: SSHKeypair,
CredentialComponentType.USERNAME: Username,
}
CREDENTIAL_COMPONENT_TYPE_TO_CLASS_SCHEMA: Mapping[CredentialComponentType, Schema] = {
CredentialComponentType.LM_HASH: LMHashSchema(),
CredentialComponentType.NT_HASH: NTHashSchema(),
CredentialComponentType.PASSWORD: PasswordSchema(),
CredentialComponentType.SSH_KEYPAIR: SSHKeypairSchema(),
CredentialComponentType.USERNAME: UsernameSchema(),
}
CredentialComponentMapping = Optional[Mapping[str, Any]]
CredentialsMapping = Mapping[str, CredentialComponentMapping]
Secret = Union[Password, LMHash, NTHash, SSHKeypair]
Identity = Username
class CredentialsSchema(Schema):
identity = fields.Mapping(allow_none=True)
secret = fields.Mapping(allow_none=True)
def get_plaintext(secret: Union[SecretStr, SecretBytes, None, str]) -> Optional[str]:
if isinstance(secret, (SecretStr, SecretBytes)):
return secret.get_secret_value()
else:
return secret
@post_load
def _make_credentials(
self,
credentials: CredentialsMapping,
**kwargs: Mapping[str, Any],
) -> Mapping[str, Optional[ICredentialComponent]]:
if not any(credentials.values()):
raise InvalidCredentialsError("At least one credentials component must be defined")
return {
key: CredentialsSchema._build_credential_component(credential_component_mapping)
for key, credential_component_mapping in credentials.items()
class Credentials(InfectionMonkeyBaseModel):
"""Represents a credential pair (an identity and a secret)"""
identity: Optional[Identity]
"""Identity part of credentials, like a username or an email"""
secret: Optional[Secret]
"""Secret part of credentials, like a password or a hash"""
class Config:
json_encoders = {
# This makes secrets dumpable to json, but not loggable
SecretStr: get_plaintext,
SecretBytes: get_plaintext,
}
@staticmethod
def _build_credential_component(
credential_component: CredentialComponentMapping,
) -> Optional[ICredentialComponent]:
if credential_component is None:
return None
try:
credential_component_type = CredentialComponentType[
credential_component["credential_type"]
]
except KeyError as err:
raise InvalidCredentialsError(f"Unknown credential component type {err}")
credential_component_class = CREDENTIAL_COMPONENT_TYPE_TO_CLASS[credential_component_type]
credential_component_schema = CREDENTIAL_COMPONENT_TYPE_TO_CLASS_SCHEMA[
credential_component_type
]
try:
return credential_component_class(
**credential_component_schema.load(credential_component)
)
except MarshmallowError as err:
raise InvalidCredentialComponentError(credential_component_class, str(err))
@pre_dump
def _serialize_credentials(self, credentials: Credentials, **kwargs) -> CredentialsMapping:
return {
"identity": CredentialsSchema._serialize_credential_component(credentials.identity),
"secret": CredentialsSchema._serialize_credential_component(credentials.secret),
}
@staticmethod
def _serialize_credential_component(
credential_component: Optional[ICredentialComponent],
) -> CredentialComponentMapping:
if credential_component is None:
return None
credential_component_schema = CREDENTIAL_COMPONENT_TYPE_TO_CLASS_SCHEMA[
credential_component.credential_type
]
return credential_component_schema.dump(credential_component)
@dataclass(frozen=True)
class Credentials(IJSONSerializable):
identity: Optional[ICredentialComponent]
secret: Optional[ICredentialComponent]
def __post_init__(self):
schema = CredentialsSchema()
try:
serialized_data = schema.dump(self)
# This will raise an exception if the object is invalid. Calling this in __post__init()
# makes it impossible to construct an invalid object
schema.load(serialized_data)
except Exception as err:
raise InvalidCredentialsError(err)
@staticmethod
def from_mapping(credentials: CredentialsMapping) -> Credentials:
"""
Construct a Credentials object from a Mapping
:param credentials: A mapping that represents a Credentials object
:return: A Credentials object
:raises InvalidCredentialsError: If the provided Mapping does not represent a valid
Credentials object
:raises InvalidCredentialComponentError: If any of the contents of `identities` or `secrets`
are not a valid ICredentialComponent
"""
try:
deserialized_data = CredentialsSchema().load(credentials)
return Credentials(**deserialized_data)
except (InvalidCredentialsError, InvalidCredentialComponentError) as err:
raise err
except MarshmallowError as err:
raise InvalidCredentialsError(str(err))
@classmethod
def from_json(cls, credentials: str) -> Credentials:
"""
Construct a Credentials object from a JSON string
:param credentials: A JSON string that represents a Credentials object
:return: A Credentials object
:raises InvalidCredentialsError: If the provided JSON does not represent a valid
Credentials object
:raises InvalidCredentialComponentError: If any of the contents of `identities` or `secrets`
are not a valid ICredentialComponent
"""
try:
deserialized_data = CredentialsSchema().loads(credentials)
return Credentials(**deserialized_data)
except (InvalidCredentialsError, InvalidCredentialComponentError) as err:
raise err
except MarshmallowError as err:
raise InvalidCredentialsError(str(err))
@staticmethod
def to_mapping(credentials: Credentials) -> CredentialsMapping:
"""
Serialize a Credentials object to a Mapping
:param credentials: A Credentials object
:return: A mapping representing a Credentials object
"""
return CredentialsSchema().dump(credentials)
@classmethod
def to_json(cls, credentials: Credentials) -> str:
"""
Serialize a Credentials object to JSON
:param credentials: A Credentials object
:return: A JSON string representing a Credentials object
"""
return CredentialsSchema().dumps(credentials)

View File

@ -1,10 +0,0 @@
from abc import ABC, abstractmethod
from . import CredentialComponentType
class ICredentialComponent(ABC):
@property
@abstractmethod
def credential_type(self) -> CredentialComponentType:
pass

View File

@ -1,23 +1,16 @@
from dataclasses import dataclass, field
import re
from marshmallow import fields
from pydantic import SecretStr, validator
from . import CredentialComponentType, ICredentialComponent
from .credential_component_schema import CredentialComponentSchema, CredentialTypeField
from .validators import credential_component_validator, ntlm_hash_validator
from ..base_models import InfectionMonkeyBaseModel
from .validators import ntlm_hash_regex
class LMHashSchema(CredentialComponentSchema):
credential_type = CredentialTypeField(CredentialComponentType.LM_HASH)
lm_hash = fields.Str(validate=ntlm_hash_validator)
class LMHash(InfectionMonkeyBaseModel):
lm_hash: SecretStr
@dataclass(frozen=True)
class LMHash(ICredentialComponent):
credential_type: CredentialComponentType = field(
default=CredentialComponentType.LM_HASH, init=False
)
lm_hash: str
def __post_init__(self):
credential_component_validator(LMHashSchema(), self)
@validator("lm_hash")
def validate_hash_format(cls, lm_hash):
if not re.match(ntlm_hash_regex, lm_hash.get_secret_value()):
raise ValueError("Invalid LM hash provided")
return lm_hash

View File

@ -1,23 +1,16 @@
from dataclasses import dataclass, field
import re
from marshmallow import fields
from pydantic import SecretStr, validator
from . import CredentialComponentType, ICredentialComponent
from .credential_component_schema import CredentialComponentSchema, CredentialTypeField
from .validators import credential_component_validator, ntlm_hash_validator
from ..base_models import InfectionMonkeyBaseModel
from .validators import ntlm_hash_regex
class NTHashSchema(CredentialComponentSchema):
credential_type = CredentialTypeField(CredentialComponentType.NT_HASH)
nt_hash = fields.Str(validate=ntlm_hash_validator)
class NTHash(InfectionMonkeyBaseModel):
nt_hash: SecretStr
@dataclass(frozen=True)
class NTHash(ICredentialComponent):
credential_type: CredentialComponentType = field(
default=CredentialComponentType.NT_HASH, init=False
)
nt_hash: str
def __post_init__(self):
credential_component_validator(NTHashSchema(), self)
@validator("nt_hash")
def validate_hash_format(cls, nt_hash):
if not re.match(ntlm_hash_regex, nt_hash.get_secret_value()):
raise ValueError("Invalid NT hash provided")
return nt_hash

View File

@ -1,19 +1,7 @@
from dataclasses import dataclass, field
from pydantic import SecretStr
from marshmallow import fields
from . import CredentialComponentType, ICredentialComponent
from .credential_component_schema import CredentialComponentSchema, CredentialTypeField
from ..base_models import InfectionMonkeyBaseModel
class PasswordSchema(CredentialComponentSchema):
credential_type = CredentialTypeField(CredentialComponentType.PASSWORD)
password = fields.Str()
@dataclass(frozen=True)
class Password(ICredentialComponent):
credential_type: CredentialComponentType = field(
default=CredentialComponentType.PASSWORD, init=False
)
password: str
class Password(InfectionMonkeyBaseModel):
password: SecretStr

View File

@ -1,23 +1,8 @@
from dataclasses import dataclass, field
from pydantic import SecretStr
from marshmallow import fields
from . import CredentialComponentType, ICredentialComponent
from .credential_component_schema import CredentialComponentSchema, CredentialTypeField
from ..base_models import InfectionMonkeyBaseModel
class SSHKeypairSchema(CredentialComponentSchema):
credential_type = CredentialTypeField(CredentialComponentType.SSH_KEYPAIR)
# TODO: Find a list of valid formats for ssh keys and add validators.
# See https://github.com/nemchik/ssh-key-regex
private_key = fields.Str()
public_key = fields.Str()
@dataclass(frozen=True)
class SSHKeypair(ICredentialComponent):
credential_type: CredentialComponentType = field(
default=CredentialComponentType.SSH_KEYPAIR, init=False
)
private_key: str
class SSHKeypair(InfectionMonkeyBaseModel):
private_key: SecretStr
public_key: str

View File

@ -1,19 +1,5 @@
from dataclasses import dataclass, field
from marshmallow import fields
from . import CredentialComponentType, ICredentialComponent
from .credential_component_schema import CredentialComponentSchema, CredentialTypeField
from ..base_models import InfectionMonkeyBaseModel
class UsernameSchema(CredentialComponentSchema):
credential_type = CredentialTypeField(CredentialComponentType.USERNAME)
username = fields.Str()
@dataclass(frozen=True)
class Username(ICredentialComponent):
credential_type: CredentialComponentType = field(
default=CredentialComponentType.USERNAME, init=False
)
class Username(InfectionMonkeyBaseModel):
username: str

View File

@ -1,50 +1,3 @@
import re
from typing import Type
from marshmallow import Schema, validate
from . import ICredentialComponent
_ntlm_hash_regex = re.compile(r"^[a-fA-F0-9]{32}$")
ntlm_hash_validator = validate.Regexp(regex=_ntlm_hash_regex)
class InvalidCredentialComponentError(Exception):
def __init__(self, credential_component_class: Type[ICredentialComponent], message: str):
self._credential_component_name = credential_component_class.__name__
self._message = message
def __str__(self) -> str:
return (
f"Cannot construct a {self._credential_component_name} object with the supplied, "
f"invalid data: {self._message}"
)
class InvalidCredentialsError(Exception):
def __init__(self, message: str):
self._message = message
def __str__(self) -> str:
return (
f"Cannot construct a Credentials object with the supplied, "
f"invalid data: {self._message}"
)
def credential_component_validator(schema: Schema, credential_component: ICredentialComponent):
"""
Validate a credential component
:param schema: A marshmallow schema used for validating the component
:param credential_component: A credential component to be validated
:raises InvalidCredentialComponent: if the credential_component contains invalid data
"""
try:
serialized_data = schema.dump(credential_component)
# This will raise an exception if the object is invalid. Calling this in __post__init()
# makes it impossible to construct an invalid object
schema.load(serialized_data)
except Exception as err:
raise InvalidCredentialComponentError(credential_component.__class__, err)
ntlm_hash_regex = re.compile(r"^[a-fA-F0-9]{32}$")

View File

@ -18,11 +18,11 @@
"default": {
"aiosmb": {
"hashes": [
"sha256:0afa901093f0ad91d0b8421dec66c80bd2e9cb237a8da405984413a5d7475398",
"sha256:0e98390ba00fdc4190e698f184dfcf72b02b592cdfe9274e03cc7316ac4ee368"
"sha256:2668d63ae6e6ca30a999696c444d4afc349f85a872a204994394aa6abbf5673d",
"sha256:b0b0e7068e757b8d3a8e4be2ebf2b499933d9fa7853bc3a8e198d6a57dc77131"
],
"markers": "python_version >= '3.7'",
"version": "==0.3.8"
"version": "==0.4.0"
},
"aiowinreg": {
"hashes": [
@ -46,13 +46,21 @@
],
"version": "==1.5.1"
},
"asyauth": {
"hashes": [
"sha256:a7a7998dcfa672ebae9837ba4996df237412a7f335c0f8291bb14091d0e6d19a",
"sha256:f20c497b02d17f25298d3701fb65c9a337893b68a9c7dddecc09ab956b09f828"
],
"markers": "python_version >= '3.7'",
"version": "==0.0.2"
},
"asysocks": {
"hashes": [
"sha256:23d5fcfae71a75826c3ed787bd9b1bc3b189ec37658961bce83c9e99455e354c",
"sha256:731eda25d41783c5243153d3cb4f9357fef337c7317135488afab9ecd6b7f1a1"
"sha256:8f4516088ebec7f08d8c549e5e75e7a86b41e6043af920ba4895cf3f6c654150",
"sha256:d4c619c9d2e8be0cbdd21fa635a7eaf5886809edc948e2f76625b33b3cccfc47"
],
"markers": "python_version >= '3.6'",
"version": "==0.1.7"
"version": "==0.2.1"
},
"attrs": {
"hashes": [
@ -71,20 +79,21 @@
},
"bcrypt": {
"hashes": [
"sha256:2b02d6bfc6336d1094276f3f588aa1225a598e27f8e3388f4db9948cb707b521",
"sha256:433c410c2177057705da2a9f2cd01dd157493b2a7ac14c8593a16b3dab6b6bfb",
"sha256:4e029cef560967fb0cf4a802bcf4d562d3d6b4b1bf81de5ec1abbe0f1adb027e",
"sha256:61bae49580dce88095d669226d5076d0b9d927754cedbdf76c6c9f5099ad6f26",
"sha256:6d2cb9d969bfca5bc08e45864137276e4c3d3d7de2b162171def3d188bf9d34a",
"sha256:7180d98a96f00b1050e93f5b0f556e658605dd9f524d0b0e68ae7944673f525e",
"sha256:7d9ba2e41e330d2af4af6b1b6ec9e6128e91343d0b4afb9282e54e5508f31baa",
"sha256:7ff2069240c6bbe49109fe84ca80508773a904f5a8cb960e02a977f7f519b129",
"sha256:88273d806ab3a50d06bc6a2fc7c87d737dd669b76ad955f449c43095389bc8fb",
"sha256:a2c46100e315c3a5b90fdc53e429c006c5f962529bc27e1dfd656292c20ccc40",
"sha256:cd43303d6b8a165c29ec6756afd169faba9396a9472cdff753fe9f19b96ce2fa"
"sha256:0b0f0c7141622a31e9734b7f649451147c04ebb5122327ac0bd23744df84be90",
"sha256:1c3334446fac200499e8bc04a530ce3cf0b3d7151e0e4ac5c0dddd3d95e97843",
"sha256:2d0dd19aad87e4ab882ef1d12df505f4c52b28b69666ce83c528f42c07379227",
"sha256:594780b364fb45f2634c46ec8d3e61c1c0f1811c4f2da60e8eb15594ecbf93ed",
"sha256:7c7dd6c1f05bf89e65261d97ac3a6520f34c2acb369afb57e3ea4449be6ff8fd",
"sha256:845b1daf4df2dd94d2fdbc9454953ca9dd0e12970a0bfc9f3dcc6faea3fa96e4",
"sha256:8780e69f9deec9d60f947b169507d2c9816e4f11548f1f7ebee2af38b9b22ae4",
"sha256:bf413f2a9b0a2950fc750998899013f2e718d20fa4a58b85ca50b6df5ed1bbf9",
"sha256:bfb67f6a6c72dfb0a02f3df51550aa1862708e55128b22543e2b42c74f3620d7",
"sha256:c59c170fc9225faad04dde1ba61d85b413946e8ce2e5f5f5ff30dfd67283f319",
"sha256:dc6ec3dc19b1c193b2f7cf279d3e32e7caf447532fbcb7af0906fe4398900c33",
"sha256:ede0f506554571c8eda80db22b83c139303ec6b595b8f60c4c8157bdd0bdee36"
],
"markers": "python_version >= '3.6'",
"version": "==3.2.2"
"version": "==4.0.0"
},
"certifi": {
"hashes": [
@ -173,11 +182,11 @@
},
"charset-normalizer": {
"hashes": [
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
],
"markers": "python_version >= '3.6'",
"version": "==2.1.0"
"version": "==2.1.1"
},
"click": {
"hashes": [
@ -204,47 +213,51 @@
},
"cryptography": {
"hashes": [
"sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59",
"sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596",
"sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3",
"sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5",
"sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab",
"sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884",
"sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82",
"sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b",
"sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441",
"sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa",
"sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d",
"sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b",
"sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a",
"sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6",
"sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157",
"sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280",
"sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282",
"sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67",
"sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8",
"sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046",
"sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327",
"sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"
"sha256:03363b92b6d76e4292ace708dcc8fd31b28ce325714f5af9f72afca6e7ac4070",
"sha256:144c9b09135caf6580fb8bf03594760a351dcb1bb6df3a4ed871527a90597e31",
"sha256:18253e7aeb7a6a12d1af7548a70349ab41da099addc0bfdc8f980c0a55624206",
"sha256:24d4137f3118900db02a2ec9a585d6dec2e79697fc90e84f19e5462dd1eeca44",
"sha256:2cbd0989e8b2bc7182f4eaa85145e1264a139da94b78adf9440be0d9ccce8898",
"sha256:3f1b0207dd0ec3167d5faec10d72368b7e1ba9eb2e3d54718963f6319254102b",
"sha256:634f38529aac216844df4df8632cdc1d7fff11c1ba2440c7299c38c1aa7a3e19",
"sha256:684134063c108b6a8af59c1869e09400a1c078336e79fa8cc1cbfb82e67d992a",
"sha256:7208cf3fa37e4c2d3ec9b49ac7721c457b793c64d548c4f7f2989ded0405f700",
"sha256:723581cbcfe89b83f86ed710b591520b6b148186b555dad2f84f717ad136f9e5",
"sha256:7ac685fe7c1103f5802e52a49f90f010dd768fd1cffd797fe01df2c674f3862a",
"sha256:7ed89815ea3483f92d6ca934ca8b2a8c35579453c1e2472a960a5086996981dd",
"sha256:88fbfc4703987ed17990a0a5b6f465447e8996cc4c4117db08afce9d0f75ff82",
"sha256:8fe3f1df20286d50dc5ab201cce4bf5c326e813d7fa39a62ed41bc01d46cd3ab",
"sha256:959cce516bacb775fb52ff122f26e23178c76ca708d68dc353e177cb7ee6f167",
"sha256:aaba2a47f0ce5c795d7caf46ff9c9ecc5d42b8ca5cecd11c0bf03d3df4626f9c",
"sha256:b224d7e73676a3ea27cd1a726591c7984db32ceb63a801e11d80669eb9964f37",
"sha256:b69f3f316711567f03eab907713a2c076f43fc84f92a6d7ce5ffc8c26e6709bc",
"sha256:c27e541b532d13a15fe969977c55753d0f187696280ba073273efb0c733e522b",
"sha256:cec962ac6db460a22ac78c1e316c7238b6979a9e369b9b1e3810af10c7445b1e",
"sha256:d2700d1593e5a03bc32d1a9e9fda9b136bf7e67e7ae4aeefcfeac5a5090c200e",
"sha256:d89a685ce8dd2c6869e9b9b884c9e0944cc74d67ca53a5bf4cd69f262fca0c3f",
"sha256:db58dcd824d67bc7f9735f3b0f402add8b33687994f0e822cbe16868d68aa2a1",
"sha256:ebe9e75f9a759e1019bdd2a823b51c8d4002bed006d60c025e3d6b9dff33ebaa",
"sha256:efa101918a6b332e1db0d3c69085217eb754b85124ab8ca94bba9b1e8d079f84",
"sha256:ff8709fcd3e49c41bc3da5720ab39882b76af8d15e268807e6d906f8c3a252fc"
],
"markers": "python_version >= '3.6'",
"version": "==37.0.4"
"version": "==38.0.0"
},
"dnspython": {
"hashes": [
"sha256:0f7569a4a6ff151958b64304071d370daa3243d15941a7beedf0c9fe5105603e",
"sha256:a851e51367fb93e9e1361732c1d60dab63eff98712e503ea7d92e6eccb109b4f"
],
"markers": "python_version >= '3.6' and python_version < '4'",
"markers": "python_version >= '3.6' and python_version < '4.0'",
"version": "==2.2.1"
},
"flask": {
"hashes": [
"sha256:3c604c48c3d5b4c63e72134044c0b4fe90ff01ef65280b9fe2d38c8860d99fe5",
"sha256:9c2b81b9b1edcc835af72d600f1955e713a065e7cb41d7e51ee762b449d9c65d"
"sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b",
"sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"
],
"markers": "python_version >= '3.7'",
"version": "==2.2.1"
"version": "==2.2.2"
},
"future": {
"hashes": [
@ -388,11 +401,11 @@
},
"marshmallow": {
"hashes": [
"sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb",
"sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"
"sha256:1172ce82765bf26c24a3f9299ed6dbeeca4d213f638eaa39a37772656d7ce408",
"sha256:48e2d88d4ab431ad5a17c25556d9da529ea6e966876f2a38d274082e270287f0"
],
"index": "pypi",
"version": "==3.17.0"
"version": "==3.17.1"
},
"marshmallow-enum": {
"hashes": [
@ -412,19 +425,19 @@
},
"minikerberos": {
"hashes": [
"sha256:3c383f67ebcf6f28105ed54f623a6a5c677a24e3f0c9ad69ed453f77e569d714",
"sha256:789f802263fa1882f701b123f6eec048b45cd731bf1b528870005daf07402047"
"sha256:c05cfcd846b1973b2b0d501e9e9fa2a263543d7762b052fc803fc1de849286a3",
"sha256:e912eb4bea899e1875707e7998001ed1047e1b32d5d7bf74d8b6137acf9614d3"
],
"markers": "python_version >= '3.6'",
"version": "==0.2.20"
"version": "==0.3.1"
},
"msldap": {
"hashes": [
"sha256:ac8174ed7e0162eb64b3e9dfeff13b2e1021612d0a4b2cfc6b8e5bed7c00ffe0",
"sha256:e2c22a6e396b4d7d65d73863ed44612120e8e2570ff895b5421ddf6a350085bb"
"sha256:236eacd04b0d2886e71b2890ec6c67fc626e1b9812c93a0fe21e998697415927",
"sha256:ccb5c1f40de165141931659eb71d4bbad326665aaff7bf23dd0dccb410dfa78c"
],
"markers": "python_version >= '3.7'",
"version": "==0.3.40"
"version": "==0.4.0"
},
"netifaces": {
"hashes": [
@ -506,49 +519,49 @@
},
"prompt-toolkit": {
"hashes": [
"sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0",
"sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"
"sha256:9696f386133df0fc8ca5af4895afe5d78f5fcfe5258111c2a79a1c3e41ffa96d",
"sha256:9ada952c9d1787f52ff6d5f3484d0b4df8952787c087edf6a1f7c2cb1ea88148"
],
"markers": "python_full_version >= '3.6.2'",
"version": "==3.0.30"
"version": "==3.0.31"
},
"psutil": {
"hashes": [
"sha256:068935df39055bf27a29824b95c801c7a5130f118b806eee663cad28dca97685",
"sha256:0904727e0b0a038830b019551cf3204dd48ef5c6868adc776e06e93d615fc5fc",
"sha256:0f15a19a05f39a09327345bc279c1ba4a8cfb0172cc0d3c7f7d16c813b2e7d36",
"sha256:19f36c16012ba9cfc742604df189f2f28d2720e23ff7d1e81602dbe066be9fd1",
"sha256:20b27771b077dcaa0de1de3ad52d22538fe101f9946d6dc7869e6f694f079329",
"sha256:28976df6c64ddd6320d281128817f32c29b539a52bdae5e192537bc338a9ec81",
"sha256:29a442e25fab1f4d05e2655bb1b8ab6887981838d22effa2396d584b740194de",
"sha256:3054e923204b8e9c23a55b23b6df73a8089ae1d075cb0bf711d3e9da1724ded4",
"sha256:32c52611756096ae91f5d1499fe6c53b86f4a9ada147ee42db4991ba1520e574",
"sha256:3a76ad658641172d9c6e593de6fe248ddde825b5866464c3b2ee26c35da9d237",
"sha256:44d1826150d49ffd62035785a9e2c56afcea66e55b43b8b630d7706276e87f22",
"sha256:4b6750a73a9c4a4e689490ccb862d53c7b976a2a35c4e1846d049dcc3f17d83b",
"sha256:56960b9e8edcca1456f8c86a196f0c3d8e3e361320071c93378d41445ffd28b0",
"sha256:57f1819b5d9e95cdfb0c881a8a5b7d542ed0b7c522d575706a80bedc848c8954",
"sha256:58678bbadae12e0db55186dc58f2888839228ac9f41cc7848853539b70490021",
"sha256:645bd4f7bb5b8633803e0b6746ff1628724668681a434482546887d22c7a9537",
"sha256:799759d809c31aab5fe4579e50addf84565e71c1dc9f1c31258f159ff70d3f87",
"sha256:79c9108d9aa7fa6fba6e668b61b82facc067a6b81517cab34d07a84aa89f3df0",
"sha256:91c7ff2a40c373d0cc9121d54bc5f31c4fa09c346528e6a08d1845bce5771ffc",
"sha256:9272167b5f5fbfe16945be3db475b3ce8d792386907e673a209da686176552af",
"sha256:944c4b4b82dc4a1b805329c980f270f170fdc9945464223f2ec8e57563139cf4",
"sha256:a6a11e48cb93a5fa606306493f439b4aa7c56cb03fc9ace7f6bfa21aaf07c453",
"sha256:a8746bfe4e8f659528c5c7e9af5090c5a7d252f32b2e859c584ef7d8efb1e689",
"sha256:abd9246e4cdd5b554a2ddd97c157e292ac11ef3e7af25ac56b08b455c829dca8",
"sha256:b14ee12da9338f5e5b3a3ef7ca58b3cba30f5b66f7662159762932e6d0b8f680",
"sha256:b88f75005586131276634027f4219d06e0561292be8bd6bc7f2f00bdabd63c4e",
"sha256:c7be9d7f5b0d206f0bbc3794b8e16fb7dbc53ec9e40bbe8787c6f2d38efcf6c9",
"sha256:d2d006286fbcb60f0b391741f520862e9b69f4019b4d738a2a45728c7e952f1b",
"sha256:db417f0865f90bdc07fa30e1aadc69b6f4cad7f86324b02aa842034efe8d8c4d",
"sha256:e7e10454cb1ab62cc6ce776e1c135a64045a11ec4c6d254d3f7689c16eb3efd2",
"sha256:f65f9a46d984b8cd9b3750c2bdb419b2996895b005aefa6cbaba9a143b1ce2c5",
"sha256:fea896b54f3a4ae6f790ac1d017101252c93f6fe075d0e7571543510f11d2676"
"sha256:14b29f581b5edab1f133563272a6011925401804d52d603c5c606936b49c8b97",
"sha256:256098b4f6ffea6441eb54ab3eb64db9ecef18f6a80d7ba91549195d55420f84",
"sha256:39ec06dc6c934fb53df10c1672e299145ce609ff0611b569e75a88f313634969",
"sha256:404f4816c16a2fcc4eaa36d7eb49a66df2d083e829d3e39ee8759a411dbc9ecf",
"sha256:42638876b7f5ef43cef8dcf640d3401b27a51ee3fa137cb2aa2e72e188414c32",
"sha256:4642fd93785a29353d6917a23e2ac6177308ef5e8be5cc17008d885cb9f70f12",
"sha256:4fb54941aac044a61db9d8eb56fc5bee207db3bc58645d657249030e15ba3727",
"sha256:561dec454853846d1dd0247b44c2e66a0a0c490f937086930ec4b8f83bf44f06",
"sha256:5d39e3a2d5c40efa977c9a8dd4f679763c43c6c255b1340a56489955dbca767c",
"sha256:614337922702e9be37a39954d67fdb9e855981624d8011a9927b8f2d3c9625d9",
"sha256:67b33f27fc0427483b61563a16c90d9f3b547eeb7af0ef1b9fe024cdc9b3a6ea",
"sha256:68b35cbff92d1f7103d8f1db77c977e72f49fcefae3d3d2b91c76b0e7aef48b8",
"sha256:7cbb795dcd8ed8fd238bc9e9f64ab188f3f4096d2e811b5a82da53d164b84c3f",
"sha256:8f024fbb26c8daf5d70287bb3edfafa22283c255287cf523c5d81721e8e5d82c",
"sha256:91aa0dac0c64688667b4285fa29354acfb3e834e1fd98b535b9986c883c2ce1d",
"sha256:94e621c6a4ddb2573d4d30cba074f6d1aa0186645917df42c811c473dd22b339",
"sha256:9770c1d25aee91417eba7869139d629d6328a9422ce1cdd112bd56377ca98444",
"sha256:b1928b9bf478d31fdffdb57101d18f9b70ed4e9b0e41af751851813547b2a9ab",
"sha256:b2f248ffc346f4f4f0d747ee1947963613216b06688be0be2e393986fe20dbbb",
"sha256:b315febaebae813326296872fdb4be92ad3ce10d1d742a6b0c49fb619481ed0b",
"sha256:b3591616fa07b15050b2f87e1cdefd06a554382e72866fcc0ab2be9d116486c8",
"sha256:b4018d5f9b6651f9896c7a7c2c9f4652e4eea53f10751c4e7d08a9093ab587ec",
"sha256:d75291912b945a7351d45df682f9644540d564d62115d4a20d45fa17dc2d48f8",
"sha256:dc9bda7d5ced744622f157cc8d8bdd51735dafcecff807e928ff26bdb0ff097d",
"sha256:e3ac2c0375ef498e74b9b4ec56df3c88be43fe56cac465627572dbfb21c4be34",
"sha256:e4c4a7636ffc47b7141864f1c5e7d649f42c54e49da2dd3cceb1c5f5d29bfc85",
"sha256:ed29ea0b9a372c5188cdb2ad39f937900a10fb5478dc077283bf86eeac678ef1",
"sha256:f40ba362fefc11d6bea4403f070078d60053ed422255bd838cd86a40674364c9",
"sha256:f4cb67215c10d4657e320037109939b1c1d2fd70ca3d76301992f89fe2edb1f1",
"sha256:f7929a516125f62399d6e8e026129c8835f6c5a3aab88c3fff1a05ee8feb840d",
"sha256:fd331866628d18223a4265371fd255774affd86244fc307ef66eaf00de0633d5",
"sha256:feb861a10b6c3bb00701063b37e4afc754f8217f0f09c42280586bd6ac712b5c"
],
"index": "pypi",
"version": "==5.9.1"
"version": "==5.9.2"
},
"pyasn1": {
"hashes": [
@ -632,44 +645,45 @@
},
"pydantic": {
"hashes": [
"sha256:1061c6ee6204f4f5a27133126854948e3b3d51fcc16ead2e5d04378c199b2f44",
"sha256:19b5686387ea0d1ea52ecc4cffb71abb21702c5e5b2ac626fd4dbaa0834aa49d",
"sha256:2bd446bdb7755c3a94e56d7bdfd3ee92396070efa8ef3a34fab9579fe6aa1d84",
"sha256:328558c9f2eed77bd8fffad3cef39dbbe3edc7044517f4625a769d45d4cf7555",
"sha256:32e0b4fb13ad4db4058a7c3c80e2569adbd810c25e6ca3bbd8b2a9cc2cc871d7",
"sha256:3ee0d69b2a5b341fc7927e92cae7ddcfd95e624dfc4870b32a85568bd65e6131",
"sha256:4aafd4e55e8ad5bd1b19572ea2df546ccace7945853832bb99422a79c70ce9b8",
"sha256:4b3946f87e5cef3ba2e7bd3a4eb5a20385fe36521d6cc1ebf3c08a6697c6cfb3",
"sha256:4de71c718c9756d679420c69f216776c2e977459f77e8f679a4a961dc7304a56",
"sha256:5565a49effe38d51882cb7bac18bda013cdb34d80ac336428e8908f0b72499b0",
"sha256:5803ad846cdd1ed0d97eb00292b870c29c1f03732a010e66908ff48a762f20e4",
"sha256:5da164119602212a3fe7e3bc08911a89db4710ae51444b4224c2382fd09ad453",
"sha256:615661bfc37e82ac677543704437ff737418e4ea04bef9cf11c6d27346606044",
"sha256:78a4d6bdfd116a559aeec9a4cfe77dda62acc6233f8b56a716edad2651023e5e",
"sha256:7d0f183b305629765910eaad707800d2f47c6ac5bcfb8c6397abdc30b69eeb15",
"sha256:7ead3cd020d526f75b4188e0a8d71c0dbbe1b4b6b5dc0ea775a93aca16256aeb",
"sha256:84d76ecc908d917f4684b354a39fd885d69dd0491be175f3465fe4b59811c001",
"sha256:8cb0bc509bfb71305d7a59d00163d5f9fc4530f0881ea32c74ff4f74c85f3d3d",
"sha256:91089b2e281713f3893cd01d8e576771cd5bfdfbff5d0ed95969f47ef6d676c3",
"sha256:9c9e04a6cdb7a363d7cb3ccf0efea51e0abb48e180c0d31dca8d247967d85c6e",
"sha256:a8c5360a0297a713b4123608a7909e6869e1b56d0e96eb0d792c27585d40757f",
"sha256:afacf6d2a41ed91fc631bade88b1d319c51ab5418870802cedb590b709c5ae3c",
"sha256:b34ba24f3e2d0b39b43f0ca62008f7ba962cff51efa56e64ee25c4af6eed987b",
"sha256:bd67cb2c2d9602ad159389c29e4ca964b86fa2f35c2faef54c3eb28b4efd36c8",
"sha256:c0f5e142ef8217019e3eef6ae1b6b55f09a7a15972958d44fbd228214cede567",
"sha256:cdb4272678db803ddf94caa4f94f8672e9a46bae4a44f167095e4d06fec12979",
"sha256:d70916235d478404a3fa8c997b003b5f33aeac4686ac1baa767234a0f8ac2326",
"sha256:d8ce3fb0841763a89322ea0432f1f59a2d3feae07a63ea2c958b2315e1ae8adb",
"sha256:e0b214e57623a535936005797567231a12d0da0c29711eb3514bc2b3cd008d0f",
"sha256:e631c70c9280e3129f071635b81207cad85e6c08e253539467e4ead0e5b219aa",
"sha256:e78578f0c7481c850d1c969aca9a65405887003484d24f6110458fb02cca7747",
"sha256:f0ca86b525264daa5f6b192f216a0d1e860b7383e3da1c65a1908f9c02f42801",
"sha256:f1a68f4f65a9ee64b6ccccb5bf7e17db07caebd2730109cb8a95863cfa9c4e55",
"sha256:fafe841be1103f340a24977f61dee76172e4ae5f647ab9e7fd1e1fca51524f08",
"sha256:ff68fc85355532ea77559ede81f35fff79a6a5543477e168ab3a381887caea76"
"sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42",
"sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624",
"sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e",
"sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559",
"sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709",
"sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9",
"sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d",
"sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52",
"sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda",
"sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912",
"sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c",
"sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525",
"sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe",
"sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41",
"sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b",
"sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283",
"sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965",
"sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c",
"sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410",
"sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5",
"sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116",
"sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98",
"sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f",
"sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644",
"sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13",
"sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd",
"sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254",
"sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6",
"sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488",
"sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5",
"sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c",
"sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1",
"sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a",
"sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2",
"sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d",
"sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"
],
"index": "pypi",
"version": "==1.9.2"
"version": "==1.10.2"
},
"pyinstaller": {
"hashes": [
@ -690,11 +704,11 @@
},
"pyinstaller-hooks-contrib": {
"hashes": [
"sha256:c4210fc50282c9c6a918e485e0bfae9405592390508e3be9fde19acc2213da56",
"sha256:e46f099934dd4577fb1ddcf37a99fa04027c92f8f5291c8802f326345988d001"
"sha256:d1dd6ea059dc30e77813cc12a5efa8b1d228e7da8f5b884fe11775f946db1784",
"sha256:e5edd4094175e78c178ef987b61be19efff6caa23d266ade456fc753e847f62e"
],
"markers": "python_version >= '3.7'",
"version": "==2022.8"
"version": "==2022.10"
},
"pymssql": {
"hashes": [
@ -795,21 +809,19 @@
},
"pyspnego": {
"hashes": [
"sha256:1ee612f20c843365fbc6cf7f95c526b4ee8795281641a9bb87083622a2f87939",
"sha256:284ca7a6218344bb90aeae02fb1d2ed73e5c991d6e4c16c0df404aeab5eb58a3",
"sha256:416fd2d67e82b44ba3d2d9062485056e4dde3c141630170e9190379d6b19972a",
"sha256:4c1be83e0aca12d64f5eec638259c77eaa8bf552c89ac69f0af2322a3be9afeb",
"sha256:4d1ea987b9c2539457235793014e0d9c5e4766da9e4e028d4b6b596cfbe53828",
"sha256:725df2030e5d1b1155bb696eca3d684f403164da8e6a6b0bee3eb02f8748f72b",
"sha256:7320539f45da463029e12f3742102085d2a0343bfe77ac550c11d2fdac1d34f5",
"sha256:77b86002082f278c3f5935d8b428a0d0659ea709e305537294ba95fc49907339",
"sha256:aa93d94775d01bf70d16840426d1ddd58c11a6a71c4d0d1d7e529ad541fa0a60",
"sha256:c2abca03b6d3c71d7ec9678c7b2220d99d9a29ef204b4c52549080169e586310",
"sha256:e6645107f200fb7bf6698512d04ea0790b292028861ce169eb97e5ad8eba14ed",
"sha256:f4784d9f8e9c50a36109d715a140150add1990fce16805a56217e8ccaf69d234"
"sha256:15cd6d3fc4d18b4b7f80259bfab1580c87dc9677d47e7cf801dad71dc23d1afc",
"sha256:210a2248060a2d789a333f7553a1a478d21812f675c507393169143cbf038d9b",
"sha256:4e967f29c099c196cbf4622587cd27e8c61f20adf78b1d3007b72596e60c9f23",
"sha256:4fab51afb757be21d543ddf78aaeb83db600a7e7daec773568db90d4b7499a2c",
"sha256:53d30afbef1255cb1a8930c14604184b07f989b6ac295a1397eac8c27fd59d8b",
"sha256:838f875ee55004a274f6470460e62b7713237ae8b66a02680a2f31e43b3b5387",
"sha256:b78a3370ace76209a52dc7816636a8c8437e323637eefe86a2193cc4ec352b3b",
"sha256:e08709c4e0838bf37d4ef8ceff2163a51abe2b071e285bb5774de5b73eab214f",
"sha256:ea8570c5363e5dd359aaf599eac6f70116e0ada734ebe557e17cc608c8bb93fc",
"sha256:fa2946ba5059f79d13cb8c47e83474de55569c16ed8f953cc47a24dda6f38f57"
],
"markers": "python_version >= '3.6'",
"version": "==0.5.3"
"markers": "python_version >= '3.7'",
"version": "==0.6.0"
},
"pywin32": {
"hashes": [
@ -858,11 +870,11 @@
},
"setuptools": {
"hashes": [
"sha256:7c7854ee1429a240090297628dc9f75b35318d193537968e2dc14010ee2f5bca",
"sha256:dc2662692f47d99cb8ae15a784529adeed535bcd7c277fee0beccf961522baf6"
"sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82",
"sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"
],
"markers": "python_version >= '3.7'",
"version": "==63.4.1"
"version": "==65.3.0"
},
"six": {
"hashes": [
@ -874,11 +886,11 @@
},
"tqdm": {
"hashes": [
"sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d",
"sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"
"sha256:5f4f682a004951c1b450bc753c710e9280c5746ce6ffedee253ddbcbf54cf1e4",
"sha256:6fee160d6ffcd1b1c68c65f14c829c22832bc401726335ce92c52d395944a6a1"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==4.64.0"
"version": "==4.64.1"
},
"twisted": {
"extras": [
@ -919,11 +931,11 @@
},
"unicrypto": {
"hashes": [
"sha256:0487f9dd9009c326ee9531a79412ae18ad673425a1c800d64947b96fdeb04cdf",
"sha256:fab49ee41926bb31be49552aa135f7aedc04436b49c8fe326d7b6a823810575e"
"sha256:4d1de0f0a379bb4c213302ae61d927eb8f98179bde9a0ffb8e120998a0c882a6",
"sha256:9d5dd858ad5ad608fa524987b17e8855d64d6d2450ca0ca11638f4d92fc6c80b"
],
"markers": "python_version >= '3.6'",
"version": "==0.0.8"
"version": "==0.0.9"
},
"urllib3": {
"hashes": [
@ -942,11 +954,11 @@
},
"werkzeug": {
"hashes": [
"sha256:4d7013ef96fd197d1cdeb03e066c6c5a491ccb44758a5b2b91137319383e5a5a",
"sha256:7e1db6a5ba6b9a8be061e47e900456355b8714c0f238b0313f53afce1a55a79a"
"sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f",
"sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"
],
"markers": "python_version >= '3.7'",
"version": "==2.2.1"
"version": "==2.2.2"
},
"winacl": {
"hashes": [
@ -956,14 +968,6 @@
"markers": "python_version >= '3.6'",
"version": "==0.1.3"
},
"winsspi": {
"hashes": [
"sha256:2f5a8d2c4b9f459144426909e26a74e550512e23b6cf9af52c2a00003c7c3fdb",
"sha256:59b7c7595f91414528cfd80c6cfc77ec6f5e4e28185ebd6418f8368ddc7aca82"
],
"markers": "python_version >= '3.6'",
"version": "==0.0.10"
},
"winsys-3.x": {
"hashes": [
"sha256:cef3df1dce2a5a71efa46446e6007ad9f7dbae31e83ffcc2ea3485c00c914cc3"
@ -976,6 +980,7 @@
"sha256:1d6b085e5c445141c475476000b661f60fff1aaa19f76bf82b7abb92e0ff4942",
"sha256:b6a6be5711b1b6c8d55bda7a8befd75c48c12b770b9d227d31c1737dbf0d40a6"
],
"index": "pypi",
"markers": "sys_platform == 'win32'",
"version": "==1.5.1"
},

View File

@ -55,22 +55,22 @@ class MimikatzCredentialCollector(ICredentialCollector):
identity = None
if wc.username:
identity = Username(wc.username)
identity = Username(username=wc.username)
if wc.password:
password = Password(wc.password)
credentials.append(Credentials(identity, password))
password = Password(password=wc.password)
credentials.append(Credentials(identity=identity, secret=password))
if wc.lm_hash:
lm_hash = LMHash(lm_hash=wc.lm_hash)
credentials.append(Credentials(identity, lm_hash))
credentials.append(Credentials(identity=identity, secret=lm_hash))
if wc.ntlm_hash:
ntlm_hash = NTHash(nt_hash=wc.ntlm_hash)
credentials.append(Credentials(identity, ntlm_hash))
credentials.append(Credentials(identity=identity, secret=ntlm_hash))
if len(credentials) == 0 and identity is not None:
credentials.append(Credentials(identity, None))
credentials.append(Credentials(identity=identity, secret=None))
return credentials

View File

@ -149,7 +149,7 @@ def to_credentials(ssh_info: Iterable[Dict]) -> Sequence[Credentials]:
secret = None
if info.get("name", ""):
identity = Username(info["name"])
identity = Username(username=info["name"])
ssh_keypair = {}
for key in ["public_key", "private_key"]:
@ -158,11 +158,12 @@ def to_credentials(ssh_info: Iterable[Dict]) -> Sequence[Credentials]:
if len(ssh_keypair):
secret = SSHKeypair(
ssh_keypair.get("private_key", ""), ssh_keypair.get("public_key", "")
private_key=ssh_keypair.get("private_key", ""),
public_key=ssh_keypair.get("public_key", ""),
)
if any([identity, secret]):
ssh_credentials.append(Credentials(identity, secret))
ssh_credentials.append(Credentials(identity=identity, secret=secret))
return ssh_credentials

View File

@ -1,7 +1,8 @@
import logging
from typing import Any, Iterable
from common.credentials import CredentialComponentType, Credentials, ICredentialComponent
from common.credentials import Credentials, LMHash, NTHash, Password, SSHKeypair, Username
from common.credentials.credentials import Identity, Secret
from infection_monkey.custom_types import PropagationCredentials
from infection_monkey.i_control_channel import IControlChannel
from infection_monkey.utils.decorators import request_cache
@ -43,18 +44,18 @@ class AggregatingPropagationCredentialsRepository(IPropagationCredentialsReposit
if credentials.secret:
self._add_secret(credentials.secret)
def _add_identity(self, identity: ICredentialComponent):
if identity.credential_type is CredentialComponentType.USERNAME:
def _add_identity(self, identity: Identity):
if isinstance(identity, Username):
self._stored_credentials.setdefault("exploit_user_list", set()).add(identity.username)
def _add_secret(self, secret: ICredentialComponent):
if secret.credential_type is CredentialComponentType.PASSWORD:
def _add_secret(self, secret: Secret):
if isinstance(secret, Password):
self._stored_credentials.setdefault("exploit_password_list", set()).add(secret.password)
elif secret.credential_type is CredentialComponentType.LM_HASH:
elif isinstance(secret, LMHash):
self._stored_credentials.setdefault("exploit_lm_hash_list", set()).add(secret.lm_hash)
elif secret.credential_type is CredentialComponentType.NT_HASH:
elif isinstance(secret, NTHash):
self._stored_credentials.setdefault("exploit_ntlm_hash_list", set()).add(secret.nt_hash)
elif secret.credential_type is CredentialComponentType.SSH_KEYPAIR:
elif isinstance(secret, SSHKeypair):
self._set_attribute(
"exploit_ssh_keys",
[{"public_key": secret.public_key, "private_key": secret.private_key}],

View File

@ -6,6 +6,7 @@ from typing import Sequence, Tuple
import pymssql
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
from common.credentials.credentials import get_plaintext
from common.utils.exceptions import FailedExploitationError
from infection_monkey.exploit.HostExploiter import HostExploiter
from infection_monkey.exploit.tools.helpers import get_agent_dst_path
@ -111,7 +112,7 @@ class MSSQLExploiter(HostExploiter):
conn = pymssql.connect(
host,
user,
password,
get_plaintext(password),
port=port,
login_timeout=self.LOGIN_TIMEOUT,
timeout=self.QUERY_TIMEOUT,

View File

@ -11,6 +11,7 @@ from pypsrp.powershell import PowerShell, RunspacePool
from typing_extensions import Protocol
from urllib3 import connectionpool
from common.credentials.credentials import get_plaintext
from infection_monkey.exploit.powershell_utils.auth_options import AuthOptions
from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType
@ -42,14 +43,16 @@ def format_password(credentials: Credentials) -> Optional[str]:
if credentials.secret_type == SecretType.CACHED:
return None
plaintext_secret = get_plaintext(credentials.secret)
if credentials.secret_type == SecretType.PASSWORD:
return credentials.secret
return plaintext_secret
if credentials.secret_type == SecretType.LM_HASH:
return f"{credentials.secret}:00000000000000000000000000000000"
return f"{plaintext_secret}:00000000000000000000000000000000"
if credentials.secret_type == SecretType.NT_HASH:
return f"00000000000000000000000000000000:{credentials.secret}"
return f"00000000000000000000000000000000:{plaintext_secret}"
raise ValueError(f"Unknown secret type {credentials.secret_type}")

View File

@ -4,6 +4,7 @@ from impacket.dcerpc.v5 import scmr, transport
from impacket.dcerpc.v5.scmr import DCERPCSessionError
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT
from common.credentials.credentials import get_plaintext
from common.utils.attack_utils import ScanStatus, UsageEnum
from infection_monkey.exploit.HostExploiter import HostExploiter
from infection_monkey.exploit.tools.helpers import get_agent_dst_path
@ -107,7 +108,14 @@ class SMBExploiter(HostExploiter):
rpctransport.setRemoteHost(self.host.ip_addr)
if hasattr(rpctransport, "set_credentials"):
# This method exists only for selected protocol sequences.
rpctransport.set_credentials(user, password, "", lm_hash, ntlm_hash, None)
rpctransport.set_credentials(
user,
get_plaintext(password),
"",
get_plaintext(lm_hash),
get_plaintext(ntlm_hash),
None,
)
rpctransport.set_kerberos(SMBExploiter.USE_KERBEROS)
scmr_rpc = rpctransport.get_dce_rpc()
@ -116,7 +124,8 @@ class SMBExploiter(HostExploiter):
scmr_rpc.connect()
except Exception as exc:
logger.debug(
f"Can't connect to SCM on exploited machine {self.host}, port {port} : {exc}"
f"Can't connect to SCM on exploited machine {self.host}, port {port} : "
f"{exc}"
)
continue

View File

@ -5,6 +5,7 @@ from pathlib import PurePath
import paramiko
from common.common_consts.timeouts import LONG_REQUEST_TIMEOUT, MEDIUM_REQUEST_TIMEOUT
from common.credentials.credentials import get_plaintext
from common.utils import Timer
from common.utils.attack_utils import ScanStatus
from common.utils.exceptions import FailedExploitationError
@ -59,7 +60,7 @@ class SSHExploiter(HostExploiter):
for user, ssh_key_pair in ssh_key_pairs_iterator:
# Creating file-like private key for paramiko
pkey = io.StringIO(ssh_key_pair["private_key"])
pkey = io.StringIO(get_plaintext(ssh_key_pair["private_key"]))
ssh_string = "%s@%s" % (user, self.host.ip_addr)
ssh = paramiko.SSHClient()

View File

@ -8,7 +8,9 @@ from typing import Optional
from impacket.dcerpc.v5 import srvs, transport
from impacket.smb3structs import SMB2_DIALECT_002, SMB2_DIALECT_21
from impacket.smbconnection import SMB_DIALECT, SMBConnection
from pydantic import SecretStr
from common.credentials.credentials import get_plaintext
from common.utils.attack_utils import ScanStatus
from infection_monkey.network.tools import get_interface_to_target
from infection_monkey.telemetry.attack.t1105_telem import T1105Telem
@ -26,8 +28,8 @@ class SmbTools(object):
host,
agent_file: BytesIO,
dst_path: PurePath,
username,
password,
username: str,
password: SecretStr,
lm_hash="",
ntlm_hash="",
timeout=30,
@ -190,7 +192,9 @@ class SmbTools(object):
return remote_full_path
@staticmethod
def new_smb_connection(host, username, password, lm_hash="", ntlm_hash="", timeout=30):
def new_smb_connection(
host, username: str, password: SecretStr, lm_hash="", ntlm_hash="", timeout=30
):
try:
smb = SMBConnection(host.ip_addr, host.ip_addr, sess_port=445)
except Exception as exc:
@ -212,7 +216,13 @@ class SmbTools(object):
# we know this should work because the WMI connection worked
try:
smb.login(username, password, "", lm_hash, ntlm_hash)
smb.login(
username,
get_plaintext(password),
"",
get_plaintext(lm_hash),
get_plaintext(ntlm_hash),
)
except Exception as exc:
logger.error(f'Error while logging into {host} using user "{username}": {exc}')
return None, dialect

View File

@ -5,6 +5,7 @@ import traceback
from impacket.dcerpc.v5.rpcrt import DCERPCException
from common.credentials.credentials import get_plaintext
from infection_monkey.exploit.HostExploiter import HostExploiter
from infection_monkey.exploit.tools.helpers import get_agent_dst_path
from infection_monkey.exploit.tools.smb_tools import SmbTools
@ -44,7 +45,14 @@ class WmiExploiter(HostExploiter):
wmi_connection = WmiTools.WmiConnection()
try:
wmi_connection.connect(self.host, user, password, None, lm_hash, ntlm_hash)
wmi_connection.connect(
self.host,
user,
get_plaintext(password),
None,
get_plaintext(lm_hash),
get_plaintext(ntlm_hash),
)
except AccessDeniedException:
self.report_login_attempt(False, user, password, lm_hash, ntlm_hash)
logger.debug(f"Failed connecting to {self.host} using WMI")

View File

@ -299,8 +299,8 @@ class ZerologonExploiter(HostExploiter):
self, user: str, lmhash: str, nthash: str
) -> None:
extracted_credentials = [
Credentials(Username(user), LMHash(lmhash)),
Credentials(Username(user), NTHash(nthash)),
Credentials(identity=Username(username=user), secret=LMHash(lm_hash=lmhash)),
Credentials(identity=Username(username=user), secret=NTHash(nt_hash=nthash)),
]
self.telemetry_messenger.send_telemetry(CredentialsTelem(extracted_credentials))

View File

@ -121,7 +121,7 @@ class ControlChannel(IControlChannel):
)
response.raise_for_status()
return [Credentials.from_mapping(credentials) for credentials in response.json()]
return [Credentials(**credentials) for credentials in response.json()]
except (
requests.exceptions.JSONDecodeError,
requests.exceptions.ConnectionError,

View File

@ -1,4 +1,3 @@
import json
from typing import Iterable
from common.common_consts.telem_categories import TelemCategoryEnum
@ -24,4 +23,4 @@ class CredentialsTelem(BaseTelem):
super().send(log_data=False)
def get_data(self):
return [json.loads(Credentials.to_json(c)) for c in self._credentials]
return [c.dict(simplify=True) for c in self._credentials]

View File

@ -1,5 +1,7 @@
import json
from pydantic import BaseModel, SecretBytes, SecretStr
from common import OperatingSystem
@ -7,4 +9,8 @@ class TelemetryJSONEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, OperatingSystem):
return obj.name
if issubclass(type(obj), BaseModel):
return obj.dict(simplify=True)
if issubclass(type(obj), (SecretStr, SecretBytes)):
return obj.get_secret_value()
return json.JSONEncoder.default(self, obj)

View File

@ -150,11 +150,11 @@
},
"charset-normalizer": {
"hashes": [
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
],
"markers": "python_version >= '3.6'",
"version": "==2.1.0"
"version": "==2.1.1"
},
"click": {
"hashes": [
@ -174,31 +174,35 @@
},
"cryptography": {
"hashes": [
"sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59",
"sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596",
"sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3",
"sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5",
"sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab",
"sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884",
"sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82",
"sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b",
"sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441",
"sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa",
"sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d",
"sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b",
"sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a",
"sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6",
"sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157",
"sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280",
"sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282",
"sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67",
"sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8",
"sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046",
"sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327",
"sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"
"sha256:0297ffc478bdd237f5ca3a7dc96fc0d315670bfa099c04dc3a4a2172008a405a",
"sha256:10d1f29d6292fc95acb597bacefd5b9e812099d75a6469004fd38ba5471a977f",
"sha256:16fa61e7481f4b77ef53991075de29fc5bacb582a1244046d2e8b4bb72ef66d0",
"sha256:194044c6b89a2f9f169df475cc167f6157eb9151cc69af8a2a163481d45cc407",
"sha256:1db3d807a14931fa317f96435695d9ec386be7b84b618cc61cfa5d08b0ae33d7",
"sha256:3261725c0ef84e7592597606f6583385fed2a5ec3909f43bc475ade9729a41d6",
"sha256:3b72c360427889b40f36dc214630e688c2fe03e16c162ef0aa41da7ab1455153",
"sha256:3e3a2599e640927089f932295a9a247fc40a5bdf69b0484532f530471a382750",
"sha256:3fc26e22840b77326a764ceb5f02ca2d342305fba08f002a8c1f139540cdfaad",
"sha256:5067ee7f2bce36b11d0e334abcd1ccf8c541fc0bbdaf57cdd511fdee53e879b6",
"sha256:52e7bee800ec869b4031093875279f1ff2ed12c1e2f74923e8f49c916afd1d3b",
"sha256:64760ba5331e3f1794d0bcaabc0d0c39e8c60bf67d09c93dc0e54189dfd7cfe5",
"sha256:765fa194a0f3372d83005ab83ab35d7c5526c4e22951e46059b8ac678b44fa5a",
"sha256:79473cf8a5cbc471979bd9378c9f425384980fcf2ab6534b18ed7d0d9843987d",
"sha256:896dd3a66959d3a5ddcfc140a53391f69ff1e8f25d93f0e2e7830c6de90ceb9d",
"sha256:89ed49784ba88c221756ff4d4755dbc03b3c8d2c5103f6d6b4f83a0fb1e85294",
"sha256:ac7e48f7e7261207d750fa7e55eac2d45f720027d5703cd9007e9b37bbb59ac0",
"sha256:ad7353f6ddf285aeadfaf79e5a6829110106ff8189391704c1d8801aa0bae45a",
"sha256:b0163a849b6f315bf52815e238bc2b2346604413fa7c1601eea84bcddb5fb9ac",
"sha256:b6c9b706316d7b5a137c35e14f4103e2115b088c412140fdbd5f87c73284df61",
"sha256:c2e5856248a416767322c8668ef1845ad46ee62629266f84a8f007a317141013",
"sha256:ca9f6784ea96b55ff41708b92c3f6aeaebde4c560308e5fbbd3173fbc466e94e",
"sha256:d1a5bd52d684e49a36582193e0b89ff267704cd4025abefb9e26803adeb3e5fb",
"sha256:d3971e2749a723e9084dd507584e2a2761f78ad2c638aa31e80bc7a15c9db4f9",
"sha256:d4ef6cc305394ed669d4d9eebf10d3a101059bdcf2669c366ec1d14e4fb227bd",
"sha256:d9e69ae01f99abe6ad646947bba8941e896cb3aa805be2597a0400e0764b5818"
],
"index": "pypi",
"version": "==37.0.4"
"version": "==38.0.1"
},
"dpath": {
"hashes": [
@ -210,19 +214,19 @@
},
"flask": {
"hashes": [
"sha256:3c604c48c3d5b4c63e72134044c0b4fe90ff01ef65280b9fe2d38c8860d99fe5",
"sha256:9c2b81b9b1edcc835af72d600f1955e713a065e7cb41d7e51ee762b449d9c65d"
"sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b",
"sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526"
],
"index": "pypi",
"version": "==2.2.1"
"version": "==2.2.2"
},
"flask-jwt-extended": {
"hashes": [
"sha256:188907ea9332bdd123a95a457e7487556770480264ce3b78c8835b4347e324cc",
"sha256:a2571df484c5635ad996d364242ec28fc69f386915cd69b1842639712b84c36d"
"sha256:62b521d75494c290a646ae8acc77123721e4364790f1e64af0038d823961fbf0",
"sha256:a85eebfa17c339a7260c4643475af444784ba6de5588adda67406f0a75599553"
],
"index": "pypi",
"version": "==4.4.3"
"version": "==4.4.4"
},
"flask-pymongo": {
"hashes": [
@ -288,64 +292,63 @@
},
"greenlet": {
"hashes": [
"sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3",
"sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711",
"sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd",
"sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073",
"sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708",
"sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67",
"sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23",
"sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1",
"sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08",
"sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd",
"sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2",
"sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa",
"sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8",
"sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40",
"sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab",
"sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6",
"sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc",
"sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b",
"sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e",
"sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963",
"sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3",
"sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d",
"sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d",
"sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe",
"sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28",
"sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3",
"sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e",
"sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c",
"sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d",
"sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0",
"sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497",
"sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee",
"sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713",
"sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58",
"sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a",
"sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06",
"sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88",
"sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965",
"sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f",
"sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4",
"sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5",
"sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c",
"sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a",
"sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1",
"sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43",
"sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627",
"sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b",
"sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168",
"sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d",
"sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5",
"sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478",
"sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf",
"sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce",
"sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c",
"sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b"
"sha256:0118817c9341ef2b0f75f5af79ac377e4da6ff637e5ee4ac91802c0e379dadb4",
"sha256:048d2bed76c2aa6de7af500ae0ea51dd2267aec0e0f2a436981159053d0bc7cc",
"sha256:07c58e169bbe1e87b8bbf15a5c1b779a7616df9fd3e61cadc9d691740015b4f8",
"sha256:095a980288fe05adf3d002fbb180c99bdcf0f930e220aa66fcd56e7914a38202",
"sha256:0b181e9aa6cb2f5ec0cacc8cee6e5a3093416c841ba32c185c30c160487f0380",
"sha256:1626185d938d7381631e48e6f7713e8d4b964be246073e1a1d15c2f061ac9f08",
"sha256:184416e481295832350a4bf731ba619a92f5689bf5d0fa4341e98b98b1265bd7",
"sha256:1dd51d2650e70c6c4af37f454737bf4a11e568945b27f74b471e8e2a9fd21268",
"sha256:1ec2779774d8e42ed0440cf8bc55540175187e8e934f2be25199bf4ed948cd9e",
"sha256:2cf45e339cabea16c07586306a31cfcc5a3b5e1626d365714d283732afed6809",
"sha256:2fb0aa7f6996879551fd67461d5d3ab0c3c0245da98be90c89fcb7a18d437403",
"sha256:44b4817c34c9272c65550b788913620f1fdc80362b209bc9d7dd2f40d8793080",
"sha256:466ce0928e33421ee84ae04c4ac6f253a3a3e6b8d600a79bd43fd4403e0a7a76",
"sha256:4f166b4aca8d7d489e82d74627a7069ab34211ef5ebb57c300ec4b9337b60fc0",
"sha256:510c3b15587afce9800198b4b142202b323bf4b4b5f9d6c79cb9a35e5e3c30d2",
"sha256:5b756e6730ea59b2745072e28ad27f4c837084688e6a6b3633c8b1e509e6ae0e",
"sha256:5fbe1ab72b998ca77ceabbae63a9b2e2dc2d963f4299b9b278252ddba142d3f1",
"sha256:6200a11f003ec26815f7e3d2ded01b43a3810be3528dd760d2f1fa777490c3cd",
"sha256:65ad1a7a463a2a6f863661329a944a5802c7129f7ad33583dcc11069c17e622c",
"sha256:694ffa7144fa5cc526c8f4512665003a39fa09ef00d19bbca5c8d3406db72fbe",
"sha256:6f5d4b2280ceea76c55c893827961ed0a6eadd5a584a7c4e6e6dd7bc10dfdd96",
"sha256:7532a46505470be30cbf1dbadb20379fb481244f1ca54207d7df3bf0bbab6a20",
"sha256:76a53bfa10b367ee734b95988bd82a9a5f0038a25030f9f23bbbc005010ca600",
"sha256:77e41db75f9958f2083e03e9dd39da12247b3430c92267df3af77c83d8ff9eed",
"sha256:7a43bbfa9b6cfdfaeefbd91038dde65ea2c421dc387ed171613df340650874f2",
"sha256:7b41d19c0cfe5c259fe6c539fd75051cd39a5d33d05482f885faf43f7f5e7d26",
"sha256:7c5227963409551ae4a6938beb70d56bf1918c554a287d3da6853526212fbe0a",
"sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32",
"sha256:88b04e12c9b041a1e0bcb886fec709c488192638a9a7a3677513ac6ba81d8e79",
"sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b",
"sha256:903fa5716b8fbb21019268b44f73f3748c41d1a30d71b4a49c84b642c2fed5fa",
"sha256:9537e4baf0db67f382eb29255a03154fcd4984638303ff9baaa738b10371fa57",
"sha256:9951dcbd37850da32b2cb6e391f621c1ee456191c6ae5528af4a34afe357c30e",
"sha256:9b2f7d0408ddeb8ea1fd43d3db79a8cefaccadd2a812f021333b338ed6b10aba",
"sha256:9c88e134d51d5e82315a7c32b914a58751b7353eb5268dbd02eabf020b4c4700",
"sha256:9fae214f6c43cd47f7bef98c56919b9222481e833be2915f6857a1e9e8a15318",
"sha256:a3a669f11289a8995d24fbfc0e63f8289dd03c9aaa0cc8f1eab31d18ca61a382",
"sha256:aa741c1a8a8cc25eb3a3a01a62bdb5095a773d8c6a86470bde7f607a447e7905",
"sha256:b0877a9a2129a2c56a2eae2da016743db7d9d6a05d5e1c198f1b7808c602a30e",
"sha256:bcb6c6dd1d6be6d38d6db283747d07fda089ff8c559a835236560a4410340455",
"sha256:caff52cb5cd7626872d9696aee5b794abe172804beb7db52eed1fd5824b63910",
"sha256:cbc1eb55342cbac8f7ec159088d54e2cfdd5ddf61c87b8bbe682d113789331b2",
"sha256:cd16a89efe3a003029c87ff19e9fba635864e064da646bc749fc1908a4af18f3",
"sha256:ce5b64dfe8d0cca407d88b0ee619d80d4215a2612c1af8c98a92180e7109f4b5",
"sha256:d58a5a71c4c37354f9e0c24c9c8321f0185f6945ef027460b809f4bb474bfe41",
"sha256:db41f3845eb579b544c962864cce2c2a0257fe30f0f1e18e51b1e8cbb4e0ac6d",
"sha256:db5b25265010a1b3dca6a174a443a0ed4c4ab12d5e2883a11c97d6e6d59b12f9",
"sha256:dd0404d154084a371e6d2bafc787201612a1359c2dee688ae334f9118aa0bf47",
"sha256:de431765bd5fe62119e0bc6bc6e7b17ac53017ae1782acf88fcf6b7eae475a49",
"sha256:df02fdec0c533301497acb0bc0f27f479a3a63dcdc3a099ae33a902857f07477",
"sha256:e8533f5111704d75de3139bf0b8136d3a6c1642c55c067866fa0a51c2155ee33",
"sha256:f2f908239b7098799b8845e5936c2ccb91d8c2323be02e82f8dcb4a80dcf4a25",
"sha256:f8bfd36f368efe0ab2a6aa3db7f14598aac454b06849fb633b762ddbede1db90",
"sha256:ffe73f9e7aea404722058405ff24041e59d31ca23d1da0895af48050a07b6932"
],
"markers": "platform_python_implementation == 'CPython'",
"version": "==1.1.2"
"version": "==1.1.3"
},
"idna": {
"hashes": [
@ -451,11 +454,11 @@
},
"marshmallow": {
"hashes": [
"sha256:00040ab5ea0c608e8787137627a8efae97fabd60552a05dc889c888f814e75eb",
"sha256:635fb65a3285a31a30f276f30e958070f5214c7196202caa5c7ecf28f5274bc7"
"sha256:1172ce82765bf26c24a3f9299ed6dbeeca4d213f638eaa39a37772656d7ce408",
"sha256:48e2d88d4ab431ad5a17c25556d9da529ea6e966876f2a38d274082e270287f0"
],
"index": "pypi",
"version": "==3.17.0"
"version": "==3.17.1"
},
"marshmallow-enum": {
"hashes": [
@ -542,44 +545,45 @@
},
"pydantic": {
"hashes": [
"sha256:1061c6ee6204f4f5a27133126854948e3b3d51fcc16ead2e5d04378c199b2f44",
"sha256:19b5686387ea0d1ea52ecc4cffb71abb21702c5e5b2ac626fd4dbaa0834aa49d",
"sha256:2bd446bdb7755c3a94e56d7bdfd3ee92396070efa8ef3a34fab9579fe6aa1d84",
"sha256:328558c9f2eed77bd8fffad3cef39dbbe3edc7044517f4625a769d45d4cf7555",
"sha256:32e0b4fb13ad4db4058a7c3c80e2569adbd810c25e6ca3bbd8b2a9cc2cc871d7",
"sha256:3ee0d69b2a5b341fc7927e92cae7ddcfd95e624dfc4870b32a85568bd65e6131",
"sha256:4aafd4e55e8ad5bd1b19572ea2df546ccace7945853832bb99422a79c70ce9b8",
"sha256:4b3946f87e5cef3ba2e7bd3a4eb5a20385fe36521d6cc1ebf3c08a6697c6cfb3",
"sha256:4de71c718c9756d679420c69f216776c2e977459f77e8f679a4a961dc7304a56",
"sha256:5565a49effe38d51882cb7bac18bda013cdb34d80ac336428e8908f0b72499b0",
"sha256:5803ad846cdd1ed0d97eb00292b870c29c1f03732a010e66908ff48a762f20e4",
"sha256:5da164119602212a3fe7e3bc08911a89db4710ae51444b4224c2382fd09ad453",
"sha256:615661bfc37e82ac677543704437ff737418e4ea04bef9cf11c6d27346606044",
"sha256:78a4d6bdfd116a559aeec9a4cfe77dda62acc6233f8b56a716edad2651023e5e",
"sha256:7d0f183b305629765910eaad707800d2f47c6ac5bcfb8c6397abdc30b69eeb15",
"sha256:7ead3cd020d526f75b4188e0a8d71c0dbbe1b4b6b5dc0ea775a93aca16256aeb",
"sha256:84d76ecc908d917f4684b354a39fd885d69dd0491be175f3465fe4b59811c001",
"sha256:8cb0bc509bfb71305d7a59d00163d5f9fc4530f0881ea32c74ff4f74c85f3d3d",
"sha256:91089b2e281713f3893cd01d8e576771cd5bfdfbff5d0ed95969f47ef6d676c3",
"sha256:9c9e04a6cdb7a363d7cb3ccf0efea51e0abb48e180c0d31dca8d247967d85c6e",
"sha256:a8c5360a0297a713b4123608a7909e6869e1b56d0e96eb0d792c27585d40757f",
"sha256:afacf6d2a41ed91fc631bade88b1d319c51ab5418870802cedb590b709c5ae3c",
"sha256:b34ba24f3e2d0b39b43f0ca62008f7ba962cff51efa56e64ee25c4af6eed987b",
"sha256:bd67cb2c2d9602ad159389c29e4ca964b86fa2f35c2faef54c3eb28b4efd36c8",
"sha256:c0f5e142ef8217019e3eef6ae1b6b55f09a7a15972958d44fbd228214cede567",
"sha256:cdb4272678db803ddf94caa4f94f8672e9a46bae4a44f167095e4d06fec12979",
"sha256:d70916235d478404a3fa8c997b003b5f33aeac4686ac1baa767234a0f8ac2326",
"sha256:d8ce3fb0841763a89322ea0432f1f59a2d3feae07a63ea2c958b2315e1ae8adb",
"sha256:e0b214e57623a535936005797567231a12d0da0c29711eb3514bc2b3cd008d0f",
"sha256:e631c70c9280e3129f071635b81207cad85e6c08e253539467e4ead0e5b219aa",
"sha256:e78578f0c7481c850d1c969aca9a65405887003484d24f6110458fb02cca7747",
"sha256:f0ca86b525264daa5f6b192f216a0d1e860b7383e3da1c65a1908f9c02f42801",
"sha256:f1a68f4f65a9ee64b6ccccb5bf7e17db07caebd2730109cb8a95863cfa9c4e55",
"sha256:fafe841be1103f340a24977f61dee76172e4ae5f647ab9e7fd1e1fca51524f08",
"sha256:ff68fc85355532ea77559ede81f35fff79a6a5543477e168ab3a381887caea76"
"sha256:05e00dbebbe810b33c7a7362f231893183bcc4251f3f2ff991c31d5c08240c42",
"sha256:06094d18dd5e6f2bbf93efa54991c3240964bb663b87729ac340eb5014310624",
"sha256:0b959f4d8211fc964772b595ebb25f7652da3f22322c007b6fed26846a40685e",
"sha256:19b3b9ccf97af2b7519c42032441a891a5e05c68368f40865a90eb88833c2559",
"sha256:1b6ee725bd6e83ec78b1aa32c5b1fa67a3a65badddde3976bca5fe4568f27709",
"sha256:1ee433e274268a4b0c8fde7ad9d58ecba12b069a033ecc4645bb6303c062d2e9",
"sha256:216f3bcbf19c726b1cc22b099dd409aa371f55c08800bcea4c44c8f74b73478d",
"sha256:2d0567e60eb01bccda3a4df01df677adf6b437958d35c12a3ac3e0f078b0ee52",
"sha256:2e05aed07fa02231dbf03d0adb1be1d79cabb09025dd45aa094aa8b4e7b9dcda",
"sha256:352aedb1d71b8b0736c6d56ad2bd34c6982720644b0624462059ab29bd6e5912",
"sha256:355639d9afc76bcb9b0c3000ddcd08472ae75318a6eb67a15866b87e2efa168c",
"sha256:37c90345ec7dd2f1bcef82ce49b6235b40f282b94d3eec47e801baf864d15525",
"sha256:4b8795290deaae348c4eba0cebb196e1c6b98bdbe7f50b2d0d9a4a99716342fe",
"sha256:5760e164b807a48a8f25f8aa1a6d857e6ce62e7ec83ea5d5c5a802eac81bad41",
"sha256:6eb843dcc411b6a2237a694f5e1d649fc66c6064d02b204a7e9d194dff81eb4b",
"sha256:7b5ba54d026c2bd2cb769d3468885f23f43710f651688e91f5fb1edcf0ee9283",
"sha256:7c2abc4393dea97a4ccbb4ec7d8658d4e22c4765b7b9b9445588f16c71ad9965",
"sha256:81a7b66c3f499108b448f3f004801fcd7d7165fb4200acb03f1c2402da73ce4c",
"sha256:91b8e218852ef6007c2b98cd861601c6a09f1aa32bbbb74fab5b1c33d4a1e410",
"sha256:9300fcbebf85f6339a02c6994b2eb3ff1b9c8c14f502058b5bf349d42447dcf5",
"sha256:9cabf4a7f05a776e7793e72793cd92cc865ea0e83a819f9ae4ecccb1b8aa6116",
"sha256:a1f5a63a6dfe19d719b1b6e6106561869d2efaca6167f84f5ab9347887d78b98",
"sha256:a4c805731c33a8db4b6ace45ce440c4ef5336e712508b4d9e1aafa617dc9907f",
"sha256:ae544c47bec47a86bc7d350f965d8b15540e27e5aa4f55170ac6a75e5f73b644",
"sha256:b97890e56a694486f772d36efd2ba31612739bc6f3caeee50e9e7e3ebd2fdd13",
"sha256:bb6ad4489af1bac6955d38ebcb95079a836af31e4c4f74aba1ca05bb9f6027bd",
"sha256:bedf309630209e78582ffacda64a21f96f3ed2e51fbf3962d4d488e503420254",
"sha256:c1ba1afb396148bbc70e9eaa8c06c1716fdddabaf86e7027c5988bae2a829ab6",
"sha256:c33602f93bfb67779f9c507e4d69451664524389546bacfe1bee13cae6dc7488",
"sha256:c4aac8e7103bf598373208f6299fa9a5cfd1fc571f2d40bf1dd1955a63d6eeb5",
"sha256:c6f981882aea41e021f72779ce2a4e87267458cc4d39ea990729e21ef18f0f8c",
"sha256:cc78cc83110d2f275ec1970e7a831f4e371ee92405332ebfe9860a715f8336e1",
"sha256:d49f3db871575e0426b12e2f32fdb25e579dea16486a26e5a0474af87cb1ab0a",
"sha256:dd3f9a40c16daf323cf913593083698caee97df2804aa36c4b3175d5ac1b92a2",
"sha256:e0bedafe4bc165ad0a56ac0bd7695df25c50f76961da29c050712596cf092d6d",
"sha256:e9069e1b01525a96e6ff49e25876d90d5a563bc31c658289a8772ae186552236"
],
"index": "pypi",
"version": "==1.9.2"
"version": "==1.10.2"
},
"pyinstaller": {
"hashes": [
@ -600,11 +604,11 @@
},
"pyinstaller-hooks-contrib": {
"hashes": [
"sha256:c4210fc50282c9c6a918e485e0bfae9405592390508e3be9fde19acc2213da56",
"sha256:e46f099934dd4577fb1ddcf37a99fa04027c92f8f5291c8802f326345988d001"
"sha256:d1dd6ea059dc30e77813cc12a5efa8b1d228e7da8f5b884fe11775f946db1784",
"sha256:e5edd4094175e78c178ef987b61be19efff6caa23d266ade456fc753e847f62e"
],
"markers": "python_version >= '3.7'",
"version": "==2022.8"
"version": "==2022.10"
},
"pyjwt": {
"hashes": [
@ -779,10 +783,10 @@
},
"pytz": {
"hashes": [
"sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7",
"sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"
"sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197",
"sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"
],
"version": "==2022.1"
"version": "==2022.2.1"
},
"pywin32": {
"hashes": [
@ -855,11 +859,11 @@
},
"setuptools": {
"hashes": [
"sha256:7c7854ee1429a240090297628dc9f75b35318d193537968e2dc14010ee2f5bca",
"sha256:dc2662692f47d99cb8ae15a784529adeed535bcd7c277fee0beccf961522baf6"
"sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82",
"sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"
],
"markers": "python_version >= '3.7'",
"version": "==63.4.1"
"version": "==65.3.0"
},
"six": {
"hashes": [
@ -874,24 +878,24 @@
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
"sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
],
"markers": "python_version >= '3.7'",
"markers": "python_version < '3.8'",
"version": "==4.3.0"
},
"urllib3": {
"hashes": [
"sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc",
"sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a"
"sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e",
"sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
"version": "==1.26.11"
"version": "==1.26.12"
},
"werkzeug": {
"hashes": [
"sha256:4d7013ef96fd197d1cdeb03e066c6c5a491ccb44758a5b2b91137319383e5a5a",
"sha256:7e1db6a5ba6b9a8be061e47e900456355b8714c0f238b0313f53afce1a55a79a"
"sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f",
"sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5"
],
"index": "pypi",
"version": "==2.2.1"
"version": "==2.2.2"
},
"wirerope": {
"hashes": [
@ -980,13 +984,6 @@
],
"version": "==0.7.12"
},
"atomicwrites": {
"hashes": [
"sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"
],
"markers": "sys_platform == 'win32'",
"version": "==1.4.1"
},
"attrs": {
"hashes": [
"sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6",
@ -1000,7 +997,7 @@
"sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51",
"sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"
],
"markers": "python_full_version >= '3.6.0'",
"markers": "python_version >= '3.6'",
"version": "==2.10.3"
},
"black": {
@ -1042,11 +1039,11 @@
},
"charset-normalizer": {
"hashes": [
"sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
"sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
"sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845",
"sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"
],
"markers": "python_version >= '3.6'",
"version": "==2.1.0"
"version": "==2.1.1"
},
"click": {
"hashes": [
@ -1066,57 +1063,66 @@
},
"coverage": {
"hashes": [
"sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32",
"sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7",
"sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996",
"sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55",
"sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46",
"sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de",
"sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039",
"sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee",
"sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1",
"sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f",
"sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63",
"sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083",
"sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe",
"sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0",
"sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6",
"sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe",
"sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933",
"sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0",
"sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c",
"sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07",
"sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8",
"sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b",
"sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e",
"sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120",
"sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f",
"sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e",
"sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd",
"sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f",
"sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386",
"sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8",
"sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae",
"sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc",
"sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783",
"sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d",
"sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c",
"sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97",
"sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978",
"sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf",
"sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29",
"sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39",
"sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452"
"sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2",
"sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820",
"sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827",
"sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3",
"sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d",
"sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145",
"sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875",
"sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2",
"sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74",
"sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f",
"sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c",
"sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973",
"sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1",
"sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782",
"sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0",
"sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760",
"sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a",
"sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3",
"sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7",
"sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a",
"sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f",
"sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e",
"sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86",
"sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa",
"sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa",
"sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796",
"sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a",
"sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928",
"sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0",
"sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac",
"sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c",
"sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685",
"sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d",
"sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e",
"sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f",
"sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558",
"sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58",
"sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781",
"sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a",
"sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa",
"sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc",
"sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892",
"sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d",
"sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817",
"sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1",
"sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c",
"sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908",
"sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19",
"sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60",
"sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"
],
"index": "pypi",
"version": "==6.4.2"
"version": "==6.4.4"
},
"distlib": {
"hashes": [
"sha256:a7f75737c70be3b25e2bee06288cec4e4c221de18455b2dd037fe2a795cab2fe",
"sha256:b710088c59f06338ca514800ad795a132da19fda270e3ce4affc74abf955a26c"
"sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46",
"sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"
],
"version": "==0.3.5"
"version": "==0.3.6"
},
"dlint": {
"hashes": [
@ -1135,11 +1141,11 @@
},
"filelock": {
"hashes": [
"sha256:37def7b658813cda163b56fc564cdc75e86d338246458c4c28ae84cabefa2404",
"sha256:3a0fd85166ad9dbab54c9aec96737b744106dc5f15c0b09a6744a445299fcf04"
"sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc",
"sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"
],
"markers": "python_version >= '3.7'",
"version": "==3.7.1"
"version": "==3.8.0"
},
"flake8": {
"hashes": [
@ -1274,10 +1280,11 @@
},
"pathspec": {
"hashes": [
"sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a",
"sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"
"sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93",
"sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"
],
"version": "==0.9.0"
"markers": "python_version >= '3.7'",
"version": "==0.10.1"
},
"platformdirs": {
"hashes": [
@ -1292,7 +1299,7 @@
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159",
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"
],
"markers": "python_full_version >= '3.6.0'",
"markers": "python_version >= '3.6'",
"version": "==1.0.0"
},
"py": {
@ -1321,11 +1328,11 @@
},
"pygments": {
"hashes": [
"sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb",
"sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"
"sha256:56a8508ae95f98e2b9bdf93a6be5ae3f7d8af858b43e02c5a2ff083726be40c1",
"sha256:f643f331ab57ba3c9d89212ee4a2dabc6e94f117cf4eefde99a0574720d14c42"
],
"markers": "python_full_version >= '3.6.0'",
"version": "==2.12.0"
"markers": "python_version >= '3.6'",
"version": "==2.13.0"
},
"pyparsing": {
"hashes": [
@ -1337,11 +1344,11 @@
},
"pytest": {
"hashes": [
"sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c",
"sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"
"sha256:1377bda3466d70b55e3f5cecfa55bb7cfcf219c7964629b967c37cf0bda818b7",
"sha256:4f365fec2dff9c1162f834d9f18af1ba13062db0c708bf7b946f8a5c76180c39"
],
"index": "pypi",
"version": "==7.1.2"
"version": "==7.1.3"
},
"pytest-cov": {
"hashes": [
@ -1353,10 +1360,10 @@
},
"pytz": {
"hashes": [
"sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7",
"sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"
"sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197",
"sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"
],
"version": "==2022.1"
"version": "==2022.2.1"
},
"requests": {
"hashes": [
@ -1382,11 +1389,11 @@
},
"setuptools": {
"hashes": [
"sha256:7c7854ee1429a240090297628dc9f75b35318d193537968e2dc14010ee2f5bca",
"sha256:dc2662692f47d99cb8ae15a784529adeed535bcd7c277fee0beccf961522baf6"
"sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82",
"sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57"
],
"markers": "python_version >= '3.7'",
"version": "==63.4.1"
"version": "==65.3.0"
},
"six": {
"hashes": [
@ -1448,7 +1455,7 @@
"sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07",
"sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"
],
"markers": "python_full_version >= '3.6.0'",
"markers": "python_version >= '3.6'",
"version": "==2.0.0"
},
"sphinxcontrib-jsmath": {
@ -1493,11 +1500,11 @@
},
"tqdm": {
"hashes": [
"sha256:40be55d30e200777a307a7585aee69e4eabb46b4ec6a4b4a5f2d9f11e7d5408d",
"sha256:74a2cdefe14d11442cedf3ba4e21a3b84ff9a2dbdc6cfae2c34addb2a14a5ea6"
"sha256:5f4f682a004951c1b450bc753c710e9280c5746ce6ffedee253ddbcbf54cf1e4",
"sha256:6fee160d6ffcd1b1c68c65f14c829c22832bc401726335ce92c52d395944a6a1"
],
"index": "pypi",
"version": "==4.64.0"
"version": "==4.64.1"
},
"typed-ast": {
"hashes": [
@ -1534,16 +1541,16 @@
"sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
"sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
],
"markers": "python_version >= '3.7'",
"markers": "python_version < '3.8'",
"version": "==4.3.0"
},
"urllib3": {
"hashes": [
"sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc",
"sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a"
"sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e",
"sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'",
"version": "==1.26.11"
"version": "==1.26.12"
},
"virtualenv": {
"hashes": [

View File

@ -59,7 +59,7 @@ class MongoCredentialsRepository(ICredentialsRepository):
for encrypted_credentials in list_collection_result:
del encrypted_credentials[MONGO_OBJECT_ID_KEY]
plaintext_credentials = self._decrypt_credentials_mapping(encrypted_credentials)
collection_result.append(Credentials.from_mapping(plaintext_credentials))
collection_result.append(Credentials(**plaintext_credentials))
return collection_result
except Exception as err:
@ -68,7 +68,7 @@ class MongoCredentialsRepository(ICredentialsRepository):
def _save_credentials_to_collection(self, credentials: Sequence[Credentials], collection):
try:
for c in credentials:
encrypted_credentials = self._encrypt_credentials_mapping(Credentials.to_mapping(c))
encrypted_credentials = self._encrypt_credentials_mapping(c.dict(simplify=True))
collection.insert_one(encrypted_credentials)
except Exception as err:
raise StorageError(err)

View File

@ -29,7 +29,7 @@ class PropagationCredentials(AbstractResource):
return propagation_credentials, HTTPStatus.OK
def put(self, collection=None):
credentials = [Credentials.from_mapping(c) for c in request.json]
credentials = [Credentials(**c) for c in request.json]
if collection == _configured_collection:
self._credentials_repository.remove_configured_credentials()
self._credentials_repository.save_configured_credentials(credentials)

View File

@ -1,33 +1,30 @@
import logging
from typing import Mapping, Sequence
from common.credentials import CredentialComponentType, Credentials
from common.credentials import Credentials, LMHash, NTHash, Password, SSHKeypair
logger = logging.getLogger(__name__)
def format_creds_for_reporting(credentials: Sequence[Credentials]) -> Sequence[Mapping]:
logger.info("Stolen creds generated for reporting")
formatted_creds = []
cred_type_dict = {
CredentialComponentType.PASSWORD: "Clear Password",
CredentialComponentType.LM_HASH: "LM hash",
CredentialComponentType.NT_HASH: "NTLM hash",
CredentialComponentType.SSH_KEYPAIR: "Clear SSH private key",
Password: "Clear Password",
LMHash: "LM hash",
NTHash: "NTLM hash",
SSHKeypair: "Clear SSH private key",
}
for cred in credentials:
secret = cred.secret
if secret is None:
continue
if secret.credential_type not in cred_type_dict:
if type(secret) not in cred_type_dict:
continue
username = _get_username(cred)
cred_row = {
"username": username,
"_type": secret.credential_type.name,
"type": cred_type_dict[secret.credential_type],
"type": cred_type_dict[type(secret)],
}
if cred_row not in formatted_creds:
formatted_creds.append(cred_row)

View File

@ -5,6 +5,7 @@ from typing import Any
import bson
from flask import make_response
from pydantic import BaseModel
from common.utils import IJSONSerializable
@ -23,6 +24,8 @@ class APIEncoder(JSONEncoder):
return loads(value.__class__.to_json(value))
if issubclass(type(value), set):
return list(value)
if issubclass(type(value), BaseModel):
return value.dict(simplify=True)
try:
return JSONEncoder.default(self, value)
except TypeError:

View File

@ -19,8 +19,6 @@ class CredentialsParser:
self._parse_credentials(telemetry_dict, _agent_configuration)
def _parse_credentials(self, telemetry_dict: Mapping, _agent_configuration):
credentials = [
Credentials.from_mapping(credential) for credential in telemetry_dict["data"]
]
credentials = [Credentials(**credential) for credential in telemetry_dict["data"]]
self._credentials_repository.save_stolen_credentials(credentials)

View File

@ -1,20 +1,26 @@
import {defaultCredentials} from '../../services/configuration/propagation/credentials';
import {PlaintextTypes, SecretTypes} from '../utils/CredentialTitles.js';
import _ from 'lodash';
export function reformatConfig(config, reverse = false) {
let formattedConfig = _.clone(config);
if (reverse) {
if(formattedConfig['payloads'].length === 1){
if (formattedConfig['payloads'].length === 1) {
// Second click on Export
formattedConfig['payloads'] = [{'name': 'ransomware', 'options': formattedConfig['payloads'][0]['options']}];
formattedConfig['payloads'] = [{
'name': 'ransomware',
'options': formattedConfig['payloads'][0]['options']
}];
} else {
formattedConfig['payloads'] = [{'name': 'ransomware', 'options': formattedConfig['payloads']}];
formattedConfig['payloads'] = [{
'name': 'ransomware',
'options': formattedConfig['payloads']
}];
}
formattedConfig['keep_tunnel_open_time'] = formattedConfig['advanced']['keep_tunnel_open_time'];
} else {
if(formattedConfig['payloads'].length !== 0)
{
if (formattedConfig['payloads'].length !== 0) {
formattedConfig['payloads'] = formattedConfig['payloads'][0]['options'];
} else {
formattedConfig['payloads'] = {'encryption': {}, 'other_behaviors': {}}
@ -29,23 +35,26 @@ export function formatCredentialsForForm(credentials) {
let formattedCredentials = _.clone(defaultCredentials);
for (let i = 0; i < credentials.length; i++) {
let identity = credentials[i]['identity'];
if(identity !== null) {
if (identity !== null) {
formattedCredentials['exploit_user_list'].push(identity.username)
}
let secret = credentials[i]['secret'];
if(secret !== null){
if (secret['credential_type'] === 'PASSWORD') {
formattedCredentials['exploit_password_list'].push(secret['password'])
if (secret !== null) {
if (Object.prototype.hasOwnProperty.call(secret, SecretTypes.Password)) {
formattedCredentials['exploit_password_list'].push(secret[SecretTypes.Password])
}
if (secret['credential_type'] === 'NT_HASH') {
formattedCredentials['exploit_ntlm_hash_list'].push(secret['nt_hash'])
if (Object.prototype.hasOwnProperty.call(secret, SecretTypes.NTHash)) {
formattedCredentials['exploit_ntlm_hash_list'].push(secret[SecretTypes.NTHash])
}
if (secret['credential_type'] === 'LM_HASH') {
formattedCredentials['exploit_lm_hash_list'].push(secret['lm_hash'])
if (Object.prototype.hasOwnProperty.call(secret, SecretTypes.LMHash)) {
formattedCredentials['exploit_lm_hash_list'].push(secret[SecretTypes.LMHash])
}
if (Object.prototype.hasOwnProperty.call(secret, SecretTypes.PrivateKey)) {
let keypair = {
'public_key': secret[PlaintextTypes.PublicKey],
'private_key': secret[SecretTypes.PrivateKey]
}
if (secret['credential_type'] === 'SSH_KEY') {
let keypair = {'public_key': secret['public_key'], 'private_key': secret['private_key']}
formattedCredentials['exploit_ssh_keys'].push(keypair)
}
}
@ -64,43 +73,36 @@ export function formatCredentialsForIsland(credentials) {
let usernames = credentials['exploit_user_list'];
for (let i = 0; i < usernames.length; i++) {
formattedCredentials.push({
'identity': {'username': usernames[i], 'credential_type': 'USERNAME'},
'identity': {'username': usernames[i]},
'secret': null
})
}
let passwords = credentials['exploit_password_list'];
for (let i = 0; i < passwords.length; i++) {
formattedCredentials.push({
'identity': null,
'secret': {'credential_type': 'PASSWORD', 'password': passwords[i]}
})
}
let nt_hashes = credentials['exploit_ntlm_hash_list'];
for (let i = 0; i < nt_hashes.length; i++) {
formattedCredentials.push({
'identity': null,
'secret': {'credential_type': 'NT_HASH', 'nt_hash': nt_hashes[i]}
})
}
let lm_hashes = credentials['exploit_lm_hash_list'];
for (let i = 0; i < lm_hashes.length; i++) {
formattedCredentials.push({
'identity': null,
'secret': {'credential_type': 'LM_HASH', 'lm_hash': lm_hashes[i]}
})
}
formattedCredentials.push(...getFormattedCredentials(credentials['exploit_password_list'], SecretTypes.Password))
formattedCredentials.push(...getFormattedCredentials(credentials['exploit_ntlm_hash_list'], SecretTypes.NTHash))
formattedCredentials.push(...getFormattedCredentials(credentials['exploit_lm_hash_list'], SecretTypes.LMHash))
let ssh_keys = credentials['exploit_ssh_keys'];
for (let i = 0; i < ssh_keys.length; i++) {
formattedCredentials.push({
'identity': null,
'secret': {'credential_type': 'SSH_KEYPAIR', 'private_key': ssh_keys[i]['private_key'],
'public_key': ssh_keys[i]['public_key']}
'secret': {
'private_key': ssh_keys[i]['private_key'],
'public_key': ssh_keys[i]['public_key']
}
})
}
return formattedCredentials;
}
function getFormattedCredentials(credentials, keyOfSecret) {
let formattedCredentials = [];
for (let i = 0; i < credentials.length; i++) {
formattedCredentials.push({
'identity': null,
'secret': {[keyOfSecret]: credentials[i]}
})
}
return formattedCredentials;
}

View File

@ -1,64 +1,68 @@
export function getAllUsernames(stolen, configured){
let usernames = [];
usernames.push(...getCredentialsUsernames(stolen));
usernames.push(...getCredentialsUsernames(configured));
return usernames;
import {CredentialTitles, SecretTypes} from '../utils/CredentialTitles.js';
export function getAllUsernames(stolen, configured) {
let usernames = new Set();
usernames.add(...getCredentialsUsernames(stolen));
usernames.add(...getCredentialsUsernames(configured));
return Array.from(usernames);
}
export function getCredentialsUsernames(credentials) {
let usernames = [];
for(let i = 0; i < credentials.length; i++){
for (let i = 0; i < credentials.length; i++) {
let username = credentials[i]['identity'];
if(username !== null) {
if (username !== null) {
usernames.push(username['username']);
}
}
return usernames;
}
export function getAllSecrets(stolen, configured){
let secrets = [];
for(let i = 0; i < stolen.length; i++){
export function getAllSecrets(stolen, configured) {
let secrets = new Set();
for (let i = 0; i < stolen.length; i++) {
let secret = stolen[i]['secret'];
if(secret !== null){
secrets.push(getSecretsFromCredential(secret));
if (secret !== null) {
secrets.add(reformatSecret(secret));
}
}
for(let i = 0; i < configured.length; i++){
for (let i = 0; i < configured.length; i++) {
let secret = configured[i]['secret'];
if(secret !== null){
secrets.push(getSecretsFromCredential(secret));
if (secret !== null) {
secrets.add(reformatSecret(secret));
}
}
return secrets;
return Array.from(secrets);
}
function getSecretsFromCredential(credential) {
if(credential['credential_type'] === 'SSH_KEYPAIR'){
return {'type': 'SSH keypair', 'content': credential['private_key']}
function reformatSecret(secret) {
if (Object.prototype.hasOwnProperty.call(secret, SecretTypes.Password)) {
return {'title': CredentialTitles.Password, 'content': secret[SecretTypes.Password]}
}
if(credential['credential_type'] === 'NT_HASH'){
return {'type': 'NT hash', 'content': credential['nt_hash']}
if (Object.prototype.hasOwnProperty.call(secret, SecretTypes.NTHash)) {
return {'title': CredentialTitles.NTHash, 'content': secret[SecretTypes.NTHash]}
}
if(credential['credential_type'] === 'LM_HASH'){
return {'type': 'LM hash', 'content': credential['lm_hash']}
if (Object.prototype.hasOwnProperty.call(secret, SecretTypes.LMHash)) {
return {'title': CredentialTitles.LMHash, 'content': secret[SecretTypes.LMHash]}
}
if (Object.prototype.hasOwnProperty.call(secret, SecretTypes.PrivateKey)) {
return {
'title': CredentialTitles.SSHKeys,
'content': secret[SecretTypes.PrivateKey]
}
if(credential['credential_type'] === 'PASSWORD'){
return {'type': 'Password', 'content': credential['password']}
}
}
export function getCredentialsTableData(credentials) {
let table_data = [];
let identites = getCredentialsUsernames(credentials);
let secrets = getAllSecrets(credentials, [])
for(let i=0; i<credentials.length; i++) {
for (let i = 0; i < credentials.length; i++) {
let row_data = {};
row_data['username'] = identites[i];
row_data['type'] = secrets[i]['type'];
row_data['title'] = secrets[i]['title'];
table_data.push(row_data);
}

View File

@ -7,7 +7,7 @@ const columns = [
Header: 'Stolen Credentials',
columns: [
{Header: 'Username', accessor: 'username'},
{Header: 'Type', accessor: 'type'}
{Header: 'Type', accessor: 'title'}
]
}
];

View File

@ -24,7 +24,7 @@ class UsedCredentials extends React.Component {
</p>
<ul>
{allSecrets.map((x, index) => <li
key={index}>{x['type']}: {x['content'].substr(0, 3) + '******'}</li>)}
key={index}>{x['title']}: {x['content'].substr(0, 3) + '******'}</li>)}
</ul>
</>
:

View File

@ -0,0 +1,18 @@
export const CredentialTitles = {
Password: 'Clear Password',
SSHKeys: 'Clear SSH private key',
LMHash: 'LM hash',
NTHash: 'NT hash',
Username: 'Username'
}
export const SecretTypes = {
Password: 'password',
PrivateKey: 'private_key',
LMHash: 'lm_hash',
NTHash: 'nt_hash'
}
export const PlaintextTypes = {
PublicKey: 'public_key'
}

View File

@ -1,31 +1,64 @@
from itertools import product
from pydantic import SecretStr
from common.credentials import Credentials, LMHash, NTHash, Password, SSHKeypair, Username
USERNAME = "m0nk3y_user"
SPECIAL_USERNAME = "m0nk3y.user"
NT_HASH = "C1C58F96CDF212B50837BC11A00BE47C"
LM_HASH = "299BD128C1101FD6299BD128C1101FD6"
PASSWORD_1 = "trytostealthis"
PASSWORD_2 = "password!"
PASSWORD_3 = "rubberbabybuggybumpers"
PLAINTEXT_NT_HASH = "C1C58F96CDF212B50837BC11A00BE47C"
PLAINTEXT_LM_HASH = "299BD128C1101FD6299BD128C1101FD6"
PLAINTEXT_PASSWORD = "trytostealthis"
PLAINTEXT_PRIVATE_KEY = "MY_PRIVATE_KEY"
NT_HASH = SecretStr(PLAINTEXT_NT_HASH)
LM_HASH = SecretStr(PLAINTEXT_LM_HASH)
PASSWORD_1 = SecretStr(PLAINTEXT_PASSWORD)
PASSWORD_2 = SecretStr("password!")
PASSWORD_3 = SecretStr("rubberbabybuggybumpers")
PUBLIC_KEY = "MY_PUBLIC_KEY"
PRIVATE_KEY = "MY_PRIVATE_KEY"
PRIVATE_KEY = SecretStr(PLAINTEXT_PRIVATE_KEY)
PASSWORD_CREDENTIALS_1 = Credentials(identity=Username(USERNAME), secret=Password(PASSWORD_1))
PASSWORD_CREDENTIALS_2 = Credentials(identity=Username(USERNAME), secret=Password(PASSWORD_2))
LM_HASH_CREDENTIALS = Credentials(identity=Username(SPECIAL_USERNAME), secret=LMHash(LM_HASH))
NT_HASH_CREDENTIALS = Credentials(identity=Username(USERNAME), secret=NTHash(NT_HASH))
SSH_KEY_CREDENTIALS = Credentials(
identity=Username(USERNAME), secret=SSHKeypair(PRIVATE_KEY, PUBLIC_KEY)
IDENTITIES = [Username(username=USERNAME), None, Username(username=SPECIAL_USERNAME)]
IDENTITY_DICTS = [{"username": USERNAME}, None]
SECRETS = (
Password(password=PASSWORD_1),
Password(password=PASSWORD_2),
Password(password=PASSWORD_3),
LMHash(lm_hash=LM_HASH),
NTHash(nt_hash=NT_HASH),
SSHKeypair(private_key=PRIVATE_KEY, public_key=PUBLIC_KEY),
None,
)
EMPTY_SECRET_CREDENTIALS = Credentials(identity=Username(USERNAME), secret=None)
EMPTY_IDENTITY_CREDENTIALS = Credentials(identity=None, secret=Password(PASSWORD_3))
PROPAGATION_CREDENTIALS = [
PASSWORD_CREDENTIALS_1,
LM_HASH_CREDENTIALS,
NT_HASH_CREDENTIALS,
PASSWORD_CREDENTIALS_2,
SSH_KEY_CREDENTIALS,
EMPTY_SECRET_CREDENTIALS,
EMPTY_IDENTITY_CREDENTIALS,
SECRET_DICTS = [
{"password": PASSWORD_1},
{"lm_hash": LM_HASH},
{"nt_hash": NT_HASH},
{
"public_key": PUBLIC_KEY,
"private_key": PRIVATE_KEY,
},
None,
]
CREDENTIALS = [
Credentials(identity=identity, secret=secret)
for identity, secret in product(IDENTITIES, SECRETS)
]
FULL_CREDENTIALS = [
credentials
for credentials in CREDENTIALS
if not (credentials.identity is None and credentials.secret is None)
]
CREDENTIALS_DICTS = [
{"identity": identity, "secret": secret}
for identity, secret in product(IDENTITY_DICTS, SECRET_DICTS)
]
FULL_CREDENTIALS_DICTS = [
credentials
for credentials in CREDENTIALS_DICTS
if not (credentials["identity"] is None and credentials["secret"] is None)
]

View File

@ -0,0 +1,20 @@
from typing import List, Union
import pytest
@pytest.fixture(scope="session")
def valid_ntlm_hash() -> str:
return "E520AC67419A9A224A3B108F3FA6CB6D"
@pytest.fixture(scope="session")
def invalid_ntlm_hashes() -> List[Union[str, int, float]]:
return [
0,
1,
2.0,
"invalid",
"0123456789012345678901234568901",
"E52GAC67419A9A224A3B108F3FA6CB6D",
]

View File

@ -1,148 +0,0 @@
from typing import Any, Mapping
import pytest
from marshmallow.exceptions import ValidationError
from common.credentials import (
CredentialComponentType,
LMHash,
NTHash,
Password,
SSHKeypair,
Username,
)
from common.credentials.lm_hash import LMHashSchema
from common.credentials.nt_hash import NTHashSchema
from common.credentials.password import PasswordSchema
from common.credentials.ssh_keypair import SSHKeypairSchema
from common.credentials.username import UsernameSchema
PARAMETRIZED_PARAMETER_NAMES = (
"credential_component_class, schema_class, credential_component_type, credential_component_data"
)
PARAMETRIZED_PARAMETER_VALUES = [
(Username, UsernameSchema, CredentialComponentType.USERNAME, {"username": "test_user"}),
(Password, PasswordSchema, CredentialComponentType.PASSWORD, {"password": "123456"}),
(
LMHash,
LMHashSchema,
CredentialComponentType.LM_HASH,
{"lm_hash": "E52CAC67419A9A224A3B108F3FA6CB6D"},
),
(
NTHash,
NTHashSchema,
CredentialComponentType.NT_HASH,
{"nt_hash": "E52CAC67419A9A224A3B108F3FA6CB6D"},
),
(
SSHKeypair,
SSHKeypairSchema,
CredentialComponentType.SSH_KEYPAIR,
{"public_key": "TEST_PUBLIC_KEY", "private_key": "TEST_PRIVATE_KEY"},
),
]
INVALID_COMPONENT_DATA = {
CredentialComponentType.USERNAME: ({"username": None}, {"username": 1}, {"username": 2.0}),
CredentialComponentType.PASSWORD: ({"password": None}, {"password": 1}, {"password": 2.0}),
CredentialComponentType.LM_HASH: (
{"lm_hash": None},
{"lm_hash": 1},
{"lm_hash": 2.0},
{"lm_hash": "0123456789012345678901234568901"},
{"lm_hash": "E52GAC67419A9A224A3B108F3FA6CB6D"},
),
CredentialComponentType.NT_HASH: (
{"nt_hash": None},
{"nt_hash": 1},
{"nt_hash": 2.0},
{"nt_hash": "0123456789012345678901234568901"},
{"nt_hash": "E52GAC67419A9A224A3B108F3FA6CB6D"},
),
CredentialComponentType.SSH_KEYPAIR: (
{"public_key": None, "private_key": "TEST_PRIVATE_KEY"},
{"public_key": "TEST_PUBLIC_KEY", "private_key": None},
{"public_key": 1, "private_key": "TEST_PRIVATE_KEY"},
{"public_key": "TEST_PUBLIC_KEY", "private_key": 999},
),
}
def build_component_dict(
credential_component_type: CredentialComponentType, credential_component_data: Mapping[str, Any]
):
return {"credential_type": credential_component_type.name, **credential_component_data}
@pytest.mark.parametrize(PARAMETRIZED_PARAMETER_NAMES, PARAMETRIZED_PARAMETER_VALUES)
def test_credential_component_serialize(
credential_component_class, schema_class, credential_component_type, credential_component_data
):
schema = schema_class()
constructed_object = credential_component_class(**credential_component_data)
serialized_object = schema.dump(constructed_object)
assert serialized_object == build_component_dict(
credential_component_type, credential_component_data
)
@pytest.mark.parametrize(PARAMETRIZED_PARAMETER_NAMES, PARAMETRIZED_PARAMETER_VALUES)
def test_credential_component_deserialize(
credential_component_class, schema_class, credential_component_type, credential_component_data
):
schema = schema_class()
credential_dict = build_component_dict(credential_component_type, credential_component_data)
expected_deserialized_object = credential_component_class(**credential_component_data)
deserialized_object = credential_component_class(**schema.load(credential_dict))
assert deserialized_object == expected_deserialized_object
@pytest.mark.parametrize(PARAMETRIZED_PARAMETER_NAMES, PARAMETRIZED_PARAMETER_VALUES)
def test_invalid_credential_type(
credential_component_class, schema_class, credential_component_type, credential_component_data
):
invalid_component_dict = build_component_dict(
credential_component_type, credential_component_data
)
invalid_component_dict["credential_type"] = "INVALID"
schema = schema_class()
with pytest.raises(ValidationError):
credential_component_class(**schema.load(invalid_component_dict))
@pytest.mark.parametrize(PARAMETRIZED_PARAMETER_NAMES, PARAMETRIZED_PARAMETER_VALUES)
def test_encorrect_credential_type(
credential_component_class, schema_class, credential_component_type, credential_component_data
):
incorrect_component_dict = build_component_dict(
credential_component_type, credential_component_data
)
incorrect_component_dict["credential_type"] = (
CredentialComponentType.USERNAME.name
if credential_component_type != CredentialComponentType.USERNAME
else CredentialComponentType.PASSWORD
)
schema = schema_class()
with pytest.raises(ValidationError):
credential_component_class(**schema.load(incorrect_component_dict))
@pytest.mark.parametrize(PARAMETRIZED_PARAMETER_NAMES, PARAMETRIZED_PARAMETER_VALUES)
def test_invalid_values(
credential_component_class, schema_class, credential_component_type, credential_component_data
):
schema = schema_class()
for invalid_component_data in INVALID_COMPONENT_DATA[credential_component_type]:
component_dict = build_component_dict(credential_component_type, invalid_component_data)
with pytest.raises(ValidationError):
credential_component_class(**schema.load(component_dict))

View File

@ -1,122 +1,72 @@
import json
from itertools import product
import logging
from pathlib import Path
import pytest
from pydantic import SecretBytes
from pydantic.types import SecretStr
from tests.data_for_tests.propagation_credentials import (
CREDENTIALS,
CREDENTIALS_DICTS,
LM_HASH,
NT_HASH,
PASSWORD_1,
PLAINTEXT_LM_HASH,
PLAINTEXT_PASSWORD,
PLAINTEXT_PRIVATE_KEY,
PRIVATE_KEY,
PUBLIC_KEY,
USERNAME,
)
from common.credentials import (
Credentials,
InvalidCredentialComponentError,
InvalidCredentialsError,
LMHash,
NTHash,
Password,
SSHKeypair,
Username,
)
IDENTITIES = [Username(USERNAME), None]
IDENTITY_DICTS = [{"credential_type": "USERNAME", "username": USERNAME}, None]
SECRETS = (
Password(PASSWORD_1),
LMHash(LM_HASH),
NTHash(NT_HASH),
SSHKeypair(PRIVATE_KEY, PUBLIC_KEY),
None,
)
SECRET_DICTS = [
{"credential_type": "PASSWORD", "password": PASSWORD_1},
{"credential_type": "LM_HASH", "lm_hash": LM_HASH},
{"credential_type": "NT_HASH", "nt_hash": NT_HASH},
{
"credential_type": "SSH_KEYPAIR",
"public_key": PUBLIC_KEY,
"private_key": PRIVATE_KEY,
},
None,
]
CREDENTIALS = [
Credentials(identity, secret)
for identity, secret in product(IDENTITIES, SECRETS)
if not (identity is None and secret is None)
]
CREDENTIALS_DICTS = [
{"identity": identity, "secret": secret}
for identity, secret in product(IDENTITY_DICTS, SECRET_DICTS)
if not (identity is None and secret is None)
]
from common.base_models import InfectionMonkeyBaseModel
from common.credentials import Credentials
from common.credentials.credentials import get_plaintext
@pytest.mark.parametrize(
"credentials, expected_credentials_dict", zip(CREDENTIALS, CREDENTIALS_DICTS)
)
def test_credentials_serialization_json(credentials, expected_credentials_dict):
serialized_credentials = Credentials.to_json(credentials)
serialized_credentials = credentials.json()
deserialized_credentials = Credentials.parse_raw(serialized_credentials)
assert json.loads(serialized_credentials) == expected_credentials_dict
assert credentials == deserialized_credentials
@pytest.mark.parametrize(
"credentials, expected_credentials_dict", zip(CREDENTIALS, CREDENTIALS_DICTS)
)
def test_credentials_serialization_mapping(credentials, expected_credentials_dict):
serialized_credentials = Credentials.to_mapping(credentials)
assert serialized_credentials == expected_credentials_dict
logger = logging.getLogger()
logger.level = logging.DEBUG
@pytest.mark.parametrize(
"expected_credentials, credentials_dict", zip(CREDENTIALS, CREDENTIALS_DICTS)
)
def test_credentials_deserialization__from_mapping(expected_credentials, credentials_dict):
deserialized_credentials = Credentials.from_mapping(credentials_dict)
def test_credentials_secrets_not_logged(caplog):
class TestSecret(InfectionMonkeyBaseModel):
some_secret: SecretStr
some_secret_in_bytes: SecretBytes
assert deserialized_credentials == expected_credentials
class TestCredentials(Credentials):
secret: TestSecret
sensitive = "super_secret"
creds = TestCredentials(
identity=None,
secret=TestSecret(some_secret=sensitive, some_secret_in_bytes=sensitive.encode()),
)
logging.getLogger().info(
f"{creds.secret.some_secret} and" f" {creds.secret.some_secret_in_bytes}"
)
assert sensitive not in caplog.text
@pytest.mark.parametrize(
"expected_credentials, credentials_dict", zip(CREDENTIALS, CREDENTIALS_DICTS)
)
def test_credentials_deserialization__from_json(expected_credentials, credentials_dict):
deserialized_credentials = Credentials.from_json(json.dumps(credentials_dict))
assert deserialized_credentials == expected_credentials
_plaintext = [
PLAINTEXT_PASSWORD,
PLAINTEXT_PRIVATE_KEY,
PLAINTEXT_LM_HASH,
"",
"already_plaintext",
Path("C:\\jolly_fella"),
None,
]
_hidden = [PASSWORD_1, PRIVATE_KEY, LM_HASH, "", "already_plaintext", Path("C:\\jolly_fella"), None]
def test_credentials_deserialization__invalid_credentials():
invalid_data = {"secret": SECRET_DICTS[0], "unknown_key": []}
with pytest.raises(InvalidCredentialsError):
Credentials.from_mapping(invalid_data)
def test_credentials_deserialization__invalid_component_type():
invalid_data = {
"secret": SECRET_DICTS[0],
"identity": {"credential_type": "FAKE", "username": "user1"},
}
with pytest.raises(InvalidCredentialsError):
Credentials.from_mapping(invalid_data)
def test_credentials_deserialization__invalid_component():
invalid_data = {
"secret": SECRET_DICTS[0],
"identity": {"credential_type": "USERNAME", "unknown_field": "user1"},
}
with pytest.raises(InvalidCredentialComponentError):
Credentials.from_mapping(invalid_data)
def test_create_credentials__none_none():
with pytest.raises(InvalidCredentialsError):
Credentials(None, None)
@pytest.mark.parametrize("expected, hidden", list(zip(_plaintext, _hidden)))
def test_get_plain_text(expected, hidden):
assert expected == get_plaintext(hidden)

View File

@ -0,0 +1,14 @@
import pytest
from common.credentials import LMHash
def test_construct_valid_nt_hash(valid_ntlm_hash):
# This test will fail if an exception is raised
LMHash(lm_hash=valid_ntlm_hash)
def test_construct_invalid_nt_hash(invalid_ntlm_hashes):
for invalid_hash in invalid_ntlm_hashes:
with pytest.raises(ValueError):
LMHash(lm_hash=invalid_hash)

View File

@ -0,0 +1,14 @@
import pytest
from common.credentials import NTHash
def test_construct_valid_nt_hash(valid_ntlm_hash):
# This test will fail if an exception is raised
NTHash(nt_hash=valid_ntlm_hash)
def test_construct_invalid_nt_hash(invalid_ntlm_hashes):
for invalid_hash in invalid_ntlm_hashes:
with pytest.raises(ValueError):
NTHash(nt_hash=invalid_hash)

View File

@ -1,26 +0,0 @@
import pytest
from common.credentials import InvalidCredentialComponentError, LMHash, NTHash
VALID_HASH = "E520AC67419A9A224A3B108F3FA6CB6D"
INVALID_HASHES = (
0,
1,
2.0,
"invalid",
"0123456789012345678901234568901",
"E52GAC67419A9A224A3B108F3FA6CB6D",
)
@pytest.mark.parametrize("ntlm_hash_class", (LMHash, NTHash))
def test_construct_valid_ntlm_hash(ntlm_hash_class):
# This test will fail if an exception is raised
ntlm_hash_class(VALID_HASH)
@pytest.mark.parametrize("ntlm_hash_class", (LMHash, NTHash))
def test_construct_invalid_ntlm_hash(ntlm_hash_class):
for invalid_hash in INVALID_HASHES:
with pytest.raises(InvalidCredentialComponentError):
ntlm_hash_class(invalid_hash)

View File

@ -41,9 +41,9 @@ def test_pypykatz_result_parsing(monkeypatch):
win_creds = [WindowsCredentials(username="user", password="secret", ntlm_hash="", lm_hash="")]
patch_pypykatz(win_creds, monkeypatch)
username = Username("user")
password = Password("secret")
expected_credentials = Credentials(username, password)
username = Username(username="user")
password = Password(password="secret")
expected_credentials = Credentials(identity=username, secret=password)
collected_credentials = collect_credentials()
assert len(collected_credentials) == 1
@ -70,10 +70,13 @@ def test_pypykatz_result_parsing_defaults(monkeypatch):
patch_pypykatz(win_creds, monkeypatch)
# Expected credentials
username = Username("user2")
password = Password("secret2")
lm_hash = LMHash("0182BD0BD4444BF8FC83B5D9042EED2E")
expected_credentials = [Credentials(username, password), Credentials(username, lm_hash)]
username = Username(username="user2")
password = Password(password="secret2")
lm_hash = LMHash(lm_hash="0182BD0BD4444BF8FC83B5D9042EED2E")
expected_credentials = [
Credentials(identity=username, secret=password),
Credentials(identity=username, secret=lm_hash),
]
collected_credentials = collect_credentials()
assert len(collected_credentials) == 2
@ -91,9 +94,12 @@ def test_pypykatz_result_parsing_no_identities(monkeypatch):
]
patch_pypykatz(win_creds, monkeypatch)
lm_hash = LMHash("0182BD0BD4444BF8FC83B5D9042EED2E")
nt_hash = NTHash("E9F85516721DDC218359AD5280DB4450")
expected_credentials = [Credentials(None, lm_hash), Credentials(None, nt_hash)]
lm_hash = LMHash(lm_hash="0182BD0BD4444BF8FC83B5D9042EED2E")
nt_hash = NTHash(nt_hash="E9F85516721DDC218359AD5280DB4450")
expected_credentials = [
Credentials(identity=None, secret=lm_hash),
Credentials(identity=None, secret=nt_hash),
]
collected_credentials = collect_credentials()
assert len(collected_credentials) == 2
@ -112,7 +118,7 @@ def test_pypykatz_result_parsing_no_secrets(monkeypatch):
]
patch_pypykatz(win_creds, monkeypatch)
expected_credentials = [Credentials(Username(username), None)]
expected_credentials = [Credentials(identity=Username(username=username), secret=None)]
collected_credentials = collect_credentials()
assert len(collected_credentials) == 1

View File

@ -31,7 +31,6 @@ def test_ssh_credentials_empty_results(monkeypatch, ssh_creds, patch_telemetry_m
def test_ssh_info_result_parsing(monkeypatch, patch_telemetry_messenger):
ssh_creds = [
{
"name": "ubuntu",
@ -56,13 +55,15 @@ def test_ssh_info_result_parsing(monkeypatch, patch_telemetry_messenger):
patch_ssh_handler(ssh_creds, monkeypatch)
# Expected credentials
username = Username("ubuntu")
username2 = Username("mcus")
username3 = Username("guest")
username = Username(username="ubuntu")
username2 = Username(username="mcus")
username3 = Username(username="guest")
ssh_keypair1 = SSHKeypair("ExtremelyGoodPrivateKey", "SomePublicKeyUbuntu")
ssh_keypair2 = SSHKeypair("", "AnotherPublicKey")
ssh_keypair3 = SSHKeypair("PrivKey", "PubKey")
ssh_keypair1 = SSHKeypair(
private_key="ExtremelyGoodPrivateKey", public_key="SomePublicKeyUbuntu"
)
ssh_keypair2 = SSHKeypair(private_key="", public_key="AnotherPublicKey")
ssh_keypair3 = SSHKeypair(private_key="PrivKey", public_key="PubKey")
expected = [
Credentials(identity=username, secret=ssh_keypair1),

View File

@ -8,7 +8,11 @@ from infection_monkey.credential_repository import (
add_credentials_from_event_to_propagation_credentials_repository,
)
credentials = [Credentials(identity=Username("test_username"), secret=Password("some_password"))]
credentials = [
Credentials(
identity=Username(username="test_username"), secret=Password(password="some_password")
)
]
credentials_stolen_event = CredentialsStolenEvent(
source=UUID("f811ad00-5a68-4437-bd51-7b5cc1768ad5"),

View File

@ -1,14 +1,15 @@
from unittest.mock import MagicMock
import pytest
from pydantic import SecretStr
from tests.data_for_tests.propagation_credentials import (
CREDENTIALS,
LM_HASH,
NT_HASH,
PASSWORD_1,
PASSWORD_2,
PASSWORD_3,
PRIVATE_KEY,
PROPAGATION_CREDENTIALS,
PUBLIC_KEY,
SPECIAL_USERNAME,
USERNAME,
@ -17,7 +18,6 @@ from tests.data_for_tests.propagation_credentials import (
from common.credentials import Credentials, LMHash, NTHash, Password, SSHKeypair, Username
from infection_monkey.credential_repository import AggregatingPropagationCredentialsRepository
CONTROL_CHANNEL_CREDENTIALS = PROPAGATION_CREDENTIALS
TRANSFORMED_CONTROL_CHANNEL_CREDENTIALS = {
"exploit_user_list": {USERNAME, SPECIAL_USERNAME},
"exploit_password_list": {PASSWORD_1, PASSWORD_2, PASSWORD_3},
@ -31,37 +31,43 @@ EMPTY_CHANNEL_CREDENTIALS = []
STOLEN_USERNAME_1 = "user1"
STOLEN_USERNAME_2 = "user2"
STOLEN_USERNAME_3 = "user3"
STOLEN_PASSWORD_1 = "abcdefg"
STOLEN_PASSWORD_2 = "super_secret"
STOLEN_PASSWORD_1 = SecretStr("abcdefg")
STOLEN_PASSWORD_2 = SecretStr("super_secret")
STOLEN_PUBLIC_KEY_1 = "some_public_key_1"
STOLEN_PUBLIC_KEY_2 = "some_public_key_2"
STOLEN_LM_HASH = "AAD3B435B51404EEAAD3B435B51404EE"
STOLEN_NT_HASH = "C0172DFF622FE29B5327CB79DC12D24C"
STOLEN_PRIVATE_KEY_1 = "some_private_key_1"
STOLEN_PRIVATE_KEY_2 = "some_private_key_2"
STOLEN_LM_HASH = SecretStr("AAD3B435B51404EEAAD3B435B51404EE")
STOLEN_NT_HASH = SecretStr("C0172DFF622FE29B5327CB79DC12D24C")
STOLEN_PRIVATE_KEY_1 = SecretStr("some_private_key_1")
STOLEN_PRIVATE_KEY_2 = SecretStr("some_private_key_2")
STOLEN_CREDENTIALS = [
Credentials(
identity=Username(STOLEN_USERNAME_1),
secret=Password(PASSWORD_1),
identity=Username(username=STOLEN_USERNAME_1),
secret=Password(password=PASSWORD_1),
),
Credentials(identity=Username(STOLEN_USERNAME_1), secret=Password(STOLEN_PASSWORD_1)),
Credentials(
identity=Username(STOLEN_USERNAME_2),
identity=Username(username=STOLEN_USERNAME_1), secret=Password(password=STOLEN_PASSWORD_1)
),
Credentials(
identity=Username(username=STOLEN_USERNAME_2),
secret=SSHKeypair(public_key=STOLEN_PUBLIC_KEY_1, private_key=STOLEN_PRIVATE_KEY_1),
),
Credentials(
identity=None,
secret=Password(STOLEN_PASSWORD_2),
secret=Password(password=STOLEN_PASSWORD_2),
),
Credentials(identity=Username(STOLEN_USERNAME_2), secret=LMHash(STOLEN_LM_HASH)),
Credentials(identity=Username(STOLEN_USERNAME_2), secret=NTHash(STOLEN_NT_HASH)),
Credentials(identity=Username(STOLEN_USERNAME_3), secret=None),
Credentials(
identity=Username(username=STOLEN_USERNAME_2), secret=LMHash(lm_hash=STOLEN_LM_HASH)
),
Credentials(
identity=Username(username=STOLEN_USERNAME_2), secret=NTHash(nt_hash=STOLEN_NT_HASH)
),
Credentials(identity=Username(username=STOLEN_USERNAME_3), secret=None),
]
STOLEN_SSH_KEYS_CREDENTIALS = [
Credentials(
Username(USERNAME),
SSHKeypair(public_key=STOLEN_PUBLIC_KEY_2, private_key=STOLEN_PRIVATE_KEY_2),
identity=Username(username=USERNAME),
secret=SSHKeypair(public_key=STOLEN_PUBLIC_KEY_2, private_key=STOLEN_PRIVATE_KEY_2),
)
]
@ -69,7 +75,7 @@ STOLEN_SSH_KEYS_CREDENTIALS = [
@pytest.fixture
def aggregating_credentials_repository() -> AggregatingPropagationCredentialsRepository:
control_channel = MagicMock()
control_channel.get_credentials_for_propagation.return_value = CONTROL_CHANNEL_CREDENTIALS
control_channel.get_credentials_for_propagation.return_value = CREDENTIALS
return AggregatingPropagationCredentialsRepository(control_channel)

View File

@ -1,4 +1,5 @@
import pytest
from pydantic import SecretStr
from infection_monkey.exploit.powershell_utils.credentials import Credentials, SecretType
from infection_monkey.exploit.powershell_utils.powershell_client import format_password
@ -14,17 +15,17 @@ def test_format_cached_credentials():
def test_format_password():
expected = "test_password"
expected = SecretStr("test_password")
creds = Credentials("test_user", expected, SecretType.PASSWORD)
actual = format_password(creds)
assert expected == actual
assert expected.get_secret_value() == actual
def test_format_lm_hash():
lm_hash = "c080132b6f2a0c4e5d1029cc06f48a92"
expected = f"{lm_hash}:00000000000000000000000000000000"
lm_hash = SecretStr("c080132b6f2a0c4e5d1029cc06f48a92")
expected = f"{lm_hash.get_secret_value()}:00000000000000000000000000000000"
creds = Credentials("test_user", lm_hash, SecretType.LM_HASH)
@ -34,8 +35,8 @@ def test_format_lm_hash():
def test_format_nt_hash():
nt_hash = "c080132b6f2a0c4e5d1029cc06f48a92"
expected = f"00000000000000000000000000000000:{nt_hash}"
nt_hash = SecretStr("c080132b6f2a0c4e5d1029cc06f48a92")
expected = f"00000000000000000000000000000000:{nt_hash.get_secret_value()}"
creds = Credentials("test_user", nt_hash, SecretType.NT_HASH)

View File

@ -13,13 +13,12 @@ PRIVATE_KEY = "priv_key"
@pytest.fixture
def credentials_for_test():
return Credentials(Username(USERNAME), Password(PASSWORD))
return Credentials(identity=Username(username=USERNAME), secret=Password(password=PASSWORD))
def test_credential_telem_send(spy_send_telemetry, credentials_for_test):
expected_data = [Credentials.to_mapping(credentials_for_test)]
expected_data = [credentials_for_test.dict(simplify=True)]
telem = CredentialsTelem([credentials_for_test])
telem.send()

View File

@ -6,14 +6,14 @@ import pytest
from pymongo import MongoClient
from pymongo.collection import Collection
from pymongo.database import Database
from tests.data_for_tests.propagation_credentials import PROPAGATION_CREDENTIALS
from tests.data_for_tests.propagation_credentials import CREDENTIALS
from common.credentials import Credentials
from monkey_island.cc.repository import MongoCredentialsRepository
from monkey_island.cc.server_utils.encryption import ILockableEncryptor
CONFIGURED_CREDENTIALS = PROPAGATION_CREDENTIALS[0:3]
STOLEN_CREDENTIALS = PROPAGATION_CREDENTIALS[3:]
CONFIGURED_CREDENTIALS = CREDENTIALS[0:3]
STOLEN_CREDENTIALS = CREDENTIALS[3:]
def reverse(data: bytes) -> bytes:
@ -59,9 +59,9 @@ def test_mongo_repository_get_all(mongo_repository):
def test_mongo_repository_configured(mongo_repository):
mongo_repository.save_configured_credentials(PROPAGATION_CREDENTIALS)
mongo_repository.save_configured_credentials(CREDENTIALS)
actual_configured_credentials = mongo_repository.get_configured_credentials()
assert actual_configured_credentials == PROPAGATION_CREDENTIALS
assert actual_configured_credentials == CREDENTIALS
mongo_repository.remove_configured_credentials()
actual_configured_credentials = mongo_repository.get_configured_credentials()
@ -82,7 +82,7 @@ def test_mongo_repository_all(mongo_repository):
mongo_repository.save_configured_credentials(CONFIGURED_CREDENTIALS)
mongo_repository.save_stolen_credentials(STOLEN_CREDENTIALS)
actual_credentials = mongo_repository.get_all_credentials()
assert actual_credentials == PROPAGATION_CREDENTIALS
assert actual_credentials == CREDENTIALS
mongo_repository.remove_all_credentials()
@ -91,7 +91,7 @@ def test_mongo_repository_all(mongo_repository):
assert mongo_repository.get_configured_credentials() == []
@pytest.mark.parametrize("credentials", PROPAGATION_CREDENTIALS)
@pytest.mark.parametrize("credentials", CREDENTIALS)
def test_configured_secrets_encrypted(
mongo_repository: MongoCredentialsRepository,
mongo_client: MongoClient,
@ -101,14 +101,14 @@ def test_configured_secrets_encrypted(
check_if_stored_credentials_encrypted(mongo_client, credentials)
@pytest.mark.parametrize("credentials", PROPAGATION_CREDENTIALS)
@pytest.mark.parametrize("credentials", CREDENTIALS)
def test_stolen_secrets_encrypted(mongo_repository, mongo_client, credentials: Credentials):
mongo_repository.save_stolen_credentials([credentials])
check_if_stored_credentials_encrypted(mongo_client, credentials)
def check_if_stored_credentials_encrypted(mongo_client: MongoClient, original_credentials):
original_credentials_mapping = Credentials.to_mapping(original_credentials)
original_credentials_mapping = original_credentials.dict(simplify=True)
raw_credentials = get_all_credentials_in_mongo(mongo_client)
for rc in raw_credentials:
@ -119,6 +119,10 @@ def check_if_stored_credentials_encrypted(mongo_client: MongoClient, original_cr
for key, value in credentials_component.items():
assert original_credentials_mapping[identity_or_secret][key] != value.decode()
# Since secrets use the pydantic.SecretType, make sure we're not just storing
# all '*' characters.
assert "***" not in value.decode()
def get_all_credentials_in_mongo(
mongo_client: MongoClient,

View File

@ -5,15 +5,10 @@ from urllib.parse import urljoin
import pytest
from tests.common import StubDIContainer
from tests.data_for_tests.propagation_credentials import (
LM_HASH_CREDENTIALS,
NT_HASH_CREDENTIALS,
PASSWORD_CREDENTIALS_1,
PASSWORD_CREDENTIALS_2,
)
from tests.data_for_tests.propagation_credentials import LM_HASH, NT_HASH, PASSWORD_1, PASSWORD_2
from tests.monkey_island import InMemoryCredentialsRepository
from common.credentials import Credentials
from common.credentials import Credentials, LMHash, NTHash, Password
from monkey_island.cc.repository import ICredentialsRepository
from monkey_island.cc.resources import PropagationCredentials
from monkey_island.cc.resources.propagation_credentials import (
@ -24,6 +19,10 @@ from monkey_island.cc.resources.propagation_credentials import (
ALL_CREDENTIALS_URL = PropagationCredentials.urls[0]
CONFIGURED_CREDENTIALS_URL = urljoin(ALL_CREDENTIALS_URL + "/", _configured_collection)
STOLEN_CREDENTIALS_URL = urljoin(ALL_CREDENTIALS_URL + "/", _stolen_collection)
CREDENTIALS_1 = Credentials(identity=None, secret=Password(password=PASSWORD_1))
CREDENTIALS_2 = Credentials(identity=None, secret=LMHash(lm_hash=LM_HASH))
CREDENTIALS_3 = Credentials(identity=None, secret=NTHash(nt_hash=NT_HASH))
CREDENTIALS_4 = Credentials(identity=None, secret=Password(password=PASSWORD_2))
@pytest.fixture
@ -42,20 +41,18 @@ def flask_client(build_flask_client, credentials_repository):
def test_propagation_credentials_endpoint_get(flask_client, credentials_repository):
credentials_repository.save_configured_credentials(
[PASSWORD_CREDENTIALS_1, NT_HASH_CREDENTIALS]
)
credentials_repository.save_stolen_credentials([LM_HASH_CREDENTIALS, PASSWORD_CREDENTIALS_2])
credentials_repository.save_configured_credentials([CREDENTIALS_1, CREDENTIALS_2])
credentials_repository.save_stolen_credentials([CREDENTIALS_3, CREDENTIALS_4])
resp = flask_client.get(ALL_CREDENTIALS_URL)
actual_propagation_credentials = [Credentials.from_mapping(creds) for creds in resp.json]
actual_propagation_credentials = [Credentials(**creds) for creds in resp.json]
assert resp.status_code == HTTPStatus.OK
assert len(actual_propagation_credentials) == 4
assert PASSWORD_CREDENTIALS_1 in actual_propagation_credentials
assert LM_HASH_CREDENTIALS in actual_propagation_credentials
assert NT_HASH_CREDENTIALS in actual_propagation_credentials
assert PASSWORD_CREDENTIALS_2 in actual_propagation_credentials
assert CREDENTIALS_1 in actual_propagation_credentials
assert CREDENTIALS_2 in actual_propagation_credentials
assert CREDENTIALS_3 in actual_propagation_credentials
assert CREDENTIALS_4 in actual_propagation_credentials
def pre_populate_repository(
@ -69,24 +66,22 @@ def pre_populate_repository(
@pytest.mark.parametrize("url", [CONFIGURED_CREDENTIALS_URL, STOLEN_CREDENTIALS_URL])
def test_propagation_credentials_endpoint__get_stolen(flask_client, credentials_repository, url):
pre_populate_repository(
url, credentials_repository, [PASSWORD_CREDENTIALS_1, LM_HASH_CREDENTIALS]
)
pre_populate_repository(url, credentials_repository, [CREDENTIALS_1, CREDENTIALS_2])
resp = flask_client.get(url)
actual_propagation_credentials = [Credentials.from_mapping(creds) for creds in resp.json]
actual_propagation_credentials = [Credentials(**creds) for creds in resp.json]
assert resp.status_code == HTTPStatus.OK
assert len(actual_propagation_credentials) == 2
assert actual_propagation_credentials[0] == PASSWORD_CREDENTIALS_1
assert actual_propagation_credentials[1] == LM_HASH_CREDENTIALS
assert actual_propagation_credentials[0].secret.password == PASSWORD_1
assert actual_propagation_credentials[1].secret.lm_hash == LM_HASH
def test_configured_propagation_credentials_endpoint_put(flask_client, credentials_repository):
pre_populate_repository(
CONFIGURED_CREDENTIALS_URL,
credentials_repository,
[PASSWORD_CREDENTIALS_1, LM_HASH_CREDENTIALS],
[CREDENTIALS_1, CREDENTIALS_2],
)
resp = flask_client.put(CONFIGURED_CREDENTIALS_URL, json=[])
assert resp.status_code == HTTPStatus.NO_CONTENT

View File

@ -1,64 +1,29 @@
from common.credentials import (
CredentialComponentType,
Credentials,
LMHash,
NTHash,
Password,
SSHKeypair,
Username,
)
from tests.data_for_tests.propagation_credentials import FULL_CREDENTIALS, USERNAME
from monkey_island.cc.services.reporting import format_creds_for_reporting
monkey_hostname = "fake_hostname"
fake_monkey_guid = "abc"
fake_username = Username("m0nk3y_user")
fake_nt_hash = NTHash("AEBD4DE384C7EC43AAD3B435B51404EE")
fake_lm_hash = LMHash("7A21990FCD3D759941E45C490F143D5F")
fake_password = Password("trytostealthis")
fake_ssh_public_key = "RSA_public_key"
fake_ssh_private_key = "RSA_private_key"
fake_ssh_key = SSHKeypair(fake_ssh_private_key, fake_ssh_public_key)
identities = (fake_username,)
secrets = (fake_nt_hash, fake_lm_hash, fake_password, fake_ssh_key)
fake_credentials = [
Credentials(fake_username, fake_nt_hash),
Credentials(fake_username, fake_lm_hash),
Credentials(fake_username, fake_password),
Credentials(fake_username, fake_ssh_key),
Credentials(None, fake_ssh_key),
Credentials(fake_username, None),
]
def test_formatting_credentials_for_report():
credentials = format_creds_for_reporting(fake_credentials)
credentials = format_creds_for_reporting(FULL_CREDENTIALS)
result1 = {
"_type": CredentialComponentType.NT_HASH.name,
"type": "NTLM hash",
"username": fake_username.username,
"username": USERNAME,
}
result2 = {
"_type": CredentialComponentType.LM_HASH.name,
"type": "LM hash",
"username": fake_username.username,
"username": USERNAME,
}
result3 = {
"_type": CredentialComponentType.PASSWORD.name,
"type": "Clear Password",
"username": fake_username.username,
"username": USERNAME,
}
result4 = {
"_type": CredentialComponentType.SSH_KEYPAIR.name,
"type": "Clear SSH private key",
"username": fake_username.username,
"username": USERNAME,
}
result5 = {
"_type": CredentialComponentType.SSH_KEYPAIR.name,
"type": "Clear SSH private key",
"username": "",
}

View File

@ -7,8 +7,7 @@ from common.agent_configuration.agent_sub_configurations import (
CustomPBAConfiguration,
ScanTargetConfiguration,
)
from common.credentials import Credentials
from common.utils import IJSONSerializable
from common.credentials import Credentials, LMHash, NTHash
from infection_monkey.exploit.log4shell_utils.ldap_server import LDAPServerFactory
from monkey_island.cc.event_queue import IslandEventTopic, PyPubSubIslandEventQueue
from monkey_island.cc.models import Report
@ -166,6 +165,9 @@ LDAPServerFactory.buildProtocol
get_file_sha256_hash
strict_slashes # unused attribute (monkey/monkey_island/cc/app.py:96)
post_breach_actions # unused variable (monkey\infection_monkey\config.py:95)
LMHash.validate_hash_format
NTHash.validate_hash_format
Credentials.Config.json_encoders
# Deployments
DEVELOP # unused variable (monkey/monkey/monkey_island/cc/deployment.py:5)
@ -314,8 +316,9 @@ EXPLOITED
CC
CC_TUNNEL
Credentials.from_json
IJSONSerializable.from_json
# TODO Remove with #2217
IJSONSerializable
from_json
IslandEventTopic.AGENT_CONNECTED
IslandEventTopic.CLEAR_SIMULATION_DATA