out-of-bounds read due to signedness error ============================================ CVE-2011-4362 was assigned to this bug. Description ------------- For http auth we need to base64-decode user input; the allowed character range includes non ASCII characters above 0x7f. The function to decode this string takes a "const char *in"; and reads each character into an "int ch", which is used as offset in the table. So characters above 0x80 lead to negative indices (as char is signed on most platforms). Thanks to Xi Wang who discovered the issue. Detailed analysis ------------------- Here the vulnerable code (src/http_auth.c:67) --- static const short base64_reverse_table[256] = ...; static unsigned char * base64_decode(buffer *out, const char *in) { ... int ch, ...; size_t i; ... ch = in[i]; ... ch = base64_reverse_table[ch]; ... } --- It doesn't matter if "broken" data is read - it just may allow more encodings of the correct login information. The only possible impact is a segfault, leading to DoS. I had a look at some debian and openSUSE binaries, and it looks like there is always enough data (>= 256 bytes) in the .rodata section before the base64_reverse_table table, so these binaries are not vulnerable. Upstream issue ---------------- http://redmine.lighttpd.net/issues/2370 Affected versions ------------------- all versions before 1.4.30 / svn revision 2806 not all binaries are vulnerable (no vulnerable one found yet) Fixed in ---------- 1.4.x: http://redmine.lighttpd.net/projects/lighttpd/repository/revisions/2806 1.5: http://redmine.lighttpd.net/projects/lighttpd/repository/revisions/2807 Solutions or Workaround ------------------------- There is no workaround (unless you don't need mod_auth and you just disable it). Apply lighttpd-fix-base64-signedness.patch, also inline below (patch applies to both 1.4.x and 1.5) Patch ------- lighttpd-fix-base64-signedness.patch: === diff --git a/src/http_auth.c b/src/http_auth.c index f2f86dd..33adf71 100644 --- a/src/http_auth.c +++ b/src/http_auth.c @@ -99,7 +99,7 @@ static unsigned char * base64_decode(buffer *out, const char *in) { ch = in[0]; /* run through the whole string, converting as we go */ for (i = 0; i < in_len; i++) { - ch = in[i]; + ch = (unsigned char) in[i]; if (ch == '\0') break; ===