Reachable Assertion
Description
Reachable Assertion occurs when a product contains an assert() or similar statement that can be triggered by an attacker, leading to an application exit or other behavior more severe than necessary. Assertions are debugging tools meant to catch logic errors during development—they should never be triggered by external input in production. When assertions are reachable through attacker-controlled input, they can be exploited to cause denial of service by crashing the application. In server applications handling multiple connections, an assertion failure can terminate the entire process, affecting all users.
Risk
Reachable assertions provide attackers with a reliable denial of service mechanism. CVE-2025-49630 in Apache HTTP Server's mod_proxy_http2 allows remote attackers to crash the server with specially crafted requests when proxying to HTTP/2 backends. CVE-2025-41068 in Open5GS 5G core network allows attackers to crash the Network Repository Function (NRF), disrupting mobile network service discovery. CVE-2025-59029 in PowerDNS Recursor enables remote DoS through crafted DNS queries. These vulnerabilities are particularly severe in infrastructure software where crashes affect many users or disrupt critical services.
Solution
Remove or disable assertions in production builds. Use compile-time flags to exclude assertions (NDEBUG in C/C++). Replace assertions with proper error handling that logs the issue and returns gracefully. Never use assertions to validate external input—use explicit input validation with appropriate error responses. If assertions must remain, ensure they cannot be triggered by user-controlled input. Implement process supervision to restart crashed services automatically.
Common Consequences
| Impact | Details |
|---|---|
| Availability | Scope: Denial of Service Assertion failures terminate the application, causing service disruption for all users. |
| Reliability | Scope: Service Interruption Repeated assertion triggers can keep services unavailable, affecting system reliability. |
| Security | Scope: Information Disclosure Assertion messages may reveal internal state, file paths, or debug information. |
Example Code + Solution Code
Vulnerable Code
// VULNERABLE: Assert on user input
#include <assert.h>
#include <string.h>
void process_request(const char* input, size_t length) {
// Attacker can send length > MAX_INPUT to crash server!
assert(length <= MAX_INPUT); // Crashes entire process
// Process the input
handle_data(input, length);
}
// VULNERABLE: Assert in network protocol handler
void handle_packet(struct Packet* pkt) {
// Malformed packet can crash server
assert(pkt->version == PROTOCOL_VERSION);
assert(pkt->checksum == calculate_checksum(pkt));
process_packet(pkt);
}
// VULNERABLE: Assert in multi-threaded server
class ConnectionHandler {
public:
void handleRequest(Request& req) {
// User-controlled type field can crash entire server
assert(req.getType() >= 0 && req.getType() < REQUEST_TYPE_COUNT);
handlers[req.getType()]->process(req);
}
};
// VULNERABLE: Assert in XML parser
void parseXMLElement(XMLNode* node) {
// Malformed XML can trigger assertion
assert(node != nullptr);
assert(node->name != nullptr);
assert(strlen(node->name) > 0);
// Process element
}
# VULNERABLE: Assert in web application
def process_upload(file_data, file_size):
# Attacker controls file_size parameter
assert file_size > 0, "File size must be positive"
assert file_size < MAX_FILE_SIZE, f"File too large: {file_size}"
# Python assertions can be disabled with -O flag,
# but server may not use that flag
# VULNERABLE: Assert in API handler
def get_user(user_id):
assert isinstance(user_id, int), "User ID must be integer"
assert user_id > 0, "User ID must be positive"
return database.find_user(user_id)
Fixed Code
// SAFE: Proper error handling instead of assert
#include <errno.h>
#include <syslog.h>
int process_request_safe(const char* input, size_t length) {
// Validate input with proper error handling
if (length > MAX_INPUT) {
syslog(LOG_WARNING, "Request too large: %zu bytes", length);
return -EINVAL; // Return error, don't crash
}
if (input == NULL) {
syslog(LOG_WARNING, "NULL input received");
return -EINVAL;
}
return handle_data(input, length);
}
// SAFE: Graceful handling of invalid packets
int handle_packet_safe(struct Packet* pkt) {
if (pkt == NULL) {
log_error("Received NULL packet");
return -EINVAL;
}
if (pkt->version != PROTOCOL_VERSION) {
log_warning("Unsupported protocol version: %d", pkt->version);
return -EPROTONOSUPPORT;
}
if (pkt->checksum != calculate_checksum(pkt)) {
log_warning("Checksum mismatch");
return -EBADMSG;
}
return process_packet(pkt);
}
// Keep assertions for internal logic only (development)
#ifndef NDEBUG
// This should NEVER be reachable via external input
assert(internal_state_is_consistent());
#endif
// SAFE: Exception-based error handling
#include <stdexcept>
#include <optional>
class SecureConnectionHandler {
public:
std::optional<Response> handleRequest(Request& req) {
// Validate with proper error handling
int type = req.getType();
if (type < 0 || type >= REQUEST_TYPE_COUNT) {
logger.warn("Invalid request type: {}", type);
return std::nullopt; // Return error, don't crash
}
try {
return handlers[type]->process(req);
} catch (const std::exception& e) {
logger.error("Request processing failed: {}", e.what());
return std::nullopt;
}
}
};
// SAFE: Null-safe XML parsing
std::optional<XMLElement> parseXMLElementSafe(XMLNode* node) {
if (node == nullptr) {
logger.warn("NULL XML node");
return std::nullopt;
}
if (node->name == nullptr || strlen(node->name) == 0) {
logger.warn("XML node with empty name");
return std::nullopt;
}
return XMLElement(*node);
}
# SAFE: Exception-based validation
class ValidationError(Exception):
pass
def process_upload_safe(file_data, file_size):
"""Process upload with proper validation."""
if not isinstance(file_size, int) or file_size <= 0:
raise ValidationError("Invalid file size")
if file_size > MAX_FILE_SIZE:
raise ValidationError(f"File too large: {file_size} bytes (max: {MAX_FILE_SIZE})")
# Process the upload
return save_file(file_data, file_size)
# SAFE: Type checking with proper error responses
def get_user_safe(user_id):
"""Get user with input validation."""
# Validate input types
try:
user_id = int(user_id)
except (TypeError, ValueError):
raise ValidationError("User ID must be a valid integer")
if user_id <= 0:
raise ValidationError("User ID must be positive")
user = database.find_user(user_id)
if user is None:
raise NotFoundError(f"User {user_id} not found")
return user
# Flask route with proper error handling
@app.route('/user/<user_id>')
def get_user_endpoint(user_id):
try:
user = get_user_safe(user_id)
return jsonify(user.to_dict())
except ValidationError as e:
return jsonify({'error': str(e)}), 400
except NotFoundError as e:
return jsonify({'error': str(e)}), 404
Exploited in the Wild
Apache HTTP Server mod_proxy_http2 (Apache, 2025)
CVE-2025-49630 in Apache HTTP Server 2.4.26-2.4.63 allows remote attackers to crash the server by triggering an assertion failure in mod_proxy_http2 when ProxyPreserveHost is enabled and proxying to HTTP/2 backends.
Open5GS 5G Core Network (Open5GS, 2025)
CVE-2025-41068 in Open5GS up to 2.7.5 allows attackers to crash the Network Repository Function (NRF) by creating an NF with an invalid type and then querying it, triggering an assertion that crashes the service discovery function.
PowerDNS Recursor (PowerDNS, 2025)
CVE-2025-59029 in PowerDNS Recursor 5.3.0 allows remote unauthenticated attackers to cause denial of service by sending DNS queries with qtype=ANY after caching crafted records, triggering an assertion failure.
Tools to test/exploit
-
AFL++ — fuzzing to discover assertion triggers.
-
Burp Suite — craft malformed requests to trigger assertions.
-
American Fuzzy Lop — coverage-guided fuzzing.
CVE Examples
-
CVE-2025-49630 — Apache HTTP Server assertion DoS.
-
CVE-2025-41068 — Open5GS NRF assertion crash.
-
CVE-2025-59029 — PowerDNS Recursor assertion DoS.
References
-
MITRE. "CWE-617: Reachable Assertion." https://cwe.mitre.org/data/definitions/617.html
-
CERT. "ERR06-C. Understand the termination behavior of assert() and abort()." https://wiki.sei.cmu.edu/confluence/display/c/ERR06-C.+Understand+the+termination+behavior+of+assert%28%29+and+abort%28%29