Buffer overflow protection for stackalloc in .Net

2020-06-04 09:50发布

From C# reference for stackalloc:

the use of stackalloc automatically enables buffer overrun detection features in the common language runtime (CLR). If a buffer overrun is detected, the process is terminated as quickly as possible to minimize the chance that malicious code is executed.

Specifically, what kind of protection mechanism is implemented for .NET?
And will it also detect buffer underruns? Against which known attacks is the protection weaker?


For a context, for example for MS C++ compiler the information is available here:
Windows ISV Software Security Defenses:

The Stack Buffer Overrun Detection capability was introduced to the C/C++ compiler in Visual Studio .NET 2002 and has been updated in subsequent versions. /GS is a compiler switch that instructs the compiler to add startup code, and function epilog and prolog code, to generate and check a random number that is placed in a function's stack.

Note that Visual C++ 2005 (and later) also reorders data on the stack to make it harder to predictably corrupt that data. Examples include:
• Moving buffers to higher memory than non-buffers. This step can help protect function pointers that reside on the stack.
• Moving pointer and buffer arguments to lower memory at run time to mitigate various buffer overrun attacks.

1条回答
Lonely孤独者°
2楼-- · 2020-06-04 10:45

Yes, the .NET jitter generates the kind of stack canary checking that also exists in native code generated by the Microsoft C/C++ compiler with the /GS compiler option. The basic scheme is to store a random 32-bit value at the top of the stack, written at method entry. At method exit it checks if the value is still there. A change in the value is a very high predictor for a stack buffer overflow, the kind that malware uses to take control of a program.

Some code to play with:

class Program {
    static void Main(string[] args) {
        Kaboom();
    }
    static unsafe void Kaboom() {
        byte* ptr = stackalloc byte[1];
        for (int ix = 0; ix < 42; ++ix) ptr[ix] = 0;
    }
}

Running this code triggers the Windows Error Reporting dialog, even with the debugger attached. You can see the crash reason in the Output window:

The program '[3636] ConsoleApplication33.exe: Native' has exited with code -1073740791 (0xc0000409).

The exception code is defined in the ntstatus.h SDK header file:

//
// MessageId: STATUS_STACK_BUFFER_OVERRUN
//
// MessageText:
//
// The system detected an overrun of a stack-based buffer in this application. This overrun could 
// potentially allow a malicious user to gain control of this application.
//
#define STATUS_STACK_BUFFER_OVERRUN      ((NTSTATUS)0xC0000409L)    // winnt

You can see the code that does this with Debug + Windows + Disassembly. The essential parts of Kaboom:

00000000  push        ebp                               ; setup stack frame
00000001  mov         ebp,esp 
00000003  sub         esp,8                             ; stack space for local variables
00000006  xor         eax,eax 
00000008  mov         dword ptr [ebp-8],eax             ; zero-initialize local variables
0000000b  mov         dword ptr [ebp-4],eax 
0000000e  mov         dword ptr [ebp-4],esp
00000011  mov         dword ptr [ebp-8],0EDDB7EDFh      ; canary stored here

// For loop code omitted

0000002d  cmp         dword ptr [ebp-8],0EDDB7EDFh      ; check canary value
00000034  je          0000003B 
00000036  call        727767A8                          ; crash program
0000003b  lea         esp,[ebp]                         ; normal exit, pop stack frame
0000003e  pop         ebp 
0000003f  ret 

The actual canary value changes every time the code is jitted.

查看更多
登录 后发表回答