Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial mako support #38

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pycryptodomex
malduck
pymisp
mwdblib>=3.3.0
mwdblib>=3.3.0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
maco
maco-model

Might be better to use the maco-model package since it only contains the Pydantic models which is the only part in use in this feature.

Module structure is the same as the full package so you won't have to update the imports 😁

maco
9 changes: 6 additions & 3 deletions src/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@


def parse(family: str, config: Dict[str, Any]) -> IocCollection:
iocs = IocCollection()
"""Parse a mwdb static config of the given family, and get a IocCollection

:param family: Family this config belongs to
:param config: MWDB configuration dict"""
iocs = IocCollection(family)
if family in modules.modules:
iocs = modules.modules[family](config)
modules.modules[family](config, iocs)

modules.parse(config, iocs)

return iocs
80 changes: 78 additions & 2 deletions src/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from urllib.parse import urlparse

from Cryptodome.PublicKey import RSA # type: ignore
from maco import model # type: ignore
from malduck import base64, rsa # type: ignore
from pymisp import MISPAttribute, MISPObject # type: ignore

Expand Down Expand Up @@ -201,8 +202,9 @@ def prettyprint(self) -> str:
class IocCollection:
"""Represents a collection of parsed IoCs"""

def __init__(self) -> None:
def __init__(self, family: str) -> None:
"""Creates an empty IocCollection instance"""
self.family = family
self.rsa_keys: List[RsaKey] = []
self.ecdsa_curves: List[EcdsaCurve] = []
self.keys: List[Tuple[str, str]] = [] # (keytype, hexencoded key)
Expand Down Expand Up @@ -331,9 +333,83 @@ def to_misp(self) -> List[MISPObject]:

# filter out objects without any attributes
to_return = list(filter(lambda x: bool(x.attributes), to_return))

return to_return

def to_maco(self) -> model.ExtractorModel:
output = model.ExtractorModel(family=self.family)

for rsakey in self.rsa_keys:
obj = model.ExtractorModel.Encryption(
algorithm="rsa",
public_key=str((rsakey.n, rsakey.e)),
)
if rsakey.d:
obj.key = str((rsakey.n, rsakey.d))
output.encryption.append(obj)

for curve in self.ecdsa_curves:
output.encryption.append(
model.ExtractorModel.Encryption(
algorithm=curve.t, # for example, "ecdsa_pub_p384"
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a convention for naming this types (we don't have an enum ourselves, though we map ecdsa_pub_p384 to something that MISP wants)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So far I don't believe we have a naming convention for the algorithm field

public_key=str((curve.x, curve.y)),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and here

)
)

for key in self.keys:
output.encryption.append(
model.ExtractorModel.Encryption(
algorithm=key[0],
key=key[1],
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hexencoded

)
)

for password in self.passwords:
output.password.append(password)

def location_type_to_maco(location_type: LocationType) -> str:
if location_type in [LocationType.CNC, LocationType.PANEL]:
# Panel is not 100% technically correct here
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panel should be close enough to C2 to do this mapping

return "c2"
elif location_type == LocationType.DOWNLOAD_URL:
return "download"
elif location_type in [LocationType.OTHER, LocationType.PEER]:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No way to add a peer I think (it's not useful nowadays anyway)

return "other"
else:
raise ValueError(f"Unknown location type: {location_type}")

for netloc in self.network_locations:
if netloc.scheme in ["https", "http"]:
output.http.append(
model.ExtractorModel.Http(
protocol=netloc.scheme,
uri=netloc.url.geturl(),
usage=location_type_to_maco(netloc.location_type),
)
)
else:
output.tcp.append(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't keep track of connection type for non-http connections, sadly. But I can't think of a situation where this was not TCP

model.ExtractorModel.Connection(
server_ip=netloc.url.hostname,
server_port=netloc.port,
)
)

for mutex in self.mutexes:
output.mutex.append(mutex)

for filename in self.dropped_filenames:
output.paths.append(filename)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think i'm not adhering to the spirit of this field - we only store filenames, not complete paths.


# Not supported by Maco
# for email in self.emails_to + self.emails_from:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, for credentials stealing

# output.emails.append(email)
Comment on lines +403 to +405
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


# Not supported by Maco
# for message in self.ransom_messages:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

used by ransomware

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this could be something added to decoded_strings field or perhaps we should introduce a body field to the SMTP model to maintain the context of the string as being a ransomware-related to an email?

# output.messages.append(message)

return output.model_dump(exclude_defaults=True)

def prettyprint(self) -> str:
"""Pretty print for debugging"""
result = []
Expand Down
Loading
Loading