Thanh xuân như một tách trà, tìm được một bug hết bà thanh xuân.

Thi thoảng tôi cũng chơi Bugbounty, chả là buổi tối ăn xong ngồi tranh thủ tí và cũng là thêm đôi ba cốc cafe Highland những lúc thèm. Sau một tuần hì hục đào bới, lượm mót dell.com, tôi kiếm được 1 P1, 1P2 và 3 cái P3, cơ mà cái P1 bị duplicate mất, và tất nhiên dup hay không dup thì cũng chẳng có tiền.

Sau đó tôi cũng có được một invite vào một private program. Bugcrowd không có thông số về thời gian start của một program (tôi tìm không thấy), chỉ có phần thông tin về update:

“Reward increase” 2 months ago, điều này có nghĩa là program này đã chạy từ lâu rồi, khi mà không thấy có hacker nào submit thì người ta tăng mức thưởng lên ☹️. Còn về số lượng reports đã được reward

Thôi cứ thử tìm xem sao biết đâu họ vẫn còn bỏ sót. Tôi bắt đầu đăng nhập vào Website, xem qua một lượt thì đây là một ứng dụng về quản lý nhà hàng, có website quản trị cho Manager, ứng dụng mobile cho nhân viên chạy bàn, nhà bếp. Vào mục quản lý nhân viên, phân quyền, ở đây có rất nhiều quyền, theo từng module, nhóm module, có thể phân quyền theo job hoặc theo từng nhân viên. Tôi focus vào mục này, tìm kiếm các lỗi IDOR, lỗi về phân quyền. Tôi setup ba tài khoản khác nhau:

  • Một tài khoản Admin full quyền của nhà hàng A
  • Một tài khoản Admin full quyền của nhà hàng B
  • Một tài khoản User bình thường của nhà hàng A

Tiến hành bỏ dần dần từng quyền của User, và kiểm tra xem:

  • Admin nhà hàng B có tác động được dữ liệu của nhà hàng A không?
  • Tài khoản User có thực hiện được chức năng mà không được set quyền không?

Tìm được hai lỗi User có thể thực hiện được chức năng của Admin, một lỗi IDOR Admin nhà hàng B có thể tạo thông báo (notification) tới tất cả nhân viên của nhà hàng A.

Tôi chuyển sang ứng dụng mobile, như thường lệ, tôi dịch ngược file apk ra, tìm danh sách các api của ứng dụng request lên server. Đồng thời sử dụng ứng dụng để hiểu logic hoạt động của ứng dụng. Có vẻ như trên mobile họ không quan tâm tới phân quyền nhiều lắm. Thế nên cũng bằng phương pháp ở trên, tôi tìm được kha khá lỗi liên quan tới phân quyền chức năng và dữ liệu. Buồn thay, họ chỉ đánh cho tôi P4, và P3, ngay cả có lỗi mà cho phép từ 1 tài khoản thông thường có thể switch sang bất cứ tài khoản nào, quản trị được cả nhà hàng vậy mà họ vẫn chỉ đánh cho tôi P3 (tất nhiên tôi đang cãi với họ, và họ nói chờ họ xem xét thêm).

Buồn quá, tôi lại tiếp tục tìm, xem trong source mobile, tôi thấy có một file resource hay ho crm-api.yaml, paste vào swagger xem thử

Đây là api dùng để quản lý danh sách khách hàng, api này khác hẳn so với các api chính sử dụng cho ứng dụng. Trong lúc test ứng dụng tôi cũng đã thấy lỗi IDOR ở chức năng thêm mới khách hàng, thay vì đáng lẽ ra phải sử dụng access_token gửi lên, nhưng họ lại dùng thêm một giá trị “Restaurant-ID” để xác định một nhà hàng. Nhưng vì nếu chỉ thêm khách hàng thôi thì impact ở đây không cao, hơn nữa ID của nhà hàng lại là ID của mongoDB, không thể đoán được, vì thế tôi không submit. Tới đây ta có thể nghĩ ra một kịch bản là sử dụng api search để lấy được danh sách customers, lấy id của customer sau đó sử dụng được các api detail để leak thông tin của customer.

Tôi thử build request search customer, nhưng đời không như là mơ response trả về là 400. Nhìn lại chức năng tạo customer trên ứng dụng tôi thấy phần body bị mã hóa:

Vậy là họ đã encrypt post data gửi lên, thay vì để chuỗi json thông thường, vì một lý do nào đó mà ứng dụng chỉ sử dụng api thêm mới customer, còn chức năng search lại sử dụng một api khác. Vậy giờ chỉ còn cách đọc mã nguồn tìm hàm encrypt rồi code lại. Với khả năng code java dốt nát của tôi + mã nguồn cũng đã bị obfuscated nên việc viết lại hàm encrypt không thành công ☹️.

Tôi chuyển hướng đó là hook vào ứng dụng (sử dụng frida), thay đổi post body trước khi bị encrypt. Sau một hồi trace code, tôi cũng tìm thấy nơi cần hook

Và thấy được một hàm hay ho trong class BodyParamBuilder

Việc bây giờ chỉ cần thay đổi

Java.perform(function () {
var dfg = Java.use(“com.xxx.service.client.AbstractHttpClient”);
dfg.executePost.overload(‘java.net.URI’, ‘com.xxx.service.client.QueryParamsBuilder’, ‘com.xxx.service.client.BodyParamBuilder’, ‘com.xxx.service.client.HeadersBuilder’, ‘com.xxx.service.client.RequestContextBuilder’, ‘java.lang.String’, ‘java.lang.Class’).implementation = function(var1, var2, var3, var4, var5, var6, var7) {
var search_str = Java.use(‘java.lang.String’).$new(‘{“query”:{“firstName”:”first”},”pageSize”:0,”page”:0}’);
var builder = var3.fromJsonBody(search);
var ret = this.executePost(var1, var2, builder, var4, var5, var6, var7);
return ret;
}
console.log(“Hooks installed.”);
});

Chạy hook, tạo một request bất kỳ trên ứng dụng… BOOM!!! vậy là tạo được request với post body được encrypt. Giờ chỉ cần chuyển sang repeater của burpsuite và thay đổi các header còn lại.

Vấn đề còn lại bây giờ là restaurant ID, đây là _id trong mongo DB không thể đoán được, cần phải tìm được cách có được giá trị này từ public. Tôi lục lọi trong đống history burpsuite xem có api nào hay ho có thể lấy được ID của restaurant khác không? Kết quả không có. Giờ phải truy cập vào trang public của nhà hàng để xem có request nào trong response có trả về id không. Scope họ cho là môi trường sanbox, bị lỗi chức năng trang public restaurant, vì thế tôi quay sang xem site production của họ.

May mắn thay nó lại có luôn trong response trang order online của nhà hàng:

Ok vậy đã đủ, giờ report thôi

Tổng kết lại với program này:

Author: Khôi Dương