在ARM Cortex-M3上编写一个简单的C任意代码执行漏洞?

我正在尝试用C语言编写一个概念certificate,它演示了ARM Cortex-M3上堆栈中内存缓冲区的代码执行。 这将有助于certificate正确使用ARM MPU可以防止此类攻击。 我想一个快速而肮脏的方法来将一些代码放入堆栈中是从常规函数中复制它然后使用goto跳转到它,如下所示:

static void loopit(void) { printf("loopit\n"); while (1); } void attack(void) { uint8_t buffer[64] __attribute__((aligned(4))); memcpy(buffer, loopit, sizeof(buffer)); goto *((void *) (int) buffer); } 

我希望当我调用攻击函数时,它会将代码复制到堆栈中,跳转到它,打印消息并进入无限循环。 但是,我在故障寄存器中得到以下值的exception:

 HFSR = 0x40000000 CFSR = 0x00020000 PSR = 0x60000000 

这似乎是UFSR中的INVSTATE位,表示“非法使用EPSR”,我读到的通常是由于BX指令试图跳转到LSB设置为0的地址,处理器将其解释为函数其中包含非Thumb代码,但Cortex-M处理器仅允许Thumb代码。 我看到memcpy被赋予了loopit函数的奇数地址,因为我假设编译器正在将实际内存地址与1或运算。 所以我认为修复就像重写我的攻击函数一样:

 void attack(void) { uint8_t buffer[64] __attribute__((aligned(4))); memcpy(buffer, ((int) loopit) & ~1, sizeof(buffer)); goto *((void *) ((int) buffer) | 1); } 

但是在这之后我得到了一个与故障寄存器不同的exception:

 HFSR = 0x40000000 CFSR = 0x00080000 PSR = 0x81000000 

这似乎没有任何意义,UFSR第3位设置意味着“处理器试图访问协处理器”。 看看PC,这次看起来跳跃成功了,但是后来有些事情发生了变化,CPU似乎正在执行奇怪的指令,而不是进入无限循环。 我尝试在goto之前关闭中断并注释掉printf,但没有运气。 有什么问题以及如何使其发挥作用?

很抱歉滥用答案表格,我已经调整了一些代码,它从堆栈中闪烁了一个LED:

 void (*_delay_ms)(uint32_t) = delay_ms; static void loopit(void) { while (1) { GPIOC->ODR ^= 1 << 13; _delay_ms(125); } } void attack(void) { volatile uint8_t buffer[64] __attribute__((aligned(4))); memcpy(buffer, (void *)((uint32_t) loopit & ~1), sizeof(buffer)); goto *(void *)((uint32_t) buffer | 1); } 

我想知道我多久会收到有关UB的投诉。

我最终没有使用goto ,也没有尝试从复制到堆栈内存的函数中执行任何函数。 还要确保使用noinlineO0编译堆栈函数。

我使用以下代码将堆栈地址转换为函数指针:

 // Needed a big buffer and copied to the middle of it #define FUNC_SIZE 256 #define BUF_SIZE (FUNC_SIZE * 3) uint8_t mybuf[BUF_SIZE] __attribute__((aligned(8))); uintptr_t stackfunc = (uintptr_t) mybuf; stackfunc += FUNC_SIZE; memcpy((void *) stackfunc, (void *) (((uintptr_t) &flashfunc) & ~1), FUNC_SIZE); void (*jump_to_stack)(void) = (void (*)(void)) ((uintptr_t) stackfunc | 1); jump_to_stack(); 

不知道为什么我必须让缓冲区这么大。 我将函数复制到缓冲区的中间。

 void attack(void) { uint16_t buffer[64]; goto *((void *) (((unsigned int)(buffer)) | 1)); } 

你要求它做一个分支,它不需要为分支设置lsbit,分支交换肯定。 在这种情况下,让工具完成它的工作。 或者,如果存在问题,请使用汇编语言来执行分支,以便您可以专门控制所使用的指令,从而控制地址。

 00000000 : 0: b0a0 sub sp, #128 ; 0x80 2: 2301 movs r3, #1 4: 466a mov r2, sp 6: 4313 orrs r3, r2 8: 469f mov pc, r3 a: 46c0 nop ; (mov r8, r8) 

在这种情况下甚至不是分支,而是一个mov pc(function相同)。 这绝对不在交互式指令列表中。 请参阅架构参考手册。