Skip to content
Merged
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: 3 additions & 0 deletions docs/detection_modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,9 @@ For example, if the domain in the traffic is _here.testing.com_,
Slips first checks if the exact domain _here.testing.com_ is in any blacklist,
and if there is no match, it checks if the domain _testing.com_ is in any blacklists too.

Slips also sets evidence about DNS answers of blacklisted domains. If test.com is blacklisted, and test.com resolves to 1.2.3.4, slips sets
an evidence when it encouters the DNS answer with 1.2.3.4.

### Matching of JA3 Hashes

Every time Slips encounters an TLS flow,
Expand Down
3 changes: 2 additions & 1 deletion docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,8 @@ File hashes and URLs aren't supported.

Slips gets every IP it can find in the network and tries to see if it is in any blacklist.

If a match is found, it generates an evidence, if no exact match is found, it searches the Blacklisted ranges taken from different TI feeds
If a match is found, it generates an evidence, if no exact match is found,
it searches the Blacklisted ranges taken from different TI feeds


#### Matching of Domains
Expand Down
144 changes: 118 additions & 26 deletions modules/threat_intelligence/threat_intelligence.py
Original file line number Diff line number Diff line change
Expand Up @@ -1669,26 +1669,20 @@ def is_malicious_domain(
timestamp,
profileid,
twid,
):
set_evidence=True,
) -> Tuple[dict, bool] | bool:
"""Evaluates a domain to determine if it is recognized as
malicious based on
threat intelligence data stored offline. If the domain is
identified as
malicious, it records an evidence entry and marks the
identified as malicious, it records an evidence entry and marks the
domain in the database.
setting an evidence is optional here determined by the set_evidence
flag

Returns:
bool: False if the domain is ignored or not found in the
offline threat intelligence data, indicating no further action
is required. Otherwise, it does not explicitly return
a value but performs operations to record the
malicious domain evidence.

Side Effects:
- Generates and stores an evidence entry for the malicious
domain in the database.
- Marks the domain as malicious in the database, enhancing
the system's future recognition of this threat.
offline threat intelligence data, and the malicious domain TI
info if its malicious.
"""
if self.is_ignored_domain(domain):
return False
Expand All @@ -1697,19 +1691,21 @@ def is_malicious_domain(
if not domain_info:
return False

self.set_evidence_malicious_domain(
domain,
uid,
timestamp,
domain_info,
is_subdomain,
profileid,
twid,
)
if set_evidence:
self.set_evidence_malicious_domain(
domain,
uid,
timestamp,
domain_info,
is_subdomain,
profileid,
twid,
)

# mark this domain as malicious in our database
domain_info = {"threatintelligence": domain_info}
self.db.set_info_for_domains(domain, domain_info)
return domain_info, is_subdomain

def update_local_file(self, filename):
"""Checks for updates to a specified local threat intelligence
Expand Down Expand Up @@ -1797,6 +1793,93 @@ def should_lookup(self, ip: str, protocol: str, ip_state: str) -> bool:
return False
return True

def is_dns_answer_of_a_malicious_query(
self,
answer,
uid,
timestamp,
profileid,
twid,
dns_query,
):
"""
Detects the DNS answers of malicious DNS queries.
even if the IPs returned in the answer is not in one of slips' TI feeds.
:param answer: the ip coming in the dns answer
"""
if utils.is_ignored_ip(answer):
return False

malicious_query_feed_info = self.is_malicious_domain(
dns_query, uid, timestamp, profileid, twid, set_evidence=False
)

if not malicious_query_feed_info:
return False

# was the subdomain of the query malicious, or the domain?
domain_info, is_subdomain = malicious_query_feed_info

try:
domain_info = domain_info["threatintelligence"]
except KeyError:
return False

confidence: float = 0.7 if is_subdomain else 1

srcip = profileid.split("_")[-1]
threat_level: float = utils.threat_levels[
domain_info.get("threat_level", "high")
]
threat_level: ThreatLevel = ThreatLevel(threat_level)
description: str = (
f"DNS answer of a blacklisted query. Query: {dns_query}. "
f"Answer: {answer}, "
f"Description: {domain_info.get('description', '')}, "
f"Query found in feed: {domain_info['source']}, "
f"Confidence: {confidence}"
)

tags = domain_info.get("tags", None)
if tags:
description += f", Tags: {tags}. "

evidence = Evidence(
evidence_type=EvidenceType.THREAT_INTELLIGENCE_ANSWER_OF_BLACKLISTED_QUERY,
attacker=Attacker(
direction=Direction.DST, ioc_type=IoCType.IP, value=answer
),
victim=Victim(
ioc_type=IoCType.IP,
direction=Direction.SRC,
value=srcip,
),
threat_level=threat_level,
confidence=confidence,
description=description,
profile=ProfileID(ip=answer),
timewindow=TimeWindow(number=int(twid.replace("timewindow", ""))),
uid=[uid],
timestamp=utils.convert_ts_format(timestamp, utils.alerts_format),
)

self.db.set_evidence(evidence)

evidence = Evidence(
evidence_type=EvidenceType.THREAT_INTELLIGENCE_ANSWER_OF_BLACKLISTED_QUERY,
attacker=Attacker(
direction=Direction.SRC, ioc_type=IoCType.IP, value=srcip
),
threat_level=threat_level,
confidence=confidence,
description=description,
profile=ProfileID(ip=srcip),
timewindow=TimeWindow(number=int(twid.replace("timewindow", ""))),
uid=[uid],
timestamp=utils.convert_ts_format(timestamp, utils.alerts_format),
)
self.db.set_evidence(evidence)

def pre_main(self):
utils.drop_root_privs_permanently()
# Load the local Threat Intelligence files that are
Expand Down Expand Up @@ -1832,13 +1915,22 @@ def main(self):
to_lookup = data.get("to_lookup", "")
# detect the type given because sometimes,
# http.log host field has ips OR domains
type_ = utils.detect_ioc_type(to_lookup)
ioc_type = utils.detect_ioc_type(to_lookup)

# ip_state can be "srcip" or "dstip"
ip_state = data.get("ip_state")
if type_ == "ip":
if ioc_type == "ip":
ip = to_lookup
if self.should_lookup(ip, protocol, ip_state):
if is_dns_response:
self.is_dns_answer_of_a_malicious_query(
ip,
uid,
timestamp,
profileid,
twid,
dns_query,
)
self.is_malicious_ip(
ip,
uid,
Expand All @@ -1861,7 +1953,7 @@ def main(self):
twid,
is_dns_response=is_dns_response,
)
elif type_ == "domain":
elif ioc_type == "domain":
if is_dns_response:
self.is_malicious_cname(
dns_query, to_lookup, uid, timestamp, profileid, twid
Expand All @@ -1870,7 +1962,7 @@ def main(self):
self.is_malicious_domain(
to_lookup, uid, timestamp, profileid, twid
)
elif type_ == "url":
elif ioc_type == "url":
self.is_malicious_url(
to_lookup, uid, timestamp, daddr, profileid, twid
)
Expand Down
8 changes: 7 additions & 1 deletion slips_files/core/database/redis_db/ioc_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,13 @@ def is_blacklisted_ssl(self, sha1):

def _match_exact_domain(self, domain: str) -> Optional[Dict[str, str]]:
"""checks if the given domain is blacklisted.
checks only the exact given domain, no subdomains"""
checks only the exact given domain, no subdomains
returns something like
{"description": "x.com",
"source": "own_malicious_iocs.csv",
"threat_level": "medium",
"tags": "local TI file"}
"""
domain_description = self.rcache.hget(
self.constants.IOC_DOMAINS, domain
)
Expand Down
2 changes: 1 addition & 1 deletion slips_files/core/database/redis_db/profile_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ def add_out_dns(self, profileid, twid, flow):
)
# send each dns answer to TI module
for answer in flow.answers:
if "TXT" in answer:
if "TXT" in answer or answer == "":
continue

extra_info = {
Expand Down
1 change: 1 addition & 0 deletions slips_files/core/structures/evidence.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ class EvidenceType(Enum):
THREAT_INTELLIGENCE_TO_BLACKLISTED_IP = auto()
THREAT_INTELLIGENCE_BLACKLISTED_DNS_ANSWER = auto()
THREAT_INTELLIGENCE_BLACKLISTED_DOMAIN = auto()
THREAT_INTELLIGENCE_ANSWER_OF_BLACKLISTED_QUERY = auto()
MALICIOUS_DOWNLOADED_FILE = auto()
THREAT_INTELLIGENCE_MALICIOUS_URL = auto()

Expand Down
2 changes: 1 addition & 1 deletion zeek-scripts/__load__.zeek
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ redef digest_salt = "Please change this value.";
@load protocols/ssh/interesting-hostnames

# Detect SQL injection attacks.
@load protocols/http/detect-sqli
@load protocols/http/detect-sql-injection.zeek

#### Network File Handling ####

Expand Down
Loading