CVE-2020-14067 (PHP, published 2020-06-15T01:15Z)
Description: value: The install_from_hash functionality in Navigate CMS 2.9 does not consider the .phtml extension when examining files within a ZIP archive that may contain PHP code, in check_upload in lib/packages/extensions/extension.class.php and lib/packages/themes/theme.class.php.
Severity: HIGH (CVSS v3 base score: 9.8)
Weakness: CWE-434 \u2014 Unrestricted Upload of File with Dangerous Type
The product allows the upload or transfer of dangerous file types that are automatically processed within its environment.
Repository: https://github.com/NavigateCMS/Navigate-CMS
Files changed:
lib/packages/extensions/extension.class.php
lib/packages/extensions/extensions.js
lib/packages/extensions/extensions.php
lib/packages/themes/theme.class.php
lib/packages/themes/themes.js
lib/packages/themes/themes.php
Commit message: * themes,extensions: secure "install_from_hash" functionality


Vulnerable code:
        $files = core_recursive_file_search($tempdir,  '/.*\/*.php/');
            // remove all spaces
// make executable the runnable extensions
$(".navigrid-item-buttonset[run='1'],.navigrid-item-buttonset[run='true']").parent().on("dblclick", function()
    // is this extension enabled?
    if($(this).find(".navigrid-item-buttonset").attr("enabled")=="0")
        return;

    var extension = $(this).find(".navigrid-item-buttonset").attr("extension");
    //location.href = NAVIGATE_APP + "?fid=extensions&act=run&extension=" + extension;
    location.href = NAVIGATE_APP + "?fid=ext_" + extension;
});
// open configuration on NON runnable extensions (if configuration is available)
$(".navigrid-item-buttonset[run='']").parent().on("dblclick", function()
{
    // is this extension enabled?
    if($(this).find(".navigrid-item-buttonset").attr("enabled")=="0")
        return;
    // has this extension a configuration option?
    if($(this).find('.navigrid-extensions-settings').length > 0)
        $(this).find('.navigrid-extensions-settings').trigger('click');
});
// show extension info window
$(".navigrid-extensions-info").bind("click", function()
{
    var extension = $(this).parent().attr("extension");
    $("#navigrid-extension-information").attr("title", $(this).parent().attr("extension-title"));
    $("#navigrid-extension-information").load("?fid=extensions&act=extension_info&extension=" + extension, function()
        $("#navigrid-extension-information").dialog(
            {
                width: 700,
                height: 500,
                modal: true,
                title: "<img src=\"img/icons/silk/information.png\" align=\"absmiddle\"> " + $("#navigrid-extension-information").attr("title")
            }).dialogExtend(
            {
                maximizable: true
            });
    });
});
// disable extension
$(".navigrid-extensions-disable").bind("click", function()
{
    var extension = $(this).parent().attr("extension");
    $.post(
        NAVIGATE_APP + "?fid=extensions&act=disable",
        { extension: extension },
        function(data)
            $("div#item-" + extension).find(".navigrid-extensions-enable").hide();
            $("div#item-" + extension).find(".navigrid-extensions-disable").hide();
            $("div#item-" + extension).find(".navigrid-extensions-remove").hide();
            if(data=="true")
            {
                $("div#item-" + extension).find(".navigrid-extensions-enable").show();
                $("div#item-" + extension).find(".navigrid-extensions-remove").show();
                $("div#item-" + extension).find(".navigrid-item-buttonset").attr("enabled", 1);
            }
            else
                $("div#item-" + extension).find(".navigrid-extensions-disable").show();
            }
            navigate_extensions_refresh();
        });
});
// enable extension
$(".navigrid-extensions-enable").bind("click", function()
{
    var extension = $(this).parent().attr("extension");
    $.post(
        NAVIGATE_APP + "?fid=extensions&act=enable",
        { extension: extension },
        function(data)
        {
            $("div#item-" + extension).find(".navigrid-extensions-enable").hide();
            $("div#item-" + extension).find(".navigrid-extensions-disable").hide();
            $("div#item-" + extension).find(".navigrid-extensions-remove").hide();
            if(data=="true")
            {
                $("div#item-" + extension).find(".navigrid-extensions-disable").show();
            }
            else
                $("div#item-" + extension).find(".navigrid-extensions-enable").show();
                $("div#item-" + extension).find(".navigrid-extensions-remove").show();
            }
            navigate_extensions_refresh();
        });
});
// add extension as favorite
$(".navigrid-extensions-favorite").bind("click", function()
{
    var extension = $(this).parent().attr("extension");
    var add_as_favorite = ($(this).parent().attr("favorite")==0);
    var el = this;

    $.post(
        NAVIGATE_APP + "?fid=extensions&act=favorite",
        { extension: extension,
            value: (add_as_favorite? 1 : 0)
        },
        function(data)
        {
            $(el).find("img").removeClass("silk-heart_add");
            $(el).find("img").removeClass("silk-heart_delete");
            if(data=="true")
                if(add_as_favorite)
                    $(el).parent().attr("favorite", 1);
                    $(el).find("img").addClass("silk-heart_delete");
                    $(el).parent().attr("favorite", 0);
                    $(el).find("img").addClass("silk-heart_add");
            }
            else
            {
                // show error
                navigate_notification(navigate_lang_dictionary[56]);
            }

            navigate_extensions_refresh();
        });
});

$(".navigrid-extensions-settings").on("click", function()
{
    var extension = $(this).parent().attr("extension");
    $("#navigrid-extension-options").attr("title", $(this).parent().attr("extension-title"));
    //$("#navigrid-extension-options").load("?fid=extensions&act=options&extension=" + extension, function()
    $("#navigrid-extension-options").html('<iframe width="100%" height="100%" frameborder="0" src="?fid=extensions&act=options&extension=' + extension + '"></iframe>');
    $("#navigrid-extension-options").dialog(
    {
        width: $(window).width() * 0.95,
        height: $(window).height() * 0.95,
        modal: true,
        title: "<img src=\"img/icons/silk/cog.png\" align=\"absmiddle\"> " + $("#navigrid-extension-options").attr("title")
    }).dialogExtend(
        maximizable: true
    });
});
$(".navigrid-extensions-remove").bind("click", function()
{
    var extension = $(this).parent().attr("extension");
    $("#navigrid-extensions-remove-confirmation").dialog(
            resizable: true,
            width: 300,
            height: 150,
            buttons:
                [
                    {
                        text: navigate_lang_dictionary[190],
                        click: function()
                            $.post(
                                NAVIGATE_APP + "?fid=extensions&act=remove&extension=" + extension,
                                { },
                                function(data)
                                {
                                    if(data=="true")
                                    {
                                        $("#item-" + extension).fadeOut("slow", function(){ $("#item-" + extension).remove(); });
                                    }
                                    else
                                        navigate_notification(navigate_lang_dictionary[56]);
                                }
                            );
                            $( this ).dialog( "close" );
                        }
                    },
                    {
                        text: navigate_lang_dictionary[58],
                        click: function()
                            $( this ).dialog( "close" );
                    }
                ]
        });
    return false;
});
$(".navigrid-extensions-update").on("click", function()
{
    var extension = $(this).parent().attr("extension");
    $("#navigrid-extensions-update").dialog(
            resizable: false,
            width: 980,
            height: 650,
            modal: true
        }
    );
    $("#navigrid-extensions-update").find('iframe').
        css({
            "width": "960",
            "height": 600
        }).
        attr('src', 'http://www.navigatecms.com/en/marketplace/purchase?extension='+extension+'&get_update')
    return false;
});
                $tmp_file = sys_get_temp_dir().DIRECTORY_SEPARATOR.$query['code'].'.zip';
                @core_file_curl($url, $tmp_file);
                if(@filesize($tmp_file) == 0)
                    @unlink($tmp_file);
                    // core file curl failed, try using file_get_contents...
                    $tmp = @file_get_contents($url);
                    if(!empty($tmp))
                    {
                        @file_put_contents($tmp_file, $tmp);
                    }
                    unset($tmp);
                if(@filesize($tmp_file) > 0)
                    // uncompress ZIP and copy it to the extensions dir
                    @mkdir(NAVIGATE_PATH.'/plugins/'.$query['code']);
                    $zip = new ZipArchive();
                    $zip_open_status = $zip->open($tmp_file);
                    if($zip_open_status === TRUE)
                        $zip->extractTo(NAVIGATE_PATH.'/plugins/'.$query['code']);
                        $zip->close();

                        $layout->navigate_notification(t(374, "Item installed successfully."), false);
                    else // zip extraction failed
                        $layout->navigate_notification('ERROR '.$zip_open_status, true, true);
                        $error = true;
                            title: "'.t(56, "Unexpected error").'"

        // don't break, we want to show the themes grid right now (theme_upload by browser upload won't trigger)

            $(window).on("load", function()
            {
                $(".navigrid-item-buttonset").each(function(i, el)
                {
                    $(el).hide().css("visibility", "visible");
                    $(el).fadeIn();
                    $(".navigrid-extensions-disable").addClass("ui-corner-right");
                });
            });

            function navitable_quicksearch(value)
            {
                $(".navigrid-item").hide();

                if(value=="")
                    $(".navigrid-item").show();
                else
                {
                    $(".navigrid-item").each(function(i, el)
                    {
                        var item_text = $(el).text().toLowerCase();
                        if( item_text.indexOf(value.toLowerCase()) >= 0 )
                            $(el).fadeIn();
                    });
                }
            }
            
            $("#extension-upload-button").on("click", function()
            {
                $("#extension-upload-button").parent().find("form").remove();
                $("#extension-upload-button").after(\'<form action="?fid=extensions&act=extension_upload" enctype="multipart/form-data" method="post"><input type="file" name="extension-upload" style=" display: none;" /><input type="hidden" id="_nv_csrf_token" name="_nv_csrf_token" value="\'+navigatecms.csrf_token+\'" /></form>\');
                $("#extension-upload-button").next().find("input").on("change", function()
                {
                    if($(this).val()!="")
                        $(this).parent().submit();
                });
                $("#extension-upload-button").next().find("input").trigger("click");

                return false;
            });

        $files = core_recursive_file_search($tempdir,  '/.*\/*.php/');
            // remove all spaces
        $("#theme-upload-button").after('<form action="?fid=themes&act=theme_upload" enctype="multipart/form-data" method="post"><input type="file" name="theme-upload" style=" display: none;" /><input type="hidden" id="_nv_csrf_token" name="_nv_csrf_token" value="'+navigatecms.csrf_token+'" /></form>');
                $tmp_file = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $query['code'] . '.zip';
                @core_file_curl($url, $tmp_file);
                if(@filesize($tmp_file) == 0)
                    @unlink($tmp_file);
                    // core file curl failed, try using file_get_contents...
                    $tmp = @file_get_contents($url);
                    if(!empty($tmp))
                    {
                        @file_put_contents($tmp_file, $tmp);
                    }
                    unset($tmp);
                if(@filesize($tmp_file) > 0)
                    // uncompress ZIP and copy it to the themes dir
                    @mkdir(NAVIGATE_PATH.'/themes/'.$query['code']);
                    $zip = new ZipArchive();
                    $zip_open_status = $zip->open($tmp_file);
                    if($zip_open_status === TRUE)
                        $zip->extractTo(NAVIGATE_PATH.'/themes/'.$query['code']);
                        $zip->close();

                        $layout->navigate_notification(t(374, "Item installed successfully."), false);
                    else // zip extraction failed
                        $layout->navigate_notification('ERROR '.$zip_open_status, true, true);
                        $error = true;
                            title: "'.t(56, "Unexpected error").'"
            // don't break, we want to show the themes grid right now (theme_upload by browser upload won't trigger)

Fixed code:
        $files = core_recursive_file_search($tempdir,  '/.*\/*.(php|phtml)/');
            if(!file_exists($file))
            {
                continue;
            }
            // remove all spaces
function navigate_extensions_init()
    // make executable the runnable extensions
    $(".navigrid-item-buttonset[run='1'],.navigrid-item-buttonset[run='true']").parent().on("dblclick", function()
    {
        // is this extension enabled?
        if($(this).find(".navigrid-item-buttonset").attr("enabled")=="0")
            return;
        var extension = $(this).find(".navigrid-item-buttonset").attr("extension");
        //location.href = NAVIGATE_APP + "?fid=extensions&act=run&extension=" + extension;
        location.href = NAVIGATE_APP + "?fid=ext_" + extension;
    });
    // open configuration on NON runnable extensions (if configuration is available)
    $(".navigrid-item-buttonset[run='']").parent().on("dblclick", function()
    {
        // is this extension enabled?
        if($(this).find(".navigrid-item-buttonset").attr("enabled")=="0")
            return;
        // has this extension a configuration option?
        if($(this).find('.navigrid-extensions-settings').length > 0)
            $(this).find('.navigrid-extensions-settings').trigger('click');
    });
    // show extension info window
    $(".navigrid-extensions-info").bind("click", function()
        var extension = $(this).parent().attr("extension");
        $("#navigrid-extension-information").attr("title", $(this).parent().attr("extension-title"));
        $("#navigrid-extension-information").load("?fid=extensions&act=extension_info&extension=" + extension, function()
            $("#navigrid-extension-information").dialog(
                {
                    width: 700,
                    height: 500,
                    modal: true,
                    title: "<img src=\"img/icons/silk/information.png\" align=\"absmiddle\"> " + $("#navigrid-extension-information").attr("title")
                }).dialogExtend(
                {
                    maximizable: true
                });
        });
    });
    // disable extension
    $(".navigrid-extensions-disable").on("click", function()
    {
        var extension = $(this).parent().attr("extension");
        $.post(
            NAVIGATE_APP + "?fid=extensions&act=disable",
            { extension: extension },
            function(data)
                $("div#item-" + extension).find(".navigrid-extensions-enable").hide();
                $("div#item-" + extension).find(".navigrid-extensions-disable").hide();
                $("div#item-" + extension).find(".navigrid-extensions-remove").hide();
                if(data=="true")
                {
                    $("div#item-" + extension).find(".navigrid-extensions-enable").show();
                    $("div#item-" + extension).find(".navigrid-extensions-remove").show();
                    $("div#item-" + extension).find(".navigrid-item-buttonset").attr("enabled", 1);
                }
                else
                {
                    $("div#item-" + extension).find(".navigrid-extensions-disable").show();
                }
                navigate_extensions_refresh();
            });
    });
    // enable extension
    $(".navigrid-extensions-enable").on("click", function()
    {
        var extension = $(this).parent().attr("extension");
        $.post(
            NAVIGATE_APP + "?fid=extensions&act=enable",
            { extension: extension },
            function(data)
                $("div#item-" + extension).find(".navigrid-extensions-enable").hide();
                $("div#item-" + extension).find(".navigrid-extensions-disable").hide();
                $("div#item-" + extension).find(".navigrid-extensions-remove").hide();
                if(data=="true")
                {
                    $("div#item-" + extension).find(".navigrid-extensions-disable").show();
                }
                else
                {
                    $("div#item-" + extension).find(".navigrid-extensions-enable").show();
                    $("div#item-" + extension).find(".navigrid-extensions-remove").show();
                }
                navigate_extensions_refresh();
            });
    });
    // add extension as favorite
    $(".navigrid-extensions-favorite").on("click", function()
    {
        var extension = $(this).parent().attr("extension");
        var add_as_favorite = ($(this).parent().attr("favorite")==0);
        var el = this;

        $.post(
            NAVIGATE_APP + "?fid=extensions&act=favorite",
            { extension: extension,
                value: (add_as_favorite? 1 : 0)
            },
            function(data)
                $(el).find("img").removeClass("silk-heart_add");
                $(el).find("img").removeClass("silk-heart_delete");

                if(data=="true")
                    if(add_as_favorite)
                    {
                        $(el).parent().attr("favorite", 1);
                        $(el).find("img").addClass("silk-heart_delete");
                    }
                    else
                    {
                        $(el).parent().attr("favorite", 0);
                        $(el).find("img").addClass("silk-heart_add");
                    }
                    // show error
                    navigate_notification(navigate_lang_dictionary[56]);
                navigate_extensions_refresh();
            });
    });
    $(".navigrid-extensions-settings").on("click", function()
        var extension = $(this).parent().attr("extension");
        $("#navigrid-extension-options").attr("title", $(this).parent().attr("extension-title"));
        //$("#navigrid-extension-options").load("?fid=extensions&act=options&extension=" + extension, function()
        $("#navigrid-extension-options").html('<iframe width="100%" height="100%" frameborder="0" src="?fid=extensions&act=options&extension=' + extension + '"></iframe>');
        $("#navigrid-extension-options").dialog(
            width: $(window).width() * 0.95,
            height: $(window).height() * 0.95,
            title: "<img src=\"img/icons/silk/cog.png\" align=\"absmiddle\"> " + $("#navigrid-extension-options").attr("title")
        }).dialogExtend(
        {
            maximizable: true
        });
    });

    $(".navigrid-extensions-remove").bind("click", function()
    {
        var extension = $(this).parent().attr("extension");

        $("#navigrid-extensions-remove-confirmation").dialog(
            {
                resizable: true,
                width: 300,
                height: 150,
                modal: true,
                buttons:
                    [
                            text: navigate_lang_dictionary[190],
                            click: function()
                            {
                                $.post(
                                    NAVIGATE_APP + "?fid=extensions&act=remove&extension=" + extension,
                                    { },
                                    function(data)
                                        if(data=="true")
                                        {
                                            $("#item-" + extension).fadeOut("slow", function(){ $("#item-" + extension).remove(); });
                                        }
                                        else
                                        {
                                            navigate_notification(navigate_lang_dictionary[56]);
                                        }
                                );
                                $( this ).dialog( "close" );
                            }
                        },
                            text: navigate_lang_dictionary[58],
                            click: function()
                            {
                                $( this ).dialog( "close" );
                            }
                    ]
            });
        return false;
    });
    $(".navigrid-extensions-update").on("click", function()
    {
        var extension = $(this).parent().attr("extension");

        $("#navigrid-extensions-update").dialog(
            {
                resizable: false,
                width: 980,
                height: 650,
                modal: true
            }
        );

        $("#navigrid-extensions-update").find('iframe').
            css({
                "width": "960",
                "height": 600
            }).
            attr('src', 'http://www.navigatecms.com/en/marketplace/purchase?extension='+extension+'&get_update')

        return false;
    });

    $(".navigrid-item-buttonset").each(function(i, el)
    {
        $(el).hide().css("visibility", "visible");
        $(el).fadeIn();
        $(".navigrid-extensions-disable").addClass("ui-corner-right");
    });

    $("#extension-upload-button").on("click", function()
    {
        $("#extension-upload-button").parent().find("form").remove();
        $("#extension-upload-button").after('<form action="?fid=extensions&act=extension_upload" enctype="multipart/form-data" method="post"></form>');
        $("#extension-upload-button").next().append('<input type="hidden" id="_nv_csrf_token" name="_nv_csrf_token" value="'+navigatecms.csrf_token+'" />');
        $("#extension-upload-button").next().append('<input type="file" name="extension-upload" style=" display: none;" />');
        $("#extension-upload-button").next().find("input").on("change", function()
            if($(this).val()!="")
            {
                $(this).parent().submit();
            }
        });
        $("#extension-upload-button").next().find("input").trigger("click");
        return false;
    });
}

function navitable_quicksearch(value)
{
    $(".navigrid-item").hide();
    if(value=="")
        $(".navigrid-item").show();
    else
    {
        $(".navigrid-item").each(function(i, el)
        {
            var item_text = $(el).text().toLowerCase();
            if( item_text.indexOf(value.toLowerCase()) >= 0 )
                $(el).fadeIn();
        });
    }
}
                $tmp_file = NULL;

                if(parse_url($url, PHP_URL_HOST) == 'www.navigatecms.com')
                    $tmp_file = sys_get_temp_dir().DIRECTORY_SEPARATOR.$query['code'].'.zip';
                    @core_file_curl($url, $tmp_file);
                if(!empty($tmp_file) && @filesize($tmp_file) > 0)
                    $secure = extension::check_upload(
                        array(
                            'type' => mime_content_type($tmp_file),
                            'name' => $query['code'].'.zip',
                            'tmp_name' => $tmp_file
                        ),
                        $query['code']
                    );
                    if($secure !== true)
                        $layout->navigate_notification(t(344, 'Security error'), true, true);
                        $error = true;
                    else // everything seems fine, go ahead
                        // uncompress ZIP and copy it to the extensions dir
                        @mkdir(NAVIGATE_PATH.'/plugins/'.$query['code']);

                        $zip = new ZipArchive();
                        $zip_open_status = $zip->open($tmp_file);
                        if($zip_open_status === TRUE)
                        {
                            $zip->extractTo(NAVIGATE_PATH.'/plugins/'.$query['code']);
                            $zip->close();

                            $layout->navigate_notification(t(374, "Item installed successfully."), false);
                        }
                        else // zip extraction failed
                        {
                            $layout->navigate_notification('ERROR '.$zip_open_status, true, true);
                            $error = true;
                        }
                            title: "'.t(56, "Unexpected error").'",
                            close: function()
                            {
                                window.location.replace("?fid=extensions");
                            }
                else
                {
                    // redirect to extensions grid
                    core_terminate('?fid=extensions');
                }
            break;
                    navigate_extensions_init();
        $files = core_recursive_file_search($tempdir,  '/.*\/*.(php|phtml)/');
            if(!file_exists($file))
            {
                continue;
            }
            // remove all spaces
        $("#theme-upload-button").after('<form action="?fid=themes&act=theme_upload" enctype="multipart/form-data" method="post"></form>');
        $("#theme-upload-button").next().append('<input type="hidden" id="_nv_csrf_token" name="_nv_csrf_token" value="'+navigatecms.csrf_token+'" />');
        $("#theme-upload-button").next().append('<input type="file" name="theme-upload" style=" display: none;" />');
                return false;
                $tmp_file = NULL;

                if(parse_url($url, PHP_URL_HOST) == 'www.navigatecms.com')
                    $tmp_file = sys_get_temp_dir().DIRECTORY_SEPARATOR.$query['code'].'.zip';
                    @core_file_curl($url, $tmp_file);
                if(!empty($tmp_file) && @filesize($tmp_file) > 0)
                    $secure = theme::check_upload(
                        array(
                            'type' => mime_content_type($tmp_file),
                            'name' => $query['code'].'.zip',
                            'tmp_name' => $tmp_file
                        ),
                        $query['code']
                    );
                    if($secure !== true)
                        $layout->navigate_notification(t(344, 'Security error'), true, true);
                        $error = true;
                    else // everything seems fine, go ahead
                        // uncompress ZIP and copy it to the themes dir
                        @mkdir(NAVIGATE_PATH.'/themes/'.$query['code']);

                        $zip = new ZipArchive();
                        $zip_open_status = $zip->open($tmp_file);
                        if($zip_open_status === TRUE)
                        {
                            $zip->extractTo(NAVIGATE_PATH.'/themes/'.$query['code']);
                            $zip->close();

                            $layout->navigate_notification(t(374, "Item installed successfully."), false);
                        }
                        else // zip extraction failed
                        {
                            $layout->navigate_notification('ERROR '.$zip_open_status, true, true);
                            $error = true;
                        }
                            title: "'.t(56, "Unexpected error").'",
                            close: function()
                            {
                                window.location.replace("?fid=themes");
                            }
                else
                {
                    // redirect to themes grid
                    core_terminate('?fid=themes');
                }
            break;