From 399d34ddfdef3933adf78510d659c8abf2562aa5 Mon Sep 17 00:00:00 2001 From: i-am-shodan <6901273+i-am-shodan@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:13:36 +0000 Subject: [PATCH 1/7] this seems to work --- lib/ESP32Marauder/esp32_marauder/SDInterface.cpp | 7 +------ lib/ESP32Marauder/esp32_marauder/settings.cpp | 8 +++++++- platformio.ini | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/ESP32Marauder/esp32_marauder/SDInterface.cpp b/lib/ESP32Marauder/esp32_marauder/SDInterface.cpp index f527727..5940957 100644 --- a/lib/ESP32Marauder/esp32_marauder/SDInterface.cpp +++ b/lib/ESP32Marauder/esp32_marauder/SDInterface.cpp @@ -7,12 +7,7 @@ bool SDInterface::initSD() { } File SDInterface::getFile(String path) { - if (this->supported) { - File file = SD.open(path, FILE_READ); - - //if (file) - return file; - } + return SD.open(path, FILE_READ); } bool SDInterface::removeFile(String file_path) { diff --git a/lib/ESP32Marauder/esp32_marauder/settings.cpp b/lib/ESP32Marauder/esp32_marauder/settings.cpp index 9aec01c..7ee3349 100644 --- a/lib/ESP32Marauder/esp32_marauder/settings.cpp +++ b/lib/ESP32Marauder/esp32_marauder/settings.cpp @@ -197,6 +197,7 @@ bool Settings::toggleSetting(String key) { if (deserializeJson(json, this->json_settings_string)) { esp32m_println("\nCould not parse json"); + return false; } for (int i = 0; i < json["Settings"].size(); i++) { @@ -215,6 +216,8 @@ bool Settings::toggleSetting(String key) { return false; } } + + return true; } String Settings::setting_index_to_name(int i) { @@ -242,12 +245,15 @@ String Settings::getSettingType(String key) { if (deserializeJson(json, this->json_settings_string)) { esp32m_println("\nCould not parse json"); + return ""; } for (int i = 0; i < json["Settings"].size(); i++) { if (json["Settings"][i]["name"].as() == key) return json["Settings"][i]["type"]; } + + return ""; } void Settings::printJsonSettings(String json_string) { @@ -267,7 +273,7 @@ void Settings::printJsonSettings(String json_string) { } bool Settings::createDefaultSettings(fs::FS &fs) { - createDefaultSettings(fs, DEFAULT_SETTING_FILE); + return createDefaultSettings(fs, DEFAULT_SETTING_FILE); } bool Settings::createDefaultSettings(fs::FS &fs, String filename) { diff --git a/platformio.ini b/platformio.ini index 2c2f69c..8818fe1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -75,7 +75,7 @@ lib_deps_core = extends = core platform = https://github.com/platformio/platform-espressif32.git platform_packages = - platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.3 + platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.4 platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 monitor_filters = esp32_exception_decoder monitor_speed = 115200 From 629b02eedb8cbe726836ebcb84f3c22813f9619b Mon Sep 17 00:00:00 2001 From: i-am-shodan <6901273+i-am-shodan@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:39:37 +0000 Subject: [PATCH 2/7] seems to work --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index 8818fe1..f21ccb7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -75,7 +75,7 @@ lib_deps_core = extends = core platform = https://github.com/platformio/platform-espressif32.git platform_packages = - platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.4 + platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.5 platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 monitor_filters = esp32_exception_decoder monitor_speed = 115200 From b2316a359db005b51230208d97d121aef5f7633f Mon Sep 17 00:00:00 2001 From: i-am-shodan <6901273+i-am-shodan@users.noreply.github.com> Date: Wed, 15 Jan 2025 10:29:43 +0000 Subject: [PATCH 3/7] newer version of TinyUSB --- platformio.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platformio.ini b/platformio.ini index f21ccb7..cb0d43d 100644 --- a/platformio.ini +++ b/platformio.ini @@ -95,7 +95,7 @@ lib_deps = ${core.lib_deps_core} AsyncTCP=https://github.com/mathieucarbou/AsyncTCP AsyncTCP-esphome=https://github.com/mathieucarbou/AsyncTCP - https://github.com/i-am-shodan/Adafruit_TinyUSB_Arduino + https://github.com/i-am-shodan/Adafruit_TinyUSB_Arduino#migrateTo334 ESPAsyncWebServer=https://github.com/mathieucarbou/ESPAsyncWebServer ayushsharma82/ElegantOTA@3.1.0 From 06604aab8e9839cf0ffc917874b5ee9c66be1028 Mon Sep 17 00:00:00 2001 From: i-am-shodan <6901273+i-am-shodan@users.noreply.github.com> Date: Wed, 15 Jan 2025 11:58:11 +0000 Subject: [PATCH 4/7] Feels a little snappier with new webserver --- platformio.ini | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/platformio.ini b/platformio.ini index cb0d43d..9ed4a33 100644 --- a/platformio.ini +++ b/platformio.ini @@ -93,10 +93,9 @@ build_flags = -D CONFIG_ASYNC_TCP_MAX_ACK_TIME=3000 lib_deps = ${core.lib_deps_core} - AsyncTCP=https://github.com/mathieucarbou/AsyncTCP - AsyncTCP-esphome=https://github.com/mathieucarbou/AsyncTCP + https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip https://github.com/i-am-shodan/Adafruit_TinyUSB_Arduino#migrateTo334 - ESPAsyncWebServer=https://github.com/mathieucarbou/ESPAsyncWebServer + ESPAsyncWebServer=https://github.com/i-am-shodan/ESPAsyncWebServer ayushsharma82/ElegantOTA@3.1.0 [core-esp32-s3] From 9952e17a224494d9c14a3e28488ea853ea76a24b Mon Sep 17 00:00:00 2001 From: i-am-shodan <6901273+i-am-shodan@users.noreply.github.com> Date: Mon, 20 Jan 2025 16:27:28 +0000 Subject: [PATCH 5/7] Added CDROM example --- .../malicious_ethernet_adapter/autorun.ds | 21 +++++ examples/malicious_ethernet_adapter/cdrom.iso | Bin 0 -> 360448 bytes platformio.ini | 2 +- src/Attacks/Ducky/DuckyPayload.cpp | 23 ++--- src/Attacks/Ducky/Extensions.h | 84 +++++++++++++++++- src/Devices/USB/USBMSC.cpp | 15 +++- src/Devices/USB/USBMSC.h | 2 +- src/Utilities/Settings.cpp | 66 ++++++++++++-- src/Utilities/Settings.h | 3 +- .../LILYGO-T-Dongle-S3/autorun.ds | 0 .../LILYGO-T-Dongle-S3/testcard.png | Bin test/{ => Devices}/M5-Atom-S3U/autorun.ds | 0 .../Waveshare-RP2040-GEEK/autorun.ds | 0 .../Waveshare-RP2040-GEEK/testcard.png | Bin test/Features/HID/autorun.ds | 61 +++++++++++++ 15 files changed, 252 insertions(+), 25 deletions(-) create mode 100644 examples/malicious_ethernet_adapter/autorun.ds create mode 100644 examples/malicious_ethernet_adapter/cdrom.iso rename test/{ => Devices}/LILYGO-T-Dongle-S3/autorun.ds (100%) rename test/{ => Devices}/LILYGO-T-Dongle-S3/testcard.png (100%) rename test/{ => Devices}/M5-Atom-S3U/autorun.ds (100%) rename test/{ => Devices}/Waveshare-RP2040-GEEK/autorun.ds (100%) rename test/{ => Devices}/Waveshare-RP2040-GEEK/testcard.png (100%) create mode 100644 test/Features/HID/autorun.ds diff --git a/examples/malicious_ethernet_adapter/autorun.ds b/examples/malicious_ethernet_adapter/autorun.ds new file mode 100644 index 0000000..89be44c --- /dev/null +++ b/examples/malicious_ethernet_adapter/autorun.ds @@ -0,0 +1,21 @@ +REM when running this script make sure your device starts in Serial mode +DEFINE #SETTING_NAME usbDeviceType +VAR $SETTING_USB_DEVICE_TYPE_ETHERNET = 2 + +IF (GET_SETTING_VALUE() != $SETTING_USB_DEVICE_TYPE_ETHERNET) THEN + USB_MOUNT_CDROM_READ_ONLY /cdrom.iso + + REM wait for the user to install the driver + REM on some OS' a popup will appear but on most modern ones the + REM user will need to be encouraged! + WHILE (AGENT_CONNECTED() == FALSE) + DELAY 2000 + END_WHILE + + REM We now need to change the device into a USB ethernet adapter + SET_SETTING_UINT16 usbDeviceType $SETTING_USB_DEVICE_TYPE_ETHERNET + REM this requires a reset + RESET +ELSE + REM add some DNS entries? +END_IF \ No newline at end of file diff --git a/examples/malicious_ethernet_adapter/cdrom.iso b/examples/malicious_ethernet_adapter/cdrom.iso new file mode 100644 index 0000000000000000000000000000000000000000..4555064d28d14facbfab276edc729af33ed436b8 GIT binary patch literal 360448 zcmeI(T}#_g7y#g8S4HN8jiKIU;>EmJO%&aV?4q=>*7alb1C+4~*$NKagjvCx{iprI zEivr|D{eQ6PM$-XoSyS0IXpS^(9kys6aoYY5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV46j)#Eq`gI2RMJczC?wR*4HzPdY_N`5aYlijp+b{_^on7@NkDVQs| z=broydzx2Qg2kd`F(~DgQt&NYn18$$Jjf5skB^jF6=G@BYkSRBHY*#KF?n|$^D@dN;+y6m8l=^~O~;#2*@!o`;w0HhHj`Idl{gM&8sb07 zY(yB`%-Edue-S9Y(`*Uc0f8Tr_5ThG#SjP(AV7cs0RjXF5FkK+0D&742rr+pXHU}= z0t5&UAV7cs0RjXF5FkL{-U?j)@?XsVPNxtcK!5-N0t5&UAV7cs0Rs0`;NLlRS48am z%EAlF!BWAqv|8*}CwY3=r#w$@zB curListOfFiles; static int curFileIndexVariable = 0; +bool startsWith(const std::string& str, const std::string& prefix) { + return str.rfind(prefix, 0) == 0; +} + static int handleCalc(const std::string &str, const std::unordered_map &constants, const std::unordered_map &variables) { Devices::USB::HID.consumer_device_keypress(HID_USAGE_CONSUMER_AL_CALCULATOR); @@ -103,13 +109,15 @@ static int handleDisplayText(const std::string &str, const std::unordered_map &constants, const std::unordered_map &variables) { + bool mountAsCdrom = startsWith(str, "USB_MOUNT_CDROM_READ_ONLY"); + std::string arg = str.substr(str.find(' ') + 1); const auto entries = Ducky::SplitString(arg); if (entries.size() == 1) { Debug::Log.info(LOG_DUCKY, "Mounting disk " + entries[0]); - return Devices::USB::MSC.mountDiskImage(entries[0]); + return Devices::USB::MSC.mountDiskImage(entries[0], mountAsCdrom); } else { @@ -211,6 +219,45 @@ static int handleSerial(const std::string &str, const std::unordered_map &constants, const std::unordered_map &variables) +{ + bool ret = false; + + do + { + const auto split = Ducky::SplitString(str); + if (split.size() != 3) + { + Debug::Log.error(LOG_DUCKY, "SET_SETTING needs two arguments has: "+std::to_string(split.size() - 1)); + Debug::Log.error(LOG_DUCKY, str); + totalErrors++; + break; + } + + const auto settingName = split[1]; + auto settingValue = split[2]; + + if (startsWith(str, "SET_SETTING_UINT16") && settingValue.rfind("0x", 0) != 0) + { + // need to add 0x to front + settingValue = "0x" + settingValue; + } + + if (startsWith(str, "SET_SETTING_BOOL") && !(settingValue[0] == 0 || settingValue[0] == 1)) + { + Debug::Log.error(LOG_DUCKY, "Invalid bool type, should be 0 or 1"); + totalErrors++; + break; + } + + Debug::Log.info(LOG_DUCKY, "Trying to set "+settingName+" to " +settingValue); + ret = setSettingValue(*preferences, settingName, settingValue); + + } while (false); + + return ret; +} + static int handleUsbNcmPcapOn(const std::string &str, const std::unordered_map &constants, const std::unordered_map &variables) { Devices::USB::NCM.startPacketCollection(); @@ -347,6 +394,34 @@ static int handleButtonPress(const std::string &str, const std::unordered_map &constants, const std::unordered_map &variables) +{ + // str is the current line, we need to peak in the constant #FILE + if (constants.find("#SETTING_NAME") != constants.cend()) + { + bool error = false; + uint16_t value = getIntegerSettingValue(*preferences, constants.at("#SETTING_NAME"), error); + if (error) + { + // todo, this isn't a great pattern. We should be able to set an error here to stop further script + // execution. The best we can do is return 0 but what if a setting was really 0. + // DuckyScript interpeter either needs: + // * a method we can call to stop execution + // * a better function declation that we can return an error through + Debug::Log.error(LOG_DUCKY, "GET_SETTING_VALUE() error, setting is unknown or string type"); + totalErrors++; + return 0; + } + Debug::Log.info(LOG_DUCKY, "GET_SETTING_VALUE() returned " + std::to_string(value)); + return value; + } + else + { + Debug::Log.info(LOG_DUCKY, "Error handling GET_SETTING_VALUE() no param"); + return false; + } +} + static int handleRunPayload(const std::string &str, const std::unordered_map &constants, const std::unordered_map &variables) { const std::string arg = str.substr(str.find(' ') + 1); @@ -572,9 +647,15 @@ void addDuckyScriptExtensions( extCommands["WIFI_OFF"] = handleWiFiOff; extCommands["WIFI_ON"] = handleWiFiOn; extCommands["SERIAL"] = handleSerial; + extCommands["SET_SETTING_BOOL"] = handleSetSetting; + extCommands["SET_SETTING_INT16"] = handleSetSetting; + extCommands["SET_SETTING_UINT16"] = handleSetSetting; + extCommands["SET_SETTING_STRING"] = handleSetSetting; + // USB extCommands["USB_MOUNT_DISK_READ_ONLY"] = handleUSBMode; + extCommands["USB_MOUNT_CDROM_READ_ONLY"] = handleUSBMode; extCommands["USB_NCM_PCAP_ON"] = handleUsbNcmPcapOn; extCommands["USB_NCM_PCAP_OFF"] = handleUsbNcmPcapOff; extCommands["WAIT_FOR_USB_STORAGE_ACTIVITY"] = handleWaitForUSBStorageActivity; @@ -594,6 +675,7 @@ void addDuckyScriptExtensions( extCommands["LOAD_FILES_FROM_SD()"] = handleFileIndex; extCommands["BUTTON_LONG_PRESS()"] = handleButtonPress; extCommands["BUTTON_SHORT_PRESS()"] = handleButtonPress; + extCommands["GET_SETTING_VALUE()"] = handleGetSettingValue; consts.emplace_back([] { diff --git a/src/Devices/USB/USBMSC.cpp b/src/Devices/USB/USBMSC.cpp index 720e2a8..c616331 100644 --- a/src/Devices/USB/USBMSC.cpp +++ b/src/Devices/USB/USBMSC.cpp @@ -184,14 +184,23 @@ void USBMSC::end() } } -bool USBMSC::mountDiskImage(const std::string &imageLocation) +bool USBMSC::mountDiskImage(const std::string &imageLocation, bool mountAsCD) { std::size_t size = open_msc(imageLocation.c_str()); if (size != 0) { + usb_msc.setCDROM(mountAsCD); + // Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively - usb_msc.setID("Adafruit", "Mass Storage", "1.0"); + if (mountAsCD) + { + usb_msc.setID("Adafruit", "USB CDROM", "1.0"); + } + else + { + usb_msc.setID("Adafruit", "Mass Storage", "1.0"); + } usb_msc.setCapacity(size / LOGICAL_BLOCK_SIZE, LOGICAL_BLOCK_SIZE); @@ -209,7 +218,7 @@ bool USBMSC::mountDiskImage(const std::string &imageLocation) return false; } - Debug::Log.info(TAG_USB, "Disk image mounted"); + Debug::Log.info(TAG_USB, mountAsCD ? "CDROM image mounted" : "Disk image mounted"); return true; } else diff --git a/src/Devices/USB/USBMSC.h b/src/Devices/USB/USBMSC.h index 39330dd..12deaef 100644 --- a/src/Devices/USB/USBMSC.h +++ b/src/Devices/USB/USBMSC.h @@ -13,7 +13,7 @@ class USBMSC : USBArmyKnifeCapability { virtual void begin(Preferences& prefs); virtual void end(); - bool mountDiskImage(const std::string&); + bool mountDiskImage(const std::string&, bool mountAsCD); bool mountSD(); bool hasActivity() { return mscActivity; } void resetActivityState() { mscActivity = false; } diff --git a/src/Utilities/Settings.cpp b/src/Utilities/Settings.cpp index 31870d0..36a7a5c 100644 --- a/src/Utilities/Settings.cpp +++ b/src/Utilities/Settings.cpp @@ -20,9 +20,10 @@ void registerSettingName(const uint8_t group, const std::string &name, const USB // ensure we can't register the same name in the same group twice const bool notFound = std::none_of(settingLookup[group].cbegin(), settingLookup[group].cend(), - [&name](const std::tuple &item){ - return std::get<0>(item) == name; - }); + [&name](const std::tuple &item) + { + return std::get<0>(item) == name; + }); if (notFound) { @@ -62,8 +63,9 @@ bool setSettingValue(Preferences &prefs, const std::string &name, const std::str } break; case USBArmyKnifeCapability::SettingType::Int16: - tempInt = std::stoi(value); - if (tempInt <= static_cast(INT16_MAX) && tempInt >= static_cast(INT16_MIN)) { + tempInt = std::stoi(value); + if (tempInt <= static_cast(INT16_MAX) && tempInt >= static_cast(INT16_MIN)) + { int16_t int16Value = 0; int16Value = static_cast(tempInt); prefs.putShort(name.c_str(), int16Value); @@ -77,8 +79,9 @@ bool setSettingValue(Preferences &prefs, const std::string &name, const std::str case USBArmyKnifeCapability::SettingType::UInt16: if (value.rfind("0x", 0) == 0) { - tempInt = std::stoi(value, nullptr, 16); - if (tempInt <= static_cast(INT16_MAX) && tempInt >= 0) { + tempInt = std::stoi(value, nullptr, 16); + if (tempInt <= static_cast(INT16_MAX) && tempInt >= 0) + { uint16_t uint16Value = 0; uint16Value = static_cast(tempInt); prefs.putUShort(name.c_str(), uint16Value); @@ -105,6 +108,55 @@ bool setSettingValue(Preferences &prefs, const std::string &name, const std::str return ret; } +uint16_t getIntegerSettingValue(Preferences &prefs, const std::string &name, bool &error) +{ + error = false; + + for (const auto &settingCategory : settingLookup) + { + const std::vector> &settingInCategory = settingCategory.second; + for (const auto &setting : settingInCategory) + { + const std::string &settingName = get<0>(setting); + const USBArmyKnifeCapability::SettingType &settingType = get<1>(setting); + + if (settingName != name) + { + continue; + } + else if (settingType == USBArmyKnifeCapability::SettingType::String) + { + goto error; + } + else if (!prefs.isKey(settingName.c_str())) + { + const std::string &defaultValue = get<2>(setting); + return (uint16_t)atoi(defaultValue.c_str()); + } + else + { + switch (settingType) + { + case USBArmyKnifeCapability::SettingType::Bool: + return prefs.getBool(settingName.c_str()); + break; + case USBArmyKnifeCapability::SettingType::Int16: + return prefs.getShort(settingName.c_str()); + break; + case USBArmyKnifeCapability::SettingType::UInt16: + return prefs.getUShort(settingName.c_str()); + default: // string and other types + break; + }; + } + } + } + +error: + error = true; + return 0; +} + void enumerateSettingsAsJson(Preferences &prefs, JsonArray array) { for (const auto &settingCategory : settingLookup) diff --git a/src/Utilities/Settings.h b/src/Utilities/Settings.h index b33b772..be6dae5 100644 --- a/src/Utilities/Settings.h +++ b/src/Utilities/Settings.h @@ -9,4 +9,5 @@ void registerSettingName(const uint8_t group, const std::string &name, const USBArmyKnifeCapability::SettingType &type, const std::string& defaultValue); void enumerateSettingsAsJson(Preferences &prefs, JsonArray array); -bool setSettingValue(Preferences &prefs, const std::string &name, const std::string &value); \ No newline at end of file +bool setSettingValue(Preferences &prefs, const std::string &name, const std::string &value); +uint16_t getIntegerSettingValue(Preferences &prefs, const std::string &name, bool &error); \ No newline at end of file diff --git a/test/LILYGO-T-Dongle-S3/autorun.ds b/test/Devices/LILYGO-T-Dongle-S3/autorun.ds similarity index 100% rename from test/LILYGO-T-Dongle-S3/autorun.ds rename to test/Devices/LILYGO-T-Dongle-S3/autorun.ds diff --git a/test/LILYGO-T-Dongle-S3/testcard.png b/test/Devices/LILYGO-T-Dongle-S3/testcard.png similarity index 100% rename from test/LILYGO-T-Dongle-S3/testcard.png rename to test/Devices/LILYGO-T-Dongle-S3/testcard.png diff --git a/test/M5-Atom-S3U/autorun.ds b/test/Devices/M5-Atom-S3U/autorun.ds similarity index 100% rename from test/M5-Atom-S3U/autorun.ds rename to test/Devices/M5-Atom-S3U/autorun.ds diff --git a/test/Waveshare-RP2040-GEEK/autorun.ds b/test/Devices/Waveshare-RP2040-GEEK/autorun.ds similarity index 100% rename from test/Waveshare-RP2040-GEEK/autorun.ds rename to test/Devices/Waveshare-RP2040-GEEK/autorun.ds diff --git a/test/Waveshare-RP2040-GEEK/testcard.png b/test/Devices/Waveshare-RP2040-GEEK/testcard.png similarity index 100% rename from test/Waveshare-RP2040-GEEK/testcard.png rename to test/Devices/Waveshare-RP2040-GEEK/testcard.png diff --git a/test/Features/HID/autorun.ds b/test/Features/HID/autorun.ds new file mode 100644 index 0000000..8a2cc11 --- /dev/null +++ b/test/Features/HID/autorun.ds @@ -0,0 +1,61 @@ +KEYBOARD_LAYOUT win_en-GB + +GUI R +DELAY 1000 +STRING notepad +ENTER +DELAY 1000 + +STRING 3 +DELAY 1000 +ENTER +STRING 2 +DELAY 1000 +ENTER +STRING 1 +DELAY 1000 +ENTER +ENTER + +STRING 1. STRING block test - should print abcd +ENTER +DELAY 1000 +STRING + a + b + c + d +END_STRING +ENTER +ENTER +ENTER + +STRING 2. STRINGLN block test - should print 'The quick brown fox jumps over the lazy dog' three times +ENTER +STRINGLN + The quick brown fox jumps over the lazy dog + The quick brown fox jumps over the lazy dog + The quick brown fox jumps over the lazy dog +END_STRINGLN +ENTER +ENTER + +STRING 3. ASCII Art test +ENTER + +STRINGLN . : +STRINGLN . ,; t#, +STRINGLN . . . f#i i i ;##W. +STRINGLN . Di Dt .E#t LE LE :#L:WE +STRINGLN . E#i E#i i#W, L#E L#E .KG ,#D +STRINGLN . E#t E#t L#D. G#W. G#W. EE ;#f +STRINGLN . E#t E#t :K#Wfff; D#K. D#K. f#. t#i +STRINGLN . E########f. i##WLLLLt E#K. E#K. :#G GK +STRINGLN . E#j..K#j... .E#L .E#E. .E#E. ;#L LW. +STRINGLN . E#t E#t f#E: .K#E .K#E t#f f#: +STRINGLN . E#t E#t ,WW; .K#D .K#D f#D#; +STRINGLN . f#t f#t .D#; .W#G .W#G G#t +STRINGLN . ii ii tt :W##########Wt :W##########Wt t +STRINGLN . :,,,,,,,,,,,,,.:,,,,,,,,,,,,,. +STRINGLN . +ENTER \ No newline at end of file From 9d96502d5cfb1bef65e92f165c7ca83ebe729061 Mon Sep 17 00:00:00 2001 From: i-am-shodan <6901273+i-am-shodan@users.noreply.github.com> Date: Tue, 21 Jan 2025 16:46:24 +0000 Subject: [PATCH 6/7] Lots of good testing done --- data/autorun.ds | 57 +++++++++- platformio.ini | 5 +- src/Devices/LED/APA102/HardwareLED.cpp | 2 +- src/Devices/LED/FastLED/HardwareLED.cpp | 2 +- .../Waveshare-ESP32-S3-LCD-1_47/autorun.ds | 105 ++++++++++++++++++ .../Waveshare-ESP32-S3-LCD-1_47/testcard.png | Bin 0 -> 25405 bytes 6 files changed, 164 insertions(+), 7 deletions(-) create mode 100644 test/Devices/Waveshare-ESP32-S3-LCD-1_47/autorun.ds create mode 100644 test/Devices/Waveshare-ESP32-S3-LCD-1_47/testcard.png diff --git a/data/autorun.ds b/data/autorun.ds index 092514f..16ab79f 100644 --- a/data/autorun.ds +++ b/data/autorun.ds @@ -1,2 +1,55 @@ -REM This file is only used by platforms without an SD card -REM Replace this file with your own content \ No newline at end of file +REM This is the system test for M5-Atom-S3 based boards +REM These do not have a screen or SD card. + +REM To run these tests copy this file to the data directory and use the upload filesystem command or platformio +REM You'll also need to have the agent running. You should see: + +REM * Web page loaded via badUSB + +REM You should connect to the device over WiFi and check +REM * The web interface loads +REM * VNC works +REM * You can see logs +REM * Results from the agent run are in the logs + +REM Now press the button and ensure the LED cycles +REM * RED +REM * GREEN +REM * BLUE +REM * OFF + +REM Press the button again and the device will do an ESP32 Maurader rick roll +REM * Ensure that the lyrics can be seen over WiFi + +REM Press once more for a reset +REM * Device resetting + +WHILE (AGENT_CONNECTED() == FALSE) + DELAY 2000 +END_WHILE + +REM Run a command to ensure we can handle the results +AGENT_RUN dir +WAIT_FOR_AGENT_RUN_RESULT + +REM Load a webpage to ensure USB + HID is working +GUI +DELAY 1000 +STRING https://github.com/i-am-shodan/USBArmyKnife/blob/master/docs/images/interface-status.png +ENTER + +LED_R +WAIT_FOR_BUTTON_PRESS +LED_G +WAIT_FOR_BUTTON_PRESS +LED_B +WAIT_FOR_BUTTON_PRESS +LED_OFF + +WIFI_OFF +ESP32M attack -t rickroll +WAIT_FOR_BUTTON_PRESS + +REM reset the device and do it all again +DELAY 5000 +RESET \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index e38b59e..4b2f82b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -182,7 +182,7 @@ build_flags = -D HAS_SD ; ESP32 Maurader -D USE_SD_MMC_INTERFACE ; ESP32 Maurader -D NO_MIC - -D BOARD_HAS_PSRAM + ;-D BOARD_HAS_PSRAM ;;;;;;;; Pin Config for TFT ;;;;;;;; -D DISPLAY_RST=39 -D DISPLAY_DC=41 @@ -207,11 +207,10 @@ build_flags = -D BTN_PIN=0 -D NUM_LEDS=1 -D LED_DI_PIN=38 - -D LED_CI_PIN=38 ;;;;;;;; End of Pin Config ;;;;;;;; lib_deps = ${core-esp32-s3.lib_deps} - https://github.com/pololu/apa102-arduino + fastled/FastLED [env:Generic-ESP32-S2] extends = core-esp32 diff --git a/src/Devices/LED/APA102/HardwareLED.cpp b/src/Devices/LED/APA102/HardwareLED.cpp index 0a897fe..5ad531d 100644 --- a/src/Devices/LED/APA102/HardwareLED.cpp +++ b/src/Devices/LED/APA102/HardwareLED.cpp @@ -1,5 +1,5 @@ #ifndef NO_LED -#ifdef LED_CI_PIN +#if defined(LED_DI_PIN) && defined(LED_CI_PIN) #include "../HardwareLED.h" #include diff --git a/src/Devices/LED/FastLED/HardwareLED.cpp b/src/Devices/LED/FastLED/HardwareLED.cpp index f9d4577..d5019c6 100644 --- a/src/Devices/LED/FastLED/HardwareLED.cpp +++ b/src/Devices/LED/FastLED/HardwareLED.cpp @@ -1,5 +1,5 @@ #ifndef NO_LED -#ifndef LED_CI_PIN +#if defined(LED_DI_PIN) && !defined(LED_CI_PIN) #include "../HardwareLED.h" #include diff --git a/test/Devices/Waveshare-ESP32-S3-LCD-1_47/autorun.ds b/test/Devices/Waveshare-ESP32-S3-LCD-1_47/autorun.ds new file mode 100644 index 0000000..5cf09cf --- /dev/null +++ b/test/Devices/Waveshare-ESP32-S3-LCD-1_47/autorun.ds @@ -0,0 +1,105 @@ +REM This is the system test for Waveshare-ESP32-S3-LCD-1_47 based boards +REM These having most of the USB Army Knife capabilities but a rubbish case! + +REM To run these tests copy this file and disk.img from the covert storage example to the SD +REM You'll also need to have the agent running. You should see: +REM * Test card with correct colours +REM * Pulsing heartbeat in the lower right of the TFT +REM * Web page loaded via badUSB +REM * Disk mounted + +REM At this point you should connect to the device over WiFi and check +REM * The web interface loads +REM * VNC works +REM * You can see logs + +REM Now press the button and ensure the LED cycles +REM * RED +REM * GREEN +REM * BLUE + +REM Press the button again and the device will do an ESP32 Maurader rick roll +REM * Ensure that the lyrics can be seen over WiFi + +REM Press once more for a reset +REM * Device resetting + +REM Wait for the agent, this also allows us to see some debug if we are monitoring +WHILE (AGENT_CONNECTED() == FALSE) + DELAY 2000 +END_WHILE + +REM Turn TFT off and on +TFT_OFF +DELAY 2000 +TFT_ON + +REM This is the system test for RP2040 based boards +REM At time of writing these don't have WiFi, LED or a button + +REM To run these tests copy this file and disk.img from the covert storage example to the SD +REM You'll also need to have the agent running. You should see: +REM * Test card with correct colours +REM * Pulsing heartbeat in the lower right of the TFT +REM * Web page loaded via badUSB +REM * Disk mounted +REM * Device resetting + +DISPLAY_CLEAR +DISPLAY_TEXT 0 0 Agent connected +DELAY 3000 + +REM Load a test card to test TFT PNG decoding and colours +DISPLAY_PNG /testcard.png +DELAY 10000 +DISPLAY_CLEAR +DISPLAY_TEXT 0 0 Agent connected +DISPLAY_TEXT 0 10 Test card + +REM Run a command to ensure we can handle the results +AGENT_RUN dir +WAIT_FOR_AGENT_RUN_RESULT + +DISPLAY_TEXT 0 20 Agent run command +REM Load a webpage to ensure USB + HID is working +GUI +DELAY 1000 +STRING https://github.com/i-am-shodan/USBArmyKnife/blob/master/docs/images/interface-status.png +ENTER + +DISPLAY_TEXT 0 30 Turning services off +REM Turn some services off and mount as a disk, some devices won't support this but must not error +LED_OFF + +DISPLAY_CLEAR +DISPLAY_TEXT 0 0 Do WiFI check +DISPLAY_TEXT 0 10 Then press button +WAIT_FOR_BUTTON_PRESS +DISPLAY_CLEAR + +DISPLAY_TEXT 0 0 Agent connected +DISPLAY_TEXT 0 10 Test card +DISPLAY_TEXT 0 20 Agent run command +DISPLAY_TEXT 0 30 Turning services off + +DISPLAY_TEXT 0 40 Mounting USB disk +USB_MOUNT_DISK_READ_ONLY /disk.img +WAIT_FOR_BUTTON_PRESS + +LED_R +WAIT_FOR_BUTTON_PRESS +LED_G +WAIT_FOR_BUTTON_PRESS +LED_B +WAIT_FOR_BUTTON_PRESS +DISPLAY_TEXT 0 50 LEDs and buttons + +WIFI_OFF +ESP32M attack -t rickroll +DISPLAY_TEXT 0 60 ESP32 Maurader +WAIT_FOR_BUTTON_PRESS + +REM reset the device and do it all again +DISPLAY_TEXT 0 70 Reset in 5s +DELAY 5000 +RESET \ No newline at end of file diff --git a/test/Devices/Waveshare-ESP32-S3-LCD-1_47/testcard.png b/test/Devices/Waveshare-ESP32-S3-LCD-1_47/testcard.png new file mode 100644 index 0000000000000000000000000000000000000000..3219f6fd97bd40c0678caa1b1d9fd3c19f557ce0 GIT binary patch literal 25405 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 Date: Tue, 21 Jan 2025 17:03:57 +0000 Subject: [PATCH 7/7] Fixed a RP2040 bug and updated the docs --- README.md | 1 + src/Devices/USB/USBMSC.cpp | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 24144f6..58cf952 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ This project implements a variety of attacks based around an easily concealable | [Simple UI](./examples/simple_ui/) | A simple yet powerful UI to select scripts/images and run these using the hardware button. Shows how you can build complex UI interactions simply. | | [Stream Mic audio over WiFi](./examples/hotmic/) | The M5Stack AtomS3U has a microphone that you can stream over WiFi. | | [Instantly crash Linux boxes](./examples/linux_panic/) | Deploy a bad filesystem which cause Linux machines which automount to panic. | +| [Evil USB CDROM/NIC](./examples/malicious_ethernet_adapter/) | Pretend to be a USB NICs which requires a driver from a CDROM device that appears when you plug the NIC in. | ## Supported Hardware diff --git a/src/Devices/USB/USBMSC.cpp b/src/Devices/USB/USBMSC.cpp index c616331..f766562 100644 --- a/src/Devices/USB/USBMSC.cpp +++ b/src/Devices/USB/USBMSC.cpp @@ -186,11 +186,21 @@ void USBMSC::end() bool USBMSC::mountDiskImage(const std::string &imageLocation, bool mountAsCD) { +#ifdef ARDUINO_ARCH_RP2040 + if (mountAsCD) + { + Debug::Log.error(TAG_USB, "CDROM support is not supported on this device"); + return false; + } +#endif + std::size_t size = open_msc(imageLocation.c_str()); if (size != 0) { +#ifndef ARDUINO_ARCH_RP2040 usb_msc.setCDROM(mountAsCD); +#endif // Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively if (mountAsCD) @@ -223,7 +233,7 @@ bool USBMSC::mountDiskImage(const std::string &imageLocation, bool mountAsCD) } else { - Debug::Log.info(TAG_USB, "Could not load " + imageLocation); + Debug::Log.error(TAG_USB, "Could not load " + imageLocation); return false; } } \ No newline at end of file