Skip to content

fix(security): stop curl_command from carrying the bearer token across a redirect - #525

Merged
carlosmmatos-cs merged 2 commits into
mainfrom
fix/523-curl-command-bearer-redirect
Sep 9, 2026
Merged

fix(security): stop curl_command from carrying the bearer token across a redirect#525
carlosmmatos-cs merged 2 commits into
mainfrom
fix/523-curl-command-bearer-redirect

Conversation

@carlosmmatos-cs

@carlosmmatos-cs carlosmmatos-cs commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Fixes #523.

curl_command() in the four bash scripts passed -L, so a redirect was followed with the OAuth bearer token still sitting on curl's configuration input. curl fixed the cross-host case in 7.58.0 as CVE-2018-1000007, which leaves roughly 7.30 through 7.57 exposed.

I measured every endpoint curl_command touches against the live API. All of them answer in a single hop on the correct region — including sensors/entities/download-installer/v3, which I had assumed redirected to a CDN, and the registry tags list on registry.crowdstrike.com. So on a correct-region run -L never fired at all.

The one case where it did something was a wrong region, because the API answers a wrong region with a 308 to the right one. That path never actually worked on a curl that strips the header, which is every version anyone still runs: on both curl 7.29.0 and 8.5.0 the redirect was followed, the header was dropped, the call came back 401, and the run died with a misleading No sensor found for OS: Ubuntu, Version: 24. The only curl versions where -L produced a working request are the same ones that leak the token.

So -L is gone, and --proto-redir with it, keeping the convention from #520/#521 that --proto-redir only appears next to -L. In its place curl_command reads x-cs-region off the un-followed redirect and re-issues against that region, resolved through cs_cloud(). The retry host therefore always comes from a closed allowlist and never from Location, which is the part an attacker would control. This is the same shape as the OAuth token retry in #521, and it means region correction now covers every request carrying the token rather than only the ones made after get_oauth_token had corrected the cloud. That matters for FALCON_ACCESS_TOKEN: there is no token POST on that path, so there is no x-cs-region to read up front, and on main every API call went to the wrong region and failed.

There is no way to discover the region up front without picking a scope. The 308 only comes back on a real routable path — an unknown path answers 404 with no x-cs-region — and it is emitted after authentication, so an unauthenticated probe just gets 401. Riding on the caller's own request avoids assuming any particular scope, and costs nothing when the region is already right.

Two details worth flagging in review. The body is buffered to a temp file because the redirect body is 107 bytes rather than empty, so letting it through would corrupt the value the caller captures; that is safe for the -o callers because curl writes their file itself and stdout stays empty. And the status is taken from the last HTTP/ line in the dump, because a proxy CONNECT writes one of its own first.

get_oauth_token also now adopts the hint when FALCON_CLOUD disagrees with it, instead of warning and then continuing to the wrong region. That is not needed for correctness any more, but it saves a redirect round trip on every subsequent call and it makes the existing warning mean something. falcon-container-sensor-pull.sh already did this; install, uninstall and migrate only warned.

Verified against the live tenant on curl 7.29.0 and 8.5.0, with client credentials and with FALCON_ACCESS_TOKEN, for us-1, us-2 and eu-1: GET, GET with -o, PATCH with a JSON body, and the query holding a literal | all reach the correct region, and the same wrong-region runs fail on main. The argument rewrite was checked separately under dash, bash and macOS sh. Exit codes are unchanged: an unresolvable proxy still yields 5 through the function, both under set +e and through a command substitution, so #526's call-site guards receive the real code. shfmt (-i 4 -ci, bash and posix) and shellcheck (bash and dash) are clean on all four files.

#522 also edits curl_command and needs rebasing against main before it goes in.

…s a redirect

curl_command() passed -L, so a redirect was followed with the OAuth bearer
token still on curl's configuration input. curl fixed the cross-host case in
7.58.0 as CVE-2018-1000007, which leaves roughly 7.30 through 7.57 exposed.

Measured against the live API: every endpoint curl_command touches answers in a
single hop on the correct region, including download-installer/v3 and the
registry tags list. So -L never fired on a correct-region run.

-L only did work when FALCON_CLOUD named the wrong region, because the API
answers a wrong region with a 308 to the right one. That never worked on a curl
that strips the header, which is every supported version: on curl 7.29.0 and
8.5.0 the redirect was followed, the header was dropped, the call came back 401
and the run died with a misleading "No sensor found for OS" error. The only
versions where -L produced a working request are the same versions that leak the
token.

Dropped -L, and with it --proto-redir, keeping the convention from #521 that
--proto-redir appears only next to -L. The wrong-region case is now handled the
way the OAuth token request already handles it: the x-cs-region hint is adopted
instead of the redirect being followed, so it works on every curl version rather
than only the leaky band. The warning naming the real region still prints.
falcon-container-sensor-pull.sh already adopted the hint; install, uninstall and
migrate only warned and kept the wrong region.

Fixes #523
@carlosmmatos-cs
carlosmmatos-cs requested a review from a team as a code owner September 8, 2026 21:06
…following it

Dropping -L closed the leak but left the wrong-region case relying on
get_oauth_token having corrected cs_falcon_cloud first. That only covers the
client id and secret path: with FALCON_ACCESS_TOKEN there is no token POST, so
there is no x-cs-region to read, and every API call went to the wrong region.

curl_command now reads x-cs-region off the un-followed redirect and re-issues
against that region, resolved through cs_cloud(), so the retry host always comes
from a closed allowlist and never from Location. Region correction now covers
every request that carries the token, whichever way the token was obtained.

There is no scope-free way to discover this up front: the 308 only comes back on
a real routable path. An unknown path answers 404 with no x-cs-region, and the
redirect is emitted after authentication, so an unauthenticated probe gets 401.
The retry therefore rides on the caller's own request rather than a probe.

The body is buffered because the redirect body is 107 bytes, not empty, so
emitting it would corrupt the value the caller captures. Buffering is safe for
the -o callers too: curl writes their file itself and stdout stays empty.

The status is read from the last HTTP status line, because a proxy CONNECT dumps
one of its own first. The exit code is captured and returned so behaviour under
set -e is unchanged and #526's call-site guards still receive the real code -
measured: rc=5 for an unresolvable proxy, both under set +e and through a
command substitution.

Verified against the live API on curl 7.29.0 and 8.5.0, with client credentials
and with FALCON_ACCESS_TOKEN, for us-1, us-2 and eu-1: GET, GET with -o, PATCH
with a JSON body, and the query holding a literal pipe all reach the correct
region. The arg rewrite was checked separately under dash, bash and macOS sh.
@carlosmmatos-cs
carlosmmatos-cs merged commit 3191390 into main Sep 9, 2026
7 checks passed
@carlosmmatos-cs
carlosmmatos-cs deleted the fix/523-curl-command-bearer-redirect branch September 9, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

curl_command() follows redirects with the OAuth bearer token attached

2 participants