Case study phân tích cách mã hóa traffic của một ứng dụng Android
Mở đầu
Khi thực hiện pentest một số ứng dụng Android, đôi khi ta sẽ gặp phải trường hợp các request từ thiết bị Android và response gửi từ server về sẽ được mã hóa. Để có thể chỉnh sửa request với mục đích pentest ứng dụng thì việc hiểu và tái hiện lại được cơ chế mã hóa và giải mã là cần thiết. Trong bài viết này, mình sẽ lấy ví dụ về một ứng dụng để xem thực tế chúng ta sẽ cần làm các bước như thế nào để có thể đạt được mục tiêu này nhé
Cài đặt công cụ
Trước hết, chúng ta cần cài đặt các công cụ emulator (mình sử dụng WSA của windows), jadx và frida. Chi tiết cách cài đặt, các bạn có thể tham khảo series bài viết sau của bạn @tranminhnhat và @minhtuan.nguy:
- https://viblo.asia/s/pentest-android-QqKLvpwrl7z
- https://viblo.asia/p/pentest-android-cai-dat-android-emulator-sao-cho-ngau-yMnKMjkAZ7P
- https://viblo.asia/p/cai-dat-wsa-windows-subsystem-for-android-de-su-dung-android-tren-windows-cai-dat-android-emulator-sao-cho-ngau-part-2-OeVKBr3JKkW
Ứng dụng mẫu
Ở đây mình có sử dụng một ứng dụng Android có chức năng login. Mình xin phép che tên ứng dụng để tránh bị ăn gậy . Sau khi sử dụng Proxydroid để chuyển hướng traffic sang Burp, thực hiện login với email abc@example.com
và password 123456
thì chúng ta thấy có traffic như sau:
Du22azpzkVjMk0l0Emtcv1S8xnawAf+zMFcnvJaNhnC7ejCWD3pdDPHJeP5N1dFhon/EFHIuZO3NpGNLn2tDmwvPElDD2BG+zGAPgwQ3Ct8=
Rõ ràng là dữ liệu của chúng ta đã được mã hóa hết vào biến param data
rồi thực hiện base64.
Ngoài ra ở một số API, dữ liệu từ server trả về tuy là JSON nhưng content thì cũng không phải là plaintext:
{"code":0,"data":{"info":"rLrUWFNS+npD4GybX979A02j5M6nJI9wy0i3unlQ9MvGnq85XXwNWqOPvqVclKQUDYTeC2sOiV2w5DwqfzRMgjLNribJEFmiyxQPsgw+mGo="}}
Giờ chúng ta cùng xem thử cách ứng dụng này mã hóa dữ liệu như thế nào.
Phân tích ứng dụng Android
Trước hết thì có vài điểm cần lưu ý:
- Để đảm bảo hiệu suất của ứng dụng thì việc mã hóa thường sẽ sử dụng mã hóa đối xứng, phổ biến nhất là AES, thay vì mã hóa bất đối xứng (như RSA)
- Do đây là mã hóa giữa client và server (và server cũng trả về dữ liệu mã hóa) nên có thể chắc chắn 99% là phía client sẽ có key để giải mã
Chúng ta sẽ sử dụng jadx để decompiler ứng dụng:
Thử search với từ khóa aes
, chúng ta thấy ngay nhiều kết quả trông rất khả quan:
Chú ý đến đoạn sau ở class getAESCipher
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
là đoạn code dùng để khởi tạo instance dùng cho mã hóa, giải mã AES được dùng khá phổ biến, khi vào hàm này chúng ta có thể thấy ngay các khai báo secret key và IV:
Tuy nhiên, khi kiểm tra nơi gọi đến hàm getAESCipher
(trong jadx, chọn vào tên hàm và bấm phím x
) thì chỉ có duy nhất một nơi sử dụng, và đọc nội dung hàm này và hàm ngay phía trên thì đây là các xử lý dùng để mã hóa và giải mã file chứ không phải traffic trên server.
Như vậy là chúng ta đã tìm đúng, nhưng chưa trúng . Quay trở lại với khung tìm kiếm, ấn vào Load more
để ra hết tất cả kết quả, chúng ta thấy có những hàm khác như sau:
Class DecodeUtils
được sử dụng ở nhiều vị trí, nghe tên thôi đã thấy tiềm năng rồi. Vào trong class này, ta thấy sử dụng rất nhiều các hàm
NativeUtil.getInstance().aesDecryptWithKey
NativeUtil.getInstance().aesEncryptWithKey
Đi tiếp vào class NativeUtil
này:
Chúng ta chỉ thấy signature của các hàm chứ không có source code, vì đơn giản là các hàm này là nằm trong các lib native của ứng dụng và không được code bằng Java.
In software design, the Java Native Interface (JNI) is a foreign function interface programming framework that enables Java code running in a Java virtual machine (JVM) to call and be called by[1] native applications (programs specific to a hardware and operating system platform) and libraries written in other languages such as C, C++ and assembly.
Như vậy, phần mã hóa này sẽ nằm trong thư viện lib + alg
. Đổi đuôi file .apk thành .zip và giải nén, tìm đến thư mục lib > arm64-v8a
:
chúng ta sẽ thấy có file libalg.so
(và ngạc nhiên hơn là có cả file debug của thư viện này )
Sử dụng Frida
Để có thể hiểu chi tiết về các mã hóa thì bắt buộc chúng ta phải reverse engineering file .so
ở trên. Tuy nhiên, nếu chỉ dừng lại ở bước xem (và có thể phần nào tạo request theo ý mình) thì đến đây, ta có thể dùng Frida hook.
Right-click ở tên hàm và chọn copy as frida snippet (f)
như dưới đây:
Sau đó copy và paste vào file hook.js
để tạo thành file như sau. Mục tiêu là log ra các tham số được truyền vào và kết quả trả về của các hàm mã hóa/giải mã này:
Chạy frida-server trong thiết bị rồi sử dụng câu lệnh sau để spawn và hook ứng dụng:
frida -U -l hook.js -f com.redacted
Thực hiện đăng nhập lại trên ứng dụng, trên console sẽ thấy output ra tham số và kết quả mã hóa:
BINGO! Chúng ta cũng có thể sửa lại code để ở bước gọi đến hàm aesEncryptWithKey
, ta thêm một đoạn nữa để gọi hàm với tham số tùy ý.
và đây là kết quả:
Phân tích thư viện native
Nếu chỉ dừng lại ở trên thì vẫn khá khó để có thể thao tác trên Burp, chúng ta cần "go deeper". Bật IDA lên và load file libalg.so
ở trên vào:
Do đã biết tên các hàm khi gọi từ phía Java, chúng ta thấy ngay các hàm tương ứng cần tìm. Việc còn lại là đọc code để tìm key.
Chú ý đến hàm getKey
ở dưới đây có truyền một tham số vào có kiểu là unsigned int
. Đây sẽ là index của key được sử dụng (do ứng dụng sử dụng nhiều key mã hóa khác nhau cho từng chức năng). Đây cũng chính là tham số int i
được truyền vào ở phía Android.
Chi tiết phần reverse để lấy key này thì mình sẽ không đi chi tiết, chúng ta chỉ cần biết các bước cần làm là đã đủ rồi . Nếu khó quá, hãy thử hỏi ChatGPT nhé mọi người:
Sau khi có được key và các thức mã hóa dữ liệu, chúng ta có thể tái hiện lại quá trình giải mã trên CyberChef như sau:
Với dữ liệu ở đầu bài viết: Du22azpzkVjMk0l0Emtcv1S8xnawAf+zMFcnvJaNhnC7ejCWD3pdDPHJeP5N1dFhon/EFHIuZO3NpGNLn2tDmwvPElDD2BG+zGAPgwQ3Ct8=
đã giải mã thành công. Đến đây thì ta có thể sử dụng Hackvertor, viết một custom tag để có thể tùy ý chỉnh sửa dữ liệu đẩy lên trong Burp. Chi tiết thì bạn có thể tham khảo thêm ở bài viết khác của mình: Biến đổi payload trong Burp Suite nhanh chóng và dễ dàng hơn cùng với extension Hackvertor
Kết
Giống như mã hóa trên ứng dụng web, chúng ta cần đọc code JS thì với Android, chúng ta cũng cần đọc hiểu code Java, và đôi khi là cả code assembly nữa
P/S: HAPPY NEW YEAR mọi người! Chúc các anh em pentester có một năm đầy bug nhé
All rights reserved