
Exploit per CVE-2022-25173 che mira al bypass della sandbox dell'interprete CPS del Plugin Jenkins Pipeline Groovy, consentendo l'esecuzione arbitraria del codice all'interno del controller Jenkins tramite script Pipeline appositamente predisposti.
Componente chiave della suite di plugin Pipeline, fornisce il motore di esecuzione standard per gli step di Pipeline, basato su un interprete Groovy personalizzato che viene eseguito all'interno del processo controller di Jenkins.
(In linea di principio potrebbero essere supportati altri motori di esecuzione, con FlowDefinition come punto di ingresso dell'API, ma nessuno è stato prototipato e scriverne uno richiederebbe probabilmente uno sforzo molto sostanziale.)
Il codice Groovy di Pipeline come
retry(3) {
for (int i = 0; i < 10; i++) {
branches["branch${i}"] = {
node {
retry(3) {
checkout scm
}
sh 'make world'
}
}
}
parallel branches
viene eseguito come programma Groovy, con alcune speciali chiamate di funzione chiamate step che eseguono operazioni specifiche di Jenkins.
In questo esempio lo step parallel è definito in questo plugin, mentre node, retry, checkout e sh sono definiti in altri plugin della suite Pipeline. La variabile globale scm è definita nel plugin Pipeline Multibranch.
Lo script Groovy viene compilato in una classe denominata WorkflowScript, quindi questo è il nome mostrato negli stack trace invece del nome del file di script (ad esempio Jenkinsfile).
A differenza di un normale programma Groovy eseguito da riga di comando, lo stato completo del programma di una build Pipeline viene salvato su disco ogni volta che viene eseguita un'operazione asincrona, il che include la maggior parte degli step di Pipeline.
Jenkins può essere riavviato mentre una build è in esecuzione e riprenderà l'esecuzione del programma da dove era stata interrotta.
Questo non è pensato per essere efficiente e quindi dovrebbe essere limitato a codice "collante" (glue) di alto livello direttamente correlato alle funzionalità di Jenkins;
la logica di build del vostro progetto dovrebbe essere eseguita da programmi esterni su un nodo di build, in uno step sh o bat.
L'epica Pipeline Groovy in JIRA copre alcune limitazioni note dell'interprete Groovy. Questi problemi derivano dal fatto che Pipeline non può eseguire Groovy direttamente, ma deve intercettare ogni operazione per salvare lo stato del programma.
L'epica Pipeline Sandbox copre i problemi relativi alla sandbox Groovy utilizzata per impedire che script Pipeline dannosi prendano il controllo di Jenkins. Gli script eseguiti con la sandbox disabilitata possono effettuare chiamate dirette alle API interne di Jenkins, il che può essere un utile workaround per funzionalità di step mancanti, ma per motivi di sicurezza solo gli amministratori possono approvare tali script.
L'epica Pipeline Snippet Generator copre i problemi relativi allo strumento utilizzato per fornire esempi di sintassi degli step basati su moduli di configurazione in tempo reale.
Questo plugin era precedentemente chiamato "Workflow CPS plugin" o "Workflow Groovy Plugin". Di conseguenza ha l'artifactId Maven workflow-cps, non pipeline-groovy.
Il plugin utilizza la libreria Groovy CPS per implementare una trasformazione in stile continuation-passing sul programma durante la compilazione.
Il compilatore Groovy standard viene utilizzato per creare l'AST, ma la generazione del bytecode viene intercettata da un CompilationCustomizer che sostituisce la maggior parte delle operazioni con varianti che sollevano un "errore" speciale, CpsCallableInvocation.
Questo viene poi catturato dal motore, che utilizza le informazioni da esso fornite (come gli argomenti che stanno per essere passati a una chiamata di metodo) per passare il controllo alla continuazione successiva.
Gli script Pipeline possono contrassegnare metodi designati con l'annotazione @NonCPS.
Questi vengono quindi compilati normalmente (tranne che per i controlli di sicurezza della sandbox) e si comportano in modo molto simile ai metodi "binari" della piattaforma Java, del runtime Groovy o del codice di Jenkins core o dei plugin.
I metodi @NonCPS possono utilizzare in sicurezza oggetti non Serializable come variabili locali, ma non dovrebbero accettare parametri non serializzabili né restituire o memorizzare valori non serializzabili.
Non è possibile chiamare metodi regolari (trasformati CPS) o step di Pipeline da un metodo @NonCPS, quindi sono utilizzati al meglio per eseguire alcuni calcoli prima di restituire un riepilogo allo script principale.
Si noti in particolare che gli @Override di metodi definiti in classi binarie,
come Object.toString(),
dovrebbero in generale essere contrassegnati come @NonCPS poiché saranno comunemente chiamati da codice binario.
Alcuni tipi di oggetti sono intrinsecamente non sicuri da serializzare come tali, ma vogliamo mantenere un riferimento ad essi nel grafo del programma.
Un esempio è l'Executor (~ slot executor su un nodo integrato o agente) che fa parte del contesto passato da uno step node a qualsiasi step nel suo blocco, specialmente sh/bat.
Pipeline utilizza l'API Pickle per sostituire questi oggetti con versioni sicure per la serializzazione.
Quando un WorkflowRun viene caricato dal disco dopo un riavvio, lo stato del programma viene deserializzato e i pickle vengono deserializzati ("reidratati") in parallelo.
Se e quando tutti i pickle vengono deserializzati con successo e gli oggetti risultanti vengono riposti nello stato del programma, il programma riprende l'esecuzione e viene chiamato StepExecution.onResume per ripristinare timer e simili.
Tutta la logica del programma viene eseguita all'interno di un "thread CPS VM", che è semplicemente un pool di thread Java in grado di eseguire metodi binari e determinare quale continuazione eseguire successivamente.
Lo step parallel utilizza "green threads" (noti anche come multitasking cooperativo): registra nomi di thread logici (~ branch) per varie azioni, ma non li esegue letteralmente in simultanea.
Il programma può sembrare eseguire attività in concorrenza, ma solo perché la maggior parte degli step viene eseguita in modo asincrono, mentre il thread VM è inattivo, e possono sovrapporsi temporalmente.
Nessun thread Java viene consumato, tranne che durante gli intervalli in genere brevi in cui il codice Groovy viene effettivamente eseguito sul thread VM.
Il widget dell'executor mostra una voce per l'executor "flyweight" sul nodo integrato solo quando il thread VM è occupato; normalmente è nascosto.