BusyBox awk: Format String Vulnerability in fmt_num() via OFMT/CONVFMT Leads to Denial of Service and Information Disclosure
In fmt_num() of editors/awk.c in BusyBox's awk applet, the user-controllable OFMT and CONVFMT awk variables are passed directly to snprintf() as the format string, with validation that inspects only the final character of the string to confirm it looks like a numeric conversion specifier (one of diouxXeEfFgGaA), rather than verifying the string contains exactly one conversion specifier. A crafted format string such as "%s%s%s%d" passes this check (its last character, 'd', is a valid numeric specifier) but causes snprintf() to be invoked with only a single supplied argument (the double value being formatted) while the format string itself requests multiple conversions. The extra %s conversions consume nonexistent variadic arguments, reading whatever values happen to occupy the integer-argument register save area and adjacent stack memory, and attempting to dereference them as pointers. This is triggerable either by an awk script assigning an attacker-influenced value to OFMT/CONVFMT, or via the -v OFMT=.../-v CONVFMT=... command-line option using externally-sourced data, whenever the resulting value is a non-integral double (the integral-value code path is unaffected, as it uses a fixed "%lld"/"%.0f" format). Exploitation is reliably reproducible and results in a process crash (SIGSEGV) in the common case, and can leak raw register/stack memory content (including, on non-PIE builds, potential code addresses) back through the applet's normal output when the crafted format uses %x-style conversions instead of %s. An additional attempt using a %n conversion (to obtain a write primitive) did not succeed on glibc builds with _FORTIFY_SOURCE enabled, which independently detects and rejects %n when the format string resides in writable memory (aborting with SIGABRT rather than performing a write); this mitigation is glibc-specific and was not verified against other C libraries (e.g. musl, uClibc-ng) commonly used in embedded BusyBox deployments.
While reviewing the number-formatting code of BusyBox's awk applet, Artfical DT Developer Talha Berk Arslan noticed that the user-controllable OFMT and CONVFMT variables were passed directly to snprintf() as the format string. He found that the validation logic checked only the final character of the string to see if it looked like a numeric conversion specifier, without ever verifying that the string actually contained exactly one conversion specifier.
fmt_num() passed the user-controllable OFMT and CONVFMT awk variables directly to snprintf() as the format string. Validation checked only that the string's final character was a valid numeric specifier (one of diouxXeEfFgGaA), not that the string contained exactly one conversion specifier. A crafted format string such as "%s%s%s%d" passed this check (its last character, 'd', is a valid numeric specifier) but caused snprintf() to be invoked with only a single supplied argument while the format string itself requested multiple conversions. The extra %s conversions consumed nonexistent variadic arguments, reading whatever values happened to occupy the integer-argument register save area and adjacent stack memory, and attempting to dereference them as pointers.
This is triggerable either by an awk script assigning an attacker-influenced value to OFMT/CONVFMT, or via the -v OFMT=.../-v CONVFMT=... command-line option using externally-sourced data, whenever the resulting value is a non-integral double (the integral-value code path is unaffected, as it uses a fixed "%lld"/"%.0f" format). Exploitation is reliably reproducible and results in a process crash (SIGSEGV) in the common case, and can leak raw register/stack memory content (including, on non-PIE builds, potential code addresses) back through the applet's normal output when the crafted format uses %x-style conversions instead of %s. An additional attempt using a %n conversion did not succeed on glibc builds with _FORTIFY_SOURCE enabled, which independently detects and rejects %n when the format string resides in writable memory; this mitigation is glibc-specific and was not verified against other C libraries (e.g. musl, uClibc-ng) commonly used in embedded BusyBox deployments.
Validation in fmt_num() inspected only the final character of the OFMT/CONVFMT string to confirm it looks like a numeric conversion specifier, rather than verifying the string contains exactly one conversion specifier — allowing format strings with multiple conversions that happen to end in a valid specifier to pass the check.
Update to a BusyBox build compiled from current git (commit 9958bbd70a357529a5ac4a7de56f0c11d28842a0, "awk: disallow OFMT/CONVFMT to have more than one %SPEC, implement %s", or later). The fix rewrites fmt_num() to locate the single %SPEC in the format string, reject the format if more than one unescaped '%' conversion is present or if the specifier uses '*' (dynamic width/precision, requiring an additional argument), and adds proper support for a %s conversion (formatting the number via "%.6g" first, matching gawk behavior). As of this writing, Ubuntu's busybox-static package (1:1.37.0-7ubuntu1) has not yet backported this fix; Ubuntu Security Team was notified on 2026-09-01.
CWE-134
Pending — reserved as CAN-2026-2035924 via MITRE CNA-LR on 2026-08-17, not yet assigned an actual CVE-YYYY-NNNNN number.
Artfical DT Developer Talha Berk Arslan