uaf.git

uaf

~
git clone https://github.com/debuman/uaf.git

ap_scanner/oui.py (6964B)

      1 """OUI (Organizationally Unique Identifier) vendor lookup from MAC address."""
      2 
      3 from pathlib import Path
      4 
      5 # Common WiFi AP/router vendors - covers most consumer and enterprise APs
      6 # Format: first 3 bytes of MAC (uppercase, colon-separated) -> vendor name
      7 _BUILTIN_OUI: dict[str, str] = {
      8     # TP-Link
      9     "5C:A6:E6": "TP-Link", "E8:48:B8": "TP-Link", "50:C7:BF": "TP-Link",
     10     "98:DA:C4": "TP-Link", "B0:BE:76": "TP-Link", "60:32:B1": "TP-Link",
     11     "A4:2B:B0": "TP-Link", "30:DE:4B": "TP-Link", "C0:06:C3": "TP-Link",
     12     "F4:EC:38": "TP-Link", "AC:84:C6": "TP-Link", "14:EB:B6": "TP-Link",
     13     "EC:08:6B": "TP-Link", "78:8C:B5": "TP-Link", "B4:B0:24": "TP-Link",
     14     # Netgear
     15     "34:98:B5": "NETGEAR", "C4:04:15": "NETGEAR", "20:0C:C8": "NETGEAR",
     16     "A0:40:A0": "NETGEAR", "E0:46:9A": "NETGEAR", "28:C6:8E": "NETGEAR",
     17     "6C:B0:CE": "NETGEAR", "B0:7F:B9": "NETGEAR", "DC:EF:09": "NETGEAR",
     18     # Cisco / Meraki
     19     "00:1B:2A": "Cisco", "00:26:0B": "Cisco", "F8:C2:88": "Cisco Meraki",
     20     "0C:8D:DB": "Cisco Meraki", "AC:17:C8": "Cisco Meraki", "34:56:FE": "Cisco",
     21     "00:18:74": "Cisco", "00:1A:A1": "Cisco", "B0:AA:77": "Cisco",
     22     # Aruba / HPE
     23     "00:0B:86": "Aruba Networks", "24:DE:C6": "Aruba Networks",
     24     "AC:A3:1E": "Aruba Networks", "D8:C7:C8": "Aruba Networks",
     25     "9C:1C:12": "Aruba Networks", "70:3A:0E": "Aruba Networks",
     26     # Ubiquiti
     27     "24:A4:3C": "Ubiquiti", "FC:EC:DA": "Ubiquiti", "68:D7:9A": "Ubiquiti",
     28     "80:2A:A8": "Ubiquiti", "F0:9F:C2": "Ubiquiti", "44:D9:E7": "Ubiquiti",
     29     "78:8A:20": "Ubiquiti", "18:E8:29": "Ubiquiti", "74:83:C2": "Ubiquiti",
     30     # Ruckus
     31     "C4:10:8A": "Ruckus", "EC:58:EA": "Ruckus", "74:91:1A": "Ruckus",
     32     "A4:12:42": "Ruckus", "58:B6:33": "Ruckus",
     33     # Samsung
     34     "A8:7C:01": "Samsung", "F4:42:8F": "Samsung", "C0:BD:D1": "Samsung",
     35     "5C:3A:45": "Samsung", "B4:3A:28": "Samsung", "D0:22:BE": "Samsung",
     36     "78:BD:BC": "Samsung", "CC:07:AB": "Samsung", "58:CB:52": "Samsung",
     37     # Apple
     38     "00:1C:B3": "Apple", "28:CF:E9": "Apple", "3C:06:30": "Apple",
     39     "70:3E:AC": "Apple", "AC:BC:32": "Apple", "F0:D1:A9": "Apple",
     40     "14:7D:DA": "Apple", "18:AF:61": "Apple", "D0:E1:40": "Apple",
     41     "A8:8E:24": "Apple", "F8:FF:C2": "Apple", "DC:A9:04": "Apple",
     42     # Intel
     43     "8C:17:59": "Intel", "14:85:7F": "Intel", "A0:C5:89": "Intel",
     44     "68:05:CA": "Intel", "34:13:E8": "Intel", "80:86:F2": "Intel",
     45     "48:51:B7": "Intel", "7C:B2:7D": "Intel",
     46     # Huawei / Honor
     47     "00:E0:FC": "Huawei", "48:46:FB": "Huawei", "88:28:B3": "Huawei",
     48     "C8:D1:5E": "Huawei", "04:F9:38": "Huawei", "4C:B1:6C": "Huawei",
     49     "70:8C:B6": "Huawei", "AC:61:EA": "Huawei",
     50     # ASUS
     51     "F8:32:E4": "ASUS", "04:D9:F5": "ASUS", "2C:FD:A1": "ASUS",
     52     "1C:87:2C": "ASUS", "38:D5:47": "ASUS", "60:45:CB": "ASUS",
     53     "AC:22:0B": "ASUS", "B0:6E:BF": "ASUS",
     54     # D-Link
     55     "00:1C:F0": "D-Link", "1C:7E:E5": "D-Link", "28:10:7B": "D-Link",
     56     "84:C9:B2": "D-Link", "C8:D3:A3": "D-Link", "FC:75:16": "D-Link",
     57     "B8:A3:86": "D-Link",
     58     # Linksys / Belkin
     59     "C0:56:27": "Linksys", "20:AA:4B": "Linksys", "58:6D:8F": "Linksys",
     60     "E8:9F:80": "Belkin", "94:44:52": "Belkin", "08:86:3B": "Belkin",
     61     # EFM Networks (ipTIME)
     62     "70:5D:CC": "EFM Networks", "58:86:94": "EFM Networks",
     63     "88:36:6C": "EFM Networks", "00:08:9F": "EFM Networks",
     64     "90:9F:33": "EFM Networks", "00:26:66": "EFM Networks",
     65     "C8:3A:35": "EFM Networks", "04:BD:70": "EFM Networks",
     66     # LG
     67     "00:1E:75": "LG Electronics", "10:68:3F": "LG Electronics",
     68     "64:99:68": "LG Electronics", "C4:36:6C": "LG Electronics",
     69     "A8:23:FE": "LG Electronics",
     70     # OHSUNG (LG IoT devices)
     71     "1C:39:29": "OHSUNG",
     72     # Fortinet
     73     "E8:1C:BA": "Fortinet", "00:09:0F": "Fortinet", "08:5B:0E": "Fortinet",
     74     # Xiaomi
     75     "28:6C:07": "Xiaomi", "64:CC:2E": "Xiaomi", "78:11:DC": "Xiaomi",
     76     "50:EC:50": "Xiaomi", "7C:1D:D9": "Xiaomi",
     77     # Qualcomm / Mediatek (chipset vendors)
     78     "00:03:7F": "Atheros", "00:1A:6B": "Atheros",
     79     "00:0C:E7": "MediaTek", "00:0C:43": "Ralink",
     80     # Realtek
     81     "00:E0:4C": "Realtek", "48:5D:60": "Realtek", "52:54:00": "Realtek",
     82     # Broadcom
     83     "00:10:18": "Broadcom", "00:1A:2B": "Broadcom",
     84     # Google
     85     "F4:F5:D8": "Google", "54:60:09": "Google", "A4:77:33": "Google",
     86     # Amazon (eero, Ring, etc.)
     87     "F0:72:EA": "Amazon", "68:54:FD": "Amazon", "40:B4:CD": "Amazon",
     88     # Seongji Industry
     89     "88:57:1D": "Seongji Industry",
     90     # SHENZHEN GONGJIN (ZTE ODM)
     91     "80:CA:4B": "SHENZHEN GONGJIN ELECTRONICS",
     92     # Alfa Network
     93     "00:C0:CA": "Alfa Network",
     94 }
     95 
     96 # Loaded entries from system OUI file
     97 _loaded_oui: dict[str, str] = {}
     98 _oui_loaded = False
     99 
    100 
    101 def _load_system_oui() -> None:
    102     """Try to load the system OUI database (e.g., from aircrack-ng or wireshark)."""
    103     global _oui_loaded, _loaded_oui
    104     if _oui_loaded:
    105         return
    106     _oui_loaded = True
    107 
    108     oui_paths = [
    109         Path("/usr/share/ieee-data/oui.txt"),
    110         Path("/usr/share/misc/oui.txt"),
    111         Path("/usr/share/wireshark/manuf"),
    112         Path("/usr/share/aircrack-ng/airodump-ng-oui.txt"),
    113         Path("/etc/aircrack-ng/airodump-ng-oui.txt"),
    114     ]
    115 
    116     for oui_path in oui_paths:
    117         if oui_path.exists():
    118             try:
    119                 _parse_oui_file(oui_path)
    120                 return
    121             except Exception:
    122                 continue
    123 
    124 
    125 def _parse_oui_file(path: Path) -> None:
    126     """Parse an OUI file (IEEE format or Wireshark manuf format)."""
    127     with open(path, "r", encoding="utf-8", errors="replace") as f:
    128         for line in f:
    129             line = line.strip()
    130             if not line or line.startswith("#"):
    131                 continue
    132 
    133             # IEEE format: "AA-BB-CC   (hex)        Vendor Name"
    134             if "(hex)" in line:
    135                 parts = line.split("(hex)")
    136                 if len(parts) == 2:
    137                     oui = parts[0].strip().replace("-", ":").upper()
    138                     vendor = parts[1].strip()
    139                     if len(oui) == 8 and vendor:
    140                         _loaded_oui[oui] = vendor
    141 
    142             # Wireshark manuf format: "AA:BB:CC<tab>ShortName<tab>Long Name"
    143             elif "\t" in line:
    144                 parts = line.split("\t")
    145                 if len(parts) >= 2:
    146                     oui = parts[0].strip().upper().replace("-", ":")
    147                     vendor = parts[-1].strip() if len(parts) >= 3 else parts[1].strip()
    148                     if len(oui) == 8 and vendor:
    149                         _loaded_oui[oui] = vendor
    150 
    151 
    152 def lookup_vendor(mac: str) -> str:
    153     """Look up vendor name from MAC address.
    154 
    155     Args:
    156         mac: MAC address in format "AA:BB:CC:DD:EE:FF"
    157 
    158     Returns:
    159         Vendor name or empty string if not found.
    160     """
    161     oui = mac[:8].upper()
    162 
    163     # Check builtin first (curated, clean names)
    164     if oui in _BUILTIN_OUI:
    165         return _BUILTIN_OUI[oui]
    166 
    167     # Try system OUI database
    168     _load_system_oui()
    169     if oui in _loaded_oui:
    170         return _loaded_oui[oui]
    171 
    172     return ""