Double Free with Basic Auth
How Double Free Manifests in Basic Auth
Basic Authentication involves the server parsing the Authorization: Basic <base64> header, decoding it, and splitting the result into a username and password. In many C/C++ implementations this decoding step allocates memory for each credential (e.g., with strdup or malloc) before they are used or stored. If the error‑handling path frees these buffers more than once, a classic double‑free (CWE‑415) can be triggered.
Consider a simplified login handler that extracts credentials:
char *parse_basic_auth(const char *header) {
const char *prefix = "Basic ";
if (strncmp(header, prefix, strlen(prefix)) != 0) return NULL;
char *b64 = strdup(header + strlen(prefix)); // allocate
if (!b64) return NULL;
char *decoded = base64_decode(b64); // allocate inside
free(b64); // first free
if (!decoded) { free(decoded); return NULL; } // BUG: double free if decoded is NULL
char *colon = strchr(decoded, ':');
if (!colon) { free(decoded); return NULL; } // second free on same pointer
*colon = '\0';
char *username = strdup(decoded);
char *password = strdup(colon + 1);
free(decoded); // third free – still safe
return username; // caller must free both username and password
}
In the snippet above, if base64_decode returns NULL (invalid Base64), the code executes free(decoded) twice: once immediately after the if (!decoded) check and again later when the function returns NULL after the colon check. This double free corrupts the heap allocator’s internal bookkeeping and can lead to crashes, arbitrary code execution, or information leakage when the server processes subsequent requests.
The vulnerability is tightly coupled to Basic Auth because the header parsing routine is the only place where the server allocates temporary buffers for the decoded credentials. Other authentication schemes (e.g., JWT, API keys) often avoid manual malloc/free pairs by using higher‑level libraries, making Basic Auth a hotspot for this class of memory‑safety bug.
Basic Auth‑Specific Detection
middleBrick performs unauthenticated, black‑box scanning of the API surface. To surface a double free in a Basic Auth endpoint, it crafts malicious Authorization headers that force the server into the error paths described above. Typical probes include:
- Extremely long Base64 strings that cause allocation failures.
- Invalid Base64 padding (
=characters in the wrong place) leadingbase64_decodeto returnNULL. - Headers missing the colon separator (
username:) after decoding, triggering the second error branch.
When the server double‑frees a pointer, the heap allocator often detects the corruption and aborts the process, resulting in an HTTP 500 Internal Server Error, an empty response body, or a TCP reset. middleBrick records these anomalous responses as findings, tags them with the Memory Safety category, and provides a severity rating based on the observed impact (e.g., crash vs. silent corruption).
Because the scan is agentless and requires no credentials, the tester simply supplies the target URL; middleBrick iterates over the detected Basic Auth endpoints, injects the malformed headers, and reports any endpoint that reproduces the crash pattern. The finding includes the exact header value that triggered the issue, enabling developers to reproduce the bug locally.
Note: middleBrick does not attempt to exploit the double free; it only detects the presence of the condition by observing abnormal server behavior. The remediation guidance is supplied separately in the report.
Basic Auth‑Specific Remediation
The fix is to ensure each dynamically allocated buffer is released exactly once. A common pattern is to centralize cleanup with a goto label or to set pointers to NULL after freeing. Below is a corrected version of the previous example:
char *parse_basic_auth_fixed(const char *header) {
const char *prefix = "Basic ";
if (strncmp(header, prefix, strlen(prefix)) != 0) return NULL;
char *b64 = strdup(header + strlen(prefix));
if (!b64) return NULL;
char *decoded = base64_decode(b64);
free(b64);
if (!decoded) { // single free path
return NULL;
}
char *colon = strchr(decoded, ':');
if (!colon) {
free(decoded);
return NULL;
}
*colon = '\0';
char *username = strdup(decoded);
char *password = strdup(colon + 1);
free(decoded); // decoded freed once
if (!username || !password) {
free(username);
free(password);
return NULL;
}
/* Caller must free username and password */
return username;
}
Key changes:
- The
decodedbuffer is freed only once, after the colon check. - Error paths return early without attempting to free
decodeda second time. - Allocations for
usernameandpasswordare checked; on failure both are freed before returningNULL.
In modern codebases, using higher‑level abstractions eliminates manual malloc/free altogether. For example, in C++ one can return std::pair<std::string, std::string> where the strings manage their own memory, or in C use Apache’s apr_pool_t allocation pools that automatically clean up at the end of a request.
After applying the fix, run middleBrick again (via the CLI: middlebrick scan https://api.example.com/resource) to confirm that the anomalous 500 responses disappear and the security score improves. The dashboard will show the updated score over time, and the GitHub Action can be configured to fail a build if the Basic Auth endpoint’s risk rating rises above a chosen threshold.