Blade `@parent` Exploitation Leading To Possible XSS in Laravel
Description
Laravel is a web application framework. Laravel prior to versions 8.75.0, 7.30.6, and 6.20.42 contain a possible cross-site scripting (XSS) vulnerability in the Blade templating engine. A broken HTML element may be clicked and the user taken to another location in their browser due to XSS. This is due to the user being able to guess the parent placeholder SHA-1 hash by trying common names of sections. If the parent template contains an exploitable HTML structure an XSS vulnerability can be exposed. This vulnerability has been patched in versions 8.75.0, 7.30.6, and 6.20.42 by determining the parent placeholder at runtime and using a random hash that is unique to each request.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Laravel Blade templating engine XSS via predictable parent placeholder hash in versions prior to 8.75.0, 7.30.6, and 6.20.42.
Vulnerability
Laravel versions prior to 8.75.0, 7.30.6, and 6.20.42 contain a cross-site scripting (XSS) vulnerability in the Blade templating engine [1]. The vulnerability arises because the parent placeholder uses a predictable SHA-1 hash based on the section name, allowing an attacker to guess the hash by trying common section names [1]. If the parent template contains an exploitable HTML structure, an XSS attack can be triggered [1].
Exploitation
An attacker needs to be able to influence the content of a Blade template that uses the @parent directive. By guessing the SHA-1 hash of the parent placeholder (e.g., by trying common section names), the attacker can craft a malicious input that, when rendered, breaks an HTML element and injects a script [1]. The user must then click on the broken element, which navigates to the attacker-controlled location, executing the XSS [1].
Impact
Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of the victim's browser, leading to potential information disclosure, session hijacking, or other client-side attacks [1]. The attack requires user interaction (clicking) and a specific vulnerable template structure.
Mitigation
The vulnerability is patched in Laravel versions 8.75.0, 7.30.6, and 6.20.42 [1]. The fix changes the parent placeholder to use a random hash unique to each request, making it unpredictable [1]. The corresponding pull requests are #39906 for 8.x, #39909 for 7.x, and #39908 for 6.x [2][3][4]. Users should upgrade to the patched versions immediately.
AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
laravel/frameworkPackagist | < 6.20.42 | 6.20.42 |
laravel/frameworkPackagist | >= 7.0.0, < 7.30.6 | 7.30.6 |
laravel/frameworkPackagist | >= 8.0.0, < 8.75.0 | 8.75.0 |
illuminate/viewPackagist | < 6.20.42 | 6.20.42 |
illuminate/viewPackagist | >= 7.0.0, < 7.30.6 | 7.30.6 |
illuminate/viewPackagist | >= 8.0.0, < 8.75.0 | 8.75.0 |
Affected products
3- ghsa-coords2 versions
< 6.20.42+ 1 more
- (no CPE)range: < 6.20.42
- (no CPE)range: < 6.20.42
- laravel/frameworkv5Range: >= 8.0.0, < 8.75.0
Patches
1b8174169b180[6.x] Fix parent call (#39908)
4 files changed · +39 −15
src/Illuminate/View/Compilers/Compiler.php+1 −1 modified@@ -48,7 +48,7 @@ public function __construct(Filesystem $files, $cachePath) */ public function getCompiledPath($path) { - return $this->cachePath.'/'.sha1($path).'.php'; + return $this->cachePath.'/'.sha1('v2'.$path).'.php'; } /**
src/Illuminate/View/Compilers/Concerns/CompilesLayouts.php+3 −3 modified@@ -2,8 +2,6 @@ namespace Illuminate\View\Compilers\Concerns; -use Illuminate\View\Factory as ViewFactory; - trait CompilesLayouts { /** @@ -50,7 +48,9 @@ protected function compileSection($expression) */ protected function compileParent() { - return ViewFactory::parentPlaceholder($this->lastSection ?: ''); + $escapedLastSection = strtr($this->lastSection, ['\\' => '\\\\', "'" => "\\'"]); + + return "<?php echo \Illuminate\View\Factory::parentPlaceholder('{$escapedLastSection}'); ?>"; } /**
src/Illuminate/View/Concerns/ManagesLayouts.php+25 −1 modified@@ -3,6 +3,7 @@ namespace Illuminate\View\Concerns; use Illuminate\Contracts\View\View; +use Illuminate\Support\Str; use InvalidArgumentException; trait ManagesLayouts @@ -28,6 +29,13 @@ trait ManagesLayouts */ protected static $parentPlaceholder = []; + /** + * The parent placeholder salt for the request. + * + * @var string + */ + protected static $parentPlaceholderSalt; + /** * Start injecting content into a section. * @@ -168,12 +176,28 @@ public function yieldContent($section, $default = '') public static function parentPlaceholder($section = '') { if (! isset(static::$parentPlaceholder[$section])) { - static::$parentPlaceholder[$section] = '##parent-placeholder-'.sha1($section).'##'; + $salt = static::parentPlaceholderSalt(); + + static::$parentPlaceholder[$section] = '##parent-placeholder-'.sha1($salt.$section).'##'; } return static::$parentPlaceholder[$section]; } + /** + * Get the parent placeholder salt. + * + * @return string + */ + protected static function parentPlaceholderSalt() + { + if (! static::$parentPlaceholderSalt) { + return static::$parentPlaceholderSalt = Str::random(40); + } + + return static::$parentPlaceholderSalt; + } + /** * Check if section exists. *
tests/View/ViewBladeCompilerTest.php+10 −10 modified@@ -18,7 +18,7 @@ protected function tearDown(): void public function testIsExpiredReturnsTrueIfCompiledFileDoesntExist() { $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); - $files->shouldReceive('exists')->once()->with(__DIR__.'/'.sha1('foo').'.php')->andReturn(false); + $files->shouldReceive('exists')->once()->with(__DIR__.'/'.sha1('v2foo').'.php')->andReturn(false); $this->assertTrue($compiler->isExpired('foo')); } @@ -33,31 +33,31 @@ public function testCannotConstructWithBadCachePath() public function testIsExpiredReturnsTrueWhenModificationTimesWarrant() { $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); - $files->shouldReceive('exists')->once()->with(__DIR__.'/'.sha1('foo').'.php')->andReturn(true); + $files->shouldReceive('exists')->once()->with(__DIR__.'/'.sha1('v2foo').'.php')->andReturn(true); $files->shouldReceive('lastModified')->once()->with('foo')->andReturn(100); - $files->shouldReceive('lastModified')->once()->with(__DIR__.'/'.sha1('foo').'.php')->andReturn(0); + $files->shouldReceive('lastModified')->once()->with(__DIR__.'/'.sha1('v2foo').'.php')->andReturn(0); $this->assertTrue($compiler->isExpired('foo')); } public function testCompilePathIsProperlyCreated() { $compiler = new BladeCompiler($this->getFiles(), __DIR__); - $this->assertEquals(__DIR__.'/'.sha1('foo').'.php', $compiler->getCompiledPath('foo')); + $this->assertEquals(__DIR__.'/'.sha1('v2foo').'.php', $compiler->getCompiledPath('foo')); } public function testCompileCompilesFileAndReturnsContents() { $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); $files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World'); - $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>'); + $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>'); $compiler->compile('foo'); } public function testCompileCompilesAndGetThePath() { $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); $files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World'); - $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>'); + $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>'); $compiler->compile('foo'); $this->assertSame('foo', $compiler->getPath()); } @@ -73,7 +73,7 @@ public function testCompileWithPathSetBefore() { $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); $files->shouldReceive('get')->once()->with('foo')->andReturn('Hello World'); - $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>'); + $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('v2foo').'.php', 'Hello World<?php /**PATH foo ENDPATH**/ ?>'); // set path before compilation $compiler->setPath('foo'); // trigger compilation with $path @@ -103,7 +103,7 @@ public function testIncludePathToTemplate($content, $compiled) { $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); $files->shouldReceive('get')->once()->with('foo')->andReturn($content); - $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('foo').'.php', $compiled); + $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('v2foo').'.php', $compiled); $compiler->compile('foo'); } @@ -157,7 +157,7 @@ public function testDontIncludeEmptyPath() { $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); $files->shouldReceive('get')->once()->with('')->andReturn('Hello World'); - $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('').'.php', 'Hello World'); + $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('v2').'.php', 'Hello World'); $compiler->setPath(''); $compiler->compile(); } @@ -166,7 +166,7 @@ public function testDontIncludeNullPath() { $compiler = new BladeCompiler($files = $this->getFiles(), __DIR__); $files->shouldReceive('get')->once()->with(null)->andReturn('Hello World'); - $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1(null).'.php', 'Hello World'); + $files->shouldReceive('put')->once()->with(__DIR__.'/'.sha1('v2').'.php', 'Hello World'); $compiler->setPath(null); $compiler->compile(); }
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
12- github.com/advisories/GHSA-66hf-2p6w-jqfwghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-43808ghsaADVISORY
- github.com/FriendsOfPHP/security-advisories/blob/master/illuminate/view/CVE-2021-43808.yamlghsaWEB
- github.com/FriendsOfPHP/security-advisories/blob/master/laravel/framework/CVE-2021-43808.yamlghsaWEB
- github.com/laravel/framework/commit/b8174169b1807f36de1837751599e2828ceddb9bghsax_refsource_MISCWEB
- github.com/laravel/framework/pull/39906ghsax_refsource_MISCWEB
- github.com/laravel/framework/pull/39908ghsax_refsource_MISCWEB
- github.com/laravel/framework/pull/39909ghsax_refsource_MISCWEB
- github.com/laravel/framework/releases/tag/v6.20.42ghsax_refsource_MISCWEB
- github.com/laravel/framework/releases/tag/v7.30.6ghsax_refsource_MISCWEB
- github.com/laravel/framework/releases/tag/v8.75.0ghsax_refsource_MISCWEB
- github.com/laravel/framework/security/advisories/GHSA-66hf-2p6w-jqfwghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.