MatesCTF writeup - IoT101

Challenge: link

Đề bài cho ta một file có định dạng Intel hex, ta có thể chuyển đổi qua file nhị phân bằng lệnh: objcopy

$ objcopy -I ihex hello.hex -O binary hello.bin

Kiểm tra qua các string trong binary thì thấy đây có thể là binary của Arduino uno
strings

Mình dùng XLoader (trên terminal có thể dùng avrdude) để upload file iHex vào board Arduino uno chạy thử (hoặc có thể chạy trên simulator), sau khi test thử thì mình phát hiện ra board có giao tiếp qua Serial ở baud 9600.test_serial

Mình sẽ dùng IDA để reverse file binary này, Arduino UNO dùng chip ATmega328P nên chúng ta cần thêm config avr.cfg vào thư mục cfg của IDA

Quá trình reverse của mình như sau:

  • Tìm hàm Serial.read()
  • Trace đến hàm xử lý key
  • Reverse để tìm key

Không biết vì lý do gì mà địa chỉ trong segment code chỉ tăng 1 nữa cho mỗi dòng, ví dụ như opcode 2 byte thì IDA chỉ tăng 1 byte, điều này dẫn đến toàn bộ địa chỉ trong IDA bị giảm 1 nửa, nên từ đoạn này trở về sau các địa chỉ hay hàm nằm trong dấu ngoặc () mới là địa chỉ thực.

Sau khi load vào IDA thì mình thấy có rất nhiều hàm, một số hàm được IDA nhận dạng và rename theo chức năng, số còn lại thì không, và không may là trong số các hàm được IDA nhận dạng không có Serial.read(). Sau một lúc tìm hiểu trên google thì mình tìm được một kĩ thuật rất hay. Đó là build một binary Arduino UNO có sử dụng Serial.read(), sau đó dùng plugin Diaphora trên IDA để so sánh giữa 2 binary, và kết quả rất tốt.compare
Hàm sub_AE4(0x15c8) chính là Serial.read()
Dùng chức năng xrefs của IDA mình tìm ra vị trí Serial.read() được gọi đếnxref
Đó là 1 hàm khá dài sub_1F2(0x3e4)!
check_key-1

Nhìn vào flow ta có thể đoán rằng key sẽ được tính toán kiểm tra lần lượt nhiều lần, đến đây mình có nghĩ đến timing attack nhưng không thành công do mỗi lần check sử dụng rất nhiều kí tự trong key.

Nhìn vào đoạn đầu ta có thể đoán rằng 0x243:0x242 chính là độ dài của input.
input-1check_input

Và kí tự ta nhập vào được so sánh với 0xA(\n). Đến đây ta có thể đoán rằng: Key của ta đưa vào có độ dài là 16 hoặc kết thúc bằng \n thì chương trình sẽ ngưng nhận input và bắt đầu nhảy vào đoạn xử lý key loc_26C(0x4D8)

Khi nhận kí tự đầu tiên thì length = 0 nên r31:r30 = length*4 - 0xFDBC = 0x244. Vậy key[0] sẽ được lưu ở 0x244 trên SRAM, Lý do mà chương trình store tới 4 byte có lẽ là vì ban đầu được khai báo là số nguyên 4 byte, tương tự vậy thì key[1] sẽ được lưu tại 0x248.
store_to_sram
Vì phần xử lý key rất dài, để chắc chắn mình phải kiểm tra lại các địa chỉ lưu key mình vừa tính ra ở trên. Mình tìm được cách debug board Arduino UNO bằng 1 board Arduino UNO khác thông qua bài viết của Wayne
Mình đặt breakpoint tại 0x4D8 và nhập vào key là ABCDEFGHIJKLMNOPview_sram_244
Đây là phần đầu trong đoạn xử lý đầu tiên loc_26C(0x4D8). Ta thấy đoạn code này lưu key vào một địa chỉ khác trên SRAM rồi mới bắt đầu xử lý, có thể mục đích là lưu key lại để sau khi kiểm tra key hợp lệ thì sẽ dùng key này để decrypt flag.
load_key

Sau khi kiểm tra các phần còn lại thì đây là kết quả mình thu được:
key_indexLý do key[15] lại thay đổi vì nó là 1 cái TRAP và mình bị dính trap 2 lần :(, ở nhánh loc_794(0xf28) key[15] sẽ bị overwrite, nếu không chú ý sẽ dính trap ngay. Còn r28 là stack pointer.

Sau bước sao lưu key trên thì quá trình kiểm tra key bắt đầu, trọng tâm của quá trình này là hàm sub_E28(0x1C50)core
Hàm này thực chất là hàm nhân 2 số, kết quả là số nguyên 4 byte. Hai đối số được đưa vào thông qua r27:r26 và r21:r20:r19:r18, kết quả được trả về thông qua r25:r24:r23:r22

Bên dưới là một nhánh kiểm tra key đơn giản, các nhánh khác tương tự nhưng dài hơnsample_check
Kết quả phép nhân đầu tiên được lưu vào

r15:r14:r13:r12 = 0x24*key[4]

Kết quả của phép nhân thứ 2 sẽ được cộng tiếp vào

r15:r14:r13:r12 = 0x24*key[4] + 0x56*key[11]

Sau đó sẽ được so sánh với 0x1D7C. Vậy key của chúng ta sẽ phải thỏa phương trình:

0x24*key[4] + 0x56*key[11] = 0x1D7C

Làm tương tự với các nhánh khác ta sẽ được 16 phương trình 16 ẩn, ta có thể dùng z3 để giải hệ này.

Nhập key vào ta được flag.
Key: R&MS4IsHereBois\n
Flag: matesctf{s1mpl3_pr0bl3ms_r3qu1r3_tr1v14l_s0lut10ns}
flag

Tham khảo:
https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_instruction_list.html
https://blog.attify.com/flare-4-ctf-write-part-4/
http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf
https://sites.google.com/site/wayneholder/debugwire3

Show Comments

Get the latest posts delivered right to your inbox.