Skip to content

Commit 364e2ea

Browse files
sebdraventomchop
andauthored
Context in entities (#1089)
Co-authored-by: Thomas Chopitea <[email protected]>
1 parent 2bcff06 commit 364e2ea

File tree

4 files changed

+147
-6
lines changed

4 files changed

+147
-6
lines changed

core/schemas/entity.py

+22
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Entity(YetiTagModel, database_arango.ArangoYetiConnector):
3434
type: str
3535
name: str = Field(min_length=1)
3636
description: str = ""
37+
context: list[dict] = []
3738
created: datetime.datetime = Field(default_factory=now)
3839
modified: datetime.datetime = Field(default_factory=now)
3940

@@ -56,6 +57,27 @@ def load(cls, object: dict) -> "EntityTypes":
5657
def is_valid(cls, object: dict) -> bool:
5758
return validate_entity(object)
5859

60+
def add_context(
61+
self, source: str, context: dict, skip_compare: set = set()
62+
) -> "Entity": # noqa: F821
63+
"""Adds context to an entity."""
64+
compare_fields = set(context.keys()) - skip_compare - {"source"}
65+
for idx, db_context in enumerate(list(self.context)):
66+
if db_context["source"] != source:
67+
continue
68+
for field in compare_fields:
69+
if db_context.get(field) != context.get(field):
70+
context["source"] = source
71+
self.context[idx] = context
72+
break
73+
else:
74+
db_context.update(context)
75+
break
76+
else:
77+
context["source"] = source
78+
self.context.append(context)
79+
return self.save()
80+
5981

6082
class Note(Entity):
6183
type: Literal[EntityType.note] = EntityType.note

plugins/feeds/public/malpedia.py

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import logging
2+
from datetime import timedelta
3+
from typing import ClassVar
4+
5+
from core import taskmanager
6+
from core.schemas import entity, task
7+
8+
9+
class MalpediaMalware(task.FeedTask):
10+
_defaults = {
11+
"frequency": timedelta(days=1),
12+
"name": "Malpedia Malware",
13+
"description": "Gets list of Malpedia malware",
14+
"source": "https://malpedia.caad.fkie.fraunhofer.de/",
15+
}
16+
17+
_SOURCE: ClassVar["str"] = (
18+
"https://malpedia.caad.fkie.fraunhofer.de/api/get/families"
19+
)
20+
21+
def run(self):
22+
response = self._make_request(self._SOURCE)
23+
if not response:
24+
return
25+
families_json = response.json()
26+
for malware_name, entry in families_json.items():
27+
self.analyze_entry(malware_name, entry)
28+
29+
def analyze_entry(self, malware_name: str, entry: dict):
30+
"""Analyzes an entry as specified in the malpedia json."""
31+
32+
if not entry.get("common_name"):
33+
return
34+
35+
m = entity.Malware.find(name=entry["common_name"])
36+
if not m:
37+
m = entity.Malware(name=entry["common_name"])
38+
39+
m.aliases = entry.get("aliases", [])
40+
refs = entry.get("urls", [])
41+
context = {
42+
"source": "Malpedia",
43+
"description": entry.get("description", ""),
44+
"external_references": "\n* " + "\n* ".join(refs),
45+
}
46+
m.family = entry.get("type", "")
47+
m = m.save()
48+
m.add_context(context["source"], context)
49+
attributions = entry.get("attribution", [])
50+
for attribution in attributions:
51+
intrusion_set = entity.IntrusionSet.find(name=attribution)
52+
if not intrusion_set:
53+
intrusion_set = entity.IntrusionSet(name=attribution).save()
54+
intrusion_set.link_to(m, "uses", "Malpedia")
55+
56+
tags = []
57+
if m.aliases:
58+
tags += m.aliases
59+
tags.append(m.name)
60+
tags.append(malware_name)
61+
m.tag(tags)
62+
63+
64+
class MalpediaActors(task.FeedTask):
65+
_defaults = {
66+
"frequency": timedelta(days=1),
67+
"name": "Malpedia Actors",
68+
"description": "Gets list of Malpedia actors",
69+
"source": "https://malpedia.caad.fkie.fraunhofer.de/",
70+
}
71+
72+
_SOURCE: ClassVar["str"] = "https://malpedia.caad.fkie.fraunhofer.de/api/get/actors"
73+
74+
def run(self):
75+
response = self._make_request(self._SOURCE)
76+
if not response:
77+
return
78+
actors_json = response.json()
79+
for actor_name, entry in actors_json.items():
80+
self.analyze_entry(actor_name, entry)
81+
82+
def analyze_entry(self, actor_name: str, entry: dict):
83+
intrusion_set = entity.IntrusionSet.find(name=entry["value"])
84+
if not intrusion_set:
85+
intrusion_set = entity.IntrusionSet(name=entry["value"])
86+
87+
refs = entry.get("meta", {}).get("refs", [])
88+
context = {
89+
"source": "Malpedia",
90+
"description": entry.get("description", ""),
91+
"external_references": "\n* " + "\n* ".join(refs),
92+
}
93+
94+
synonyms = entry.get("meta", {}).get("synonyms", [])
95+
96+
if synonyms:
97+
intrusion_set.aliases = synonyms
98+
99+
intrusion_set = intrusion_set.save()
100+
intrusion_set.add_context(context["source"], context)
101+
tags = []
102+
103+
if intrusion_set.aliases:
104+
tags += intrusion_set.aliases
105+
tags.append(intrusion_set.name)
106+
tags.append(actor_name)
107+
try:
108+
intrusion_set.tag(tags)
109+
except Exception as e:
110+
logging.error(f"Error tagging IntrusionSet {intrusion_set.name}: {e}")
111+
112+
113+
taskmanager.TaskManager.register_task(MalpediaActors)
114+
taskmanager.TaskManager.register_task(MalpediaMalware)

tests/feeds.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@
99
feodo_tracker_ip_blocklist,
1010
hybrid_analysis,
1111
lolbas,
12+
malpedia,
1213
openphish,
1314
sslblacklist_ja3,
1415
timesketch,
1516
tor_exit_nodes,
16-
tweetlive,
1717
)
1818

1919

@@ -73,12 +73,17 @@ def test_tor_exit_nodes(self):
7373
feed = tor_exit_nodes.TorExitNodes(**defaults)
7474
feed.run()
7575

76-
def test_tweetlive(self):
77-
defaults = tweetlive.TweetLive._defaults.copy()
78-
feed = tweetlive.TweetLive(**defaults)
79-
feed.run()
80-
8176
def test_sslblacklist_ja3(self):
8277
defaults = sslblacklist_ja3.SSLBlacklistJA3._defaults.copy()
8378
feed = sslblacklist_ja3.SSLBlacklistJA3(**defaults)
8479
feed.run()
80+
81+
def test_malpedia_malware(self):
82+
defaults = malpedia.MalpediaMalware._defaults.copy()
83+
feed = malpedia.MalpediaMalware(**defaults)
84+
feed.run()
85+
86+
def test_malpedia_actor(self):
87+
defaults = malpedia.MalpediaActors._defaults.copy()
88+
feed = malpedia.MalpediaActors(**defaults)
89+
feed.run()

0 commit comments

Comments
 (0)