Friday, August 17, 2012

Today's Security Update: XML vulnerabilities

If you're subscribed to the PostgreSQL News feed (and if not, why aren't you?) you already know that we released another security patch today.  This update release patches two security holes having to do with XML functionality, which can be used by any authenticated user for privilege escalation.  Everyone should apply the updates at the next reasonable downtime, and users of contrib/xml2 should schedule a downtime this weekend.

You'll notice that this security update release is on a Friday.  This is not our normal practice, since it makes it difficult for IT departments to respond in a timely fashion (especially the folks in Australia/Japan, who will be getting the announcement on Saturday).  However, as our number of packagers for different platforms has grown, it's become increasingly difficult to schedule coordinated releases, especially during the summer and the India/Pakistan holiday season.  As it is, the Solaris folks will be getting their binaries late.

Anyway, about these vulnerabilities:  The first one is a vulnerability in the built-in xmlparse() function.  Among other things, this function used to allow users to validate the XML against a DTD contained in an external file.  However, a creative user would be able to call any file which the "postgres" user has permissions on, and read parts of that file in the error messages from the DTD reader.  Since "files the postgres user has access to" includes pg_hba.conf and .pgpass (if defined), this is a dangerous capability.  As such, we have had to disable the DTD-validation feature.  This validation feature may return at a later date if we can figure out a reasonable way to make it safe.

The second security hole is in the outdated extension (or "contrib module"), xml2.  Users still use xml2 rather than the built-in XML because of its XSLT support.  The problem is that xslt_process() had a documented "feature" which allowed the function to transform XML documents fetched from external resources, such as URLs -- without requiring superuser permissions.  Not only could this be used to read local files on the PostgreSQL server, it could be used to write them as well, making this a much worse security hole if you have xml2 installed.   As such, the xslt_process() feature has been disabled, and will probably not return.

We've been sitting on both of these patches for an embarassingly long time (for our project, at least), because we were looking for a solution which didn't involve disabling functionality which is potentially valuable to some users.  Sadly, we were not able to.


  1. > the obvious workaround for the xml_parse() issue is to revoke EXECUTE permission on the function from "public" and all users

    Erm, which "the function"? There is no "xml_parse" function. As far as I can tell SQLPARSE() is a SQL-level construct. The release notes are very confusing with regards to this, too.

    "\df *parse*" only displays some irrelevant functions.

    Here's what I tried:
    create role someuser;
    revoke execute on function xml_in(cstring) from someuser;
    revoke execute on function xml_in(xml, text) from someuser;
    revoke execute on function xmlvalidate(xml, text) from someuser;
    revoke execute on function xml_is_well_formed(text) from someuser;
    revoke execute on function xml_is_well_formed_content(text) from someuser;
    revoke execute on function xml_is_well_formed_document(text) from someuser;

    set role=someuser;
    select xmlparse(document '');

    Still doesn't prevent the usage of xmlparse(), what am I missing?

    1. Intgr,

      No, you're correct. xmlparse() is a low-level function and it doesn't have specific permissions. I've revised my blog post accordingly.