Lỗ hổng trên thiết bị D-link

Lỗ hổng trên thiết bị D-link
Photo by Sigmund / Unsplash

Vừa qua mình vừa tìm được một lỗ hổng trên thiết bị D-link DIR-601. Hiện tại thiết bị này đã end-of-life nên lỗ hổng vẫn sẽ tồn tại. Lỗ hổng không quá khó để khai thác, kèm theo đó là thiết bị này tại VN vẫn đang được sử dụng khá phổ biến. Trong bài viết này mình sẽ phân tích lỗ hổng trên thiết bị này.

Trước khi đi vào phân tích, chúng ta cần các bước chuẩn bị để có thể khai thác được lỗ hổng.

  1. Thu thập:
  • Name: Dlink 601
  • H/W version: A1
  • Firm version: 1.01NA
  • User: admin
  • Pass: <blank>
  • Firmware type: DD-WRT/OpenWrt (both support) (OpenSource firmware Linux based)
  • SoC: Atheros AR7240(Speed 350 MHz)
  • Flash chip: Macronix MX25L3205DMI-12G (4 Mib)
  • LCD power management chip: MP1484EN
  • Retail: 21/10/2009

2. Phân tích:

  • Lỗ hổng buffer overflow nằm trong dịch vụ web của thiết bị. Lỗ hổng cho phép hacker có thể ghi tràn bộ nhớ và khai thác chiếm quyền điều khiển thiết bị.
  • Ở đây chúng ta có thể thấy biến path_1 được nối vào chuỗi v105 bằng hàm strcat, hàm này nối chuỗi nhưng không kiểm tra độ dài chuỗi. Nếu chúng ta nối vào một chuỗi đủ dài có thể làm cho chuỗi v105 vượt quá giới hạn có thể lưu trữ, và sẽ tràn qua những biến khác trong bộ nhớ gây ra lỗi buffer overflow.
  • Chúng ta cần dịch ngược lại biến path_1 để tìm hiểu xem có thể kiểm soát được biến này hay không.
  • Dịch ngược để tìm hiểu chương trình, ta thấy ngay từ đầu có 1 biến full_request chứa request của người dùng. Khi người dùng truy cập vào dịch vụ web của router, chương trình sẽ lưu request của người dùng vào trong biến full_request và tách ra từng phần để xử lý cũng như kiểm tra xem có hợp lệ hay không.
  • Request của người dùng được kiểm tra tính hợp lệ, xem gói tin là GET hay POST, sau đó lưu vào biến path_1 là đường dẫn của file.
  • Trước khi xử lý file, đường dẫn file cũng được đảm bảo là hợp lệ.
  • Sau đó chương trình sẽ kiểm tra xem URL có tồn tại hay không. Nếu không chương trình sẽ báo lỗi.
  • Tương ứng với URL tìm được, nếu là URL cần phải đăng nhập thì chương trình sẽ gọi hàm để kiểm tra đăng nhập. Nếu chưa đăng nhập thì sẽ chuyển hướng sang trang login
  • Ngay sau đó path_1 sẽ thay đổi, không thể kiểm soát được.
  • Tuy nhiên nếu như URL không cần phải đăng nhập, chúng ta sẽ tránh được đoạn này.
  • Điều kiện cần bây giờ là làm sao để URL vừa không cần phải đăng nhập mà vừa hợp lệ để có thể vượt qua đoạn kiểm tra URL.
  • Mình bắt đầu đi sâu vào hàm find_path để xem nó hoạt động như nào, có cách nào để vượt qua đoạn kiểm tra URL trong hàm hay không
  • Sau khi tìm hiểu thì URL được check khá kỹ, nên khi nhập vào URL không hợp lệ sẽ lỗi ngay, tuy nhiên có một số URL đặc biệt mà khi check, hàm chỉ kiểm tra đuôi của URL đó
  • Tận dụng điều này mình đã thử nhập “aaaaaa.gif” và đã thành công. Hàm chỉ check đuôi gif mà không kiểm tra tên file.
  • Đến đây vẫn chưa xong, vì chương trình được nằm trong vòng while(1), thế nên mình cần phải tìm cách break ra ngoài.
  • Sau khi nối chuỗi vào biến v105, hàm fopen được gọi để mở file theo đường dẫn trước đó, tuy nhiên do đường dẫn mình đang thay đổi để khai thác lỗ hổng nên sẽ xảy ra lỗi vào sẽ gọi hàm báo lỗi File Not Found
  • Sau ghi gọi hàm với tham số phía trên, hàm sẽ quay lại và START AGAIN
  • Ở đây ta có thể thấy nếu như client_socket < 0 sẽ nhảy đến LABEL_35, cũng là thoát ra ngoài vòng lặp và kết thúc chương trình.
  • Chúng ta cần phải làm cho điều kiện này đúng, để chương trình thoát ra ngoài.
  • Với buffer mà chúng ta kiểm soát được, ta hoàn toàn có thể ghi đè return address. Với việc thoát ra ngoài vòng lặp thành công, chúng ta có thể thực thi mã độc tùy ý.
  • Đối với con router này, vùng nhớ stack có full quyền rwx nên mình có thể ghi shellcode lên và gọi tới nó, chiếm quyền điều khiển router.
  • Tuy nhiên trong quá trình viết shellcode, mình đã bị lỗi
  • Quay lại phía trước, khi chúng ta đang cố thoát ra ngoài vòng lặp, hàm sub_424394 được gọi để báo lỗi do URL mà chúng ta nhập “not found”
  • Bên trong hàm sẽ gọi đến hàm sub_424204 và gọi hàm fwrite để in ra màn hình cho người dùng
  • Trong hàm sub_424204 đó, hàm xử lý thông tin như lấy thời gian hiện tại và chuyển đổi format bằng hàm strftime
  • Dịch ngược bên trong hàm thư viện thì hàm strftime có gọi đến hàm setenv, trong đó biến môi trường nằm ngay phía dưới return address, vì thế nên khi ghi đè, biến đó đã bị thay đổi làm cho hàm không xử lý được và bị lỗi.
  • Đến đây, mình đưa ra 2 hướng:
  • Một là chia shellcode ra thành 2 phần, 1 phần trên biến môi trường và 1 phần phía dưới, khi ghi đè thì tránh thay đổi biến môi trường đó là được, tuy nhiên khi chạy thử thì không thành công
  • Hướng thứ hai là ghi shellcode ở phía dưới biến môi trường đó rồi nhảy thẳng xuống phía dưới đó để thực thi, nhưng vẫn không thành công
  • Sau nhiều lần test thì mình phát hiện biến môi trường không cố định trong stack nên mình đã nghĩ ra hướng khác đó là tận dụng header của người dùng làm buffer, sau đó tính toán và nhảy lên phía trên đó để thực thi shellcode, và hướng này đã thành công do buffer dành cho header cũng khá to, đủ để chứa hết shellcode cần thiết
  • Tiếp đến có 2 vấn đề mình gặp phải đó là do request nhận vào buffer bằng hàm fgets nên trong quá trình exploit cũng như shellcode sẽ không được sử dụng byte NULL \x00.
  • Trong quá trình code shell thì mình cũng đã thay thế instruction sao cho không dính byte NULL.
  • Vấn đề tiếp theo là trên router có một cơ chế cache coherency giúp tối ưu hệ thống trên router. Do cơ chế này nên trước khi gọi shellcode chúng ta sẽ cần phải gọi hàm sleep và hệ thống sẽ update lại cache instruction, khi đó shellcode của chúng ta mới có thể hoạt động
  • Đến đây là chúng ta đã có thể hoàn thành exploit và thực hiện tấn công khai thác lỗ hổng.