-
Notifications
You must be signed in to change notification settings - Fork 0
/
shellcode.py
executable file
·97 lines (73 loc) · 3.87 KB
/
shellcode.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env python3
#####################################################
# #
# Shellcode dev and testing environment #
# Written by Philippe Dugre #
# #
#####################################################
import ctypes, struct, binascii, argparse
from keystone import *
def main():
parser = argparse.ArgumentParser()
parser.add_argument("file",
type=argparse.FileType('r'),
help="File containing the assembly code.")
args = parser.parse_args()
assembly_code = args.file.read()
ks = Ks(KS_ARCH_X86, KS_MODE_64)
machine_code, count = ks.asm(assembly_code)
print("Number of instructions: " + str(count))
# Packs it and put in in a bytearray...
buf = b''
for i in machine_code:
buf += struct.pack("B", i)
buf = bytearray(buf)
# Print shellcode in a copy-pasteable format
print("\nShellcode length: %d\n" % len(buf))
print(format_shellcode(buf) + "\n")
# Load libraries
libc = ctypes.cdll.LoadLibrary("libc.so.6")
libpthread = ctypes.cdll.LoadLibrary("libpthread.so.0")
# Put the shellcode into a ctypes valid type.
buf = (ctypes.c_char * len(buf)).from_buffer(buf)
# Both function returns 64bits pointers
libc.malloc.restype = ctypes.POINTER(ctypes.c_int64)
libc.mmap.restype = ctypes.POINTER(ctypes.c_int64)
# Get page size for mmap
page_size = libc.getpagesize()
# mmap acts like malloc, but can also set memory protection so we can create a Write/Execute buffer
# void *mmap(void *addr, size_t len, int prot, int flags,
# int fildes, off_t off);
ptr = libc.mmap(ctypes.c_int64(0), # NULL
ctypes.c_int(page_size), # Pagesize, needed for alignment
ctypes.c_int(0x07), # Read/Write/Execute: PROT_READ | PROT_WRITE | PROT_EXEC
ctypes.c_int(0x21), # MAP_ANONYMOUS | MAP_SHARED
ctypes.c_int(-1), # No file descriptor
ctypes.c_int(0)) # No offset
# Copy shellcode to newly allocated page.
libc.memcpy(ptr, # Destination of our shellcode
buf, # Shellcode location in memory
ctypes.c_int(len(buf))) # Nomber of bytes to copy
# Allocate space for pthread_t object.
# Note that pthread_t is 8 bytes long, so we'll treat it as an opaque int64 for simplicity
thread = libc.malloc(ctypes.c_int(8))
# Create pthread in the buffer.
# int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
# void *(*start_routine) (void *), void *arg);
libpthread.pthread_create(thread, # The pthread_t structure pointer where the thread id will be stored
ctypes.c_int(0),# attributes = NULL
ptr, # Our shellcode, which is what we want to execute
ctypes.c_int(0))# NULL, as we don't pass arguments
# Wait for the thread.
# int pthread_join(pthread_t thread, void **retval);
libpthread.pthread_join(thread.contents,# Here, we pass the actual thread object, not a pointer to it
ctypes.c_int(0))# Null, as we don't expect a return value
# Function to format shellcode to a printable output. Modify according to the language you use.
def format_shellcode(shellcode):
LINE_LENGTH=40
raw = binascii.hexlify(shellcode)
escaped = (b"\\x" + b"\\x".join(raw[i:i+2] for i in range (0, len(raw), 2))).decode('utf-8')
lines = [escaped[i: i+LINE_LENGTH] for i in range(0, len(escaped), LINE_LENGTH)]
return "shellcode = \tb\"" + "\nshellcode += \tb\"".join(lines) + "\""
if __name__ == "__main__":
main()