Great tutorials. Just a quick note, you make a couple of declarations of $nop2 when it isn’t used in the script. Just a small housecleaning thing. Thanks.
Some notes about the overflow. In the beginning of the vulnerable function you can see that it reserves 0x8918 bytes of memory.: 0041E2B0 B8 18890000 mov eax, 8918 0041E2B5 E8 C6940100 call RM2MP3Co.00437780 Then the overflow occurs later when it calls the function Playlist_FindNextItem exported from MSRMfilter03.dll: 0041E3E8 8D8C24 28230000 lea ecx, dword ptr ss:[esp+2328] 0041E3EF 51 push ecx 0041E3F0 FF93 72640000 call dword ptr ds:[ebx+6472]; <– Playlist_FindNextItem From 8918 and from the reference esp+2328 you can estimate the buffer size, it is 8918-2318 = 65F0, however this is not the correct size because there are some gaps between. The exact buffer size to reach the limit is 0x6600 bytes (26112 in decimal). Although creating an m3u file with exactly this file size should not crash the application, right? No. It will crash anyway, because the program not just reads the whole "AAAAAAAAAAAA" from the m3u, but also appends the current working directory where the m3u file was placed. The buffer will look like something like this: buffer begin..c:\test\AAAAAAAAAAAAAAAAAAA..shellcode…end of buffer..return address Take care of the offsets it may change depending on the current directory 😉
Nice tutorial! I think at the beginning of ur article at blind return description –> “A RET instruction will ‘pop’ the last value (4bytes) from the stack and will put that address in ESP” . I think should be “will put that address in EIP” rite? Because ret = pop eip + jmp eip.
Peter, Where does 281 come from? You mentioned the goal is to jump to esp+281. This is in the section “Dealing with small buffers : jumping anywhere with custom jumpcode”.
281 is just a number, for the sake of the exercise The idea is to use a small amount of shellcode to jump over a block of garbage to reach the final shellcode
Two corrections at the beginning of the article (“blind return”): 1. “…A RET instruction will ‘pop’ the last value (4bytes) from the stack and will put that address in ESP.” -> should be EIP instead od ESP (I see other readers commented on this also) – could you please correct the article itself (for new readers not to get confused too much) ? 2. “So if you overwrite EIP with the address that will perform a RET instruction …” -> should be “So if you overwrite return address …”. Bye!
Once you jump to your shellcode, you often need to know what address EIP actually is. The stack is usually not at the same position. Here’s the easiest way to do it: call getip getip: pop eax ; eax now contains eip The problem is that the above code compiles to E8 00 00 00 00 58, which is useless if you can’t have null bytes. My solution is the following: xor eax, eax getip: cmp eax, -1 je popip mov eax, -1 call getip popip: pop eax The above compiles to: 33 C0 83 F8 FF 74 0A B8 FF FF FF FF E8 F1 FF FF FF 58. It’s longer, but no nulls! You can then xor any null bytes in your code with FFFFFFFF and use xor dword ptr [eax + ], FFFFFFFF to patch them back at runtime.
Amazing tutorial. There’s something I don’t understand though. Why is it that when we dump ESP, we see the XXXX’s the NOPs and then the AAAA’s ? (from the section “Dealing with small buffers : jumping anywhere with custom jumpcode”). How did the A’s get there ? I thought that at the moment of overwriting the buffer, the array is layed out this way : |local vars|saved EBP|saved EIP|params , Thanks,
Awesome tutorials! One qustion though: What about directly putting the (approximate) address of the shellcode in eip? I tried it on the Easy RM To MP3 Converter and it worked, so my payload looks something like this: [ ~20000 * 0x90 ] [ Shellcode ] [ some 0x41 to fill remaining space ] [ 0x0001952c ] ^ somewhere here is 0x0001952c Its basically like the blind return, but without the extra jump. Are there any drawbacks of this?
yes, that address is most likely from the stack or heap, and you can’t rely on the fact that this address would be reliable on other systems. Especially with multi-threaded applications, it would be very unlikely to always hit the same thread (and thus the same stack address) every time.