Skip to content

Commit

Permalink
Merge pull request #47 from cisagov/feature/add-snmp-data
Browse files Browse the repository at this point in the history
feature: add snmp data
  • Loading branch information
Dbones202 authored Sep 7, 2023
2 parents bc7013c + 6ee4e4b commit 13831bb
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 46 deletions.
15 changes: 7 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.PHONY: run
.PHONY: generate launch

include .env
export

Expand All @@ -10,17 +11,15 @@ help:
install-develop:
pip install -e .

# target: install - Install application
# target: install - Install production application
install:
pip install navv

# target: generate - Generate analysis from pcap
# target: generate - Generate analysis excel sheet
# optionally set PCAP_PATH to a relative pcap file path
# example: make generate PCAP_PATH=test-path/to/file.pcap
generate:
navv generate -o analysis -p test-data/test_data.pcap -z test-data/logs test-customer

# target: load-metadata - Load metadata
load-metadata:
navv generate -o analysis -z test-data/logs test-customer
navv generate -o analysis -z test-data/logs test-customer $(if $(PCAP_PATH), -p $(PCAP_PATH))

# target: launch - Launch GUI application
launch:
Expand Down
2 changes: 1 addition & 1 deletion src/navv/_version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""This file defines the version of this module."""
__version__ = "3.0.5"
__version__ = "3.1.0"
38 changes: 17 additions & 21 deletions src/navv/bll.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,13 @@
import os
import pandas as pd

from navv.zeek import perform_zeekcut
from navv.utilities import get_mac_vendor, timeit
from navv.validators import is_ipv4_address, is_ipv6_address


MAC_VENDORS_JSON_FILE = os.path.abspath(__file__ + "/../" + "data/mac-vendors.json")


def get_zeek_data(zeek_logs):
"""Return a list of Zeek conn.log data."""
return (
perform_zeekcut(
fields=[
"id.orig_h",
"id.resp_h",
"id.resp_p",
"proto",
"conn_state",
"orig_l2_addr",
"resp_l2_addr",
],
log_file=os.path.join(zeek_logs, "conn.log"),
)
.decode("utf-8")
.split("\n")[:-1]
)


def get_zeek_df(zeek_data: list, dns_data: dict):
"""Return a pandas dataframe of the conn.log data with its dns data."""
zeek_data = [row.split("\t") for row in zeek_data]
Expand Down Expand Up @@ -163,3 +142,20 @@ def get_inventory_report_df(zeek_df: pd.DataFrame):
)

return grouped_df


@timeit
def get_snmp_df(zeek_data: list):
"""Return a pandas dataframe of the snmp.log data."""
zeek_data = [row.split("\t") for row in zeek_data]
return pd.DataFrame(
zeek_data,
columns=[
"src_ip",
"src_port",
"dst_ip",
"dst_port",
"version",
"community",
],
)
25 changes: 17 additions & 8 deletions src/navv/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

# Third-Party Libraries
import click
from navv.bll import get_inventory_report_df, get_zeek_data, get_zeek_df
from navv.bll import get_inventory_report_df, get_snmp_df, get_zeek_df

# cisagov Libraries
from navv.gui import app
Expand All @@ -21,10 +21,17 @@
write_conn_states_sheet,
write_externals_sheet,
write_inventory_report_sheet,
write_snmp_sheet,
write_stats_sheet,
write_unknown_internals_sheet,
)
from navv.zeek import get_dns_data, run_zeek, perform_zeekcut
from navv.zeek import (
get_conn_data,
get_dns_data,
get_snmp_data,
run_zeek,
perform_zeekcut,
)
from navv.utilities import pushd


Expand Down Expand Up @@ -69,17 +76,17 @@ def generate(customer_name, output_dir, pcap, zeek_logs):
else:
timer_data["run_zeek"] = "NOT RAN"

# Get zeek data
zeek_data = get_zeek_data(zeek_logs)
# Get zeek data from conn.log, dns.log and snmp.log
zeek_data = get_conn_data(zeek_logs)
snmp_data = get_snmp_data(zeek_logs)
dns_filtered = get_dns_data(customer_name, output_dir, zeek_logs)

# Get dns data for resolution
json_path = os.path.join(output_dir, f"{customer_name}_dns_data.json")

# Get dns data from zeek logs
dns_filtered = get_dns_data(customer_name, output_dir, zeek_logs)

# Get zeek dataframe
# Get zeek dataframes
zeek_df = get_zeek_df(zeek_data, dns_filtered)
snmp_df = get_snmp_df(snmp_data)

# Get inventory report dataframe
inventory_df = get_inventory_report_df(zeek_df)
Expand Down Expand Up @@ -109,6 +116,8 @@ def generate(customer_name, output_dir, pcap, zeek_logs):

write_unknown_internals_sheet(unk_int_IPs, wb)

write_snmp_sheet(snmp_df, wb)

auto_adjust_width(wb["Analysis"])

times = (
Expand Down
35 changes: 34 additions & 1 deletion src/navv/spreadsheet_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,40 @@ def write_inventory_report_sheet(inventory_df, wb):
for cell in ir_sheet[f"{index}:{index}"]:
cell.fill = openpyxl.styles.PatternFill("solid", fgColor="AAAAAA")
auto_adjust_width(ir_sheet, 40)
# ir_sheet.column_dimensions.width = 39 * 1.2


def write_snmp_sheet(snmp_df, wb):
"""Write SNMP log data to excel sheet."""
sheet = make_sheet(wb, "SNMP", idx=4)
sheet.append(
["Src IPv4", "Src Port", "Dest IPv4", "Dest Port", "Version", "Community"]
)

for index, row in enumerate(snmp_df.to_dict(orient="records"), start=2):
# Source IPv4 column
sheet[f"A{index}"].value = row["src_ip"]

# Source Port column
sheet[f"B{index}"].value = row["src_port"]

# Destination IPv4 column
sheet[f"C{index}"].value = row["dst_ip"]

# Destination Port column
sheet[f"D{index}"].value = row["dst_port"]

# Version column
sheet[f"E{index}"].value = row["version"]

# Community column
sheet[f"F{index}"].value = row["community"]

# Add styling to every other row
if index % 2 == 0:
for cell in sheet[f"{index}:{index}"]:
cell.fill = openpyxl.styles.PatternFill("solid", fgColor="AAAAAA")

auto_adjust_width(sheet, 40)


def write_externals_sheet(IPs, wb):
Expand Down
55 changes: 48 additions & 7 deletions src/navv/zeek.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,24 @@


@timeit
def run_zeek(pcap_path, zeek_logs_path, **kwargs):
with pushd(zeek_logs_path):
# can we add Site::local_nets to the zeek call here?
try:
check_call(["zeek", "-C", "-r", pcap_path, "local.zeek"])
except Exception as e:
error_msg(e)
def get_conn_data(zeek_logs):
"""Return a list of Zeek conn.log data."""
return (
perform_zeekcut(
fields=[
"id.orig_h",
"id.resp_h",
"id.resp_p",
"proto",
"conn_state",
"orig_l2_addr",
"resp_l2_addr",
],
log_file=os.path.join(zeek_logs, "conn.log"),
)
.decode("utf-8")
.split("\n")[:-1]
)


@timeit
Expand All @@ -31,6 +42,26 @@ def get_dns_data(customer_name, output_dir, zeek_logs):
return trim_dns_data(dns_data)


@timeit
def get_snmp_data(zeek_logs):
"""Get SNMP data from zeek logs or from a json file if it exists"""
return (
perform_zeekcut(
fields=[
"id.orig_h",
"id.orig_p",
"id.resp_h",
"id.resp_p",
"version",
"community",
],
log_file=os.path.join(zeek_logs, "snmp.log"),
)
.decode("utf-8")
.split("\n")[:-1]
)


def perform_zeekcut(fields, log_file):
"""Perform the call to zeek-cut with the identified fields on the specified log file"""
try:
Expand All @@ -42,3 +73,13 @@ def perform_zeekcut(fields, log_file):
except OSError as e:
# probably "file does not exist"
return b""


@timeit
def run_zeek(pcap_path, zeek_logs_path, **kwargs):
with pushd(zeek_logs_path):
# can we add Site::local_nets to the zeek call here?
try:
check_call(["zeek", "-C", "-r", pcap_path, "local.zeek"])
except Exception as e:
error_msg(e)

0 comments on commit 13831bb

Please sign in to comment.