Skip to content

Commit 0b8a568

Browse files
authored
Merge pull request #1450 from stratosphereips/alya/immune/keep_track_of_all_hosts_ips
Keep track of all ipv6 of the host
2 parents 14c3596 + bd60248 commit 0b8a568

9 files changed

Lines changed: 78 additions & 67 deletions

File tree

install/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ black==24.10.0
3737
ruff==0.11.7
3838
pre-commit==4.0.1
3939
coverage==7.8.0
40+
netifaces==0.11.0
4041
pyyaml
4142
pytest-asyncio
4243
git+https://github.com/SECEF/python-idmefv2.git

modules/flowalerts/conn.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def init(self):
4040
self.dns_analyzer = DNS(self.db, flowalerts=self)
4141
self.is_running_non_stop: bool = self.db.is_running_non_stop()
4242
self.classifier = FlowClassifier()
43-
self.our_ips = utils.get_own_ips()
43+
self.our_ips: List[str] = utils.get_own_ips(ret=List)
4444
self.input_type: str = self.db.get_input_type()
4545
self.multiple_reconnection_attempts_threshold = 5
4646
# we use this to try to detect if there's dns server that has a

modules/flowalerts/dns.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def init(self):
4040
self.arpa_scan_threshold = 10
4141
self.is_running_non_stop: bool = self.db.is_running_non_stop()
4242
self.classifier = FlowClassifier()
43-
self.our_ips = utils.get_own_ips()
43+
self.our_ips: List[str] = utils.get_own_ips(ret=List)
4444
# In mins
4545
self.dns_without_conn_interface_wait_time = 30
4646
# to store dns queries that we should check later. the purpose of

modules/ip_info/ip_info.py

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
# SPDX-FileCopyrightText: 2021 Sebastian Garcia <sebastian.garcia@agents.fel.cvut.cz>
22
# SPDX-License-Identifier: GPL-2.0-only
3-
import platform
43
from asyncio import Task
54
from typing import (
65
Union,
@@ -16,7 +15,7 @@
1615
import json
1716
from contextlib import redirect_stdout, redirect_stderr
1817
import subprocess
19-
import re
18+
import netifaces
2019
import time
2120
import asyncio
2221
import multiprocessing
@@ -338,26 +337,12 @@ def get_gateway_ip_if_interface(self):
338337
# only works if running on an interface
339338
return False
340339

341-
gw_ip = False
342-
if platform.system() == "Darwin":
343-
route_default_result = subprocess.check_output(
344-
["route", "get", "default"]
345-
).decode()
346-
try:
347-
gw_ip = re.search(
348-
r"\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}",
349-
route_default_result,
350-
).group(0)
351-
except AttributeError:
352-
pass
353-
354-
elif platform.system() == "Linux":
355-
route_default_result = re.findall(
356-
r"([\w.][\w.]*'?\w?)",
357-
subprocess.check_output(["ip", "route"]).decode(),
358-
)
359-
gw_ip = route_default_result[2]
360-
return gw_ip
340+
gws = netifaces.gateways()
341+
try:
342+
return gws["default"][netifaces.AF_INET][0]
343+
except (KeyError, IndexError):
344+
# no default gateway found
345+
return False
361346

362347
def get_gateway_mac(self, gw_ip: str) -> Optional[str]:
363348
"""

slips_files/common/slips_utils.py

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from re import findall
88
from threading import Thread
99

10+
import netifaces
1011
from uuid import UUID
1112
import tldextract
1213
import validators
@@ -19,12 +20,7 @@
1920
import sys
2021
import ipaddress
2122
import aid_hash
22-
from typing import (
23-
Any,
24-
Optional,
25-
Union,
26-
List,
27-
)
23+
from typing import Any, Optional, Union, List, Dict
2824
from ipaddress import IPv4Network, IPv6Network, IPv4Address, IPv6Address
2925
from dataclasses import is_dataclass, asdict
3026
from enum import Enum
@@ -405,26 +401,41 @@ def to_delta(self, time_in_seconds):
405401
def get_human_readable_datetime(self) -> str:
406402
return utils.convert_format(datetime.now(), self.alerts_format)
407403

408-
def get_own_ips(self) -> list:
404+
def get_own_ips(self, ret=Dict) -> Union[Dict[str, List[str]], List[str]]:
409405
"""
410-
Returns a list of our local and public IPs
406+
Returns a dict of our private and public IPs
407+
return a dict by default
408+
e.g. { "ipv4": [..], "ipv6": [..] }
409+
and returns a list of all the ips combined if ret=List is given
411410
"""
412-
if "-i" not in sys.argv:
411+
if "-i" not in sys.argv and "-g" not in sys.argv:
413412
# this method is only valid when running on an interface
414413
return []
415414

416-
IPs = []
417-
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
418-
try:
419-
s.connect(("10.255.255.255", 1))
420-
IPs.append(s.getsockname()[0])
421-
except Exception:
422-
IPs.append("127.0.0.1")
423-
finally:
424-
s.close()
415+
ips = {"ipv4": [], "ipv6": []}
425416

426-
# get public ip
417+
interfaces = netifaces.interfaces()
418+
419+
for interface in interfaces:
420+
try:
421+
addrs = netifaces.ifaddresses(interface)
427422

423+
# get IPv4 addresses
424+
if netifaces.AF_INET in addrs:
425+
for addr in addrs[netifaces.AF_INET]:
426+
ips["ipv4"].append(addr["addr"])
427+
428+
# get IPv6 addresses
429+
if netifaces.AF_INET6 in addrs:
430+
for addr in addrs[netifaces.AF_INET6]:
431+
# remove interface suffix
432+
ip = addr["addr"].split("%")[0]
433+
ips["ipv6"].append(ip)
434+
435+
except Exception as e:
436+
print(f"Error processing interface {interface}: {e}")
437+
438+
# get public ip
428439
try:
429440
response = requests.get(
430441
"http://ipinfo.io/json",
@@ -435,19 +446,27 @@ def get_own_ips(self) -> list:
435446
requests.exceptions.ChunkedEncodingError,
436447
requests.exceptions.ReadTimeout,
437448
):
438-
return IPs
449+
return ips
439450

440451
if response.status_code != 200:
441-
return IPs
452+
return ips
442453
if "Connection timed out" in response.text:
443-
return IPs
454+
return ips
444455
try:
445456
response = json.loads(response.text)
446457
except json.decoder.JSONDecodeError:
447-
return IPs
458+
return ips
459+
448460
public_ip = response["ip"]
449-
IPs.append(public_ip)
450-
return IPs
461+
if validators.ipv4(public_ip):
462+
ips["ipv4"].append(public_ip)
463+
elif validators.ipv6(public_ip):
464+
ips["ipv6"].append(public_ip)
465+
466+
if ret == Dict:
467+
return ips
468+
elif ret == List:
469+
return [ip for sublist in ips.values() for ip in sublist]
451470

452471
def convert_to_mb(self, bytes):
453472
return int(bytes) / (10**6)

slips_files/core/database/redis_db/database.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class RedisDB(IoCHandler, AlertHandler, ProfileHandler, P2PHandler):
115115
# flag to know if we found the gateway MAC using the most seen MAC method
116116
_gateway_MAC_found = False
117117
_conf_file = "config/redis.conf"
118-
our_ips = utils.get_own_ips()
118+
our_ips: List[str] = utils.get_own_ips(ret=List)
119119
# flag to know which flow is the start of the pcap/file
120120
first_flow = True
121121
# to make sure we only detect and store the user's localnet once

slips_files/core/evidence_handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def init(self):
9696
self.jsonfile = self.clean_file(self.output_dir, "alerts.json")
9797
utils.change_logfiles_ownership(self.jsonfile.name, self.UID, self.GID)
9898
# this list will have our local and public ips when using -i
99-
self.our_ips = utils.get_own_ips()
99+
self.our_ips: List[str] = utils.get_own_ips(ret=List)
100100
self.formatter = EvidenceFormatter(self.db)
101101
# thats just a tmp value, this variable will be set and used when
102102
# the

tests/test_ip_info.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
import asyncio
66

7+
import netifaces
8+
79
from tests.module_factory import ModuleFactory
810
import maxminddb
911
import pytest
@@ -405,27 +407,28 @@ async def test_shutdown_gracefully(
405407

406408

407409
@pytest.mark.parametrize(
408-
"platform_system, subprocess_output, expected_ip",
410+
"is_running_non_stop, netifaces_ret, expected_return_value",
409411
[
410-
# Testcase 1: MacOS (Darwin) with valid output
411-
("Darwin", b"gateway: 192.168.1.1", "192.168.1.1"),
412-
# Testcase 2: Linux with valid output
413-
("Linux", b"default via 10.0.0.1 dev eth0", "10.0.0.1"),
414-
# Testcase 3: MacOS with invalid output
415-
("Darwin", b"No default gateway", False),
416-
# Testcase 4: Unsupported OS
417-
("Windows", b"", False),
412+
(
413+
True,
414+
{"default": {netifaces.AF_INET: ["192.168.1.1"]}},
415+
"192.168.1.1",
416+
),
417+
(True, {}, False),
418+
(True, {"default": {}}, False),
419+
(True, {"default": {netifaces.AF_INET: []}}, False),
420+
(False, "192.168.1.1", False),
418421
],
419422
)
420423
def test_get_gateway_ip(
421-
mocker, platform_system, subprocess_output, expected_ip
424+
mocker, is_running_non_stop, netifaces_ret, expected_return_value
422425
):
423426
ip_info = ModuleFactory().create_ip_info_obj()
424-
mocker.patch("platform.system", return_value=platform_system)
425-
mocker.patch("subprocess.check_output", return_value=subprocess_output)
426-
mocker.patch("sys.argv", ["-i", "eth0"])
427+
ip_info.is_running_non_stop = is_running_non_stop
428+
mocker.patch("netifaces.gateways", return_value=netifaces_ret)
429+
427430
result = ip_info.get_gateway_ip_if_interface()
428-
assert result == expected_ip
431+
assert result == expected_return_value
429432

430433

431434
@pytest.mark.parametrize(

tests/test_slips_utils.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import pytz
1111
import json
1212
from collections import namedtuple
13+
from typing import List
1314

1415

1516
def test_get_sha256_hash():
@@ -289,13 +290,15 @@ def _check_ip_presence(utils, expected_ip):
289290
Helper function to check if the given IP is present
290291
in the list of own IPs.
291292
"""
292-
return expected_ip in utils.get_own_ips() or not utils.get_own_ips()
293+
return (
294+
expected_ip in utils.get_own_ips(ret=List) or not utils.get_own_ips()
295+
)
293296

294297

295298
def test_get_own_ips_success():
296299
"""Test that the function returns a list when successful."""
297300
utils = ModuleFactory().create_utils_obj()
298-
ips = utils.get_own_ips()
301+
ips = utils.get_own_ips(ret=List)
299302
assert isinstance(ips, list), "Should return a list of IPs"
300303

301304

0 commit comments

Comments
 (0)