Control-Flow Enforcement Technology
I was analyzing common approach to Endpoint Detection using Linux Audit subsystem and its rules and I thought that it would be better to monitor syscalls instead of commands. We can step one level below. For example: “ifconfig eth0 promisc” can be replaced with “PARAM=promisc cat $(which ifconfig) > /tmp/cmd; /tmp/cmd eth0 $PARAM”. If we would use detection rule based on keywords “ifconfig” and “promisc” a tenager would be able to evade our detection. But it we would like to detect ioctl putting network interface into promiscuous mode we would be more successful.
Among many rules I created:
-a always,exit -F arch=b64 -S prctl -F a0!=13 -F a0!=14 -F a0!=27 possible_exploit_prctl
-a always,exit -F arch=b32 -S arch_prctl -F a0!=0x1002 -F a0!=0x3001 -k possible_exploit_prctl
Process control is a very interesting system call. Using this kernel function it’s possible to deal with zombies (PR_SET_CHILD_SUBREAPER, PR_SET_PDEATHSIG) force memory layout (PR_SET_MM) or protection (PR_MPX_ENABLE_MANAGEMENT), change process name visible in /proc/$id/task/$thread_id/comm (PR_SET_NAME), limit/unlimit system calls using seccomp (PR_SET_SECCOMP), disable Spectre/Meltdown mitigations (PR_SET_SPECULATION_CTRL), modify FS and GS registers. So this is definitely interesting operation and there is some probability that potential malware would be using it.
OK. Audit rules applied. Red Hat 7.9 is clean. Let’s switch to Red Hat 8.3. I see a lot of possible_exploit_prctl in /var/log/audit/audit.log with executables simple as /bin/bash or /bin/rm. Argument is 0x3001, ARCH_CET_STATUS. CET stands for Intel’s Control-Flow Enforcement Technology. To understand it we need to recall that when we have “if” block in the C/C++ code after checking the condition we have assembler jump instruction to some subroutine and then return. Jump and return can be hijacked and control flow of the program can be totally different than expected by the programmer. What would be the first software protection against attacks targeting jmp (jump) and ret (return)? Adding NOP (no operation) assembler instructions in the neighborhood of jmp and ret, so nothing random would be executed. We need to return to the correct program location and return address is on the stack. If the stack is modified we can go back to a wrong place. The second protection is to have a shadow copy of the stack and compare 2 return addresses. If these are different then we know that the program was compromised. The address of the shadow stack should be random, so the attacker doesn’t know how to defeat our protection. What if the attacker knows how GCC (or MSVC) compiler creates shadow stack? We lose. Or we must have a hardware based mechanism. Intel came up with CET. CPU provides additional instructions to managed hardware-base shadow stack. For code branches the ENDBR32/64 instruction should be executed after returning from a branch. If the CPU detects that it was missing it means that the program was hacked.
Intel documentation is here: https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf
Linux support for every place in the OS is still in progress (as of time of writing): https://patchwork.kernel.org/project/linux-mm/list/?series=434721
Glibc supports CET configuration via prctl: https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/x86/include/asm/prctl.h;h=45ad0b052f528f2a1653b0b12bedaa58ab4b9620;hb=refs/heads/master
Conclusion: we should monitor ARCH_CET_DISABLE (0x3002) via audit rules.