https://bz.apache.org/bugzilla/show_bug.cgi?id=70210

            Bug ID: 70210
           Summary: mod_rewrite prefix_stat() reaches a UNC host before
                    UNCList is consulted - incomplete "expand UNC
                    checking" (r1927041), UNCList bypassed on Windows
           Product: Apache httpd-2
           Version: 2.5-HEAD
          Hardware: PC
            Status: NEW
          Severity: normal
          Priority: P2
         Component: mod_rewrite
          Assignee: [email protected]
          Reporter: [email protected]
  Target Milestone: ---

On a Windows httpd where UNCList is unset — the default, which denies all UNC
access - a remote unauthenticated user can make the server open an SMB
connection to a host of their choosing, and hold a worker thread for 21 seconds
per request, by sending a request that a server-context RewriteRule maps onto a
UNC path.

Steps to reproduce :
No UNCList directive anywhere in the configuration, so all UNC access is denied
by default.

1. Configure a server-context rewrite whose substitution begins with two
literal slashes.
   No rule flags are needed:

       RewriteEngine On
       RewriteRule ^/v/(.*)$ "//$1"

   (The [UNC] flag reaches the same code, and is arguably the more troubling
entry point: it is documented as controlling slash merging only, so an
administrator who sets it has no reason to expect it also removes UNCList from
the path.)

2. Add a control that reaches the same UNC host through the core directory
walk, which r1927041's sibling change in server/core.c does guard:

       Alias /alias-unc "//203.0.113.50/share"

3. Request the control. Use an address you do not route to; RFC 5737 space is
ideal:

       curl -s -o NUL -w "%{http_code} %{time_total}\n" \
            http://127.0.0.1:8099/alias-unc/f.txt

Expected, and observed: 404 in 0.0016 s, and in the error log

       AH10504: check_unc: UNC path //203.0.113.50/share/f.txt not allowed by
UNCList

The guard fires. No network traffic is generated.

4. Request the rewrite rule, with a DIFFERENT address - Windows negative-caches
a failed UNC lookup, so reusing the address from step 3 returns instantly and
produces a false negative:

       curl -s -o NUL -w "%{http_code} %{time_total}\n" \
            http://127.0.0.1:8099/v/203.0.113.61/share/f.txt

   Expected if the check applied: sub-millisecond, plus an AH10504 line.
   Observed: 21.02 seconds, and NO check_unc line of any kind in the error log
at LogLevel core:trace6, which logs check_unc's own trace output.

5. The unmistakable observable is the pair from steps 3 and 4 side by side:
same UNC target shape, same server, same run - 1.6 ms and an AH10504 denial on
one path, 21 seconds of SMB connection attempt and complete silence from
check_unc on the other. Confirm with a packet capture on port 445 if you want
to see the network activity directly.


Result :
Unprotected RewriteRule ^/v/(.*)$ "//$1"     ->  prefix_stat
      GET /v/203.0.113.61/share/f.txt
                                21023.7 ms    404
      error.log:
        mod_rewrite.c(548): local path result: //203.0.113.61/share/f.txt
      check_unc log lines for this request: 0

Isolating the cost to one call. Timing apr_filepath_root() directly, via
ctypes, against the same libapr-1.dll the server is running, one fresh address
per row:

  apr_filepath_root(..., APR_FILEPATH_TRUENAME, ...)
    control: C:/Windows/win.ini              0.0 ms   rv=0        root=C:/
    \\203.0.113.41\share/f.txt           21038.3 ms   rv=20024   
(APR_EBADPATH)
    //203.0.113.42/share/f.txt           21040.7 ms   rv=20024
  the same calls WITHOUT APR_FILEPATH_TRUENAME
    \\203.0.113.45\share/f.txt               0.0 ms   rv=0       
root=\\203.0.113.45\share/
    //203.0.113.46/share/f.txt               0.0 ms   rv=0       
root=//203.0.113.46/share/

So the network operation is apr_filepath_root() under APR_FILEPATH_TRUENAME -
it calls filepath_root_test() -> GetDriveTypeW() on the UNC root - and it then
fails, which is why prefix_stat() returns at

    modules/mappers/mod_rewrite.c:1012 (2.4.68)
        rv = apr_filepath_root(&root, &curpath, APR_FILEPATH_TRUENAME, pool);
        if (rv != APR_SUCCESS) {
            return 0;
        }

before ever reaching the apr_filepath_merge() below it.
ap_filepath_merge()/check_unc() are therefore never invoked, which matches the
zero check_unc log lines above.

-- 
You are receiving this mail because:
You are the assignee for the bug.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to