Return of Wrong Status Code

Description

Return of Wrong Status Code is a vulnerability that occurs when a function or operation returns an incorrect return value or status code that does not reflect the true execution result. This causes calling code to modify its behavior based on faulty information. When used in security-critical decisions, incorrect status codes may cause the product to incorrectly assume an action is safe or correct when it is not, leading to security bypasses and other vulnerabilities.

Risk

Returning incorrect status codes can lead to severe security consequences. Security checks that return success when they should return failure enable unauthorized access. API endpoints that return HTTP 404 (Not Found) for authentication failures leak information about valid resources. Certificate validation that incorrectly returns success allows man-in-the-middle attacks. Transaction systems that report success for failed operations can cause data inconsistency and financial losses. The famous Apple "goto fail" bug (CVE-2014-1266) demonstrated how incorrect control flow can bypass SSL certificate validation entirely.

Solution

Ensure return values and status codes accurately reflect the outcome of operations. Use clear, unambiguous return value conventions and document them thoroughly. For security functions, fail closed by defaulting to returning error states. Test error paths as thoroughly as success paths. Use code review and static analysis to verify return values match intended semantics. Consider using enumerated types or result objects instead of raw integers to make return values self-documenting. Use fuzzing to discover code paths that return incorrect status codes.

Common Consequences

ImpactDetails
IntegrityScope: Integrity

Unexpected State, Altered Execution Logic - The weakness could place the system in a state leading to unexpected logic execution or unintended behaviors.

Example Code

Vulnerable Code

// Vulnerable: Returns wrong HTTP status code
@WebServlet("/api/user")
public class VulnerableApiServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
                         throws ServletException, IOException {
        String userId = request.getParameter("id");

        try {
            User user = userService.findById(userId);
            if (user == null) {
                // Vulnerable: 404 reveals user doesn't exist
                response.setStatus(404);  // Information leak!
                return;
            }
            sendUserResponse(response, user);
        } catch (IOException e) {
            // Vulnerable: Wrong status for I/O error
            response.setStatus(404);  // Should be 500!
        }
    }
}

// Vulnerable: Security check returns wrong value
public class VulnerableAuthChecker {

    public boolean isAuthorized(User user, Resource resource) {
        try {
            return checkPermissions(user, resource);
        } catch (Exception e) {
            // Vulnerable: Returns true (authorized) on error!
            logger.error("Permission check failed", e);
            return true;  // Should return false!
        }
    }
}
// Vulnerable: Wrong return code on validation failure
int vulnerable_validate_certificate(X509 *cert) {
    int result;

    result = check_certificate_chain(cert);
    if (result != 0) {
        goto fail;  // First goto works
    }

    result = check_signature(cert);
    if (result != 0) {
        goto fail;  // Second goto works
    }

    // Vulnerable: Duplicate goto (Apple "goto fail" style bug)
    goto fail;  // This unconditionally jumps to fail!

    result = check_expiration(cert);
    if (result != 0) {
        goto fail;  // Never reached!
    }

    return 0;  // Success - never reached!

fail:
    return 0;  // Vulnerable: Returns success (0) even on failure!
}

// Vulnerable: Returns wrong value for non-existent record
int vulnerable_lookup_record(const char *name) {
    Record *rec = find_record(name);

    if (rec == NULL) {
        // Vulnerable: Wrong response code
        return RECORD_FOUND;  // Should be RECORD_NOT_FOUND!
    }

    return process_record(rec);
}
# Vulnerable: Wrong return value on authentication failure
def vulnerable_authenticate(username, password):
    user = database.find_user(username)

    if user is None:
        # Vulnerable: Returns 1 (often means success)
        return 1  # Should indicate failure!

    if not verify_password(password, user.password_hash):
        # Vulnerable: Also returns 1
        return 1  # Should indicate failure!

    return 0  # Success

# Vulnerable: Inconsistent error codes
def vulnerable_file_operation(path):
    if not os.path.exists(path):
        return -1  # File not found

    try:
        with open(path, 'r') as f:
            return f.read()
    except PermissionError:
        return -1  # Vulnerable: Same code, different error!
    except IOError:
        return None  # Vulnerable: Inconsistent return type!

Fixed Code

// Fixed: Return appropriate status codes
@WebServlet("/api/user")
public class SecureApiServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
                         throws ServletException, IOException {
        String userId = request.getParameter("id");

        try {
            User user = userService.findById(userId);
            if (user == null) {
                // Fixed: Generic error, don't reveal user existence
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                return;
            }
            sendUserResponse(response, user);
        } catch (IOException e) {
            // Fixed: Correct status for server error
            logger.error("Error fetching user", e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
}

// Fixed: Fail closed on error
public class SecureAuthChecker {

    public boolean isAuthorized(User user, Resource resource) {
        try {
            return checkPermissions(user, resource);
        } catch (Exception e) {
            // Fixed: Return false (denied) on error
            logger.error("Permission check failed - denying access", e);
            return false;  // Fail closed!
        }
    }
}
// Fixed: Proper certificate validation
typedef enum {
    CERT_OK = 0,
    CERT_CHAIN_INVALID = -1,
    CERT_SIGNATURE_INVALID = -2,
    CERT_EXPIRED = -3
} CertStatus;

CertStatus secure_validate_certificate(X509 *cert) {
    CertStatus status;

    status = check_certificate_chain(cert);
    if (status != CERT_OK) {
        return status;  // Return specific error
    }

    status = check_signature(cert);
    if (status != CERT_OK) {
        return status;  // Return specific error
    }

    // Fixed: No duplicate jump, code flows properly
    status = check_expiration(cert);
    if (status != CERT_OK) {
        return status;  // Return specific error
    }

    return CERT_OK;  // Only return success if all checks pass
}

// Fixed: Proper error codes
typedef enum {
    RECORD_FOUND = 0,
    RECORD_NOT_FOUND = -1,
    RECORD_ERROR = -2
} RecordStatus;

RecordStatus secure_lookup_record(const char *name, Record **out_rec) {
    Record *rec = find_record(name);

    if (rec == NULL) {
        // Fixed: Return correct status
        *out_rec = NULL;
        return RECORD_NOT_FOUND;
    }

    *out_rec = rec;
    return RECORD_FOUND;
}
# Fixed: Use result objects for clear status reporting
from enum import Enum
from dataclasses import dataclass
from typing import Optional

class AuthStatus(Enum):
    SUCCESS = "success"
    USER_NOT_FOUND = "user_not_found"
    INVALID_PASSWORD = "invalid_password"
    ACCOUNT_LOCKED = "account_locked"

@dataclass
class AuthResult:
    status: AuthStatus
    user: Optional['User'] = None

def secure_authenticate(username, password) -> AuthResult:
    user = database.find_user(username)

    if user is None:
        # Fixed: Clear status for user not found
        return AuthResult(AuthStatus.USER_NOT_FOUND)

    if user.is_locked:
        return AuthResult(AuthStatus.ACCOUNT_LOCKED)

    if not verify_password(password, user.password_hash):
        # Fixed: Clear status for invalid password
        return AuthResult(AuthStatus.INVALID_PASSWORD)

    return AuthResult(AuthStatus.SUCCESS, user=user)

# Fixed: Consistent error handling with specific exceptions
class FileNotFoundError(Exception): pass
class PermissionDeniedError(Exception): pass
class FileReadError(Exception): pass

def secure_file_operation(path):
    if not os.path.exists(path):
        raise FileNotFoundError(f"File not found: {path}")

    try:
        with open(path, 'r') as f:
            return f.read()
    except PermissionError as e:
        raise PermissionDeniedError(f"Cannot read {path}") from e
    except IOError as e:
        raise FileReadError(f"Error reading {path}") from e

CVE Examples

  • CVE-2003-1132 — DNS server returns incorrect response code for non-existent AAAA records.
  • CVE-2001-1509 — Hardware-specific system call returns incorrect geteuid results.
  • CVE-2014-1266 — Apple SSL "goto fail" bug - incorrect control flow bypasses certificate validation.

References

  1. MITRE Corporation. "CWE-393: Return of Wrong Status Code." https://cwe.mitre.org/data/definitions/393.html