Skip to content

Heap buffer overflow in CDatagramSocketProxy::SendTo — unbounded memcpy into fixed 5120-byte SOCKS5 proxy buffer #881

Description

@ngosang
  • Criticality: Medium
  • Component: SOCKS5 UDP proxy relay (CDatagramSocketProxy / CProxyStateMachine)
  • File: src/Proxy.cpp:1374 (write), buffer declared in src/Proxy.h:248
  • CWE: CWE-787 (Out-of-bounds Write) / CWE-120 (Buffer Copy without Checking Size of Input)
  • Reachability: Local outbound path; requires a SOCKS5 UDP proxy to be configured and active, plus an outbound UDP datagram larger than 5110 bytes.
  • Impact: Heap out-of-bounds write (memory corruption) of the bytes past the end of the fixed m_buffer[5120].

Summary

When aMule sends a UDP datagram through a SOCKS5 proxy, CDatagramSocketProxy::SendTo
prepends a 10-byte SOCKS5 UDP request header into a fixed 5120-byte buffer
(m_buffer[PROXY_BUFFER_SIZE]) and then memcpys the entire payload (nBytes) right
after that header — without any bounds check. If the payload is larger than
PROXY_BUFFER_SIZE - PROXY_UDP_OVERHEAD_IPV4 (5120 − 10 = 5110) bytes, the copy writes
past the end of the buffer, corrupting adjacent heap/struct memory.

The sibling receive path, CDatagramSocketProxy::RecvFrom, performs exactly the size
check that is missing here (it allocates a dynamic buffer when the datagram would not
fit), which confirms the omission in SendTo is an oversight rather than an intended
invariant.

Affected code

src/Proxy.cppCDatagramSocketProxy::SendTo (no size guard before the copy):

uint32 CDatagramSocketProxy::SendTo(const amuleIPV4Address& addr, const void* buf, uint32 nBytes)
{
    ...
    if (m_proxyTCPSocket.GetUseProxy()) {
        if (m_udpSocketOk) {
            m_proxyTCPSocket.GetBuffer()[0] = SOCKS5_RSV;                       // :1368
            m_proxyTCPSocket.GetBuffer()[1] = SOCKS5_RSV;
            m_proxyTCPSocket.GetBuffer()[2] = 0;                                // FRAG
            m_proxyTCPSocket.GetBuffer()[3] = SOCKS5_ATYP_IPV4_ADDRESS;
            PokeUInt32( m_proxyTCPSocket.GetBuffer()+4, StringIPtoUint32(addr.IPAddress()));
            RawPokeUInt16( m_proxyTCPSocket.GetBuffer()+8, ENDIAN_HTONS( addr.Service() ) );
            memcpy(m_proxyTCPSocket.GetBuffer() + PROXY_UDP_OVERHEAD_IPV4, buf, nBytes);  // :1374  UNBOUNDED
            nBytes += PROXY_UDP_OVERHEAD_IPV4;
            sent = CLibUDPSocket::SendTo(...);
        }
    }
    ...
}

The destination is a fixed-size member buffer:

src/Proxy.h:

const unsigned int PROXY_BUFFER_SIZE = 5*1024;          // :178   == 5120
const unsigned int PROXY_UDP_OVERHEAD_IPV4 = 10;        // :496
...
char *GetBuffer() { return m_buffer; }                  // :230
...
char  m_buffer[PROXY_BUFFER_SIZE];                      // :248

So the writable space after the 10-byte header is 5120 - 10 = 5110 bytes, and
memcpy(... + 10, buf, nBytes) overflows whenever nBytes > 5110.

Why this is a real defect (the receive path proves it)

CDatagramSocketProxy::RecvFrom — the mirror of SendTo — contains the exact guard that
SendTo lacks:

// src/Proxy.cpp  RecvFrom
if (nBytes + PROXY_UDP_MAXIMUM_OVERHEAD > PROXY_BUFFER_SIZE) {   // :1300
    bufUDP = new char[nBytes + PROXY_UDP_MAXIMUM_OVERHEAD];      // dynamic buffer when too big
} else {
    bufUDP = m_proxyTCPSocket.GetBuffer();
}

SendTo writes into GetBuffer() directly with no equivalent check, so the asymmetry is
the bug.

Attack vector / trigger

  1. The user has configured a SOCKS5 proxy with UDP support and the UDP ASSOCIATE
    negotiation has succeeded (m_udpSocketOk == true).
  2. aMule sends an outbound UDP datagram whose payload exceeds 5110 bytes through this
    proxy (CDatagramSocketProxy::SendTo, reached via MuleUDPSocket-style send paths).
  3. Line 1374 copies the oversized payload past the end of m_buffer[5120], corrupting
    whatever follows the buffer in the enclosing object/heap.

nBytes is the size of an outbound datagram that aMule itself assembles, so it is not
directly attacker-controlled over the wire; this is why the criticality is Medium rather
than High. The harm requires an outbound UDP packet larger than 5110 bytes while a SOCKS5
UDP proxy is active. Standard eD2K/Kad UDP datagrams are normally well under this size, but
the interface places no upper bound on the payload, so any present or future caller passing
a larger buffer corrupts the heap.

Impact

  • Out-of-bounds heap write of nBytes - 5110 bytes immediately after m_buffer. Because
    m_buffer is an inline member of CProxyStateMachine, the overflow corrupts adjacent
    members and/or heap metadata, leading to crashes or potentially exploitable corruption.
  • No read-side OOB; the defect is purely the unbounded write.

Suggested fix

Mirror the bound that RecvFrom already uses — reject or truncate payloads that do not fit
after the SOCKS5 UDP header:

if (nBytes > PROXY_BUFFER_SIZE - PROXY_UDP_OVERHEAD_IPV4) {
    // Payload does not fit in the fixed SOCKS5 UDP relay buffer; drop it.
    return 0;
}
memcpy(m_proxyTCPSocket.GetBuffer() + PROXY_UDP_OVERHEAD_IPV4, buf, nBytes);

(Alternatively, allocate a dynamic buffer for oversized datagrams exactly as RecvFrom
does at Proxy.cpp:1300.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions