@@ -84,9 +84,14 @@ def check
8484 )
8585 return CheckCode ::Unknown ( 'Connection failed' ) unless res
8686
87- unless res . body . include? ( 'Grav' ) || res . body . include? ( 'grav' )
88- return CheckCode ::Safe ( 'Target is not a Grav CMS installation' )
89- end
87+ html = res . get_html_document
88+ return CheckCode ::Unknown ( 'Could not parse HTML' ) unless html
89+
90+ # First, verify this is a Grav installation
91+ return CheckCode ::Safe ( 'Target does not appear to be a Grav installation' ) unless grav_installation? ( html )
92+
93+ # Then verify we have access to the login form
94+ return CheckCode ::Detected ( 'Grav detected but login form not accessible' ) unless login_form_present? ( html )
9095
9196 version_str = get_version_after_login
9297 unless version_str
@@ -120,6 +125,26 @@ def exploit
120125
121126 private
122127
128+ def grav_installation? ( html )
129+ # Check for Grav-specific data attributes
130+ grav_checks = [
131+ html . at ( '//*[@data-gpm-grav]' ) ,
132+ html . at ( '//*[@data-grav-field]' ) ,
133+ html . at ( '//*[@data-grav-disabled]' ) ,
134+ html . at ( '//*[@data-grav-default]' )
135+ ]
136+
137+ grav_checks . count { |elem | !elem . nil? } >= 2
138+ end
139+
140+ def login_form_present? ( html )
141+ # Check for the specific login form inputs we need
142+ username_input = html . at ( 'input[@name="data[username]"]' )
143+ password_input = html . at ( 'input[@name="data[password]"]' )
144+
145+ username_input && password_input
146+ end
147+
123148 def get_version_after_login
124149 result = authenticate
125150 return nil unless result == :success || result == :already_authenticated
0 commit comments