TL;DR
Xin mở đầu bài viết bằng chiếc meme miêu tả xúc tích cảm giác của mình trong tháng qua. Do việc bypass SSL pinning cho flutter application tốn quá nhiều thời gian dẫn đến cuối tháng mà vẫn vừa nghiên cứu vừa viết blog :v
Dạo đôi vòng lịch sử blog mà chưa có bài viết nào nói về pentest mobile application nên hôm nay mình cũng mạo muội viết đôi dòng kiến thức về vùng kiến thức này.
Trong hầu hết các dự án mình làm trước đây đều gặp phải các moblie app check rooted/jailbreak, ssl pinning và đủ mọi thể loại check khác, lúc này thì có hai cách giải quyết cho vấn đề này là: dùng hooking với Frida hoặc patch lại mobile application. Tùy theo mỗi người mà sẽ có những lựa chọn, đối với mình thì cách nào ít tốn công và thời gian hơn thì làm :v.
Lúc đầu mình có ý định viết về lý thuyết thôi để lần sau có cái để cái cớ mà viết phần hai (phần ứng dụng lý thuyết) nhưng ngẫm lại viết blog mà chỉ nói lý thuyết thì nhạt vãi, thôi thì bỏ thêm nhiều buổi tối viết thêm phần ứng dụng kiến thức cho bypass ssl pinning cho flutter app luôn cho có tâm xíu.
Trong blog này mình có nói cơ bản về frida hooking, nếu bạn nào ok phần này rồi thì nhảy luôn đến phần Bypass SSL pinning trong Flutter application cho đỡ mất thời gian nhé.
Như trong tiêu đề blog là sẽ nói về android hooking với Frida, vậy hooking là gì? → là chúng ta cung cấp cho Frida sử dụng các Javascript API để tương tác trực tiếp với Java functions, để thực hiện các thao tác như: lấy dữ liệu hay thay đổi luồng hoạt động của ứng dụng theo ý định
Tiếp theo, cần kiểm tra liệu có hoạt động:
Bây giờ mình sẽ giải thích một số Javascript API cơ bản kèm theo hook một mobile application của OWASP nhé (học đi đôi với hành mà :v)
Hình ảnh đầu tiên mở lên thì là dòng chữ Root detected! to tướng. Lúc này ứng dụng không thể sử dụng được, nhiệm vụ bây giờ là bypass check root.
Bước đầu tiên là mình cần phải biết được ứng dụng đang check root như thế nào rồi mới hook đúng không, vậy thì dùng Jadx - Java decompiler (hoặc dùng bất kỳ tool nào bạn cảm thấy thuận lợi là được) để đọc được java code.
Okay, sau hồi review thì đây là đoạn code check root
Class sg.vantagepoint.a.c
Việc check root của ứng dụng hoạt động bằng cách gọi 3 function a(), b(), c() từ class sg.vantagepoint.a.c
Chỉ cần một trong ba function trên trả về true thì sẽ gọi đến function a(str) để alert thông báo và thoát ứng dụng khi người dùng click vào OK button
Sau khi đã hiểu rõ chương trình check root ra sao thì bước tiếp theo là lên ý tưởng bypass, bằng cách hook vào 3 hàm a(), b(), c() của class sg.vantagepoint.a.c trả về false.
Đoạn code bypass check root khá là đơn giản như sau:
Ngoài ra, các bạn có thấy mình có đưa console.log(); vào trong code bypass không? Đó là một tip nhỏ mình thường dùng để debug biết được là đoạn liệu code đó có hoạt động?. Nếu các bạn code ngon thì khỏi cần thêm cũng được :v
Okay, sau thử thách bypass check root thành công sẽ là thử thách nhập vào đúng secret string của ứng dụng :v
Tiếp tục công việc đọc hiểu đoạn code xử lý chỗ này để giải quyết thôi
(1): Ứng dụng nhận chuỗi ký tự từ người dùng và lưu vào biến obj
(2): Truyền biến obj vào method a() (của class a nằm chung package với class MainActivity) để kiểm tra, nếu kết quả của method a() về là true thì ứng dụng sẽ alert chuỗi Success! This is the correct secret.
Như đoạn code trên sẽ so sánh giữa biến str và kết quả của hàm
Cách 2: Là tìm ra chuỗi secret string luôn. Mình thường dùng cách này cần thu thập các chuỗi ký tự đã được mã hóa trong các ứng dụng mà mình gặp phải.
Việc cần làm gọi vào hàm a() ở trên để trả về mảng byte, lúc này cần phải thêm một bước nữa là chuyển mảng byte thành String
Okay, nhập vào thôi
Ngoài ra có thể tham khảo thêm Javascript API thêm ở đây, với các hàm cơ bản như
Java.choose(): Tìm instance của className bằng cách scan trong Java heap, nếu trùng khớp sẽ gọi đến onMatch(), ...
Cấu hình proxy ở proxy-droid cho qua Burp Suite rồi mở app lên bạn sẽ được thông báo như sau ở tab Dashboard: The client failed to negotiate a TLS connection to test.com:443: Remote host terminated the handshake
Lúc này tất nhiên sẽ không thể bắt được http traffic
Đầu tiên cũng cực kỳ quan trọng là cần kiểm tra kiến trúc trên thiết bị để đảm bảo rằng load đúng thư viện bằng command sau:
Bước tiếp theo: import libflutter.so vào vào Ghidra, thời gian import khá lâu, thôi để đó, bật logcat lên xem thử như nào
Nhận thấy rằng verify certificate đã thất bại tại file handshake.cc
Sau hồi đau đầu đọc blog của anh horangi thì nhận thấy rằng phần thực thi tại hàm session_verify_cert_chain và sẽ trả về kết quả ssl_verify_ok hoặc ssl_verify_invalid và lưu trong biến ret
Định nghĩa của hàm session_verify_cert_chain nằm trong file ssl_x509.cc
Okay, mục tiêu giờ phải thay đổi kết quả trả về của hàm trên thành true. Và cũng lưu ý rằng hàm trên có 3 arguments được truyền vào (sẽ giúp ta tìm được hàm chính xác khi decompile bằng Ghidra)
Chuyển qua Ghidra click Search -> For Strings… tìm ssl_x509.cc sẽ nhận được kết quả như hình bên dưới
Double click vào kết quả tìm được, bạn sẽ thấy được các References (như trong hình có 7 chỗ gọi đến string này)
Double click vào XREF[7] sẽ hiển thị như hình dưới
Lúc này phải duyệt qua tất cả các Xrefs để tìm được function có 3 arguments (đã nói lưu ý ở trên) và return 0
Tìm được đến đây thì có hai sự lựa chọn để giải quyết bài toán bypass SSL Pinning:
Cách thứ nhất: patch lại app chỉnh sửa 0x0 thành 0x1
Sau đó, lưu lại và chọn Export Program, dùng apktool.jar để compile app, tiếp theo dùng uber-apk-signer.jar để ký app
Cách thứ hai: dùng frida để hook, lấy mã hex rồi dùng mã để bypass
Ném vào đây và chạy thôi

- Giới thiệu Frida

- Yêu cầu trong blog
-
- Java decompiler (JD-GUI, Jadx, …)
- Thiết bị android/máy ảo (Genymotion, Nox Player, ..)
- Dynamic instrumentation toolkit (Frida)
- Ghidra hoặc ida
- Apktool.jar
- uber-apk-signer.jar
pip install frida-tools pip install fridaTiếp theo, tải và cài đặt frida-server trên thiết bị android/máy ảo tại đây và cài đặt theo hướng dẫn sau đây. Hoặc nếu muốn nhanh gọn hơn thì cài đặt trực tiếp Frida Server từ Playstore :v Dùng command sau để đảm bảo rằng thiết bị android/máy ảo đã kết nối với máy tính
adb devices -l

frida-ps -U #List packages and processes
- Cơ bản về Frida Hooking
Java.perform(function () { //do something });Trong document của Frida thì có nói như sau




//frida -U --no-pause -l bypass_root.js -f owasp.mstg.uncrackable1 Java.perform(function () { var root = Java.use("sg.vantagepoint.a.c"); root.a.implementation = function() { console.log("vao ham a"); return false; }; root.b.implementation = function() { console.log("vao ham b"); return false; }; root.c.implementation = function() { console.log("vao ham c"); return false; }; });Trong đó:
-
- sg.vantagepoint.a.c là class name.
- Java.use(“class”) cho phép custom method trong class bằng cách gọi đến implementation
root.a.implementation = function() { //do something };





sg.vantagepoint.a.a.a(b("8d127684cbc37c17616d806cf50473cc"), Base64.decode("5UJiFctbmgbDoLXmpL12mkno8HT4Lv8dlat8FxR2GOc=", 0))Sau khi hiểu cách thức hoạt động xong mình nghĩ ra được hai cách để bypass Cách 1: Dùng Java.use() để thực hiện return true; Đọc code đến đây là có thể dùng Frida để alert dòng chữ Success lên với đoạn code tí xíu này.
var check = Java.use("sg.vantagepoint.uncrackable1.a"); check.a.implementation = function() { console.log("vao ham check secret"); return true; };Lúc này, bạn nhập bất kỳ chuỗi ký tự nào vào cũng sẽ alert Success lên

// cach 2 var check = Java.use("sg.vantagepoint.a.a"); check.a.implementation = function(x,y) { console.log("vao ham decrypt"); var bte = this.a(x,y); console.log("byte: "+ bte); var result; for (var i = 0; i < bte.length; i++) { result += String.fromCharCode(bte[i]); } console.log("secret key: " + result); return bte; };Trong đó:
-
- Do hàm a() có hai arguments nên trong code js cần phải truyền vào 2 x, y
- Gọi hàm a(x,y) với cú pháp this.a(x,y) để lấy giá trị ở kiểu dữ liệu byte



- Bypass SSL pinning trong Flutter application


adb shell getprop ro.product.cpu.abi













- Phần tham khảo
9408 lượt xem