chore:PHOLUS removal

This commit is contained in:
jokob-sk
2024-11-22 20:32:49 +11:00
parent e776c3ac41
commit 0e438ffd57
29 changed files with 64 additions and 3191 deletions

View File

@@ -21,7 +21,6 @@ You can access the following files:
| `notification_text.html` | The full HTML of the last email notification. |
| `notification_json_final.json` | The json version of the last notification (e.g. used for webhooks - [sample JSON](https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json)). |
| `table_devices.json` | The current (at the time of the last update as mentioned above on this page) state of all of the available Devices detected by the app. |
| `table_pholus_scan.json` | The latest state of the [pholus](https://github.com/jokob-sk/NetAlertX/tree/main/pholus) (A multicast DNS and DNS Service Discovery Security Assessment Tool) scan results. |
| `table_plugins_events.json` | The list of the unprocessed (pending) notification events (plugins_events DB table). |
| `table_plugins_history.json` | The list of notification events history. |
| `table_plugins_objects.json` | The content of the plugins_objects table. Find more info on the [Plugin system here](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins)|

View File

@@ -14,7 +14,6 @@
| Events | Used to collect connection/disconnection events. | ![Screen4][screen4] |
| Online_History | Used to display the `Device presence` chart | ![Screen6][screen6] |
| Parameters | Used to pass values between the frontend and backend. | ![Screen7][screen7] |
| Pholus_Scan | Scan results of the Pholus python network penetration script. | ![Screen8][screen8] |
| Plugins_Events | For capturing events exposed by a plugin via the `last_result.log` file. If unique then saved into the `Plugins_Objects` table. Entries are deleted once processed and stored in the `Plugins_History` and/or `Plugins_Objects` tables. | ![Screen10][screen10] |
| Plugins_History | History of all entries from the `Plugins_Events` table | ![Screen11][screen11] |
| Plugins_Language_Strings | Language strings collected from the plugin `config.json` files used for string resolution in the frontend. | ![Screen12][screen12] |
@@ -29,7 +28,6 @@
[screen4]: /docs/img/DATABASE/Events.png
[screen6]: /docs/img/DATABASE/Online_History.png
[screen7]: /docs/img/DATABASE/Parameters.png
[screen8]: /docs/img/DATABASE/Pholus_Scan.png
[screen10]: /docs/img/DATABASE/Plugins_Events.png
[screen11]: /docs/img/DATABASE/Plugins_History.png
[screen12]: /docs/img/DATABASE/Plugins_Language_Strings.png

View File

@@ -15,6 +15,7 @@
<?php
require 'php/templates/header.php';
require 'php/components/graph_online_history.php';
// check permissions
$dbPath = "../db/app.db";
@@ -55,6 +56,7 @@
<div class="box-body">
<div class="chart">
<script src="lib/AdminLTE/bower_components/chart.js/Chart.js?v=<?php include 'php/templates/version.php'; ?>"></script>
<!-- The online history chart is rendered here -->
<canvas id="OnlineChart" style="width:100%; height: 150px; margin-bottom: 15px;"></canvas>
</div>
</div>
@@ -62,40 +64,6 @@
</div>
</div>
</div>
<script src="js/graph_online_history.js"></script>
<script>
$.get('api/table_online_history.json?nocache=' + Date.now(), function(res) {
// Extracting data from the JSON response
var timeStamps = [];
var onlineCounts = [];
var downCounts = [];
var offlineCounts = [];
var archivedCounts = [];
res.data.forEach(function(entry) {
var dateObj = new Date(entry.Scan_Date);
var formattedTime = dateObj.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', hour12: false});
timeStamps.push(formattedTime);
onlineCounts.push(entry.Online_Devices);
downCounts.push(entry.Down_Devices);
offlineCounts.push(entry.Offline_Devices);
archivedCounts.push(entry.Archived_Devices);
});
// Call your presenceOverTime function after data is ready
presenceOverTime(
timeStamps,
onlineCounts,
offlineCounts,
archivedCounts,
downCounts
);
}).fail(function() {
// Handle any errors in fetching the data
console.error('Error fetching online history data.');
});
</script>
<!-- datatable ------------------------------------------------------------- -->
<div class="row">

View File

@@ -0,0 +1,42 @@
<?php
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
?>
<script src="js/graph_online_history.js"></script>
<script>
$.get('api/table_online_history.json?nocache=' + Date.now(), function(res) {
// Extracting data from the JSON response
var timeStamps = [];
var onlineCounts = [];
var downCounts = [];
var offlineCounts = [];
var archivedCounts = [];
res.data.forEach(function(entry) {
var dateObj = new Date(entry.Scan_Date);
var formattedTime = dateObj.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', hour12: false});
timeStamps.push(formattedTime);
onlineCounts.push(entry.Online_Devices);
downCounts.push(entry.Down_Devices);
offlineCounts.push(entry.Offline_Devices);
archivedCounts.push(entry.Archived_Devices);
});
// Call your presenceOverTime function after data is ready
presenceOverTime(
timeStamps,
onlineCounts,
offlineCounts,
archivedCounts,
downCounts
);
}).fail(function() {
// Handle any errors in fetching the data
console.error('Error fetching online history data.');
});
</script>

View File

@@ -258,7 +258,7 @@ function cleanLog($logFile)
$path = "";
$allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', "pholus_lastrun.log", 'app.php_errors.log', 'execution_queue.log'];
$allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', 'app.php_errors.log', 'execution_queue.log'];
if(in_array($logFile, $allowedFiles))
{

View File

@@ -578,7 +578,7 @@
"REPORT_TITLE": "Report",
"RandomMAC_hover": "Autodetected - indicates if the device randomizes it's MAC address.",
"Reports_Sent_Log": "Sent Reports Log",
"SCAN_SUBNETS_description": "Most on-network scanners (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) rely on scanning specific network interfaces and subnets. Check the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnets documentation</a> for help on this setting, especially VLANs, what VLANs are supported, or how to figure out the network mask and your interface. <br/> <br/> An alternative to on-network scanners is to enable some other Device scanners/importers that don't rely on NetAlert<sup>X</sup> having access to the network (UNIFI, dhcp.leases, PiHole, etc.). <br/> <br/> Note: The scan time itself depends on the number of IP addresses to check, so set this up carefully with the appropriate network mask and interface.",
"SCAN_SUBNETS_description": "Most on-network scanners (ARP-SCAN, NMAP, NSLOOKUP, DIG) rely on scanning specific network interfaces and subnets. Check the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnets documentation</a> for help on this setting, especially VLANs, what VLANs are supported, or how to figure out the network mask and your interface. <br/> <br/> An alternative to on-network scanners is to enable some other Device scanners/importers that don't rely on NetAlert<sup>X</sup> having access to the network (UNIFI, dhcp.leases, PiHole, etc.). <br/> <br/> Note: The scan time itself depends on the number of IP addresses to check, so set this up carefully with the appropriate network mask and interface.",
"SCAN_SUBNETS_name": "Networks to scan",
"SYSTEM_TITLE": "System Information",
"Setting_Override": "Override value",

View File

@@ -631,7 +631,7 @@
"REPORT_WEBHOOK_name": "Habilitar webhooks",
"RandomMAC_hover": "Autodetectado - indica si el dispositivo aleatoriza su dirección MAC.",
"Reports_Sent_Log": "Registro de informes enviados",
"SCAN_SUBNETS_description": "La mayoría de los escáneres en red (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) se basan en el escaneo de interfaces de red y subredes específicas. Consulte la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentación sobre subredes</a> para obtener ayuda sobre esta configuración, especialmente VLANs, qué VLANs son compatibles, o cómo averiguar la máscara de red y su interfaz. <br/> <br/>Una alternativa a los escáneres en red es habilitar algunos otros escáneres/importadores de dispositivos que no dependen de que NetAlert<sup>X</sup> tenga acceso a la red (UNIFI, dhcp.leases, PiHole, etc.). <br/> <br/> Nota: El tiempo de escaneo en sí depende del número de direcciones IP a comprobar, así que configure esto cuidadosamente con la máscara de red y la interfaz adecuadas.",
"SCAN_SUBNETS_description": "La mayoría de los escáneres en red (ARP-SCAN, NMAP, NSLOOKUP, DIG) se basan en el escaneo de interfaces de red y subredes específicas. Consulte la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentación sobre subredes</a> para obtener ayuda sobre esta configuración, especialmente VLANs, qué VLANs son compatibles, o cómo averiguar la máscara de red y su interfaz. <br/> <br/>Una alternativa a los escáneres en red es habilitar algunos otros escáneres/importadores de dispositivos que no dependen de que NetAlert<sup>X</sup> tenga acceso a la red (UNIFI, dhcp.leases, PiHole, etc.). <br/> <br/> Nota: El tiempo de escaneo en sí depende del número de direcciones IP a comprobar, así que configure esto cuidadosamente con la máscara de red y la interfaz adecuadas.",
"SCAN_SUBNETS_name": "Subredes para escanear",
"SMTP_FORCE_SSL_description": "Forzar SSL al conectarse a su servidor SMTP",
"SMTP_FORCE_SSL_name": "Forzar SSL",

2
front/php/templates/language/it_it.json Normal file → Executable file
View File

@@ -578,7 +578,7 @@
"REPORT_TITLE": "Rapporto",
"RandomMAC_hover": "Rilevato automaticamente: indica se il dispositivo genera il suo indirizzo MAC casualmente.",
"Reports_Sent_Log": "Log rapporti inviati",
"SCAN_SUBNETS_description": "La maggior parte degli scanner di rete (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) si basano sulla scansione di interfacce di rete e sottoreti specifiche. Consulta la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentazione sulle sottoreti</a> per assistenza su questa impostazione, in particolare VLAN, quali VLAN sono supportate o come individuare la maschera di rete e l'interfaccia. <br/> <br/> Un'alternativa agli scanner in rete è abilitare altri scanner/importatori di dispositivi che non si affidano a NetAlert<sup>X</sup> che hanno accesso alla rete (UNIFI, dhcp.leases , PiHole, ecc.). <br/> <br/> Nota: il tempo di scansione stesso dipende dal numero di indirizzi IP da controllare, quindi impostalo attentamente con la maschera di rete e l'interfaccia appropriate.",
"SCAN_SUBNETS_description": "La maggior parte degli scanner di rete (ARP-SCAN, NMAP, NSLOOKUP, DIG) si basano sulla scansione di interfacce di rete e sottoreti specifiche. Consulta la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentazione sulle sottoreti</a> per assistenza su questa impostazione, in particolare VLAN, quali VLAN sono supportate o come individuare la maschera di rete e l'interfaccia. <br/> <br/> Un'alternativa agli scanner in rete è abilitare altri scanner/importatori di dispositivi che non si affidano a NetAlert<sup>X</sup> che hanno accesso alla rete (UNIFI, dhcp.leases , PiHole, ecc.). <br/> <br/> Nota: il tempo di scansione stesso dipende dal numero di indirizzi IP da controllare, quindi impostalo attentamente con la maschera di rete e l'interfaccia appropriate.",
"SCAN_SUBNETS_name": "Reti da scansionare",
"SYSTEM_TITLE": "Informazioni sistema",
"Setting_Override": "Sovrascrivi valore",

View File

@@ -578,7 +578,7 @@
"REPORT_TITLE": "Rapport",
"RandomMAC_hover": "Autodetektert - indikerer om enheten randomiserer MAC-adressen sin.",
"Reports_Sent_Log": "Sendte rapport logger",
"SCAN_SUBNETS_description": "De fleste skannere på nettet (ARP-Scan, NMAP, NSlookup, Dig, Pholus) er avhengige av å skanne spesifikke nettverksgrensesnitt og undernett. Sjekk <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnett dokumentasjonen</a> for hjelp på denne innstillingen, spesielt VLAN-er, hvilke VLAN-er som støttes, eller hvordan du kan finne ut nettverksmasken og grensesnittet ditt. <br/> <br/> Et alternativ til skannere på nettet er å aktivere noen andre enhetsskannere/importører som ikke er avhengige av Netalert<sup>X</sup> med tilgang til nettverket (UniFi, DHCP-Leaser, Pihole, osv.). <br/> <br/> Merk: Selve skanningstiden avhenger av antall IP -adresser som skal sjekkes, så sett dette opp nøye med riktig nettverksmaske og grensesnitt.",
"SCAN_SUBNETS_description": "De fleste skannere på nettet (ARP-Scan, NMAP, NSlookup, Dig) er avhengige av å skanne spesifikke nettverksgrensesnitt og undernett. Sjekk <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnett dokumentasjonen</a> for hjelp på denne innstillingen, spesielt VLAN-er, hvilke VLAN-er som støttes, eller hvordan du kan finne ut nettverksmasken og grensesnittet ditt. <br/> <br/> Et alternativ til skannere på nettet er å aktivere noen andre enhetsskannere/importører som ikke er avhengige av Netalert<sup>X</sup> med tilgang til nettverket (UniFi, DHCP-Leaser, Pihole, osv.). <br/> <br/> Merk: Selve skanningstiden avhenger av antall IP -adresser som skal sjekkes, så sett dette opp nøye med riktig nettverksmaske og grensesnitt.",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "Systeminformasjon",
"Setting_Override": "Overstyr verdi",

View File

@@ -578,7 +578,7 @@
"REPORT_TITLE": "Zgłoszenie",
"RandomMAC_hover": "Auto wykrywanie - oznacza czy urządzenie randomizuje swój adres MAC.",
"Reports_Sent_Log": "Wyślij zgłoszenie logów",
"SCAN_SUBNETS_description": "Większość skanerów sieciowych (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) opiera się na konkretnych interfejsach sieciowych oraz podsieci. Sprawdź <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\"> dokumentacji podsieci</a> jeżeli potrzebujesz pomocy w ustawieniach, a szczególnie z VLAN'ami, jakie VLAN'y są wspierane oraz jak rozgryźć maskę podsieci twojego interfejsu.<br/><br/> Alternatywą do skanerów sieciowych jest uruchomienie innego Skanera Urządzeń/Importera który nie polega by NetAlert<sup>X</sup> miał dostęp do sieci (UNIFI, dhcp.leases, PiHole, itp.).<br/><br/> Notatka: Czas skanu zależy od liczby adresów IP do sprawdzenia, więc ustaw go tak by skanował odpowiedni interfejs i maskę sieciową.",
"SCAN_SUBNETS_description": "Większość skanerów sieciowych (ARP-SCAN, NMAP, NSLOOKUP, DIG) opiera się na konkretnych interfejsach sieciowych oraz podsieci. Sprawdź <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\"> dokumentacji podsieci</a> jeżeli potrzebujesz pomocy w ustawieniach, a szczególnie z VLAN'ami, jakie VLAN'y są wspierane oraz jak rozgryźć maskę podsieci twojego interfejsu.<br/><br/> Alternatywą do skanerów sieciowych jest uruchomienie innego Skanera Urządzeń/Importera który nie polega by NetAlert<sup>X</sup> miał dostęp do sieci (UNIFI, dhcp.leases, PiHole, itp.).<br/><br/> Notatka: Czas skanu zależy od liczby adresów IP do sprawdzenia, więc ustaw go tak by skanował odpowiedni interfejs i maskę sieciową.",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "Informacje o Systemie",
"Setting_Override": "Nadpisz wartość",

View File

@@ -578,7 +578,7 @@
"REPORT_TITLE": "Отчет",
"RandomMAC_hover": "Автоматически обнаружено — указывает, рандомизирует ли устройство свой MAC-адрес.",
"Reports_Sent_Log": "Отправленные уведомления",
"SCAN_SUBNETS_description": "Большинство сетевых сканеров (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) полагаются на сканирование определенных сетевых интерфейсов и подсетей. Дополнительную информацию по этому параметру можно найти в <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">документации по подсетям</a>, особенно VLAN, какие VLAN поддерживаются или как разобраться в маске сети и своем интерфейсе. <br/> <br/> Альтернативой сетевым сканерам является включение некоторых других сканеров/импортеров устройств, которые не полагаются на NetAlert<sup>X</sup>, имеющий доступ к сети (UNIFI, dhcp.leases , PiHole и др.). <br/> <br/> Примечание. Само время сканирования зависит от количества проверяемых IP-адресов, поэтому тщательно настройте его, указав соответствующую маску сети и интерфейс.",
"SCAN_SUBNETS_description": "Большинство сетевых сканеров (ARP-SCAN, NMAP, NSLOOKUP, DIG) полагаются на сканирование определенных сетевых интерфейсов и подсетей. Дополнительную информацию по этому параметру можно найти в <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">документации по подсетям</a>, особенно VLAN, какие VLAN поддерживаются или как разобраться в маске сети и своем интерфейсе. <br/> <br/> Альтернативой сетевым сканерам является включение некоторых других сканеров/импортеров устройств, которые не полагаются на NetAlert<sup>X</sup>, имеющий доступ к сети (UNIFI, dhcp.leases , PiHole и др.). <br/> <br/> Примечание. Само время сканирования зависит от количества проверяемых IP-адресов, поэтому тщательно настройте его, указав соответствующую маску сети и интерфейс.",
"SCAN_SUBNETS_name": "Сети для сканирования",
"SYSTEM_TITLE": "Системная информация",
"Setting_Override": "Переопределить значение",

View File

@@ -578,7 +578,7 @@
"REPORT_TITLE": "报告",
"RandomMAC_hover": "自动检测 - 表示设备是否随机化其 MAC 地址。",
"Reports_Sent_Log": "已发送报告日志",
"SCAN_SUBNETS_description": "大多数网络扫描器ARP-SCAN、NMAP、NSLOOKUP、DIG、PHOLUS)依赖于扫描特定的网络接口和子网。查看<a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">子网文档</a>以获取有关此设置的帮助,尤其是 VLAN、支持哪些 VLAN或者如何确定网络掩码和接口。<br/> <br/> 网络扫描器的替代方法是启用一些其他不依赖于 NetAlert<sup>X</sup> 访问网络的设备扫描器/导入器UNIFI、dhcp.leases、PiHole 等)。<br/> <br/> 注意:扫描时间本身取决于要检查的 IP 地址数量,因此请使用适当的网络掩码和接口仔细设置。",
"SCAN_SUBNETS_description": "大多数网络扫描器ARP-SCAN、NMAP、NSLOOKUP、DIG依赖于扫描特定的网络接口和子网。查看<a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">子网文档</a>以获取有关此设置的帮助,尤其是 VLAN、支持哪些 VLAN或者如何确定网络掩码和接口。<br/> <br/> 网络扫描器的替代方法是启用一些其他不依赖于 NetAlert<sup>X</sup> 访问网络的设备扫描器/导入器UNIFI、dhcp.leases、PiHole 等)。<br/> <br/> 注意:扫描时间本身取决于要检查的 IP 地址数量,因此请使用适当的网络掩码和接口仔细设置。",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "系统信息",
"Setting_Override": "覆盖值",

View File

@@ -45,7 +45,6 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
| `NTFPRCS` | ⚙ | Notification processing | | Yes | Template | [notification_processing](/front/plugins/notification_processing/)|
| `NTFY` | ▶️ | NTFY notifications | | | Script | [_publisher_ntfy](/front/plugins/_publisher_ntfy/) |
| `OMDSDN` | 📥 | OMADA TP-Link import | 🖧 🔄 | | Script | [omada_sdn_imp](/front/plugins/omada_sdn_imp/) |
| `PHOLUS` | ♻ | Pholus name resolution | | | Script | [pholus_scan](/front/plugins/pholus_scan/) |
| `PIHOLE` | 🔍/📥 | Pi-hole device import & sync | | | SQLite DB | [pihole_scan](/front/plugins/pihole_scan/) |
| `PUSHSAFER` | ▶️ | Pushsafer notifications | | | Script | [_publisher_pushsafer](/front/plugins/_publisher_pushsafer/) |
| `PUSHOVER` | ▶️ | Pushover notifications | | | Script | [_publisher_pushover](/front/plugins/_publisher_pushover/) |

View File

@@ -1,6 +1,6 @@
## Overview
Most on-network scanners (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) rely on scanning specific network interfaces and subnets. Check the [subnets documentation](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md) for help on this setting, especially VLANs, what VLANs are supported, or how to figure out the network mask and your interface.
Most on-network scanners (ARP-SCAN, NMAP, NSLOOKUP, DIG) rely on scanning specific network interfaces and subnets. Check the [subnets documentation](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md) for help on this setting, especially VLANs, what VLANs are supported, or how to figure out the network mask and your interface.
An alternative to on-network scanners is to enable some other Device scanners/importers that don't rely on NetAlert<sup>X</sup> having access to the network (UNIFI, dhcp.leases, PiHole, etc.).

View File

@@ -13,8 +13,6 @@ The database cleanup plugin (DBCLNP) helps maintain the system by removing outda
- **`DAYS_TO_KEEP_EVENTS`**:
Specifies the number of days to retain event logs. Event entries older than the given number of days will be automatically deleted during cleanup. Recommended value: `30` days.
- **`PHOLUS_DAYS_DATA`**:
Deletes all data older than specified number of days for teh Pholus plugin used for name discovery. Recommended value: `30` days.
By fine-tuning these settings, you ensure that the database remains optimized, preventing performance degradation in the NetAlertX system.

View File

@@ -36,14 +36,13 @@ def main():
HRS_TO_KEEP_NEWDEV = int(get_setting_value("HRS_TO_KEEP_NEWDEV"))
HRS_TO_KEEP_OFFDEV = int(get_setting_value("HRS_TO_KEEP_OFFDEV"))
DAYS_TO_KEEP_EVENTS = int(get_setting_value("DAYS_TO_KEEP_EVENTS"))
PHOLUS_DAYS_DATA = get_setting_value("PHOLUS_DAYS_DATA")
CLEAR_NEW_FLAG = get_setting_value("CLEAR_NEW_FLAG")
mylog('verbose', [f'[{pluginName}] In script'])
# Execute cleanup/upkeep
cleanup_database(fullDbPath, DAYS_TO_KEEP_EVENTS, PHOLUS_DAYS_DATA, HRS_TO_KEEP_NEWDEV, HRS_TO_KEEP_OFFDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG)
cleanup_database(fullDbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KEEP_OFFDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG)
mylog('verbose', [f'[{pluginName}] Cleanup complete'])
@@ -52,7 +51,7 @@ def main():
#===============================================================================
# Cleanup / upkeep database
#===============================================================================
def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, PHOLUS_DAYS_DATA, HRS_TO_KEEP_NEWDEV, HRS_TO_KEEP_OFFDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG):
def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KEEP_OFFDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG):
"""
Cleaning out old records from the tables that don't need to keep all data.
"""
@@ -163,29 +162,6 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, PHOLUS_DAYS_DATA, HRS_TO_KEEP
cursor.execute(query)
# -----------------------------------------------------
# Cleanup Pholus_Scan
if PHOLUS_DAYS_DATA != "" and PHOLUS_DAYS_DATA != 0:
mylog('verbose', [f'[{pluginName}] Pholus_Scan: Delete all older than ' + str(PHOLUS_DAYS_DATA) + ' days (PHOLUS_DAYS_DATA setting)'])
# todo: improvement possibility: keep at least N per mac
cursor.execute (f"""DELETE FROM Pholus_Scan
WHERE Time <= date('now', '-{str(PHOLUS_DAYS_DATA)} day')""")
# -----------------------------------------------------
# De-Dupe (de-duplicate - remove duplicate entries) from the Pholus_Scan table
mylog('verbose', [f'[{pluginName}] Pholus_Scan: Delete all duplicates'])
cursor.execute ("""DELETE FROM Pholus_Scan
WHERE rowid > (
SELECT MIN(rowid) FROM Pholus_Scan p2
WHERE Pholus_Scan.MAC = p2.MAC
AND Pholus_Scan.Value = p2.Value
AND Pholus_Scan.Record_Type = p2.Record_Type
);""")
# -----------------------------------------------------
# De-dupe (de-duplicate) from the Plugins_Objects table
# TODO This shouldn't be necessary - probably a concurrency bug somewhere in the code :(

View File

@@ -1,13 +0,0 @@
## Overview
A plugin to resolve `(unknown)` device names. It uses the [Pholus](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/pholus_scan/pholus) sniffing tool.
### Usage
- Go to settings and find Pholus (Name discovery) in the list of settings.
- Enable the plugin by changing the `RUN` parameter from disabled to one you prefer (`schedule`, `always_after_scan`, `on_new_device`).
- Specify the `PHOLUS_RUN_TIMEOUT` (Will be divided by the number of subnets specified in the Arp-Scan (Network scan) plugin setting `SCAN_SUBNETS`)
- SAVE
- Wait for the next scan to finish

View File

@@ -1,517 +0,0 @@
{
"code_name": "pholus_scan",
"unique_prefix": "PHOLUS",
"plugin_type": "other",
"enabled": true,
"data_source": "script",
"mapped_to_table": "Pholus_Scan",
"data_filters": [
{
"compare_column": "Object_PrimaryID",
"compare_operator": "==",
"compare_field_id": "txtMacFilter",
"compare_js_template": "'{value}'.toString()",
"compare_use_quotes": true
}
],
"show_ui": true,
"localized": ["display_name", "description", "icon"],
"display_name": [
{
"language_code": "en_us",
"string": "Pholus (Name discovery)"
},
{
"language_code": "es_es",
"string": "Pholus (Descubrimiento de nombre)"
}
],
"icon": [
{
"language_code": "en_us",
"string": "<i class=\"fa-solid fa-search\"></i>"
},
{
"language_code": "es_es",
"string": "<i class=\"fa-solid fa-search\"></i>"
}
],
"description": [
{
"language_code": "en_us",
"string": "This plugin is to execute a Pholus (name discovery) on the local network"
},
{
"language_code": "es_es",
"string": "Este plugin sirve para ejecutar un escaneo Pholus (descubrimiento de nombres) en la red local"
}
],
"params": [
{
"name": "subnets",
"type": "setting",
"value": "SCAN_SUBNETS",
"base64": true
},
{
"name": "timeout",
"type": "setting",
"value": "PHOLUS_RUN_TIMEOUT"
}
],
"settings": [
{
"function": "RUN",
"type": {
"dataType": "string",
"elements": [
{ "elementType": "select", "elementOptions": [], "transformers": [] }
]
},
"default_value": "disabled",
"options": [
"disabled",
"once",
"schedule",
"always_after_scan",
"on_new_device"
],
"localized": ["name", "description"],
"events": ["run"],
"name": [
{
"language_code": "en_us",
"string": "When to run"
},
{
"language_code": "es_es",
"string": "Cuando ejecutar"
}
],
"description": [
{
"language_code": "en_us",
"string": "<a href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/pholus_scan/pholus\" target=\"_blank\" >Pholus</a> is a sniffing tool to discover additional information about the devices on the network, including the device name. If enabled this will execute the scan before every network scan cycle until there are no <code>(unknown)</code> or <code>(name not found)</code> devices. Please be aware it can spam the network with unnecessary traffic. Depends on the <a onclick=\"toggleAllSettings()\" href=\"#SCAN_SUBNETS\"><code>SCAN_SUBNETS</code> setting</a>. For a scheduled or one-off scan, check the <a href=\"#PHOLUS_RUN\"><code>PHOLUS_RUN</code> setting</a>.Specify when your Name-discovery scan will run. Typical setting would be <code>on_new_device</code> or <code>schedule</code> and then you specify a cron-like schedule in the <a href=\"#PHOLUS_RUN_SCHD\"><code>PHOLUS_RUN_SCHD</code>setting</a>."
},
{
"language_code": "es_es",
"string": "<a href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/pholus_scan/pholus\" target=\"_blank\" >Pholus</a> es una herramienta de rastreo para descubrir información adicional sobre los dispositivos en la red, incluido el nombre del dispositivo. Si está habilitado, ejecutará el escaneo antes de cada ciclo de escaneo de red hasta que no haya dispositivos <code>(unknown)</code> o <code>(name not found)</code>. Tenga en cuenta que puede enviar spam a la red con tráfico innecesario. Depende de la configuración de <a onclick=\"toggleAllSettings()\" href=\"#SCAN_SUBNETS\"><code>SCAN_SUBNETS</code></a>. Para un análisis programado o único, verifique la configuración de <a href=\"#PHOLUS_RUN\"><code>PHOLUS_RUN</code></a>."
}
]
},
{
"function": "CMD",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "readonly": "true" }],
"transformers": []
}
]
},
"default_value": "python3 /app/front/plugins/pholus_scan/script.py userSubnets={subnets} timeoutSec={timeout}",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Command"
},
{
"language_code": "es_es",
"string": "Comando"
}
],
"description": [
{
"language_code": "en_us",
"string": "Command to run. This should not be changed"
},
{
"language_code": "es_es",
"string": "Comando para ejecutar. Esto no debe ser cambiado"
}
]
},
{
"function": "RUN_TIMEOUT",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"default_value": 300,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Run timeout"
},
{
"language_code": "es_es",
"string": "Tiempo límite de ejecución"
}
],
"description": [
{
"language_code": "en_us",
"string": "Network scan time in seconds. Pholus scan will always run this long. The longer it runs the more device names might be resolved. Will be divided by the number of subnets."
},
{
"language_code": "es_es",
"string": "Tiempo de escaneo de red en segundos. El escaneo de Pholus siempre durará este tiempo. Cuanto más tiempo se ejecute, más nombres de dispositivos se podrán resolver. Se dividirá por el número de subredes."
}
]
},
{
"function": "RUN_SCHD",
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
]
},
"default_value": "30 3 * * *",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Schedule"
},
{
"language_code": "es_es",
"string": "Schedule"
}
],
"description": [
{
"language_code": "en_us",
"string": "Only enabled if you select <code>schedule</code> in the <a href=\"#PHOLUS_RUN\"><code>PHOLUS_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). For example entering <code>30 3 * * *</code> will run the scan at 3:30 am. Will be run NEXT time the time passes. <br/>"
},
{
"language_code": "es_es",
"string": "Solo está habilitado si selecciona <code>schedule</code> en la configuración <a href=\"#PHOLUS_RUN\"><code>PHOLUS_RUN</code></a>. Asegúrese de ingresar la programación en el formato cron correcto (por ejemplo, validar en <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). Por ejemplo, al ingresar <code>30 3 * * *</code> se ejecutará el escaneo a las 3:30 am. Se ejecutará la PRÓXIMA vez que pase el tiempo. <br/>"
}
]
},
{
"function": "DAYS_DATA",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"default_value": 30,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Retention of data"
},
{
"language_code": "es_es",
"string": "Retención de datos"
}
],
"description": [
{
"language_code": "en_us",
"string": "How many days of Pholus scan entries should be kept (globally, not device specific!) Enter <code>0</code> to disable."
},
{
"language_code": "es_es",
"string": "Cuántos días de entradas de escaneo de Pholus deben conservarse (globalmente, ¡no específico del dispositivo!). Introduzca <code>0</code> para desactivar."
}
]
},
{
"function": "WATCH",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "select",
"elementOptions": [{ "multiple": "true" }],
"transformers": []
}
]
},
"default_value": ["Watched_Value1", "Watched_Value2"],
"options": [
"Watched_Value1",
"Watched_Value2",
"Watched_Value3",
"Watched_Value4"
],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Watched"
},
{
"language_code": "es_es",
"string": "Watched"
}
],
"description": [
{
"language_code": "en_us",
"string": "Send a notification if selected values change. Use <code>CTRL + Click</code> to select/deselect. <ul> <li><code>Watched_Value1</code> is Info</li><li><code>Watched_Value2</code> is Record type</li><li><code>Watched_Value3</code> is Info </li><li><code>Watched_Value4</code> is N/A </li></ul>"
},
{
"language_code": "es_es",
"string": "Enviar una notificación si los valores seleccionados cambian. Utilice <code>CTRL + Clic</code> para seleccionar/deseleccionar. <ul> <li><code>Watched_Value1</code> es Información</li><li><code>Watched_Value2</code> es Tipo de registro</li><li><code>Watched_Value3</code> es La información </li><li><code>Watched_Value4</code> es N/A </li></ul>"
}
]
},
{
"function": "REPORT_ON",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "select",
"elementOptions": [{ "multiple": "true" }],
"transformers": []
}
]
},
"default_value": ["new"],
"options": [
"new",
"watched-changed",
"watched-not-changed",
"missing-in-last-scan"
],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Report on"
},
{
"language_code": "es_es",
"string": "Informar sobre"
}
],
"description": [
{
"language_code": "en_us",
"string": "When should notification be sent out."
},
{
"language_code": "es_es",
"string": "Cuándo debe enviarse una notificación."
}
]
}
],
"database_column_definitions": [
{
"column": "Index",
"css_classes": "col-sm-2",
"show": true,
"type": "none",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Index"
}
]
},
{
"column": "Object_PrimaryID",
"mapped_to_column": "MAC",
"css_classes": "col-sm-2",
"show": true,
"type": "device_name_mac",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "MAC"
},
{
"language_code": "es_es",
"string": "MAC"
}
]
},
{
"column": "Object_SecondaryID",
"mapped_to_column": "IP_v4_or_v6",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "IP"
},
{
"language_code": "es_es",
"string": "IP"
}
]
},
{
"column": "Watched_Value1",
"mapped_to_column": "Info",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Info"
},
{
"language_code": "es_es",
"string": "Info"
}
]
},
{
"column": "Watched_Value2",
"mapped_to_column": "Record_Type",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Type"
},
{
"language_code": "es_es",
"string": "Tipo"
}
]
},
{
"column": "Watched_Value3",
"mapped_to_column": "Value",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Info"
},
{
"language_code": "es_es",
"string": "Info"
}
]
},
{
"column": "DateTimeCreated",
"mapped_to_column": "Time",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Created"
},
{
"language_code": "es_es",
"string": "Creado"
}
]
},
{
"column": "DateTimeChanged",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Changed"
},
{
"language_code": "es_es",
"string": "Cambiado"
}
]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": true,
"type": "replace",
"default_value": "",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
},
{
"equals": "watched-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
},
{
"equals": "new",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
},
{
"equals": "missing-in-last-scan",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-question'></i></div>"
}
],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Status"
},
{
"language_code": "es_es",
"string": "Estado"
}
]
}
]
}

View File

@@ -1,39 +0,0 @@
# INFO
For anyone reading, `pholus.py` is not actively developed as this project only uses `pholus3.py`.
# Pholus
A multicast DNS and DNS Service Discovery Security Assessment Tool
It can perform recconnaisance, Denial of Service, Man in the Middle attacks
## Scan passively
Scan passively (for amount of timeout)
python pholus3.py eth0 -stimeout 60
## Discovery of available services
Sends a DNS query for PTR records with the name "_services._dns-sd._udp.<Domain>";
this yields a set of PTR records where the rdata of each PTR record is the two-label
<Service> name plus the same domain, e.g., "_http._tcp.<Domain>".
By sending such a query, we can automatically discover all the services advertised in the network.
python pholus3.py eth0 -sscan
#If you want to perform the scan both for IPv4 and IPv6:
python pholus3.py eth0 -sscan -4 -6
#You can also spoof the souce address to perform this reconnaissance in a stealthy way.
python pholus3.py eth0 -sscan -s4 192.168.2.30
## Send mdns request
python pholus3.py eth0 --request
## Perform a scan using reverse mDNS by providing a subnet
python pholus3.py eth0 -rdns_scanning 192.168.2.0/24
## Send automatically fake responses
python pholus3.py eth0 -afre -stimeout 100
## further MiTM (and other) capabilities
use --help to identify specific spoofing capabilities for MiTM purposes, eg -printer)
## Read a pcap file and pring mDNS info (no sudo/root required)
python pholus3.py ../mdns_traffic.pcap --readpcap

View File

@@ -1,23 +0,0 @@
__copyright__ = """Copyright (C) 2016 Antonios Atlasis
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
This tool may be used for legal purposes only. Users take full responsibility
for any actions performed using this tool. If these terms are not acceptable to
you, then do not use this tool.
You are encouraged to send comments, improvements or suggestions to
aatlasis@secfu.net
"""

View File

@@ -1,975 +0,0 @@
#!/usr/bin/python
from scapy.all import *
import argparse
import re
import binascii
import random
import multiprocessing
import logging
import itertools
from scapy.utils import PcapWriter
sys.setrecursionlimit(30000)
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)#supress Scapy warnings`
#####################################################
### DEFINE AN IPV4 RANGE FOR IN-ADDR.ARPA QUERIES ###
#####################################################
def ip_range(input_string):
octets = input_string.split('.')
octets.reverse()
chunks = [map(int, octet.split('-')) for octet in octets]
ranges = [range(c[0], c[1] + 1) if len(c) == 2 else c for c in chunks]
for address in itertools.product(*ranges):
address = address + ('in-addr','arpa')
yield '.'.join(map(str, address))
######################################
### OBTAIN THE SYSTEM IPV6 ADDRESS ###
######################################
def get_my_ipv6_addr(interface):
myip=""
try:
for ifaces in scapy.arch.linux.in6_getifaddr(): #in6_getifaddr() #return a list of IPs - ifaces, etc
if ifaces[2]==interface:
if not myip:
myip=ifaces[0]
elif myip[0:6] == "fe80::":
myip=ifaces[0]
return myip
except:
print "The interface",interface,"does not exist. Please, try again."
exit(0)
######################################
### OBTAIN THE SYSTEM IPV4 ADDRESS ###
######################################
def get_my_ipv4_addr(interface):
myip=""
try:
myip=scapy.arch.get_if_addr(interface)
return myip
except:
print "The interface",interface,"does not exist. Please, try again."
exit(0)
##########################
##### SNIFFER CLASS #####
##########################
class Sniffer():
def __init__ (self,filter,interface,sniffer_timeout,queue,dns,show_ttl,dos_ttl, conflict, ttl,d4, d6, target_mac, auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,hlimit,workstation,printer,googlecast,airtv,flood,flooding_timeout,flooding_interval, v4, v6):
self.filter = filter
self.interface = interface
self.sniffer_timeout=sniffer_timeout
self.queue=queue
self.dns=dns
self.show_ttl=show_ttl
self.dos_ttl=dos_ttl
self.conflict=conflict
self.ttl=ttl
self.d4=d4
self.d6=d6
self.target_mac=target_mac
self.auto_fake_responses=auto_fake_responses
self.source_IPv6=source_IPv6
self.source_IPv4=source_IPv4
self.target_mac1=target_mac1
self.target_mac2=target_mac2
self.source_mac=source_mac
self.hlimit=hlimit
self.workstation=workstation
self.printer=printer
self.airtv=airtv
self.googlecast=googlecast
self.flood=flood
self.flooding_interval=flooding_interval
self.flooding_timeout=flooding_timeout
self.v4=v4
self.v6=v6
sniff(filter=self.filter, iface=self.interface, prn=self.handler, store=0, timeout=self.sniffer_timeout)
def handler(self,packets):
ext_handler(packets,self.queue,self.dns,self.show_ttl,1,self.dos_ttl,self.conflict, self.ttl,self.interface,self.d4,self.d6,self.target_mac,self.auto_fake_responses,self.source_IPv6,self.source_IPv4,self.target_mac1,self.target_mac2,self.source_mac,self.hlimit,self.workstation,self.printer,self.googlecast,self.airtv,self.flood,self.flooding_timeout,self.flooding_interval,self.v4,self.v6)
##################################
##### OFFLINE SNIFFER CLASS #####
##################################
class Sniffer_Offline():
def __init__ (self,interface,queue,show_ttl,d4, d6, target_mac,auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,hlimit):
self.interface = interface
self.queue=queue
self.show_ttl=show_ttl
self.d4=d4
self.d6=d6
self.target_mac=target_mac
self.auto_fake_responses=auto_fake_responses
self.source_IPv6=source_IPv6
self.source_IPv4=source_IPv4
self.target_mac1=target_mac1
self.target_mac2=target_mac2
self.source_mac=source_mac
self.hlimit=hlimit
sniff(filter="udp and (port 5353 or port 53)", offline=self.interface, prn=self.handler, timeout=1)
def handler(self,packets):
ext_handler(packets,self.queue,False,self.show_ttl,1,False,False,4500,self.interface,self.d4,self.d6,self.target_mac,False,self.source_IPv6,self.source_IPv4,self.target_mac1,self.target_mac2,self.source_mac,self.hlimit,False,False,False,False,10.0,0.1,True,False)
########################################################################
### THE HANDLER THAT THE TWO SNIFFERS CALL - THIS MAKES THE MAIN JOB ###
########################################################################
def ext_handler(packets,queue,unidns,show_ttl,print_res,dos_ttl,conflict,ttl,interface,d4,d6,target_mac,auto_fake_responses,source_IPv6,source_IPv4,target_mac1,target_mac2,source_mac,hlimit,workstation,printer,googlecast,airtv,flood,flooding_timeout,flodding_interval,v4,v6):
dns_type = {12: "PTR", 28: "AAAA", 13: "HINFO",33: "SRV", 1: "A", 255: "* (ANY)", 16: "TXT", 15: "MX", 6: "SOA", 256: "URI", 5: "CNAME",39: "DNAME"}
Ether_src=packets.getlayer(Ether).src
IP_src=None
if packets.haslayer(IPv6):
IP_src=packets.getlayer(IPv6).src
elif packets.haslayer(IP):
IP_src=packets.getlayer(IP).src
res0= Ether_src + " " + IP_src
#try:
if packets.haslayer(DNS):
dns=packets.getlayer(DNS)
if (conflict or dos_ttl) and dns.ancount>0:
DNSBlocks = [ ]
DNSBlocks.append(dns.an)
#new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=dns[DNS].qdcount,ancount=dns[DNS].ancount,nscount=dns[DNS].nscount,arcount=dns[DNS].arcount,qd=dns[DNS].qd)
if conflict:
new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=0,ancount=dns[DNS].ancount,nscount=0,arcount=0,qd=dns[DNS].qd)
if target_mac:
new_packet=Ether(src=source_mac,dst=target_mac)
else:
new_packet=Ether(src=source_mac,dst=packets[Ether].dst)
if packets.haslayer(IPv6):
if d6:
new_packet=new_packet/IPv6(src=source_IPv6,dst=d6,hlim=packets[IPv6].hlim)
else:
new_packet=new_packet/IPv6(src=source_IPv6,dst=packets[IPv6].dst,hlim=packets[IPv6].hlim)
else:
if d4:
new_packet=new_packet/IP(src=source_IPv4,dst=d4,ttl=packets[IP].ttl)
else:
new_packet=new_packet/IP(src=source_IPv4,dst=packets[IP].dst,ttl=packets[IP].ttl)
for p in DNSBlocks:
if isinstance(p,DNSRR):
new_DNS_packet=new_DNS_packet/p
elif dos_ttl:
new_DNS_packet=DNS(id=dns[DNS].id,qr=dns[DNS].qr,opcode=dns[DNS].opcode,aa=dns[DNS].aa,tc=dns[DNS].tc,rd=dns[DNS].rd,ra=dns[DNS].ra,z=dns[DNS].z,ad=dns[DNS].ad,cd=dns[DNS].cd,rcode=dns[DNS].rcode,qdcount=0,ancount=dns[DNS].ancount,nscount=0,arcount=dns[DNS].arcount,qd=dns[DNS].qd)
DNSBlocks.append(dns.ar)
if target_mac:
new_packet=Ether(src=source_mac,dst=target_mac)
else:
new_packet=Ether(src=packets[Ether].src,dst=packets[Ether].dst)
if packets.haslayer(IPv6):
if d6:
new_packet=new_packet/IPv6(src=packets[IPv6].src,dst=d6,hlim=packets[IPv6].hlim)
else:
new_packet=new_packet/IPv6(src=packets[IPv6].src,dst=packets[IPv6].dst,hlim=packets[IPv6].hlim)
else:
if d4:
new_packet=new_packet/IP(src=packets[IP].src,dst=d4,ttl=packets[IP].ttl)
else:
new_packet=new_packet/IP(src=packets[IP].src,dst=packets[IP].dst,ttl=packets[IP].ttl)
for p in DNSBlocks:
if isinstance(p,DNSRR):
new_p=DNSRR()
new_p.ttl=0
new_p.rrname=p.rrname
new_p.type=p.type
new_p.rclass=p.rclass
new_p.rdlen=p.rdlen
new_p.rdata=p.rdata
new_DNS_packet=new_DNS_packet/new_p
if unidns:
new_packet=new_packet/UDP(dport=53)/new_DNS_packet
else:
new_packet=new_packet/UDP(dport=5353,sport=5353)/new_DNS_packet
for x in range(0,2):#Send each packet twice
sendp(new_packet,iface=interface)
elif auto_fake_responses or (not (dos_ttl or conflict)):
## IF THIS IS A QUERY ##
if dns.opcode==0:
res0 = res0 + " QUERY"
if dns.qdcount>0:
DNSBlocks = [ ]
DNSBlocks.append(dns.qd)
for block in DNSBlocks:
while isinstance(block,DNSQR):
dnsqr=block.getlayer(DNSQR)
### IF WE NEED TO AUTO RESPOND WITH A FAKE | DOS RESPONSE ###
if auto_fake_responses:
myttl=int(ttl)
if (("in-addr.arpa" in dnsqr.qname) or ("ip6.arpa" in dnsqr.name)) and workstation:
#ip=dnsqr.qname.split(".")
#qname = ip[3]+"."+ip[2]+"."+ip[1]+"."+ip[0]
#print "Fake IP =",qname
if unidns:
dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=0)/DNSRR(rrname=dnsqr.qname,ttl=myttl,rdata='mitsos.local',type="PTR")
else:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=0)/DNSRR(rrname=dnsqr.qname,ttl=myttl,rdata='mitsos.local',type="PTR")
elif ("_workstation._tcp." in dnsqr.qname) and workstation:
qname=dnsqr.qname
if qname.endswith('.'):
qname=qname[:-1]
if unidns:
dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=3)/DNSRR(rrname="_workstation._tcp.local",ttl=myttl,rdata="mitsos._workstation._tcp.local",type="PTR",rclass=32769)/DNSRR(rrname=qname,ttl=myttl,type="TXT",rclass=32769)/DNSRR(rrname="mitsos.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769)
else:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=3)/DNSRR(rrname="_workstation._tcp.local",ttl=myttl,rdata="mitsos._workstation._tcp.local",type="PTR",rclass=32769)/DNSRR(rrname=qname,ttl=myttl,type="TXT",rclass=32769)/DNSRR(rrname="mitsos.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769)
elif (("_pdl-datastream._tcp." in dnsqr.qname) or ("_ipp._tcp." in dnsqr.qname)) and printer:
qname=dnsqr.qname
if qname.endswith('.'):
qname=qname[:-1]
#make the SRV additional record
port=9100
port='{0:016b}'.format(port)
port="{0:0>4X}".format(int(port, 2))
weight=0
weight='{0:016b}'.format(weight)
weight="{0:0>4X}".format(int(weight, 2))
priority=0
priority='{0:016b}'.format(priority)
priority="{0:0>4X}".format(int(priority, 2))
data=priority.decode("hex")+weight.decode("hex")+port.decode("hex")
#for explanation, check http://stackoverflow.com/questions/26933016/rdata-field-of-a-dns-sd-ptr-packet-sent-via-scapy-results-in-an-unknown-extended
sublabels = "HP10000.local".split(".") + [""]
label_format = ""
for s in sublabels:
label_format = '%s%dp' % (label_format, len(s) + 1)
label_data = struct.pack(label_format, *sublabels)
srv_rrname=data+label_data
txt_record=""
rdata=['txtvers=1','qtotal=1','pdl=application/vnd.hp-PCL','ty=MyOfficejet100000','product=(Trexa gureue)','priority=0','adminur=http://'+source_IPv4]
for r in rdata:
length=hex(len(r))[2:]
#check http://code.activestate.com/recipes/576617-converting-arbitrary-size-python-integers-to-packe/
if len(r) > 255:
s=struct.Struct('I')# CHECK IT
else:
s=struct.Struct('B')
value=(len(r),)
packed_data=s.pack(*value)
txt_record=txt_record+packed_data+r
pkt= DNSRR(rrname="MyOfficejet10000._pdl-datasream._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
pkt.rdlen-=1
mylength=len(pkt.rrname)+12
dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::]
pkt=dnsrr_packet
if unidns:
dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="MyOffice10000."+qname,type="PTR",rclass=32769)/DNSRR(rrname="HP10000.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)/DNSRR(rrname="MyOffice100000."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/pkt
else:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="MyOffice10000."+qname,type="PTR",rclass=32769)/DNSRR(rrname="HP10000.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)/DNSRR(rrname="MyOffice100000."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/pkt
elif ("_googlecast._tcp." in dnsqr.qname) and googlecast:
qname=dnsqr.qname
if qname.endswith('.'):
qname=qname[:-1]
#make the SRV additional record
port=8009
port='{0:016b}'.format(port)
port="{0:0>4X}".format(int(port, 2))
weight=0
weight='{0:016b}'.format(weight)
weight="{0:0>4X}".format(int(weight, 2))
priority=0
priority='{0:016b}'.format(priority)
priority="{0:0>4X}".format(int(priority, 2))
data=priority.decode("hex")+weight.decode("hex")+port.decode("hex")
sublabels = "32799abf-18ea-631d-62ae-390515bb47c5.local".split(".") + [""]
label_format = ""
for s in sublabels:
label_format = '%s%dp' % (label_format, len(s) + 1)
label_data = struct.pack(label_format, *sublabels)
srv_rrname=data+label_data
txt_record=""
rdata=['id=32799abf-18ea-631d-62ae-390515bb47c5','rm=','ve=05','md=Chromecast Ultra','ic=/setup/icon.png','fn=LivingRoom','ca=4101','st=0','bs=FA8FCA7EO948','rs=0']
for r in rdata:
length=hex(len(r))[2:]
if len(r) > 255:
s=struct.Struct('I')# CHECK IT
else:
s=struct.Struct('B')
value=(len(r),)
packed_data=s.pack(*value)
txt_record=txt_record+packed_data+r
pkt= DNSRR(rrname="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
pkt.rdlen-=1
mylength=len(pkt.rrname)+12
dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::]
pkt=dnsrr_packet
if unidns:
dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="PTR")/pkt/DNSRR(rrname="ChromecastUltra."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="32799abf-18ea-631d-62ae-390515bb47c5.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)
else:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1,arcount=3)/DNSRR(rrname=qname,ttl=myttl,rdata="Chromecast-Ultra-32799abf-18ea-631d-62ae-390515bb47c5."+qname,type="PTR")/pkt/DNSRR(rrname="ChromecastUltra."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="32799abf-18ea-631d-62ae-390515bb47c5.local",ttl=myttl,type="A",rclass=32769,rdata=source_IPv4)
elif ("_airplay." in dnsqr.qname) and airtv:
qname=dnsqr.qname
if qname.endswith('.'):
qname=qname[:-1]
txt_record=""
rdata=['model=J33AP']
for r in rdata:
length=hex(len(r))[2:]
if len(r) > 255:
s=struct.Struct('I')# CHECK IT
else:
s=struct.Struct('B')
value=(len(r),)
packed_data=s.pack(*value)
txt_record=txt_record+packed_data+r
pkt= DNSRR(rrname="Apple TV-mitsos._device-info._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
pkt.rdlen-=1
mylength=len(pkt.rrname)+12
dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::]
pkt=dnsrr_packet
txt_record=""
rdata=['deviceid=9C:20:7B:AD:6B:E4','features=0x5A7FFFF7,0xE','flags=0x44','model=AppleTV3,1','pk=ab69a89af6fe7ff1fd803f74c6681d786aff1e6bc52087e49cc8f22585916ccd','pi=a62a851e-d024-439f-9ee0-01ef1b23d6ca','srcvers=220.68','vv=2']
for r in rdata:
length=hex(len(r))[2:]
if len(r) > 255:
s=struct.Struct('I')# CHECK IT
else:
s=struct.Struct('B')
value=(len(r),)
packed_data=s.pack(*value)
txt_record=txt_record+packed_data+r
pkt2= DNSRR(rrname="Apple TV-mitsos._airplay._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
pkt2.rdlen-=1
mylength=len(pkt2.rrname)+12
dnsrr_packet=str(pkt2)[0:mylength]+str(pkt2)[mylength+1::]
pkt2=dnsrr_packet
txt_record=""
rdata=['cn=0,1,2,3','da=true','et=0,3,5','ft=0x5A7FFFF7,0xE','md=0,1,2','am=AppleTV3,1','pk=ab69a89af6fe7ff1fd803f74c6681d786aff1e6bc52087e49cc8f22585916ccd','sf=0x44','tp=UDP','vn=65537','vs=220.68','vv=2']
for r in rdata:
length=hex(len(r))[2:]
if len(r) > 255:
s=struct.Struct('I')# CHECK IT
else:
s=struct.Struct('B')
value=(len(r),)
packed_data=s.pack(*value)
txt_record=txt_record+packed_data+r
pkt3= DNSRR(rrname="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
pkt3.rdlen-=1
mylength=len(pkt3.rrname)+12
dnsrr_packet=str(pkt3)[0:mylength]+str(pkt3)[mylength+1::]
pkt3=dnsrr_packet
#make the SRV additional record
port=7000
port='{0:016b}'.format(port)
port="{0:0>4X}".format(int(port, 2))
weight=0
weight='{0:016b}'.format(weight)
weight="{0:0>4X}".format(int(weight, 2))
priority=0
priority='{0:016b}'.format(priority)
priority="{0:0>4X}".format(int(priority, 2))
data=priority.decode("hex")+weight.decode("hex")+port.decode("hex")
sublabels = "Apple-TV.local".split(".") + [""]
label_format = ""
for s in sublabels:
label_format = '%s%dp' % (label_format, len(s) + 1)
label_data = struct.pack(label_format, *sublabels)
srv_rrname=data+label_data
if unidns:
dns_packet=UDP(dport=53)
else:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=3,arcount=6)/DNSRR(rrname=qname,ttl=myttl,rdata="Apple TV-mitsos._device-info."+qname,type="PTR")/pkt/DNSRR(rrname="_raop._tcp.local",ttl=myttl,rdata="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",type="PTR")/pkt2/pkt3/DNSRR(rrname="Apple TV-mitsos."+qname,ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="9C207BAD6BE4@Apple TV-mitsos._raop._tcp.local",ttl=myttl,type="SRV",rclass=32769,rdata=srv_rrname)/DNSRR(rrname="Apple-TV.local",ttl=myttl,rdata=source_IPv4,type="A",rclass=32769)/DNSRR(rrname="Apple-TV.local",ttl=myttl,rdata=source_IPv6,type="AAAA",rclass=32769)
else:
qname=dnsqr.qname
if qname.endswith('.'):
qname=qname[:-1]
print "Query Name = ",qname," Type=",dnsqr.qtype
if unidns:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1)/DNSRR(rrname=qname,ttl=myttl,rdata=source_IPv4,type="A")
else:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0,ancount=1)/DNSRR(rrname=qname,ttl=myttl,rdata=source_IPv4,type="A")
send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,False,10.0,0.1)#CHANGE DEFAULT VALUES
#CHANGE: SEND IT TWICE, IF NOT FLOOD
#if not flood:
# for x in range(0,10):#Send each packet twice
# send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,False,10.0,0.1)#CHANGE DEFAULT VALUES
### END "IF WE NEED TO AUTO RESPOND WITH A FAKE RESPONSE
### NEXT LINES ARE ONLY USED TO PRINT RESULTS ###
if dnsqr.qclass==32769:
res = res0 + " Question: "+dnsqr.qname + " " + dns_type[dnsqr.qtype] +" QU Class:IN"
elif dnsqr.qclass==1:
res = res0 + " Question: "+dnsqr.qname + " "+ dns_type[dnsqr.qtype] + " QM Class:IN"
elif dnsqr.qclass==255:
res = res0 + " Question: "+dnsqr.qname + " "+ dns_type[dnsqr.qtype] + " QM Class:ANY"
else:
print "DNSQR:"
print "-----"
print dnsqr.show()
print "DEBUGGING IS NEEDED"
exit(0)
if print_res==1:
print res
queue.put(res)
block = block.payload
if dns.arcount>0:
DNSBlocks = [ ]
DNSBlocks.append(dns.ar)
for block in DNSBlocks:
if block.haslayer(DNSRROPT):
while isinstance(block,DNSRROPT):#Somewhat equivalent: while not isinstance(an, NoPayload):
dnsrropt=block.getlayer(DNSRROPT)
#print "DNS OPT Resource Record"
if dnsrropt.rrname == ".":
rrname = "<Root>"
else:
rrname = dnsrropt.rrname
if dnsrropt.type==41:
ARtype="OPT"
else:
ARtype=str(dnsrropt.type)
res = res0 + " Additional_Record: " + rrname + " " + ARtype
if dnsrropt.haslayer(EDNS0TLV):
edns0tlv=dnsrropt.getlayer(EDNS0TLV)
if edns0tlv.optcode==4:
optcode="Reserved"
else:
optcode=str(edns0tlv.optcode)
res = res + " EDNS0TLV: " + optcode + " " + str(edns0tlv.optdata).encode("HEX")
if print_res==1:
print res
queue.put(res)
block = block.payload
elif block.haslayer(DNSRR):
while isinstance(block,DNSRR):#Somewhat equivalent: while not isinstance(an, NoPayload):
dnsrr=block.getlayer(DNSRR)
if dnsrr.rclass==32769:
res = res0 + " DNS Resource Record: "+ dnsrr.rrname + " " + dns_type[dnsrr.type] +" QU Class:IN "+dnsrr.rdata
elif dnsrr.rclass==1:
res = res0 + " DNS Resource Record: "+dnsrr.rrname + " "+ dns_type[dnsrr.type] + " QM Class:IN "+dnsrr.rdata
elif dnsrr.qclass==255:
res = res0 + " Question: "+dnsrr.qname + " "+ dns_type[dnsrr.qtype] + " QM Class:ANY"
else:
print "DNSRR:"
print "-----"
print dnsrr.show()
print "DEBUGGING IS NEEDED HERE"
exit(0)
if dnsrr.type==33:#SRV Record
priority=str(dnsrr.rdata)[0].encode("HEX")+str(dnsrr.rdata)[1].encode("HEX")
weight=str(dnsrr.rdata)[2].encode("HEX")+str(dnsrr.rdata)[3].encode("HEX")
port_number=str(dnsrr.rdata)[4].encode("HEX")+str(dnsrr.rdata)[5].encode("HEX")
res = res0 + " Additional_Record: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + str(dnsrr.rclass) + " priority="+str(int(priority,16))+" weight="+str(int(weight,16))+" port="+str(int(port_number,16))+" target="+str(dnsrr.rdata)[6::]
else:
rdata=dnsrr.rdata
if "._tcp." not in rdata and "._udp." not in rdata:
if rdata == "_dhnap.":
rdata=dnsrr.rdata+"_tcp."
res = res0 + " Additional_Record: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + str(dnsrr.rclass) + ' "' +rdata+'"'
if show_ttl:
res = res + " TTL:"+str(dnsrr.ttl)
if print_res==1:
print res
queue.put(res)
block = block.payload
if dns.ancount>0:
DNSBlocks = [ ]
DNSBlocks.append(dns.an)
for block in DNSBlocks:
while isinstance(block,DNSRR):
dnsrr=block.getlayer(DNSRR)
if dnsrr.rclass==1:
rclass="Class:IN"
else:
rclass="Class:"+str(dnsrr.rclass)
rdata=dnsrr.rdata
if dnsrr.type==33:#SRV Record
priority=str(dnsrr.rdata)[0].encode("HEX")+str(dnsrr.rdata)[1].encode("HEX")
weight=str(dnsrr.rdata)[2].encode("HEX")+str(dnsrr.rdata)[3].encode("HEX")
port_number=str(dnsrr.rdata)[4].encode("HEX")+str(dnsrr.rdata)[5].encode("HEX")
res = res0 + " Answer: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + rclass + " priority="+str(int(priority,16))+" weight="+str(int(weight,16))+" port="+str(int(port_number,16))+" target="+str(dnsrr.rdata)[6::]
else:
if "._tcp." not in rdata and "._udp." not in rdata:
if rdata == "_dhnap.":
rdata=dnsrr.rdata+"_tcp."
res = res0 + " Answer: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + rclass + ' "' +rdata+'"'
if show_ttl:
res = res + " TTL:"+str(dnsrr.ttl)
if print_res==1:
print res
queue.put(res)
block = block.payload
if dns.nscount>0:
DNSBlocks = [ ]
DNSBlocks.append(dns.ns)
for block in DNSBlocks:
while isinstance(block,DNSRR):
dnsrr=block.getlayer(DNSRR)
if dnsrr.rclass==1:
rclass="Class:IN"
else:
rclass="Class:"+str(dnsrr.rclass)
res = res0 + " Auth_NS: "+dnsrr.rrname + " " + dns_type[dnsrr.type]+" " + rclass + ' "' +dnsrr.rdata+'"'
if show_ttl:
res = res + " TTL:"+str(dnsrr.ttl)
if print_res==1:
print res
queue.put(res)
block = block.payload
else:
print "not a DNS Query", dns.summary()
#except Exception,e:
# print "Exception",str(e),packets.summary()
########################################
########### REQUEST FUNCTION ###########
########################################
def requests(interface,v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,d4,d6,hlimit,unidns,domain,query,types_of_queries,add_domain,query_class,flood,flooding_interval,flooding_timeout):
if add_domain:
print "Sending mdns requests"
domain_list = domain.split(",")
query_list = query.split(",")
if add_domain:
the_query=query_list[0]+"."+domain_list[0]
else:
types_of_queries="ALL"#implies that first a generic scan has beem performed
the_query=query_list[0]
dns_query=DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class))
for j in range(1,len(query_list)):
if add_domain:
the_query=query_list[j]+"."+domain_list[0]
else:
the_query=query_list[j]
dns_query=dns_query/DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class))
if add_domain:
for i in range(1,len(domain_list)):
for j in query_list:
the_query=j+"."+domain_list[i]
dns_query=dns_query/DNSQR(qname=the_query,qtype=types_of_queries,qclass=int(query_class))
if unidns:
dns_packet=UDP(dport=53)/DNS(qr=0,qd=dns_query)
else:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=0,qd=dns_query)
send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,d4,source_IPv6,d6,interface,hlimit,dns_packet,flood,flooding_timeout,flooding_interval)
########################################
########################################
########################################
def send_packets(v4,v6,source_mac,target_mac1,target_mac2,source_IPv4,dst_ipv4,source_IPv6,dst_ipv6,interface,hlimit,payload,flood,flooding_timeout,flooding_interval):
if v4 or not v6:
packet=IP(src=source_IPv4,dst=dst_ipv4,ttl=hlimit,proto="udp")/payload
if len(packet)>1500:
frags=fragment(packet)
packets=[]
for frag in frags:
pkt1=Ether(src=source_mac,dst=target_mac1)/frag
packets.append(pkt1)
if flood:
counter=0.0
print "Stop flooding after ",flooding_timeout," sec."
while(counter<float(flooding_timeout)):
for packt in packets:
sendp(pakt,iface=interface)
counter+=float(flooding_interval)
time.sleep(float(flooding_interval))
else:
for pakt in packets:
sendp(pakt,iface=interface)
else:
pkt1=Ether(src=source_mac,dst=target_mac1)/packet
if flood:
counter=0.0
print "Stop flooding after ",flooding_timeout," sec."
while(counter<float(flooding_timeout)):
sendp(pkt1,iface=interface)
counter+=float(flooding_interval)
time.sleep(float(flooding_interval))
else:
sendp(pkt1,iface=interface)
if v6:
packet=IPv6(src=source_IPv6,dst=dst_ipv6,hlim=hlimit)/payload
if len(packet)>1500:
frags2=fragment6(IPv6(src=source_IPv6,dst=dst_ipv6,hlim=hlimit)/IPv6ExtHdrFragment()/payload,1480)
packets=[]
for frag2 in frags2:
pkt2=Ether(src=source_mac,dst=target_mac2)/frag2
packets.append(pkt2)
if flood:
counter=0.0
print "Stop flooding after ",flooding_timeout," sec."
while(counter<float(flooding_timeout)):
for packt in packets:
sendp(pakt,iface=interface)
counter+=float(flooding_interval)
time.sleep(float(flooding_interval))
else:
for packt in packets:
sendp(pkt2,iface=interface)
else:
pkt2=Ether(src=source_mac,dst=target_mac2)/packet
if flood:
counter=0.0
print "Stop flooding after ",flooding_timeout," sec."
while(counter<float(flooding_timeout)):
sendp(pkt2,iface=interface)
counter+=float(flooding_interval)
time.sleep(float(flooding_interval))
else:
pkt2=Ether(src=source_mac,dst=target_mac2)/packet
sendp(pkt2,iface=interface)
########################################
########################################
########################################
def main():
parser = argparse.ArgumentParser(version='0.1',description='Pholus: An mdns testing tool')
parser.add_argument('interface', action="store", help="the network interface to use, or the file (if -rpcap switch is used).")
parser.add_argument('-rpcap','--readpcap', action="store_true", dest="rpcap", default=False, help="Read packets from a pcap file")
parser.add_argument('-dns', '--DNS', action="store_true", dest="dns", default=False, help="Use unicast DNS request; remember to define target address")
parser.add_argument('-4', '--v4', action="store_true", dest="v4", default=False, help="Send mdns request/response using IPv4.")
parser.add_argument('-6', '--v6', action="store_true", dest="v6", default=False, help="Send mdns request/response using IPv6.")
parser.add_argument('-hl', '--hlimit', action="store", dest="hlimit", default=255, type=int, help="The TTL (for IPv4) or Hop Limit (for IPv6).")
parser.add_argument('-rq', '--request', action="store_true", dest="request", default=False, help="Send mdns request.")
parser.add_argument('-rp', '--response', action="store_true", dest="response", default=False, help="Send unsolicired mdns response.")
parser.add_argument('-ll', '--link_local', action="store_true", dest="link_local", default=False, help="As IPv6 source address, use link local address instead of global address")
parser.add_argument('-s6', '--source_addr_v6', action="store", dest="source6", default=False, help="The IPv6 address of the sender (if you want to spoof it).")
parser.add_argument('-s4', '--source_addr_v4', action="store", dest="source4", default=False, help="The IPv4 address of the sender (if you want to spoof it).")
parser.add_argument('-d6', '--dest_addr_v6', action="store", dest="d6", default="ff02::fb", help="The IPv6 destination address of the mdns packets")
parser.add_argument('-d4', '--dest_addr_v4', action="store", dest="d4", default="224.0.0.251", help="The IPv4 destination address of the mdns packets")
parser.add_argument('-qtype', '--gtype', action="store", dest="qtype", default="ALL", help="qtype of mDNS query (e.g. PTR, etc.)")
parser.add_argument('-query', '--query', action="store", dest="query", default="_services._dns-sd._udp", help="Query/ies of the mDNS packet; it can be a comma-separated list")
parser.add_argument('-qclass', '--query_class', action="store", dest="q_class", default=1, help="The class of the query (e.g. QU vs QM)")
parser.add_argument('-qu', '--qu', action="store_true", dest="qu", default=False, help="Send a Uniqast (QU) question in Queries")
parser.add_argument('-dns_response', '--dns_response', action="store", dest="dns_response", default="", help="Responses of the mDNS packet; it can be a comma-separated list")
parser.add_argument('-sttl', '--show_ttl', action="store_true", dest="show_ttl", default=False, help="Display the DNS TTL information of the received packets.")
parser.add_argument('-rm', '--random-mac', action="store_true", dest="random_mac", default=False, help="Randomise the source MAC address.")
parser.add_argument('-tm', '--target_mac', action="store", dest="target_mac", default=False, help="The mac address of the target (optional).")
parser.add_argument('-sm', '--source_mac', action="store", dest="source_mac", default=False, help="The mac address of the source, e.g. for spoofing purposes (optional).")
parser.add_argument('-stimeout','--sniffer_timeout', action="store", dest="sniffer_timeout", default=5, help="The timeout (in seconds) when the integrated sniffer (IF used) will exit automatically.")
parser.add_argument('-domain','--domain', action="store", dest="domain", default="local", help="The domain to query; default:local")
parser.add_argument('-rdns_scanning', '--reverse_dns_scanning', action="store", dest="rdns_scanning", default=False, help="The IPv4 address ranges that we want to scan, e.g. 192.168-169.2-3.1-255")
parser.add_argument('-sscan', '--service_scanning', action="store_true", dest="service_scan", default=False, help="A service scan, initiated using _services._dns-sd._udp.local")
parser.add_argument('-ttl','--define_ttl', action="store", dest="ttl", default=4500, help="The TTL to be used in the DNS messages.")
parser.add_argument('-dos_ttl', '--DoS_zeroizing_ttl', action="store_true", dest="dos_ttl", default=False, help="DoS hosts by zeroizing the TTL value of the DNS packets.")
parser.add_argument('-conflict', '--send_conflicting_questions', action="store_true", dest="conflict", default=False, help="Send conflicting questions with the received ones.")
parser.add_argument('-afre', '--auto_fake_responses', action="store_true", dest="auto_fake_responses", default=False, help="Send automatically fake resposnes")
parser.add_argument('-printer', '--printer', action="store_true", dest="printer", default=False, help="fake responses for printer")
parser.add_argument('-airplay', '--airplay', action="store_true", dest="airtv", default=False, help="fake responses for AirPlay TV")
parser.add_argument('-workstation', '--workstation', action="store_true", dest="workstation", default=False, help="fake responses for workstation")
parser.add_argument('-googlecast', '--googlecast', action="store_true", dest="googlecast", default=False, help="fake responses for googlecast")
parser.add_argument('-fl','--flood', action="store_true", dest="flood", default=0, help="flood the targets")
parser.add_argument('-flooding-interval','--interval-of-flooding', action="store", dest="flooding_interval", default=1, help="the interval between packets when flooding the targets")
parser.add_argument('-ftimeout','--flooding_timeout', action="store", dest="flooding_timeout", default=200, help="The time (in seconds) to flood your target.")
values = parser.parse_args()
if values.rpcap:
print "Read packets from a pcap file"
if not os.path.isfile(values.interface):
print '[-] ' + values.interface + ' does not exist'
exit(0)
elif not os.access(values.interface, os.R_OK):
print '[-] ' + values.interface + ' access is denied'
exit(0)
print "Press Ctrl-C to exit and print the results"
q = multiprocessing.Queue()
#def __init__ (self,interface,queue,show_ttl,d4, d6, target_mac,auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,hlimit):
pr = multiprocessing.Process(target=Sniffer_Offline, args=(values.interface,q,values.show_ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,values.source6,values.source4,values.target_mac,values.target_mac,values.source_mac,values.hlimit))
#pr.daemon = True
pr.start()
pr.join()
results=[]
while not q.empty():
results.append(q.get())
myset=set(results)
results=list(myset)
results.sort()
print "\n*********************************************RESULTS*********************************************"
for r in results:
print r
exit(0)
else:
#####################################LETS DO SOME CHECKS FIRST TO SEE IF WE CAN WORK####################
if os.geteuid() != 0:
print "You must be root to run this script."
exit(1)
conf.verb=0
#########################################DEFINE SOURCE ADDRESSES########################################
source_mac=None
if values.random_mac:
source_mac = ':'.join(map(lambda x: "%02x" % x, [ 0x00, 0x16, 0x3E, random.randint(0x00, 0x7F), random.randint(0x00, 0xFF), random.randint(0x00, 0xFF) ]))
elif values.source_mac:
source_mac=values.source_mac
else:
try:
source_mac=get_if_hwaddr(values.interface)
except:
print "interface ",values.interface," does not exist"
exit(0)
if values.qu:
q_class=32769
else:
q_class=int(values.q_class)
if not values.source6:
source_IPv6=None
source_IPv6=get_my_ipv6_addr(values.interface)#Selects global IPv6 address, if available
if not source_IPv6 or values.random_mac or values.link_local: #If a random MAC is used, construct the corresponding IPv6 Link Local Address
mac_value = int(source_mac.translate(None, ' .:-'), 16)
# Split out the bytes that slot into the IPv6 address
# XOR the most significant byte with 0x02, inverting the
# Universal / Local bit
#thanks to http://codereview.stackexchange.com/questions/90263/network-mac-address-conversion-to-ipv6-link-local-address/90264
high2 = mac_value >> 32 & 0xffff ^ 0x0200
high1 = mac_value >> 24 & 0xff
low1 = mac_value >> 16 & 0xff
low2 = mac_value & 0xffff
source_IPv6 = 'fe80::{:04x}:{:02x}ff:fe{:02x}:{:04x}'.format(high2, high1, low1, low2)
else:
source_IPv6=values.source6
if not values.source4:
source_IPv4=get_my_ipv4_addr(values.interface)
else:
source_IPv4=values.source4
print "source MAC address:",source_mac,"source IPv4 Address:",source_IPv4,"source IPv6 address:",source_IPv6
#########################################################################################################
if values.target_mac:
target_mac1=values.target_mac
target_mac2=values.target_mac
else:
target_mac2="33:33:00:00:00:02"
target_mac1="01:00:5e:00:00:fb"
############################################Sniffing requirements########################################
q = multiprocessing.Queue()
if values.dos_ttl or values.auto_fake_responses:
if values.auto_fake_responses:
print "Send fake responses to requests"
if values.target_mac:
myfilter = "not ether src " + source_mac + " and not ether dst " + values.target_mac +" and udp and port 5353"
else:
myfilter = "not ether src " + source_mac + " and udp and port 5353"
elif values.target_mac:
print "Performing implicit DoS by sending automated spoofed DNS Answers with TTL=0"
myfilter = "not ether dst " + values.target_mac + " and udp and port 5353"
else:
print "Performing implicit DoS by sending automated spoofed DNS Answers with TTL=0"
myfilter = "udp and port 5353"
print "Sniffer filter is:",myfilter
print "I will sniff for",values.sniffer_timeout,"seconds, unless interrupted by Ctrl-C"
print "Press Ctrl-C to exit"
try:
Sniffer(myfilter, values.interface, float(values.sniffer_timeout),q,values.dns,values.show_ttl, values.dos_ttl, values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6)
except KeyboardInterrupt:
print "Exiting on user's request"
exit(0)
exit(0)
myfilter = "not ether src " + source_mac + " and udp and port 5353"
print "Sniffer filter is:",myfilter
print "I will sniff for",values.sniffer_timeout,"seconds, unless interrupted by Ctrl-C"
pr = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q,values.dns,values.show_ttl, values.dos_ttl, values.conflict, values.ttl,values.d4,values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2, source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6))
pr.daemon = True
pr.start()
print "------------------------------------------------------------------------"
time.sleep(1)#to make sure than sniffer has started before we proceed, otherwise you may miss some traffic
##########################################################################################################
if values.request:
requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,values.query,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
elif values.response:
#qr=1=>Response, aa=1=>Server is an authority for the domain, rd=0=> Do not query recursively
if values.dns:
dns_packet=UDP(dport=53)/DNS(qr=1,aa=1,rd=0)
#dns_packet=UDP(dport=53)/DNS(opcode=0,qr=1,aa=1,qdcount=0,ancount=len(responses),qd=dns_response)
else:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=1,aa=1,rd=0)
#dns_packet=UDP(sport=5353,dport=5353)/DNS(opcode=0,qr=1,aa=1,qdcount=0,ancount=len(responses),qd=dns_response)
responses = values.dns_response.split(",")
no_of_answers=0
no_of_additional_records=0
dnsar=None
for r in responses:
values_of_responses = r.split("/")
dnsrr=DNSRR()
port=0
weight=0
priority=0
no_of_answers+=1
this_is_a_dns_ar=False
#rdata=None
rdata=[]
for dns_values in values_of_responses:
#print dns_values
value = dns_values.split("==")
if value[0]=="Type":
dnsrr.type=value[1]
elif value[0]=="Name":
dnsrr.rrname=value[1]
elif value[0]=="Target":
rdata.append(value[1])
#rdata=value[1]
elif value[0]=="TTL":
dnsrr.ttl=int(value[1])
elif value[0]=="Flush":
if value[1]=="True":
dnsrr.rclass=32769
elif value[0]=="Priority":
priority=int(value[1])
elif value[0]=="Weight":
weight=int(value[1])
elif value[0]=="Port":
port=int(value[1])
elif value[0]=="AR":
if value[1]=="True":
no_of_additional_records+=1
this_is_a_dns_ar=True
if dnsrr.type==33:
port='{0:016b}'.format(port)
port="{0:0>4X}".format(int(port, 2))
weight='{0:016b}'.format(weight)
weight="{0:0>4X}".format(int(weight, 2))
priority='{0:016b}'.format(priority)
priority="{0:0>4X}".format(int(priority, 2))
data=priority.decode("hex")+weight.decode("hex")+port.decode("hex")
#http://stackoverflow.com/questions/26933016/rdata-field-of-a-dns-sd-ptr-packet-sent-via-scapy-results-in-an-unknown-extended
sublabels = rdata[0].split(".") + [""]
label_format = ""
for s in sublabels:
label_format = '%s%dp' % (label_format, len(s) + 1)
label_data = struct.pack(label_format, *sublabels)
dnsrr.rdata=data+label_data
elif dnsrr.type==16:
txt_record=""
for r in rdata:
length=hex(len(r))[2:]
#check http://code.activestate.com/recipes/576617-converting-arbitrary-size-python-integers-to-packe/
if len(r) > 255:
s=struct.Struct('I')# CHECK IT
else:
s=struct.Struct('B')
value=(len(r),)
packed_data=s.pack(*value)
txt_record=txt_record+packed_data+r
dnsrr.rdata=txt_record
#pkt= DNSRR(rrname="MyOfficejet10000._pdl-datasream._tcp.local",type="TXT",ttl=myttl,rclass=32769,rdata=txt_record)
#pkt.rdlen-=1
#mylength=len(pkt.rrname)+12
#dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::]
#pkt=dnsrr_packet
##################
pkt=dnsrr
pkt.rdlen-=1
mylength=len(pkt.rrname)+12
dnsrr_packet=str(pkt)[0:mylength]+str(pkt)[mylength+1::]
##################
#mylength=len(dnsrr.rrname)+12
#dnsrr_packet=str(dnsrr)[0:mylength]+str(dnsrr)[mylength+1::]
dnsrr=dnsrr_packet
else:
#print rdata
dnsrr.rdata=rdata[0]
if not this_is_a_dns_ar:
dns_packet=dns_packet/dnsrr
else:
if not dnsar:
dnsar=dnsrr
else:
dnsar=dnsar/dnsrr
if dnsar:
dns_packet=dns_packet/dnsar
dns_packet[DNS].ancount=no_of_answers-no_of_additional_records
dns_packet[DNS].arcount=no_of_additional_records
send_packets(values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,values.d4,source_IPv6,values.d6,values.interface,values.hlimit,dns_packet,values.flood,values.flooding_timeout,values.flooding_interval)
elif values.rdns_scanning:
dns_query=None
for address in ip_range(values.rdns_scanning):
the_query=address
if not dns_query:
dns_query=DNSQR(qname=the_query,qtype=values.qtype,qclass=values.q_class)
else:
dns_query=dns_query/DNSQR(qname=the_query,qtype=values.qtype,qclass=values.q_class)
if values.dns:
dns_packet=UDP(dport=53)/DNS(qr=0,qd=dns_query)
else:
dns_packet=UDP(sport=5353,dport=5353)/DNS(qr=0,qd=dns_query)
send_packets(values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,values.d4,source_IPv6,values.d6,values.interface,values.hlimit,dns_packet,values.flood,values.flooding_timeout,values.flooding_interval)
elif values.service_scan:
requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,values.query,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
############################################################################################
############################################################################################
if pr:
try:
pr.join()
except KeyboardInterrupt:
print "Exiting on user's request"
exit(0)
#### AFTER EXITING, PRINT THE RESULTS ####
results=[]
while not q.empty():
results.append(q.get())
if values.rdns_scanning:
targets=[]
q2 = multiprocessing.Queue()
pr2 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q2,values.dns,values.show_ttl, values.dos_ttl,values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6))
pr2.daemon = True
pr2.start()
time.sleep(1) #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic
for r in results:
r2=r.split(" ")
service=r2[7].strip('"')
if service.endswith('local.'):
service=service[:-6]
if service.endswith('.'):
service=service[:-1]
if (r2[1],service) not in targets:
targets.append((r2[1],service))
requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
if pr2:
try:
pr2.join()
except KeyboardInterrupt:
print "Exiting on user's request"
while not q2.empty():
results.append(q2.get())
elif values.service_scan:
targets=[]
q2 = multiprocessing.Queue()
pr2 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q2,values.dns,values.show_ttl, values.dos_ttl,values.conflict, values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6))
pr2.daemon = True
pr2.start()
time.sleep(1) #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic
for r in results:
r2=r.split(" ")
service=r2[7].strip('"')[:-1]
if (r2[1],service) not in targets:
print (r2[1],service)
targets.append((r2[1],service))
requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
if pr2:
try:
pr2.join()
except KeyboardInterrupt:
print "Exiting on user's request"
while not q2.empty():
results.append(q2.get())
targets2=[]
q3 = multiprocessing.Queue()
pr3 = multiprocessing.Process(target=Sniffer, args=(myfilter, values.interface, float(values.sniffer_timeout),q3,values.dns,values.show_ttl, values.dos_ttl, values.conflict,values.ttl,values.d4, values.d6, values.target_mac, values.auto_fake_responses,source_IPv6, source_IPv4, target_mac1, target_mac2,source_mac,values.hlimit,values.workstation,values.printer,values.googlecast,values.airtv,values.flood,values.flooding_timeout,values.flooding_interval,values.v4,values.v6))
pr3.daemon = True
pr3.start()
time.sleep(1) #to make sure than sniffer has started before we proceed, otherwise you may miss some traffic
for r in results:
r2=r.split(" ")
service=r2[4]
if service.endswith('local.'):
service=service[:-6]
if service.endswith('.'):
service=service[:-1]
if (r2[1],service) not in targets and (r2[1],service) not in targets2 and "_services._dns-sd._udp" not in service:
targets2.append((r2[1],service))
requests(values.interface,values.v4,values.v6,source_mac,target_mac1,target_mac2,source_IPv4,source_IPv6,values.d4,values.d6,values.hlimit,values.dns,values.domain,service,values.qtype,True,q_class,values.flood,values.flooding_interval,values.flooding_timeout)
if pr3:
try:
pr3.join()
except KeyboardInterrupt:
print "Exiting on user's request"
while not q3.empty():
results.append(q3.get())
print "\n*********************************************RESULTS*********************************************"
for r in results:
print r
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@@ -1,193 +0,0 @@
#!/usr/bin/env python
import os
import pathlib
import argparse
import sys
import re
import base64
import subprocess
from time import strftime
# Register NetAlertX directories
INSTALL_PATH="/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from logger import mylog
from plugin_helper import Plugin_Object, Plugin_Objects
from helper import timeNowTZ, get_setting_value
from const import logPath, applicationPath
import conf
from pytz import timezone
# Make sure the TIMEZONE for logging is correct
conf.tz = timezone(get_setting_value('TIMEZONE'))
CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
LOG_FILE = os.path.join(CUR_PATH, 'script.log')
RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
fullPholusPath = os.path.join(CUR_PATH, 'pholus/pholus3.py')
pluginName = 'PHOLUS'
def main():
# the script expects a parameter in the format of userSubnets=subnet1,subnet2,...
parser = argparse.ArgumentParser(description='Import devices from settings')
parser.add_argument('userSubnets', nargs='+', help="list of subnets with options")
parser.add_argument('timeoutSec', nargs='+', help="timeout")
values = parser.parse_args()
# Assuming Plugin_Objects is a class or function that reads data from the RESULT_FILE
# and returns a list of objects called 'devices'.
plugin_objects = Plugin_Objects(RESULT_FILE)
# Print a message to indicate that the script is starting.
mylog('verbose',[f'[{pluginName}] In script'])
# Assuming 'values' is a dictionary or object that contains a key 'userSubnets'
# which holds a list of user-submitted subnets.
# Printing the userSubnets list to check its content.
mylog('verbose',[f'[{pluginName}] Subnets: ', values.userSubnets])
mylog('verbose',[f'[{pluginName}] len Subnets: ', len(values.userSubnets)])
# Extract the base64-encoded subnet information from the first element of the userSubnets list.
# The format of the element is assumed to be like 'userSubnets=b<base64-encoded-data>'.
userSubnetsParamBase64 = values.userSubnets[0].split('userSubnets=b')[1]
timeoutSec = values.timeoutSec[0].split('=')[1]
# Printing the extracted base64-encoded subnet information.
mylog('verbose', [f'[{pluginName}] { userSubnetsParamBase64 }'])
mylog('verbose', [f'[{pluginName}] { timeoutSec }'])
# Decode the base64-encoded subnet information to get the actual subnet information in ASCII format.
userSubnetsParam = base64.b64decode(userSubnetsParamBase64).decode('ascii')
# Print the decoded subnet information.
mylog('verbose', [f'[{pluginName}] userSubnetsParam { userSubnetsParam } '])
# Check if the decoded subnet information contains multiple subnets separated by commas.
# If it does, split the string into a list of individual subnets.
# Otherwise, create a list with a single element containing the subnet information.
if ',' in userSubnetsParam:
subnets_list = userSubnetsParam.split(',')
else:
subnets_list = [userSubnetsParam]
# Execute the ARP scanning process on the list of subnets (whether it's one or multiple subnets).
# The function 'execute_arpscan' is assumed to be defined elsewhere in the code.
all_entries = execute_pholus_scan(subnets_list, timeoutSec)
for entry in all_entries:
plugin_objects.add_object(
# "Info", "Time", "MAC", "IP_v4_or_v6", "Record_Type", "Value"
primaryId = entry[2],
secondaryId = entry[3],
watched1 = entry[0],
watched2 = entry[4],
watched3 = entry[5],
watched4 = '',
extra = entry[0],
foreignKey = entry[2])
plugin_objects.write_result_file()
return 0
def execute_pholus_scan(userSubnets, timeoutSec):
# output of possible multiple interfaces
result_list = []
timeoutPerSubnet = float(timeoutSec) / len(userSubnets)
mylog('verbose', [f'[{pluginName}] { timeoutPerSubnet } '])
# scan each interface
for interface in userSubnets:
temp = interface.split("--interface=")
if len(temp) != 2:
mylog('verbose', [f'[{pluginName}] Skip scan (need interface in format "192.168.1.0/24 --inteface=eth0"), got: ', interface])
return
mask = temp[0].strip()
interface = temp[1].strip()
pholus_output_list = execute_pholus_on_interface (interface, timeoutPerSubnet, mask)
mylog('verbose', [f'[{pluginName}] { pholus_output_list } '])
result_list += pholus_output_list
mylog('verbose', [f'[{pluginName}] Pholus output number of entries:', len(result_list)])
mylog('verbose', [f'[{pluginName}] List:', result_list])
return result_list
def execute_pholus_on_interface(interface, timeoutSec, mask):
# logging & updating app state
mylog('verbose', [f'[{pluginName}] Scan: Pholus for ', str(timeoutSec), 's ('+ str(round(int(timeoutSec) / 60, 1)) +'min)'])
mylog('verbose', [f'[{pluginName}] Pholus scan on [interface] ', interface, ' [mask] ' , mask])
# the scan always lasts 2x as long, so the desired user time from settings needs to be halved
adjustedTimeout = str(round(int(timeoutSec) / 2, 0))
# python3 -m trace --trace /app/pholus/pholus3.py eth1 -rdns_scanning 192.168.1.0/24 -stimeout 600
pholus_args = ['python3', fullPholusPath, interface, "-rdns_scanning", mask, "-stimeout", adjustedTimeout]
# Execute command
output = ""
try:
# try runnning a subprocess with a forced (timeout + 30 seconds) in case the subprocess hangs
output = subprocess.check_output (pholus_args, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(timeoutSec + 30))
except subprocess.CalledProcessError as e:
# An error occured, handle it
mylog('verbose', [f'[{pluginName}]', e.output])
mylog('verbose', [f'[{pluginName}] ⚠ ERROR - Pholus Scan - check logs'])
except subprocess.TimeoutExpired as timeErr:
mylog('verbose', [f'[{pluginName}] Pholus TIMEOUT - the process forcefully terminated as timeout reached'])
if output == "": # check if the subprocess failed
mylog('verbose', [f'[{pluginName}] Scan: Pholus FAIL - check logs'])
else:
mylog('verbose', [f'[{pluginName}] Scan: Pholus SUCCESS'])
# check the last run output
f = open(logPath + '/pholus_lastrun.log', 'r+')
newLines = f.read().split('\n')
f.close()
# cleanup - select only lines containing a separator to filter out unnecessary data
newLines = list(filter(lambda x: '|' in x, newLines))
# build SQL query parameters to insert into the DB
params = []
for line in newLines:
columns = line.split("|")
if len(columns) == 4:
# "Info", "Time", "MAC", "IP_v4_or_v6", "Record_Type", "Value"
params.append( [interface + " " + mask, timeNowTZ() , columns[0].replace(" ", ""), columns[1].replace(" ", ""), columns[2].replace(" ", ""), columns[3]])
return params
#===============================================================================
# BEGIN
#===============================================================================
if __name__ == '__main__':
main()

View File

@@ -14,6 +14,7 @@
<?php
require 'php/templates/header.php';
require 'php/components/graph_online_history.php';
?>
<!-- Page ------------------------------------------------------------------ -->
@@ -125,41 +126,6 @@
</div>
</div>
<script src="js/graph_online_history.js"></script>
<script>
$.get('api/table_online_history.json?nocache=' + Date.now(), function(res) {
// Extracting data from the JSON response
var timeStamps = [];
var onlineCounts = [];
var downCounts = [];
var offlineCounts = [];
var archivedCounts = [];
res.data.forEach(function(entry) {
var dateObj = new Date(entry.Scan_Date);
var formattedTime = dateObj.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', hour12: false});
timeStamps.push(formattedTime);
onlineCounts.push(entry.Online_Devices);
downCounts.push(entry.Down_Devices);
offlineCounts.push(entry.Offline_Devices);
archivedCounts.push(entry.Archived_Devices);
});
// Call your presenceOverTime function after data is ready
presenceOverTime(
timeStamps,
onlineCounts,
offlineCounts,
archivedCounts,
downCounts
);
}).fail(function() {
// Handle any errors in fetching the data
console.error('Error fetching online history data.');
});
</script>
<!-- /.row -->
<!-- Calendar -------------------------------------------------------------- -->

View File

@@ -835,7 +835,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
if(!canReadAndWriteConfig)
{
showMessage (getString("settings_readonly"), 10000, "modal_red");
console.log(`app.conf seems to be read only (canRWConfig: ${canReadAndWriteConfig}`);
console.log(`app.conf seems to be read only (canRWConfig: ${canReadAndWriteConfig})`);
} else
{
// check if config file has been updated

View File

@@ -476,10 +476,6 @@ class DB():
self.sql.execute(""" DROP VIEW IF EXISTS Sessions_Devices;""")
self.sql.execute("""CREATE VIEW Sessions_Devices AS SELECT * FROM Sessions LEFT JOIN "Devices" ON ses_MAC = devMac;""")
# -------------------------------------------------------------------------
# Settings table setup
# -------------------------------------------------------------------------
@@ -505,24 +501,9 @@ class DB():
""")
# -------------------------------------------------------------------------
# Pholus_Scan table setup
# -------------------------------------------------------------------------
# Create Pholus_Scan table if missing
mylog('verbose', ["[upgradeDB] Re-creating Pholus_Scan table"])
self.sql.execute("""CREATE TABLE IF NOT EXISTS "Pholus_Scan" (
"Index" INTEGER,
"Info" TEXT,
"Time" TEXT,
"MAC" TEXT,
"IP_v4_or_v6" TEXT,
"Record_Type" TEXT,
"Value" TEXT,
"Extra" TEXT,
PRIMARY KEY("Index" AUTOINCREMENT)
);
""")
mylog('verbose', ["[upgradeDB] Removing Pholus_Scan table"])
self.sql.execute("""DROP TABLE IF EXISTS Pholus_Scan""")
# -------------------------------------------------------------------------

View File

@@ -4,7 +4,7 @@ import subprocess
import conf
import os
import re
from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, resolve_device_name_dig, resolve_device_name_pholus, get_device_name_nbtlookup, get_device_name_nslookup, get_device_name_mdns, check_IP_format, sanitize_SQL_input
from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, resolve_device_name_dig, get_device_name_nbtlookup, get_device_name_nslookup, get_device_name_mdns, check_IP_format, sanitize_SQL_input
from logger import mylog, print_log
from const import vendorsPath, vendorsPathNewest, sql_generateGuid
@@ -501,7 +501,6 @@ def update_devices_names (db):
foundmDNSLookup = 0
foundNsLookup = 0
foundNbtLookup = 0
foundPholus = 0
# Gen unknown devices
sql.execute ("SELECT * FROM Devices WHERE devName IN ('(unknown)','', '(name not found)') AND devLastIP <> '-'")
@@ -515,15 +514,6 @@ def update_devices_names (db):
# Devices without name
mylog('verbose', f'[Update Device Name] Trying to resolve devices without name. Unknown devices count: {len(unknownDevices)}')
# get names from Pholus scan
sql.execute ('SELECT * FROM Pholus_Scan where "Record_Type"="Answer"')
pholusResults = list(sql.fetchall())
db.commitDB()
# Number of entries from previous Pholus scans
mylog('verbose', ['[Update Device Name] Pholus entries from prev scans: ', len(pholusResults)])
for device in unknownDevices:
newName = nameNotFound
@@ -548,27 +538,13 @@ def update_devices_names (db):
if newName != nameNotFound:
foundNsLookup += 1
# Resolve device name with NSLOOKUP plugin data
# Resolve device name with NBTLOOKUP plugin data
if newName == nameNotFound:
newName = get_device_name_nbtlookup(db, device['devMac'], device['devLastIP'])
if newName != nameNotFound:
foundNbtLookup += 1
# Resolve with Pholus
if newName == nameNotFound:
# Try MAC matching
newName = resolve_device_name_pholus (device['devMac'], device['devLastIP'], pholusResults, nameNotFound, False)
# Try IP matching
if newName == nameNotFound:
newName = resolve_device_name_pholus (device['devMac'], device['devLastIP'], pholusResults, nameNotFound, True)
# count
if newName != nameNotFound:
foundPholus += 1
# if still not found update name so we can distinguish the devices where we tried already
if newName == nameNotFound :
@@ -579,11 +555,11 @@ def update_devices_names (db):
if device['devName'] != nameNotFound:
recordsNotFound.append (["(name not found)", device['devMac']])
else:
# name was found with DiG or Pholus
# name was found
recordsToUpdate.append ([newName, device['devMac']])
# Print log
mylog('verbose', [f'[Update Device Name] Names Found (DiG/mDNS/NSLOOKUP/NBTSCAN/Pholus): {len(recordsToUpdate)} ({foundDig}/{foundmDNSLookup}/{foundNsLookup}/{foundNbtLookup}/{foundPholus})'] )
mylog('verbose', [f'[Update Device Name] Names Found (DiG/mDNS/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundDig}/{foundmDNSLookup}/{foundNsLookup}/{foundNbtLookup})'] )
mylog('verbose', [f'[Update Device Name] Names Not Found : {notFound}'] )
# update not found devices with (name not found)

View File

@@ -695,89 +695,9 @@ def resolve_device_name_dig (pMAC, pIP):
#-------------------------------------------------------------------------------
# DNS record (Pholus/Name resolution) cleanup methods
# DNS record (Name resolution) cleanup methods
#-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Disclaimer - I'm interfacing with a script I didn't write (pholus3.py) so it's possible I'm missing types of answers
# it's also possible the pholus3.py script can be adjusted to provide a better output to interface with it
# Hit me with a PR if you know how! :)
def resolve_device_name_pholus (pMAC, pIP, allRes, nameNotFound, match_IP = False):
pholusMatchesIndexes = []
result = nameNotFound
# Collect all Pholus entries with matching MAC and of type Answer
index = 0
for result in allRes:
# limiting entries used for name resolution to the ones containing the current IP (v4 only)
if ((match_IP and result["IP_v4_or_v6"] == pIP ) or ( result["MAC"] == pMAC )) and result["Record_Type"] == "Answer" and '._googlezone' not in result["Value"]:
# found entries with a matching MAC address, let's collect indexes
pholusMatchesIndexes.append(index)
index += 1
# return if nothing found
if len(pholusMatchesIndexes) == 0:
return nameNotFound
# we have some entries let's try to select the most useful one
# Do I need to pre-order allRes to have the most valuable onse on the top?
for i in pholusMatchesIndexes:
if not checkIPV4(allRes[i]['IP_v4_or_v6']):
continue
value = allRes[i]["Value"]
# airplay matches contain a lot of information
# Matches for example:
# Brand Tv (50)._airplay._tcp.local. TXT Class:32769 "acl=0 deviceid=66:66:66:66:66:66 features=0x77777,0x38BCB46 rsf=0x3 fv=p20.T-FFFFFF-03.1 flags=0x204 model=XXXX manufacturer=Brand serialNumber=XXXXXXXXXXX protovers=1.1 srcvers=777.77.77 pi=FF:FF:FF:FF:FF:FF psi=00000000-0000-0000-0000-FFFFFFFFFF gid=00000000-0000-0000-0000-FFFFFFFFFF gcgl=0 pk=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
if '._airplay._tcp.local. TXT Class:32769' in value:
return cleanDeviceName(value.split('._airplay._tcp.local. TXT Class:32769')[0], match_IP)
# second best - contains airplay
# Matches for example:
# _airplay._tcp.local. PTR Class:IN "Brand Tv (50)._airplay._tcp.local."
if '_airplay._tcp.local. PTR Class:IN' in value and ('._googlecast') not in value:
return cleanDeviceName(value.split('"')[1], match_IP)
# Contains PTR Class:32769
# Matches for example:
# 3.1.168.192.in-addr.arpa. PTR Class:32769 "MyPc.local."
if 'PTR Class:32769' in value:
return cleanDeviceName(value.split('"')[1], match_IP)
# Contains AAAA Class:IN
# Matches for example:
# DESKTOP-SOMEID.local. AAAA Class:IN "fe80::fe80:fe80:fe80:fe80"
if 'AAAA Class:IN' in value:
return cleanDeviceName(value.split('.local.')[0], match_IP)
# Contains _googlecast._tcp.local. PTR Class:IN
# Matches for example:
# _googlecast._tcp.local. PTR Class:IN "Nest-Audio-ff77ff77ff77ff77ff77ff77ff77ff77._googlecast._tcp.local."
if '_googlecast._tcp.local. PTR Class:IN' in value and ('Google-Cast-Group') not in value:
return cleanDeviceName(value.split('"')[1], match_IP)
# Contains A Class:32769
# Matches for example:
# Android.local. A Class:32769 "192.168.1.6"
if ' A Class:32769' in value:
return cleanDeviceName(value.split(' A Class:32769')[0], match_IP)
# Contains PTR Class:IN
# Matches for example:
# _esphomelib._tcp.local. PTR Class:IN "ceiling-light-1._esphomelib._tcp.local."
if 'PTR Class:IN' in value and len(value.split('"')) > 1:
return cleanDeviceName(value.split('"')[1], match_IP)
return nameNotFound
#-------------------------------------------------------------------------------
import dns.resolver
def cleanDeviceName(str, match_IP):