1. 문제 생성하기

드림핵에 워게임 문제를 생성하는 방법을 알아보겠습니다.

워게임 문제 업로드 페이지에 접속하면 아래 사진과 같은 화면이 나옵니다.

워게임 문제는 내부적으로 Git 리포지토리 형태로 관리됩니다. 문제를 만들기 위해 가장 먼저 해야 할 일은 문제에 사용할 Git 리포지토리 이름을 지정하는 것입니다. 위 사진에서 아래쪽 빨간 네모로 표시된 부분에 워게임 문제가 사용할 Git 리포지토리 이름을 입력해주세요.

리포지토리 이름에는 Hyphen(-), Underscore(_), a-z0-9 문자만 사용해주세요.

한편 위 사진에서 위쪽 빨간 네모로 표시된 부분의 문제 이름 입력란은 Git 리포지토리 이름을 자동으로 추천하기 위한 도우미입니다. 실제로 이용자들에게 표시될 최종적인 문제 이름은 이후 업로드하는 버전에 따라 자유롭게 변경할 수 있습니다.

약관에 동의한 후 문제 생성하기 버튼을 눌러서 문제를 생성하고 나면 아래 사진과 같은 화면이 나오게 됩니다.


2. SSH 키 생성하기

워게임 문제에 사용될 파일은 Git을 이용해 업로드할 수 있습니다. 앞서 생성한 문제의 Git 리포지토리에 접근하려면 먼저 SSH 키를 드림핵 서버에 추가해야 합니다.

🔑 SSH 키를 생성한 적이 없다면 본 링크를 통해 자세히 알아보시기를 바랍니다.

SSH 키는 내 계정의 SSH 키 설정 페이지에서 추가하거나 삭제할 수 있습니다. SSH 키 추가를 정상적으로 마쳤다면 다음의 셸 명령어를 통해 SSH 키가 드림핵 서버에 잘 등록되었는지 확인할 수 있습니다.

ssh -T git@git.dreamhack.io

위 명령어를 실행하면 아래 결과와 같이 Welcome to DreamHack, @userid! 형태로 출력되어야 합니다.

$ ssh -T git@git.dreamhack.io
Welcome to DreamHack, @userid!

3. Git 리포지토리 복사하기

앞서 생성한 문제의 Git 리포지토리를 로컬에 복사해보도록 하겠습니다. 아래 사진에서 빨간 네모로 표시된 부분을 클릭하면 Git 리포지토리를 복사하는 명령어(git clone)를 클립보드에 복사할 수 있습니다.

복사한 명령어를 셸에 붙여넣고 실행하면 아래와 같이 출력됩니다.

user@machine:~/$ git clone git@git.dreamhack.io:[repository-name].git
Cloning into '[repository-name]'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.
user@machine:~/$

현재 git init 명령어를 통해 직접 생성한 리포지토리를 워게임 시스템에 복사해 넣는 행위는 지원되지 않습니다.


4. 문제 파일 구성하기

하나의 워게임 문제의 리포지토리에는 다음과 같은 파일과 디렉토리를 필수적으로 구성해야 합니다.

Repository Sample Download

  • Specfile
    • 문제에 대한 자세한 설정을 기술한 파일입니다.
    • 아래의 예제 파일을 복사하여 문제에 필요한 형태로 설정을 작성합니다.
  • Description.md
    • 문제에 대한 설명을 작성하는 파일입니다. Markdown 포맷으로 작성합니다.
    • 문제에 대한 정보나 문제를 해결하기 위해 필요한 단서를 기술하는 등의 문제와 관련된 내용을 적을 수 있습니다.
  • public/
    • 문제를 푸는데 필요한 파일들을 포함하는 폴더입니다.
    • 이 폴더의 파일들은 문제에 접근하는 모든 사용자가 자유롭게 다운로드할 수 있습니다.

만약 VM을 필요로 하는 문제라면 추가적으로 다음과 같은 파일을 필수적으로 업로드해야 합니다.

  • Dockerfile

    • Docker 컨테이너를 만들기 위한 설정을 기술한 파일입니다.

    • 자세한 내용은 Dockerfile Reference를 참조해 주세요.

    • Docker version을 명시할 때는, FROM ubuntu:22.04 보다는

      FROM ubuntu:22.04@sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac
      

      와 같이 베이스 이미지의 SHA256 해시값을 함께 명시해주는 것을 권장합니다.

    • 베이스 이미지의 SHA256 해시값은 docker inspect [Image Name] 이후 RepoDigests 값을 사용하면 됩니다.
      e.g) docker inspect ubuntu:22.04

    • libc file은 익스플로잇 과정에서 필요하다면 public/에 넣어주는 것을 권장합니다.

Specfile

모든 Specfile에는 [wargame] section이 필수적으로 포함되어야 하고, VM을 필요로 하는 문제라면 [vm] section이 추가적으로 포함되어야 합니다.

[wargame] section

[wargame] section에는 다음과 같은 속성들이 필수적으로 작성되어야 합니다.

  • title
    • 문제를 대표하는 제목입니다.
    • 정해진 서식이 없으며 자유롭게 설정할 수 있습니다.
  • flag
    • 문제를 해결한 뒤 획득할 수 있는 Flag입니다.
    • 정해진 형식은 없지만 유추하기 어렵게 작성하는 것이 이상적입니다.
  • tags
    • 문제에 해당하는 분야를 태그합니다. 모든 문제는 하나 이상의 태그가 지정되어야 합니다.
    • 다음과 같은 태그들을 사용할 수 있으며, 이외에도 적절한 태그를 사용할 수 있습니다.
      • pwnable
      • reversing
      • web
      • crypto
      • forensics
      • cloud
      • misc

[vm] section

[vm] section에는 다음과 같은 속성들이 필수적으로 작성되어야 합니다.

  • os
    • VM에서 사용하는 운영체제의 종류를 지정합니다.
    • 다음과 같은 운영체제를 사용할 수 있습니다.
      • linux
  • memory
    • VM 인스턴스가 사용하는 최대 메모리 크기를 MB 단위로 지정합니다.
  • disk
    • VM 인스턴스가 사용하는 최대 디스크 크기를 MB 단위로 지정합니다.
  • ports
    • 외부에서 VM으로 접근할 수 있는 포트를 지정합니다.
    • comma(,)로 구분된 [port]/[protocol] 형태의 쌍 목록을 사용하여야 합니다.
      • protocol로는 tcpudp를 사용할 수 있습니다.
  • allow_outgoing
    • VM이 외부 네트워크에 접근할 수 있는지 여부를 지정합니다.
    • true 또는 false 의 값을 사용할 수 있습니다.
  • docker_compose
    • docker-compose를 활용하여 이미지를 빌드합니다.
    • 해당 옵션을 지정하는 경우 Dockerfile 이 아닌 docker-compose.yml 파일을 요구합니다.

필요한 메모리 및 디스크 크기는 실제로 Docker를 사용해 image를 생성하고 container를 실행하는 것으로 필요한 크기를 확인할 수 있습니다.
Image의 크기는 docker image 명령어를, 메모리의 크기는 docker stats 명령어를 통해 확인할 수 있습니다.

필요한 공간의 최소화를 위해 Base image는 slim 혹은 alpine 버전을 사용하는 것을 권장합니다. 빌드하는 시간을 단축시킬 수 있으며, 디스크 크기 문제로 인한 에러를 크게 줄일 수 있습니다.

예제

Docker Version

[wargame]

title = My challenge
flag = flag{my_challenge}
tags = web

[vm]

os = linux
memory = 128
disk = 256
ports = 80/tcp, 443/tcp
allow_outgoing = false
docker_compose = false

Docker Compose Version

[wargame]

title = Compose challenge
flag = flag{compose_challenge}
tags = misc

[vm]
os = linux
memory = 128
disk = 1024
ports = 5000/tcp
allow_outgoing = false
docker_compose = true

5. Add, Commit, Tag, Push 하기

문제에 필요한 파일들을 리포지토리에 모두 구성한 뒤 그대로 드림핵 서버에 업로드하기 위해서는, 먼저 현재 상태를 커밋(Commit) 해야 합니다. 아래와 같이 커밋을 진행할 수 있습니다.

$ git add -A

$ git commit -m "commit message"
[master f2b6efd] commit message
(...)

커밋이 만들어진 후, 아래와 같은 명령어로 해당 커밋에 태그(Tag) 를 추가해야 합니다. 이 태그가 해당 워게임 문제의 버전 구분에 해당합니다.

$ git tag [tag-name]

태그한 결과물을 아래와 같이 푸쉬(Push) 하여 서버에 해당하는 버전을 업로드할 수 있습니다.

$ git push origin master --tags
Total 8 (delta 1), reused 0 (delta 0)
(...)
 * [new tag]         [tag-name] -> [tag-name]

레퍼런스: Git의 기초 - 수정하고 저장소에 저장하기
https://git-scm.com/book/ko/v2/Git의-기초-수정하고-저장소에-저장하기


6. 문제 검증하기

문제 파일 업로드에 성공한 경우 업로드된 파일이 정상적인 구성인지 검증해야 합니다. 검증에 성공한 버전에 한해서 문제를 배포할 수 있으므로 검증에 성공해야 합니다. 검증은 아래 사진과 같이 Deploy 탭에서 앞서 업로드한 태그(v0.1)를 클릭한 후 검증 버튼을 누르면 됩니다.

검증 여부는 아래 사진과 같이 Tasks 탭에서 확인할 수 있습니다. 만약 검증에 실패하는 경우 실패 사유가 표시되며, 우측의 Detail 버튼을 눌러서 자세한 로그를 확인할 수 있습니다.


7. 문제 배포하기&테스트하기

검증에 성공하였으면 아래 사진과 같이 다시 Deploy 탭으로 돌아와서 해당하는 태그를 누른 후 배포 버튼을 눌러 문제를 배포할 수 있습니다.

배포를 하고 나면 설정된 버전의 문제 설명 및 공개 첨부 파일 등이 이용자에게 공개됩니다.

VM을 사용하는 문제의 경우 의도한 대로 작동하는지 아래 사진과 같이 Manage 탭의 서버 생성하기 버튼을 눌러 테스트해볼 수 있습니다.

모든 것이 원하는 대로 작동하여 이 문제를 공개해도 좋은 상태라면, 아래 사진과 같이 문제 공개하기 버튼을 클릭하여 모든 드림핵 이용자들이 문제에 접근하여 문제를 풀 수 있도록 공개할 수 있습니다.

한 번 공개한 문제는 다시 비공개로 전환할 수 없습니다.

워게임 구조

Dreamhack 워게임은 만약 문제를 동작시키기 위해 별도의 서버를 필요로 하는 경우(예: 리모트 서버, 웹 서버) 해당 문제를 해결하기 위해 각 유저를 대상으로 별도의 가상 환경을 제공합니다. 해당 가상 환경을 공격하여 사용자는 Flag를 획득하고, 문제에 따른 점수를 획득합니다.

  • 사용자가 VM을 필요로 하는 문제를 도전하려고 할 때 새로운 VM 인스턴스가 생성되며 각 유저에게 독자적으로 제공됩니다.
    • 이를 통해 문제를 도전 중인 여러 사용자의 상태가 간섭되는 것을 방지합니다.
  • 워게임 서버의 임의의 포트가 배정되며, Specfile에 정의된 포트로 포워딩됩니다.
    • 포트는 TCP와 UDP를 지원합니다.
    • 각 문제는 여러 개의 포트를 Expose 할 수 있지만, 워게임 서버에서 순차적인 포트를 배정하지 않을 수 있습니다.
      예를 들어, Specfile에서 Expose 하기로 지정한 포트가 8080/tcp,8081/tcp 과 같은 연속된 포트여도, 시스템에서 배정하는 포트는 10254/tcp,10532/tcp 와 같이 연속적이지 않을 수 있습니다.

서버 설정

Dreamhack 문제가 제공하는 서버는 아래의 설정을 기반으로 동작하며, 문제는 해당 환경 아래에서 반드시 호환성을 테스트해야 합니다.

  • 모든 문제 VM은 Firecracker microVM 아래에서 동작합니다.
  • 문제를 만든 사람이 제공한 image를 빌드한 container가 문제의 VM으로 사용됩니다.
  • Container는 문제를 만든 사람이 명시한 Dockerfile를 기반으로 생성됩니다.
  • 문제의 리포지토리 내 Specfile에 문제 VM이 필요로 하는 최소의 CPU, 메모리, 디스크 크기가 설정되어야 합니다.
    • 문제를 만드는 사람의 별도 요청에 따라 변경될 수 있습니다.
  • 문제 VM은 CPU 사용량 제한이 걸려 있습니다. (전체 서버의 CPU 사용량 중 약 10% 규모)
    • 부팅 과정은 예외로 CPU 사용량 제한이 해제됩니다.
  • VM 자체의 메모리 사용량에 의해 최소 메모리 필요 용량은 128MB입니다.
  • 디스크 크기는 image를 빌드하기 위한 공간을 포함해야합니다.
    • 문제를 만드는 사람이 직접 Dockerfile을 테스트하여 필요한 공간의 크기를 알고 있어야 합니다.
    • docker images 명령어를 통해 Docker image의 크기를 알 수 있습니다.

문제 구성 관리

Dreamhack은 문제의 버전 관리의 용이함을 위해 Git을 내부적으로 사용하고 있습니다.

  • 문제가 생성됨과 동시에 실제 Git 리포지토리가 서버에 생성되며 관리됩니다.
  • 문제 출제자가 문제를 개발하는 과정 중 원하는 상태의 커밋을 tag하고 웹페이지를 통해 Deploy할 수 있습니다.
    • 이 과정을 통해 문제 출제자와 Dreamhack 스탭들이 쉽게 기록을 통해 발생한 서버 문제를 해결할 수 있습니다.
  • Git 리포지토리는 현재 128MB의 크기 제한이 있습니다.
  • Deploy를 요청하기 전에 반드시 설정된 값이 정상적인지 검사해야 하며, 웹페이지에서 검사를 요청할 수 있습니다.
  • Test 과정이 완료되면 Dockerfile을 포함한 문제인 경우 image를 빌드합니다.
  • 만약 image를 재생성 및 재배포해야 하는 경우 새로운 Tag를 부여하여 빌드해야 합니다.
  • Test 과정과 image 빌드 과정이 모두 끝나면 "Live" 버전으로 배포할 수 있게 됩니다.
  • 유저가 문제의 정보를 요청하는 경우 문제를 만든 사람이 기술한 문제의 "Live" 버전의 정보를 받게 됩니다.