-
-
Notifications
You must be signed in to change notification settings - Fork 296
Description
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.c — http_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:
- No bounds checking on the output buffer — base64 output can exceed 1024 bytes
- 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) {