Merge pull request #1715 from guardicore/1695-credential-collectors
Agent: define credential collector, credentials interfaces
This commit is contained in:
commit
976c46cf86
|
@ -0,0 +1,5 @@
|
|||
from .credential_components.nt_hash import NTHash
|
||||
from .credential_components.lm_hash import LMHash
|
||||
from .credential_components.password import Password
|
||||
from .credential_components.username import Username
|
||||
from .mimikatz_collector import MimikatzCredentialCollector
|
|
@ -0,0 +1,9 @@
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from infection_monkey.i_puppet import CredentialType, ICredentialComponent
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LMHash(ICredentialComponent):
|
||||
credential_type: CredentialType = field(default=CredentialType.LM_HASH, init=False)
|
||||
lm_hash: str
|
|
@ -0,0 +1,9 @@
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from infection_monkey.i_puppet import CredentialType, ICredentialComponent
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NTHash(ICredentialComponent):
|
||||
credential_type: CredentialType = field(default=CredentialType.NT_HASH, init=False)
|
||||
nt_hash: str
|
|
@ -0,0 +1,9 @@
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from infection_monkey.i_puppet import CredentialType, ICredentialComponent
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Password(ICredentialComponent):
|
||||
credential_type: CredentialType = field(default=CredentialType.PASSWORD, init=False)
|
||||
password: str
|
|
@ -0,0 +1,9 @@
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from infection_monkey.i_puppet import CredentialType, ICredentialComponent
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Username(ICredentialComponent):
|
||||
credential_type: CredentialType = field(default=CredentialType.USERNAME, init=False)
|
||||
username: str
|
|
@ -0,0 +1 @@
|
|||
from .mimikatz_credential_collector import MimikatzCredentialCollector
|
|
@ -0,0 +1,39 @@
|
|||
from typing import Iterable
|
||||
|
||||
from infection_monkey.credential_collectors import LMHash, NTHash, Password, Username
|
||||
from infection_monkey.i_puppet.credential_collection import Credentials, ICredentialCollector
|
||||
|
||||
from . import pypykatz_handler
|
||||
from .windows_credentials import WindowsCredentials
|
||||
|
||||
|
||||
class MimikatzCredentialCollector(ICredentialCollector):
|
||||
def collect_credentials(self, options=None) -> Iterable[Credentials]:
|
||||
creds = pypykatz_handler.get_windows_creds()
|
||||
return MimikatzCredentialCollector._to_credentials(creds)
|
||||
|
||||
@staticmethod
|
||||
def _to_credentials(win_creds: Iterable[WindowsCredentials]) -> [Credentials]:
|
||||
all_creds = []
|
||||
for win_cred in win_creds:
|
||||
identities = []
|
||||
secrets = []
|
||||
if win_cred.username:
|
||||
identity = Username(win_cred.username)
|
||||
identities.append(identity)
|
||||
|
||||
if win_cred.password:
|
||||
password = Password(win_cred.password)
|
||||
secrets.append(password)
|
||||
|
||||
if win_cred.lm_hash:
|
||||
lm_hash = LMHash(lm_hash=win_cred.lm_hash)
|
||||
secrets.append(lm_hash)
|
||||
|
||||
if win_cred.ntlm_hash:
|
||||
lm_hash = NTHash(nt_hash=win_cred.ntlm_hash)
|
||||
secrets.append(lm_hash)
|
||||
|
||||
if identities != [] or secrets != []:
|
||||
all_creds.append(Credentials(identities, secrets))
|
||||
return all_creds
|
|
@ -3,9 +3,7 @@ from typing import Any, Dict, List, NewType
|
|||
|
||||
from pypykatz.pypykatz import pypykatz
|
||||
|
||||
from infection_monkey.system_info.windows_cred_collector.windows_credentials import (
|
||||
WindowsCredentials,
|
||||
)
|
||||
from .windows_credentials import WindowsCredentials
|
||||
|
||||
CREDENTIAL_TYPES = [
|
||||
"msv_creds",
|
|
@ -10,3 +10,9 @@ from .i_puppet import (
|
|||
UnknownPluginError,
|
||||
)
|
||||
from .i_fingerprinter import IFingerprinter
|
||||
from .credential_collection import (
|
||||
Credentials,
|
||||
CredentialType,
|
||||
ICredentialCollector,
|
||||
ICredentialComponent,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
from .i_credential_collector import ICredentialCollector
|
||||
from .credentials import Credentials
|
||||
from .i_credential_component import ICredentialComponent
|
||||
from .credential_type import CredentialType
|
|
@ -0,0 +1,8 @@
|
|||
from enum import Enum
|
||||
|
||||
|
||||
class CredentialType(Enum):
|
||||
USERNAME = 1
|
||||
PASSWORD = 2
|
||||
NT_HASH = 3
|
||||
LM_HASH = 4
|
|
@ -0,0 +1,10 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Tuple
|
||||
|
||||
from .i_credential_component import ICredentialComponent
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Credentials:
|
||||
identities: Tuple[ICredentialComponent]
|
||||
secrets: Tuple[ICredentialComponent]
|
|
@ -0,0 +1,10 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import Iterable, Mapping, Optional
|
||||
|
||||
from .credentials import Credentials
|
||||
|
||||
|
||||
class ICredentialCollector(ABC):
|
||||
@abstractmethod
|
||||
def collect_credentials(self, options: Optional[Mapping]) -> Iterable[Credentials]:
|
||||
pass
|
|
@ -0,0 +1,10 @@
|
|||
from abc import ABC, abstractmethod
|
||||
|
||||
from .credential_type import CredentialType
|
||||
|
||||
|
||||
class ICredentialComponent(ABC):
|
||||
@property
|
||||
@abstractmethod
|
||||
def credential_type(self) -> CredentialType:
|
||||
pass
|
|
@ -1,25 +0,0 @@
|
|||
from typing import List
|
||||
|
||||
from infection_monkey.system_info.windows_cred_collector import pypykatz_handler
|
||||
from infection_monkey.system_info.windows_cred_collector.windows_credentials import (
|
||||
WindowsCredentials,
|
||||
)
|
||||
|
||||
|
||||
class MimikatzCredentialCollector(object):
|
||||
@staticmethod
|
||||
def get_creds():
|
||||
creds = pypykatz_handler.get_windows_creds()
|
||||
return MimikatzCredentialCollector.cred_list_to_cred_dict(creds)
|
||||
|
||||
@staticmethod
|
||||
def cred_list_to_cred_dict(creds: List[WindowsCredentials]):
|
||||
cred_dict = {}
|
||||
for cred in creds:
|
||||
# TODO: This should be handled by the island, not the agent. There is already similar
|
||||
# code in monkey_island/cc/models/report/report_dal.py.
|
||||
# Lets not use "." and "$" in keys, because it will confuse mongo.
|
||||
# Ideally we should refactor island not to use a dict and simply parse credential list.
|
||||
key = cred.username.replace(".", ",").replace("$", "")
|
||||
cred_dict.update({key: cred.to_dict()})
|
||||
return cred_dict
|
|
@ -2,7 +2,7 @@ import logging
|
|||
import sys
|
||||
|
||||
from common.common_consts.system_info_collectors_names import MIMIKATZ_COLLECTOR
|
||||
from infection_monkey.system_info.windows_cred_collector.mimikatz_cred_collector import (
|
||||
from infection_monkey.credential_collectors.windows_cred_collector.mimikatz_cred_collector import (
|
||||
MimikatzCredentialCollector,
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
from typing import List
|
||||
|
||||
import pytest
|
||||
|
||||
from infection_monkey.credential_collectors import (
|
||||
LMHash,
|
||||
MimikatzCredentialCollector,
|
||||
NTHash,
|
||||
Password,
|
||||
Username,
|
||||
)
|
||||
from infection_monkey.credential_collectors.mimikatz_collector.windows_credentials import (
|
||||
WindowsCredentials,
|
||||
)
|
||||
from infection_monkey.i_puppet import Credentials
|
||||
|
||||
|
||||
def patch_pypykatz(win_creds: [WindowsCredentials], monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"infection_monkey.credential_collectors"
|
||||
".mimikatz_collector.pypykatz_handler.get_windows_creds",
|
||||
lambda: win_creds,
|
||||
)
|
||||
|
||||
|
||||
def collect_credentials() -> List[Credentials]:
|
||||
return list(MimikatzCredentialCollector().collect_credentials())
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"win_creds", [([WindowsCredentials(username="", password="", ntlm_hash="", lm_hash="")]), ([])]
|
||||
)
|
||||
def test_empty_results(monkeypatch, win_creds):
|
||||
patch_pypykatz(win_creds, monkeypatch)
|
||||
collected_credentials = collect_credentials()
|
||||
assert not collected_credentials
|
||||
|
||||
|
||||
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])
|
||||
|
||||
collected_credentials = collect_credentials()
|
||||
assert len(collected_credentials) == 1
|
||||
assert collected_credentials[0] == expected_credentials
|
||||
|
||||
|
||||
def test_pypykatz_result_parsing_duplicates(monkeypatch):
|
||||
win_creds = [
|
||||
WindowsCredentials(username="user", password="secret", ntlm_hash="", lm_hash=""),
|
||||
WindowsCredentials(username="user", password="secret", ntlm_hash="", lm_hash=""),
|
||||
]
|
||||
patch_pypykatz(win_creds, monkeypatch)
|
||||
|
||||
collected_credentials = collect_credentials()
|
||||
assert len(collected_credentials) == 2
|
||||
|
||||
|
||||
def test_pypykatz_result_parsing_defaults(monkeypatch):
|
||||
win_creds = [
|
||||
WindowsCredentials(username="user2", password="secret2", lm_hash="lm_hash"),
|
||||
]
|
||||
patch_pypykatz(win_creds, monkeypatch)
|
||||
|
||||
# Expected credentials
|
||||
username = Username("user2")
|
||||
password = Password("secret2")
|
||||
lm_hash = LMHash("lm_hash")
|
||||
expected_credentials = Credentials([username], [password, lm_hash])
|
||||
|
||||
collected_credentials = collect_credentials()
|
||||
assert len(collected_credentials) == 1
|
||||
assert collected_credentials[0] == expected_credentials
|
||||
|
||||
|
||||
def test_pypykatz_result_parsing_no_identities(monkeypatch):
|
||||
win_creds = [
|
||||
WindowsCredentials(username="", password="", ntlm_hash="ntlm_hash", lm_hash="lm_hash"),
|
||||
]
|
||||
patch_pypykatz(win_creds, monkeypatch)
|
||||
|
||||
lm_hash = LMHash("lm_hash")
|
||||
nt_hash = NTHash("ntlm_hash")
|
||||
expected_credentials = Credentials([], [lm_hash, nt_hash])
|
||||
|
||||
collected_credentials = collect_credentials()
|
||||
assert len(collected_credentials) == 1
|
||||
assert collected_credentials[0] == expected_credentials
|
|
@ -1,6 +1,6 @@
|
|||
from unittest import TestCase
|
||||
|
||||
from infection_monkey.system_info.windows_cred_collector.pypykatz_handler import (
|
||||
from infection_monkey.credential_collectors.mimikatz_collector.pypykatz_handler import (
|
||||
_get_creds_from_pypykatz_session,
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue