Gần đây mình có thực hiện nghiên cứu liên quan đến mảng thiết bị IOT, theo một dự án pentest mình được tiếp cận tới một thiết bị Hikvision Camera.

Trước tiên khi đánh giá thì ít nhất thì cũng phải có firmware sau đó extract binary. Binary được sử dụng để run các dịch vụ trên thiết bị như port RSTP sử dụng để streaming data xem CAM từ xa, port Webapp cho web người dùng có thể vào xem video, quản lý thiết bị, ….

Một cách đơn giản là vào trang web của nhà cung cấp thiết bị download firmware. Sau đó phân tích bằng một số công cụ trên linux: strings, binwalk nhưng không có được kỳ vọng như mong đợi, không có bất kỳ một chuỗi nào có nghĩa (strings), binwalk không phân tích được bất kỳ phân vùng nào.  Như vậy có thể khẳng định rằng firmware của thiết bị đã bị mã hóa.

Có một số cách trước đây được dùng thể lấy firmware không bị mã hóa là từ bộ nhớ sau khi thiết bị thực hiện decrypt firmware hoặc binary từ một lỗi như LFI, file backup,.. bằng  kỹ thuật blackbox hay truy cập vào terminal của hệ điều hành để lấy ra. Bằng cách kết nối thiết bị với pin UART cho phép giao tiếp với bootloader và terminal OS.

  • Uboot thông thường sẽ có một vài chức năng cho phép cập nhật firmware và môt số chức năng đặc biệt khác cho phép bạn có thể lợi dụng nó để dump firmware sau khi bootloader decrypt firmware từ bộ nhớ. Nhưng nhà sản xuất đã loại bỏ những tính năng không cần thiết để giảm thiểu lộ các thông tin nhạy cảm từ việc dump bộ nhớ, thay vào đó bằng chức năng update, updateb an toàn hơn là không cho phép truy cập bộ nhớ. Dó đó cách này không thể giúp lấy firmware được.
  • Thực hiện kiểm thử blackbox không phát hiện lỗi nào nguy hiểm cho phép lấy được binary.
  • Truy cập vào terminal của hệ điều hành đã bị thay thế vào đó bằng các chức năng cụ thể như liệt kê danh sách port, tiến trình, … điều quan tâm là không có chức năng nào cho phép truy cập đến các tập tin và có gắng unbirk để có được /bin/sh nhưng phần này được dev khá kỹ bằng việc cấm ghi một số environment.

Chức năng update firmware trên webmanager không thể reverse vì không tìm được phiên bản firmware không mã hóa trước đó hay có được từ bước trên. Vì vậy quyết định sẽ thực hiện reverse chức năng update của bootloader để tìm key, decrypt firmware và phần nội dung chính.

Dump Bootloader (Uboot)

Trong quá tình tìm cách dump plain text firmware ở phần trước (log boot device) có đưa ra một số thông tin hữu ích như phân vùng của bootloader nằm vị trí: 0x0 - 0x60000. Mỗi phần vùng khác chúng cách nhau bởi các byte NULL(0xFF) trên flash (nhận định sau khi dump flash).

Lúc này sử dụng công cụ flashrom và thực hiện kết nối thiết bị (PI) của mình giao tiếp với CHIP flash thông qua giao thức SPI.

 SPI protocol

Lúc đầu có nhờ anh đồng nghiệp hàn giúp mấy cái dây để kết nối như hình dưới nhưng trong quá trình rút ra cắm vào và cắm vào lại rút ra bị đất khá nhiều lần nhờ nhiều thì ngại thì thôi mình tự làm thì hơn.

Solder chip flash

Mỗi loại chip flash của các nhà sản xuất khác nhau sẽ có manufurer ID khác nhau và cách lệnh để giao tiếp với chip khác nhau. Flashrom sẽ thực hiện theo các bước sau:

  • Flashrom gửi một số lệnh để xác định ID của chip từ đó xác định được loại chip, nhà sản xuất, các lệnh chức năng của chip đó.
  • Thực hiện các chức năng đọc, ghi, xóa của người dùng.
  • Chip flash trả lại kết quả ứng với mỗi lệnh.

Flashrom xác định loại chip flash và đọc ghi nôi dung :

Flashrom –p linux_spi:dev=/dev/spidev0.0,spispeed=[speed][k|m] –V
Flashrom –p linux_spi:dev=/dev/spidev0.0,spispeed=[speed][k|m] –c [name chip] –r|w [file name]

  • Spidev0.0 tương ứng với pin 19, pin 21, spdev0.1 tương ứng với pin pin 35, pin 20 của PI
  • Speed: tốc độ phù hợp với chip flash và PI

Flashroom không hỗ trợ tất cả các loại chip, đôi khi sẽ phải thực hiện viết chương trình với mục đích mong muốn chip.

Analyze bootloader

Trước khi đi vào phân tích bootloader hãy xem một quá trình boot sẽ diễn như thế nào và tạm gọi quá trình đó gồm có 3 giai đoạn như sau:

  • Stage 1: Chịu tránh nhiệm khởi tạo board và cấp phát, load bootloader được lưu trên SD, FLASH, eMMC, … bởi chương trình Bootrom trong CHIP sau đó xử lý chuyển hướng luồng thực thi tới chương trình chính trong bootloader.
  • Stage 2: bootloader thực hiện load biến môi trường cung cấp một số chức năng load kernel, boot kernel, …. Bootloader sẽ chuyển hướng boot kernel được định nghĩa trong biến môi trường.
  • Stage 3: Kernel sẽ thực hiện khởi tạo hardware, mount filesystem, khởi tạo card mạch, chạy dịch vụ được cấu hình.

Như vậy cần tìm entrypoint là phần code được thực thi đầu tiên của bootloader, không biết được tên chip do đó không xác đinh được điểm bắt đầu.

Thực hiện như thường lệ load cipher text bootloader(CT_bootloader) từ phần 1 vào công cụ IDA. Cửa sổ strings IDA không có quá nhiều strings do khả năng CT_bootloader được compress hoặc encrypt, Đoạn strings không có thông tin về referent do đoạn code sử dụng strings đó không được IDA nhận nhiện code mà là data.

IDA Window Strings 

Lần này khá hoang mang không biết bắt đầu từ đâu, sau khi trấn tĩnh xác định được đoạn structure data. Mãi sau khi reverse mới biết đoạn này được sử dụng để khởi tạo các thiêt bị bộ nhớ, clock, thiết bị ngoại vi.

Raw Data
Structure Data

Sau khi thu hẹp được phạm vi chuyển một số đoạn data thành code bằng nhấn phím C trên IDA và có viết đoạn code nhỏ để chuyển một đoạn bất kỳ cho nhanh và đoạn data có cấu trúc.

IDA Window Xref

Sau khi có referent của strings, trace ngược lại thì tìm được endtrypoint

Entry point Bootloader

Đoạn dữ liệu có cấu trúc được sử dụng bởi hàm init_register_, tiếp tới  có hàm copy_to_ddr  được sử dụng để relocate Uboot, sau đó uboot được Uncompress.

Init Register and relocate CT_bootloader

Sau nhiều giờ reverse hàm Uncompress khá bế tắc không biết nó dùng thuật toán gì hay tự code.

Dạo này cũng đi coi khá nhiều hội thảo trong đó có nói về unicorn cho phép emulator CPU, nó cũng có những hạn chế nhất định như không hiểu được khái niệm ở mức cao như dynamic libraries, system calls, I/O handling hoặc executable formats like PE, MachO, ELF.

Để có thể emulator function Uncompress bạn cần khởi tạo memory chứa code static, memory chứa uboot trước, sau khi được uncompress, xác định tham số chuyền vào hàm và điểm kết thúc của hàm uncompress.

Tiếp tục reverse sẽ thấy được hàm start của Uboot đi sâu vào trong sẽ thấy khá quen thuộc.

Uncompress bootloader and jump to start of bootloader

Uboot sẽ nhận lệnh từ người dùng sau đó kiểm tra trong TABLE_CMD xem lệnh có tồn tại không? Nếu tồn tại thì thực thi lệnh băng hàm func_uboot() là một struct function như hình bên dưới.:

Call uboot function
Table uboot command 

Trong quá trình phân tích static với IDA sẽ có tính huống gặp phải. Hàm được gọi từ một struct_function nó được gán danh sách địa chỉ của hàm, để giải quyết bạn cần tìm tới đoạn gán giá trị đó mới có thể đi tiếp được.

Unknown function
Assigned address function to structure.
Clear function

Analyze Decrypted firmware

Sau các bước thực hiện ở trên có thể hình dung quá trình tìm key, decrypt firmware sẽ diễn ra như sau.

Workflow bootloader
Workflow decrypt firmware

Phân tích khối đầu tiên, phần header_1 of CT_firmware được xor KEY_XOR, sau đó sẽ thực hiện kiểm tra checksum.

Load key and decode header 1
Check header 1 (PT_H1_firmware)
Key XOR

Định dạng của header như sau:

  • HK02 là tên deader 1.
  • 0x2112 là checksum header 1.
  • 0x6C độ dài header 1 .
  • 0x1 là có một package nằm trong firmware.
  • 0x6C là offset phần header tiếp theo.
  • Tương tự với header 2 tên header 2, checksum, length:
Plain Text Header 1

Phần header 2 và phần body của CP_H2_firmware được encrypt sử dụng:

  • Thuật toán AES 256 và chế độ ECB:
  • Khóa được chọn trong TABLE_KEY_STATIC bằng giá trị được cố định như: 0x850000,
  • Khóa static được gen ra chỗi khác để giải mã.
Load key AES and gen key
Table Key Static

Có thể thấy phần body của header 2 (PT_H2_BODY_Firmware) có chứa

  • Danh sách tập tin _cfgUpgClass, LiteOS.bin, ipc_db.jffs2
  • Các phần bôi đỏ lần lượt là tên file, offset của file, length, checksum, signature được ký bằng thuật toán rsa.
Plain Text Body Header 2

PT_H2_BODY_FIRMWARE được xác minh bằng crc32 và signature bắng thuật toán RSA.

Calculate Checksum
Verify Body
RSA public key and private key

Khối số 3 sẽ thực hiện decrypt tập tin sau khi biết được vị trí, độ dài của từng file. Lần này uboot sẽ chọn một key khác để giải mã 3 tập tin và quá trình giải mã diễn ra như khối số 2

Select key AES decrypt

Tập tin giải mã thu được và được xác minh bằng crc32. Trong đó _cfgUpgClass được bỏ qua khi ghi vào flash, ipc_db.jffs2 chứa mã html dùng để tạo lên trang quản trị xem CAM, LiteOS.bin là kernel có chứa app chạy các dịch vụ.

Write file into flash
File ipc_db.jsffs2 HTML website

Sau khi quá trình decrypt hoàn thành, tập tin sẽ được lưu lại vào flash giống với phân vùng lúc trước dump được. Có một điều nữa là chỉ tập tin ipc_db.jsffs2 hiện tại lưu ở bản rõ. Còn LiteOS.bin còn một lớp mã hóa tiếp theo.

Analyze Decrypt Kernel

Bootloader còn một công việc nữa là load tập tin LiteOS.bin từ flash và thực hiện decrypt một lần nữa sau khi load xong sẽ sang đến giai đoạn 3 là boot kernel.

Hexdump tập tin cipher text  LiteOS.bin có chứa thông tin để decrypt Kernel:

  • 2 giá trị 0x40, 0x100 được sử dụng để tính toán offset đến key_static, iv.
  • Offset_0x30: đoạn hash được sử dụng để kiểm tra tính toàn vẹn sau khi kernel được giải mã.
  • Offset_0x170: chứa key static key này sẽ được tính toán từ bộ cipher hardware với key OTP được lưu trong chíp sẽ tạo ra một key mới được dùng để giải mã kernel(Liteos.bin)
  • Offset_0x190: là vector khởi tạo của bộ cipher
Cipher Text Kernel LiteOS
Parse KEY, IV, Length from Cipher Text

Một điểm mới thiết bị sử dụng một loại bộ nhớ chứa keygen nằm trong CHIP xử lý đươc gọi là OTP, nó chỉ được ghi duy nhất một lần và không thể đọc bởi vì bộ key_gen chỉ được load trực tiếp vào bộ nhớ cipher hardware trên CHIP để thực hiện xử lý.

Để thực hiện mã hóa hay giải mã băng bộ cipher hardware sẽ trải qua các bước sau:

  • Bộ Cipher Hardware sẽ được cấu hình qua các thanh ghi với loại mã hóa (DES, 3DES, AES, ..), độ dài khóa, loại chế độ (ECB, CBC, OFB, CFB, …), Vector Init, độ dài mỗi block.
  • Load key OTP lên bộ Cipher Hardware (OTP được load trưc tiếp lên Cipher của chip không qua trung gian).
  • Cấu hình địa chỉ cipher_text và load địa chỉ plain_text lên Cipher Hardware Register
  • Thực hiện bằng cách đẩy bit 1 Cipher Control Register.

Conclusion

  • Do việc giải mã không hoàn toàn dùng Cipher Hardware do đó có nguy cơ có thể xảy ra bên thứ 3 có thể sử đổi firmware bằng các đoạn mã độc javascript như keyloger gửi thông tin nhạy cảm về server của attacker.
  • Việc sử dụng CHIP có OTP và Cipher Hardware sẽ là xu hướng cho việc boot secure trong thời gian sắp tới tại việt nam, nó được để xác minh được tính toàn vẹn, danh tính của firmware để tránh thiết bị bên thứ 3 cài backdoor.