Skip to content

Heap Buffer Overflow in http_auth_token() #476

@z00z00z00

Description

@z00z00z00

OCD-2026-007 — Heap Buffer Overflow in http_auth_token()

Summary

Field Value
Product Axel Download Accelerator
Version ≤ 2.17.14 (all versions)
Component src/http.chttp_auth_token()
CWE CWE-120 (Buffer Copy without Checking Size of Input)
CVSS 3.1 7.5 (High) — AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:N/A:H
Impact Denial of Service (confirmed), memory corruption (confirmed), code execution (theoretical under specific conditions)
Reporter Guillaume Meunier — Orange Cyberdefense VOC Team
Date 2026-02-28
Status Confirmed — crash reproduced with ASAN and release builds

Description

The http_auth_token() function in src/http.c encodes user:password credentials into base64 and writes the output into a fixed-size buffer (conn->auth[MAX_STRING], 1024 bytes) without any bounds checking. Additionally, the function does not write a null terminator after the base64 output.

Since user and pass are each stored in char[MAX_STRING] (1024) fields parsed from the URL by conn_set(), the combined input user:pass can reach 2047 bytes. The base64 encoding expands this to approximately 2732 bytes, overflowing the 1024-byte destination buffer by up to 1708 bytes.

The http_t structure is heap-allocated (embedded in conn_t, allocated via calloc() in axel_new()). The overflow therefore corrupts adjacent heap-resident fields, including abuf_t pointers used by realloc().

Root Cause

// src/http.c:73-95
static void
http_auth_token(char *token, const char *user, const char *pass)
{
    const char base64_encode[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz" "0123456789+/";
    const char *auth[] = { user, ":", pass, NULL };
    const char **p = auth;

    while (*p) {
        char a = chain_next(&p);
        if (!a) break;
        *token++ = base64_encode[a >> 2];      // No bounds check
        char b = chain_next(&p);
        *token++ = base64_encode[...];          // Writes continue unbounded
        // ...
    }
    // BUG: No null terminator written
}

Called from http_connect() at line 136:

http_auth_token(conn->auth, user, pass);  // conn->auth is char[1024]

Two bugs:

  1. No bounds checking on the output buffer — base64 output can exceed 1024 bytes
  2. No null termination — relies on buffer being pre-zeroed

Memory Layout (http_t, heap-allocated)

Offset   Field                Size    Status after overflow (284-byte case)
──────   ─────                ────    ─────────────────────────────────────
0x0000   char host[1024]      1024    Unaffected
0x0400   char auth[1024]      1024    ← BASE64 OUTPUT STARTS HERE
0x0800   abuf_t request.p     8       ← CORRUPTED (triggers crash in realloc)
0x0808   abuf_t request.len   8       ← CORRUPTED
0x0810   abuf_t headers.p     8       ← CORRUPTED
0x0818   abuf_t headers.len   8       ← CORRUPTED
0x0820   int port             4       ← CORRUPTED
0x0824   int proto            4       ← CORRUPTED
0x0828   int proxy            4       ← CORRUPTED
0x082C   char proxy_auth[]    ...     ← PARTIALLY CORRUPTED (up to 240 bytes)

Proof of Concept

# Trigger: URL with oversized user:pass (total URL ≤ 1024 chars)
# user=490 chars, pass=490 chars → base64 output = 1308 bytes → 284-byte overflow

USER=$(python3 -c "print('A'*490)")
PASS=$(python3 -c "print('B'*490)")
axel "http://${USER}:${PASS}@127.0.0.1:8080/f"
# Result: Segmentation fault (exit code 139)

ASAN output:

==PID==ERROR: AddressSanitizer: SEGV on unknown address
    #0 __sanitizer::atomic_load (sanitizer_atomic_clang.h:43)
    #1 __asan::Allocator::Reallocate (asan_allocator.cpp:768)
    #3 realloc (asan_malloc_linux.cpp:82)
    #4 abuf_setup (src/abuf.c:59)         ← realloc() with corrupted pointer
    #5 conn_setup (src/conn.c:317)
    #6 conn_info (src/conn.c:437)
    #7 axel_new (src/axel.c:219)
    #8 main (src/text.c:334)

Reproduction

Parameter Value
OS Ubuntu 25.10 (x86_64)
Compiler GCC 15.1.0
ASAN -fsanitize=address,undefined -g -O0
Commit b1b8c2fc67eef61d421a61699eaed46efcdb97d5 (2025-02-03)
Build ./configure --without-ssl && make

Also reproduced on release build (no ASAN, -O2) — segfault exit code 139.

Impact Assessment

Scenario Status Notes
Denial of Service (crash) Confirmed Immediate segfault on any platform
Heap metadata corruption Confirmed abuf_t pointers overwritten with base64 data
Code execution Theoretical Corrupted request.p creates a write-what-where primitive via realloc() + http_get(). Exploitation is constrained by the base64 output charset (A-Za-z0-9+/=), which cannot produce null bytes required for valid x86_64 heap pointers. RCE would require heap layout manipulation or a separate information leak.

Note: The CLI frontend (text.c:275) rejects URLs longer than 1024 bytes, but the overflow is achievable within this limit (490+490 = 1005-char URL). Programs using axel's internal API bypass this check entirely.

Remediation

Minimal fix

// In http_connect(), before calling http_auth_token():
size_t cred_len = strlen(user) + 1 + strlen(pass);
size_t b64_len = ((cred_len + 2) / 3) * 4;
if (b64_len >= sizeof(conn->auth)) {
    fprintf(stderr, "Credentials exceed maximum length\n");
    return 0;
}
http_auth_token(conn->auth, user, pass);

Comprehensive fix

static int
http_auth_token(char *token, size_t token_size, const char *user, const char *pass)
{
    size_t input_len = strlen(user) + 1 + strlen(pass);
    size_t needed = ((input_len + 2) / 3) * 4 + 1;  // +1 for null terminator
    if (needed > token_size) {
        *token = '\0';
        return -1;  // proper error return for library use
    }
    // ... existing encoding logic ...
    *token = '\0';  // always null-terminate
    return 0;
}

Suggested Patch

--- a/src/http.c
+++ b/src/http.c
@@ -133,7 +133,14 @@ http_connect(http_t *conn, int proto, char *proxy, char *host, int port,
 	if (*user == 0) {
 		*conn->auth = 0;
 	} else {
-		http_auth_token(conn->auth, user, pass);
+		size_t cred_len = strlen(user) + 1 + strlen(pass);
+		size_t b64_len = ((cred_len + 2) / 3) * 4;
+		if (b64_len >= sizeof(conn->auth)) {
+			fprintf(stderr, "Credentials too long\n");
+			return 0;
+		}
+		http_auth_token(conn->auth, user, pass);
+		conn->auth[sizeof(conn->auth) - 1] = '\0';
 	}
 
 	if (!conn->proxy || !puser || *puser == 0) {

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions