Concourse Enumeration & Attacks
Concourse Enumeration & Attacks
User Roles & Permissions
Concourse vem com cinco funções:
Concourse Admin: Esta função é dada apenas aos proprietários da equipe principal (equipe inicial padrão do concourse). Admins podem configurar outras equipes (ex.:
fly set-team
,fly destroy-team
...). As permissões desta função não podem ser afetadas por RBAC.owner: Proprietários de equipe podem modificar tudo dentro da equipe.
member: Membros da equipe podem ler e escrever dentro dos ativos da equipe, mas não podem modificar as configurações da equipe.
pipeline-operator: Operadores de pipeline podem realizar operações de pipeline como acionar builds e fixar recursos, mas não podem atualizar configurações de pipeline.
viewer: Visualizadores da equipe têm acesso "somente leitura" a uma equipe e seus pipelines.
Além disso, as permissões das funções owner, member, pipeline-operator e viewer podem ser modificadas configurando RBAC (configurando mais especificamente suas ações). Leia mais sobre isso em: https://concourse-ci.org/user-roles.html
Note que o Concourse agrupa pipelines dentro de Equipes. Portanto, usuários pertencentes a uma Equipe poderão gerenciar esses pipelines e várias Equipes podem existir. Um usuário pode pertencer a várias Equipes e ter permissões diferentes em cada uma delas.
Vars & Credential Manager
Nos configs YAML você pode configurar valores usando a sintaxe ((_source-name_:_secret-path_._secret-field_))
.
Dos documentos: O source-name é opcional, e se omitido, o gerenciador de credenciais de cluster será usado, ou o valor pode ser fornecido estaticamente.
O opcional _secret-field_ especifica um campo no segredo buscado para ler. Se omitido, o gerenciador de credenciais pode escolher ler um 'campo padrão' do segredo buscado se o campo existir.
Além disso, o secret-path e o secret-field podem ser cercados por aspas duplas "..."
se contiverem caracteres especiais como .
e :
. Por exemplo, ((source:"my.secret"."field:1"))
definirá o secret-path como my.secret
e o secret-field como field:1
.
Static Vars
Vars estáticas podem ser especificadas em passos de tarefas:
Ou usando os seguintes argumentos do fly
:
-v
ou--var
NAME=VALUE
define a stringVALUE
como o valor para a variávelNAME
.-y
ou--yaml-var
NAME=VALUE
analisaVALUE
como YAML e define como o valor para a variávelNAME
.-i
ou--instance-var
NAME=VALUE
analisaVALUE
como YAML e define como o valor para a variável de instânciaNAME
. Veja Grouping Pipelines para saber mais sobre variáveis de instância.-l
ou--load-vars-from
FILE
carregaFILE
, um documento YAML contendo mapeamento de nomes de variáveis para valores, e define todos eles.
Gerenciamento de Credenciais
Existem diferentes maneiras de um Gerenciador de Credenciais ser especificado em um pipeline, leia como em https://concourse-ci.org/creds.html. Além disso, o Concourse suporta diferentes gerenciadores de credenciais:
Note que se você tiver algum tipo de acesso de escrita ao Concourse você pode criar jobs para exfiltrar esses segredos já que o Concourse precisa ser capaz de acessá-los.
Enumeração do Concourse
Para enumerar um ambiente Concourse, você primeiro precisa coletar credenciais válidas ou encontrar um token autenticado, provavelmente em um arquivo de configuração .flyrc
.
Login e Enumeração do Usuário Atual
Para fazer login, você precisa saber o endpoint, o nome da equipe (o padrão é
main
) e uma equipe à qual o usuário pertence:fly --target example login --team-name my-team --concourse-url https://ci.example.com [--insecure] [--client-cert=./path --client-key=./path]
Obter alvos configurados:
fly targets
Verificar se a conexão do alvo configurado ainda é válida:
fly -t <target> status
Obter papel do usuário contra o alvo indicado:
fly -t <target> userinfo
Note que o token da API é salvo em $HOME/.flyrc
por padrão, ao saquear uma máquina você pode encontrar as credenciais lá.
Equipes & Usuários
Obter uma lista das Equipes
fly -t <target> teams
Obter papéis dentro da equipe
fly -t <target> get-team -n <team-name>
Obter uma lista de usuários
fly -t <target> active-users
Pipelines
Listar pipelines:
fly -t <target> pipelines -a
Obter yaml do pipeline (informações sensíveis podem ser encontradas na definição):
fly -t <target> get-pipeline -p <pipeline-name>
Obter todas as variáveis declaradas na configuração do pipeline
for pipename in $(fly -t <target> pipelines | grep -Ev "^id" | awk '{print $2}'); do echo $pipename; fly -t <target> get-pipeline -p $pipename -j | grep -Eo '"vars":[^}]+'; done
Obter todos os nomes de segredos usados nos pipelines (se você puder criar/modificar um job ou sequestrar um container, você poderia exfiltrá-los):
Containers & Workers
Listar workers:
fly -t <target> workers
Listar containers:
fly -t <target> containers
Listar builds (para ver o que está em execução):
fly -t <target> builds
Concourse Attacks
Força Bruta de Credenciais
admin:admin
test:test
Enumeração de Secrets e parâmetros
Na seção anterior vimos como você pode obter todos os nomes de secrets e variáveis usados pelo pipeline. As variáveis podem conter informações sensíveis e o nome dos secrets será útil mais tarde para tentar roubá-los.
Sessão dentro de um container em execução ou recentemente executado
Se você tiver privilégios suficientes (papel de membro ou superior) você poderá listar pipelines e papéis e simplesmente obter uma sessão dentro do container <pipeline>/<job>
usando:
Com essas permissões, você pode ser capaz de:
Roubar os segredos dentro do container
Tentar escapar para o nó
Enumerar/Abusar do endpoint de metadados da nuvem (do pod e do nó, se possível)
Criação/Modificação de Pipeline
Se você tiver privilégios suficientes (papel de membro ou mais), você poderá criar/modificar novos pipelines. Veja este exemplo:
Com a modificação/criação de um novo pipeline, você poderá:
Roubar os segredos (via exibição deles ou entrando no container e executando
env
)Escapar para o nó (dando a você privilégios suficientes -
privileged: true
)Enumerar/Abusar do endpoint de cloud metadata (do pod e do nó)
Excluir o pipeline criado
Executar Tarefa Personalizada
Isso é semelhante ao método anterior, mas em vez de modificar/criar um pipeline inteiro, você pode apenas executar uma tarefa personalizada (o que provavelmente será muito mais furtivo):
Escaping to the node from privileged task
Nas seções anteriores, vimos como executar uma tarefa privilegiada com concourse. Isso não dará ao container exatamente o mesmo acesso que a flag privilegiada em um container docker. Por exemplo, você não verá o dispositivo do sistema de arquivos do nó em /dev, então a fuga pode ser mais "complexa".
No seguinte PoC, vamos usar o release_agent para escapar com algumas pequenas modificações:
Como você pode ter notado, isso é apenas um regular release_agent escape apenas modificando o caminho do cmd no node.
Escapando para o node a partir de um contêiner Worker
Um regular release_agent escape com uma pequena modificação é suficiente para isso:
Escaping to the node from the Web container
Mesmo que o contêiner web tenha algumas defesas desativadas, ele não está sendo executado como um contêiner privilegiado comum (por exemplo, você não pode montar e as capacidades são muito limitadas, então todas as maneiras fáceis de escapar do contêiner são inúteis).
No entanto, ele armazena credenciais locais em texto claro:
Você pode usar essas credenciais para fazer login no servidor web e criar um contêiner privilegiado e escapar para o nó.
No ambiente, você também pode encontrar informações para acessar a instância postgresql que o concourse usa (endereço, nome de usuário, senha e banco de dados, entre outras informações):
Abusando do Garden Service - Não é um ataque real
Estas são apenas algumas notas interessantes sobre o serviço, mas como ele está ouvindo apenas no localhost, essas notas não apresentarão nenhum impacto que já não tenhamos explorado antes.
Por padrão, cada worker do concourse estará executando um serviço Garden na porta 7777. Este serviço é usado pelo Web master para indicar ao worker o que ele precisa executar (baixar a imagem e executar cada tarefa). Isso soa muito bem para um atacante, mas há algumas boas proteções:
Está apenas exposto localmente (127.0.0.1) e eu acho que quando o worker se autentica contra o Web com o serviço SSH especial, um túnel é criado para que o servidor web possa falar com cada serviço Garden dentro de cada worker.
O servidor web está monitorando os containers em execução a cada poucos segundos, e containers inesperados são deletados. Então, se você quiser executar um container personalizado, você precisa adulterar a comunicação entre o servidor web e o serviço garden.
Os workers do Concourse executam com altos privilégios de container:
No entanto, técnicas como montar o dispositivo /dev do nó ou release_agent não funcionarão (já que o dispositivo real com o sistema de arquivos do nó não está acessível, apenas um virtual). Não podemos acessar processos do nó, então escapar do nó sem exploits de kernel fica complicado.
Na seção anterior vimos como escapar de um contêiner privilegiado, então se pudermos executar comandos em um contêiner privilegiado criado pelo trabalhador atual, poderíamos escapar para o nó.
Note que brincando com concourse, observei que quando um novo contêiner é gerado para executar algo, os processos do contêiner são acessíveis a partir do contêiner do trabalhador, então é como um contêiner criando um novo contêiner dentro dele.
Entrando em um contêiner privilegiado em execução
Criando um novo contêiner privilegiado
Você pode criar um novo contêiner muito facilmente (basta executar um UID aleatório) e executar algo nele:
No entanto, o servidor web está verificando a cada poucos segundos os containers que estão em execução, e se um inesperado for descoberto, ele será deletado. Como a comunicação está ocorrendo em HTTP, você pode adulterar a comunicação para evitar a exclusão de containers inesperados:
Referências
https://concourse-ci.org/vars.html
Last updated