Skip to content

Commit

Permalink
Add detectors to endpoint outputs
Browse files Browse the repository at this point in the history
  • Loading branch information
omar2535 committed Nov 11, 2024
1 parent b705945 commit f4106ef
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 23 deletions.
40 changes: 20 additions & 20 deletions graphqler/fuzzer/engine/dengine.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
from .detectors import injection_detectors
from .detectors import api_detectors
from .detectors import misc_detectors
from .detectors.detector import Detector
import graphqler.config as config
from graphqler.graph.node import Node
from graphqler.utils.api import API
from graphqler.utils.logging_utils import Logger
from graphqler.utils.objects_bucket import ObjectsBucket

import graphqler.config as config
from .detectors import api_detectors, injection_detectors, misc_detectors
from .detectors.detector import Detector


class DEngine:
Expand All @@ -29,7 +28,8 @@ def run_detections_on_api(self):
- Uses API as the key for nodes_ran marking
"""
for api_detector in api_detectors:
detector = api_detector(api=self.api, name=self.api.url, objects_bucket=ObjectsBucket(self.api), graphql_type="")
node = Node(graphql_type='misc', name=self.api.url, body={}) # Create a dummy node object for the API
detector = api_detector(api=self.api, node=node, objects_bucket=ObjectsBucket(self.api), graphql_type="")
if not self.__should_run_detection(detector, self.api.url):
continue
try:
Expand All @@ -39,55 +39,55 @@ def run_detections_on_api(self):
except Exception as e:
self.logger.error(f"Detector {detector.DETECTION_NAME} failed with error: {e}")

def run_detections_on_graphql_object(self, name: str, objects_bucket: ObjectsBucket, graphql_type: str):
def run_detections_on_graphql_object(self, node: Node, objects_bucket: ObjectsBucket, graphql_type: str):
"""Runs all detectors on a specific GraphQL object (either QUERY or MUTATION)
Args:
name (str): Name of the query or mutation
node (Node): The node object
objects_bucket (ObjectsBucket): The objects bucket
graphql_type (str): The GraphQL type
"""
if not config.SKIP_INJECTION_ATTACKS:
self.__run_injection_detections(name, objects_bucket, graphql_type)
self.__run_injection_detections(node, objects_bucket, graphql_type)

if not config.SKIP_MISC_ATTACKS:
self.__run_misc_detections(name, objects_bucket, graphql_type)
self.__run_misc_detections(node, objects_bucket, graphql_type)

def __run_misc_detections(self, name: str, objects_bucket: ObjectsBucket, graphql_type: str):
def __run_misc_detections(self, node: Node, objects_bucket: ObjectsBucket, graphql_type: str):
"""Runs miscellaneous detections
Args:
name (str): The name of the node
node (Node): The node object
objects_bucket (ObjectsBucket): The objects bucket
graphql_type (str): The type of the GraphQL operation
"""
for misc_detector in misc_detectors:
detector = misc_detector(api=self.api, name=name, objects_bucket=objects_bucket, graphql_type=graphql_type)
if not self.__should_run_detection(detector, name):
detector = misc_detector(api=self.api, node=node, objects_bucket=objects_bucket, graphql_type=graphql_type)
if not self.__should_run_detection(detector, node.name):
continue
try:
is_vulnerable, potentially_vulnerable = detector.detect()
self.logger.info(f"Detector {detector.DETECTION_NAME} finished detecting - is_vulnerable: {is_vulnerable} - potentially_vulnerable: {potentially_vulnerable}")
self.__add_ran_node(name, detector.DETECTION_NAME)
self.__add_ran_node(node.name, detector.DETECTION_NAME)
except Exception as e:
self.logger.error(f"Detector {detector.DETECTION_NAME} failed with error: {e}")

def __run_injection_detections(self, name: str, objects_bucket: ObjectsBucket, graphql_type: str):
def __run_injection_detections(self, node: Node, objects_bucket: ObjectsBucket, graphql_type: str):
"""Runs injection detections
Args:
name (str): The name of the node
node (Node): The node object
objects_bucket (ObjectsBucket): The objects bucket
graphql_type (str): The type of the GraphQL operation
"""
for injection_detector in injection_detectors:
detector = injection_detector(api=self.api, name=name, objects_bucket=objects_bucket, graphql_type=graphql_type)
if not self.__should_run_detection(detector, name):
detector = injection_detector(api=self.api, node=node, objects_bucket=objects_bucket, graphql_type=graphql_type)
if not self.__should_run_detection(detector, node.name):
continue
try:
is_vulnerable, potentially_vulnerable = detector.detect()
self.logger.info(f"Detector {detector.DETECTION_NAME} finished detecting - is_vulnerable: {is_vulnerable} - potentially_vulnerable: {potentially_vulnerable}")
self.__add_ran_node(name, detector.DETECTION_NAME)
self.__add_ran_node(node.name, detector.DETECTION_NAME)
except Exception as e:
self.logger.error(f"Detector {detector.DETECTION_NAME} failed with error: {e}")

Expand Down
16 changes: 14 additions & 2 deletions graphqler/fuzzer/engine/detectors/detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@

import requests

from graphqler.graph.node import Node
from graphqler.utils.api import API
from graphqler.utils.logging_utils import Logger
from graphqler.utils.objects_bucket import ObjectsBucket
from graphqler.utils.stats import Stats
from graphqler.utils import plugins_handler
from graphqler.fuzzer.engine.types import ResultEnum, Result

from ..materializers.materializer import Materializer

Expand Down Expand Up @@ -43,9 +45,10 @@ def materializer(self) -> Type[Materializer]:
"""Materializer class to be used for payload generation"""
pass

def __init__(self, api: API, name: str, objects_bucket: ObjectsBucket, graphql_type: str):
def __init__(self, api: API, node: Node, objects_bucket: ObjectsBucket, graphql_type: str):
self.api = api
self.name = name
self.node = node
self.name = node.name
self.objects_bucket = objects_bucket
self.graphql_type = graphql_type
self.detector_logger = Logger().get_detector_logger()
Expand All @@ -64,8 +67,17 @@ def detect(self) -> tuple[bool, bool]:
self.fuzzer_logger.debug(f"[Fuzzer] Payload:\n{self.payload}")
self.detector_logger.info(f"[Detector] Payload:\n{self.payload}")

# Send the GraphQL request
graphql_response, request_response = plugins_handler.get_request_utils().send_graphql_request(self.api.url, self.payload)
result = Result(
result_enum=ResultEnum.GENERAL_SUCCESS,
payload_string=self.payload,
status_code=request_response.status_code,
graphql_response=graphql_response,
raw_response_text=request_response.text
)
Stats().add_http_status_code(self.name, request_response.status_code)
Stats().update_stats_from_result(self.node, result)

self.detector_logger.info(f"[{request_response.status_code}]Response: {request_response.text}")
self.fuzzer_logger.info(f"[{request_response.status_code}]Response: {graphql_response}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import requests

from graphqler.fuzzer.engine.types import Result, ResultEnum
from graphqler.utils.api import API
from graphqler.fuzzer.engine.materializers.getter import Getter
from graphqler.fuzzer.engine.detectors.detector import Detector
Expand Down Expand Up @@ -122,8 +123,22 @@ def detect(self) -> tuple[bool, bool]:
else:
self.potentially_vulnerable = False
self.confirmed_vulnerable = False

non_aliased_result = Result(ResultEnum.GENERAL_SUCCESS,
payload_string=non_aliased_payload,
status_code=non_aliased_request_response.status_code,
graphql_response=non_aliased_graphql_response,
raw_response_text=non_aliased_request_response.text)
aliased_result = Result(ResultEnum.GENERAL_SUCCESS,
payload_string=aliased_payload,
status_code=aliased_request_response.status_code,
graphql_response=aliased_graphql_response,
raw_response_text=aliased_request_response.text)

Stats().add_http_status_code(self.name, non_aliased_request_response.status_code)
Stats().add_http_status_code(self.name, aliased_request_response.status_code)
Stats().update_stats_from_result(self.node, non_aliased_result)
Stats().update_stats_from_result(self.node, aliased_result)
Stats().add_vulnerability(self.DETECTION_NAME, self.name, self.confirmed_vulnerable, self.potentially_vulnerable)
return (self.confirmed_vulnerable, self.potentially_vulnerable)

Expand Down
2 changes: 1 addition & 1 deletion graphqler/fuzzer/fuzzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def __fuzz(self, node: Node, visit_path: list[Node]):

def __detect_vulnerabilities_on_node(self, node: Node):
if node.graphql_type in ["Query", "Mutation"]:
self.dengine.run_detections_on_graphql_object(node.name, self.objects_bucket, node.graphql_type)
self.dengine.run_detections_on_graphql_object(node, self.objects_bucket, node.graphql_type)

def _get_new_visit_path_with_neighbors(self, neighboring_nodes: list[Node], visit_path: list[Node]) -> list[list[Node]]:
"""Gets the new visit path with the neighbors by creating a new path for each neighboring node
Expand Down
2 changes: 2 additions & 0 deletions graphqler/utils/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ def save_endpoint_results(self):
unique_results = {}
# Filter out for only unique results
for node_name, results in self.results.items():
# If the node name has slashes, replace them with underscores
node_name = node_name.replace("/", "_")
for result in results:
result_type = "success" if result.success else "failure"
result_file_path = Path(self.endpoint_results_dir) / node_name / result_type / f"{result.status_code}"
Expand Down

0 comments on commit f4106ef

Please # to comment.