CVE-2014-3537: Insufficient checking leads to privilege escalation #4450

Closed
michaelrsweet opened this Issue Jul 3, 2014 · 11 comments

Comments

Projects
None yet
1 participant
Collaborator

michaelrsweet commented Jul 3, 2014

Version: 1.7.3
CUPS.org User: twaugh.redhat

An HTTP GET request on resources beginning "/rss/" will serve the corresponding file from /var/cache/cups/rss/. This directory has group lp write permission.

The cupsd service runs as root, and follows symlinks when serving files.

A user in the "lp" group is able to create a symlink in /var/cache/cups/rss/ pointing to a local target file which they are not able to read, and can gain read access by fetching the corresponding "/rss/" resource.

Collaborator

michaelrsweet commented Jul 3, 2014

CUPS.org User: mike

This is not exploitable out-of-the-box as we disable the web interface by default.

Once enabled, someone with "_lp" group membership could create a symlink pointing to a sensitive file, and then retrieve the contents of that file using a HTTP GET request to the local cupsd.

The fix will be to lstat the file to be served and then disallow any symlinks (404 response - not found).

(I'm not going to get fancy with this and allow symlinking to world-readable files, we just won't allow it...)?

Collaborator

michaelrsweet commented Jul 3, 2014

CUPS.org User: mike

Proposed patch is attached. Basically, if a file is a symlink or does not have world read permissions, don't try to serve it up.

Collaborator

michaelrsweet commented Jul 4, 2014

CUPS.org User: twaugh.redhat

Thanks for the patch.

It's actually not just information disclosure, as a group lp user can symlink to /var/run/cups/certs/0 and use that to gain '@System' group privilege with cupsd. I've updated the bug title.

Collaborator

michaelrsweet commented Jul 4, 2014

CUPS.org User: mike

OK, so you're good with the patch? I'd like to release 1.7.4 next week (say, July 10th) if you have no objection.

I'm not sure if any OS's put any user other than "lp" in the "lp" group; I know on OS X the only way you'd be able to take advantage of this is to escalate to root first, making this bug less of an issue (why bother if you have root, right?)

Collaborator

michaelrsweet commented Jul 6, 2014

CUPS.org User: twaugh.redhat

Yes, the patch looks good to me.

I don't know whether there are generally any other users in the lp group, but of course if there is any code execution exploit in a filter, that code would run in group lp.

Collaborator

michaelrsweet commented Jul 6, 2014

CUPS.org User: twaugh.redhat

I'm checking whether there are objections with releasing this on July 10th...

Collaborator

michaelrsweet commented Jul 7, 2014

CUPS.org User: twaugh.redhat

I've heard no objections to July 10th.

Collaborator

michaelrsweet commented Jul 8, 2014

CUPS.org User: twaugh.redhat

Acknowledgment:
This issue was discovered by Francisco Alonso of the Red Hat Security Response Team.

Collaborator

michaelrsweet commented Jul 8, 2014

CUPS.org User: mike

OK, July 10th it is.

Collaborator

michaelrsweet commented Jul 14, 2014

CUPS.org User: mike

Fixed in Subversion repository.

Collaborator

michaelrsweet commented Jul 14, 2014

"str4450.patch":

Index: scheduler/client.c

--- scheduler/client.c (revision 11982)
+++ scheduler/client.c (working copy)
@@ -2961,7 +2961,7 @@
if ((ptr = strchr(filename, '?')) != NULL)
*ptr = '\0';

  • if ((status = stat(filename, filestats)) != 0)

  • if ((status = lstat(filename, filestats)) != 0)
    {
    /*

  • Drop the language prefix and try the root directory...
    @@ -2973,14 +2973,35 @@
    if ((ptr = strchr(filename, '?')) != NULL)
    *ptr = '\0';

  •  status = stat(filename, filestats);
    
  •  status = lstat(filename, filestats);
    

    }
    }

    /*

  • * If we're found a directory, get the index.html file instead...

    • If we've found a symlink, 404 the sucker to avoid disclosing information.
      */
  • if (!status && S_ISLNK(filestats->st_mode))

  • {

  • cupsdLogClient(con, CUPSD_LOG_INFO, "Symlinks such as "%s" are not allowed.", filename);

  • return (NULL);

  • }

  • /*
  • * Similarly, if the file/directory does not have world read permissions, do
  • * not allow access...
  • */
  • if (!status && !(filestats->st_mode & S_IROTH))
  • {
  • cupsdLogClient(con, CUPSD_LOG_INFO, "Files/directories such as "%s" must be world-readable.", filename);
  • return (NULL);
  • }
  • /*
  • * If we've found a directory, get the index.html file instead...
  • _/

if (!status && S_ISDIR(filestats->st_mode))
{
/_

michaelrsweet added this to the Stable milestone Mar 17, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment