VYPR
Moderate severityNVD Advisory· Published Dec 7, 2021· Updated Aug 4, 2024

Blade `@parent` Exploitation Leading To Possible XSS in Laravel

CVE-2021-43808

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.

PackageAffected versionsPatched versions
laravel/frameworkPackagist
< 6.20.426.20.42
laravel/frameworkPackagist
>= 7.0.0, < 7.30.67.30.6
laravel/frameworkPackagist
>= 8.0.0, < 8.75.08.75.0
illuminate/viewPackagist
< 6.20.426.20.42
illuminate/viewPackagist
>= 7.0.0, < 7.30.67.30.6
illuminate/viewPackagist
>= 8.0.0, < 8.75.08.75.0

Affected products

3

Patches

1
b8174169b180

[6.x] Fix parent call (#39908)

https://github.com/laravel/frameworkDries VintsDec 6, 2021via ghsa
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

News mentions

0

No linked articles in our index yet.