• เพื่อควบคุมเครื่องฟอกอากาศที่ใช้ ESP32 ซึ่งถูกผูกกับแอปของผู้ผลิตและคลาวด์โดยตรงจาก Home Assistant จึงทำการวิศวกรรมย้อนกลับเส้นทางการควบคุมระยะไกลและแทนที่ด้วยเซิร์ฟเวอร์ภายในเครื่อง
  • จากการวิเคราะห์แอป, การหลบเลี่ยง DNS และการดักจับด้วย Wireshark พบว่าอุปกรณ์ส่ง แพ็กเก็ต UDP ไปยัง smartdeviceep.---.com:41014 และใช้โปรโตคอลเฉพาะของตัวเองแทน DTLS มาตรฐาน
  • ผ่านการเชื่อมต่อ UART และการดัมพ์แฟลช 4MB จึงดึง dev_key.key, ใบรับรอง, การตั้งค่าเซิร์ฟเวอร์ และการตั้งค่า WiFi ออกมาได้ แล้ววิเคราะห์โครงสร้างเฟิร์มแวร์ด้วย Ghidra และ esp32knife
  • แพ็กเก็ตประกอบด้วยเฮดเดอร์ 13 ไบต์และ CRC-16 ใน 2 ไบต์สุดท้าย พร้อมการสร้างคีย์แบบ ECDH/HKDF, AES-128-CBC และการซีเรียลไลซ์แบบ MessagePack โดยสามารถถอดรหัสได้สำเร็จหลังแพตช์เฟิร์มแวร์ให้พิมพ์ shared secret ออกทาง serial log
  • โครงสร้างสุดท้ายเชื่อมต่อกันเป็น MITM proxy, local server และ MQTT bridge ที่ใช้ Mosquitto ทำให้ควบคุมการเปิดปิดและความเร็วพัดลมผ่าน MQTT Fan ของ Home Assistant ได้อย่างเสถียรเป็นเวลาหลายสัปดาห์

เปลี่ยนเครื่องฟอกอากาศที่พึ่งพาคลาวด์ให้ควบคุมแบบโลคัลได้

  • เป้าหมายคือควบคุมเครื่องฟอกอากาศที่เชื่อมต่อได้เฉพาะผ่านแอปมือถือของผู้ผลิตและบัญชีคลาวด์ด้วย Home Assistant
  • จากการสลับเปิดปิด Bluetooth, WiFi และ 5G บนโทรศัพท์ พบว่าแอปควบคุมอุปกรณ์ได้ผ่าน การเชื่อมต่ออินเทอร์เน็ต เท่านั้น ไม่ใช่ Bluetooth หรือ WiFi ภายในเครือข่าย
  • เนื่องจากค่าควบคุมอย่างความเร็วพัดลมวิ่งอยู่ระหว่างอุปกรณ์กับเซิร์ฟเวอร์คลาวด์ ช่วงเครือข่ายนี้จึงเป็นจุดโจมตีสำคัญ
    • หากดักทราฟฟิกและเปลี่ยนค่าได้ ก็จะควบคุมอุปกรณ์ได้
    • หากจำลองการตอบกลับของเซิร์ฟเวอร์ได้ ก็จะทำให้อุปกรณ์ทำงานได้โดยไม่ต้องพึ่งอินเทอร์เน็ตและคลาวด์ของผู้ผลิต
  • เนื้อหาการวิศวกรรมย้อนกลับมีไว้เพื่อการศึกษา และข้อมูลอ่อนไหวเฉพาะผลิตภัณฑ์ เช่น private key, โดเมน และ API endpoint ถูกทำให้คลุมเครือหรือลบออกแล้ว
  • การดัดแปลงอุปกรณ์อาจทำให้การรับประกันสิ้นสุดลงหรือทำให้อุปกรณ์เสียหายถาวรได้

การวิเคราะห์แอปและการดักจับทราฟฟิก UDP

  • ดึงไฟล์ .apk ของ Android ออกมา แล้วเปิด classes.dex ด้วย dex2jar และ jd-gui เพื่อตรวจสอบภายใน
  • ใน MainActivity.class ยืนยันได้ว่าแอปพัฒนาด้วย React Native และพบการเชื่อมต่อ WebSocket แบบปลอดภัยใน assets/index.android.bundle
    • โค้ดตัวอย่างมีการเชื่อมต่อไปยัง wss://smartdeviceapi.---.com
  • ใช้ฟังก์ชันดู DNS query ของ Pi-hole เพื่อตรวจสอบโดเมนเซิร์ฟเวอร์คลาวด์ที่อุปกรณ์เชื่อมต่อ
  • ใช้ฟังก์ชัน Local DNS ของ Pi-hole ส่งโดเมนนั้นไปยังเวิร์กสเตชันภายใน 192.168.0.10 แล้วกรองทราฟฟิกของ IP อุปกรณ์ 192.168.0.61 ใน Wireshark
  • อุปกรณ์กำลังส่ง แพ็กเก็ต UDP ไปยังพอร์ต 41014 ของเวิร์กสเตชัน

การตั้งค่ารีเลย์และร่องรอยของโปรโตคอลเฉพาะ

  • เนื่องจาก Local DNS ถูกตั้งให้ resolve โดเมนคลาวด์ไปยังเวิร์กสเตชัน จึงใช้ Cloudflare DNS resolver 1.1.1.1 เพื่อค้นหา IP จริงของเซิร์ฟเวอร์
  • ใช้ node-udp-forwarder ให้เวิร์กสเตชันทำหน้าที่เป็น UDP relay ระหว่างอุปกรณ์กับเซิร์ฟเวอร์คลาวด์
  • จับแพ็กเก็ตแรกตอนบูตและการตอบกลับจากเซิร์ฟเวอร์ได้ แต่ข้อมูลดูเหมือนเป็นไบต์สุ่มโดยไม่มีสตริงที่อ่านออก จึงมีความเป็นไปได้ว่าเข้ารหัสอยู่
  • Wireshark ไม่รู้จักแพ็กเก็ตเป็น DTLS และรูปแบบเฮดเดอร์ตามสเปก DTLS ก็ไม่ตรงกับแพ็กเก็ตที่จับได้
  • เนื่องจากดูไม่ใช่โปรโตคอลมาตรฐาน จึงต้องวิศวกรรมย้อนกลับโครงสร้างแพ็กเก็ตและวิธีเข้ารหัสด้วยตัวเอง

แกะ ESP32 และเข้าถึง serial

  • เมื่อแกะอุปกรณ์ออก พบแผงวงจรหลัก, พอร์ตเชื่อมต่อพัดลม และสายแพของแผงควบคุมด้านหน้า
  • คอนโทรลเลอร์หลักมีสกรีนว่า ESP32-WROOM-32D ซึ่งเป็นไมโครคอนโทรลเลอร์ตระกูล ESP32 ที่รองรับ WiFi และ Bluetooth
  • อ้างอิงข้อมูลด้านการวิศวกรรมย้อนกลับ ESP32 จากรีโพซิทอรี ESP32-reversing
  • ตรวจสอบพิน TXD0 และ RXD0 จาก datasheet ของ ESP32 แล้วไล่ตามลายวงจรที่เชื่อมกับรูพินสำหรับดีบักบน PCB เพื่อหาจุดเชื่อมต่อ serial
  • ใช้ USB-UART Bridge ของ Flipper Zero เพื่อตั้งค่าการเชื่อมต่อ UART
    • TX ของ Flipper Zero เชื่อมกับ RX ของ ESP32
    • RX ของ Flipper Zero เชื่อมกับ TX ของ ESP32
    • GND เชื่อมกับ GND
  • เมื่อเชื่อมต่อผ่าน Putty ที่ COM7 ความเร็ว 115200 ก็มี boot log แสดงออกมา

ไฟล์และการตั้งค่าเซิร์ฟเวอร์ที่เผยออกมาใน boot log

  • serial log แสดงว่า ESP32 เป็นชิปที่มี CPU 2 คอร์, WiFi/BT/BLE และแฟลชภายนอกขนาด 4MB
  • แอปพลิเคชันกำลังทำงานจากพาร์ทิชัน factory
  • มีการเมานต์ไฟล์ซิสเต็ม FAT และแสดงพื้นที่รวม 122 KiB กับพื้นที่ว่าง 0 KiB
  • แอปพลิเคชันกำลังอ่านไฟล์ต่อไปนี้
    • serial
    • dev_key.key
    • SmartDevice-root-ca.crt
    • SmartDevice-signer-ca.crt
    • server_config
  • การตั้งค่าเซิร์ฟเวอร์มี smartdeviceep.---.com:41014 อยู่ด้วย

การดัมพ์แฟลชและโครงสร้างพาร์ทิชัน

  • เพื่อบูต ESP32 เข้าสู่โหมด Download Boot ได้มีการเชื่อมพิน IO0 เข้ากับ GND ขณะเปิดเครื่อง
  • ใช้ esptool ดัมพ์แฟลช 4MB ทั้งหมด
    • คำสั่งคือ esptool -p COM7 -b 115200 read_flash 0 0x400000 flash.bin
  • ทำการดัมพ์หลายครั้งเพื่อยืนยันว่าอ่านได้ถูกต้อง และเก็บสำรองไว้เผื่อจำเป็นต้องแฟลชกลับเมื่อเกิดปัญหา
  • วิเคราะห์ดัมพ์ด้วย esp32knife เพื่อให้ได้ partitions.csv
  • โครงสร้างพาร์ทิชันมีรายการดังนี้
    • nvs: ที่เก็บคีย์-ค่า 16K
    • otadata: ข้อมูล OTA 8K
    • phy_init: ข้อมูล PHY 4K
    • factory: พาร์ทิชันแอป 768K
    • ota_0, ota_1: พาร์ทิชันแอป OTA ขนาด 768K อย่างละหนึ่งชุด
    • storage: พาร์ทิชันข้อมูล FAT 1M
  • ตามข้อมูลจากผู้อ่าน การดัมพ์แฟลชนี้อาจถูกป้องกันได้หากเปิดใช้ flash encryption แต่ในอุปกรณ์นี้ไม่ได้เปิดใช้

คีย์และใบรับรองที่พบใน storage

  • สถานะล่าสุดของพาร์ทิชัน nvs มี SSID และรหัสผ่าน WiFi และในประวัติบันทึกยังเห็นข้อมูลรับรอง WiFi ที่เคยใช้งานก่อนหน้านี้ด้วย
  • พาร์ทิชัน FAT storage ถูกเมานต์เป็นดิสก์เสมือนด้วย OSFMount เพื่อตรวจสอบ
  • ภายใน storage มีไฟล์ต่อไปนี้
    • dev_info
    • dev_key.key
    • serial
    • server_config
    • SmartDevice-root-ca.crt
    • SmartDevice-signer-ca.crt
    • wifi_config
  • dev_key.key ขึ้นต้นด้วย -----BEGIN EC PRIVATE KEY----- ซึ่งเป็น Elliptic Curve private key และยืนยันได้ด้วย openssl ec -in dev_key.key -text -noout
  • ไฟล์ .crt ทั้งสองขึ้นต้นด้วย -----BEGIN CERTIFICATE----- ซึ่งเป็นใบรับรอง และยืนยันได้ด้วย openssl x509
  • เมื่อมีทั้งใบรับรองและคีย์ของอุปกรณ์เก็บอยู่ในตัวเครื่อง จึงมีความเป็นไปได้สูงว่าจะถูกใช้สำหรับเข้ารหัสข้อมูลในแพ็กเก็ต UDP

การตั้งค่าสภาพแวดล้อมการวิเคราะห์ Ghidra

  • เปิดอิมเมจพาร์ทิชัน factory ที่กำลังรันอยู่ใน CodeBrowser ของ Ghidra เพื่อวิเคราะห์
  • เนื่องจาก ESP32 ใช้ชุดคำสั่ง Xtensa จึงเลือกภาษา Tensilica Xtensa 32-bit little-endian
  • เนื่องจากอิมเมจพาร์ทิชันแบบดิบสะท้อนการแมปหน่วยความจำเสมือนได้ไม่ถูกต้อง จึงสร้าง part.3.factory.elf ด้วย esp32knife แล้วนำเข้ามาใหม่
  • เผยแพร่คอมมิตที่แก้ไข esp32knife ให้รองรับเซกเมนต์ RTC_DATA ด้วย
  • โหลดโครงสร้างอุปกรณ์ต่อพ่วงและแผนที่หน่วยความจำของ ESP32 ด้วย SVD-Loader-Ghidra
  • ใช้ SymbolImportScript ของ Ghidra เพื่อนำเข้าป้ายกำกับฟังก์ชัน ROM ของ ESP32 ทำให้ระบุฟังก์ชัน ROM ทั่วไปอย่าง printf ได้ง่ายขึ้น

เบาะแสการเข้ารหัสที่พบจากสตริง

  • ติดตามสตริงที่เห็นใน serial log และสตริงรอบข้างจาก Defined Strings ของ Ghidra
  • ในสตริงรอบข้างพบเบาะแสดังต่อไปนี้
    • Message CRC error
    • Seed Error
    • PRNG fail
    • ECDH setup failed
    • mbedtls_ecdh_gen_public failed
    • mbedtls_ecdh_compute_shared failed
    • MBED HKDF failed
    • Write ECC conn packet
  • mbedtls เป็นไลบรารีโอเพนซอร์สที่ทำงานด้าน cryptographic primitives, การจัดการใบรับรอง X509 และการใช้งาน SSL/TLS กับ DTLS
  • จากการที่มีการใช้ฟังก์ชัน ECDH และ HKDF โดยตรง และไม่ได้ใช้ DTLS จึงวิเคราะห์ได้ว่าได้มีการทำ key exchange และ key derivation ภายในโปรโตคอลที่พัฒนาขึ้นเอง
  • สตริง ECC conn packet แสดงให้เห็นว่าแพ็กเก็ตเชื่อมต่อแรกเกี่ยวข้องกับกระบวนการแลกเปลี่ยนกุญแจ ECDH

แพตช์เฟิร์มแวร์เพื่อตัดการพึ่งพาแผงควบคุม

  • การวิเคราะห์ขณะเชื่อม PCB กับพัดลมและแผงควบคุมนั้นไม่สะดวก จึงถอดแผงควบคุมออก แต่ระหว่างบูตเกิด panic พร้อม log No Cap device found!
  • ฟังก์ชันรอบสตริง No Cap device found! พิมพ์ CapSense Init ออกมาด้วย จึงสรุปได้ว่าเป็นลอจิกเริ่มต้นอินพุตแบบ capacitive ของแผงด้านหน้า
  • ใน Ghidra ตั้งชื่อฟังก์ชันนั้นว่า InitCapSense และตั้งชื่อ service ที่เรียกมันว่า StartCapSenseService
  • เปลี่ยนคำสั่งเรียก StartCapSenseService เป็น nop เพื่อตัดการเริ่ม service ของแผงควบคุมออก
  • แก้ไขไบต์ในอิมเมจ part.3.factory แบบดิบแล้วแฟลชกลับที่ออฟเซ็ต 0x10000 แต่บูตไม่ขึ้นเพราะ checksum ของอิมเมจ ESP32 ผิดพลาด
  • เพิ่มสคริปต์สำหรับซ่อม checksum ของ app partition โดยอ้างอิงลอจิกภายในของ esptool
  • เมื่อนำอิมเมจที่ซ่อม checksum แล้วไปแฟลช อุปกรณ์ก็ทำงานได้ตามปกติแม้ไม่มีแผงควบคุม และการแก้ไขเฟิร์มแวร์ก็สำเร็จ

โครงสร้างส่วนหัวแพ็กเก็ตและ CRC

  • จากการเปรียบเทียบแพ็กเก็ตระหว่างการบูตหลายครั้ง พบว่า 13 ไบต์แรกคล้ายกัน ส่วนที่เหลือดูเหมือนถูกเข้ารหัส
  • รูปแบบส่วนหัวแพ็กเก็ตเป็นดังนี้
    • 55: magic byte สำหรับระบุโปรโตคอล
    • 00 31: ความยาวแพ็กเก็ต
    • 02: ตัวระบุข้อความ
    • 01 23 45 67 89 AB CD EF FF: serial ของอุปกรณ์ 9 ไบต์
  • แพตเทิร์นของ message ID มีดังนี้
    • 0x02: แพ็กเก็ตแรกที่อุปกรณ์สมาร์ตส่งออก
    • 0x82: การตอบกลับแรกที่ cloud server ส่งมา
    • 0x01: แพ็กเก็ตถัดมาที่อุปกรณ์สมาร์ตส่งออก
    • 0x81: การตอบกลับถัดมาที่ server ส่งมา
  • บิตบนใช้แยก client request กับ server response และบิตล่างใช้แยกการแลกเปลี่ยนครั้งแรกกับแพ็กเก็ตถัดไป
  • ไล่ตามฟังก์ชันที่อ้างอิงสตริง Message CRC error เพื่อยืนยันลอจิกตรวจสอบ CRC
  • 2 ไบต์สุดท้ายเป็น checksum แบบ CRC-16 ของแพ็กเก็ตที่เหลือทั้งหมด
    • พหุนามคือ 0x1021
    • ค่าเริ่มต้นคือ 0xFFFF
    • ยืนยันด้วยวิธีเดียวกันจากหลายแพ็กเก็ตที่ดักจับได้

โฟลว์การสร้างกุญแจ ECDH/HKDF

  • ในแพ็กเก็ตที่ดูเหมือนเป็นการแลกเปลี่ยนกุญแจครั้งแรก ข้อมูลที่ตัดส่วนหัว 13 ไบต์และ CRC 2 ไบต์ออกแล้วมีขนาด 32 ไบต์ ซึ่งตรงกับขนาด public key 256 บิต
  • ใน client request มี 00 01 นำหน้าอยู่ และค่าดังกล่าวไม่เปลี่ยนไปในแต่ละการบูต จึงถือว่าเป็นตัวบรรยายข้อมูล
  • ใน Ghidra ไล่ตามสตริง error เพื่อหาฟังก์ชันสร้างกุญแจ แล้วเทียบกับซอร์สของ mbedtls เพื่อสรุปในระดับ pseudocode
  • ฟังก์ชันสร้างกุญแจทำงานดังนี้
    • สร้างคู่กุญแจ ECDH ด้วย mbedtls_ecdh_gen_public
    • พบลักษณะการเขียนทับกุญแจที่สร้างขึ้นด้วยกุญแจอีกชุดหนึ่งในหน่วยความจำ
    • โหลด public key อีกตัวหนึ่ง
    • คำนวณ shared secret ด้วย mbedtls_ecdh_compute_shared
    • สร้างค่ารandom 32 ไบต์ด้วย mbedtls_ctr_drbg_random
    • อนุมานกุญแจสุดท้ายด้วย mbedtls_hkdf
  • การตั้งค่า HKDF เป็นดังนี้
    • แฮช: SHA-256
    • salt: shared secret ของ ECDH
    • input: ค่าสุ่ม 32 ไบต์ที่อุปกรณ์สร้างขึ้น
    • info: serial ของอุปกรณ์ 9 ไบต์
    • ขนาดกุญแจผลลัพธ์: 0x10 หรือ 16 ไบต์
  • ฟังก์ชันที่ถูกเรียกได้ส่งข้อมูลขนาด 0x22 ไบต์ โดยนำค่าสุ่ม 32 ไบต์ไปต่อท้าย 00 01 ซึ่งตรงกับรูปแบบแพ็กเก็ตแลกเปลี่ยนกุญแจแรกที่ดักจับได้

การพิมพ์ shared secret และการถอดรหัส AES

  • การคำนวณกุญแจสำหรับถอดรหัสขั้นสุดท้ายจำเป็นต้องใช้ shared secret ของ ECDH
  • แทนที่จะดีบักผ่าน JTAG ได้ทำการแพตช์เฟิร์มแวร์โดยเขียนทับตำแหน่งลอจิก CapSense ที่ปิดใช้งานไปแล้วด้วยฟังก์ชันแบบกำหนดเอง เพื่อพิมพ์ shared secret ออกทาง serial
  • แทรกการเรียกฟังก์ชันทันทีหลังจากสร้าง shared secret ใน GenerateNetworkKey และใช้ key pointer ในรีจิสเตอร์เพื่อพิมพ์ข้อมูล 32 ไบต์
  • ตอนบูต shared secret ถูกพิมพ์เป็นเลขฐานสิบหกหลัง Write ECC conn packet และแม้รีบูตหลายครั้งค่าก็ไม่เปลี่ยน
  • ยืนยันกุญแจผลลัพธ์จาก HKDF ได้ด้วยแพตช์อีกตัว และสามารถทำซ้ำลอจิกสร้างกุญแจเดียวกันจากแพ็กเก็ตที่ดักจับได้
  • ภายในฟังก์ชันเข้ารหัสพบตารางคงที่ที่ขึ้นต้นด้วย 63 7C 77 7B F2 6B 6F C5 ซึ่งตรงกับ AES Forward S-Box ของ mbedtls
  • รูปแบบการเข้ารหัสสุดท้ายคือ AES-128-CBC และค่าสุ่ม 16 ไบต์ในแพ็กเก็ตถูกใช้เป็น IV
  • ในแพ็กเก็ตที่ถอดรหัสแล้วพบค่าที่อ่านได้ เช่น mirror_data_get, FAN_SPEED, BOOST, FILTER1, FILTER2

การทำ MITM proxy

  • ได้ private key ของอุปกรณ์และลอจิกการอนุมานกุญแจแล้ว อีกทั้งข้อมูลไดนามิกที่จำเป็นก็เปิดเผยอยู่บนเครือข่าย จึงสามารถเขียน MITM proxy ได้โดยไม่ต้องแพตช์เฟิร์มแวร์
  • สคริปต์ Node.js สร้าง local UDP socket และ UDP socket สำหรับ cloud server แล้วส่งต่อแพ็กเก็ตได้สองทาง
  • แพ็กเก็ตที่รับจากอุปกรณ์สมาร์ตจะถูกบันทึก log แล้วส่งต่อไปยัง cloud server และแพ็กเก็ตที่รับจาก cloud server ก็จะถูกบันทึก log แล้วส่งต่อไปยังอุปกรณ์สมาร์ต
  • มองแพ็กเก็ตที่ messageId เป็น 2 ว่าเป็นแพ็กเก็ตแลกเปลี่ยนกุญแจ และใช้ค่าสุ่มภายในนั้นในการคำนวณกุญแจ AES สำหรับแพ็กเก็ตถัดไป
  • ระหว่างควบคุมอุปกรณ์ด้วยแอปมือถือ ได้สะสม log จาก MITM เพื่อยืนยันรูปแบบ request และ response ที่จำเป็นต่อการพัฒนา local server

โครงสร้างข้อความ MessagePack

  • ข้อมูลที่ถอดรหัสแล้วยังคงอยู่ในรูปแบบการซีเรียลไลซ์แบบไบนารี
  • เฮดเดอร์ข้อมูลภายในดูเหมือนจะเป็น ID และความยาวแบบ little-endian
    • 01 00: packet ID
    • 64 00: transaction ID
    • 29 00: ความยาวข้อมูลที่ซีเรียลไลซ์
  • เดิมมีการย้อนวิศวกรรมรูปแบบการซีเรียลไลซ์ด้วยตัวเองบางส่วน แต่เมื่อตรวจสอบแล้วพบว่าเป็น MessagePack
  • หากใช้ implementation อย่าง msgpackr ก็สามารถแปลงข้อมูลไบนารีออกมาเป็นรูปแบบ JSON ได้ง่าย
  • ข้อความหลักที่ยืนยันได้มีดังนี้
    • การแลกเปลี่ยนคีย์: อุปกรณ์ส่งไบต์สุ่มที่ใช้กับ HKDF ไปยังเซิร์ฟเวอร์
    • mirror_data_get: ดึงสถานะเริ่มต้นจากเซิร์ฟเวอร์ตอนบูต
    • connect: ส่ง UUID ของเฟิร์มแวร์ปัจจุบัน และเซิร์ฟเวอร์จะตอบกลับข้อมูลเฟิร์มแวร์ การตั้งค่า เวลา และที่อยู่เซิร์ฟเวอร์
    • mirror_data: เซิร์ฟเวอร์เปลี่ยนสถานะของอุปกรณ์ หรืออุปกรณ์รายงานสถานะที่เปลี่ยนไปกลับไปยังเซิร์ฟเวอร์
    • keep_alive: อุปกรณ์ส่งสถานะเป็นระยะ เช่น RSSI, RTT, packet drop, จำนวนครั้งที่เชื่อมต่อ, uptime เป็นต้น

MQTT bridge และการเชื่อมต่อกับ Home Assistant

  • ใช้ MQTT เพื่อเชื่อม Home Assistant กับ custom server
  • ใน Home Assistant มีการตั้งค่าแอดออน Mosquitto ซึ่งเป็น MQTT broker แบบโอเพนซอร์ส
  • โครงสร้างการเชื่อมต่อเป็น Home AssistantMQTT BrokerCustom ServerSmart Device
  • custom server ทำงานดังนี้
    • เมื่ออุปกรณ์ร้องขอสถานะด้วย mirror_data_get จะตอบกลับโดยใช้ค่า retained ของ MQTT broker หรือใช้ค่าเริ่มต้น
    • เมื่อ Home Assistant ส่งคำสั่งเปลี่ยนสถานะไปยัง MQTT topic, custom server จะส่งต่อคำสั่งนั้นไปยังอุปกรณ์
    • หากสถานะอุปกรณ์เปลี่ยนไม่ว่าด้วยเหตุผลใด custom server จะ publish และ retain แพ็กเก็ต mirror_data ของอุปกรณ์ไปยัง MQTT broker
  • แหล่งข้อมูลจริงของสถานะเป็นอุปกรณ์เสมอ
    • หากการอัปเดตสถานะล้มเหลว จะไม่แสดงบน MQTT broker ว่าอัปเดตแล้ว
    • แม้สถานะจะเปลี่ยนผ่านแผงควบคุมจริง ก็จะสะท้อนไปยัง MQTT broker
  • ใช้การรวมระบบ MQTT Fan ของ Home Assistant เพื่อแมปเครื่องฟอกอากาศเป็นอุปกรณ์พัดลม
  • ใน configuration.yaml มีการตั้งค่า topic สถานะพลังงาน, command topic, topic สถานะความเร็วพัดลม, topic คำสั่งความเร็วพัดลม และช่วงความเร็ว 1~4
  • ตั้งค่าให้ local DNS ของ Pi-hole แก้โดเมนคลาวด์ของผู้ผลิตให้ชี้ไปยัง custom server เพื่อให้เซิร์ฟเวอร์ภายในเครื่องทำหน้าที่เป็นเซิร์ฟเวอร์ของอุปกรณ์

การประเมินความปลอดภัยและผลลัพธ์

  • ผู้ผลิตเลือกใช้โปรโตคอลที่พัฒนาขึ้นเองแทนโปรโตคอลมาตรฐานอย่าง DTLS
  • ยังไม่แน่ชัดว่าแต่ละอุปกรณ์มี private key เฉพาะของตัวเองหรือไม่ แต่ไม่ว่ากรณีไหนก็มีข้อเสีย
    • หากทุกอุปกรณ์ใช้ private key ของเฟิร์มแวร์ร่วมกัน เพียงย้อนวิศวกรรมอุปกรณ์เครื่องเดียวก็อาจพยายามทำการโจมตี MITM กับอุปกรณ์อื่นได้
    • หากแต่ละอุปกรณ์มี private key เฉพาะ เซิร์ฟเวอร์ก็ต้องเก็บ mapping ระหว่าง serial number กับคีย์ของอุปกรณ์ และหากข้อมูลนี้สูญหาย เซิร์ฟเวอร์จะไม่สามารถตอบสนองการสื่อสารของอุปกรณ์ได้
  • มี static private key ฝังอยู่ในเฟิร์มแวร์ ทำให้ผู้โจมตีสามารถดึงคีย์จากการ dump เฟิร์มแวร์เพียงครั้งเดียวแล้วทำการโจมตี MITM ได้
  • ตัว implementation ไม่ได้แย่ไปเสียทั้งหมดในมุมมองด้านความปลอดภัย และการโจมตียังคงต้องอาศัยการเข้าถึงตัวอุปกรณ์จริง
  • การพัฒนาขึ้นเองทำให้การสื่อสารเครือข่ายดูทึบขึ้น แต่ Security through obscurity ใกล้เคียงกับการชะลอการโจมตีทั่วไปที่มุ่งเป้าไปยัง implementation มาตรฐานได้เพียงชั่วคราว และยังเป็นอุปสรรคที่ผู้โจมตีข้ามผ่านได้
  • บรรลุเป้าหมายสุดท้ายคือการเชื่อมต่อกับ Home Assistant และเครื่องฟอกอากาศทำงานได้โดยไม่มีปัญหาตลอดหลายสัปดาห์
  • ยังตั้งค่า automation ให้เร่งเครื่องฟอกอากาศช่วงเวลาหนึ่งเมื่อค่าฝุ่น PM2.5 หรือ VOC จาก air monitor แยกต่างหากสูงเกินไป

ยังไม่มีความคิดเห็น

ยังไม่มีความคิดเห็น