ESCALATED: Blind SSRF on External Authorization Service

HackerOne report #692159 by xanbanx on 2019-09-11, assigned to hackerjuan:

Hi GitLab Security Team,

Summary

I found a blind SSRF vulnerability on the external authorization server configuration. An attacker can thereby make POST requests to the local network or to any other one on behalf of the GitLab server.

Steps to reproduce

This has been tested on a local installation of GitLab EE 12.3.0-pre d777fa6d.

  1. Set local listener on, e.g., port 10000: nc -dklp 10000 -vv
  2. As an administrator, go to http://example.gitlab.com/admin/application_settings#js-external-auth-settings
  3. Enable and enter the Service URL http://localhost:10000 and save the settings. Don't forget to fill out the default classification label, this this field is required.
  4. Save the settings again to trigger a call authorization server, monitor the POST request and the previously set up listener
    • In the user interface you see the error External authorization service default label is unavailable: read timeout reached because the listener did not respond
    • On http://localhost:22 you'll get the error message External authorization service default label is unavailable: malformed header (Excon::Error::ResponseParse)
    • On a not open port you get the error message External authorization service default label is unavailable: Connection refused - connect(2) for 127.0.0.1:1000 (Errno::ECONNREFUSED)

Impact

An attacker can create blind post requests on behalf of the GitLab server including to the local infrastructure. You can easily scan the ports of any network be capturing the response. An administrator, who does not have access to the underlying infrastructure can use this to scan the network to findout how to go forward with an attack.

What is the current bug behavior?

URL input for external authorization accepts local addresses. Furthermore, upon request, the URL is not validated

What is the expected correct behavior?

The service URL should not access local addresses nor the request should perform requests to local networks. After looking to the code, I found that in lib/gitlab/external_authorization/client.rb#20 the POST request is performed using Excon.post() rather than Gitlab::HTTP, which contains all SSRF mitigations.

Relevant logs and/or screenshots

Output of checks

This has been tested on a local installation of GitLab EE 12.3.0-pre d777fa6d

Results of GitLab environment info

System information  
System:         Ubuntu 18.04  
Proxy:          no  
Current User:   xanbanx  
Using RVM:      no  
Ruby Version:   2.6.3p62  
Gem Version:    3.0.3  
Bundler Version:1.17.2  
Rake Version:   12.3.2  
Redis Version:  4.0.9  
Git Version:    2.23.0  
Sidekiq Version:5.2.7  
Go Version:     go1.12.6 linux/amd64

GitLab information  
Version:        12.3.0-pre  
Revision:       d777fa6dd9f  
Directory:      /home/xanbanx/gdk/gdk-ee/gitlab  
DB Adapter:     PostgreSQL  
DB Version:     10.10  
URL:            http://localhost:3001  
HTTP Clone URL: http://localhost:3001/some-group/some-project.git  
SSH Clone URL:  ssh://xanbanx@localhost:2222/some-group/some-project.git  
Elasticsearch:  no  
Geo:            no  
Using LDAP:     no  
Using Omniauth: yes  
Omniauth Providers: 

GitLab Shell  
Version:        10.0.0  
Repository storage paths:  
- default:      /home/xanbanx/gdk/gdk-ee/repositories  
GitLab Shell path:              /home/xanbanx/gdk/gdk-ee/gitlab-shell  
Git:            /usr/bin/git  

Best regards,
Xanbanx

Impact

See above.

Edited Dec 29, 2019 by GitLab SecurityBot
Assignee Loading
Time tracking Loading