基于 memcpy() 的竞争条件是否可用于导致远程代码执行?

信息安全 linux C 开发开发 x86 竞争条件
2021-08-31 06:27:30

假设我在沙盒的受信任部分中有以下伪代码,可以防止不受信任的代码调用mprotect()直接无法从沙盒内存访问 mutext) ......mmap()ptrace()

//src and dest are user controlled but must be valid.
TrustedPart_for_safe_jit(void * mutext, uint8_t *src,uint8_t *dest, uint32_t size) // in the current case, *dest targets a PROT_NONE memory region
{
    MutexLock(mutext);
    ValidateOpcodesOrCrash(src,size); // uses calls to mmap on size internally. Contains many different loops and use several 10k thousands lines of codes in the trusted part of the sandbox : this is the longest part. Please also note that src is write protected while being in this function.
    unwriteprotect(dest,size); // calls many sandbox’s internal functions

    SafeMemcpy(src,dest,size); // THIS IS the function which contains the race condition

    asm("mfence");
    unEXECprotect(dest,size); // involve write protecting as well as allowing reading
    MutexUnlock(mutext);
}

SafeMemcpy(uint8_t *src,uint8_t *dest, uint32_t size) // the data to be copied cannot exceed 128Mb
{
    if(!CheckUserTargetPointToValidMemroyRange(dest,size) {
        uint8_t *src_ptr=src;
        uint8_t *dest_ptr=dest;
        uint8_t *end_ptr=des+size;
        while (dest_ptr < end_ptr) { // that loop should execute very fast
            *(uint32_t *) dest_ptr = *(uint32_t *) src_ptr;
            dest_ptr += 4;
            src_ptr += 4;
        }
    }
}

该部分负责允许不受信任的代码使用ᴊɪᴛ编译。
关键是不受信任的线程没有被挂起。

如您所知,当 2 个线程使用memcpy()相同的目的地时,它们会生成随机数据。在这种情况下,此类数据可能包含类似的指令int 0x80,从而允许逃离沙箱。

到目前为止我想到的事情:

  • 将数据写入文件并通过沙箱使用 read 系统调用读取它。如果内存仍处于写保护状态,则程序不会崩溃。这将涉及循环,即使要复制的数据可以是 128Mb 大,由于系统调用开销,我不确定它是否会起作用。
    另一种方法是多次创建代码并尝试使用多个时间读取,但我不知道如何选择初始时间窗口。
  • 使用 futex... 但是我找不到 futex 是否可以用来检查未分配内存的值。我也不确定线程​​是否可以在内存被写保护之前唤醒。

那么,是否可以为 memcpy 竞争条件计划时间窗口?

2个回答

根据我在这里看到的内容,您可以在 ValidateOpcodesOrCrash 完成检查该部分内存之后但在 SafeMemcpy 启动之前修改 *src。

我不知道 ValidateOpcodesOrCrash 是如何实现的,但假设它只是循环[src,src+size]并查找非法指令,那么您可以使用相当大的 TrustedPart_for_safe_jit 调用size,忙等待数百个 CPU 周期,然后开始覆盖*srcValidateOpcodesOrCrash 可能已完成检查。如果 ValidateOpcodesOrCrash 做了更复杂的事情,您可以找出 ValidateOpcodesOrCrash 检查的最快和最慢的指令序列,将最快的放在前面,将很多最慢的指令放在最后。在开始覆盖 src 之前,您可能不需要等待 ValidateOpcodesOrCrash 完成。

(免责声明:我不是安全专业人士——只是一个对 C 有一定了解的学生)

我认为这很可能是可利用的,至少会导致崩溃。要修复该错误,您需要:

  • dest可写时挂起所有不受信任的线程
  • TrustedPart_for_safe_jit返回新分配的可执行内存(我假设不允许不受信任的代码将任意机器地址作为指针咳出)。

这意味着您需要停止世界,而不是使用互斥锁。