Monero · Report #3185083
Connection Count Bug in Monero Node Enables Outbound Peer Reset Attack
Summary
An attacker can exploit this vulnerability to control all outgoing connection of the victim nodes and do further attacking. For example, (1) For the node with the upper limit of incoming connections, the attacker can do eclipse attacks successfully. (2) For the node without the upper limit of incoming connections, the attaker can infer the originating transaction of the victim nodes when the nodes are in fluff status.
How it was found
Summary:
We disclosed a vulnerability that could cause all outbound connections of a Monero node to be dropped. The vulnerability exploits a flaw in how the Monero node incorrectly counts the number of current outbound connections. An attacker can take advantage of this flaw to trick the node into mistakenly believing it has exceeded the outbound connection maximum limit, prompting it to actively disconnect legitimate outbound connections.
Releases Affected:
- Versions v0.18.4.0 and earlier.
Attack Mechanism:
A Monero node may disconnect outbound connections for reasons such as executing update_sync_search every 101 seconds, or when an outbound connection times out waiting for response to Timed Sync request. After disconnecting an outgoing connection, the Monero node checks whether the current number of outgoing connections (calculated in real-time by the get_outgoing_connections_count function) is below the outgoing connection maximum limit (default: 12). If so, it will attempt to establish a new outgoing connection. When attempting to establish a new outgoing connection, the Monero node will first disconnect existing connections if the outgoing connection maximum limit is exceeded, randomly dropping one from all active outgoing connections.
However, when the Monero node calculates the current number of outgoing connections (computed once per second in the mPeersLoggerThread thread), it counts both fully-handshaked outgoing connections and temporary outgoing connections created solely for sending Ping requests.
This counting mechanism enables a potential attack. An attacker can flood the target node with numerous Handshake requests, each originating from a different IP address. Each incoming Handshake request forces the target node to initiate a new outgoing connection to the attacker's IP in order to send Ping request. This causes the target node's calculated outgoing connection count to significantly exceed the outbound connection maximum limit, triggering random disconnection of existing outbound connections (including legitimate fully-handshaked ones). We classify this attack as a Handshake Flood attack.
Therefore, the following situation may occur:
(1) The target node disconnects from one outbound connection due to update_sync_search or a timeout waiting for a Timed Sync response.
(2) While the target node updates the number of outbound connections every second in the mPeersLoggerThread thread, the attacker is performing Handshake Flood attack. As a result, the updated m_current_number_of_out_peers greatly exceeds the outbound connection maximum limit.
(3) When the attacker is not executing a Handshake Flood attack, the target node runs its periodic connections_maker() function (triggered every second). Since the node's real-time calculated outgoing connections remain below the outbound connection maximum limit, it will proceed to establish new outgoing connections.
(4) When the target node attempts to establish new outgoing connections, its try_to_connect_and_handshake_with_new_peer() function evaluates the current outgoing connection count (m_current_number_of_out_peers) as exceeding the maximum limit. This triggers disconnection of legitimate fully-handshaked outgoing connections. Since the connections_maker() function invokes try_to_connect_and_handshake_with_new_peer() multiple times, this results in multiple legitimate outbound connections being disconnected.
From the above description, it's evident that the attacker cannot sustain the Handshake Flood attack continuously. Otherwise, in step (3), the target node would never detect its real-time outgoing connections as being below the maximum limit, thus preventing subsequent attack phases.
Steps To Reproduce:
An attack was carried out on a controllable target node running version v0.18.4.0. The target machine was equipped with a 4-core CPU and 13 GB of RAM.
A Handshake Flood attack was launched using 1,000 IP addresses. Each of these IPs sent Handshake requests to the controllable node, while also monitoring incoming connections and Ping requests from the node, accepting incoming connections and responding to Ping requests accordingly.
After completing the Handshake Flood attack, the program sleeps for 5 seconds to give the controllable node an opportunity, as described in step (3) of the attack mechanism to assess that the real-time current number of outbound connections is below the limit. This attack process is then repeated.
Supporting Material/References:
We modified the target node’s source code to log the number of outbound connections obtained in the get_outgoing_connections_count() function, the number of outbound connections obtained in the mPeersLoggerThread thread, and to record when connections are dropped in try_to_connect_and_handshake_with_new_peer() due to exceeding the limit.
The log of the target node is provided in the attached bitmonero.log file. After approximately three minutes of attack (from 12:05 to 12:08 on June 9, 2025), the target node disconnected all outbound connections.
A portion of the typical logs during the attack is shown below:
2025-06-09 12:05:54.472 I in mPeersLoggerThread, Update m_current_number_of_out_peers: 12
// -! Step (1) in the attack mechanism: the target node disconnects from one outbound connection
2025-06-09 12:05:54.972 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:05:54.972 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:05:54.973 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:05:54.973 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:05:54.996 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:05:54.996 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:05:54.997 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:05:55.472 I in mPeersLoggerThread, Update m_current_number_of_out_peers: 11
2025-06-09 12:05:55.998 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:05:56.473 I in mPeersLoggerThread, Update m_current_number_of_out_peers: 11
2025-06-09 12:05:57.474 I in mPeersLoggerThread, Update m_current_number_of_out_peers: 11
2025-06-09 12:05:58.398 I in get_outgoing_connections_count(), out conn count is : 14
2025-06-09 12:05:58.400 I in get_outgoing_connections_count(), out conn count is : 14
2025-06-09 12:05:58.401 I in get_outgoing_connections_count(), out conn count is : 14
2025-06-09 12:05:58.480 I in mPeersLoggerThread, Update m_current_number_of_out_peers: 26
2025-06-09 12:05:59.414 I in get_outgoing_connections_count(), out conn count is : 211
2025-06-09 12:05:59.423 I in get_outgoing_connections_count(), out conn count is : 213
2025-06-09 12:05:59.431 I in get_outgoing_connections_count(), out conn count is : 213
2025-06-09 12:05:59.585 I in mPeersLoggerThread, Update m_current_number_of_out_peers: 214
2025-06-09 12:06:00.433 I in get_outgoing_connections_count(), out conn count is : 278
2025-06-09 12:06:00.435 I in get_outgoing_connections_count(), out conn count is : 276
2025-06-09 12:06:00.463 I in get_outgoing_connections_count(), out conn count is : 278
// -! Step (2) in the attack mechanism: While the target node updates the number of outbound connections every second in the `mPeersLoggerThread` thread, the attacker is performing Handshake Flood attack. As a result, the updated `m_current_number_of_out_peers` greatly exceeds the outbound connection maximum limit.
2025-06-09 12:06:00.585 I in mPeersLoggerThread, Update m_current_number_of_out_peers: 362
2025-06-09 12:06:01.464 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:06:01.464 I in get_outgoing_connections_count(), out conn count is : 11
2025-06-09 12:06:01.464 I in get_outgoing_connections_count(), out conn count is : 11
// -! Step (3) in the attack mechanism: When the attacker is not executing a Handshake Flood attack, the target node runs its periodic `connections_maker()` function (triggered every second). Since the node's real-time calculated outgoing connections remain below the outbound connection maximum limit, it will proceed to establish new outgoing connections.
2025-06-09 12:06:01.464 I in get_outgoing_connections_count(), out conn count is : 11
// -! Step (4) in the attack mechanism: When the target node attempts to establish new outgoing connections, its `try_to_connect_and_handshake_with_new_peer()` function evaluates the current outgoing connection count (`m_current_number_of_out_peers`) as exceeding the maximum limit. This triggers disconnection of legitimate fully-handshaked outgoing connections.
2025-06-09 12:06:01.471 I in try_to_connect_and_handshake_with_new_peer, current out count 362 is more than 12, del_out_connections
2025-06-09 12:06:01.477 I in try_to_connect_and_handshake_with_new_peer, current out count 361 is more than 12, del_out_connections
2025-06-09 12:06:01.484 I in try_to_connect_and_handshake_with_new_peer, current out count 360 is more than 12, del_out_connections
2025-06-09 12:06:01.485 I in get_outgoing_connections_count(), out conn count is : 8
2025-06-09 12:06:01.485 I in get_outgoing_connections_count(), out conn count is : 8
2025-06-09 12:06:01.485 I in try_to_connect_and_handshake_with_new_peer, current out count 359 is more than 12, del_out_connections
2025-06-09 12:06:01.486 I in try_to_connect_and_handshake_with_new_peer, current out count 358 is more than 12, del_out_connections
2025-06-09 12:06:01.486 I in try_to_connect_and_handshake_with_new_peer, current out count 357 is more than 12, del_out_connections
2025-06-09 12:06:01.487 I in get_outgoing_connections_count(), out conn count is : 5
2025-06-09 12:06:01.586 I in mPeersLoggerThread, Update m_current_number_of_out_peers: 5
2025-06-09 12:06:02.487 I in get_outgoing_connections_count(), out conn count is : 5
2025-06-09 12:06:02.587 I in mPeersLoggerThread, Update m_current_number_of_out_peers: 5
2025-06-09 12:06:03.488 I in get_outgoing_connections_count(), out conn count is : 5
2025-06-09 12:06:03.488 I in get_outgoing_connections_count(), out conn count is : 5
2025-06-09 12:06:03.488 I in get_outgoing_connections_count(), out conn count is : 5
The specific source code modifications are as follows:
-
log the number of outbound connections obtained in the
get_outgoing_connections_count()functionmPeersLoggerThread.reset(new boost::thread([&]() { //...... zone.second.m_current_number_of_in_peers = number_of_in_peers; zone.second.m_current_number_of_out_peers = number_of_out_peers; MGINFO("in mPeersLoggerThread, Update m_current_number_of_out_peers: " << zone.second.m_current_number_of_out_peers); //add this code //...... })); // lambda -
log the number of outbound connections obtained in the
mPeersLoggerThreadthreadelse if (zone.m_current_number_of_out_peers > zone.m_config.m_net_config.max_out_connection_count) { MGINFO("in try_to_connect_and_handshake_with_new_peer, current out count " << zone.m_current_number_of_out_peers << " is more than 12, del_out_connections"); //add this code zone.m_net_server.get_config_object().del_out_connections(1); --(zone.m_current_number_of_out_peers); // atomic variable, update time = 1s return false; } -
record when connections are dropped in
try_to_connect_and_handshake_with_new_peer()due to exceeding the limitsize_t node_server<t_payload_net_handler>::get_outgoing_connections_count(network_zone& zone) { //...... MGINFO("in get_outgoing_connections_count(), out conn count is : "<< count); //add this code return count; }
Housekeeping
XMR Address: 44srHrZEhspX1k1f7jvFBB6XBnEUvEJNnD455cRBSPvxKupT8YDFuibgAow7AMmFzGfiyiXhBbm7nSWJdMiWhZsNCA6oP3w
Impact
An attacker with a large number of IPs ( for example, 1,000 IPs) can disconnect many outgoing connections of a node. Combined with the peerlist occupation method in the ndss2025 paper "Eclipse attack on menero's peer to peer network", attackers can occupy the outgoing connections of a node.
Mitigating factors
When calculating the current number of outgoing connections, only the fully-handshaked outgoing connections are counted.
Impact
An attacker with a large number of IPs ( for example, 1,000 IPs) can disconnect many outgoing connections of a node. Combined with the peerlist occupation method in the ndss2025 paper "Eclipse attack on menero's peer to peer network", attackers can occupy the outgoing connections of a node.