Original post on my old site
Before getting to the bug itself, I need to explain a few concepts. You can jump directly to it if you want.
Key concepts
Before getting to the bug itself, I need to explain a few concepts. You can jump directly to it if you want.
Key concepts
Google has a service called Google Service Management, which provides a way to manage several aspects of services (APIs) that use Google's systems (Such as Google's own public and internal APIs, and services deployed by users using Google Cloud Endpoints).
This service allows users to enable/disable services on their Google Cloud Platfom projects,
list available services (Such as Maps API, Gmail API, private APIs you
have access to, etc.), and manage the settings of their own services
through service configurations.
Developers will rarely have to use this service directly, mostly interacting with it through the Google Cloud Console or command line
(For enabling/disabling services), or through Google Cloud Endpoints
(For managing settings), but it also has a very interesting API.
This API can not only do everything stated above and in its official documentation, but it also has some hidden capabilities only accessible for Google services that use the API.
Both, hidden methods and hidden parameters, are use a feature of Google services called "Visibility" (Documented externally, though only used internally by Google).
Note: Hidden parts of a Google-owned
API can be found using several ways, and most of the time they have
hidden documentation as well, Google does not consider finding hidden
API capabilities or hidden API documentation a security vulnerability (I
already tried reporting those to them).
But, there are some hidden capabilities that, if used successfully, are considered a security vulnerability (Like the "usage.dependsOnServices" field in this bug).
But, there are some hidden capabilities that, if used successfully, are considered a security vulnerability (Like the "usage.dependsOnServices" field in this bug).
GET /v1/services?key=AIzaSyCI-zsRP85UVOi0DjtiCwWBwQ1djDy741g HTTP/1.1
Host:
servicemanagement.clients6.google.com
Authorization: SAPISIDHASH <SAPISIDHASH>
X-Origin:
https://console.cloud.google.com
Cookie: <GOOGLE COOKIES>
Let's see what each part is:
- "clients6.google.com" is just another way of saying "googleapis.com",
which is needed since the cookies can only reach google.com subdomains.
One of several capabilities this client has is creating a service
with a configuration attached since the beginning (Normal clients will
just ignore the "serviceConfig" parameter, since it is hidden,and create
a service with no initial configuration), a simple request like this:
POST /v1/services?key=AIzaSyCI-zsRP85UVOi0DjtiCwWBwQ1djDy741g HTTP/1.1
Host:
servicemanagement.clients6.google.com
Authorization: SAPISIDHASH <SAPISIDHASH>
X-Origin:
https://console.cloud.google.com
Cookie: <GOOGLE COOKIES>
Content-Length: <CONTENT-LENGTH>
{
"serviceName": "<SERVICE NAME>",
"producerProjectId": "<PROJECT>",
"serviceConfig": {
"name": "
<SERVICE NAME>
",
"producerProjectId": "<PROJECT>",
"configVersion": 3
}
}
The bug
Normally, the "serviceName" and the "serviceConfig.name" parameters must match in any request where both are specified, but during creation this constraint was not being checked when the "configVersion" was set to either 1, 2, or a value between 2147483648 and 4294967295 (An integer overflow occurs in the back-end), therefore any user could create a service with a real name (Such as "the-expanse.appspot.com") but with a configuration that claims it is a different service (Such as "my-private-secure-api.appspot.com").
Note: This bug did not work for a few old Google services due to a special setting they have for compatibility reasons.
This was a very big deal, a few important processes use the name
found in the configuration of a service for performing any action,
except for checking permissions, in that case they use the service name
itself. So, with a different name in the configuration, an attacker
could be permitted to perform important actions on a different service.
Among this actions were:
- Enabling the other service:
If I have my service
"the-expanse.appspot.com" with "very-important-api.example.com" in its
configuration, when enabling "the-expanse.appspot.com" for a project,
Google will proceed because I have permission to enable
"the-expanse.appspot.com", but the action will really be performed on
"very-important-api.example.com", so I end up having enabled
"very-important-api.example.com".
If a customer sets up an API to use Google
API keys and/or Google Auth tokens to authenticate legitimate clients,
this could be bypassed by an attacker.
Google itself uses this method for
authenticating legitimate clients, so an attacker could be able to use
private Google APIs that are in development, are meant to be used only
internally, or that are accessible for a few white-listed users (Trusted
Testers, Google My Business API, etc.).
- Getting access to hidden capabilities:
A hidden method in the Service Management
API is "PatchProjectSettings", this allows a service owner to set some
hidden settings of a specific service consumer (Project). Among this
settings is the option for setting visibility labels (Which manage
access to hidden capabilities).
For instance, if I have my service
"the-expanse.appspot.com" with "cloudresourcemanager.googleapis.com" in
its configuration, I can send the following request and get access to
capabilities being tested by a select few (Trusted Testers) in the Cloud Resource Manager API on my project (the-expanse):
PATCH
/v1/services/the-expanse.appspot.com/projectSettings/the-expanse?updateMask=visibilitySettings&key=AIzaSyCI-zsRP85UVOi0DjtiCwWBwQ1djDy741g
HTTP/1.1
<..SAME HEADERS AS BEFORE...>
{
"visibilitySettings": {
"visibilityLabels": [
"TRUSTED_TESTER"
]
}
}
- Disabling the service on someone else's project:
Using the same method as before, we can
set another setting that controls whether a project has enabled or not
the service. Note that you cannot enable your service on a project you
do not own, only disable it.
For instance, if I have my service "the-expanse.appspot.com" with
"cloudresourcemanager.googleapis.com" in its configuration, I can send
the following request and disable the Cloud Resource Manager API on Cloud SDK (Which uses the project "google.com:cloudsdktool"):
PATCH /v1/services/the-expanse.appspot.com/projectSettings/google.com:cloudsdktool?updateMask=
usageSettings&key=AIzaSyCI-zsRP85UVOi0DjtiCwWBwQ1djDy741g HTTP/1.1
<..SAME HEADERS AS BEFORE...>
{
"
usageSettings": {
"
consumerEnableStatus": "
DISABLED"
}
}
This bug could lead to lots of issues, enabling private APIs,
getting access to hidden capabilities and disabling services for others
could probably lead to several issues arising for lots of people very
easily. I have not tested most of these scenarios and maybe they were never possible, but I am very confident they could be done:
- Accessing several Google APIs in development to use features not yet released to the general public
- Accessing billed Google APIs for free by enabling them using this
bug (The billing constraint is enforced using the real service name)
- Accessing private APIs developed by Google users using Google Cloud Endpoints
- Accessing hidden features not yet released for the general public in public Google APIs
- Bypassing quota limits (They are stored in the project settings too)
- Exploiting bugs not available without the use of this one
- Disabling key APIs for some projects that lead to service outages
(Such as the Cloud SDK not being able to access projects, Android's
YouTube app not being able to retrieve videos' metadata, etc.).
Timeline (UTC-3)
- 2018-01-27 afternoon - Bug found
- 2018-01-27, 06:45 PM - Initial submission
- 2018-01-27, around 7:45 PM - Google Service Management API development team discovered the bug independently (Note it was a Saturday!)
- 2018-01-29 - Bug during creation fixed by the development team
- 2018-01-29, 12:53 PM - Bug report triaged
- 2018-01-30 - All services with mismatched serviceName/serviceConfig.name pairs were completely purged from Google's systems, thus this bug could no longer be exploited
- 2018-01-30, 06:46 PM - Security team cannot reproduce scenario #3, the security engineer keeps getting 401 errors
- 2018-01-30, 08:21 PM - It is confirmed that the development team discovered the "hack" and patched an emergency fix during the weekend
- 2018-01-31, 12:42 PM - I am told the development team independently discovered the bug an hour after my report, nevertheless my report is sent to the security panel to check if a reward can be issued
- 2018-02-14, 06:12 AM - A reward of 7500 dollars is issued
Comments
Post a Comment