CVE-2018-15536
Description
/filemanager/ajax_calls.php in tecrail Responsive FileManager before 9.13.4 does not properly validate file paths in archives, allowing for the extraction of crafted archives to overwrite arbitrary files via an extract action, aka Directory Traversal.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Responsive FileManager before 9.13.4 fails to validate file paths in archives, allowing directory traversal during extraction to overwrite arbitrary files.
Vulnerability
The vulnerability resides in /filemanager/ajax_calls.php when handling the extract action. The code does not validate file paths contained within uploaded archives, allowing directory traversal sequences (e.g., ../../tmp/source.txt) to be processed. This affects tecrail Responsive FileManager versions before 9.13.4 [1].
Exploitation
An attacker must be an authenticated user with the ability to upload archives via the file manager interface. The attacker crafts a ZIP archive containing entries with path traversal payloads. A POST request is sent to /filemanager/ajax_calls.php?action=extract with the path parameter pointing to the uploaded archive. The server extracts the archive without sanitizing paths, writing files to arbitrary locations on the filesystem [1].
Impact
Successful exploitation allows an authenticated attacker to overwrite arbitrary files on the server. This could lead to code execution (e.g., overwriting PHP files in web-accessible directories) or denial of service. The provided example demonstrates writing to /tmp/source.txt, but the impact depends on the target file's location and permissions [1].
Mitigation
The vulnerability is fixed in version 9.13.4, released on or before August 24, 2018. Users should upgrade to this version or later. No workarounds are documented. The CVE is not listed on the CISA Known Exploited Vulnerabilities catalog [1].
AI Insight generated on May 26, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
2- Range: 9.9, 9.9.1, 9.9.2, …
- Range: < 9.13.4
Patches
110 files changed · +133 −93
changelog.txt+14 −0 modified@@ -1,5 +1,19 @@ Responsive Filemanager Changelog +********************************************************* +* RFM 9.13.4 +********************************************************* +- fix Directory Traversal Allows to Read Any File (thanks to Simon Uvarov for reporting) +- fix Path Traversal While Upacking Archives (thanks to Simon Uvarov for reporting) +- Fix foreach warning on URL upload +- Fix http https URL upload +- add toggle on config for extract_files +- prevent image creation for broken links in URL upload (thanks to davodavodavo3) +- Migrate to yarn on development (thanks to mklkj) +- code refactoring + + + ********************************************************* * RFM 9.13.3 *********************************************************
filemanager/ajax_calls.php+34 −43 modified@@ -23,6 +23,21 @@ response(trans('Lang_Not_Found').AddErrorLocation())->send(); exit; } + + +//check $_GET['file'] +if(isset($_GET['file']) && !checkRelativePath($_GET['file'])) { + response(trans('wrong path'))->send(); + exit; +} + +//check $_GET['file'] +if(isset($_GET['path']) && !checkRelativePath($_GET['path'])) { + response(trans('wrong path'))->send(); + exit; +} + + $ftp = ftp_con($config); if(isset($_GET['action'])) @@ -83,12 +98,7 @@ case 'save_img': $info = pathinfo($_POST['name']); - if ( - strpos($_POST['path'], '/') === 0 - || strpos($_POST['path'], '../') !== false - || strpos($_POST['path'], '..\\') !== false - || strpos($_POST['path'], './') === 0 - || (strpos($_POST['url'], 'http://s3.amazonaws.com/feather') !== 0 && strpos($_POST['url'], 'https://s3.amazonaws.com/feather') !== 0) + if ((strpos($_POST['url'], 'http://s3.amazonaws.com/feather') !== 0 && strpos($_POST['url'], 'https://s3.amazonaws.com/feather') !== 0) || $_POST['name'] != fix_filename($_POST['name'], $config) || ! in_array(strtolower($info['extension']), array( 'jpg', 'jpeg', 'png' )) ) @@ -135,15 +145,9 @@ } break; case 'extract': - if ( strpos($_POST['path'], '/') === 0 - || strpos($_POST['path'], '../') !== false - || strpos($_POST['path'], '..\\') !== false - || strpos($_POST['path'], './') === 0) - { - response(trans('wrong path'.AddErrorLocation()))->send(); - exit; + if(!$config['extract_files']){ + response(trans('wrong action'))->send(); } - if($ftp){ $path = $config['ftp_base_url'].$config['upload_dir'] . $_POST['path']; $base_folder = $config['ftp_base_url'].$config['upload_dir'] . fix_dirname($_POST['path']) . "/"; @@ -186,28 +190,24 @@ exit; } - //make all the folders - for ($i = 0; $i < $zip->numFiles; $i++) - { - $OnlyFileName = $zip->getNameIndex($i); - $FullFileName = $zip->statIndex($i); - if (substr($FullFileName['name'], -1, 1) == "/") - { - create_folder($base_folder . $FullFileName['name']); - } - } - //unzip into the folders + //make all the folders and unzip into the folders for ($i = 0; $i < $zip->numFiles; $i++) { - $OnlyFileName = $zip->getNameIndex($i); $FullFileName = $zip->statIndex($i); - if ( ! (substr($FullFileName['name'], -1, 1) == "/")) - { - $fileinfo = pathinfo($OnlyFileName); - if (in_array(strtolower($fileinfo['extension']), $config['ext'])) + if(checkRelativePath($FullFileName['name'])){ + if (substr($FullFileName['name'], -1, 1) == "/") { - copy('zip://' . $path . '#' . $OnlyFileName, $base_folder . $FullFileName['name']); + create_folder($base_folder . $FullFileName['name']); + } + + if ( ! (substr($FullFileName['name'], -1, 1) == "/")) + { + $fileinfo = pathinfo($FullFileName['name']); + if (in_array(strtolower($fileinfo['extension']), $config['ext'])) + { + copy('zip://' . $path . '#' . $FullFileName['name'], $base_folder . $FullFileName['name']); + } } } } @@ -232,7 +232,7 @@ $phar = new PharData($path); $phar->decompressFiles(); $files = array(); - check_files_extensions_on_phar($phar, $files, '', $config['ext']); + check_files_extensions_on_phar($phar, $files, '', $config); $phar->extractTo($base_folder, $files, true); break; @@ -365,16 +365,7 @@ case 'copy_cut': if ($_POST['sub_action'] != 'copy' && $_POST['sub_action'] != 'cut') { - response(trans('wrong sub-action').AddErrorLocation())->send(); - exit; - } - - if (strpos($_POST['path'],'../') !== FALSE - || strpos($_POST['path'],'./') !== FALSE - || strpos($_POST['path'],'..\\') !== FALSE - || strpos($_POST['path'],'.\\') !== FALSE ) - { - response(trans('wrong path'.AddErrorLocation()))->send(); + response(trans('wrong sub-action'))->send(); exit; } @@ -611,7 +602,7 @@ if ($sub_action != 'preview' && $sub_action != 'edit') { - response(trans('wrong action').AddErrorLocation())->send(); + response(trans('wrong action'))->send(); exit; }
filemanager/config/config.php+1 −0 modified@@ -329,6 +329,7 @@ 'rename_files' => true, 'rename_folders' => true, 'duplicate_files' => true, + 'extract_files' => true, 'copy_cut_files' => true, // for copy/cut files 'copy_cut_dirs' => true, // for copy/cut directories 'chmod_files' => true, // change file permissions
filemanager/dialog.php+8 −13 modified@@ -47,15 +47,8 @@ }elseif(isset($_SESSION['RF']['fldr']) && !empty($_SESSION['RF']['fldr'])){ $subdir_path = rawurldecode(trim(strip_tags($_SESSION['RF']['fldr']),"/")); } -$subdir_path_decoded = urldecode($subdir_path); -if (strpos($subdir_path,'../') === FALSE - && strpos($subdir_path,'./') === FALSE - && strpos($subdir_path,'..\\') === FALSE - && strpos($subdir_path,'.\\') === FALSE - && strpos($subdir_path_decoded,'../') === FALSE - && strpos($subdir_path_decoded,'./') === FALSE - && strpos($subdir_path_decoded,'..\\') === FALSE - && strpos($subdir_path_decoded,'.\\') === FALSE) + +if ( checkRelativePath($subdir_path)) { $subdir = strip_tags($subdir_path) ."/"; $_SESSION['RF']['fldr'] = $subdir_path; @@ -99,9 +92,10 @@ } $rfm_subfolder = ''; -if (!empty($_SESSION['RF']["subfolder"]) && strpos($_SESSION['RF']["subfolder"],'../') === FALSE && strpos($_SESSION['RF']["subfolder"],'..\\') === FALSE -&& strpos($_SESSION['RF']["subfolder"],'./') === FALSE && strpos($_SESSION['RF']["subfolder"],"/") !== 0 -&& strpos($_SESSION['RF']["subfolder"],'.') === FALSE) +if (!empty($_SESSION['RF']["subfolder"]) + && strpos($_SESSION['RF']["subfolder"],"/") !== 0 + && strpos($_SESSION['RF']["subfolder"],'.') === FALSE +) { $rfm_subfolder = $_SESSION['RF']['subfolder']; } @@ -257,7 +251,7 @@ $ext_tmp = array(); foreach($extensions as $extension){ $extension = fix_strtolower($extension); - if(in_array( $extension, $config['ext'])){ + if(check_file_extension( $extension, $config)){ $ext_tmp[]=$extension; } } @@ -482,6 +476,7 @@ <input type="hidden" id="lang_error_upload" value="<?php echo trans('Error_Upload');?>" /> <input type="hidden" id="lang_select" value="<?php echo trans('Select');?>" /> <input type="hidden" id="lang_extract" value="<?php echo trans('Extract');?>" /> + <input type="hidden" id="extract_files" value="<?php if($config['extract_files']) echo 1; else echo 0;?>" /> <input type="hidden" id="transliteration" value="<?php echo $config['transliteration']?"true":"false";?>" /> <input type="hidden" id="convert_spaces" value="<?php echo $config['convert_spaces']?"true":"false";?>" /> <input type="hidden" id="replace_with" value="<?php echo $config['convert_spaces']? $config['replace_with'] : "";?>" />
filemanager/execute.php+4 −8 modified@@ -9,13 +9,9 @@ exit; } -if (strpos($_POST['path'],'/')===0 - || strpos($_POST['path'],'../')!==FALSE - || strpos($_POST['path'],'./')===0 - || strpos($_POST['path'],'..\\')!==FALSE - || strpos($_POST['path'],'.\\')===0) +if (!checkRelativePath($_POST['path'])) { - response(trans('wrong path'.AddErrorLocation()))->send(); + response(trans('wrong path'))->send(); exit; } @@ -373,7 +369,7 @@ function returnPaths($_path,$_name,$config){ // something terribly gone wrong if ($action != 'copy' && $action != 'cut'){ - response(trans('wrong action').AddErrorLocation())->send(); + response(trans('wrong action'))->send(); exit; } if($ftp){ @@ -514,7 +510,7 @@ function returnPaths($_path,$_name,$config){ break; default: - response(trans('wrong action').AddErrorLocation())->send(); + response(trans('wrong action'))->send(); exit; } }
filemanager/force_download.php+3 −7 modified@@ -12,20 +12,16 @@ } -if ( +if (!checkRelativePath($_POST['path']) || strpos($_POST['path'], '/') === 0 - || strpos($_POST['path'], '../') !== false - || strpos($_POST['path'], './') === 0 - || strpos($_POST['path'], '..\\') !== false - || strpos($_POST['path'], '.\\') === 0 ) { - response(trans('wrong path' . AddErrorLocation()), 400)->send(); + response(trans('wrong path'), 400)->send(); exit; } if (strpos($_POST['name'], '/') !== false) { - response(trans('wrong path' . AddErrorLocation()), 400)->send(); + response(trans('wrong path' ), 400)->send(); exit; }
filemanager/include/utils.php+57 −3 modified@@ -94,6 +94,32 @@ function trans($var) } } + + + +/** +* Check relative path +* +* @param string $path +* +* @return boolean is it correct? +*/ +function checkRelativePath($path){ + $path_correct = true; + $path_decoded = rawurldecode($path); + if (strpos($path, '../') !== false + || strpos($path, './') !== false + || strpos($path, '..\\') !== false + || strpos($path, '.\\') !== false + || strpos($path_decoded, '../') !== false + || strpos($path_decoded, './') !== false + || strpos($path_decoded, '..\\') !== false + || strpos($path_decoded, '.\\') !== false) { + $path_correct = false; + } + return $path_correct; +} + /** * Delete file * @@ -567,6 +593,34 @@ function check_files_extensions_on_path($path, $ext) } } + +/** +* Check file extension +* +* @param string $extension +* @param array $config +*/ + +function check_file_extension($extension,$config){ + $check = false; + if (!$config['ext_blacklist']) { + if(in_array(mb_strtolower($extension), $conf['ext'])){ + $check = true; + } + } else { + if(!in_array(mb_strtolower($extension), $conf['ext_blacklist'])){ + $check = true; + } + } + + if($config['files_without_extension'] && $extension == ''){ + $check = true; + } + + return $check; +} + + /** * Get file extension present in PHAR file * @@ -575,13 +629,13 @@ function check_files_extensions_on_path($path, $ext) * @param string $basepath * @param string $ext */ -function check_files_extensions_on_phar($phar, &$files, $basepath, $ext) +function check_files_extensions_on_phar($phar, &$files, $basepath, $config) { foreach ($phar as $file) { if ($file->isFile()) { - if (in_array(mb_strtolower($file->getExtension()), $ext)) + if (check_file_extension($file->getExtension())) { $files[] = $basepath . $file->getFileName(); } @@ -591,7 +645,7 @@ function check_files_extensions_on_phar($phar, &$files, $basepath, $ext) if ($file->isDir()) { $iterator = new DirectoryIterator($file); - check_files_extensions_on_phar($iterator, $files, $basepath . $file->getFileName() . '/', $ext); + check_files_extensions_on_phar($iterator, $files, $basepath . $file->getFileName() . '/', $config); } } }
filemanager/upload.php+2 −5 modified@@ -34,11 +34,8 @@ $fldr = rawurldecode(trim(strip_tags($_POST['fldr']), "/") . "/"); - if (strpos($fldr, '../') !== false - || strpos($fldr, './') !== false - || strpos($fldr, '..\\') !== false - || strpos($fldr, '.\\') !== false) { - response(trans('wrong path' . AddErrorLocation()))->send(); + if (!checkRelativePath($fldr)) { + response(trans('wrong path'))->send(); exit; }
gulpfile.js+0 −5 modified@@ -88,9 +88,4 @@ elixir(function (mix) { ['modernizr.custom.js'], 'filemanager/js/modernizr.custom.js' ); - - mix.scripts( - ['load_more.js'], - 'filemanager/js/load_more.js' - ); });
resources/assets/js/include.js+10 −9 modified@@ -3,7 +3,7 @@ var encodeURL,show_animation,hide_animation,apply,apply_none,apply_img,apply_any { "use strict"; - var version = "9.13.3"; + var version = "9.13.4"; var active_contextmenu = true; var myLazyLoad = null; var clipboard = null; @@ -239,9 +239,10 @@ var encodeURL,show_animation,hide_animation,apply,apply_none,apply_img,apply_any disabled: false }; // extract - if ($trigger.find('.img-precontainer-mini .filetype').hasClass('zip') || + if (($trigger.find('.img-precontainer-mini .filetype').hasClass('zip') || $trigger.find('.img-precontainer-mini .filetype').hasClass('tar') || - $trigger.find('.img-precontainer-mini .filetype').hasClass('gz')) + $trigger.find('.img-precontainer-mini .filetype').hasClass('gz')) && + jQuery('#extract_files').val() == 1) { options.items.unzip = { name: jQuery('#lang_extract').val(), @@ -840,7 +841,7 @@ var encodeURL,show_animation,hide_animation,apply,apply_none,apply_img,apply_any // info btn jQuery('#info').on('click', function () { - bootbox.alert('<div class="text-center"><br/><img src="img/logo.png" alt="responsive filemanager"/><br/><br/><p><strong>RESPONSIVE filemanager v.' + version + '</strong><br/><a href="http://www.responsivefilemanager.com">responsivefilemanager.com</a></p><br/><p>Copyright © <a href="http://www.tecrail.com" alt="tecrail">Tecrail</a> - Alberto Peripolli. All rights reserved.</p><br/><p>License<br/><small><img alt="Creative Commons License" style="border-width:0" src="http://responsivefilemanager.com/license.php" /><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc/3.0/">Creative Commons Attribution-NonCommercial 3.0 Unported License</a>.</small></p></div>'); + bootbox.alert('<div class="text-center"><br/><img src="img/logo.png" alt="responsive filemanager"/><br/><br/><p><strong>RESPONSIVE filemanager v.' + version + '</strong><br/><a href="http://www.responsivefilemanager.com">responsivefilemanager.com</a></p><br/><p>Copyright © <a href="http://www.tecrail.com" alt="tecrail">Tecrail</a> - Alberto Peripolli. All rights reserved.</p><br/><p>License<br/><small><img alt="Creative Commons License" style="border-width:0" src="https://www.responsivefilemanager.com/license.php" /><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-nc/3.0/">Creative Commons Attribution-NonCommercial 3.0 Unported License</a>.</small></p></div>'); }); jQuery('#change_lang_btn').on('click', function () @@ -982,11 +983,10 @@ var encodeURL,show_animation,hide_animation,apply,apply_none,apply_img,apply_any }); var getFiles = function(path){ var files = []; - var subdir = jQuery('#subdir').val(); jQuery('.selection:checkbox:checked:visible').each(function () { var file = jQuery(this).val(); if(path){ - file = subdir + file; + file = jQuery(this).closest('figure').attr('data-path'); } files.push(file); }); @@ -1017,6 +1017,7 @@ var encodeURL,show_animation,hide_animation,apply,apply_none,apply_img,apply_any if (result == true) { var files = getFiles(true); + execute_multiple_action('delete_files', files, '', '', ''); var fil = jQuery('#files_number'); fil.text(parseInt(fil.text())-files.length); @@ -1752,8 +1753,8 @@ var encodeURL,show_animation,hide_animation,apply,apply_none,apply_img,apply_any function returnUrls(files){ var path = jQuery('#cur_dir').val(); path = path.replace('\\', '/'); - var subdir = jQuery('#subdir').val(); - subdir = subdir.replace('\\', '/'); + var sub_folder = jQuery('#sub_folder').val(); + sub_folder = sub_folder.replace('\\', '/'); var base_url = jQuery('#base_url').val(); var urls=[]; var is_return_relative_url = jQuery('#return_relative_url').val(); @@ -1763,7 +1764,7 @@ var encodeURL,show_animation,hide_animation,apply,apply_none,apply_img,apply_any if(is_ftp){ urls.push(encodeURL(jQuery('#ftp_base_url').val() + jQuery('#upload_dir').val() + jQuery('#fldr_value').val() + file)); }else{ - urls.push(encodeURL((is_return_relative_url == 1 ? subdir : base_url + path) + file)); + urls.push(encodeURL((is_return_relative_url == 1 ? sub_folder : base_url + path) + file)); } } return urls;
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2- www.exploit-db.com/exploits/45271/mitreexploitx_refsource_EXPLOIT-DB
- seclists.org/fulldisclosure/2018/Aug/34mitremailing-listx_refsource_FULLDISC
News mentions
0No linked articles in our index yet.