[SYS] Bound remaining MQTT/HTTP/BLE/RF inputs into fixed buffers (#2324)

Follow-up to #2321. That PR fixed five strcpy sites in XtoSYS/createOrUpdateDevice
but left adjacent attack surfaces using the same pattern (strncpy with the
destination's own size, leaving the buffer non-NUL-terminated when the source
is at the limit; or strcpy of WiFiManager/HTTP form values that bypass the
client-side maxlength). The over-long string then flows into saveConfig() and
back into a strcpy on next boot, corrupting heap/.bss on every reload.

main/main.cpp - XtoSYS: length-check before copying mqtt_topic, discovery_prefix,
  gateway_name, gw_pass (ota_pass), and ble_aes. Reject over-length with
  THEENGS_LOG_WARNING, preserving existing values.
main/main.cpp - loadConfigFromFlash: new loadStrField() helper applies the same
  guard to every SPIFFS-loaded string (mqtt_server/port/user/pass per slot,
  mqtt_topic, discovery_prefix, gateway_name, ota_pass, ble_aes). Defense in
  depth in case a pre-fix build left an over-length value in /config.json.
main/main.cpp - WiFiManager save: bound mqtt_topic against the 150-byte portal
  buffer overflowing the 66-byte destination (HTML maxlength is client-side
  only; a raw POST bypasses it).
main/webUI.cpp - handleCG /gw save: bound gateway-password form input before
  the strncpy that previously left ota_pass non-NUL-terminated.
main/blufi.cpp - ESP_BLUFI_EVENT_RECV_STA_SSID/PASSWD: clamp the wire-supplied
  ssid_len/passwd_len before the memcpy and the trailing NUL so a crafted
  BluFi provisioning frame cannot overrun gl_sta_ssid[32]/gl_sta_passwd[64].
main/gatewayBT.cpp - BLEDecryptor (Victron, encr==3): remove `nonce[16] = {0}`,
  a one-byte stack-OOB write reachable from any BLE adv. The line was dead
  (the memset two lines later zeroes the whole array); deleting it eliminates
  the OOB write.
main/gatewaySRFB.cpp - SRFBtoX: bound the _uartbuf writer so a 433 MHz frame
  with no STOP can no longer let _uartpos grow past sizeof(_uartbuf).
main/gatewaySRFB.cpp - _rfbSend: the hex output buffer was sized RF_MESSAGE_SIZE
  (9) but _rawToHex writes 2*N+2 bytes per call. Resize to fit.

Tested on a Theengs Bridge: built theengs-bridge-v11 env, OTA-flashed, and
verified that publishing {"discovery_prefix":"Z"*200} to MQTTtoSYS/config
leaves discovery_prefix unchanged (no Z*65 truncation in subsequent discovery
topics), bridge keeps decoding BLE adverts, and SYStoMQTT/LWT stay healthy.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
FlorianandClaude Opus 4.7 authored and GitHub committed 2026-06-14 17:20:24 -05:00
1 parent 2459e1c29b
commit 0b6780678f
5 files changed
+113 -29

No files matched your search

+17 -6
View File
@@ -321,16 +321,27 @@ static void example_event_callback(esp_blufi_cb_event_t event, esp_blufi_cb_para
THEENGS_LOG_TRACE(F("BLUFI recv slave disconnect a ble connection" CR));
esp_blufi_disconnect();
break;
case ESP_BLUFI_EVENT_RECV_STA_SSID:
strncpy((char*)gl_sta_ssid, (char*)param->sta_ssid.ssid, param->sta_ssid.ssid_len);
gl_sta_ssid[param->sta_ssid.ssid_len] = '\0';
case ESP_BLUFI_EVENT_RECV_STA_SSID: {
// ssid_len comes from an unauthenticated BLE provisioning frame; clamp it
// before the strncpy and the trailing NUL so a crafted long SSID cannot
// write past gl_sta_ssid.
size_t ssid_len = param->sta_ssid.ssid_len;
if (ssid_len > sizeof(gl_sta_ssid) - 1)
ssid_len = sizeof(gl_sta_ssid) - 1;
memcpy(gl_sta_ssid, param->sta_ssid.ssid, ssid_len);
gl_sta_ssid[ssid_len] = '\0';
THEENGS_LOG_NOTICE(F("Recv STA SSID %s" CR), gl_sta_ssid);
break;
case ESP_BLUFI_EVENT_RECV_STA_PASSWD:
strncpy((char*)gl_sta_passwd, (char*)param->sta_passwd.passwd, param->sta_passwd.passwd_len);
gl_sta_passwd[param->sta_passwd.passwd_len] = '\0';
}
case ESP_BLUFI_EVENT_RECV_STA_PASSWD: {
size_t passwd_len = param->sta_passwd.passwd_len;
if (passwd_len > sizeof(gl_sta_passwd) - 1)
passwd_len = sizeof(gl_sta_passwd) - 1;
memcpy(gl_sta_passwd, param->sta_passwd.passwd, passwd_len);
gl_sta_passwd[passwd_len] = '\0';
THEENGS_LOG_NOTICE(F("Recv STA PASSWORD" CR));
break;
}
case ESP_BLUFI_EVENT_GET_WIFI_LIST: {
WiFi.scanNetworks(true);
break;