Skip to content

Commit d17dc18

Browse files
committed
Strengthen Grav CMS fingerprinting with strict HTML parsing
1 parent 3c4fdfc commit d17dc18

File tree

1 file changed

+28
-3
lines changed

1 file changed

+28
-3
lines changed

modules/exploits/multi/http/grav_twig_ssti_sandbox_bypass_rce.rb

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)