Mailing List Archive

[Bug 66034] New: Read beyond bounds via mod_substitute.c [zhbug_httpd_22.1]
https://bz.apache.org/bugzilla/show_bug.cgi?id=66034

Bug ID: 66034
Summary: Read beyond bounds via mod_substitute.c
[zhbug_httpd_22.1]
Product: Apache httpd-2
Version: 2.5-HEAD
Hardware: All
OS: All
Status: NEW
Severity: normal
Priority: P2
Component: mod_substitute
Assignee: bugs@httpd.apache.org
Reporter: generalbugs@zippenhop.com
Target Milestone: ---

There is a read-beyond-bounds bug in do_pattmatch()
(modules/filters/mod_substitute.c) in httpd 2.4.53+ [1] when built with PCRE2
[2]. The bug is caused by a sign extension of an |int| to an |apr_size_t| ,
which causes lengths >= 0x80000000 to become 0xffffffffxxxxxxxx with a
consequent read beyond bounds.

Attached is a POC that demonstrates the bug.

The bug is in line 278 (v2.4.53), which truncates |apr_size_t bytes| to |int
left|. The call to ap_regexec_len() on lines 282-83 then sign-extends |left| to
an |apr_size_t|:

127: static apr_status_t do_pattmatch(ap_filter_t *f, apr_bucket *inb,
128: apr_bucket_brigade *mybb,
129: apr_pool_t *pool)
130: {
...
158: for (b = APR_BRIGADE_FIRST(mybb);
159: b != APR_BRIGADE_SENTINEL(mybb);
160: b = APR_BUCKET_NEXT(b)) {
...
168: if (apr_bucket_read(b, &buff, &bytes, APR_BLOCK_READ)
169: == APR_SUCCESS) {
...
277: else if (script->regexp) {
278: int left = bytes;
279: const char *pos = buff;
280: char *repl;
281: apr_size_t space_left = cfg->max_line_length;
282: while (!ap_regexec_len(script->regexp, pos, left,
283: AP_MAX_REG_MATCH, regm, 0)) {

(ap_regexec_len() takes an |apr_size_t| for argument 3).

Use the POC thusly:

1. Copy the httpd lines into httpd.conf.
2. Create a file 0x80000000 bytes long filled with 'a'.
3. Create an empty file /bug22.1/bug22.1.htm in an httpd installation's
server root.
4. Start httpd (built with PCRE2) and attach a debugger to it.
5. Set a BP on do_pattmatch() line 278, above.
6. Send the long file to httpd using

curl -v -i -X POST -T <long file's path> -k <httpd's IP
address>/bug22.1/bug22.1.htm

7. When the BP fires (this will take ~10-60m, depending on CPU power),
examine |bytes| and notice it's 0x80000000.
8. Step into ap_regexec_len() and notice that its |len| argument is
0xffffffff80000000.
9. Proceed. httpd will die with a read beyond bounds in pcre2_match_*() on a
call to memchr() while searching the long string for the first character of the
search string ('f').

In mitigation, (1) the bug can be reached only (a) if the administrator allows
mod_substitute to process lines >= 0x80000000 bytes long and (b) that amount of
data is available to be sent as a response body (such as via mod_reflector or
as a file present on the server); and (2) httpd probably will die before
sending any out-of-bounds data back to the attacker.

[1] The bug is still present in trunk. The security team asked me to file this
bug publicly, on the idea that the circumstances in which it could cause an
issue are too extreme to keep it secret.

[2] This appears to have become the default as of v2.4.53 :
https://dlcdn.apache.org/httpd/CHANGES_2.4.53 .

-------- httpd lines ----------------------------------------------------
<Location /bug22.1>
SetHandler reflector
SetOutputFilter substitute
AddOutputFilter substitute *.htm
SubstituteMaxLineLength 4g
Substitute "s/foo/bar/i"
</Location>
-------- httpd lines ----------------------------------------------------

--
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: bugs-unsubscribe@httpd.apache.org
For additional commands, e-mail: bugs-help@httpd.apache.org