From c0984b242ed3e352f2610144a4336a1246fd47ee Mon Sep 17 00:00:00 2001 From: overcuriousity Date: Mon, 1 Sep 2025 21:55:56 +0200 Subject: [PATCH] progress --- src/main.c | 296 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 260 insertions(+), 36 deletions(-) diff --git a/src/main.c b/src/main.c index 6bcea29..6f226c3 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND #define GROWTH_FACTOR 2 // wird in mem_expand_dynamically() genutzt, um den Speicher zu vergrößern #define MAX_FILTERS 100 #define MAX_REQUEST_LENGTH 1024 // das hohe Limit ist erforderlich, da teilweise ausufernde JSON-Requests in nginx auflaufen können. +#define TOP_X 20 // definiert die Anzahl der Einträge, die in den Top-Listen angezeigt werden sollen // definiert Variablen für den Filtermodus. FILTER_INCLUDE=0, FILTER_EXCLUDE=1. Verbessert die Lesbarkeit des Codes. typedef enum { @@ -82,6 +83,12 @@ struct user_agent_filter { filter_mode_t mode; }; +// Filter für URL-Pfad/Request +struct url_filter { + char pattern[MAX_REQUEST_LENGTH]; + filter_mode_t mode; +}; + // Struktur zum erhalten aller Filtereinträge, kann im Dialogbetrieb bearbeitet werden. Mit Zähler. struct filter_system { struct status_filter status_filters[MAX_FILTERS]; @@ -98,6 +105,9 @@ struct filter_system { struct user_agent_filter user_agent_filters[MAX_FILTERS]; int user_agent_count; + + struct url_filter url_filters[MAX_FILTERS]; + int url_count; int combination_mode; // 0=AND-Filter oder 1=OR-Filter }; @@ -674,6 +684,47 @@ int user_agent_matches(char* user_agent) { return 1; // Keine Einschlussfilter, keine zutreffenden Ausschlussfilter, positiver Rückgabewert =1 } +// Filterfunktion für URL-Pfad. Nimmt den Datensatz entgegen und prüft gegen die gesetzten Filter, gibt dann 0 oder 1 zurück +int url_matches(char* url_path) { + if (filters.url_count == 0) return 1; + // Ausschluss-Filter geht vor + for (int i = 0; i < filters.url_count; i++) { + if (filters.url_filters[i].mode == FILTER_EXCLUDE) { + int pattern_found = search_in_string(url_path, filters.url_filters[i].pattern); + if (pattern_found) { + return 0; // früheres Verlassen der Schleife, sobald Ausschlussfilter zutrifft + } + } + } + // Prüfung im entsprechenden Modus + int include_count = 0; + int include_matches = 0; + + for (int i = 0; i < filters.url_count; i++) { + if (filters.url_filters[i].mode == FILTER_INCLUDE) { + include_count++; + int pattern_found = search_in_string(url_path, filters.url_filters[i].pattern); + if (pattern_found) { + include_matches++; + if (filters.combination_mode == 1) { // OR-Modus + return 1; // Früheres Verlassen der Schleife bei erstem zutreffendem Einschlussfilter im OR-Modus + } + } + } + } + + // Diese Prüfung wird ausgeführt, wenn Einschlussfilter vorhanden sind + if (include_count > 0) { + if (filters.combination_mode == 0) { // AND-Modus + return include_matches == include_count; // Alle Einschlussfilter müssen zutreffen, die Treffer müssen Anzahl der Filter entsprechen + } else { // OR-Modus + return include_matches > 0; // Ausschlussfilter im ODER-Modus - wenn ein beliebiger Eintrag zum Ausschlussfilter passt, wird 0=negativ zurückgegeben + } + } + + return 1; // Keine Einschlussfilter, keine zutreffenden Ausschlussfilter, positiver Rückgabewert =1 +} + // Filterfunktion für Zugriffsmethode. Nimmt den Datensatz entgegen und prüft gegen die gesetzten Filter, gibt dann 0 oder 1 zurück int method_matches(char* request_method) { if (filters.method_count == 0) return 1; @@ -837,18 +888,64 @@ int time_matches(struct simple_time entry_time) { // Prüfen aller Filter im AND-Modus oder OR-Modus (combination_mode) pro Log-Eintrag int passes_filter(int entry_index) { - int status_match = status_code_matches(all_entries[entry_index].status_code); - int method_match = method_matches(all_entries[entry_index].request_method); - int ip_match = ip_address_matches(all_entries[entry_index].ip_address); - int time_match = time_matches(all_entries[entry_index].time); - int user_agent_match = user_agent_matches(all_entries[entry_index].user_agent); - if (filters.combination_mode == 0) { - return status_match && ip_match && time_match && user_agent_match && method_match; - } else { - int total_filters = filters.status_count + filters.method_count + filters.ip_count + filters.time_count + filters.user_agent_count; + if (filters.combination_mode == 0) { // AND-Modus + // alle AND-verknüpften Filter müssen zutreffen + int status_match = status_code_matches(all_entries[entry_index].status_code); + int method_match = method_matches(all_entries[entry_index].request_method); + int ip_match = ip_address_matches(all_entries[entry_index].ip_address); + int time_match = time_matches(all_entries[entry_index].time); + int user_agent_match = user_agent_matches(all_entries[entry_index].user_agent); + int url_match = url_matches(all_entries[entry_index].url_path); + + return status_match && ip_match && time_match && user_agent_match && method_match && url_match; + } else { // OR-Modus + // für den geprüften Eintrag muss mindestens eine der Filterkategorien zutreffen. + // Eine Prüfung über Filter1 || Filter2 || etc ist nicht möglich, da auch + // nicht gesetzte Filter hier keine Einschränkng bilden. Daher muss jede KAtegorie einzeln geprüft werden. + int total_filters = filters.status_count + filters.method_count + filters.ip_count + + filters.time_count + filters.user_agent_count + filters.url_count; if (total_filters == 0) return 1; - return status_match || method_match || ip_match || time_match || user_agent_match; + int has_passing_filter = 0; + + // Jede Filterkategorie wird nur geprüft, wenn auch entsprechende Filter gesetzt sind. + if (filters.status_count > 0) { + if (status_code_matches(all_entries[entry_index].status_code)) { + has_passing_filter = 1; + } + } + + if (filters.method_count > 0) { + if (method_matches(all_entries[entry_index].request_method)) { + has_passing_filter = 1; + } + } + + if (filters.ip_count > 0) { + if (ip_address_matches(all_entries[entry_index].ip_address)) { + has_passing_filter = 1; + } + } + + if (filters.time_count > 0) { + if (time_matches(all_entries[entry_index].time)) { + has_passing_filter = 1; + } + } + + if (filters.user_agent_count > 0) { + if (user_agent_matches(all_entries[entry_index].user_agent)) { + has_passing_filter = 1; + } + } + + if (filters.url_count > 0) { + if (url_matches(all_entries[entry_index].url_path)) { + has_passing_filter = 1; + } + } + + return has_passing_filter; } } @@ -874,7 +971,7 @@ void show_status() { } printf("\n🔍 Aktive Filter-Logik:\n"); - int total_filters = filters.status_count + filters.method_count + filters.ip_count + filters.time_count + filters.user_agent_count; + int total_filters = filters.status_count + filters.method_count + filters.ip_count + filters.time_count + filters.user_agent_count + filters.url_count; if (total_filters == 0) { printf(" Keine Filter gesetzt → alle Einträge werden angezeigt\n"); @@ -1037,13 +1134,51 @@ void show_status() { printf("\n"); } + if (filters.url_count > 0) { + printf(" URL: "); + + int excludes = 0, includes = 0; + for (int i = 0; i < filters.url_count; i++) { + if (filters.url_filters[i].mode == FILTER_EXCLUDE) excludes++; + else includes++; + } + + if (excludes > 0) { + printf("!("); + int first = 1; + for (int i = 0; i < filters.url_count; i++) { + if (filters.url_filters[i].mode == FILTER_EXCLUDE) { + if (!first) printf(" OR "); + printf("\"%s\"", filters.url_filters[i].pattern); + first = 0; + } + } + printf(")"); + if (includes > 0) printf(" AND "); + } + + if (includes > 0) { + if (includes > 1) printf("("); + int first = 1; + for (int i = 0; i < filters.url_count; i++) { + if (filters.url_filters[i].mode == FILTER_INCLUDE) { + if (!first) printf(" %s ", filters.combination_mode == 0 ? "AND" : "OR"); + printf("\"%s\"", filters.url_filters[i].pattern); + first = 0; + } + } + if (includes > 1) printf(")"); + } + printf("\n"); + } + if (filters.time_count > 0) { printf(" Time: %d Filter(s) gesetzt\n", filters.time_count); } // Zeigt aktuellen Modus int active_types = (filters.status_count > 0) + (filters.method_count > 0 ) + (filters.ip_count > 0) + - (filters.user_agent_count > 0) + (filters.time_count > 0); + (filters.user_agent_count > 0) + (filters.time_count > 0) + (filters.url_count > 0); if (active_types > 1) { printf("\n %s-Verknüpfung - Ausschlussfilter vorrangig\n", filters.combination_mode == 0 ? "UND" : "ODER"); @@ -1148,7 +1283,7 @@ struct ip_stat { int count; }; -void show_top_10_ips() { +void show_top_x_ips() { struct ip_stat ip_stats[1000]; int unique_ips = 0; @@ -1186,11 +1321,11 @@ void show_top_10_ips() { } } - printf("\n=== TOP 10 IP-ADRESSEN ===\n"); + printf("\n=== TOP %d IP-ADRESSEN ===\n", TOP_X); printf("Rang | IP-Adresse | Anzahl Anfragen\n"); printf("-----|------------------|----------------\n"); - int show_count = (unique_ips < 10) ? unique_ips : 10; + int show_count = (unique_ips < TOP_X) ? unique_ips : TOP_X; for (int i = 0; i < show_count; i++) { printf("%-4d | %-16s | %d\n", i + 1, ip_stats[i].ip_address, ip_stats[i].count); } @@ -1246,11 +1381,11 @@ void show_top_user_agents() { } } - printf("\n=== TOP 10 USER AGENTS ===\n"); + printf("\n=== TOP %d USER AGENTS ===\n", TOP_X); printf("Rang | User Agent | Anzahl Anfragen\n"); printf("-----|--------------------------------------|----------------\n"); - int show_count = (unique_agents < 10) ? unique_agents : 10; + int show_count = (unique_agents < TOP_X) ? unique_agents : TOP_X; for (int i = 0; i < show_count; i++) { printf("%-4d | %-34s | %d\n", i + 1, agent_stats[i].user_agent, agent_stats[i].count); } @@ -1327,9 +1462,46 @@ void show_statistics() { } } +void print_filter_examples() { + printf("\nFILTER-DOKUMENTATION\n"); + + printf("\n🔴 EXKLUSIONS-FILTER (immer OR-Logik, unabhängig vom Modus):\n"); + printf("Beispiel: !('uptime' OR 'scanner')\n"); + printf("→ Schließt ALLE Einträge aus, die 'uptime' ODER 'scanner' enthalten\n\n"); + + printf("🟢 INKLUSIONS-FILTER im AND-Modus:\n"); + printf("Beispiel: ('bot' AND 'crawl')\n"); + printf("→ Zeigt nur Einträge mit BEIDEN Begriffen\n\n"); + + printf("🟡 INKLUSIONS-FILTER im OR-Modus:\n"); + printf("Beispiel: ('bot' OR 'crawl')\n"); + printf("→ Zeigt Einträge mit 'bot' ODER 'crawl'\n\n"); + + printf("⚡ KOMBINATION: Exklusion + Inklusion:\n"); + printf("AND-Modus: !('uptime') AND ('bot' AND 'crawl')\n"); + printf("OR-Modus: !('uptime') AND ('bot' OR 'crawl')\n\n"); + + printf("🎯 PRAKTISCHE ANWENDUNGSFÄLLE:\n"); + printf("Malware-Erkennung:\n"); + printf(" '.git' OR '.env' OR '/admin'\n"); + printf(" → Verdächtige Pfad-Zugriffe\n\n"); + + printf("Bot-Traffic bereinigen:\n"); + printf(" !('bot') AND Status=200 AND Method='GET'\n"); + printf(" → Nur menschliche, erfolgreiche GET-Anfragen\n\n"); + + printf("Zeitraum-Analyse:\n"); + printf(" Time='08:00-18:00' AND Status=500\n"); + printf(" → Server-Fehler nur während Geschäftszeiten\n\n"); + + printf("DDoS-Verdacht:\n"); + printf(" Status=429 OR Status=503 OR Status=500\n"); + printf(" → Alle Überlastungs- und Fehler-Codes\n"); +} + void menu_set_filters() { int choice = 0; - while (choice != 6) { + while (choice != 7) { show_status(); printf("\n=== FILTER SETZEN ===\n"); printf("1. Status-Code Filter hinzufügen (exakte Suche)\n"); @@ -1337,7 +1509,8 @@ void menu_set_filters() { printf("3. Zeitraum Filter hinzufügen (interaktiv)\n"); printf("4. User-Agent-Filter setzen (Freitext)\n"); printf("5. HTTP-Methode Filter hinzufügen (Freitext)\n"); - printf("6. Zurück zum Filter-Menü\n"); + printf("6. URL-Pfad Filter hinzufügen (Freitext)\n"); + printf("7. Zurück zum Filter-Menü\n"); printf("Auswahl: "); choice = read_safe_integer(); @@ -1542,7 +1715,7 @@ void menu_set_filters() { continue; } - printf("HTTP-Methode eingeben (z.B. 'GET', 'POST', 'PUT'): "); + printf("HTTP-Methode eingeben (z.B. 'GET', 'POST', 'PUT', ... Sonderwert 'MALFORMED'): "); char pattern[10]; if (scanf("%9s", pattern) == 1) { printf("Filter-Typ wählen:\n"); @@ -1565,15 +1738,43 @@ void menu_set_filters() { } } else if (choice == 6) { + if (filters.url_count >= MAX_FILTERS) { + printf("FEHLER: Maximale Anzahl URL-Filter erreicht (%d)!\n", MAX_FILTERS); + continue; + } + + printf("URL-Pfad Suchtext eingeben (z.B. '.git', '.php', '/admin'): "); + char pattern[MAX_REQUEST_LENGTH]; + if (scanf("%1023s", pattern) == 1) { + printf("Filter-Typ wählen:\n"); + printf("1. Einschließen (nur URLs mit '%s' anzeigen)\n", pattern); + printf("2. Ausschließen (URLs mit '%s' NICHT anzeigen)\n", pattern); + printf("Auswahl: "); + int filter_type = read_safe_integer(); + + if (filter_type == 1 || filter_type == 2) { + strcpy(filters.url_filters[filters.url_count].pattern, pattern); + filters.url_filters[filters.url_count].mode = (filter_type == 2) ? FILTER_EXCLUDE : FILTER_INCLUDE; + filters.url_count++; + printf("✅ URL-Filter hinzugefügt. Total: %d\n", filters.url_count); + } else { + printf("FEHLER: Ungültiger Filter-Typ!\n"); + } + } else { + printf("FEHLER: Ungültiger Suchtext!\n"); + clear_input_buffer(); + } + + } else if (choice == 7) { return; } else if (choice != -1) { - printf("Ungültige Auswahl! Bitte wählen Sie 1-6.\n"); + printf("Ungültige Auswahl! Bitte wählen Sie 1-7.\n"); } } } void menu_delete_filters() { - int total_filters = filters.status_count + filters.ip_count + filters.time_count + filters.user_agent_count; + int total_filters = filters.status_count + filters.ip_count + filters.time_count + filters.user_agent_count + filters.method_count + filters.url_count; if (total_filters == 0) { printf("Keine Filter gesetzt zum Löschen.\n"); @@ -1604,6 +1805,11 @@ void menu_delete_filters() { char* mode_str = (filters.user_agent_filters[i].mode == FILTER_EXCLUDE) ? "(ausschließen)" : "(einschließen)"; printf("%d. User-Agent: \"%s\" %s\n", filter_index++, filters.user_agent_filters[i].pattern, mode_str); } + + for (int i = 0; i < filters.url_count; i++) { + char* mode_str = (filters.url_filters[i].mode == FILTER_EXCLUDE) ? "(ausschließen)" : "(einschließen)"; + printf("%d. URL-Pfad: \"%s\" %s\n", filter_index++, filters.url_filters[i].pattern, mode_str); + } for (int i = 0; i < filters.time_count; i++) { char* mode_str = (filters.time_filters[i].mode == FILTER_EXCLUDE) ? "(ausschließen)" : "(einschließen)"; @@ -1685,6 +1891,18 @@ void menu_delete_filters() { } current_index++; } + + for (int i = 0; i < filters.url_count; i++) { + if (current_index == choice) { + for (int j = i; j < filters.url_count - 1; j++) { + filters.url_filters[j] = filters.url_filters[j + 1]; + } + filters.url_count--; + printf("✅ URL-Filter gelöscht.\n"); + return; + } + current_index++; + } for (int i = 0; i < filters.time_count; i++) { if (current_index == choice) { @@ -1702,13 +1920,13 @@ void menu_delete_filters() { void menu_filter_mode() { printf("=== FILTER-MODUS ===\n"); printf("Aktueller Modus: %s\n", filters.combination_mode == 0 ? "AND" : "OR"); - printf("\nDieser Modus bestimmt:\n"); - printf("1. Wie mehrere Filter desselben Typs kombiniert werden\n"); - printf("2. Wie verschiedene Filter-Typen kombiniert werden\n"); - printf("\nBeispiele:\n"); - printf("AND: IP=1.2.3.4 UND User-Agent=mozilla (beide müssen zutreffen)\n"); - printf("OR: IP=1.2.3.4 ODER User-Agent=mozilla (einer muss zutreffen)\n"); - printf("Auswahl: "); + printf("\nACHTUNG:\n"); + printf("Der Modus wirkt sich nur auf inklusive Filter aus.\n"); + printf("Die exklusiven Filter arbeiten immer im OR-Modus und \nschließen jeden Eintrag aus, der zu einem beliebigen Exklusions-Filter passt.\nExklusions-Filter haben immer Vorrang.\n"); + printf("\nAuswahl: \n"); + printf("1. AND-Modus\n"); + printf("2. OR-Modus\n"); + printf("3. Detaillierte Beispiele\n"); int choice = read_safe_integer(); @@ -1718,6 +1936,8 @@ void menu_filter_mode() { } else if (choice == 2) { filters.combination_mode = 1; printf("✅ Filter-Modus auf OR gesetzt.\n"); + } else if (choice == 3) { + print_filter_examples(); } else if (choice != -1) { printf("Ungültige Auswahl!\n"); } @@ -1729,6 +1949,7 @@ void menu_reset_filters() { filters.ip_count = 0; filters.time_count = 0; filters.user_agent_count = 0; + filters.url_count = 0; filters.combination_mode = 0; printf("✅ Alle Filter zurückgesetzt.\n"); @@ -1738,12 +1959,13 @@ void menu_filter_management() { int choice = 0; while (choice != 5) { show_status(); - printf("\n=== FILTER VERWALTEN ===\n"); + printf("\nFILTER VERWALTEN\n"); printf("1. Filter setzen\n"); printf("2. Filter löschen\n"); printf("3. Filter-Modus (AND/OR)\n"); printf("4. Filter zurücksetzen\n"); - printf("5. Zurück zum Hauptmenü\n"); + printf("5. Zeige Filter-Dokumentation\n"); + printf("6. Zurück zum Hauptmenü\n"); printf("Auswahl: "); choice = read_safe_integer(); @@ -1757,9 +1979,11 @@ void menu_filter_management() { } else if (choice == 4) { menu_reset_filters(); } else if (choice == 5) { + print_filter_examples(); + } else if (choice == 6) { return; } else if (choice != -1) { - printf("Ungültige Auswahl! Bitte wählen Sie 1-5.\n"); + printf("Ungültige Auswahl! Bitte wählen Sie 1-6.\n"); } } } @@ -1768,13 +1992,13 @@ void menu_show_entries() { int choice = 0; while (choice != 4) { show_status(); - printf("\n=== EINTRÄGE ANZEIGEN ===\n"); + printf("\nANZEIGEN UND EXPORTIEREN\n"); int filtered_count = count_filtered_entries(); printf("Aktuell %d gefilterte Einträge verfügbar.\n", filtered_count); printf("1. Vollständig anzeigen\n"); printf("2. Zu .csv exportieren (Timesketch-kompatibel)\n"); - printf("3. Top 10 IP-Adressen\n"); - printf("4. Top 10 User-Agents\n"); + printf("3. Top %d IP-Adressen\n", TOP_X); + printf("4. Top %d User-Agents\n", TOP_X); printf("5. Zurück zum Hauptmenü\n"); printf("Auswahl: "); @@ -1794,7 +2018,7 @@ void menu_show_entries() { } else if (choice == 2) { export_filtered_entries(); } else if (choice == 3) { - show_top_10_ips(); + show_top_x_ips(); } else if (choice == 4) { show_top_user_agents(); } else if (choice == 5) {