Bug Report: Heap Buffer Overflow in OP_SERVERMESSAGE Handler
Summary
An unsigned integer underflow in CServerSocket::ProcessPacket allows a remote e2k server to trigger a heap buffer overflow, leading to denial of service (crash) and potential remote code execution.
Affected Component
- File:
src/ServerSocket.cpp
- Function:
CServerSocket::ProcessPacket(const uint8_t* packet, uint32 size, int8 opcode)
- Case:
OP_SERVERMESSAGE (opcode 0x38)
Vulnerability Details
The ProcessPacket function processes incoming packets from e2k servers. The size parameter is of type uint32 (unsigned 32-bit integer). When the OP_SERVERMESSAGE opcode is received, the handler performs the following operations without validating size:
char* buffer = new char[size-1];
memcpy(buffer, &packet[2], size-2);
buffer[size-2] = 0;
The protocol format for server messages is: [2-byte header][message bytes][null terminator]. This means a valid message requires size >= 3. However, no bounds check exists before the arithmetic on the unsigned size value.
Underflow Scenarios
size |
size - 1 |
size - 2 |
Behavior |
| 0 |
0xFFFFFFFF (4,294,967,295) |
0xFFFFFFFE |
new char[] attempts ~4GB allocation → OOM crash or massive buffer; memcpy copies ~4GB → heap overflow / segfault |
| 1 |
0x00000000 (0) |
0xFFFFFFFF |
Zero-size allocation; memcpy copies ~4GB into 0-byte buffer → heap buffer overflow / crash |
| 2 |
0x00000001 (1) |
0x00000000 (0) |
1-byte allocation; memcpy copies 0 bytes; null terminator written to buffer[0] — safe but semantically meaningless |
Attack Vector
A malicious or compromised e2k server sends a malformed OP_SERVERMESSAGE packet with packetlength = 2 (which yields size = 1 in the handler). The client, upon connecting to the server and receiving this packet:
- Allocates a zero-length buffer (
new char[0])
- Attempts
memcpy of 0xFFFFFFFF bytes into it → heap buffer overflow
- Result: application crash (denial of service) at minimum; potential arbitrary code execution if heap layout is controllable
No authentication beyond initial connection is required — the server message is processed immediately after the login handshake, which the server itself controls.
Severity
High
Risk Assessment
| Factor |
Detail |
| Attack complexity |
Low — trivially exploitable with a single malformed packet |
| Privileges required |
None — any server the client connects to can exploit this |
| User interaction |
Minimal — user must connect to the malicious server (e2k links or server lists can direct clients) |
| Impact: Availability |
High — guaranteed crash via heap corruption |
| Impact: Integrity |
Low to Medium — heap corruption may allow code execution depending on heap state |
| Impact: Confidentiality |
Low — potential memory disclosure via heap corruption |
| Scope |
Network — any e2k server the client connects to |
Why This Is High Severity
- Remote trigger: The attacker only needs to operate a server — no client-side vulnerability needed beyond connecting.
- Trivial to exploit: The PoC requires just 7 bytes on the wire. No heap feng shui is needed for a DoS crash.
- No encryption required: The e2k server protocol is cleartext TCP.
- Client trust model: eD2k clients inherently trust servers for protocol messages, making this a design-level trust boundary issue.
crash_poc.py
Proof of Concept
A minimal PoC (provided as crash_poc.py) operates a TCP server that:
- Accepts an incoming aMule client connection
- Sends a valid login response (
OP_IDCHANGE)
- Sends a malformed
OP_SERVERMESSAGE with packetlength = 2 (yielding size = 1)
Result: aMule client crashes due to the unsigned underflow in the memcpy call.
Wire format of crash packet:
E3 00 00 00 02 38 00
│ ───────── │ │
│ packetlen │ payload (1 byte)
│ (LE u32) opcode
protocol
Fix
Added a minimum size check before the unsigned arithmetic operations, consistent with existing validation patterns elsewhere in the same function (lines 320, 468, 486):
case OP_SERVERMESSAGE: {
AddDebugLogLineN( logServer, wxT("Server: OP_SERVERMESSAGE") );
if (size < 3) {
throw wxString(wxT("Corrupt or invalid server message received (size too small)"));
}
theStats::AddDownOverheadServer(size);
char* buffer = new char[size-1];
memcpy(buffer, &packet[2], size-2);
buffer[size-2] = 0;
The throw wxString is caught by the existing try/catch block in ProcessPacket, which logs the error and gracefully closes the connection instead of crashing.
Recommendations
- Apply this fix to all active aMule branches.
- Audit all
size - N arithmetic on unsigned types in packet handlers for similar underflow risks — the OP_SERVERMESSAGE case was the only instance found in ServerSocket.cpp, but other packet handlers in EMSocket.cpp, ClientUDPSocket.cpp, etc. may have analogous issues.
- Consider fuzzing the
ProcessPacket function with malformed sizes and opcodes to uncover additional edge cases.
- Consider hardening the connection pipeline to reject packets with implausibly small sizes at the protocol layer before they reach opcode-specific handlers.
Bug Report: Heap Buffer Overflow in OP_SERVERMESSAGE Handler
Summary
An unsigned integer underflow in
CServerSocket::ProcessPacketallows a remote e2k server to trigger a heap buffer overflow, leading to denial of service (crash) and potential remote code execution.Affected Component
src/ServerSocket.cppCServerSocket::ProcessPacket(const uint8_t* packet, uint32 size, int8 opcode)OP_SERVERMESSAGE(opcode0x38)Vulnerability Details
The
ProcessPacketfunction processes incoming packets from e2k servers. Thesizeparameter is of typeuint32(unsigned 32-bit integer). When theOP_SERVERMESSAGEopcode is received, the handler performs the following operations without validatingsize:The protocol format for server messages is:
[2-byte header][message bytes][null terminator]. This means a valid message requiressize >= 3. However, no bounds check exists before the arithmetic on the unsignedsizevalue.Underflow Scenarios
sizesize - 1size - 20xFFFFFFFF(4,294,967,295)0xFFFFFFFEnew char[]attempts ~4GB allocation → OOM crash or massive buffer;memcpycopies ~4GB → heap overflow / segfault0x00000000(0)0xFFFFFFFFmemcpycopies ~4GB into 0-byte buffer → heap buffer overflow / crash0x00000001(1)0x00000000(0)memcpycopies 0 bytes; null terminator written tobuffer[0]— safe but semantically meaninglessAttack Vector
A malicious or compromised e2k server sends a malformed
OP_SERVERMESSAGEpacket withpacketlength = 2(which yieldssize = 1in the handler). The client, upon connecting to the server and receiving this packet:new char[0])memcpyof0xFFFFFFFFbytes into it → heap buffer overflowNo authentication beyond initial connection is required — the server message is processed immediately after the login handshake, which the server itself controls.
Severity
High
Risk Assessment
Why This Is High Severity
crash_poc.py
Proof of Concept
A minimal PoC (provided as
crash_poc.py) operates a TCP server that:OP_IDCHANGE)OP_SERVERMESSAGEwithpacketlength = 2(yieldingsize = 1)Result: aMule client crashes due to the unsigned underflow in the
memcpycall.Wire format of crash packet:
Fix
Added a minimum size check before the unsigned arithmetic operations, consistent with existing validation patterns elsewhere in the same function (lines 320, 468, 486):
The
throw wxStringis caught by the existingtry/catchblock inProcessPacket, which logs the error and gracefully closes the connection instead of crashing.Recommendations
size - Narithmetic on unsigned types in packet handlers for similar underflow risks — theOP_SERVERMESSAGEcase was the only instance found inServerSocket.cpp, but other packet handlers inEMSocket.cpp,ClientUDPSocket.cpp, etc. may have analogous issues.ProcessPacketfunction with malformed sizes and opcodes to uncover additional edge cases.