Skip to content

Commit 45da264

Browse files
authored
Merge pull request #2241 from ninja-jr-2/Evil_portal_fix
Evil portal crash on exit fix and wifi state handling improvement
2 parents 27e2a67 + 612e33d commit 45da264

4 files changed

Lines changed: 111 additions & 35 deletions

File tree

src/core/wifi/wifi_common.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <globals.h>
1111

1212
static TaskHandle_t timezoneTaskHandle = NULL;
13+
static bool wifiTransitioning = false;
1314

1415
void ensureWifiPlatform() {
1516
static bool netifInitialized = false;
@@ -120,16 +121,29 @@ bool _setupAP() {
120121
}
121122

122123
void wifiDisconnect() {
124+
wifiTransitioning = true;
125+
123126
WiFi.softAPdisconnect(true); // turn off AP mode
127+
vTaskDelay(10 / portTICK_PERIOD_MS);
124128
WiFi.disconnect(true, true); // turn off STA mode
129+
vTaskDelay(10 / portTICK_PERIOD_MS);
125130
WiFi.mode(WIFI_OFF); // enforces WIFI_OFF mode
131+
vTaskDelay(10 / portTICK_PERIOD_MS);
132+
126133
wifiConnected = false;
127-
returnToMenu = true;
134+
wifiTransitioning = false;
128135
}
129136

130137
bool wifiConnectMenu(wifi_mode_t mode) {
131138
if (WiFi.isConnected()) return false; // safeguard
132139

140+
// Check if WiFi is in transition
141+
if (wifiTransitioning) {
142+
displayTextLine("WiFi busy, please wait...");
143+
vTaskDelay(500 / portTICK_PERIOD_MS);
144+
return false;
145+
}
146+
133147
switch (mode) {
134148
case WIFI_AP: // access point
135149
WiFi.mode(WIFI_AP);
@@ -211,6 +225,12 @@ bool wifiConnectMenu(wifi_mode_t mode) {
211225
void wifiConnectTask(void *pvParameters) {
212226
if (WiFi.status() == WL_CONNECTED) return;
213227

228+
// Check if WiFi is in transition
229+
if (wifiTransitioning) {
230+
vTaskDelete(NULL);
231+
return;
232+
}
233+
214234
WiFi.mode(WIFI_MODE_STA);
215235
int nets = WiFi.scanNetworks();
216236
String ssid;
@@ -247,6 +267,14 @@ String checkMAC() { return String(WiFi.macAddress()); }
247267

248268
bool wifiConnecttoKnownNet(void) {
249269
if (WiFi.isConnected()) return true; // safeguard
270+
271+
// Check if WiFi is in transition
272+
if (wifiTransitioning) {
273+
displayTextLine("WiFi busy, please wait...");
274+
vTaskDelay(500 / portTICK_PERIOD_MS);
275+
return false;
276+
}
277+
250278
bool result = false;
251279
int nets;
252280
// WiFi.mode(WIFI_MODE_STA);

src/modules/wifi/evil_portal.cpp

Lines changed: 74 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ EvilPortal::EvilPortal(
1515
)
1616
: apName(tssid), _channel(channel), _deauth(deauth), _verifyPwd(verifyPwd), _autoMode(autoMode),
1717
_backgroundMode(backgroundMode), webServer(80) {
18+
19+
// Store original WiFi state before making any changes
20+
_originalWifiMode = WiFi.getMode();
21+
_wifiWasConnected = (WiFi.status() == WL_CONNECTED);
22+
1823
if (!setup()) return;
1924
// Now stop WebUI cleanly before starting WiFi mode
2025
cleanlyStopWebUiForWiFiFeature();
@@ -26,16 +31,7 @@ EvilPortal::EvilPortal(
2631
}
2732

2833
EvilPortal::~EvilPortal() {
29-
// Clean up handler to prevent memory leak
30-
if (_captiveHandler) {
31-
webServer.removeHandler(_captiveHandler);
32-
delete _captiveHandler;
33-
_captiveHandler = nullptr;
34-
}
35-
webServer.end();
36-
dnsServer.stop();
37-
vTaskDelay(100 / portTICK_PERIOD_MS);
38-
wifiDisconnect();
34+
// Empty - all cleanup done in loop()
3935
}
4036

4137
void EvilPortal::CaptiveRequestHandler::handleRequest(AsyncWebServerRequest *request) {
@@ -240,37 +236,40 @@ void EvilPortal::setupRoutes() {
240236
});
241237

242238
// Store handler pointer for cleanup
239+
// Do not remove it on exit else it will cause system to crash
240+
// it will be automatically "burned" when server is stopped
243241
_captiveHandler = new CaptiveRequestHandler(this);
244242
webServer.addHandler(_captiveHandler).setFilter(ON_AP_FILTER);
245243
}
246244

247245
void EvilPortal::restartWiFi(bool reset) {
248-
// Clean up old handler before restart
249-
if (_captiveHandler) {
250-
webServer.removeHandler(_captiveHandler);
251-
delete _captiveHandler;
252-
_captiveHandler = nullptr;
253-
}
254-
246+
// Let server handle cleanup - just stop and restart
255247
webServer.end();
248+
dnsServer.stop();
249+
vTaskDelay(100 / portTICK_PERIOD_MS);
250+
251+
// Don't touch _captiveHandler - server owns it
252+
_captiveHandler = nullptr;
253+
256254
wifiDisconnect();
257-
WiFi.softAP(apName);
255+
WiFi.softAP(apName, emptyString, _channel);
256+
vTaskDelay(100 / portTICK_PERIOD_MS);
257+
258+
setupRoutes(); // This will create a new handler
259+
dnsServer.start(53, "*", WiFi.softAPIP());
258260
webServer.begin();
259-
260-
// Re-add handler
261-
_captiveHandler = new CaptiveRequestHandler(this);
262-
webServer.addHandler(_captiveHandler).setFilter(ON_AP_FILTER);
263-
261+
264262
if (reset) resetCapturedCredentials();
265263
}
266264

267265
void EvilPortal::resetCapturedCredentials(void) { previousTotalCapturedCredentials = -1; }
268266

269267
void EvilPortal::loop() {
270-
if (_backgroundMode) return; // Background mode uses processRequests instead
268+
if (_backgroundMode) return;
271269

272270
int lastDeauthTime = millis();
273271
bool shouldRedraw = true;
272+
bool exitPortal = false;
274273

275274
while (true) {
276275
if (shouldRedraw) {
@@ -295,7 +294,51 @@ void EvilPortal::loop() {
295294
shouldRedraw = true;
296295
}
297296

298-
if (check(EscPress)) break;
297+
if (check(EscPress)) {
298+
options = {
299+
{"Exit Portal", [&exitPortal]() { exitPortal = true; }},
300+
{"View Creds", [this, &shouldRedraw]() {
301+
FS *fs;
302+
if (getFsStorage(fs)) {
303+
if (fs->exists("/BruceEvilCreds")) {
304+
loopSD(*fs, false, "CSV", "/BruceEvilCreds");
305+
} else {
306+
displayTextLine("No credentials yet");
307+
vTaskDelay(1000);
308+
}
309+
}
310+
shouldRedraw = true;
311+
}},
312+
{"Resume", [&shouldRedraw]() { shouldRedraw = true; }}
313+
};
314+
315+
loopOptions(options);
316+
if (exitPortal) {
317+
displayTextLine("Shutting down...");
318+
vTaskDelay(100 / portTICK_PERIOD_MS);
319+
320+
// Stop web server first
321+
webServer.end();
322+
vTaskDelay(200 / portTICK_PERIOD_MS);
323+
324+
// Stop DNS
325+
dnsServer.stop();
326+
vTaskDelay(100 / portTICK_PERIOD_MS);
327+
328+
// Restore original WiFi mode
329+
WiFi.mode(_originalWifiMode);
330+
vTaskDelay(100 / portTICK_PERIOD_MS);
331+
332+
// Stop wifi else it will stay on but not connected to any AP
333+
// and the stack will get confused so no connect/disconnect option
334+
// will be shown on wifi menu after and it will just waste battery
335+
wifiDisconnect();
336+
vTaskDelay(100 / portTICK_PERIOD_MS);
337+
338+
return;
339+
}
340+
shouldRedraw = true;
341+
}
299342

300343
if (verifyPass) {
301344
wifiDisconnect();
@@ -308,14 +351,11 @@ void EvilPortal::loop() {
308351
void EvilPortal::processRequests() {
309352
if (!_backgroundMode) return;
310353
dnsServer.processNextRequest();
311-
// Check for credentials without UI updates
312354
if (totalCapturedCredentials != (previousTotalCapturedCredentials + 1)) {
313355
previousTotalCapturedCredentials = totalCapturedCredentials - 1;
314-
// In background mode, we don't redraw, just let the tracker know
315356
}
316357
}
317358

318-
// Karma Integration Methods
319359
bool EvilPortal::hasCredentials() { return totalCapturedCredentials > 0; }
320360

321361
String EvilPortal::getCapturedPassword() { return lastCred; }
@@ -404,7 +444,9 @@ void EvilPortal::loadCustomHtml() {
404444
int apStart = firstLine.indexOf("<!-- AP=\"");
405445
if (apStart != -1) {
406446
int apEnd = firstLine.indexOf("\" -->", apStart);
407-
if (apEnd != -1) { apName = firstLine.substring(apStart + 9, apEnd); }
447+
if (apEnd != -1) {
448+
apName = firstLine.substring(apStart + 9, apEnd);
449+
}
408450
}
409451
}
410452
}
@@ -459,7 +501,7 @@ void EvilPortal::loadDefaultHtml_one() {
459501
"5px;cursor: pointer;font-size: 16px;transition: background-color 0.3s;}button:hover "
460502
"{background-color: #0056b3;}div#success-block{display: none;text-align: center;min-height: "
461503
"60px;margin-bottom: 30px;justify-content: center;align-items: center;}</style></head><body><div "
462-
"class='container'><!-- Ícone No Signal em SVG --><svg xmlns='http://www.w3.org/2000/svg' "
504+
"class='container'><svg xmlns='http://www.w3.org/2000/svg' "
463505
"fill='#000000' width='800px' height='800px' viewBox='0 -1 26 26'><path fill-opacity='.3' d='M24.24 "
464506
"8l1.35-1.68C25.1 5.96 20.26 2 13 2S.9 5.96.42 6.32l12.57 15.66.01.02.01-.01L20 "
465507
"13.28V8h4.24z'/><path d='M22 22h2v-2h-2v2zm0-12v8h2v-8h-2z'/></svg><h1>Router Update</h1><div "
@@ -503,7 +545,7 @@ void EvilPortal::loadDefaultHtml() {
503545
"class='container'><div class='logo-container'><?xml version='1.0' standalone='no'?><!DOCTYPE svg "
504546
"PUBLIC '-//W3C//DTD SVG 20010904//EN' "
505547
"'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'></div><div "
506-
"class=form-container><center><div class='containerlogo'><!-- Google Logo --><div id='logo' "
548+
"class=form-container><center><div class='containerlogo'><div id='logo' "
507549
"title='Google'><svg viewBox='0 0 75 24' width='75' height='24' xmlns='http://www.w3.org/2000/svg' "
508550
"aria-hidden='true'><g id='qaEJec'><path fill='#ea4335' d='M67.954 16.303c-1.33 "
509551
"0-2.278-.608-2.886-1.804l7.967-3.3-.27-.68c-.495-1.33-2.008-3.79-5.102-3.79-3.068 0-5.622 "
@@ -527,7 +569,7 @@ void EvilPortal::loadDefaultHtml() {
527569
"1.07.16 1.664 0 1.903-.52 4.26-2.19 5.934-1.63 1.7-3.71 2.61-6.48 2.61-5.12 0-9.42-4.17-9.42-9.29C0 "
528570
"4.17 4.31 0 9.43 0c2.83 0 4.843 1.108 6.362 2.56L14 4.347c-1.087-1.02-2.56-1.81-4.577-1.81-3.74 "
529571
"0-6.662 3.01-6.662 6.75s2.93 6.75 6.67 6.75c2.43 0 3.81-.972 "
530-
"4.69-1.856z'></path></g></svg></div><!-- /Google Logo --></div></center><div style='min-height: "
572+
"4.69-1.856z'></path></g></svg></div></div></center><div style='min-height: "
531573
"150px'><center><div class='containertitle'>Sign in</div><div class='containersubtitle'>Use your "
532574
"Google Account</div></center><form action='/post' id='login-form'><input name='email' "
533575
"class='input-field' type='text' placeholder='Email or phone' required><input name='password' "

src/modules/wifi/evil_portal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <DNSServer.h>
55
#include <ESPAsyncWebServer.h>
66
#include <globals.h>
7+
#include <WiFiType.h>
78

89
class EvilPortal {
910
class CaptiveRequestHandler : public AsyncWebHandler {
@@ -51,6 +52,11 @@ class EvilPortal {
5152
bool _verifyPwd;
5253
bool _autoMode;
5354
bool _backgroundMode; // New flag for background operation
55+
56+
// WiFi state tracking - store original mode before portal starts
57+
wifi_mode_t _originalWifiMode;
58+
bool _wifiWasConnected;
59+
5460
AsyncWebServer webServer;
5561

5662
DNSServer dnsServer;

src/modules/wifi/karma_attack.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const uint8_t karma_channels[] PROGMEM = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
5454
#define MAC_ROTATION_INTERVAL 30000
5555
#define MAX_PORTAL_TEMPLATES 10
5656
#define MAX_PENDING_PORTALS 10
57-
#define MAX_SSID_DB_SIZE 200
57+
#define MAX_SSID_DB_SIZE 15000
5858
#define MAX_POPULAR_SSIDS 20
5959
#define MAX_NETWORK_HISTORY 30
6060
#define ACTIVE_PORTAL_CHANNEL 0
@@ -2888,4 +2888,4 @@ void saveProbesToFile(FS &fs, bool compressed) {
28882888
file.close();
28892889
}
28902890
}
2891-
}
2891+
}

0 commit comments

Comments
 (0)