保护函数返回的利器——Linux Shadow Call Stack

0x01 写在前面

        提到内核栈溢出的漏洞缓解,许多朋友首先想到的是栈内金丝雀(Stack Canary)。今天向大家介绍一项在近年,于Android设备中新增,且默默生效的安全机制——影子调用栈:SCS(Shadow Call Stack)

0x02 功能介绍

        在通常的函数调用中,被调用函数的返回地址存储在栈上,攻击者可以通过篡改栈上返回地址劫持程序的执行流,常见的攻击方式如堆栈溢出、ROP(Return Oriented Programming)攻击等。

        SCS是一项基于LLVM(Low Level Virtual Machine)的安全功能,它通过在函数调用时使用一种安全栈帧来解决这个问题。SCS开启后,这个安全栈帧上存储了函数调用的返回地址,而不是直接存储在常规栈上。函数在使用返回地址时,将直接从安全栈帧中读取。从而避免了传统的、针对栈上返回地址的攻击方法。

0x03 Google要求

        自Android R开始,AOSP(Android Open Source Project)的CDD(Compatibility Definition Document: https://source.android.com/docs/compatibility/cdd) 要求中,就已强烈建议使能CFI, SCS, IntSan。而在kernel 5.4的版本AOSP的kernel版本默认开启。

保护函数返回的利器——Linux Shadow Call Stack_第1张图片

0x04 依赖条件

        SCS需要硬件支持,目前只在特定处理器架构上受到支持,如x86_64架构中的Intel CET或aarch64架构中的Pointer Authentication。这些硬件提供了必要的指令和功能来支持SCS的运行。

        SCS目前仅支持aarch64架构,有数据表明在X86_64架构上具有严重性能和安全缺陷,LLVM 在9.0中已将其删除。

保护函数返回的利器——Linux Shadow Call Stack_第2张图片

0x05 开启方法

    根据Google介绍,可以为整个内核或者单独为某个用户空间的进程、服务开启SCS,开启方法请参考:

或在编译时将-fsanitize=shadow-call-stack 标志传递给链接命令行。如果当前代码不需要应用SCS,可以使用__attribute__((no_sanitize("shadow-call-stack")))对函数声明。

保护函数返回的利器——Linux Shadow Call Stack_第3张图片

0x06 安全原理

    SCS旨在补充 -fstack-protector,以构建安全纵深防御的目标,防止非线性溢出等任意攻击栈上返回地址的手段。

        在 Aarch64 架构上,SCS使用 X18 寄存器来引用影子调用栈,这意味着不必再将 SCS的引用存储在内存中。以此避免将影子调用堆栈的地址暴露给可以读取任意内存的攻击者。SCS不需要错误处理,因为它无条件地信任、使用影子堆栈里的返回地址。

以下面这段demo为例:

Int foo () {

    Return bar()+1;

}

Demo转换成汇编:

push   %rax
callq  bar
add    $0x1,%eax
pop    %rcx

Retq

当这段代码在开启SCS后,汇编代码变化为:

str     x30, [x18], #8  
stp     x29, x30, [sp, #-16]!
mov     x29, sp
bl      bar
add     w0, w0, #1
ldp     x29, x30, [sp], #16
ldr     x30, [x18, #-8]!

Ret

 

        在Aarch64架构上通用寄存器如下,X18目前被大多数ARM预置且未使用。而X30是LR,用于存放函数返回值。

保护函数返回的利器——Linux Shadow Call Stack_第4张图片

上述代码框中标红指令就是SCS的核心逻辑,分段解释:

str     x30, [x18], #8  

    首先将寄存器 X30 中的数据存储到 X18 指向的内存地址处,然后将 X18 中的地址值增加 8 字节。

ldr     x30, [x18, #-8]!  

    将 X18 寄存器中存储的地址值减去 8 字节,然后将存储在该地址处的数据加载到 X30 寄存器中,同时更新 X18 的值。

0x07 功能风险

        SCS需要使用Aarch64的X18寄存器来存储影子调用栈的地址,需要默认该寄存器不会用于任何其他目的。虽然当前所有系统库都编译为预留 X18 寄存器,但如果存在第三方库、早期库时,需要确定第三方应用没有使用X18寄存器。

        当前2023年国内所有手机厂商均默认支持SCS,不存在兼容性问题,未发现该功能上存在明显缺陷。

0x08 功能结果

    从OPPO FIND X3设备里导出bootimage,使用IDA逆向,检测任意内核地址的返回值,确认具备SCS的明显特征,如下截图。

保护函数返回的利器——Linux Shadow Call Stack_第5张图片

你可能感兴趣的:(linux,安全,内核,漏洞缓解)