1 điểm bởi GN⁺ 2024-03-06 | 1 bình luận | Chia sẻ qua WhatsApp
  • Nhóm Texts.com muốn phân tích Meta Messenger cho macOS, một ứng dụng desktop độc lập tương tự sản phẩm của họ, nhưng việc phân tích MITM dựa trên proxy bị chặn do certificate pinning
  • Certificate pinning của Meta khiến ứng dụng chỉ tin cậy các chứng chỉ được ứng dụng cho phép, chặn cách dùng cơ quan chứng thực do người dùng tạo để chặn bắt và giải mã yêu cầu
  • Dynamic instrumentation dựa trên Frida gây crash trên Messenger và phức tạp khi triển khai, nên nhóm chọn một bản vá nhị phân nhỏ hơn và dễ tái lập hơn
  • Phân tích bằng Hopper cho thấy chỉ cần thay đổi 4 byte để IsUsingSandbox() trả về true là có thể đi vào nhánh mã tắt xác minh SSL khi dùng custom sandbox
  • Sau khi thay thế binary gốc bằng file thực thi đã vá và xử lý chữ ký, công cụ proxy đã có thể hiển thị header, thân phản hồi và thông tin yêu cầu

Vì sao Texts.com phân tích Messenger

  • Batuhan İçöz, người phụ trách dự án nền tảng Meta tại Texts.com, nhận định ứng dụng Messenger cho macOS là một ứng dụng desktop độc lập gần với mô hình của công ty nên đáng để phân tích
  • Chặn bắt request mạng có rào cản gia nhập thấp, phù hợp làm bước đầu để hiểu cách ứng dụng hoạt động
  • Tuy nhiên, Meta áp dụng certificate pinning trong ứng dụng để tăng cường mô hình bảo mật, đồng thời chặn cả phân tích MITM mà người dùng tự thực hiện trên chính mình

Certificate pinning chặn điều gì

  • Để chặn bắt request bằng client proxy, người dùng cần thiết lập và tin cậy một cơ quan chứng thực do mình tạo
  • Có thể chặn bắt và giải mã thông tin request thông qua chứng chỉ do cơ quan chứng thực đó cấp
  • Khi một dịch vụ triển khai certificate pinning, ứng dụng chỉ chấp nhận chứng chỉ do một số cơ quan chứng thực nhất định cấp
  • Trong trường hợp này, chứng chỉ do người dùng tạo không hợp lệ nên không thể chặn bắt request

Trạng thái trước khi vá và mục tiêu

  • Nếu không tắt certificate pinning, mọi request đều trả về “Internal Error
  • Phần mềm proxy hiển thị “SSL Handshake Failed” và vòng đời request không chạy đến cuối
  • Ở trạng thái này, rất khó suy luận nội dung request
  • Mục tiêu là có thể đọc trực tiếp request, response và header trong công cụ debug mạng

Các phương án vượt qua thất bại và lựa chọn cuối cùng

  • Một phương pháp từng hoạt động trước đây là thay chuỗi URL trong binary bằng endpoint tự host không triển khai TLS
    • Endpoint này chuyển tiếp request và response giữa client và server
    • Cách này phù hợp với ứng dụng nhỏ hơn là một ứng dụng lớn như Messenger
  • Các thư viện dynamic instrumentation như Frida cũng được cân nhắc, nhưng kém ổn định trên Messenger
    • Ứng dụng thường xuyên crash khi đặt hook
    • Overhead khiến việc tìm điểm gây lỗi trở nên khó khăn
    • Môi trường và bộ công cụ cần thiết để chạy khiến việc phân phối cho thành viên trong nhóm trở nên phức tạp
  • Nhóm cũng thử một script Frida đã duy trì trong nhiều năm
    • Script này được dùng cho các thư viện certificate pinning phổ biến và các cách vượt qua tương ứng, hoạt động với hầu hết ứng dụng
    • Nhóm ứng dụng của Meta không nằm trong “hầu hết” đó
  • Cuối cùng, nhóm chọn cách tắt hoàn toàn certificate pinning bằng bản vá nhị phân có thể dễ dàng chuyển cho đồng đội

Điểm vá tìm được bằng Hopper

  • Sau khi tải Messenger và chuyển vào thư mục Applications, nhóm đưa binary ARM đã biên dịch tại /Applications/Messenger.app/Content/MacOS/Messenger vào Hopper
  • Hopper có thể disassemble, decompile, recompile, debug và trực quan hóa binary đã biên dịch
  • Sau khi tải binary và các tham chiếu, nhóm tìm các thuật ngữ như certificate, ssl, pinning
  • Chuỗi "SSL pinning verification failed for host:" trở thành điểm bắt đầu phân tích
  • Vì binary đã biên dịch có thể crash nếu bị sửa quá nhiều, nhóm dùng chiến lược chỉ áp dụng thay đổi nhỏ nhất có thể
    • Thay đổi lý tưởng là bản vá có phạm vi ảnh hưởng hẹp, như đảo giá trị boolean, đảo điều kiện, hoặc sửa vài lệnh

Biến IsUsingSandbox() thành luôn trả về true

  • Nhóm trực quan hóa luồng thực thi bằng control flow graph và lần theo các tham chiếu liên quan ngược lên trên
  • Tìm thấy chuỗi "Using custom sandbox -> turn off SSL verification"
  • Nhóm tìm trong file các tham chiếu đến hàm quyết định flag này và thấy tham chiếu đó ở đầu procedure
  • Trong hàm IsUsingSandbox(), nhóm lần theo điểm mà giá trị trả về được gán
    • Thanh ghi w0 được chuyển từ w19 rồi trả về
    • w19 ban đầu được gán bằng lệnh load byte
  • Nếu không load w19 mà luôn đặt thành true, IsUsingSandbox() sẽ trả về true
  • Theo chuỗi đã tìm thấy trước đó, khi dùng custom sandbox thì xác minh SSL bị tắt, nên thay đổi này sẽ vô hiệu hóa certificate pinning
Original
ARM: ldrb w19, [sp, #0x40 + var_20]
HEX: F3 83 40 39

Rewritten
ARM: mov w19, #1
HEX: 33 00 80 52
  • Việc thay thế này được thực hiện bằng cách sửa trực tiếp byte code của ứng dụng trong hexadecimal mode

Kết quả chạy và ký lại

  • Nhóm xuất file thực thi mới bằng tùy chọn “Produce New Executable” của Hopper
  • Sau khi gỡ chữ ký của file thực thi, nhóm thay binary Messenger gốc bằng binary mới
  • Khi chạy lại Messenger, công cụ proxy hiển thị header, thân phản hồi và các thông tin request khác
  • Chỉ cần sửa 4 byte trong tổng kích thước binary 97.477.728 byte là đã có thể chặn bắt request
  • Nếu muốn xem cách tiếp cận tương tự trên iOS, có thể đọc bài viết năm 2020 của Hassan Mostafa về vượt qua certificate pinning của Instagram
    • Bài viết đó là trường hợp tắt certificate pinning của Instagram trên iPhone đã jailbreak bằng cách đảo lệnh rẽ nhánh có điều kiện
  • Binary đã biên dịch được chuyển cho Batuhan
    • Batuhan lấy và cài đặt chứng chỉ ký, rồi ký ứng dụng
    • Sau đó, anh có thể dùng binary đó trên hệ thống của mình để xem các request của chính mình
codesign --force --deep -s CERTNAME_OR_ID /Applications/Messenger.app

1 bình luận

 
GN⁺ 2024-03-06
Ý kiến trên Hacker News
  • Tôi cũng đi theo con đường tương tự nhưng bỏ cuộc ngay khi định làm đến mức decompile/chỉnh sửa/recompile
    Đến mức này đúng là sự kiên trì, và tôi thật sự tò mò không biết đã mất bao nhiêu tiếng. Tôi đã đặt sẵn tiêu chí dừng và bám đúng theo đó

    • Bài này ban đầu là bài viết nội bộ của Texts.com, và khi biên tập lại để chia sẻ công khai thì tôi đã bỏ phần nói rằng vài tuần trước tôi cũng thử đúng cách tiếp cận này rồi bỏ cuộc vì chạm giới hạn thời gian đã tự đặt ra
      Ban đầu tôi thử thay đổi nhiều lệnh khác nhau trong 2 giờ rồi bỏ cuộc. Sau đó tôi đọc bài của kỹ sư đảo ngược “Hassan Mostafa” (cyclon3), người trước đây đã thành công với đúng cách này, cụ thể là bài áp dụng Hopper Disassembler cho Instagram trên iOS, nên tối hôm đó tôi thử lại nhưng vẫn thất bại. Tôi cũng tìm ra và sửa cùng các lệnh đó
      Rồi tôi quyết định dừng lại, và vài tuần sau, trong lúc vẫn còn hơi vương vấn, tôi ngẫu hứng thử lại thì sau khi tìm ra hàm sandbox đã xong trong khoảng 30 phút
  • Có vẻ dùng eBPF sẽ cho phép đọc dữ liệu trước khi TLS mã hóa: Debugging with eBPF Part 3: Tracing SSL/TLS connections https://blog.px.dev/ebpf-openssl-tracing/

    • Đây là một cách tiện lợi, và các cách khác như hook các hàm gửi/nhận TLS bằng Frida hay công cụ tương tự gần như chắc chắn cũng làm được. Tuy vậy, nếu vượt qua certificate pinning thì nhà nghiên cứu có lợi thế là có thể cho traffic đi qua các công cụ quen thuộc như Burp Suite hay mitmproxy
      Nếu route qua một proxy chặn traffic của ứng dụng thực, thời gian có thể giảm đi rất nhiều tùy mục đích. Ví dụ, nếu bạn muốn tự động thay đổi một tham số của request chỉ xuất hiện sau bước xác thực/thiết lập phiên, thì để ứng dụng tự làm toàn bộ quá trình rồi chỉ sửa một chỗ ở proxy sẽ nhanh hơn nhiều so với việc viết lại một client mới thực hiện toàn bộ bước đầu hoặc viết logic sửa đổi bằng bộ lọc eBPF
    • Nhân tiện, cách này không áp dụng được với các chương trình Rust liên kết tĩnh rustls, thư viện TLS Rust phổ biến nhất
  • Cách này rất thông minh. Nhưng có vẻ ngay cả ở chế độ sandbox họ vẫn có thể ép certificate pinning
    Hồi đại học tôi từng thử soi Snapchat bằng tấn công trung gian, nhưng bên đó cũng dùng certificate pinning nên cuối cùng không phá được

    • Tôi cũng từng thử như vậy, và đã thành công đến mức vá ứng dụng để chặn request, nhưng rồi bỏ cuộc khi cố đảo ngược shared object phụ trách ký request
      Tôi thậm chí còn không tìm được điểm vào. Với một ứng dụng mạng xã hội tương đối nhỏ, mức bảo mật của họ vào năm 2015 đã mạnh đến mức khó tin
    • Nếu người dùng có thể sửa binary thì về bản chất rất khó cưỡng chế certificate pinning
      Ngay cả nếu họ dùng certificate pinning trong chế độ sandbox, nhiều khả năng vẫn sẽ có cách khác để loại bỏ phần kiểm tra chứng chỉ đã ghim
    • Đúng vậy. Họ cũng có thể triển khai bằng cách khiến trong hàm gọi chỉ luôn gán đầu ra của hàm cờ sandbox thành true, nhưng trong trường hợp này thì cách hiện tại cũng hoạt động tốt :)
    • Buồn cười là có nhiều người từng thử làm chuyện này trên app di động rồi bị chặn như vậy
      Dù giờ thì không còn nữa
  • Bài này làm tôi nhớ đến thời +Orc. Có vẻ nhiều kiến thức từng rất phổ biến khi đó, như cách tìm nhánh không mong muốn rồi NOP nó đi, đã dần biến mất
    Nhưng cũng dễ hiểu thôi vì bây giờ có nhiều kỹ năng khác để học hơn rất nhiều
    [1]: https://en.m.wikipedia.org/wiki/Old_Red_Cracker

    • Mỗi lần thấy ai nhắc đến +Orc hay Fravia (RIP) là tôi lại thấy hoài niệm
      Dù vậy, tôi nghĩ vẫn còn nhiều người làm NOP patch. Chỉ là độ phức tạp đã cao hơn mà thôi. Vẫn luôn có người phá DRM hoặc mổ xẻ các app di động bất kỳ bằng hex editor và những công cụ tương tự
      Chương trình ngày nay phức tạp hơn nên bắt đầu khó hơn, nhưng đồng thời kiến thức cần thiết lại dễ tiếp cận hơn
  • Nếu muốn chặn traffic của ứng dụng Meta thì thực ra không cần phải làm vậy
    https://www.facebook.com/whitehat/bugbounty-education/261571...

    • Cái này chỉ hoạt động trên Android. Bọn tôi không quan tâm đến việc chặn ứng dụng Android
  • Tôi tự hỏi liệu checksum binary lúc runtime có giúp khiến kiểu chỉnh sửa này khó hơn không
    Với app di động thì đây chẳng phải là thông lệ tiêu chuẩn sao? SDK iOS hay Android có cung cấp sẵn tính năng này không? Có vẻ như nó phải gắn với quy trình phát hành chính thức và được cưỡng chế trên từng nền tảng chưa jailbreak của họ
    Câu hỏi khá cơ bản thôi, nhưng vì cách giải cuối cùng chỉ là sửa vài byte trong binary nên có vẻ vẫn có thể ngăn được

    • Đây là macOS (desktop), không phải iOS (mobile)
    • Dù sao thì khi sửa binary bạn cũng phải ký lại, nên hiệu ứng cuối cùng vẫn tương tự
      Trên các nền tảng chưa jailbreak, việc này thường được làm bằng chứng chỉ nhà phát triển
  • Có vẻ Meta, ít nhất là Messenger, có biện pháp chống đảo ngược khá lỏng lẻo
    Ngay cả chưa cần đến obfuscation cao cấp, có lẽ chỉ việc loại bỏ hẳn IsUsingSandbox() khỏi bản build production cũng đã khá dễ rồi

    • Theo những gì tôi biết khi còn làm ở đó thì chống đảo ngược chưa bao giờ là mục tiêu
      Certificate pinning là để khiến kẻ tấn công khó can thiệp hơn, chứ không phải để khiến người dùng khó làm điều đó hơn
    • Các ứng dụng Meta còn mang nguyên cả menu debug vào bản build production. Chuỗi mà tác giả tìm thấy cũng có thể là một phần của menu đó
  • Lúc mới crack app, tôi đương nhiên nghĩ là sẽ thất bại, nhưng hóa ra việc tìm các điểm JNE/JEZ dễ sửa kiểu này lại đơn giản hơn tôi tưởng nhiều
    Ngay cả nếu chọn nhầm thì chỉ cần khôi phục file gốc rồi thử điểm khác
    Mấy việc kiểu này có vẻ AI có thể tự động hóa khá dễ. Chỉ cần lật JEZ/JNZ ở nhiều điểm ứng viên, chạy app lên rồi xem có hiện màn hình cằn nhằn hay không

    • Cái này không hẳn là bài toán AI mà gần với fuzzing hơn
      Nếu điều kiện thất bại được xác định rõ thì cuối cùng chỉ là thu hẹp tập ứng viên thôi
      Tất nhiên, nếu AI có thể phá kiểu Denuvo theo zero-shot thì đó lại là câu chuyện khác
    • Công cụ kiểu đó đã có từ những năm 90 rồi. Không phải AI, chỉ đơn giản là brute force thôi
  • Tôi thắc mắc vì sao một ứng dụng của công ty lớn như vậy lại không được làm rối mã hoàn toàn, và cũng không cài đủ cơ chế bảo vệ để ngăn chạy binary đã bị sửa đổi

    • Từ góc độ người từng đưa ra quyết định này đầu tiên ở ứng dụng Facebook, thì điều đó không đáng làm.
      Nếu là cá nhân/nhóm/chính phủ đủ thành thạo hoặc đủ động lực thì cuối cùng họ vẫn sẽ vượt qua được. Bản chất của việc phát hành client binary vốn là như vậy.
      Bạn có thể tốn rất nhiều thời gian và tiền bạc để cố ngăn điều đó. Trước đây Pinterest từng định phân phối ngôn ngữ riêng và máy ảo của họ, nhưng tôi đã phản đối. Hoặc có thể chấp nhận rằng mã phía client về cơ bản đã bị xâm phạm sẵn, rồi chuyển logic sang phía server và tiếp tục.
      Certificate pinning gần như rẻ như cho không, và giống như một cơ chế kiểu “phải có khóa này trở lên mới được lên xe”. Nó không thực sự an toàn, nhưng có thể lọc bớt các nỗ lực linh tinh
    • Làm rối mã có chi phí, còn certificate pinning gần với mục đích khiến các cuộc tấn công trung gian bất lợi cho người dùng trở nên khó hơn hơn là để ngăn đảo ngược kỹ thuật.
      Tất nhiên nó vẫn có ảnh hưởng đến việc đảo ngược kỹ thuật, nhưng điều đó gần như chỉ là tác dụng phụ.
      Cuối cùng thì mã vẫn chạy trên thiết bị của người dùng, và người dùng có thể quan sát những gì mã đang làm, nên việc khử rối mã luôn là khả thi. Chỉ cần một người gỡ được rồi chia sẻ kết quả thì việc sao chép cũng trở nên rất dễ. Không phải là làm rối mã vô dụng, nhưng đây không phải thứ đáng để đổ quá nhiều thời gian vào
    • Với ứng dụng di động/frontend thì làm vậy cũng không có nhiều tác dụng.
      Nếu kẻ tấn công có thể tiếp cận vật lý với thiết bị thì đến thời điểm đó không còn cách nào để chặn được nữa. Điều duy nhất có thể làm là khiến quy trình trở nên phiền phức hơn để họ bực mình mà bỏ cuộc
    • Khả năng cao đây là vấn đề về ưu tiên và hiệu quả so với chi phí.
      Làm rối mã có lẽ hầu như không ảnh hưởng gì đến kết quả của thử nghiệm này, dù cách tiếp cận có thể đã chuyển sang dùng đo đạc động nhiều hơn một chút. Kiểu làm rối mã hiệu quả nhất mà tôi từng thấy là VM obfuscation, nhưng tác động tới hiệu năng là đáng kể. Làm rối mã cũng khiến việc debug thông thường khó hơn.
      Việc ngăn binary bị sửa đổi được thực hiện ở cấp hệ thống, cũng có thể triển khai ở cấp ứng dụng và khá phổ biến. Nhưng ngay cả chức năng đó cũng có thể bị vượt qua, và sau khi kiểm tra bảo mật kết thúc thì vẫn có thể sửa bằng các thư viện đo đạc động như Frida.
      Từ góc nhìn của Meta, lao vào trò mèo vờn chuột với các kỹ sư reverse engineering có lẽ không phải lựa chọn tốt nhất
    • Tại sao không phải mọi ngân hàng đều được bảo vệ như Fort Knox?
  • Tôi tò mò không biết công cụ proxy được dùng trong bài là gì. Khi nó chạy thì toàn bộ lưu lượng của mọi ứng dụng đều được định tuyến qua đó à?
    Xin lỗi nếu đây là câu hỏi ngớ ngẩn

    • Câu hỏi hay đấy. Thứ được dùng trong bài là Proxyman.
      Trên macOS, bạn có thể định tuyến lưu lượng của mọi ứng dụng qua đó, và nếu cài chứng chỉ tự ký của nó lên thiết bị rồi kết nối với proxy thì cũng có thể proxy cả thiết bị iOS