diff --git a/GranefAPI/main.py b/GranefAPI/main.py index 1042afbfc4cbb57da62c12f2d9f39ee3883aa034..2d38605eb9a05bed57908e60fceee5f0e1f3414f 100644 --- a/GranefAPI/main.py +++ b/GranefAPI/main.py @@ -46,7 +46,7 @@ from fastapi.middleware.cors import CORSMiddleware # Custom modules of Granef API from utilities.dgraph_client import DgraphClient -from routers import general_queries, overview_queries, graph_queries, analysis_queries +from routers import general_queries, overview_queries, graph_queries, analysis_queries, suricata_queries # Application definition ("description" key may be added too). @@ -60,6 +60,7 @@ app.include_router(general_queries.router, tags=["General"]) app.include_router(graph_queries.router, prefix="/graph", tags=["Graph queries"]) app.include_router(overview_queries.router, prefix="/overview", tags=["Overview queries"]) app.include_router(analysis_queries.router, prefix="/analysis", tags=["Analysis queries"]) +app.include_router(suricata_queries.router, prefix="/suricata", tags=["Suricata queries"]) @app.get("/", summary="Get API information", tags=["General"]) diff --git a/GranefAPI/models/query_models.py b/GranefAPI/models/query_models.py index 099b0eed69b11e5b8494601743efb512486a84eb..fde17eee0fbe6dfea18117adf21c862dc50e0dbc 100644 --- a/GranefAPI/models/query_models.py +++ b/GranefAPI/models/query_models.py @@ -88,4 +88,8 @@ class AdressProtocolTimestampsQuery(BaseModel): address: str = Field(None, example='192.168.1.16') protocol: str = Field(None, example='HTTP') timestamp_min: str = Field(None, example='2008-07-22T01:51:07.095278Z') - timestamp_max: str = Field(None, example='2008-07-22T01:55:00') \ No newline at end of file + timestamp_max: str = Field(None, example='2008-07-22T01:55:00') + +class AlertFilterQuery(BaseModel): + severity: int = Field(0, example='5') + regexp: str = Field(None, example='/JA3/') diff --git a/GranefAPI/routers/suricata_queries.py b/GranefAPI/routers/suricata_queries.py new file mode 100644 index 0000000000000000000000000000000000000000..7482b5e4f23d523334b5cf4d80a1f24c1a5f18d0 --- /dev/null +++ b/GranefAPI/routers/suricata_queries.py @@ -0,0 +1,104 @@ +#! /usr/bin/env python3 +# -*- coding: utf-8 -*- + +# +# Granef -- graph-based network forensics toolkit +# Copyright (C) 2020-2021 Milan Cermak, Institute of Computer Science of Masaryk University +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# + + +""" +Definition of queries providing an overview of the network data. +""" + +# Common Python modules +import json + +# FastAPI modules +from fastapi import APIRouter + +# GranefAPI +from models import query_models +from utilities import validation, preprocessing +from utilities.dgraph_client import DgraphClient + + +# Initialize FastAPI router +router = APIRouter() + +@router.post("/alert", + response_model=query_models.GeneralResponseList, + summary="Get alerts produced by suricata.") +def alert(request: query_models.AlertFilterQuery) -> dict: + """ + Get alerts produced by suricata. + """ + + dgraph_client = DgraphClient() + + regexp = "" if not request.regexp else f"and regexp(alert.alert.signature, {request.regexp})" + + query = f"""{{ + getAlert(func:type(Alert)) @filter (le(alert.alert.severity, {request.severity}) {regexp} ) {{ + dgraph.type + alert.alert.signature + alert.alert.severity + alert.alert.action + }} + }} + """ + + result = json.loads(dgraph_client.query(preprocessing.add_default_attributes(query))) + return {"response": result["getAlert"]} + + +@router.post("/alert-connection", + response_model=query_models.GeneralResponseList, + summary="Get alerts produced by suricata with related connections.") +def alert_connections(request: query_models.AlertFilterQuery) -> dict: + """ + Get alerts produced by suricata with related connections. + """ + + dgraph_client = DgraphClient() + + regexp = "" if not request.regexp else f"and regexp(alert.alert.signature, {request.regexp})" + + query = f"""{{ + getConnectionsWithAlerts(func:type(Connection)) @filter (has(connection.alert)) @cascade {{ + dgraph.type + ~host.responded {{ + uid + dgraph.type + host.ip + }} + ~host.originated {{ + uid + dgraph.type + host.ip + }} + connection.alert @filter(le(alert.alert.severity, {request.severity}) {regexp} ) {{ + dgraph.type + alert.alert.signature + alert.alert.severity + alert.alert.action + }} + }} + }} + """ + + result = json.loads(dgraph_client.query(preprocessing.add_default_attributes(query))) + return {"response": result["getConnectionsWithAlerts"]} diff --git a/requirements.txt b/requirements.txt index e5603b125ba763ead57f5081cfcaceaa7977df89..1f853a51966d9083fcb3ed804003ee9a1ed18a91 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,4 @@ gunicorn ipaddress typing-extensions coloredlogs +networkx \ No newline at end of file