Skip to content

Commit

Permalink
Code actually runs now, tests still need to be written
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Woellner committed Jun 20, 2024
1 parent 7d6b2e1 commit 455d1d5
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 33 deletions.
19 changes: 11 additions & 8 deletions wtfis/clients/r7insight.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import Optional

from requests.exceptions import HTTPError
from requests.auth import HTTPBasicAuth

from wtfis.clients.base import BaseIpEnricherClient, BaseRequestsClient, BaseDomainEnricherClient
from wtfis.wtfis.models.r7insight import Rapid7Insight, Rapid7InsightMap
from wtfis.models.r7insight import Rapid7Insight, Rapid7InsightMap


class Rapid7InsightClient(BaseRequestsClient, BaseDomainEnricherClient, BaseIpEnricherClient):
Expand All @@ -17,6 +18,8 @@ def __init__(self, user_id: str, api_key: str) -> None:
super().__init__()
self.user_id = user_id
self.api_key = api_key
self.s.auth = HTTPBasicAuth(self.user_id, self.api_key)


@property
def name(self) -> str:
Expand All @@ -25,7 +28,7 @@ def name(self) -> str:
def _get_host(self, host: str) -> Rapid7Insight:
try:
return Rapid7Insight.model_validate(
self._get(f"/v3/iocs/ioc-by-value?iocValue={ip}", headers={"key": self.api_key})
self._get(f"v3/iocs/ioc-by-value?iocValue={host}")
)
except HTTPError as e:
if e.response.status_code == 404:
Expand All @@ -34,15 +37,15 @@ def _get_host(self, host: str) -> Rapid7Insight:

def _enrich(self, *entities: str) -> Rapid7InsightMap:
"""Method is the same whether input is a domain or IP"""
urlhaus_map = {}
rapid7insight_map = {}
for entity in entities:
data = self._get_host(entity)
if data.host:
urlhaus_map[data.host] = data
return Rapid7InsightMap.model_validate(urlhaus_map)
if data.value:
rapid7insight_map[data.value] = data
return Rapid7InsightMap.model_validate(rapid7insight_map)

def enrich_ips(self, *ips: str) -> Rapid7InsightMap:
return self._enrich(*ips)


def enrich_domains(self, *ips: str) -> Rapid7InsightMap:
return self._enrich(*ips)
11 changes: 11 additions & 0 deletions wtfis/handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from wtfis.clients.types import IpGeoAsnClientType, IpWhoisClientType
from wtfis.clients.urlhaus import UrlHausClient
from wtfis.clients.virustotal import VTClient
from wtfis.clients.r7insight import Rapid7InsightClient
from wtfis.models.abuseipdb import AbuseIpDbMap
from wtfis.models.base import WhoisBase
from wtfis.models.greynoise import GreynoiseIpMap
Expand All @@ -27,6 +28,7 @@
from wtfis.models.types import IpGeoAsnMapType
from wtfis.models.urlhaus import UrlHausMap
from wtfis.models.virustotal import Domain, IpAddress
from wtfis.models.r7insight import Rapid7InsightMap
from wtfis.ui.theme import Theme
from wtfis.utils import error_and_exit, refang

Expand Down Expand Up @@ -79,6 +81,7 @@ def __init__(
greynoise_client: Optional[GreynoiseClient],
abuseipdb_client: Optional[AbuseIpDbClient],
urlhaus_client: Optional[UrlHausClient],
rapid7insight_client: Optional[Rapid7InsightClient],
):
# Process-specific
self.entity = refang(entity)
Expand All @@ -93,6 +96,7 @@ def __init__(
self._greynoise = greynoise_client
self._abuseipdb = abuseipdb_client
self._urlhaus = urlhaus_client
self._rapid7insight = rapid7insight_client

# Dataset containers
self.vt_info: Union[Domain, IpAddress]
Expand All @@ -102,6 +106,7 @@ def __init__(
self.greynoise: GreynoiseIpMap = GreynoiseIpMap.empty()
self.abuseipdb: AbuseIpDbMap = AbuseIpDbMap.empty()
self.urlhaus: UrlHausMap = UrlHausMap.empty()
self.rapid7insight: Rapid7InsightMap = Rapid7InsightMap.empty()

# Warning messages container
self.warnings: List[str] = []
Expand Down Expand Up @@ -134,6 +139,12 @@ def _fetch_abuseipdb(self, *ips: str) -> None:
if self._abuseipdb:
self.abuseipdb = self._abuseipdb.enrich_ips(*ips)

@common_exception_handler
@failopen_exception_handler("_rapid7insight")
def _fetch_rapid7insight(self, *ips: str) -> None:
if self._rapid7insight:
self.rapid7insight = self._rapid7insight.enrich_ips(*ips)

@common_exception_handler
def _fetch_whois(self) -> None:
# Let continue if rate limited
Expand Down
11 changes: 11 additions & 0 deletions wtfis/handlers/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from wtfis.clients.types import IpGeoAsnClientType, IpWhoisClientType
from wtfis.clients.urlhaus import UrlHausClient
from wtfis.clients.virustotal import VTClient
from wtfis.clients.r7insight import Rapid7InsightClient
from wtfis.handlers.base import (
BaseHandler,
common_exception_handler,
Expand All @@ -35,6 +36,7 @@ def __init__(
greynoise_client: Optional[GreynoiseClient],
abuseipdb_client: Optional[AbuseIpDbClient],
urlhaus_client: Optional[UrlHausClient],
rapid7insight_client: Optional[Rapid7InsightClient],
max_resolutions: int = 0,
):
super().__init__(
Expand All @@ -48,6 +50,7 @@ def __init__(
greynoise_client,
abuseipdb_client,
urlhaus_client,
rapid7insight_client,
)

# Extended attributes
Expand Down Expand Up @@ -116,6 +119,14 @@ def fetch_data(self):
self._fetch_abuseipdb(*self.resolutions.ip_list(self.max_resolutions))
self.progress.update(task_g, completed=100)

if self._rapid7insight:
task_r = self.progress.add_task(
f"Fetching IP data from {self._rapid7insight.name}"
)
self.progress.update(task_r, advance=50)
self._fetch_rapid7insight(*self.resolutions.ip_list(self.max_resolutions))
self.progress.update(task_r, completed=100)

if self._urlhaus:
task_u = self.progress.add_task(
f"Fetching domain data from {self._urlhaus.name}"
Expand Down
9 changes: 9 additions & 0 deletions wtfis/handlers/ip.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,22 @@ def fetch_data(self):
self.progress.update(task_g, completed=100)

if self._abuseipdb:
print(self._abuseipdb)
task_a = self.progress.add_task(
f"Fetching IP data from {self._abuseipdb.name}"
)
self.progress.update(task_a, advance=50)
self._fetch_abuseipdb(self.entity)
self.progress.update(task_a, completed=100)

if self._rapid7insight:
task_r = self.progress.add_task(
f"Fetching IP data from {self._rapid7insight.name}"
)
self.progress.update(task_r, advance=50)
self._fetch_rapid7insight(self.entity)
self.progress.update(task_r, completed=100)

task_w = self.progress.add_task(f"Fetching IP whois from {self._whois.name}")
self.progress.update(task_w, advance=50)
self._fetch_whois()
Expand Down
12 changes: 7 additions & 5 deletions wtfis/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def parse_args() -> Namespace:
elif option in ("-u", "--use-urlhaus"):
parsed.use_urlhaus = not parsed.use_urlhaus
elif option in ("-r", "--use-rapid7"):
parsed.use_urlhaus = not parsed.use_rapid7
parsed.use_rapid7 = not parsed.use_rapid7
elif option in ("-n", "--no-color"):
parsed.no_color = not parsed.no_color
elif option in ("-1", "--one-column"):
Expand Down Expand Up @@ -177,8 +177,8 @@ def generate_entity_handler(
urlhaus_client = UrlHausClient() if args.use_urlhaus else None

# Rapid7 Insights client (optional)
abuseipdb_client = (
Rapid7InsightClient(os.environ["RaPID7_ACCOUNT_ID"], os.environ["RAPID7_API_KEY"]) if args.use_rapid7 else None
rapid7insight_client = (
Rapid7InsightClient(os.environ["RAPID7_ACCOUNT_ID"], os.environ["RAPID7_API_KEY"]) if args.use_rapid7 else None
)

# Domain / FQDN handler
Expand All @@ -195,6 +195,7 @@ def generate_entity_handler(
abuseipdb_client=abuseipdb_client,
urlhaus_client=urlhaus_client,
max_resolutions=args.max_resolutions,
rapid7insight_client=rapid7insight_client,
)
# IP address handler
else:
Expand All @@ -209,6 +210,7 @@ def generate_entity_handler(
greynoise_client=greynoise_client,
abuseipdb_client=abuseipdb_client,
urlhaus_client=urlhaus_client,
rapid7insight_client=rapid7insight_client,
)

return entity
Expand All @@ -231,7 +233,7 @@ def generate_view(
entity.greynoise,
entity.abuseipdb,
entity.urlhaus,
entity.rapid7Insight,
entity.rapid7insight,
max_resolutions=args.max_resolutions,
)
elif isinstance(entity, IpAddressHandler) and isinstance(entity.vt_info, IpAddress):
Expand All @@ -244,7 +246,7 @@ def generate_view(
entity.greynoise,
entity.abuseipdb,
entity.urlhaus,
entity.rapid7Insight,
entity.rapid7insight,
)
else:
raise WtfisException("Unsupported entity!")
Expand Down
2 changes: 1 addition & 1 deletion wtfis/models/r7insight.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class Rapid7Insight(BaseModel):


class Rapid7InsightMap(RootModel):
root: Dict[str, Rapid7InsightIp]
root: Dict[str, Rapid7Insight]

@classmethod
def empty(cls) -> Rapid7InsightMap:
Expand Down
2 changes: 1 addition & 1 deletion wtfis/models/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from wtfis.models.ipwhois import IpWhois, IpWhoisMap
from wtfis.models.shodan import ShodanIpMap
from wtfis.models.urlhaus import UrlHausMap
from wtfis.wtfis.models.r7insight import Rapid7InsightMap
from wtfis.models.r7insight import Rapid7InsightMap

# IP enrichment map types
IpEnrichmentType = Union[
Expand Down
26 changes: 14 additions & 12 deletions wtfis/ui/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from wtfis.models.types import IpGeoAsnMapType, IpGeoAsnType
from wtfis.models.urlhaus import UrlHaus, UrlHausMap
from wtfis.models.virustotal import LastAnalysisStats, PopularityRanks
from wtfis.wtfis.models.r7insight import Rapid7Insight, Rapid7InsightMap
from wtfis.models.r7insight import Rapid7Insight, Rapid7InsightMap
from wtfis.ui.theme import Theme
from wtfis.utils import Timestamp, is_ip, smart_join

Expand All @@ -39,7 +39,7 @@ def __init__(
greynoise: GreynoiseIpMap,
abuseipdb: AbuseIpDbMap,
urlhaus: UrlHausMap,
rapid7Insight: Rapid7InsightMap,
rapid7insight: Rapid7InsightMap,
) -> None:
self.console = console
self.entity = entity
Expand All @@ -49,7 +49,7 @@ def __init__(
self.greynoise = greynoise
self.abuseipdb = abuseipdb
self.urlhaus = urlhaus
self.rapid7Insight = rapid7Insight
self.rapid7insight = rapid7insight
self.theme = Theme()

def _vendors_who_flagged_malicious(self) -> List[str]:
Expand Down Expand Up @@ -308,16 +308,13 @@ def _gen_rapid7_tuple(self, ip: Rapid7Insight) -> Tuple[Text, Text]:
#
# Title
#
title = self._gen_linked_field_name(
"Rapid7 Insight", hyperlink=f""
)
title = "Rapid7 Insight"

#
# Content
#
if ip.whitelisted:
style = self.theme.info
text = Text("Whitelisted", style=style)
text = Text("Whitelisted", style=self.theme.info)
return title, text

if ip.severity == "High":
Expand All @@ -335,17 +332,17 @@ def _gen_rapid7_tuple(self, ip: Rapid7Insight) -> Tuple[Text, Text]:
)

if len(ip.relatedMalware) > 0:
text.append(f"\n related malware", style=self.theme.table_value)
text.append(f"\nrelated malware:", style=self.theme.tags)
for malware in ip.relatedMalware:
text.append(f" {malware}", style=self.theme.table_value)

if len(ip.relatedCampaigns) > 0:
text.append(f"\n related campaigns", style=self.theme.table_value)
text.append(f"\nrelated campaigns:", style=self.theme.tags)
for campaign in ip.relatedCampaigns:
text.append(f" {campaign}", style=self.theme.table_value)

if len(ip.reportedFeeds) > 0:
text.append(f"\n reported feeds", style=self.theme.table_value)
text.append(f"\nreported feeds:", style=self.theme.tags)
for feed in ip.reportedFeeds:
text.append(f" {feed.name}", style=self.theme.table_value)

Expand Down Expand Up @@ -383,7 +380,7 @@ def _get_urlhaus_enrichment(self, entity: str) -> Optional[UrlHaus]:
return self.urlhaus.root[entity] if entity in self.urlhaus.root.keys() else None

def _get_rapid7insight_enrichment(self, entity: str) -> Optional[Rapid7Insight]:
return self.rapid7Insight.root[entity] if entity in self.rapid7Insight.root.keys() else None
return self.rapid7insight.root[entity] if entity in self.rapid7insight.root.keys() else None

def _gen_vt_section(self) -> RenderableType:
"""Virustotal section. Applies to both domain and IP views"""
Expand Down Expand Up @@ -586,6 +583,11 @@ def _gen_ip_other_section(self) -> Optional[RenderableType]:
if abuseipdb:
data.append(self._gen_abuseipdb_tuple(abuseipdb))

# Rapid7 Insight
rapid7 = self._get_rapid7insight_enrichment(entity=self.entity.data.id_)
if rapid7:
data.append(self._gen_rapid7_tuple(rapid7))

if data:
return self._gen_section(
self._gen_table(*data), self._gen_heading_text("Other")
Expand Down
12 changes: 6 additions & 6 deletions wtfis/ui/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from wtfis.models.types import IpGeoAsnMapType
from wtfis.models.urlhaus import UrlHausMap
from wtfis.models.virustotal import Domain, IpAddress, Resolutions
from wtfis.wtfis.models.r7insight import Rapid7InsightMap
from wtfis.models.r7insight import Rapid7InsightMap
from wtfis.ui.base import BaseView
from wtfis.utils import Timestamp, smart_join

Expand Down Expand Up @@ -129,9 +129,9 @@ def resolutions_panel(self) -> Optional[Panel]:
data += [self._gen_abuseipdb_tuple(abuseipdb)]

# Rapid7 Insight
rapid7Insight = self._get_rapid7Insight_enrichment(attributes.ip_address)
if rapid7Insight:
data += [self._gen_rapid7Insight_tuple(rapid7Insight)]
rapid7insight = self._get_rapid7insight_enrichment(attributes.ip_address)
if rapid7insight:
data += [self._gen_rapid7_tuple(rapid7insight)]

# Include a disclaimer if last seen is older than 1 year
# Note: Disabled for now because I originally understood that the
Expand Down Expand Up @@ -208,10 +208,10 @@ def __init__(
greynoise: GreynoiseIpMap,
abuseipdb: AbuseIpDbMap,
urlhaus: UrlHausMap,
rapid7Insight: Rapid7InsightMap,
rapid7insight: Rapid7InsightMap,
) -> None:
super().__init__(
console, entity, geoasn, whois, shodan, greynoise, abuseipdb, urlhaus, rapid7Insight
console, entity, geoasn, whois, shodan, greynoise, abuseipdb, urlhaus, rapid7insight
)

def ip_panel(self) -> Panel:
Expand Down

0 comments on commit 455d1d5

Please sign in to comment.