httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dgau...@hotwired.com (Dean Gaudet)
Subject Re: security holes and other fun stuff
Date Mon, 15 Jul 1996 03:12:46 GMT
In article <hot.mailing-lists.new-httpd-Pine.SGI.3.93.960713191831.5816D-100000@fully.organic.com>,
Brian Behlendorf  <new-httpd@hyperreal.com> wrote:
>On 13 Jul 1996, Dean Gaudet wrote:
>> While thinking on my VirtualHost syntax problem, and Cliff's request
>> I think I've discovered several configuration-related security (and
>> reliability) problems, one of which bypasses access controls.
>> 
>> First, a serious one.  Suppose the same webserver has an internal vhost,
>> an external vhost, 
>
>(I'm presuming here you mean, two IP numbers, say X and Y?)

Actually I've tried to exploit it now that I've had sleep, and
I can.  Suppose that you have an ip-vhost vhost.foobar.com, and
that phys.foobar.com is the machine's physical address (i.e. the
"main server address" in our documentation on vhosts), and doesn't
appear in any <VirtualHost> statement.  Then sending a request with
"Host: vhost.foobar.com" or "GET http://vhost.foobar.com/ HTTP/1.1"
to the physical address will cause the webserver to serve data from
vhost.foobar.com instead of phys.foobar.com.

That is, the config file does not specify HTTP/1.1 virtual hosts but
apache is allowing them to happen... in a case where I don't think it
should.

Here is an example config:

    ServerType standalone
    Port 80
    HostnameLookups on
    User nobody
    Group #-1
    ServerAdmin you@your.address
    ServerRoot /tmp/apache_1.1.1
    ErrorLog logs/error_log
    TransferLog logs/access_log
    PidFile logs/httpd.pid
    ScoreBoardFile logs/apache_status
    ResourceConfig /dev/null
    AccessConfig /dev/null
    DocumentRoot /tmp/apache_1.1.1/real-doc
    <VirtualHost vhost.foobar.com>
    DocumentRoot /tmp/apache_1.1.1/vhost-doc
    </VirtualHost>

real-doc/index.html and vhost-doc/index.html distinguish the
two servers.  Replace vhost.foobar.com with an alias on your box.
telnet to your box's main address and attempt to exploit it by issuing
"GET http://vhost.foobar.com/ HTTP/1.1" or "GET / HTTP/1.0\r\nHost:
vhost.foobar.com".  (Note if you do this exploit on another port make
sure to adjust your requests appropriately.)

Essentially what is happening right now is that a request can come into
the "main" address(s) and can then be mapped to any vhost, regardless
of whether that vhost is a name-vhost or an ip-vhost.  This violates
the Principle of Least Astonishment I think.

You want a real world example of where this might be a problem?  Suppose
you've created some forms-based mgmt software for your webserver, and
suppose you don't trust Apache's access control (I don't -- I can get
into this later -- but any good admin doesn't trust any access control and
piles it all on).  Then a reasonable solution is to put that forms-based
control stuff on addresses which the outside world "can't reach" (i.e. RFC
1597 private network addresses).  Of course if you're clued enough to
make it that far you'd also slap password and group restrictions on it.
But you'd still be astonished that suddenly your web server is happily
"routing" stuff it shouldn't.

I dunno, the whole thing feels like having ip source-routing enabled,
or "ip forwarding" enabled on hosts that really shouldn't act as
gateways.

Can the proxy folks comment on whether this would allow someone to exploit
the proxy into snarfing stuff off internal machines?

>So, the policy here should be, if DNS names fail to resolve correctly to
>an IP address on the machine, then that particular vhost setting fails
>but the rest of the server comes up.

Ahh but there's more than that happening here.  I'm talking about
configuration gotchas... and I'm hoping that we'll amend the
documentation.  Let me give an explicit example.

Suppose www.victim.com is served from ISP.  Their competitor decides
they want to steal web traffic.  So call up the ISP and ask for service
for www.fakename.com (competitor controls fakename.com).  ISP gives
them an IP address and tells them how to configure things, blah blah.
ISP's httpd.conf looks like this:

    <VirtualHost www.victim.com>
    blah blah
    </VirtualHost>
    ...
    <VirtualHost www.fakename.com>
    blah blah
    </VirtualHost>

Now, competitor changes their DNS so that www.fakename.com maps to
www.victim.com's address.  Take a boo at virtualhost_section() and
you'll see that it places www.fakename.com BEFORE www.victim.com in the
virtual host searchlist.  Bingo, any hit to www.victim.com's address will
be served out of the www.fakename.com configuration.  The ISP probably
won't notice ('cause of the thing Cliff reported and I said was hard to
figure out in general with http/1.1 to consider).

Sure, it won't last long.  But maybe it will... maybe the competitor is
also smart enough to clone most of victim.com's site and only change a
few important details... like where the credit card numbers are mailed.
Some sites are changed so infrequently this charade might be easy to
keep up for ages... victim.com may just figure "there's really no money
in the net" as all their traffic is stolen by competitor.

>> Suppose abc.com is your "main server address" and maps to 10.1.1.1 and
>> you expect def.com to map to 10.1.1.2, but you don't control def.com.
>> Then the def.com controller (or Evil Person) can force you to use
>> name-vhosts just by mapping def.com to 10.1.1.1.
>
>How is this a problem?  Oh, I suppose for clients which don't support
>Host:, this might make def.com the default host for that IP address, but I
>don't know...

Yep, it does -- the "main" host is only chosen if none of the virtual
hosts match the ip (see find_virtual_server()).  (Hey didn't you say that
www.organic.com was your default host?  Hey, hope one of your clients
doesn't accidentally set their www.foobar.com DNS to www.organic.com's
address :)

>I'd prefer the name the vhost responds to should be a union of the
>ServerName and ServerAlias settings, not just ServerAlias.  Ugh, but then
>you get things like
>
><VirtualHost 204.76.138.65>
>ServerName www.apache.org
>....
></VirtualHost>
>
><VirtualHost 204.76.138.65>
>ServerName dev.apache.org>
>...
></VirtualHost>
>
>hmm... slight semantic change, I suppose.

I'm moving in this direction... I'm thinking that we should have
ip addresses which serve sets of vhosts (even in the name-vhost
world there are cases when you'll want to spread them over several
addresses).  Within each set HTTP/1.1 methods are used to determine
which vhost to use.  The administrator should be able to statically
control set membership.  We're almost there now if we use my suggestion
of <VirtualHost A.B.C.D>+ServerName "www.foobar.com" for all virtualhosts
in the config... two things short:

- can only do HTTP/1.1 stuff with the "main" server address
- it doesn't respect "set" boundaries

But I think it's confusing...

>> So... do we want to rethink the syntax?  At the moment it's actually a bad
>> thing to be forced into name-vhost mode because not all clients support
>> it.  In the future this may not be an issue.
>
>On the flip side, the current setup would allow ISP's who are currently
>burning IP addresses with vhosts to allow those sites whose DNS is
>controlled elsewhere to migrate without having to coordinate it with the
>server authority; in other words, if I have a web site at best.com using
>www.bong.com, and best currently gives me an IP number but want it back,
>then I can change the IP mapping of "www.bong.com" at any time to the
>"centralized" best.com vhost IP number, and I won't need to coordinate
>that with best's sysadmins.  So, what to some people is a bug, is a
>feature to others. :)

Best needs to know about the change anyhow -- since they'll want to know
that you've freed up the number.  It isn't hard for them to move your
vhost to another spot in the config file.

>> - Apache doesn't do double reverse lookup for DNS based authentication.
>
>At least when I wrote about this for my book, I did mention that
>host-based access control was unsafe unless used with MAXIMUM_DNS (time
>to make this a runtime config?) or unless one used IP numbers.

We should probably put this on the mod_access todo list:

    - allow double reverse lookup to be configured only for those hits
	requiring it so that you don't have to suffer from MAXIMUM_DNS
	for every hit
    - provide a global "IPAuthByNumberOnly" that will barf on any allow/deny
	that isn't numeric
	-- this is because some configs are built out of lots of small files
	    that are edited by less knowledgeable people and/or you use
	    .htaccess files
	-- the barf should be a warning and the result should be to deny
	    everything (a nice safe way to continue after the error in the
	    config file)

>> - If ServerName isn't set, apache does a reverse lookup in
>>     default_server_hostnames to find it.  If DNS is down when the
>>     server boots this will cause ServerName to be defaulted incorrectly.
>> 
>>     Actually, default_server_hostnames will segfault if somewhere
>>     between get_local_host and "main = gethostbyname..." there is a DNS
>>     failure... or if the servername is bogus.  patch:
>...
>> +     if( main == NULL ) {
>> +         fprintf(stderr,"httpd: cannot resolve main ServerName.\n");
>> +         exit(1);
>> +     }
>
>Wouldn't it be saner to just set it to the IP number?

Actually, I like this approach:

*** http_main.c.orig    Sun Jul 14 19:35:11 1996
--- http_main.c Sun Jul 14 19:36:53 1996
***************
*** 1058,1065 ****

      /* Main host first */

!     if (!s->server_hostname)
        s->server_hostname = get_local_host(pconf);

      def_hostname = s->server_hostname;
      main = gethostbyname(def_hostname);
--- 1058,1069 ----

      /* Main host first */

!     if (!s->server_hostname) {
!       fprintf(stderr,"httpd: leaving ServerName unset is considered harmful to your health\n");
!       fprintf(stderr,"httpd: but I'll let it slip for the moment, and attempt to use DNS\n");
!       fprintf(stderr,"httpd: to find it... who knows what I'll do if your name server is
down.\n");
        s->server_hostname = get_local_host(pconf);
+     }

      def_hostname = s->server_hostname;
      main = gethostbyname(def_hostname);


Maybe I should just write a page "DNS Considered Harmful" and get you
guys to link to it from the docs? :)

Seriously... I have to get in there and dig around in order to implement
my multi-ip vhost stuff.  Is there a better way we should do it?

I figure I got no comments on my previous post about multi-ip syntax (i.e.
<VirtualHost 10.1.1.1 10.1.1.2> responding to both addresses) because:

    (a) everyone liked it (or didn't care)
    (b) I didn't include a patch and everyone was waiting for the patch
	to show up before arguing about how it should be done.

My experience with this list says (b) is the answer :)

I see two ways to approach vhost mapping.  One is to define the mapping
when the vhost is defined, and the other is to define all the mappings
together and have them symbolically refer to vhosts.  The first is great
for newbie admins because it does a lot of stuff implicitly (i.e. creates
name-vhosts without telling you).  The second is great for the person
writing the mapping code because it abstracts the mapping out of the
server_rec structure (which is a Good Thing).  Consider:

    # syntax <VirualHost SYMBOLIC-VHOST-NAME>
    <VirtualHost vhost1>
    ServerName www.vhost1.com
    # other goo blah blah
    </VirtualHost>

    <VirtualHost vhost2>
    ServerName www.vhost2.com
    # other goo blah blah
    </VirtualHost>
    ... and so on up to 4

    <VirtualHost go-away-server>
    ServerName www.blah.com
    # a server which sends back "go away" for any request
    </VirtualHost>
    
    # We now map ip addresses to symbolic-vhost-names.
    # Syntax: <Map ip-address*> ... </Map>
    # Ordering within a Map is important.  Ordering of Map statements isn't,
    # Map addresses can't overlap (we want to move towards hashing at least
    # ips).  If you want to use DNS names in map statements then you have
    # to define SHOOT_FOOT when compiling the server.
    <Map 10.1.1.1>
    # MatchHost SYMBOLIC-VHOST-NAME PATTERN+
    MatchHost vhost1 www.vhost1.com
    MatchHost vhost2 *.vhost2.com
    # MatchPath SYMBOLIC-VHOST-NAME PATTERN+
    MatchPath vhost1 /vhost1 /alpha
    MatchPath vhost2 /vhost2
    #
    Default vhost1
    </Map>

    # and now a mapping for another address, this is an ip-vhost only
    <Map 10.1.1.2>
    Default vhost3
    </Map>

    # this one responds on multiple addresses
    <Map 10.1.1.3 10.1.1.4>
    Default vhost4
    </Map>

    # and this one responds to any ip not present in the other rules
    <Map>
    Default go-away-server
    </Map>
    # an alternate option is to return 403 for anything that isn't matched
    # by a map...

Notice that this way it's easy to see all the aliases and path mappings
for a particular ip.  It'd also be possible to do some easy optimizations
such as turning a sequence of "simple" MatchHost statements (those
without wildcards) into a hashtable (a big win later on as name-vhosts
become common and every ISP is happily giving them out).

Dean

Mime
View raw message