Skip to content

Security: validate nested stream wrapper in stream: resource (CWE-22)#1195

Merged
wisskid merged 1 commit into
masterfrom
security/stream-wrapper-bypass
Jun 29, 2026
Merged

Security: validate nested stream wrapper in stream: resource (CWE-22)#1195
wisskid merged 1 commit into
masterfrom
security/stream-wrapper-bypass

Conversation

@wisskid

@wisskid wisskid commented Jun 29, 2026

Copy link
Copy Markdown
Member

The built-in stream: resource type let a template bypass Security stream restrictions. BasePlugin::load() matches the 'stream' sysplugin before the stream_get_wrappers()/isTrustedStream() check, so a resource such as stream:php://filter/read=convert.base64-encode/resource=/path was opened by StreamPlugin::getContent() via fopen() on the nested php:// wrapper without ever validating it. This bypassed Security::$streams (including Security::$streams = null) and allowed reading arbitrary local files.

Parse the wrapper scheme from the resolved path in StreamPlugin::getContent() and validate it with Security::isTrustedStream() before fopen(), giving the stream: resource the same check the direct wrapper path already receives.

Adds regression tests covering the disabled-streams bypass, the not-on-allowlist case, and a positive test that an explicitly allowed wrapper still works.

The built-in stream: resource type let a template bypass Security stream
restrictions. BasePlugin::load() matches the 'stream' sysplugin before the
stream_get_wrappers()/isTrustedStream() check, so a resource such as
stream:php://filter/read=convert.base64-encode/resource=/path was opened by
StreamPlugin::getContent() via fopen() on the nested php:// wrapper without
ever validating it. This bypassed Security::$streams (including
Security::$streams = null) and allowed reading arbitrary local files.

Parse the wrapper scheme from the resolved path in StreamPlugin::getContent()
and validate it with Security::isTrustedStream() before fopen(), giving the
stream: resource the same check the direct wrapper path already receives.

Adds regression tests covering the disabled-streams bypass, the
not-on-allowlist case, and a positive test that an explicitly allowed wrapper
still works.
@wisskid wisskid merged commit 3c9f77a into master Jun 29, 2026
18 checks passed
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.

1 participant