Skip to content
This repository was archived by the owner on Nov 2, 2024. It is now read-only.

Commit ba65498

Browse files
drosettivaclavbartos
authored andcommitted
improved quad9 analyzers (intelowlproject#2453)
* improved quad9 analyzers * fix
1 parent 51500c5 commit ba65498

File tree

2 files changed

+52
-51
lines changed

2 files changed

+52
-51
lines changed

api_app/analyzers_manager/observable_analyzers/dns/dns_malicious_detectors/quad9_malicious_detector.py

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
"""Check if the domains is reported as malicious in Quad9 database"""
55

6-
import logging
7-
from typing import Tuple
86
from urllib.parse import urlparse
97

108
import requests
@@ -14,8 +12,6 @@
1412

1513
from ..dns_responses import malicious_detector_response
1614

17-
logger = logging.getLogger(__name__)
18-
1915

2016
class Quad9MaliciousDetector(classes.ObservableAnalyzer):
2117
"""Check if a domain is malicious by Quad9 public resolver.
@@ -27,9 +23,9 @@ class Quad9MaliciousDetector(classes.ObservableAnalyzer):
2723
we can guess that the domain was in the Quad9 blacklist.
2824
"""
2925

30-
HEADERS = {"Accept": "application/dns-json"}
31-
QUAD9_URL = "https://dns.quad9.net:5053/dns-query"
32-
GOOGLE_URL = "https://dns.google.com/resolve"
26+
headers: dict = {"Accept": "application/dns-json"}
27+
url: str = "https://dns.quad9.net:5053/dns-query"
28+
google_url: str = "https://dns.google.com/resolve"
3329

3430
def update(self) -> bool:
3531
pass
@@ -40,7 +36,7 @@ def run(self):
4036
if self.observable_classification == self.ObservableTypes.URL:
4137
observable = urlparse(self.observable_name).hostname
4238

43-
quad9_answer, timeout = self._quad9_dns_query(observable)
39+
quad9_answer = self._quad9_dns_query(observable)
4440
# if Quad9 has not an answer the site could be malicious
4541
if not quad9_answer:
4642
# Google dns request
@@ -50,38 +46,35 @@ def run(self):
5046
if google_answer:
5147
return malicious_detector_response(self.observable_name, True)
5248

53-
return malicious_detector_response(self.observable_name, False, timeout)
49+
return malicious_detector_response(self.observable_name, False)
5450

55-
def _quad9_dns_query(self, observable) -> Tuple[bool, bool]:
51+
def _quad9_dns_query(self, observable) -> bool:
5652
"""Perform a DNS query with Quad9 service, return True if Quad9 answer the
5753
DNS query with a non-empty response.
5854
5955
:param observable: domain to resolve
6056
:type observable: str
61-
:return: True in case of answer for the DNS query else False.
62-
:rtype: bool
6357
"""
64-
answer_found = False
65-
timeout = False
6658
params = {"name": observable}
6759

68-
quad9_response = requests.get(
69-
self.QUAD9_URL, headers=self.HEADERS, params=params
70-
)
71-
if quad9_response.status_code == 503:
72-
msg = (
73-
"503 status code! "
74-
"It may be normal for this service to"
75-
" happen from time to time"
76-
)
77-
logger.info(msg)
78-
self.report.errors.append(msg)
79-
timeout = True
80-
return answer_found, timeout
81-
quad9_response.raise_for_status()
82-
answer_found = bool(quad9_response.json().get("Answer", None))
83-
84-
return answer_found, timeout
60+
# sometimes it can respond with 503, I suppose to avoid DoS.
61+
# In 1k requests just 20 fails and at least with 30 requests between 2 failures
62+
# with 2 or 3 attemps the analyzer should get the data
63+
attempt_number = 3
64+
for attempt in range(0, attempt_number):
65+
try:
66+
quad9_response = requests.get(
67+
self.url, headers=self.headers, params=params, timeout=10
68+
)
69+
except requests.exceptions.ConnectionError as exception:
70+
# if the last attempt fails, raise an error
71+
if attempt == attempt_number - 1:
72+
raise exception
73+
else:
74+
quad9_response.raise_for_status()
75+
break
76+
77+
return bool(quad9_response.json().get("Answer", None))
8578

8679
def _google_dns_query(self, observable) -> bool:
8780
"""Perform a DNS query with Google service, return True if Google answer the
@@ -93,7 +86,7 @@ def _google_dns_query(self, observable) -> bool:
9386
:rtype: bool
9487
"""
9588
params = {"name": observable}
96-
google_response = requests.get(self.GOOGLE_URL, params=params)
89+
google_response = requests.get(self.google_url, params=params)
9790
google_response.raise_for_status()
9891

9992
return bool(google_response.json().get("Answer", None))

api_app/analyzers_manager/observable_analyzers/dns/dns_resolvers/quad9_dns_resolver.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22
# See the file 'LICENSE' for copying permission.
33

44
"""Quad9 DNS resolutions"""
5-
65
from urllib.parse import urlparse
76

87
import requests
98

109
from api_app.analyzers_manager import classes
11-
from api_app.analyzers_manager.exceptions import AnalyzerRunException
1210
from tests.mock_utils import MockUpResponse, if_mock_connections, patch
1311

1412
from ..dns_responses import dns_resolver_response
@@ -17,26 +15,36 @@
1715
class Quad9DNSResolver(classes.ObservableAnalyzer):
1816
"""Resolve a DNS query with Quad9"""
1917

18+
url: str = "https://dns.quad9.net:5053/dns-query"
19+
headers: dict = {"Accept": "application/dns-json"}
2020
query_type: str
2121

2222
def run(self):
23-
try:
24-
observable = self.observable_name
25-
# for URLs we are checking the relative domain
26-
if self.observable_classification == "url":
27-
observable = urlparse(self.observable_name).hostname
28-
29-
headers = {"Accept": "application/dns-json"}
30-
url = "https://dns.quad9.net:5053/dns-query"
31-
params = {"name": observable, "type": self.query_type}
32-
33-
quad9_response = requests.get(url, headers=headers, params=params)
34-
quad9_response.raise_for_status()
35-
resolutions = quad9_response.json().get("Answer", [])
36-
except requests.RequestException:
37-
raise AnalyzerRunException(
38-
"an error occurred during the connection to Quad9"
39-
)
23+
observable = self.observable_name
24+
# for URLs we are checking the relative domain
25+
if self.observable_classification == self.ObservableTypes.URL:
26+
observable = urlparse(self.observable_name).hostname
27+
28+
params = {"name": observable, "type": self.query_type}
29+
30+
# sometimes it can respond with 503, I suppose to avoid DoS.
31+
# In 1k requests just 20 fails and at least with 30 requests between 2 failures
32+
# with 2 or 3 attemps the analyzer should get the data
33+
attempt_number = 3
34+
for attempt in range(0, attempt_number):
35+
try:
36+
quad9_response = requests.get(
37+
self.url, headers=self.headers, params=params, timeout=10
38+
)
39+
except requests.exceptions.ConnectionError as exception:
40+
# if the last attempt fails, raise an error
41+
if attempt == attempt_number - 1:
42+
raise exception
43+
else:
44+
quad9_response.raise_for_status()
45+
break
46+
47+
resolutions = quad9_response.json().get("Answer", [])
4048

4149
return dns_resolver_response(self.observable_name, resolutions)
4250

0 commit comments

Comments
 (0)