Gabriel Landau @ Elastic Security
See the accompanying Elastic Security Labs article, Finding Truth in the Shadows.
The shadow stack provides an interesting detection opportunity. Adversaries can use tools like ThreadStackSpoofer and CallStackSpoofer to obfuscate their presence against thread stack scans (e.g. StackWalk64
) and inline stack traces like Sysmon operations.
By comparing a traditional stack walk against its shadowy sibling, we can both detect and see through thread stack spoofing. This tool implements CaptureStackBackTrace
/StackWalk64
with the shadow stack (aka CET/HSP) to catch thread stack spoofing. When the stack is normal, it functions similarly to CaptureStackBackTrace
and StackWalk64
:
Control run demonstrating equivalent output...
CONTROL CaptureStackBackTrace: dps 000001CE41EDA800
0: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!main + 0x5f
1: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!__scrt_common_main_seh + 0x10c
2: \Device\HarddiskVolume3\Windows\System32\kernel32.dll!BaseThreadInitThunk + 0x14
3: \Device\HarddiskVolume3\Windows\System32\ntdll.dll!RtlUserThreadStart + 0x21
CONTROL StackWalk64: dps 000001CE42F80D80
0: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!main + 0x5f
1: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!__scrt_common_main_seh + 0x10c
2: \Device\HarddiskVolume3\Windows\System32\kernel32.dll!BaseThreadInitThunk + 0x14
3: \Device\HarddiskVolume3\Windows\System32\ntdll.dll!RtlUserThreadStart + 0x21
CONTROL CET Stack: dps 000001CE41ED79C0
0: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!main + 0x5f
1: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!__scrt_common_main_seh + 0x10c
2: \Device\HarddiskVolume3\Windows\System32\kernel32.dll!BaseThreadInitThunk + 0x14
3: \Device\HarddiskVolume3\Windows\System32\ntdll.dll!RtlUserThreadStart + 0x21
It's unaffected by intentional breaks of the call stack such as ThreadStackSpoofer.
Breaking stack walk with a NULL return address...
BROKEN CaptureStackBackTrace: dps 000001CE41ED79C0
0: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!SpoofStackThenCall + 0x40
BROKEN StackWalk64: dps 000001CE42E1FCD0
0: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!SpoofStackThenCall + 0x40
BROKEN CET Stack: dps 000001CE41F23020
0: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!SpoofStackThenCall + 0x40
1: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!main + 0x8c
2: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!__scrt_common_main_seh + 0x10c
3: \Device\HarddiskVolume3\Windows\System32\kernel32.dll!BaseThreadInitThunk + 0x14
4: \Device\HarddiskVolume3\Windows\System32\ntdll.dll!RtlUserThreadStart + 0x21
It doesn't care about forged stack frames:
Spoofing call stack to hide ShadowStackWalk.exe!main...
SPOOFED CaptureStackBackTrace: dps 000001CE41ED79C0
0: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!SpoofStackThenCall + 0x40
1: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!__scrt_common_main_seh + 0x10c
2: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!__xi_z + 0x0
SPOOFED StackWalk64: dps 000001CE41F23020
0: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!SpoofStackThenCall + 0x40
1: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!__scrt_common_main_seh + 0x10c
2: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!__xi_z + 0x0
SPOOFED CET Stack: dps 000001CE42C927C0
0: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!SpoofStackThenCall + 0x40
1: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!main + 0xbe
2: \Device\HarddiskVolume3\git\ShadowStackWalk\x64\Release\ShadowStackWalk.exe!__scrt_common_main_seh + 0x10c
3: \Device\HarddiskVolume3\Windows\System32\kernel32.dll!BaseThreadInitThunk + 0x14
4: \Device\HarddiskVolume3\Windows\System32\ntdll.dll!RtlUserThreadStart + 0x21