diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index e56abb6..ed6062b 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -11,7 +11,6 @@ on: jobs: build: - runs-on: ubuntu-latest strategy: fail-fast: false @@ -24,17 +23,58 @@ jobs: uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install flake8 pytest if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Install nmap + run: sudo apt-get install -y nmap + + lint: + runs-on: ubuntu-latest + needs: build + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install nmap + run: sudo apt-get install -y nmap - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + + test: + runs-on: ubuntu-latest + needs: build + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11"] + + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install nmap + run: sudo apt-get install -y nmap - name: Test with pytest run: | pytest diff --git a/network_scan.log b/network_scan.log new file mode 100644 index 0000000..a45a52d --- /dev/null +++ b/network_scan.log @@ -0,0 +1,12 @@ +2025-02-19 20:17:55,273 - INFO - Starting network scan of 192.168.0.0/24 +2025-02-19 20:17:55,273 - ERROR - Scan failed: [Errno 1] Operation not permitted +2025-02-19 20:19:44,969 - INFO - Starting network scan of 192.168.0.0/24 +2025-02-19 20:19:47,365 - ERROR - Scan failed for 192.168.0.130: 'nmap program was not found in path. PATH is : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' +2025-02-19 20:19:47,365 - ERROR - Scan failed for 192.168.0.96: 'nmap program was not found in path. PATH is : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' +2025-02-19 20:19:47,367 - ERROR - Scan failed for 192.168.0.173: 'nmap program was not found in path. PATH is : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' +2025-02-19 20:19:47,367 - ERROR - Scan failed for 192.168.0.150: 'nmap program was not found in path. PATH is : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' +2025-02-19 20:19:47,367 - ERROR - Scan failed for 192.168.0.22: 'nmap program was not found in path. PATH is : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' +2025-02-19 20:19:47,367 - ERROR - Scan failed for 192.168.0.1: 'nmap program was not found in path. PATH is : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' +2025-02-19 20:19:47,369 - ERROR - Scan failed for 192.168.0.244: 'nmap program was not found in path. PATH is : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' +2025-02-19 20:19:47,377 - ERROR - Scan failed for 192.168.0.205: 'nmap program was not found in path. PATH is : /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' +2025-02-19 20:20:45,880 - INFO - Starting network scan of 192.168.0.0/24 diff --git a/network_scanner.py b/network_scanner.py index 8b1e504..7b9b11b 100644 --- a/network_scanner.py +++ b/network_scanner.py @@ -15,9 +15,17 @@ from typing import Dict, List, Optional import texttable import textwrap +from mac_vendor_lookup import MacLookup +import nmap +from scapy.all import ARP, Ether, srp +from tqdm import tqdm +import colorama +from colorama import init, Fore, Style +import random +import os +import shutil # Initialize colorama for cross-platform colored output -from colorama import init, Fore, Style init() # Configure logging @@ -42,6 +50,7 @@ def __init__(self, ip: str, mac: str): self.response_time = 0.0 self.services = {} self.is_gateway = False + self.vulnerabilities = [] class EnhancedNetworkScanner: def __init__(self, target_network: str, stealth_level: int = 1, ports: List[int] = None): @@ -78,7 +87,7 @@ def _get_default_gateway(self) -> Optional[str]: for line in result.stdout.split('\n'): if "Default Gateway" in line: gateway = line.split(":")[1].strip() - if gateway and gateway != "": + if gateway: return gateway return None except Exception as e: @@ -94,7 +103,7 @@ def validate_network(self) -> bool: return False def adaptive_delay(self) -> float: - return self.base_delay * (1 + (self.stealth_level - 1) * 0.5) + return self.base_delay * (1 + (self.stealth_level - 1) * 0.5) + random.uniform(0.1, 0.5) def get_service_banner(self, ip: str, port: int) -> str: try: @@ -105,6 +114,15 @@ def get_service_banner(self, ip: str, port: int) -> str: except: return "" + def check_vulnerabilities(self, service: str) -> List[str]: + vulnerabilities = [] + # Placeholder for vulnerability checking logic + # For example, you can use CVE databases or other sources to check for known vulnerabilities + # Here, we simulate with dummy data + if service in ["ftp", "ssh", "telnet", "http", "smb"]: + vulnerabilities.append(f"Vulnerable service detected: {service}") + return vulnerabilities + def scan_device(self, device: NetworkDevice): nm = nmap.PortScanner() try: @@ -129,6 +147,7 @@ def scan_device(self, device: NetworkDevice): 'banner': banner, 'response_time': scan_time }) + device.vulnerabilities.extend(self.check_vulnerabilities(service)) time.sleep(self.adaptive_delay()) except Exception as e: logging.debug(f"Port {port} scan failed for {device.ip}: {e}") @@ -187,11 +206,12 @@ def _print_scan_summary(self, scan_time: float): table = texttable.Texttable(max_width=100) table.set_deco(texttable.Texttable.HEADER | texttable.Texttable.VLINES) - headers = ["IP Address", "Hostname", "MAC Address", "Vendor", "OS", "Open Ports"] + headers = ["IP Address", "Hostname", "MAC Address", "Vendor", "OS", "Open Ports", "Vulnerabilities"] table.header(headers) for device in sorted(self.devices, key=lambda x: ipaddress.IPv4Address(x.ip)): ports_str = ", ".join([f"{p['port']}/{p['service']}" for p in device.open_ports[:3]]) + vulns_str = "\n".join(device.vulnerabilities) if len(device.open_ports) > 3: ports_str += f" (+{len(device.open_ports)-3} more)" @@ -201,7 +221,8 @@ def _print_scan_summary(self, scan_time: float): device.mac, device.vendor[:20] + "..." if len(device.vendor) > 20 else device.vendor, device.os[:20] + "..." if len(device.os) > 20 else device.os, - ports_str + ports_str, + vulns_str ] table.add_row(row) @@ -224,7 +245,7 @@ def save_results(self, format: str = "csv"): with open(filename, mode='w', newline='', encoding='utf-8') as file: writer = csv.DictWriter(file, fieldnames=[ "IP Address", "MAC Address", "Hostname", "Vendor", "OS", - "Open Ports", "Is Gateway", "Last Seen" + "Open Ports", "Is Gateway", "Last Seen", "Vulnerabilities" ]) writer.writeheader() for device in self.devices: @@ -236,7 +257,8 @@ def save_results(self, format: str = "csv"): "OS": device.os, "Open Ports": ', '.join([f"{p['port']}/{p['service']}" for p in device.open_ports]), "Is Gateway": device.is_gateway, - "Last Seen": device.last_seen.isoformat() + "Last Seen": device.last_seen.isoformat(), + "Vulnerabilities": '; '.join(device.vulnerabilities) }) else: # JSON format filename = f"scan_results_{timestamp}.json" @@ -249,7 +271,8 @@ def save_results(self, format: str = "csv"): "os": d.os, "open_ports": d.open_ports, "is_gateway": d.is_gateway, - "last_seen": d.last_seen.isoformat() + "last_seen": d.last_seen.isoformat(), + "vulnerabilities": d.vulnerabilities } for d in self.devices], file, indent=2) print(f"\n{Fore.GREEN}Results saved to: {filename}{Style.RESET_ALL}") @@ -267,22 +290,27 @@ def parse_arguments(): ) parser.add_argument('network', help='Target network (e.g., 192.168.1.0/24)') parser.add_argument('-s', '--stealth', type=int, choices=[1, 2, 3], default=1, - help='Stealth level (1=normal, 2=stealthier, 3=stealthiest)') + help='Stealth level (1=normal, 2=stealthier, 3=stealthiest)') parser.add_argument('-p', '--ports', type=int, nargs='+', - help='Specific ports to scan (default: common ports)') + help='Specific ports to scan (default: common ports)') parser.add_argument('-f', '--format', choices=['csv', 'json'], default='csv', - help='Output format for results (default: csv)') + help='Output format for results (default: csv)') return parser.parse_args() def main(): args = parse_arguments() + + # Check for root privileges + if os.geteuid() != 0: + logging.error("This script must be run with root privileges.") + sys.exit(1) + + # Check if nmap is installed + if not shutil.which("nmap"): + logging.error("nmap program was not found in path. Please install nmap and ensure it is in your PATH.") + sys.exit(1) try: - from mac_vendor_lookup import MacLookup - from scapy.all import ARP, Ether, srp - from tqdm import tqdm - from nmap import PortScanner - scanner = EnhancedNetworkScanner( target_network=args.network, stealth_level=args.stealth, @@ -301,4 +329,4 @@ def main(): sys.exit(1) if __name__ == "__main__": - main() + main() diff --git a/scan_results_20250219_201947.csv b/scan_results_20250219_201947.csv new file mode 100644 index 0000000..6f17bd6 --- /dev/null +++ b/scan_results_20250219_201947.csv @@ -0,0 +1,9 @@ +IP Address,MAC Address,Hostname,Vendor,OS,Open Ports,Is Gateway,Last Seen +192.168.0.1,d4:b9:2f:5b:06:dc,_gateway,Vantiva USA LLC,Unknown,,False,2025-02-19T20:19:47.110989 +192.168.0.22,d8:be:65:44:7b:fd,Unknown,Amazon Technologies Inc.,Unknown,,False,2025-02-19T20:19:47.290450 +192.168.0.96,10:ce:02:25:d0:9d,Unknown,Amazon Technologies Inc.,Unknown,,False,2025-02-19T20:19:47.301361 +192.168.0.130,5c:fb:3a:da:d3:83,Unknown,"CHONGQING FUGUI ELECTRONICS CO.,LTD.",Unknown,,False,2025-02-19T20:19:47.311597 +192.168.0.173,e8:38:a0:64:de:d5,Unknown,"Vizio, Inc",Unknown,,False,2025-02-19T20:19:47.319835 +192.168.0.150,48:b4:23:f4:5c:83,Unknown,Amazon Technologies Inc.,Unknown,,False,2025-02-19T20:19:47.328293 +192.168.0.244,84:ea:ed:53:84:9a,Unknown,"Roku, Inc",Unknown,,False,2025-02-19T20:19:47.334984 +192.168.0.205,40:ed:00:13:94:fb,Unknown,TP-Link Corporation Limited,Unknown,,False,2025-02-19T20:19:47.357526 diff --git a/scan_results_20250219_202611.csv b/scan_results_20250219_202611.csv new file mode 100644 index 0000000..146898f --- /dev/null +++ b/scan_results_20250219_202611.csv @@ -0,0 +1,9 @@ +IP Address,MAC Address,Hostname,Vendor,OS,Open Ports,Is Gateway,Last Seen +192.168.0.1,d4:b9:2f:5b:06:dc,_gateway,Vantiva USA LLC,Linux 3.2 - 4.9,"53/domain, 80/http, 443/https",False,2025-02-19T20:20:48.038700 +192.168.0.96,10:ce:02:25:d0:9d,Unknown,Amazon Technologies Inc.,Linux 2.6.32,443/https,False,2025-02-19T20:20:48.050433 +192.168.0.130,5c:fb:3a:da:d3:83,Unknown,"CHONGQING FUGUI ELECTRONICS CO.,LTD.",Microsoft Windows 10,445/microsoft-ds,False,2025-02-19T20:20:48.066363 +192.168.0.22,d8:be:65:44:7b:fd,Unknown,Amazon Technologies Inc.,Linux 3.2 - 4.9,,False,2025-02-19T20:20:48.083209 +192.168.0.173,e8:38:a0:64:de:d5,Unknown,"Vizio, Inc",Linux 2.6.32,,False,2025-02-19T20:20:48.095063 +192.168.0.150,48:b4:23:f4:5c:83,Unknown,Amazon Technologies Inc.,Android 4.1 - 6.0 (Linux 3.4 - 3.14),,False,2025-02-19T20:20:48.104084 +192.168.0.205,40:ed:00:13:94:fb,Unknown,TP-Link Corporation Limited,Microsoft Windows 10,445/microsoft-ds,False,2025-02-19T20:20:48.114414 +192.168.0.244,84:ea:ed:53:84:9a,Unknown,"Roku, Inc",Android 5.1.1,,False,2025-02-19T20:20:48.127414