Commit 9ba2dfbe authored by nmalcolm's avatar nmalcolm

Block limited broadcast address (255.255.255.255) in UrlBlocker

`UrlBlocker` protects GitLab and its users from attacks such as
Server Side Request Forgery and DNS Rebind attacks.

Until now, setting `allow_local_network` had no effect on blocking
`255.255.255.255`, whether true or false. Now, when
`allow_local_network` is set to `false` `255.255.255.255` is
blocked through the introduction of a check named
`validate_limited_broadcast_address`.

`255.255.255.255` is the "limited broadcast address", which is used to
make requests to all hosts on a local physical network [1]. Properly
configured routers won't route it. Historically it was used to wake up
offline PCs on a LAN which, since they were asleep, didn't have IP
addresses [2].

While `UrlBlocker` defaults `allow_local_network` to `true`, in
practice it is almost always `false` because of a convention to
use the GitLab configuration option which defaults to `false`.
If a GitLab administrator still wants to  reach `255.255.255.255`,
it can be added explicitly in the Allow List [3].

There is no reason a GitLab user would want to reach this, but it
could potentially be misused if an attacker finds a component
vulnerable to DNS rebinding, for example.

This commit aims to fulfil https://gitlab.com/gitlab-org/gitlab/-/issues/337796

[1]: https://datatracker.ietf.org/doc/html/rfc919#section-7
[2]: https://superuser.com/a/1006951
[3]: https://docs.gitlab.com/ee/security/webhooks.html#allowlist-for-local-requests

Changelog: changed
parent 4c3e65b0
......@@ -149,6 +149,7 @@ module Gitlab
validate_local_network(address_info)
validate_link_local(address_info)
validate_shared_address(address_info)
validate_limited_broadcast_address(address_info)
end
end
......@@ -253,6 +254,17 @@ module Gitlab
raise BlockedUrlError, "Requests to the link local network are not allowed"
end
# Raises a BlockedUrlError if any IP in `addrs_info` is the limited
# broadcast address.
# https://datatracker.ietf.org/doc/html/rfc919#section-7
def validate_limited_broadcast_address(addrs_info)
blocked_ips = ["255.255.255.255"]
return if (blocked_ips & addrs_info.map(&:ip_address)).empty?
raise BlockedUrlError, "Requests to the limited broadcast address are not allowed"
end
def internal?(uri)
internal_web?(uri) || internal_shell?(uri)
end
......
......@@ -299,6 +299,21 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
]
end
let(:limited_broadcast_address_variants) do
[
'255.255.255.255', # "normal" dotted decimal
'0377.0377.0377.0377', # Octal
'0377.00000000377.00377.0000377', # Still octal
'0xff.0xff.0xff.0xff', # hex
'0xffffffff', # still hex
'0xBaaaaaaaaaaaaaaaaffffffff', # padded hex
'255.255.255.255:65535', # with a port
'4294967295', # as an integer / dword
'[::ffff:ffff:ffff]', # short IPv6
'[0000:0000:0000:0000:0000:ffff:ffff:ffff]' # long IPv6
]
end
let(:fake_domain) { 'www.fakedomain.fake' }
shared_examples 'allows local requests' do |url_blocker_attributes|
......@@ -336,6 +351,12 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
expect(described_class).not_to be_blocked_url('http://[::ffff:a9fe:a864]', **url_blocker_attributes)
expect(described_class).not_to be_blocked_url('http://[fe80::c800:eff:fe74:8]', **url_blocker_attributes)
end
it 'allows limited broadcast address 255.255.255.255 and variants' do
limited_broadcast_address_variants.each do |variant|
expect(described_class).not_to be_blocked_url("https://#{variant}", **url_blocker_attributes), "Expected #{variant} to be allowed"
end
end
end
context 'true (default)' do
......@@ -368,6 +389,17 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
expect(described_class).to be_blocked_url('http://[fe80::c800:eff:fe74:8]', allow_local_network: false)
end
it 'blocks limited broadcast address 255.255.255.255 and variants' do
# Raise BlockedUrlError for invalid URLs.
# The padded hex version, for example, is a valid URL on Mac but
# not on Ubuntu.
stub_env('RSPEC_ALLOW_INVALID_URLS', 'false')
limited_broadcast_address_variants.each do |variant|
expect(described_class).to be_blocked_url("https://#{variant}", allow_local_network: false), "Expected #{variant} to be blocked"
end
end
context 'when local domain/IP is allowed' do
let(:url_blocker_attributes) do
{
......@@ -394,6 +426,7 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
'::ffff:169.254.168.100',
'::ffff:a9fe:a864',
'fe80::c800:eff:fe74:8',
'255.255.255.255',
# garbage IPs
'45645632345',
......@@ -415,6 +448,10 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do
expect(described_class).to be_blocked_url(url, **attrs)
end
end
it 'allows the limited broadcast address 255.255.255.255' do
expect(described_class).not_to be_blocked_url('http://255.255.255.255', **url_blocker_attributes)
end
end
context 'with domains in allowlist' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment