CTF/docker

CTF pwnable docker 구축

혹시 처음으로 CTF를 개최할 때 Docker를 사용해야 하는 사람들을 위해 많이 부족하지만 실제 교내에서 CTF에 사용할 Docker 구축할 때 구축 과정을 자세하게 기록을 해두려고 한다.

처음으로 CTF를 진행하고 Docker도 처음으로 다루어 본것이므로 많이 부족한 내용일 수 있습니다.

환경

  • 개인적으로 구축할떄는 kali를 이용하였고 CTF에는 ubuntu를 사용하였다.
  • docker-compose를 이용하였다.
들어가기에 앞서 도커와 컨테이너에 대한 아무 지식이 없으면
https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html 글을 한번 읽어 보는것이 좋다.

 

NC

컨테이너를 구축을 하기위해서는 다음과 같은 파일이 필요하다

  1. Dockerfile
  2. docker-compose.yml
  3. 실행파일
  4. xinted 서비스파일
  5. /etc/services에 해당 서비스 포트 추가한 파일
2번은 compose 미사용시 필요하지 않고 5번도 따로 설정을 해주면 필요가 없다.

간단하게 플래그를 출력하는 프로그램이다.

#include <stdio.h>
#include <stdlib.h>
​
​
int main(){
    setvbuf(stdout, NULL, _IONBF, 0);
    printf("ctfCTF{Test_test!}");
}

gcc -o nc nc.c 로 파일을 만들어준다.

 

Dockerfile을 만들어준다

FROM ubuntu:16.04
MAINTAINER leemon
RUN apt update
RUN apt install xinetd -y
RUN useradd -mU leemon
COPY ./pwn /etc/xinetd.d/pwn
COPY ./nc /home/leemon/nc
COPY ./services /etc/services
RUN chmod 4750 /home/leemon /home/leemon/nc
RUN chown -R leemon:leemon /home/leemon
CMD ["/usr/sbin/xinetd","-dontfork"]

FROM

  • FROM은 어떤 이미지를 기반으로 이미지를 생성할지를 설정한다. 이때 기존에 있는 이미지를 기반으로 생성하기 때문에 FROM은 반드시 설정해야 한다.

MAINTAINER

  • 이미지를 생성한 사람의 정보 설정 (생략 가능)

RUN

  • RUN은 FROM에서 설정한 이미지 위에서 스크립트 혹은 명령을 실행한다.

COPY

  • 파일을 이미지에 추가한다.
  • COPY <복사할 파일 경로> <이미지에서 파일이 위치할 경로> 형식으로 사용한다.
  • <복사할 파일 경로>는 콘텍스트 아래를 기준으로 하여 컨텍스트 바깥의 파일, 디렉터리나 절대 경로는 사용할 수 없다.
  • ex) COPY ../hello /home/hello (x)
  • ex) COPY /home/hello/flag /home/lfag (x)
  • ex) COPY /flag /home/flag (o)

CMD

  • CMD는 컨테이너가 시작되었을 때 스크립트 혹은 명령을 실행한다. docker run 명령으로 컨테이너를 생성하거나, docker strat 명령으로 정지된 컨테이너를 시작할 때 실행된다.
  • CMD는 Dokcerfile에서 한 번만 사용 가능하다.

이외에도 많은 명령어가 존재한다.

이후 docker-compose.yml 파일도 만들어준다.

version: '2.2'
​
services:
  pwn_nc:
    build: ./
    ports:
          - "12370:12370" //"호스트포트:컨테이너포트"

이때 호스트 포트는 접속할 때에 포트 ex)nc 172.31.9.31 12370 이때 사용하는 포트이고 컨테이너는

도커에서 컨테이너로 접속할 때에 포트이다 즉 "12370:22" 로 하고 Dockerfile을 ssh설정으로 조금 바꿔주면

ssh id@172.31.9.31 -p 12370으로 ssh접속이 가능해진다.

docker-compose up -d 으로 이미지와 컨테이너를 생성한 후 Docker or docker-compose.yml을 수정했다면
docker-compose up -d --build 으로 다시 이미지를 생성한 후 컨테이너를 생성한다.

 

다음으로 컨테이너 안에 들어갈 xinetd 서비스 파일과 /etc/services 파일에 해당 서비스 포트가 추가된 파일을 만든다.

 

xinetd 서비스 파일

service pwn_nc
{
    disable     = no
    type        = UNLISTED
    wait        = no
    server      = /home/leemon/nc
    socket_type = stream
    protocol    = tcp
    user        = leemon
    port        = 12370
    flags       = REUSE
}

 

/etc/services 파일

            .
            .
            .
tfido       60177/tcp           # fidonet EMSI over telnet
fido        60179/tcp           # fidonet EMSI over TCP
​
# Local services
pwn_nc         12370/tcp

 

다음과 같이 docker-compose.yml, Dockerfile, nc(실행파일, pwn_nc(xinetd 서비스 파일), services(해당 서비스 포트 추가) 파일이 있다면 이제 준비는 끝이다.

docker-compose up -d로 새로운 이미지와 컨테이너를 만들고 컨테이너를 background로 실행시킨다.

[docker-compose up -d 실행]

 

이후 이미지는 만들어지고 컨테이너 또한 생성 후 실행 중인 모습을 볼 수 있다.

 

컨테이너는 성공적으로 열었으니 접속을 하려 한다.

[ifconfig 출력]

위 결과를 볼 때 접속은 무엇으로 해야 할까...? docker? 컨테이너(br-ee08cd1ddab6)?

정답은 docker-host이다.

아주 간단하게 설명을 하자면

외부에서 nc 172.31.9.31 12370 요청을 하면

docker-host에서는 그것을 받고 PREROUTING chain을 통해 DOCKER chain으로 전달한다.

이후 DOCKER chain에서 12370으로 들어온 요청을 컨테이너 12370으로 전달해준다.

 

실제로 접속이 되는지 테스트는 netcat win을 사용하였다.

[window에서 nc 접속]

flag가 출력된 것을 보아 접속은 성공적으로 이루어진 것을 볼 수 있다.

 

추가

만약 docker-compose up -d이후 이미지 완성은 되었는데 컨테이너가 안 열릴 때 nc나 ssh가 똑바로 안 열린 것이므로 그 부분 예를 들어 Dockerfile에 CMD ["/usr/sbin/xinetd","-dontfork"]요 부분이 빠졌다던지.

 

Reference