혹시 처음으로 CTF를 개최할 때 Docker를 사용해야 하는 사람들을 위해 많이 부족하지만 실제 교내에서 CTF에 사용할 Docker 구축할 때 구축 과정을 자세하게 기록을 해두려고 한다.
처음으로 CTF를 진행하고 Docker도 처음으로 다루어 본것이므로 많이 부족한 내용일 수 있습니다.
환경
- 개인적으로 구축할떄는 kali를 이용하였고 CTF에는 ubuntu를 사용하였다.
- docker-compose를 이용하였다.
들어가기에 앞서 도커와 컨테이너에 대한 아무 지식이 없으면
https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html 글을 한번 읽어 보는것이 좋다.
NC
컨테이너를 구축을 하기위해서는 다음과 같은 파일이 필요하다
- Dockerfile
- docker-compose.yml
- 실행파일
- xinted 서비스파일
- /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? 컨테이너(br-ee08cd1ddab6)?
정답은 docker-host이다.
아주 간단하게 설명을 하자면
외부에서 nc 172.31.9.31 12370 요청을 하면
docker-host에서는 그것을 받고 PREROUTING chain을 통해 DOCKER chain으로 전달한다.
이후 DOCKER chain에서 12370으로 들어온 요청을 컨테이너 12370으로 전달해준다.
실제로 접속이 되는지 테스트는 netcat win을 사용하였다.
flag가 출력된 것을 보아 접속은 성공적으로 이루어진 것을 볼 수 있다.
추가
만약 docker-compose up -d
이후 이미지 완성은 되었는데 컨테이너가 안 열릴 때 nc나 ssh가 똑바로 안 열린 것이므로 그 부분 예를 들어 Dockerfile에 CMD ["/usr/sbin/xinetd","-dontfork"]
요 부분이 빠졌다던지.
Reference
- docker
http://pyrasis.com/Docker/Docker-HOWTO
https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html - docker-compose
https://blog.d0ngd0nge.xyz/docker-compose/
https://www.44bits.io/ko/post/almost-perfect-development-environment-with-docker-and-docker-compose
- docker network
https://bluese05.tistory.com/15
https://doitnow-man.tistory.com/183 - netcat win download
https://eternallybored.org/misc/netcat/