Xorg LPE CVE 2018-14665

2 minute read

On October 25th 2018 a post was made on SecurityTracker disclosing CVE 2018-14665. The interesting thing is this CVE has two bugs in two different arguments. The first is a flaw in the -modulepath argument which could lead to arbitrary code execution. The second was a flaw in the -logfile argument which could allow arbitrary files to be deleted from the system. Both of these issues were caused by poor command line validation.

How does it work?

It is worth noting that the reason this can be used for privilege escalation is because the process runs as root by default. The problem here is that there is no check to see whether the user is already root or not. Hence, if you’re not user, you can pass the argument -modulepath and get root.

We’ll start with -modulepath, all this argument is intended for is to allow us to set a directory to search for Xorg server modules. Since this process runs as root, if we set this to an insecure path we can get it to run unpriviliged code in a privileged context.

The -logfile argument is used to set a new log file for the Xorg server instead of using the default log file that is found in /var/log. Since this runs in a privileged context, we can use this bug to overwite /etc/shadow for example.

The one drawback of these priv-esc bugs is that you can only run them via a console session and not over SSH, at least, that is how it seems for Red Hat systems running Xorg. This is due to a default module called Pluggable Authentication Module however, researcher Matthew Hickey found that on affected OpenBSD systems you can exploit this vulnerability over SSH, you can find his tweet here which includes a shell script you can use to exploit this vulnerability.

Code Review

There isn’t much code to look at here since the bugs are very simple, with a very simple fix. All of the code below you can find on the Xorg GitLab repo, the commit we’re specifically looking at is commit 50c0cf88. As you can see, the issue was found in xf86Init.c:

/* First the options that are not allowed with elevated privileges */
    if (!strcmp(argv[i], "-modulepath")) {
        CHECK_FOR_REQUIRED_ARGUMENT();
        xf86CheckPrivs(argv[i], argv[i + 1]);
        xf86ModulePath = argv[i + 1];
        xf86ModPathFrom = X_CMDLINE;
        return 2;
    }
    if (!strcmp(argv[i], "-logfile")) {
        CHECK_FOR_REQUIRED_ARGUMENT();
        xf86CheckPrivs(argv[i], argv[i + 1]);
        xf86LogFile = argv[i + 1];
        xf86LogFileFrom = X_CMDLINE;
        return 2;
    }

The code above is where the bug lies. As you can see, there are no checks for the current users authentication level. Because the process runs as root, if a non-root user passes in a flag, they are now executing in a privileged process.